vsgCs显示谷歌全球倾斜模型-节点
前言
本章将在上章json解析与vsg场景图根节点创建的基础上,继续深入vsgCs中的节点,包含vsgCs::WorldNode和vsgCs::TilesetNode,深入两者与Cesium3DTilesSelection::Tileset的结合,并进一步剖析场景图的更新过程。
目录
- 1 vsgCs节点与CesiumNative的结合
- 2 场景图更新
1 vsgCs节点与CesiumNative的结合
上章深入讲解了vsgCs中节点vsgCs::WorldNode与vsg::TilesetNode通过解析json字符串的创建过程,其中vsgCs::WorldNode可包含多个vsgCs::TilesetNode节点,其中vsgCs::TilesetNode是对Cesium3DTilesSelection::Tileset的封装,此处为vsgCs与CesiumNative的结合处。Cesium3DTilesSelection::Tileset的创建通过”ionAssetID"、"ionAccessToken',针对谷歌全球倾斜模型不涉及overlays,创建过程如下:
_tileset= std::make_unique<Cesium3DTilesSelection::Tileset>
(*externals,source.ionAssetID.value(),source.ionAccessToken.value(),options);
其中变量externals类型为Cesium3DTilesSelection::TilesetExternals,依赖关系如下:
其中CesiumAsync::IAssetAccessor为资产访问器,向服务器发起HTTP/HTTPS请求,获取所需要的瓦片数据;Cesium3DTilesSelection::IPrepareRendererResources 为渲染资源准备器,将加载好的数据转换为渲染引擎可用的资源,vsgCs中会将加载好的数据转换为vsg节点;CesiumUtility::CreditSystem为信用系统,如可通过CesiumUtility::CreditSystem获取google的logo在视口左下角显示;spdlog::logger用于CesiumNative日志的输出。
接着Cesium3DTilesSelection::Tileset载入元数据,示例代码如下:
// ExplorationtilesetNode->getTileset()->loadMetadata().thenInMainThread([source](const Cesium3DTilesSelection::TilesetMetadata* metadata){std::string tilesetName;if (source.url){tilesetName = *source.url;}else if (source.ionAssetID){tilesetName = "ion asset " + std::to_string(*source.ionAssetID);}if (metadata){vsg::debug(tilesetName + " has metadata.");if (metadata->schema){vsg::debug(tilesetName + " has schema.");}if (metadata->schemaUri){vsg::debug(tilesetName + " schema uri: " + *metadata->schemaUri);}}else{vsg::debug("Tileset has no metadata?");}}).catchInMainThread([](std::exception&&){vsg::warn("exception getting metadata");});
上述代码不会阻塞主线程,可将上述代码修改为如下实现同步:、
bool loaded = false;tilesetNode->getTileset()->loadMetadata().thenInMainThread([source,&loaded](const Cesium3DTilesSelection::TilesetMetadata* metadata){std::string tilesetName;if (source.url){tilesetName = *source.url;}else if (source.ionAssetID){tilesetName = "ion asset " + std::to_string(*source.ionAssetID);}if (metadata){OSG_DEBUG << tilesetName <<" has metadata."<<std::endl;if (metadata->schema){OSG_DEBUG << tilesetName << " has schema." << std::endl;}if (metadata->schemaUri){OSG_DEBUG << tilesetName << " schema uri: " << *metadata->schemaUri << std::endl;}loaded = true;}else{OSG_DEBUG << "Tileset has no metadata?" << std::endl;}}).catchInMainThread([](std::exception&&){OSG_WARN << "exception getting metadata" << std::endl;});while (!loaded){osgCs::Context::instance()->_asyncSystem.dispatchMainThreadTasks();std::this_thread::sleep_for(std::chrono::milliseconds(10));}
通过添加while循环,实现同步。
2 场景图更新
bool TilesetNode::initialize(const vsg::ref_ptr<vsg::Viewer>& viewer)
{updateViews(viewer);// Making a ref_ptr from this is gross. If the caller doesn't hold a ref, then this will be// deleted at the end of the function! We could do unref_nodelete, but UpdateTileset holds// observer_ptrs... Anyway, keeping this "alive" for the whole function avoids a compiler /// clang-tidy error.vsg::ref_ptr<TilesetNode> ref(this);viewer->addUpdateOperation(UpdateTileset::create(ref, viewer), vsg::UpdateOperations::ALL_FRAMES);return true;
}
上述代码为通过给vsg视景器添加更新操作,实现场景图的更新。
更新回调UpdateTileset的具体实现如下:
void TilesetNode::UpdateTileset::run()
{vsg::ref_ptr<vsg::Viewer> ref_viewer = viewer;vsg::ref_ptr<TilesetNode> ref_tileset = tilesetNode;if (!ref_viewer || !ref_tileset || !ref_tileset->_tileset){return;}auto& tileset = *ref_tileset->_tileset;VSGCS_ZONESCOPEDN("update view");float deltaTime = 0.0f;vsg::ref_ptr<vsg::FrameStamp> currentFrameStamp(ref_viewer->getFrameStamp());if (ref_tileset->_lastFrameStamp){std::chrono::duration<float> diff = currentFrameStamp->time - ref_tileset->_lastFrameStamp->time;deltaTime = diff.count();}std::vector<Cesium3DTilesSelection::ViewState> viewStates;for_each_view(viewer,[&viewStates](const vsg::ref_ptr<vsg::View>& view, const vsg::ref_ptr<vsg::RenderGraph>& rg){if (auto viewState = createViewState(view, rg)){viewStates.push_back(viewState.value());}});ref_tileset->_viewUpdateResult = &tileset.updateViewGroup(tileset.getDefaultViewGroup(), viewStates, deltaTime);for (const auto& tile : ref_tileset->_viewUpdateResult->tilesToRenderThisFrame){fadeTile(tile, false);}for (const auto& tile : ref_tileset->_viewUpdateResult->tilesFadingOut){fadeTile(tile, true);}tileset.loadTiles();ref_tileset->_lastFrameStamp = currentFrameStamp;
}
将vsg的视口信息传递给Cesium3DTilesSelection::Tileset,并调用器updateViewGroup方法,更新CesiumNative的场景图,并将CesiumNative的场景图当前要绘制的节点在vsg中进行绘制,当然CesiumNative的场景图准备好的数据需要转换为vsg节点数据,这个过程在vsgResourcePreparer中完成,下章将会详细分析这个转换过程。
文末:本章重点介绍了vsgCs中的关键节点vsgCs::WorldNode与vsgCs::TilesetNode,并深入分析了vsgCs::TilesetNode与CesiumNative的集成机制,以及vsg场景图的更新流程。具体而言,该流程通过将vsg视口信息传递至Cesium3DTilesSelection::Tileset,实现对CesiumNative场景图的更新,并确定当前帧中需要在vsg中绘制的节点。下一章将进一步探讨CesiumNative数据转换为vsg节点的具体实现过程。