summaryrefslogtreecommitdiff
path: root/libs/assimp/code/AssetLib/M3D/M3DImporter.cpp
diff options
context:
space:
mode:
authorsanine <sanine.not@pm.me>2022-04-16 11:55:09 -0500
committersanine <sanine.not@pm.me>2022-04-16 11:55:09 -0500
commitdb81b925d776103326128bf629cbdda576a223e7 (patch)
tree58bea8155c686733310009f6bed7363f91fbeb9d /libs/assimp/code/AssetLib/M3D/M3DImporter.cpp
parent55860037b14fb3893ba21cf2654c83d349cc1082 (diff)
move 3rd-party librarys into libs/ and add built-in honeysuckle
Diffstat (limited to 'libs/assimp/code/AssetLib/M3D/M3DImporter.cpp')
-rw-r--r--libs/assimp/code/AssetLib/M3D/M3DImporter.cpp789
1 files changed, 789 insertions, 0 deletions
diff --git a/libs/assimp/code/AssetLib/M3D/M3DImporter.cpp b/libs/assimp/code/AssetLib/M3D/M3DImporter.cpp
new file mode 100644
index 0000000..895b2bf
--- /dev/null
+++ b/libs/assimp/code/AssetLib/M3D/M3DImporter.cpp
@@ -0,0 +1,789 @@
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2022, assimp team
+Copyright (c) 2019 bzt
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+copyright notice, this list of conditions and the
+following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the
+following disclaimer in the documentation and/or other
+materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+contributors may be used to endorse or promote products
+derived from this software without specific prior
+written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+#ifndef ASSIMP_BUILD_NO_M3D_IMPORTER
+
+#define M3D_IMPLEMENTATION
+#define M3D_NONORMALS /* leave the post-processing to Assimp */
+#define M3D_NOWEIGHTS
+#define M3D_NOANIMATION
+
+#include <assimp/DefaultIOSystem.h>
+#include <assimp/IOStreamBuffer.h>
+#include <assimp/ai_assert.h>
+#include <assimp/importerdesc.h>
+#include <assimp/scene.h>
+#include <assimp/DefaultLogger.hpp>
+#include <assimp/Importer.hpp>
+#include <memory>
+
+#include "M3DImporter.h"
+#include "M3DMaterials.h"
+#include "M3DWrapper.h"
+
+// RESOURCES:
+// https://gitlab.com/bztsrc/model3d/blob/master/docs/m3d_format.md
+// https://gitlab.com/bztsrc/model3d/blob/master/docs/a3d_format.md
+
+/*
+ Unfortunately aiNode has bone structures and meshes too, yet we can't assign
+ the mesh to a bone aiNode as a skin may refer to several aiNodes. Therefore
+ I've decided to import into this structure:
+
+ aiScene->mRootNode
+ | |->mMeshes (all the meshes)
+ | \->children (empty if there's no skeleton imported, no meshes)
+ | \->skeleton root aiNode*
+ | |->bone aiNode
+ | | \->subbone aiNode
+ | |->bone aiNode
+ | | ...
+ | \->bone aiNode
+ \->mMeshes[]
+ \->aiBone, referencing mesh-less aiNodes from above
+
+ * - normally one, but if a model has several skeleton roots, then all of them
+ are listed in aiScene->mRootNode->children, but all without meshes
+*/
+
+static const aiImporterDesc desc = {
+ "Model 3D Importer",
+ "",
+ "",
+ "",
+ aiImporterFlags_SupportTextFlavour | aiImporterFlags_SupportBinaryFlavour,
+ 0,
+ 0,
+ 0,
+ 0,
+ "m3d a3d"
+};
+
+namespace Assimp {
+
+using namespace std;
+
+// ------------------------------------------------------------------------------------------------
+// Default constructor
+M3DImporter::M3DImporter() :
+ mScene(nullptr) {
+ // empty
+}
+
+// ------------------------------------------------------------------------------------------------
+// Returns true, if file is a binary or ASCII Model 3D file.
+bool M3DImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool /*checkSig*/) const {
+ // don't use CheckMagicToken because that checks with swapped bytes too, leading to false
+ // positives. This magic is not uint32_t, but char[4], so memcmp is the best way
+ std::unique_ptr<IOStream> pStream(pIOHandler->Open(pFile, "rb"));
+ unsigned char data[4];
+ if (4 != pStream->Read(data, 1, 4)) {
+ return false;
+ }
+ return !memcmp(data, "3DMO", 4) /* bin */
+#ifdef M3D_ASCII
+ || !memcmp(data, "3dmo", 4) /* ASCII */
+#endif
+ ;
+}
+
+// ------------------------------------------------------------------------------------------------
+const aiImporterDesc *M3DImporter::GetInfo() const {
+ return &desc;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Model 3D import implementation
+void M3DImporter::InternReadFile(const std::string &file, aiScene *pScene, IOSystem *pIOHandler) {
+ // Read file into memory
+ std::unique_ptr<IOStream> pStream(pIOHandler->Open(file, "rb"));
+ if (!pStream.get()) {
+ throw DeadlyImportError("Failed to open file ", file, ".");
+ }
+
+ // Get the file-size and validate it, throwing an exception when fails
+ size_t fileSize = pStream->FileSize();
+ if (fileSize < 8) {
+ throw DeadlyImportError("M3D-file ", file, " is too small.");
+ }
+ std::vector<unsigned char> buffer(fileSize);
+ if (fileSize != pStream->Read(buffer.data(), 1, fileSize)) {
+ throw DeadlyImportError("Failed to read the file ", file, ".");
+ }
+ // extra check for binary format's first 8 bytes. Not done for the ASCII variant
+ if (!memcmp(buffer.data(), "3DMO", 4) && memcmp(buffer.data() + 4, &fileSize, 4)) {
+ throw DeadlyImportError("Bad binary header in file ", file, ".");
+ }
+ // make sure there's a terminator zero character, as input must be ASCIIZ
+ if (!memcmp(buffer.data(), "3dmo", 4)) {
+ buffer.push_back(0);
+ }
+
+ // Get the path for external assets
+ std::string folderName("./");
+ std::string::size_type pos = file.find_last_of("\\/");
+ if (pos != std::string::npos) {
+ folderName = file.substr(0, pos);
+ if (!folderName.empty()) {
+ pIOHandler->PushDirectory(folderName);
+ }
+ }
+
+ //DefaultLogger::create("/dev/stderr", Logger::VERBOSE);
+ ASSIMP_LOG_DEBUG("M3D: loading ", file);
+
+ // let the C SDK do the hard work for us
+ M3DWrapper m3d(pIOHandler, buffer);
+
+ if (!m3d) {
+ throw DeadlyImportError("Unable to parse ", file, " as M3D.");
+ }
+
+ // create the root node
+ pScene->mRootNode = new aiNode;
+ pScene->mRootNode->mName = aiString(m3d.Name());
+ pScene->mRootNode->mTransformation = aiMatrix4x4();
+ pScene->mRootNode->mNumChildren = 0;
+ mScene = pScene;
+
+ ASSIMP_LOG_DEBUG("M3D: root node ", m3d.Name());
+
+ // now we just have to fill up the Assimp structures in pScene
+ importMaterials(m3d);
+ importTextures(m3d);
+ importBones(m3d, M3D_NOTDEFINED, pScene->mRootNode);
+ importMeshes(m3d);
+ importAnimations(m3d);
+
+ // Pop directory stack
+ if (pIOHandler->StackSize() > 0) {
+ pIOHandler->PopDirectory();
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// convert materials. properties are converted using a static table in M3DMaterials.h
+void M3DImporter::importMaterials(const M3DWrapper &m3d) {
+ unsigned int i, j, k, l, n;
+ m3dm_t *m;
+ aiString name = aiString(AI_DEFAULT_MATERIAL_NAME);
+ aiColor4D c;
+ ai_real f;
+
+ ai_assert(mScene != nullptr);
+ ai_assert(m3d);
+
+ mScene->mNumMaterials = m3d->nummaterial + 1;
+ mScene->mMaterials = new aiMaterial *[mScene->mNumMaterials];
+
+ ASSIMP_LOG_DEBUG("M3D: importMaterials ", mScene->mNumMaterials);
+
+ // add a default material as first
+ aiMaterial *defaultMat = new aiMaterial;
+ defaultMat->AddProperty(&name, AI_MATKEY_NAME);
+ c.a = 1.0f;
+ c.b = c.g = c.r = 0.6f;
+ defaultMat->AddProperty(&c, 1, AI_MATKEY_COLOR_DIFFUSE);
+ mScene->mMaterials[0] = defaultMat;
+
+ if (!m3d->nummaterial || !m3d->material) {
+ return;
+ }
+
+ for (i = 0; i < m3d->nummaterial; i++) {
+ m = &m3d->material[i];
+ aiMaterial *newMat = new aiMaterial;
+ name.Set(std::string(m->name));
+ newMat->AddProperty(&name, AI_MATKEY_NAME);
+ for (j = 0; j < m->numprop; j++) {
+ // look up property type
+ // 0 - 127 scalar values,
+ // 128 - 255 the same properties but for texture maps
+ k = 256;
+ for (l = 0; l < sizeof(m3d_propertytypes) / sizeof(m3d_propertytypes[0]); l++)
+ if (m->prop[j].type == m3d_propertytypes[l].id ||
+ m->prop[j].type == m3d_propertytypes[l].id + 128) {
+ k = l;
+ break;
+ }
+ // should never happen, but be safe than sorry
+ if (k == 256)
+ continue;
+
+ // scalar properties
+ if (m->prop[j].type < 128 && aiProps[k].pKey) {
+ switch (m3d_propertytypes[k].format) {
+ case m3dpf_color:
+ c = mkColor(m->prop[j].value.color);
+ newMat->AddProperty(&c, 1, aiProps[k].pKey, aiProps[k].type, aiProps[k].index);
+ break;
+ case m3dpf_float:
+ f = m->prop[j].value.fnum;
+ newMat->AddProperty(&f, 1, aiProps[k].pKey, aiProps[k].type, aiProps[k].index);
+ break;
+ default:
+ n = m->prop[j].value.num;
+ if (m->prop[j].type == m3dp_il) {
+ switch (n) {
+ case 0:
+ n = aiShadingMode_NoShading;
+ break;
+ case 2:
+ n = aiShadingMode_Phong;
+ break;
+ default:
+ n = aiShadingMode_Gouraud;
+ break;
+ }
+ }
+ newMat->AddProperty(&n, 1, aiProps[k].pKey, aiProps[k].type, aiProps[k].index);
+ break;
+ }
+ }
+ // texture map properties
+ if (m->prop[j].type >= 128 && aiTxProps[k].pKey &&
+ // extra check, should never happen, do we have the referred texture?
+ m->prop[j].value.textureid < m3d->numtexture &&
+ m3d->texture[m->prop[j].value.textureid].name) {
+ name.Set(std::string(std::string(m3d->texture[m->prop[j].value.textureid].name) + ".png"));
+ newMat->AddProperty(&name, aiTxProps[k].pKey, aiTxProps[k].type, aiTxProps[k].index);
+ n = 0;
+ newMat->AddProperty(&n, 1, _AI_MATKEY_UVWSRC_BASE, aiProps[k].type, aiProps[k].index);
+ }
+ }
+ mScene->mMaterials[i + 1] = newMat;
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// import textures, this is the simplest of all
+void M3DImporter::importTextures(const M3DWrapper &m3d) {
+ unsigned int i;
+ const char *formatHint[] = {
+ "rgba0800",
+ "rgba0808",
+ "rgba8880",
+ "rgba8888"
+ };
+ m3dtx_t *t;
+
+ ai_assert(mScene != nullptr);
+ ai_assert(m3d);
+
+ mScene->mNumTextures = m3d->numtexture;
+ ASSIMP_LOG_DEBUG("M3D: importTextures ", mScene->mNumTextures);
+
+ if (!m3d->numtexture || !m3d->texture) {
+ return;
+ }
+
+ mScene->mTextures = new aiTexture *[m3d->numtexture];
+ for (i = 0; i < m3d->numtexture; i++) {
+ unsigned int j, k;
+ t = &m3d->texture[i];
+ aiTexture *tx = new aiTexture;
+ tx->mFilename = aiString(std::string(t->name) + ".png");
+ if (!t->w || !t->h || !t->f || !t->d) {
+ /* without ASSIMP_USE_M3D_READFILECB, we only have the filename, but no texture data ever */
+ tx->mWidth = 0;
+ tx->mHeight = 0;
+ memcpy(tx->achFormatHint, "png\000", 4);
+ tx->pcData = nullptr;
+ } else {
+ /* if we have the texture loaded, set format hint and pcData too */
+ tx->mWidth = t->w;
+ tx->mHeight = t->h;
+ strcpy(tx->achFormatHint, formatHint[t->f - 1]);
+ tx->pcData = new aiTexel[tx->mWidth * tx->mHeight];
+ for (j = k = 0; j < tx->mWidth * tx->mHeight; j++) {
+ switch (t->f) {
+ case 1: tx->pcData[j].g = t->d[k++]; break;
+ case 2:
+ tx->pcData[j].g = t->d[k++];
+ tx->pcData[j].a = t->d[k++];
+ break;
+ case 3:
+ tx->pcData[j].r = t->d[k++];
+ tx->pcData[j].g = t->d[k++];
+ tx->pcData[j].b = t->d[k++];
+ tx->pcData[j].a = 255;
+ break;
+ case 4:
+ tx->pcData[j].r = t->d[k++];
+ tx->pcData[j].g = t->d[k++];
+ tx->pcData[j].b = t->d[k++];
+ tx->pcData[j].a = t->d[k++];
+ break;
+ }
+ }
+ }
+ mScene->mTextures[i] = tx;
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// this is tricky. M3D has a global vertex and UV list, and faces are indexing them
+// individually. In assimp there're per mesh vertex and UV lists, and they must be
+// indexed simultaneously.
+void M3DImporter::importMeshes(const M3DWrapper &m3d) {
+ ASSIMP_LOG_DEBUG("M3D: importMeshes ", m3d->numface);
+
+ if (!m3d->numface || !m3d->face || !m3d->numvertex || !m3d->vertex) {
+ return;
+ }
+
+ unsigned int i, j, k, l, numpoly = 3, lastMat = M3D_INDEXMAX;
+ std::vector<aiMesh *> *meshes = new std::vector<aiMesh *>();
+ std::vector<aiFace> *faces = nullptr;
+ std::vector<aiVector3D> *vertices = nullptr;
+ std::vector<aiVector3D> *normals = nullptr;
+ std::vector<aiVector3D> *texcoords = nullptr;
+ std::vector<aiColor4D> *colors = nullptr;
+ std::vector<unsigned int> *vertexids = nullptr;
+ aiMesh *pMesh = nullptr;
+
+ ai_assert(mScene != nullptr);
+ ai_assert(m3d);
+ ai_assert(mScene->mRootNode != nullptr);
+
+ for (i = 0; i < m3d->numface; i++) {
+ // we must switch mesh if material changes
+ if (lastMat != m3d->face[i].materialid) {
+ lastMat = m3d->face[i].materialid;
+ if (pMesh && vertices && vertices->size() && faces && faces->size()) {
+ populateMesh(m3d, pMesh, faces, vertices, normals, texcoords, colors, vertexids);
+ meshes->push_back(pMesh);
+ delete faces;
+ delete vertices;
+ delete normals;
+ delete texcoords;
+ delete colors;
+ delete vertexids; // this is not stored in pMesh, just to collect bone vertices
+ }
+ pMesh = new aiMesh;
+ pMesh->mPrimitiveTypes = aiPrimitiveType_TRIANGLE;
+ pMesh->mMaterialIndex = lastMat + 1;
+ faces = new std::vector<aiFace>();
+ vertices = new std::vector<aiVector3D>();
+ normals = new std::vector<aiVector3D>();
+ texcoords = new std::vector<aiVector3D>();
+ colors = new std::vector<aiColor4D>();
+ vertexids = new std::vector<unsigned int>();
+ }
+ // add a face to temporary vector
+ aiFace *pFace = new aiFace;
+ pFace->mNumIndices = numpoly;
+ pFace->mIndices = new unsigned int[numpoly];
+ for (j = 0; j < numpoly; j++) {
+ aiVector3D pos, uv, norm;
+ k = static_cast<unsigned int>(vertices->size());
+ pFace->mIndices[j] = k;
+ l = m3d->face[i].vertex[j];
+ if (l >= m3d->numvertex) continue;
+ pos.x = m3d->vertex[l].x;
+ pos.y = m3d->vertex[l].y;
+ pos.z = m3d->vertex[l].z;
+ vertices->push_back(pos);
+ colors->push_back(mkColor(m3d->vertex[l].color));
+ // add a bone to temporary vector
+ if (m3d->vertex[l].skinid != M3D_UNDEF && m3d->vertex[l].skinid != M3D_INDEXMAX && m3d->skin && m3d->bone) {
+ // this is complicated, because M3D stores a list of bone id / weight pairs per
+ // vertex but assimp uses lists of local vertex id/weight pairs per local bone list
+ vertexids->push_back(l);
+ }
+ l = m3d->face[i].texcoord[j];
+ if (l != M3D_UNDEF && l < m3d->numtmap) {
+ uv.x = m3d->tmap[l].u;
+ uv.y = m3d->tmap[l].v;
+ uv.z = 0.0;
+ texcoords->push_back(uv);
+ }
+ l = m3d->face[i].normal[j];
+ if (l != M3D_UNDEF && l < m3d->numvertex) {
+ norm.x = m3d->vertex[l].x;
+ norm.y = m3d->vertex[l].y;
+ norm.z = m3d->vertex[l].z;
+ normals->push_back(norm);
+ }
+ }
+ faces->push_back(*pFace);
+ delete pFace;
+ }
+ // if there's data left in the temporary vectors, flush them
+ if (pMesh && vertices->size() && faces->size()) {
+ populateMesh(m3d, pMesh, faces, vertices, normals, texcoords, colors, vertexids);
+ meshes->push_back(pMesh);
+ }
+
+ // create global mesh list in scene
+ mScene->mNumMeshes = static_cast<unsigned int>(meshes->size());
+ mScene->mMeshes = new aiMesh *[mScene->mNumMeshes];
+ std::copy(meshes->begin(), meshes->end(), mScene->mMeshes);
+
+ // create mesh indices in root node
+ mScene->mRootNode->mNumMeshes = static_cast<unsigned int>(meshes->size());
+ mScene->mRootNode->mMeshes = new unsigned int[meshes->size()];
+ for (i = 0; i < meshes->size(); i++) {
+ mScene->mRootNode->mMeshes[i] = i;
+ }
+
+ delete meshes;
+ if (faces) delete faces;
+ if (vertices) delete vertices;
+ if (normals) delete normals;
+ if (texcoords) delete texcoords;
+ if (colors) delete colors;
+ if (vertexids) delete vertexids;
+}
+
+// ------------------------------------------------------------------------------------------------
+// a reentrant node parser. Otherwise this is simple
+void M3DImporter::importBones(const M3DWrapper &m3d, unsigned int parentid, aiNode *pParent) {
+ unsigned int i, n;
+
+ ai_assert(pParent != nullptr);
+ ai_assert(mScene != nullptr);
+ ai_assert(m3d);
+
+ ASSIMP_LOG_DEBUG("M3D: importBones ", m3d->numbone, " parentid ", (int)parentid);
+
+ if (!m3d->numbone || !m3d->bone) {
+ return;
+ }
+
+ for (n = 0, i = parentid + 1; i < m3d->numbone; i++) {
+ if (m3d->bone[i].parent == parentid) {
+ n++;
+ }
+ }
+ pParent->mChildren = new aiNode *[n];
+
+ for (i = parentid + 1; i < m3d->numbone; i++) {
+ if (m3d->bone[i].parent == parentid) {
+ aiNode *pChild = new aiNode;
+ pChild->mParent = pParent;
+ pChild->mName = aiString(std::string(m3d->bone[i].name));
+ convertPose(m3d, &pChild->mTransformation, m3d->bone[i].pos, m3d->bone[i].ori);
+ pChild->mNumChildren = 0;
+ pParent->mChildren[pParent->mNumChildren] = pChild;
+ pParent->mNumChildren++;
+ importBones(m3d, i, pChild);
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// this is another headache. M3D stores list of changed bone id/position/orientation triplets and
+// a timestamp per frame, but assimp needs timestamp and lists of position, orientation lists per
+// bone, so we have to convert between the two conceptually different representation forms
+void M3DImporter::importAnimations(const M3DWrapper &m3d) {
+ unsigned int i, j, k, l, pos, ori;
+ double t;
+ m3da_t *a;
+
+ ai_assert(mScene != nullptr);
+ ai_assert(m3d);
+
+ mScene->mNumAnimations = m3d->numaction;
+
+ ASSIMP_LOG_DEBUG("M3D: importAnimations ", mScene->mNumAnimations);
+
+ if (!m3d->numaction || !m3d->action || !m3d->numbone || !m3d->bone || !m3d->vertex) {
+ return;
+ }
+
+ mScene->mAnimations = new aiAnimation *[m3d->numaction];
+ for (i = 0; i < m3d->numaction; i++) {
+ a = &m3d->action[i];
+ aiAnimation *pAnim = new aiAnimation;
+ pAnim->mName = aiString(std::string(a->name));
+ pAnim->mDuration = ((double)a->durationmsec) / 10;
+ pAnim->mTicksPerSecond = 100;
+ // now we know how many bones are referenced in this animation
+ pAnim->mNumChannels = m3d->numbone;
+ pAnim->mChannels = new aiNodeAnim *[pAnim->mNumChannels];
+ for (l = 0; l < m3d->numbone; l++) {
+ unsigned int n;
+ pAnim->mChannels[l] = new aiNodeAnim;
+ pAnim->mChannels[l]->mNodeName = aiString(std::string(m3d->bone[l].name));
+ // now n is the size of positions / orientations arrays
+ pAnim->mChannels[l]->mNumPositionKeys = pAnim->mChannels[l]->mNumRotationKeys = a->numframe;
+ pAnim->mChannels[l]->mPositionKeys = new aiVectorKey[a->numframe];
+ pAnim->mChannels[l]->mRotationKeys = new aiQuatKey[a->numframe];
+ pos = m3d->bone[l].pos;
+ ori = m3d->bone[l].ori;
+ for (j = n = 0; j < a->numframe; j++) {
+ t = ((double)a->frame[j].msec) / 10;
+ for (k = 0; k < a->frame[j].numtransform; k++) {
+ if (a->frame[j].transform[k].boneid == l) {
+ pos = a->frame[j].transform[k].pos;
+ ori = a->frame[j].transform[k].ori;
+ }
+ }
+ if (pos >= m3d->numvertex || ori >= m3d->numvertex) continue;
+ m3dv_t *v = &m3d->vertex[pos];
+ m3dv_t *q = &m3d->vertex[ori];
+ pAnim->mChannels[l]->mPositionKeys[j].mTime = t;
+ pAnim->mChannels[l]->mPositionKeys[j].mValue.x = v->x;
+ pAnim->mChannels[l]->mPositionKeys[j].mValue.y = v->y;
+ pAnim->mChannels[l]->mPositionKeys[j].mValue.z = v->z;
+ pAnim->mChannels[l]->mRotationKeys[j].mTime = t;
+ pAnim->mChannels[l]->mRotationKeys[j].mValue.w = q->w;
+ pAnim->mChannels[l]->mRotationKeys[j].mValue.x = q->x;
+ pAnim->mChannels[l]->mRotationKeys[j].mValue.y = q->y;
+ pAnim->mChannels[l]->mRotationKeys[j].mValue.z = q->z;
+ } // foreach frame
+ } // foreach bones
+ mScene->mAnimations[i] = pAnim;
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// convert uint32_t into aiColor4D
+aiColor4D M3DImporter::mkColor(uint32_t c) {
+ aiColor4D color;
+ color.a = ((float)((c >> 24) & 0xff)) / 255;
+ color.b = ((float)((c >> 16) & 0xff)) / 255;
+ color.g = ((float)((c >> 8) & 0xff)) / 255;
+ color.r = ((float)((c >> 0) & 0xff)) / 255;
+ return color;
+}
+
+// ------------------------------------------------------------------------------------------------
+// convert a position id and orientation id into a 4 x 4 transformation matrix
+void M3DImporter::convertPose(const M3DWrapper &m3d, aiMatrix4x4 *m, unsigned int posid, unsigned int orientid) {
+ ai_assert(m != nullptr);
+ ai_assert(m3d);
+ ai_assert(posid != M3D_UNDEF);
+ ai_assert(posid < m3d->numvertex);
+ ai_assert(orientid != M3D_UNDEF);
+ ai_assert(orientid < m3d->numvertex);
+ if (!m3d->numvertex || !m3d->vertex)
+ return;
+ m3dv_t *p = &m3d->vertex[posid];
+ m3dv_t *q = &m3d->vertex[orientid];
+
+ /* quaternion to matrix. Do NOT use aiQuaternion to aiMatrix3x3, gives bad results */
+ if (q->x == 0.0 && q->y == 0.0 && q->z >= 0.7071065 && q->z <= 0.7071075 && q->w == 0.0) {
+ m->a2 = m->a3 = m->b1 = m->b3 = m->c1 = m->c2 = 0.0;
+ m->a1 = m->b2 = m->c3 = -1.0;
+ } else {
+ m->a1 = 1 - 2 * (q->y * q->y + q->z * q->z);
+ if (m->a1 > -M3D_EPSILON && m->a1 < M3D_EPSILON) m->a1 = 0.0;
+ m->a2 = 2 * (q->x * q->y - q->z * q->w);
+ if (m->a2 > -M3D_EPSILON && m->a2 < M3D_EPSILON) m->a2 = 0.0;
+ m->a3 = 2 * (q->x * q->z + q->y * q->w);
+ if (m->a3 > -M3D_EPSILON && m->a3 < M3D_EPSILON) m->a3 = 0.0;
+ m->b1 = 2 * (q->x * q->y + q->z * q->w);
+ if (m->b1 > -M3D_EPSILON && m->b1 < M3D_EPSILON) m->b1 = 0.0;
+ m->b2 = 1 - 2 * (q->x * q->x + q->z * q->z);
+ if (m->b2 > -M3D_EPSILON && m->b2 < M3D_EPSILON) m->b2 = 0.0;
+ m->b3 = 2 * (q->y * q->z - q->x * q->w);
+ if (m->b3 > -M3D_EPSILON && m->b3 < M3D_EPSILON) m->b3 = 0.0;
+ m->c1 = 2 * (q->x * q->z - q->y * q->w);
+ if (m->c1 > -M3D_EPSILON && m->c1 < M3D_EPSILON) m->c1 = 0.0;
+ m->c2 = 2 * (q->y * q->z + q->x * q->w);
+ if (m->c2 > -M3D_EPSILON && m->c2 < M3D_EPSILON) m->c2 = 0.0;
+ m->c3 = 1 - 2 * (q->x * q->x + q->y * q->y);
+ if (m->c3 > -M3D_EPSILON && m->c3 < M3D_EPSILON) m->c3 = 0.0;
+ }
+
+ /* set translation */
+ m->a4 = p->x;
+ m->b4 = p->y;
+ m->c4 = p->z;
+
+ m->d1 = 0;
+ m->d2 = 0;
+ m->d3 = 0;
+ m->d4 = 1;
+}
+
+// ------------------------------------------------------------------------------------------------
+// find a node by name
+aiNode *M3DImporter::findNode(aiNode *pNode, const aiString &name) {
+ ai_assert(pNode != nullptr);
+ ai_assert(mScene != nullptr);
+
+ if (pNode->mName == name) {
+ return pNode;
+ }
+
+ for (unsigned int i = 0; i < pNode->mNumChildren; i++) {
+ aiNode *pChild = findNode(pNode->mChildren[i], name);
+ if (pChild) {
+ return pChild;
+ }
+ }
+ return nullptr;
+}
+
+// ------------------------------------------------------------------------------------------------
+// fills up offsetmatrix in mBones
+void M3DImporter::calculateOffsetMatrix(aiNode *pNode, aiMatrix4x4 *m) {
+ ai_assert(pNode != nullptr);
+ ai_assert(mScene != nullptr);
+
+ if (pNode->mParent) {
+ calculateOffsetMatrix(pNode->mParent, m);
+ *m *= pNode->mTransformation;
+ } else {
+ *m = pNode->mTransformation;
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// because M3D has a global mesh, global vertex ids and stores materialid on the face, we need
+// temporary lists to collect data for an aiMesh, which requires local arrays and local indices
+// this function fills up an aiMesh with those temporary lists
+void M3DImporter::populateMesh(const M3DWrapper &m3d, aiMesh *pMesh, std::vector<aiFace> *faces, std::vector<aiVector3D> *vertices,
+ std::vector<aiVector3D> *normals, std::vector<aiVector3D> *texcoords, std::vector<aiColor4D> *colors,
+ std::vector<unsigned int> *vertexids) {
+
+ ai_assert(pMesh != nullptr);
+ ai_assert(faces != nullptr);
+ ai_assert(vertices != nullptr);
+ ai_assert(normals != nullptr);
+ ai_assert(texcoords != nullptr);
+ ai_assert(colors != nullptr);
+ ai_assert(vertexids != nullptr);
+ ai_assert(m3d);
+
+ ASSIMP_LOG_DEBUG("M3D: populateMesh numvertices ", vertices->size(), " numfaces ", faces->size(),
+ " numnormals ", normals->size(), " numtexcoord ", texcoords->size(), " numbones ", m3d->numbone);
+
+ if (vertices->size() && faces->size()) {
+ pMesh->mNumFaces = static_cast<unsigned int>(faces->size());
+ pMesh->mFaces = new aiFace[pMesh->mNumFaces];
+ std::copy(faces->begin(), faces->end(), pMesh->mFaces);
+ pMesh->mNumVertices = static_cast<unsigned int>(vertices->size());
+ pMesh->mVertices = new aiVector3D[pMesh->mNumVertices];
+ std::copy(vertices->begin(), vertices->end(), pMesh->mVertices);
+ if (normals->size() == vertices->size()) {
+ pMesh->mNormals = new aiVector3D[pMesh->mNumVertices];
+ std::copy(normals->begin(), normals->end(), pMesh->mNormals);
+ }
+ if (texcoords->size() == vertices->size()) {
+ pMesh->mTextureCoords[0] = new aiVector3D[pMesh->mNumVertices];
+ std::copy(texcoords->begin(), texcoords->end(), pMesh->mTextureCoords[0]);
+ pMesh->mNumUVComponents[0] = 2;
+ }
+ if (colors->size() == vertices->size()) {
+ pMesh->mColors[0] = new aiColor4D[pMesh->mNumVertices];
+ std::copy(colors->begin(), colors->end(), pMesh->mColors[0]);
+ }
+ // this is complicated, because M3D stores a list of bone id / weight pairs per
+ // vertex but assimp uses lists of local vertex id/weight pairs per local bone list
+ pMesh->mNumBones = m3d->numbone;
+ // we need aiBone with mOffsetMatrix for bones without weights as well
+ if (pMesh->mNumBones && m3d->numbone && m3d->bone) {
+ pMesh->mBones = new aiBone *[pMesh->mNumBones];
+ for (unsigned int i = 0; i < m3d->numbone; i++) {
+ aiNode *pNode;
+ pMesh->mBones[i] = new aiBone;
+ pMesh->mBones[i]->mName = aiString(std::string(m3d->bone[i].name));
+ pMesh->mBones[i]->mNumWeights = 0;
+ pNode = findNode(mScene->mRootNode, pMesh->mBones[i]->mName);
+ if (pNode) {
+ calculateOffsetMatrix(pNode, &pMesh->mBones[i]->mOffsetMatrix);
+ pMesh->mBones[i]->mOffsetMatrix.Inverse();
+ } else
+ pMesh->mBones[i]->mOffsetMatrix = aiMatrix4x4();
+ }
+ if (vertexids->size() && m3d->numvertex && m3d->vertex && m3d->numskin && m3d->skin) {
+ unsigned int i, j;
+ // first count how many vertices we have per bone
+ for (i = 0; i < vertexids->size(); i++) {
+ if (vertexids->at(i) >= m3d->numvertex) {
+ continue;
+ }
+ unsigned int s = m3d->vertex[vertexids->at(i)].skinid;
+ if (s != M3D_UNDEF && s != M3D_INDEXMAX) {
+ for (unsigned int k = 0; k < M3D_NUMBONE && m3d->skin[s].weight[k] > 0.0; k++) {
+ aiString name = aiString(std::string(m3d->bone[m3d->skin[s].boneid[k]].name));
+ for (j = 0; j < pMesh->mNumBones; j++) {
+ if (pMesh->mBones[j]->mName == name) {
+ pMesh->mBones[j]->mNumWeights++;
+ break;
+ }
+ }
+ }
+ }
+ }
+ // allocate mWeights
+ for (j = 0; j < pMesh->mNumBones; j++) {
+ aiBone *pBone = pMesh->mBones[j];
+ if (pBone->mNumWeights) {
+ pBone->mWeights = new aiVertexWeight[pBone->mNumWeights];
+ pBone->mNumWeights = 0;
+ }
+ }
+ // fill up with data
+ for (i = 0; i < vertexids->size(); i++) {
+ if (vertexids->at(i) >= m3d->numvertex) continue;
+ unsigned int s = m3d->vertex[vertexids->at(i)].skinid;
+ if (s != M3D_UNDEF && s != M3D_INDEXMAX && s < m3d->numskin) {
+ for (unsigned int k = 0; k < M3D_NUMBONE && m3d->skin[s].weight[k] > 0.0; k++) {
+ if (m3d->skin[s].boneid[k] >= m3d->numbone) continue;
+ aiString name = aiString(std::string(m3d->bone[m3d->skin[s].boneid[k]].name));
+ for (j = 0; j < pMesh->mNumBones; j++) {
+ if (pMesh->mBones[j]->mName == name) {
+ aiBone *pBone = pMesh->mBones[j];
+ pBone->mWeights[pBone->mNumWeights].mVertexId = i;
+ pBone->mWeights[pBone->mNumWeights].mWeight = m3d->skin[s].weight[k];
+ pBone->mNumWeights++;
+ break;
+ }
+ }
+ } // foreach skin
+ }
+ } // foreach vertexids
+ }
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+
+} // Namespace Assimp
+
+#endif // !! ASSIMP_BUILD_NO_M3D_IMPORTER