Browse Source

Exporting to MDL format

Josh Engebretson 10 years ago
parent
commit
cc6a28fbf1

+ 275 - 0
Source/ToolCore/Import/OpenAssetImporter.cpp

@@ -32,6 +32,10 @@
 #include <Atomic/Atomic3D/AnimatedModel.h>
 #include <Atomic/Atomic3D/AnimatedModel.h>
 #include <Atomic/Atomic3D/Animation.h>
 #include <Atomic/Atomic3D/Animation.h>
 
 
+#include <Atomic/Graphics/Geometry.h>
+#include <Atomic/Graphics/IndexBuffer.h>
+#include <Atomic/Graphics/VertexBuffer.h>
+
 #include "OpenAssetImporter.h"
 #include "OpenAssetImporter.h"
 
 
 namespace ToolCore
 namespace ToolCore
@@ -58,6 +62,7 @@ OpenAssetImporter::OpenAssetImporter(Context* context) : Object(context) ,
     noOverwriteTexture_(false),
     noOverwriteTexture_(false),
     noOverwriteNewerTexture_(false),
     noOverwriteNewerTexture_(false),
     checkUniqueModel_(true),
     checkUniqueModel_(true),
+    maxBones_(64),
     defaultTicksPerSecond_(4800.0f)
     defaultTicksPerSecond_(4800.0f)
 {
 {
 
 
@@ -94,10 +99,280 @@ bool OpenAssetImporter::Load(const String &assetPath)
     if (verboseLog_)
     if (verboseLog_)
         Assimp::DefaultLogger::kill();
         Assimp::DefaultLogger::kill();
 
 
+    rootNode_ = scene_->mRootNode;
+
+    DumpNodes(rootNode_, 0);
+
     return true;
     return true;
 
 
 }
 }
 
 
+void OpenAssetImporter::ExportModel(const String& outName, bool animationOnly)
+{
+    if (outName.Empty())
+        ErrorExit("No output file defined");
+
+    OutModel model;
+    model.rootNode_ = rootNode_;
+    model.outName_ = outName;
+
+    CollectMeshes(scene_, model, model.rootNode_);
+    CollectBones(model, animationOnly);
+    BuildBoneCollisionInfo(model);
+    BuildAndSaveModel(model);
+    if (!noAnimations_)
+    {
+        CollectAnimations(&model);
+        BuildAndSaveAnimations(&model);
+
+        // Save scene-global animations
+        CollectAnimations();
+        BuildAndSaveAnimations();
+    }
+}
+void OpenAssetImporter::BuildAndSaveModel(OutModel& model)
+{
+    if (!model.rootNode_)
+        ErrorExit("Null root node for model");
+    String rootNodeName = FromAIString(model.rootNode_->mName);
+    if (!model.meshes_.Size())
+        ErrorExit("No geometries found starting from node " + rootNodeName);
+
+    PrintLine("Writing model " + rootNodeName);
+
+    SharedPtr<Model> outModel(new Model(context_));
+    Vector<PODVector<unsigned> > allBoneMappings;
+    BoundingBox box;
+
+    unsigned numValidGeometries = 0;
+
+    bool combineBuffers = true;
+    // Check if buffers can be combined (same vertex element mask, under 65535 vertices)
+    unsigned elementMask = GetElementMask(model.meshes_[0]);
+    for (unsigned i = 0; i < model.meshes_.Size(); ++i)
+    {
+        if (GetNumValidFaces(model.meshes_[i]))
+        {
+            ++numValidGeometries;
+            if (i > 0 && GetElementMask(model.meshes_[i]) != elementMask)
+                combineBuffers = false;
+        }
+    }
+    // Check if keeping separate buffers allows to avoid 32-bit indices
+    if (combineBuffers && model.totalVertices_ > 65535)
+    {
+        bool allUnder65k = true;
+        for (unsigned i = 0; i < model.meshes_.Size(); ++i)
+        {
+            if (GetNumValidFaces(model.meshes_[i]))
+            {
+                if (model.meshes_[i]->mNumVertices > 65535)
+                    allUnder65k = false;
+            }
+        }
+        if (allUnder65k == true)
+            combineBuffers = false;
+    }
+
+    SharedPtr<IndexBuffer> ib;
+    SharedPtr<VertexBuffer> vb;
+    Vector<SharedPtr<VertexBuffer> > vbVector;
+    Vector<SharedPtr<IndexBuffer> > ibVector;
+    unsigned startVertexOffset = 0;
+    unsigned startIndexOffset = 0;
+    unsigned destGeomIndex = 0;
+
+    outModel->SetNumGeometries(numValidGeometries);
+
+    for (unsigned i = 0; i < model.meshes_.Size(); ++i)
+    {
+        aiMesh* mesh = model.meshes_[i];
+        unsigned elementMask = GetElementMask(mesh);
+        unsigned validFaces = GetNumValidFaces(mesh);
+        if (!validFaces)
+            continue;
+
+        bool largeIndices;
+        if (combineBuffers)
+            largeIndices = model.totalIndices_ > 65535;
+        else
+            largeIndices = mesh->mNumVertices > 65535;
+
+        // Create new buffers if necessary
+        if (!combineBuffers || vbVector.Empty())
+        {
+            vb = new VertexBuffer(context_);
+            ib = new IndexBuffer(context_);
+
+            if (combineBuffers)
+            {
+                ib->SetSize(model.totalIndices_, largeIndices);
+                vb->SetSize(model.totalVertices_, elementMask);
+            }
+            else
+            {
+                ib->SetSize(validFaces * 3, largeIndices);
+                vb->SetSize(mesh->mNumVertices, elementMask);
+            }
+
+            vbVector.Push(vb);
+            ibVector.Push(ib);
+            startVertexOffset = 0;
+            startIndexOffset = 0;
+        }
+
+        // Get the world transform of the mesh for baking into the vertices
+        Matrix3x4 vertexTransform;
+        Matrix3 normalTransform;
+        Vector3 pos, scale;
+        Quaternion rot;
+        GetPosRotScale(GetMeshBakingTransform(model.meshNodes_[i], model.rootNode_), pos, rot, scale);
+        vertexTransform = Matrix3x4(pos, rot, scale);
+        normalTransform = rot.RotationMatrix();
+
+        SharedPtr<Geometry> geom(new Geometry(context_));
+
+        PrintLine("Writing geometry " + String(i) + " with " + String(mesh->mNumVertices) + " vertices " +
+            String(validFaces * 3) + " indices");
+
+        unsigned char* vertexData = vb->GetShadowData();
+        unsigned char* indexData = ib->GetShadowData();
+
+        // Build the index data
+        if (!largeIndices)
+        {
+            unsigned short* dest = (unsigned short*)indexData + startIndexOffset;
+            for (unsigned j = 0; j < mesh->mNumFaces; ++j)
+                WriteShortIndices(dest, mesh, j, startVertexOffset);
+        }
+        else
+        {
+            unsigned* dest = (unsigned*)indexData + startIndexOffset;
+            for (unsigned j = 0; j < mesh->mNumFaces; ++j)
+                WriteLargeIndices(dest, mesh, j, startVertexOffset);
+        }
+
+        // Build the vertex data
+        // If there are bones, get blend data
+        Vector<PODVector<unsigned char> > blendIndices;
+        Vector<PODVector<float> > blendWeights;
+        PODVector<unsigned> boneMappings;
+        if (model.bones_.Size())
+            GetBlendData(model, mesh, boneMappings, blendIndices, blendWeights, maxBones_);
+
+        float* dest = (float*)((unsigned char*)vertexData + startVertexOffset * vb->GetVertexSize());
+        for (unsigned j = 0; j < mesh->mNumVertices; ++j)
+            WriteVertex(dest, mesh, j, elementMask, box, vertexTransform, normalTransform, blendIndices, blendWeights);
+
+        // Calculate the geometry center
+        Vector3 center = Vector3::ZERO;
+        if (validFaces)
+        {
+            for (unsigned j = 0; j < mesh->mNumFaces; ++j)
+            {
+                if (mesh->mFaces[j].mNumIndices == 3)
+                {
+                    center += vertexTransform * ToVector3(mesh->mVertices[mesh->mFaces[j].mIndices[0]]);
+                    center += vertexTransform * ToVector3(mesh->mVertices[mesh->mFaces[j].mIndices[1]]);
+                    center += vertexTransform * ToVector3(mesh->mVertices[mesh->mFaces[j].mIndices[2]]);
+                }
+            }
+
+            center /= (float)validFaces * 3;
+        }
+
+        // Define the geometry
+        geom->SetIndexBuffer(ib);
+        geom->SetVertexBuffer(0, vb);
+        geom->SetDrawRange(TRIANGLE_LIST, startIndexOffset, validFaces * 3, true);
+        outModel->SetNumGeometryLodLevels(destGeomIndex, 1);
+        outModel->SetGeometry(destGeomIndex, 0, geom);
+        outModel->SetGeometryCenter(destGeomIndex, center);
+        if (model.bones_.Size() > maxBones_)
+            allBoneMappings.Push(boneMappings);
+
+        startVertexOffset += mesh->mNumVertices;
+        startIndexOffset += validFaces * 3;
+        ++destGeomIndex;
+    }
+
+    // Define the model buffers and bounding box
+    PODVector<unsigned> emptyMorphRange;
+    outModel->SetVertexBuffers(vbVector, emptyMorphRange, emptyMorphRange);
+    outModel->SetIndexBuffers(ibVector);
+    outModel->SetBoundingBox(box);
+
+    // Build skeleton if necessary
+    if (model.bones_.Size() && model.rootBone_)
+    {
+        PrintLine("Writing skeleton with " + String(model.bones_.Size()) + " bones, rootbone " +
+            FromAIString(model.rootBone_->mName));
+
+        Skeleton skeleton;
+        Vector<Bone>& bones = skeleton.GetModifiableBones();
+
+        for (unsigned i = 0; i < model.bones_.Size(); ++i)
+        {
+            aiNode* boneNode = model.bones_[i];
+            String boneName(FromAIString(boneNode->mName));
+
+            Bone newBone;
+            newBone.name_ = boneName;
+
+            aiMatrix4x4 transform = boneNode->mTransformation;
+            // Make the root bone transform relative to the model's root node, if it is not already
+            if (boneNode == model.rootBone_)
+                transform = GetDerivedTransform(boneNode, model.rootNode_);
+
+            GetPosRotScale(transform, newBone.initialPosition_, newBone.initialRotation_, newBone.initialScale_);
+
+            // Get offset information if exists
+            newBone.offsetMatrix_ = GetOffsetMatrix(model, boneName);
+            newBone.radius_ = model.boneRadii_[i];
+            newBone.boundingBox_ = model.boneHitboxes_[i];
+            newBone.collisionMask_ = BONECOLLISION_SPHERE | BONECOLLISION_BOX;
+            newBone.parentIndex_ = i;
+            bones.Push(newBone);
+        }
+        // Set the bone hierarchy
+        for (unsigned i = 1; i < model.bones_.Size(); ++i)
+        {
+            String parentName = FromAIString(model.bones_[i]->mParent->mName);
+            for (unsigned j = 0; j < bones.Size(); ++j)
+            {
+                if (bones[j].name_ == parentName)
+                {
+                    bones[i].parentIndex_ = j;
+                    break;
+                }
+            }
+        }
+
+        outModel->SetSkeleton(skeleton);
+        if (model.bones_.Size() > maxBones_)
+            outModel->SetGeometryBoneMappings(allBoneMappings);
+    }
+
+    File outFile(context_);
+    if (!outFile.Open(model.outName_, FILE_WRITE))
+        ErrorExit("Could not open output file " + model.outName_);
+    outModel->Save(outFile);
+
+    // If exporting materials, also save material list for use by the editor
+    if (!noMaterials_ && saveMaterialList_)
+    {
+        String materialListName = ReplaceExtension(model.outName_, ".txt");
+        File listFile(context_);
+        if (listFile.Open(materialListName, FILE_WRITE))
+        {
+            for (unsigned i = 0; i < model.meshes_.Size(); ++i)
+                listFile.WriteLine(GetMeshMaterialName(model.meshes_[i]));
+        }
+        else
+            PrintLine("Warning: could not write material list file " + materialListName);
+    }
+}
+
 String OpenAssetImporter::GetMeshMaterialName(aiMesh* mesh)
 String OpenAssetImporter::GetMeshMaterialName(aiMesh* mesh)
 {
 {
     aiMaterial* material = scene_->mMaterials[mesh->mMaterialIndex];
     aiMaterial* material = scene_->mMaterials[mesh->mMaterialIndex];

+ 9 - 2
Source/ToolCore/Import/OpenAssetImporter.h

@@ -46,23 +46,29 @@ public:
 
 
     bool Load(const String& assetPath);
     bool Load(const String& assetPath);
 
 
+    void ExportModel(const String& outName, bool animationOnly = false);
+
     void SetVerboseLog(bool verboseLog) { verboseLog_ = verboseLog; }
     void SetVerboseLog(bool verboseLog) { verboseLog_ = verboseLog; }
 
 
 private:
 private:
 
 
-    void BuildAndSaveAnimations(OutModel* model);
+    void BuildAndSaveModel(OutModel& model);
+    void BuildAndSaveAnimations(OutModel* model = 0);
 
 
     void CollectSceneModels(OutScene& scene, aiNode* node);
     void CollectSceneModels(OutScene& scene, aiNode* node);
     void CollectBones(OutModel& model, bool animationOnly = false);
     void CollectBones(OutModel& model, bool animationOnly = false);
     void CollectBonesFinal(PODVector<aiNode*>& dest, const HashSet<aiNode*>& necessary, aiNode* node);
     void CollectBonesFinal(PODVector<aiNode*>& dest, const HashSet<aiNode*>& necessary, aiNode* node);
     void BuildBoneCollisionInfo(OutModel& model);
     void BuildBoneCollisionInfo(OutModel& model);
-    void CollectAnimations(OutModel* model);
+    void CollectAnimations(OutModel* model = 0);
 
 
     String GetMeshMaterialName(aiMesh* mesh);
     String GetMeshMaterialName(aiMesh* mesh);
     String GenerateMaterialName(aiMaterial* material);
     String GenerateMaterialName(aiMaterial* material);
     String GetMaterialTextureName(const String& nameIn);
     String GetMaterialTextureName(const String& nameIn);
     String GenerateTextureName(unsigned texIndex);
     String GenerateTextureName(unsigned texIndex);
 
 
+    // TODO: See AssetImporter
+    // void CombineLods(const PODVector<float>& lodDistances, const Vector<String>& modelNames, const String& outName)
+
     void DumpNodes(aiNode* rootNode, unsigned level);
     void DumpNodes(aiNode* rootNode, unsigned level);
 
 
     const aiScene* scene_;
     const aiScene* scene_;
@@ -89,6 +95,7 @@ private:
     bool noOverwriteTexture_;
     bool noOverwriteTexture_;
     bool noOverwriteNewerTexture_;
     bool noOverwriteNewerTexture_;
     bool checkUniqueModel_;
     bool checkUniqueModel_;
+    unsigned maxBones_;
 
 
     unsigned aiFlagsDefault_;
     unsigned aiFlagsDefault_;
     unsigned aiCurrentFlags_;
     unsigned aiCurrentFlags_;

+ 2 - 0
Source/ToolCore/Import/OpenAssetUtils.h

@@ -108,6 +108,8 @@ unsigned GetNumValidFaces(aiMesh* mesh);
 void GetBlendData(OutModel& model, aiMesh* mesh, PODVector<unsigned>& boneMappings, Vector<PODVector<unsigned char> >&
 void GetBlendData(OutModel& model, aiMesh* mesh, PODVector<unsigned>& boneMappings, Vector<PODVector<unsigned char> >&
     blendIndices, Vector<PODVector<float> >& blendWeights, unsigned maxBones = 64);
     blendIndices, Vector<PODVector<float> >& blendWeights, unsigned maxBones = 64);
 
 
+void CollectMeshes(const aiScene* scene, OutModel& model, aiNode* node);
+
 void DumpNodes(aiNode* rootNode, unsigned level);
 void DumpNodes(aiNode* rootNode, unsigned level);
 
 
 }
 }