赞
踩
最近参与一个智慧城市类的项目的开发,城市的数据通过专业设备扫描为osgb格式的数据,我负责将osgb数据加载并在ue里显示,此系列文章用于记录开发历程。
下载链接:https://freesouth.blog.csdn.net/article/details/121093781
在网上找到了别人编译好的第三方库,而且这个网盘里还有比较基础的接入教程,介绍的比较详细了(注意按照视频里面的步骤配置环境变量),接下来我只介绍关键步骤。
在项目根目录下创建ThirdParty文件夹,创建OSG文件夹,将编译好的第三方库的dll,lib和头文件全都复制进来。
接下来在Build里引入dll
string OSGBPath = Path.Combine(ModuleDirectory, "../../ThirdParty/OSG");
string IncDir = Path.Combine(OSGBPath, "include");
string LibDir = Path.Combine(OSGBPath, "lib");
PublicIncludePaths.Add(IncDir);
foreach (string file in Directory.GetFiles(LibDir))
{
if(!file.Contains("pkgconfig"))
{
PublicAdditionalLibraries.Add(Path.Combine(LibDir, file));
}
}
之后将编译好的bin文件夹下面的文件全部复制到工程的Binary中去,这步必做,不复制虽然会过编译但是打开引擎加载到75%会跳出警告并闪退,我有尝试在build文件里去包含dll,但是不得行,会闪退(目前还不知原因);
ok,到这一步,敲两行代码验证下是否接入成功:
osg::ref_ptr<osg::Node> node = osgDB::readNodeFile("D:\\OSGSetting\\OpenSceneGraph-Data\\glider.OSG");//路径为绝对地址
如果你发现地址没给错,命令行读取osg文件没问题,但是node一直返回为空指针,那么你需要把项目的Binary的地址添加到Path变量里去。
参考文章:https://blog.csdn.net/qq_31709249/article/details/94357183
首先我们定义了这两个类;在OSGeom储存了一个drawable的顶点,法线,UV坐标和三角形索引。OSGNodeVisiter是继承于osg::NodeVisiter的,用于储存所有drawble数据。
class OSGGeom { public: TArray<FVector> vertexArray; TArray<FVector> normalArray; TArray<FVector2D> textCoordArray; TArray<int> triangleArray; public: OSGGeom() { vertexArray = TArray<FVector>(); normalArray = TArray<FVector>(); textCoordArray = TArray<FVector2D>(); triangleArray = TArray<int>(); } }; class OSGNodeVisiter :public osg::NodeVisitor { public: TArray<OSGGeom*> NodeGeoms; public: virtual void apply(osg::Geode& node) override; OSGNodeVisiter() { NodeGeoms = TArray<OSGGeom*>(); } };
定义继承于AttributeVisiter的OSGAttributeVisiter 类,用于记录drawble的顶点,法线和UV坐标
定义模板类TriangleIndex,用于访问三角形索引。
class OSGAttributeVisiter :public osg::Drawable::AttributeFunctor { public: TArray<FVector> vertexArray; TArray<FVector> normalArray; TArray<FVector2D> textCoordArray; virtual void apply(osg::Drawable::AttributeType, unsigned, osg::Vec2*) override; virtual void apply(osg::Drawable::AttributeType, unsigned, osg::Vec3*) override; }; class TriangleIndex { public: //osg::ref_ptr<osg::UIntArray> indexs; TArray<int> triangleIndexes; int triangleNum; TriangleIndex(){}; ~TriangleIndex(){}; void operator()(const unsigned int& v1, const unsigned int& v2, const unsigned int& v3); };
OSGNodevisiter是继承于Nodevisiter的,当调用node->accept(visitor)时,最终会调用visitor的apply
osg::ref_ptr<osg::Node> node = osgDB::readNodeFile("D:\\OSGSetting\\OpenSceneGraph-Data\\glider.OSG");
OSGNodeVisiter aNodeVisiter = OSGNodeVisiter();
node->accept(aNodeVisiter);
在OSGNodevisiter的apply函数里,我们实例化一个AttributeFUnctor和osg::TriangleIndexFunctor ,因为OSG顶点数据的范围与UE不同,不符合我们的需求,所以我们暂且先把顶点坐标都乘1000.
void OSGNodeVisiter:: apply(osg::Geode& node) { const float multiNum = 1000; for (size_t i = 0; i < node.getNumDrawables(); i++) { osg::ref_ptr<osg::Drawable> drawable = node.getDrawable(i); OSGAttributeVisiter functor; drawable->accept(functor); osg::TriangleIndexFunctor<TriangleIndex> triangleIndex; drawable->accept(triangleIndex); OSGGeom*newGeom = new OSGGeom; for (FVector vValue : functor.vertexArray) { newGeom->vertexArray.Add(vValue* multiNum); } for (FVector nValue : functor.normalArray) { newGeom->normalArray.Add(nValue); } for (FVector2D texValue : functor.textCoordArray) { newGeom->textCoordArray.Add(texValue); } for (int triValue : triangleIndex.triangleIndexes) { newGeom->triangleArray.Add(triValue); } NodeGeoms.Add(newGeomptr); } }
父类AttributeFunctor中的apply是多态的,如果数据类型是一样的,那么可以通过type来分别获取的是什么数据
所以我们在我们定义的OSGNodevisiter根据我们的需求来重写apply:
void OSGAttributeVisiter::apply(osg::Drawable::AttributeType type, unsigned size, osg::Vec2* front) { UE_LOG(LogTemp, Warning, TEXT("OSGAttributeVisiter::applyVec2 %d"), type); if (type == osg::Drawable::TEXTURE_COORDS_0) { for (unsigned i = 0; i < size; i++) { osg::Vec2 vec = *(front + i); double X = vec._v[0]; double Y = vec._v[1]; textCoordArray.Add(FVector2D(X, Y)); } } } void OSGAttributeVisiter::apply(osg::Drawable::AttributeType type, unsigned size, osg::Vec3* front) { if (type == osg::Drawable::VERTICES) { for (unsigned i = 0; i < size; i++) { osg::Vec3 vec = *(front + i); double X = vec._v[0]; double Y = vec._v[1]; double Z = vec._v[2]; vertexArray.Add(FVector(X, Y, Z)); // vertexList->push_back(*(front + i)); } } else if (type == osg::Drawable::NORMALS) { for (unsigned i = 0; i < size; i++) { osg::Vec3 vec = *(front + i); double X = vec._v[0]; double Y = vec._v[1]; double Z = vec._v[2]; normalArray.Add(FVector(X, Y, Z)); } } }
至此我们就把这个osg模型的基本数据读出来啦
首先,因为这个项目里以后会读取多个osg模型,所以我定义了一个OSGGridHolderc++类,用ProceduralMeshComponent负责绘制网格体。
法线数据虽然我们已经得到了,但是这里我选择用UKismetProceduralMeshLibrary::CalculateTangentsForMesh去计算法线和切线,因为这个Glider是没有纹理坐标的,所以我这里UV坐标就随便赋值使程序正常运行即可。
主要代码如下:
void AOSGBGridHolder::DrawOSGBObject(const TArray<OSGGeom*>& Geoms) { for (int i = 0; i < Geoms.Num(); i++) { OSGGeom* Geom = Geoms[i]; if (Geom) { TArray<FVector> Verteces = Geom->vertexArray; TArray<FVector> Normals = Geom->normalArray; TArray<FVector2D> UV; if (!Geom->textCoordArray.IsEmpty()) { UV = Geom->textCoordArray; } else { for (int j = 0; j < Verteces.Num(); j++) { UV.Add(FVector2D(1, 1)); } } TArray<int> Triangles = Geom->triangleArray; FString compName = FString("bp_RoadMeshComp") + FString::FromInt(i); UProceduralMeshComponent* newPComp = NewObject<UProceduralMeshComponent>(this, *compName); newPComp->AttachToComponent(RootComponent, FAttachmentTransformRules::KeepWorldTransform); newPComp->CreationMethod = EComponentCreationMethod::Instance; newPComp->RegisterComponent(); TArray<FProcMeshTangent> tangents; TArray<FVector> normals; TArray<FColor> vertexColors; UKismetProceduralMeshLibrary::CalculateTangentsForMesh(Verteces, Triangles, UV, normals, tangents); newPComp->CreateMeshSection(0, Verteces, Triangles, normals, UV, TArray<FVector2D>(), TArray<FVector2D>(), TArray<FVector2D>(), vertexColors, tangents, false); PMeshArray.Add(newPComp); } } }
最终成功将模型读取到场景中来:
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。