diff --git a/code/nel/tools/3d/mesh_utils/assimp_shape.cpp b/code/nel/tools/3d/mesh_utils/assimp_shape.cpp index 5c43c3b38..cef0b5605 100644 --- a/code/nel/tools/3d/mesh_utils/assimp_shape.cpp +++ b/code/nel/tools/3d/mesh_utils/assimp_shape.cpp @@ -30,12 +30,207 @@ #include #include +#include + using namespace std; using namespace NLMISC; +using namespace NL3D; -void assimpShape(CMeshUtilsContext &context, CNodeContext &nodeContext) +// TODO: buildParticleSystem ?? +// TODO: buildWaveMakerShape ?? +// TODO: buildRemanence ?? +// TODO: buildFlare ?? +// Probably specific settings we can only do in meta editor on a dummy node.. + +// TODO: buildWaterShape specifics when node has water material + +// TODO: CMeshMultiLod::CMeshMultiLodBuild multiLodBuild; export_mesh.cpp ln 228 +// TODO: LOD MRM + +// TODO: Skinned - reverse transform by skeleton root bone to align? + +void assimpBuildBaseMesh(CMeshBase::CMeshBaseBuild &buildBaseMesh, CNodeContext &nodeContext) { + const aiNode *node = nodeContext.InternalNode; + // TODO: Reference CExportNel::buildBaseMeshInterface +} +inline CVector convVector(const aiVector3D &av) +{ + return CVector(-av.x, av.z, av.y); // COORDINATE CONVERSION +} + +inline CRGBA convColor(const aiColor4D &ac) +{ + return CRGBA(ac.r * 255.99f, ac.g * 255.99f, ac.b * 255.99f, ac.a * 255.99f); +} + +bool assimpBuildMesh(CMesh::CMeshBuild &buildMesh, CMeshBase::CMeshBaseBuild &buildBaseMesh, CMeshUtilsContext &context, CNodeContext &nodeContext) +{ + // TODO + // *** If the mesh is skined, vertices will be exported in world space. + // *** If the mesh is not skined, vertices will be exported in offset space. + + // TODO Support skinning + + const aiNode *node = nodeContext.InternalNode; + + // Basic validations before processing starts + for (unsigned int mi = 0; mi < node->mNumMeshes; ++mi) + { + // TODO: Maybe needs to be the same count too for all meshes, so compare with mesh 0 + const aiMesh *mesh = context.InternalScene->mMeshes[node->mMeshes[mi]]; + if (mesh->GetNumColorChannels() > 2) + { + tlerror(context.ToolLogger, context.Settings.SourceFilePath.c_str(), + "(%s) mesh->GetNumColorChannels() > 2", node->mName.C_Str()); + return false; + } + if (mesh->GetNumUVChannels() > 1) + { + tlerror(context.ToolLogger, context.Settings.SourceFilePath.c_str(), + "(%s) mesh->GetNumUVChannels() > 1", node->mName.C_Str()); + return false; + } + if (!mesh->HasNormals()) + { + tlerror(context.ToolLogger, context.Settings.SourceFilePath.c_str(), + "(%s) !mesh->HasNormals()", node->mName.C_Str()); + return false; + } + } + + // TODO: UV Channels + for (uint i = 0; i < CVertexBuffer::MaxStage; ++i) + buildMesh.UVRouting[i] = i; + + // Meshes in assimp are separated per material, so we need to re-merge them for the mesh build process + // This process also deduplicates vertices + sint32 numVertices = 0; + for (unsigned int mi = 0; mi < node->mNumMeshes; ++mi) + numVertices += context.InternalScene->mMeshes[node->mMeshes[mi]]->mNumVertices; + buildMesh.Vertices.resize(numVertices); + numVertices = 0; + map vertexIdentifiers; + vector > vertexRemapping; + vertexRemapping.resize(node->mNumMeshes); + for (unsigned int mi = 0; mi < node->mNumMeshes; ++mi) + { + const aiMesh *mesh = context.InternalScene->mMeshes[node->mMeshes[mi]]; + vertexRemapping[mi].resize(mesh->mNumVertices); + for (unsigned int vi = 0; vi < mesh->mNumVertices; ++vi) + { + CVector vec = convVector(mesh->mVertices[vi]); + map::iterator vecit = vertexIdentifiers.find(vec); + if (vecit == vertexIdentifiers.end()) + { + buildMesh.Vertices[numVertices] = vec; + vertexIdentifiers[vec] = numVertices; + vertexRemapping[mi][vi] = numVertices; + ++numVertices; + } + else + { + vertexRemapping[mi][vi] = vecit->second; + } + } + } + buildMesh.Vertices.resize(numVertices); + + // Process all faces + // WONT IMPLEMENT: Radial faces generation... is linked to smoothing group... + // leave radial normals generation to modeling tool for now... + sint32 numFaces = 0; + for (unsigned int mi = 0; mi < node->mNumMeshes; ++mi) + numFaces += context.InternalScene->mMeshes[node->mMeshes[mi]]->mNumFaces; + buildMesh.Faces.resize(numFaces); + numFaces = 0; + for (unsigned int mi = 0; mi < node->mNumMeshes; ++mi) + { + const aiMesh *mesh = context.InternalScene->mMeshes[node->mMeshes[mi]]; + unsigned int numColorChannels = mesh->GetNumColorChannels(); // TODO: Maybe needs to be same on all mesh parts + for (unsigned int fi = 0; fi < mesh->mNumFaces; ++fi) + { + const aiFace &af = mesh->mFaces[fi]; + if (af.mNumIndices != 3) + { + tlerror(context.ToolLogger, context.Settings.SourceFilePath.c_str(), + "(%s) Face %i on mesh %i has %i faces", node->mName.C_Str(), fi, mi, af.mNumIndices); + continue; // return false; Keep going, just drop the face for better user experience + } + CMesh::CFace &face = buildMesh.Faces[numFaces]; + face.MaterialId = mi; + face.SmoothGroup = 0; // No smoothing group (bitfield) + face.Corner[0].Vertex = vertexRemapping[mi][af.mIndices[0]]; + face.Corner[1].Vertex = vertexRemapping[mi][af.mIndices[1]]; + face.Corner[2].Vertex = vertexRemapping[mi][af.mIndices[2]]; + face.Corner[0].Normal = convVector(mesh->mNormals[af.mIndices[0]]); + face.Corner[0].Normal = convVector(mesh->mNormals[af.mIndices[1]]); + face.Corner[0].Normal = convVector(mesh->mNormals[af.mIndices[2]]); + // TODO: UV + if (numColorChannels > 0) // TODO: Verify + { + face.Corner[0].Color = convColor(mesh->mColors[0][af.mIndices[0]]); + face.Corner[1].Color = convColor(mesh->mColors[0][af.mIndices[1]]); + face.Corner[2].Color = convColor(mesh->mColors[0][af.mIndices[2]]); + if (numColorChannels > 1) // TODO: Verify + { + face.Corner[0].Specular = convColor(mesh->mColors[0][af.mIndices[0]]); + face.Corner[1].Specular = convColor(mesh->mColors[0][af.mIndices[1]]); + face.Corner[2].Specular = convColor(mesh->mColors[0][af.mIndices[2]]); + } + } + // TODO: Color modulate, alpha, use color alpha for vp tree, etc + ++numFaces; + } + } + buildMesh.Faces.resize(numFaces); + + // clear for MRM info + buildMesh.Interfaces.clear(); + buildMesh.InterfaceLinks.clear(); + + // TODO: Export VP + buildMesh.MeshVertexProgram = NULL; + + return true; +} + +bool assimpShape(CMeshUtilsContext &context, CNodeContext &nodeContext) +{ + // Reference: export_mesh.cpp, buildShape + nodeContext.Shape = NULL; + + const aiNode *node = nodeContext.InternalNode; + nlassert(node->mNumMeshes); + + // Fill the build interface of CMesh + CMeshBase::CMeshBaseBuild buildBaseMesh; + assimpBuildBaseMesh(buildBaseMesh, nodeContext); + + CMesh::CMeshBuild buildMesh; + if (!assimpBuildMesh(buildMesh, buildBaseMesh, context, nodeContext)) + return false; + + // Make a CMesh object + CMesh *mesh = new CMesh(); + + // Build the mesh with the build interface + mesh->build(buildBaseMesh, buildMesh); + + // TODO + // Reference: export_mesh.cpp, buildShape + // Must be done after the build to update vertex links + // Pass to buildMeshMorph if the original mesh is skinned or not + // buildMeshMorph(buildMesh, node, time, nodeMap != NULL); + // mesh->setBlendShapes(buildMesh.BlendShapes); + + // optimize number of material + // mesh->optimizeMaterialUsage(materialRemap); + + // Store mesh in context + nodeContext.Shape = mesh; + return true; } /* end of file */ diff --git a/code/nel/tools/3d/mesh_utils/assimp_shape.h b/code/nel/tools/3d/mesh_utils/assimp_shape.h index 5f1c3c817..b4bdc2490 100644 --- a/code/nel/tools/3d/mesh_utils/assimp_shape.h +++ b/code/nel/tools/3d/mesh_utils/assimp_shape.h @@ -20,6 +20,6 @@ struct CMeshUtilsContext; struct CNodeContext; -void assimpShape(CMeshUtilsContext &context, CNodeContext &nodeContext); +bool assimpShape(CMeshUtilsContext &context, CNodeContext &nodeContext); /* end of file */ diff --git a/code/nel/tools/3d/mesh_utils/mesh_utils.cpp b/code/nel/tools/3d/mesh_utils/mesh_utils.cpp index 46ad1465e..f6313fc98 100644 --- a/code/nel/tools/3d/mesh_utils/mesh_utils.cpp +++ b/code/nel/tools/3d/mesh_utils/mesh_utils.cpp @@ -47,7 +47,7 @@ void importShapes(CMeshUtilsContext &context, const aiNode *node) { CNodeContext &nodeContext = context.Nodes[node->mName.C_Str()]; CNodeMeta &nodeMeta = context.SceneMeta.Nodes[node->mName.C_Str()]; - if (nodeMeta.ExportMesh == TMeshShape) + if (nodeMeta.ExportMesh == TMeshShape && nodeMeta.InstanceName.empty()) { if (node->mNumMeshes) { @@ -232,7 +232,11 @@ int exportScene(const CMeshUtilsSettings &settings) CDatabaseConfig::initTextureSearchDirectories(); Assimp::Importer importer; - const aiScene *scene = importer.ReadFile(settings.SourceFilePath, aiProcess_Triangulate | aiProcess_ValidateDataStructure); // aiProcess_SplitLargeMeshes | aiProcess_LimitBoneWeights + const aiScene *scene = importer.ReadFile(settings.SourceFilePath, 0 + | aiProcess_Triangulate + | aiProcess_ValidateDataStructure + | aiProcess_GenNormals // Or GenSmoothNormals? TODO: Validate smoothness between material boundaries! + ); // aiProcess_SplitLargeMeshes | aiProcess_LimitBoneWeights if (!scene) { const char *errs = importer.GetErrorString(); @@ -265,6 +269,9 @@ int exportScene(const CMeshUtilsSettings &settings) // ] // -- SKEL FLAG -- + // TODO + // First import materials... + importShapes(context, context.InternalScene->mRootNode); return EXIT_SUCCESS; diff --git a/code/nel/tools/3d/mesh_utils/scene_context.h b/code/nel/tools/3d/mesh_utils/scene_context.h index 0f23f9694..737800d6f 100644 --- a/code/nel/tools/3d/mesh_utils/scene_context.h +++ b/code/nel/tools/3d/mesh_utils/scene_context.h @@ -24,6 +24,8 @@ #include #include +#include +#include #ifndef NL_NODE_INTERNAL_TYPE #define NL_NODE_INTERNAL_TYPE void @@ -32,6 +34,10 @@ #define NL_SCENE_INTERNAL_TYPE void #endif +namespace NL3D { + class IShape; +} + struct CNodeContext { CNodeContext() : @@ -43,6 +49,9 @@ struct CNodeContext const NL_NODE_INTERNAL_TYPE *InternalNode; bool IsBone; + + // NLMISC::CMatrix Transform; // TODO + NLMISC::CRefPtr Shape; }; typedef std::map TNodeContextMap; diff --git a/code/nel/tools/3d/mesh_utils/scene_meta.cpp b/code/nel/tools/3d/mesh_utils/scene_meta.cpp index 4c3440819..a81505492 100644 --- a/code/nel/tools/3d/mesh_utils/scene_meta.cpp +++ b/code/nel/tools/3d/mesh_utils/scene_meta.cpp @@ -28,7 +28,8 @@ using namespace NLMISC; CNodeMeta::CNodeMeta() : AddToIG(true), ExportMesh(TMeshShape), - ExportBone(TBoneAuto) + ExportBone(TBoneAuto), + AutoAnim(false) { } @@ -42,6 +43,7 @@ void CNodeMeta::serial(NLMISC::IStream &s) s.serial(InstanceShape); s.serial(InstanceName); s.serial(InstanceGroupName); + s.serial(AutoAnim); } CSceneMeta::CSceneMeta() : diff --git a/code/nel/tools/3d/mesh_utils/scene_meta.h b/code/nel/tools/3d/mesh_utils/scene_meta.h index 98d8621f6..972132fbc 100644 --- a/code/nel/tools/3d/mesh_utils/scene_meta.h +++ b/code/nel/tools/3d/mesh_utils/scene_meta.h @@ -32,6 +32,8 @@ enum TMesh TMeshCollisionInt = 2, TMeshCollisionExt = 3, TMeshZone = 4, + TMeshPortal = 5, + TMeshCluster = 6, }; enum TBone @@ -47,12 +49,14 @@ struct CNodeMeta bool AddToIG; // Add this node to an instance group TMesh ExportMesh; - TBone ExportBone; + TBone ExportBone; std::string InstanceShape; std::string InstanceName; std::string InstanceGroupName; + bool AutoAnim; + void serial(NLMISC::IStream &s); };