Shape import
--HG-- branch : feature-export-assimp
This commit is contained in:
parent
e60a9747b8
commit
9e20f83e48
6 changed files with 223 additions and 6 deletions
|
@ -30,12 +30,207 @@
|
|||
#include <nel/misc/path.h>
|
||||
#include <nel/misc/tool_logger.h>
|
||||
|
||||
#include <nel/3d/mesh.h>
|
||||
|
||||
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<CVector, sint32> vertexIdentifiers;
|
||||
vector<vector<sint32> > 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<CVector, sint32>::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 */
|
||||
|
|
|
@ -20,6 +20,6 @@
|
|||
struct CMeshUtilsContext;
|
||||
struct CNodeContext;
|
||||
|
||||
void assimpShape(CMeshUtilsContext &context, CNodeContext &nodeContext);
|
||||
bool assimpShape(CMeshUtilsContext &context, CNodeContext &nodeContext);
|
||||
|
||||
/* end of file */
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -24,6 +24,8 @@
|
|||
|
||||
#include <nel/misc/sstring.h>
|
||||
#include <nel/misc/tool_logger.h>
|
||||
#include <nel/misc/smart_ptr.h>
|
||||
#include <nel/misc/matrix.h>
|
||||
|
||||
#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<NL3D::IShape> Shape;
|
||||
};
|
||||
|
||||
typedef std::map<NLMISC::CSString, CNodeContext> TNodeContextMap;
|
||||
|
|
|
@ -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() :
|
||||
|
|
|
@ -32,6 +32,8 @@ enum TMesh
|
|||
TMeshCollisionInt = 2,
|
||||
TMeshCollisionExt = 3,
|
||||
TMeshZone = 4,
|
||||
TMeshPortal = 5,
|
||||
TMeshCluster = 6,
|
||||
};
|
||||
|
||||
enum TBone
|
||||
|
@ -53,6 +55,8 @@ struct CNodeMeta
|
|||
std::string InstanceName;
|
||||
std::string InstanceGroupName;
|
||||
|
||||
bool AutoAnim;
|
||||
|
||||
void serial(NLMISC::IStream &s);
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in a new issue