Unity加载OSGB倾斜摄影数据
显而易见有一个最方便的办法就是使用CesiumForUnity确定是可以通过osgb数据转换成3dtiles进行加载的,然而有没有直接加载osgb格式数据的方法呢?
我们知道osgb的osg推出的倾斜摄影数据的数据结构,所以,osg引擎一定是支持osgb最好的三维引擎,研究osgb的加载,了解osg的库也就是必然项!
然而但是,三维引擎方向,UE,Unity一定是主流中的主流了,怎么让osgb在这两个引擎中工作成了大家研究的一个侧重点。有需求才有方向嘛,不然盲人摸象。
所以,我们看看既有的成熟成果:
https://github.com/xarray/UnityOSGB.git
(git上的一个作者写好的项目,下载下来直接运行试试即可,注意下unity版本的使用)
启动项目之后示例项目挂载了ReadOSGB脚本,如下图:
加载osgb数据取决于AsOsgbFolder,分:文件夹加载和单个osgb文件加载。
需要注意的是:文件夹加载方式并没有使用lod,所以加载的数据只是data文件夹下,瓦片文件夹下的同名osgb数据(效果如下图所示),并没有加载到层级osgb数据,显示并不尽如人意,需要自己再跟进一下(随后吧,我也没太清楚)!
文件读取的整体代码量也还好,贴出来,如下:
using System.Collections;
using System.Collections.Generic;
using System.IO;
using UnityEngine;
namespace osgEx
{
public class ReaderOSGB : MonoBehaviour
{
const int OSG_HEADER_L = 0x6C910EA1;
const int OSG_HEADER_H = 0x1AFB4545;
public Material _template;
public string _fileOrPathName;
public bool _asOsgbFolder, _withMeshCollider;
public int _sceneType = 0, _version = 0;
public bool _useBrackets = false;
public bool _useSchemaData = false;
public bool _useDomains = false;
public string _currentFileName;
public Plane[] _currentFrustum;
public Texture2D _preloadedTexture = null;
public Dictionary<uint, Object> _sharedObjects = new Dictionary<uint, Object>();
public Dictionary<uint, Texture2D> _sharedTextures = new Dictionary<uint, Texture2D>();
public class PagingRequest
{
public PagedData pagedData;
public int childIndex, updatedStamp;
}
List<PagingRequest> _loadingRequests = new List<PagingRequest>();
List<PagingRequest> _unloadingRequests = new List<PagingRequest>();
Task _pagingRequestHandler;
object _taskMutex = new object();
bool _isTaskRunning = false;
public void RequestLoadingAndUnloading(PagedData data, List<int> toLoad, List<int> toUnload)
{
lock (_taskMutex)
{
foreach (int index in toLoad)
{
PagingRequest req = _loadingRequests.Find(
c => c.pagedData.Equals(data) && c.childIndex.Equals(index));
if (req == null)
{
req = new PagingRequest
{ pagedData = data, childIndex = index, updatedStamp = Time.frameCount };
_loadingRequests.Add(req);
}
else
req.updatedStamp = Time.frameCount;
}
foreach (int index in toUnload)
{
PagingRequest req = _unloadingRequests.Find(
c => c.pagedData.Equals(data) && c.childIndex.Equals(index));
if (req == null)
{
req = new PagingRequest
{ pagedData = data, childIndex = index, updatedStamp = Time.frameCount };
_unloadingRequests.Add(req);
}
else
req.updatedStamp = Time.frameCount;
}
}
}
void LoadOrUnloadData(PagedData data, int index, bool toLoad)
{
if (toLoad)
{
string fileName = data.getFullFileName(index);
GameObject fineNode = LoadSceneFromFile(fileName);
if (fineNode != null)
{
fineNode.name = Path.GetFileNameWithoutExtension(fileName);
fineNode.transform.SetParent(data.gameObject.transform, false);
data._pagedNodes[index] = fineNode;
data._pagedNodes[0].SetActive(false); // FIXME: assume only 1 rough level
}
else
Debug.LogWarning("Unable to read OSGB data from " + fileName);
}
else
{
Destroy(data._pagedNodes[index]);
data._pagedNodes[index] = null;
data._pagedNodes[0].SetActive(true); // FIXME: assume only 1 rough level
}
}
IEnumerator PagingTask()
{
_isTaskRunning = true;
while (_isTaskRunning)
{
PagedData pData0 = null;
int index0 = -1, currentFrame = Time.frameCount;
lock (_taskMutex)
{
foreach (PagingRequest req in _loadingRequests)
{
if (req.updatedStamp >= currentFrame - 1) // latest req
{
pData0 = req.pagedData; index0 = req.childIndex;
_loadingRequests.Remove(req); break;
}
}
if (index0 < 0)
{
for (int i = 0; i < _loadingRequests.Count;)
{
PagingRequest req = _loadingRequests[i];
if (req.updatedStamp < currentFrame - 10) // too-old req
_loadingRequests.RemoveAt(i);
else ++i;
}
}
if (_unloadingRequests.Count > 60)
{
for (int i = 0; i < _unloadingRequests.Count;)
{
PagingRequest req = _unloadingRequests[i];
if (req.updatedStamp >= currentFrame - 1) // latest req
{
LoadOrUnloadData(req.pagedData, req.childIndex, false);
_unloadingRequests.RemoveAt(i);
}
else if (req.updatedStamp < currentFrame - 10) // too-old req
_unloadingRequests.RemoveAt(i);
else ++i;
}
}
}
if (pData0 && index0 >= 0)
LoadOrUnloadData(pData0, index0, true);
//Debug.Log(_loadingRequests.Count + ", " + _unloadingRequests.Count);
yield return null;
}
}
IEnumerator StopPagingTask(Task task)
{
_isTaskRunning = false;
yield return null;
task.Stop();
}
//加载场景数据,直接osgb文件
public GameObject LoadSceneData(string fileName, BinaryReader reader)
{
// Load header data
int magicNumL = reader.ReadInt32();
int magicNumH = reader.ReadInt32();
if (magicNumL != OSG_HEADER_L || magicNumH != OSG_HEADER_H)
{
Debug.LogWarning("Unmatched magic number");
return null;
}
_sceneType = reader.ReadInt32();
_version = reader.ReadInt32();
int attributes = reader.ReadInt32();
Debug.Log("OSGB file " + fileName + ": version " + _version +
", " + attributes.ToString("X"));
_useBrackets = (attributes & 0x4) != 0;
_useSchemaData = (attributes & 0x2) != 0;
_useDomains = (attributes & 0x1) != 0;
// TODO: handle attributes
string compressor = ObjectBase.ReadString(reader);
if (compressor != "0")
{
Debug.LogWarning("Decompressor " + compressor + " not implemented");
return null;
}
// Load root object
GameObject scene = new GameObject(Path.GetFileNameWithoutExtension(fileName));
if (!ObjectBase.LoadObject(scene, reader, this))
{
Debug.LogWarning("Failed to load scene");
return null;
}
// Clear temperatory variables
_preloadedTexture = null;
_sharedObjects.Clear();
_sharedTextures.Clear();
return scene;
}
//从文件中加载osgb数据
public GameObject LoadSceneFromFile(string fileName)
{
_currentFileName = fileName;
if (!File.Exists(fileName))
{
Debug.LogWarning("Unable to find file " + fileName);
return null;
}
FileStream stream = File.Open(fileName, FileMode.Open, FileAccess.Read);
if (!stream.CanRead)
{
Debug.LogWarning("Unable to read binary stream from " + fileName);
return null;
}
GameObject gameScene = LoadSceneData(fileName, new BinaryReader(stream));
stream.Close(); return gameScene;
}
void Start()
{
_pagingRequestHandler = new Task(PagingTask());
if (_template == null)
_template = new Material(Shader.Find("Standard"));
if (_asOsgbFolder)
{
foreach (string folderName in Directory.GetDirectories(_fileOrPathName))
{
//只是取到了子文件夹的同名文件,lod文件没有加载,所以lod层级不足,显示效果不好
string rootFile = folderName + Path.DirectorySeparatorChar
+ Path.GetFileName(folderName) + ".osgb";
GameObject scene = LoadSceneFromFile(rootFile);
if (scene != null)
scene.transform.SetParent(this.transform, false);
else
Debug.LogWarning("Unable to read OSGB data from " + rootFile);
}
}
else
{
GameObject scene = LoadSceneFromFile(_fileOrPathName);
if (scene != null)
scene.transform.SetParent(this.transform, false);
else
Debug.LogWarning("Unable to read OSGB data from " + _fileOrPathName);
}
// Get global center & extents
MeshFilter[] mfList = GetComponentsInChildren<MeshFilter>();
Bounds totalBounds = new Bounds();
for (int j = 0; j < mfList.Length; ++j)
{
Matrix4x4 l2w = mfList[j].transform.localToWorldMatrix;
Vector3[] vertices = mfList[j].sharedMesh.vertices;
Bounds meshBounds = new Bounds();
for (int i = 0; i < vertices.Length; ++i)
{
if (i == 0) meshBounds.center = l2w.MultiplyPoint(vertices[i]);
else meshBounds.Encapsulate(l2w.MultiplyPoint(vertices[i]));
}
if (j == 0) totalBounds = meshBounds;
else totalBounds.Encapsulate(meshBounds);
}
this.transform.position = -totalBounds.center;
}
void Update()
{
_currentFrustum = GeometryUtility.CalculateFrustumPlanes(Camera.main);
}
void OnDestroy()
{
StopPagingTask(_pagingRequestHandler);
}
}
}