Some cleanup of M3D support

Wrap the m3d.h header
Note: C++11 support required to use in a threaded environment
Fix export memory leak (although exporter apopears to be unused)
Apply clangformat.
pull/2805/head
RichardTea 2019-12-03 12:56:21 +00:00
parent 6117c3f589
commit e668eead19
7 changed files with 1117 additions and 924 deletions

View File

@ -411,6 +411,8 @@ ADD_ASSIMP_IMPORTER( M3D
M3D/M3DMaterials.h M3D/M3DMaterials.h
M3D/M3DImporter.h M3D/M3DImporter.h
M3D/M3DImporter.cpp M3D/M3DImporter.cpp
M3D/M3DWrapper.h
M3D/M3DWrapper.cpp
M3D/m3d.h M3D/m3d.h
) )

View File

@ -55,17 +55,19 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <string> #include <string>
#include <vector> #include <vector>
#include <assimp/version.h> // aiGetVersion
#include <assimp/IOSystem.hpp>
#include <assimp/Exporter.hpp>
#include <assimp/DefaultLogger.hpp>
#include <assimp/StreamWriter.h> // StreamWriterLE
#include <assimp/Exceptional.h> // DeadlyExportError #include <assimp/Exceptional.h> // DeadlyExportError
#include <assimp/StreamWriter.h> // StreamWriterLE
#include <assimp/material.h> // aiTextureType #include <assimp/material.h> // aiTextureType
#include <assimp/scene.h>
#include <assimp/mesh.h> #include <assimp/mesh.h>
#include <assimp/scene.h>
#include <assimp/version.h> // aiGetVersion
#include <assimp/DefaultLogger.hpp>
#include <assimp/Exporter.hpp>
#include <assimp/IOSystem.hpp>
#include "M3DExporter.h" #include "M3DExporter.h"
#include "M3DMaterials.h" #include "M3DMaterials.h"
#include "M3DWrapper.h"
// RESOURCES: // RESOURCES:
// https://gitlab.com/bztsrc/model3d/blob/master/docs/m3d_format.md // https://gitlab.com/bztsrc/model3d/blob/master/docs/m3d_format.md
@ -80,105 +82,17 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* - aiAnimation -> m3d_action (frame with timestamp and list of bone id, position, orientation * - aiAnimation -> m3d_action (frame with timestamp and list of bone id, position, orientation
* triplets, instead of per bone timestamp + lists) * triplets, instead of per bone timestamp + lists)
*/ */
using namespace Assimp;
namespace Assimp {
// ---------------------------------------------------------------------
// Worker function for exporting a scene to binary M3D.
// Prototyped and registered in Exporter.cpp
void ExportSceneM3D (
const char* pFile,
IOSystem* pIOSystem,
const aiScene* pScene,
const ExportProperties* pProperties
){
// initialize the exporter
M3DExporter exporter(pScene, pProperties);
// perform binary export
exporter.doExport(pFile, pIOSystem, false);
}
// ---------------------------------------------------------------------
// Worker function for exporting a scene to ASCII A3D.
// Prototyped and registered in Exporter.cpp
void ExportSceneA3D (
const char* pFile,
IOSystem* pIOSystem,
const aiScene* pScene,
const ExportProperties* pProperties
){
// initialize the exporter
M3DExporter exporter(pScene, pProperties);
// perform ascii export
exporter.doExport(pFile, pIOSystem, true);
}
} // end of namespace Assimp
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
M3DExporter::M3DExporter ( const aiScene* pScene, const ExportProperties* pProperties ) // Conversion functions
: mScene(pScene)
, mProperties(pProperties)
, outfile()
, m3d(nullptr) { }
// ------------------------------------------------------------------------------------------------
void M3DExporter::doExport (
const char* pFile,
IOSystem* pIOSystem,
bool toAscii
){
// TODO: convert mProperties into M3D_EXP_* flags
(void)mProperties;
// open the indicated file for writing (in binary / ASCII mode)
outfile.reset(pIOSystem->Open(pFile, toAscii ? "wt" : "wb"));
if (!outfile) {
throw DeadlyExportError( "could not open output .m3d file: " + std::string(pFile) );
}
// use malloc() here because m3d_free() will call free()
m3d = (m3d_t*)calloc(1, sizeof(m3d_t));
if(!m3d) {
throw DeadlyExportError( "memory allocation error" );
}
m3d->name = _m3d_safestr((char*)&mScene->mRootNode->mName.data, 2);
// Create a model from assimp structures
aiMatrix4x4 m;
NodeWalk(mScene->mRootNode, m);
// serialize the structures
unsigned int size;
unsigned char *output = m3d_save(m3d, M3D_EXP_FLOAT,
M3D_EXP_EXTRA | (toAscii ? M3D_EXP_ASCII : 0), &size);
m3d_free(m3d);
if(!output || size < 8) {
throw DeadlyExportError( "unable to serialize into Model 3D" );
}
// Write out serialized model
outfile->Write(output, size, 1);
// explicitly release file pointer,
// so we don't have to rely on class destruction.
outfile.reset();
}
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// helper to add a vertex (private to NodeWalk) // helper to add a vertex (private to NodeWalk)
m3dv_t *M3DExporter::AddVrtx(m3dv_t *vrtx, uint32_t *numvrtx, m3dv_t *v, uint32_t *idx) m3dv_t *AddVrtx(m3dv_t *vrtx, uint32_t *numvrtx, m3dv_t *v, uint32_t *idx) {
{ if (v->x == (M3D_FLOAT)-0.0) v->x = (M3D_FLOAT)0.0;
if(v->x == (M3D_FLOAT)-0.0) v->x = (M3D_FLOAT)0.0; if (v->y == (M3D_FLOAT)-0.0) v->y = (M3D_FLOAT)0.0;
if(v->y == (M3D_FLOAT)-0.0) v->y = (M3D_FLOAT)0.0; if (v->z == (M3D_FLOAT)-0.0) v->z = (M3D_FLOAT)0.0;
if(v->z == (M3D_FLOAT)-0.0) v->z = (M3D_FLOAT)0.0; if (v->w == (M3D_FLOAT)-0.0) v->w = (M3D_FLOAT)0.0;
if(v->w == (M3D_FLOAT)-0.0) v->w = (M3D_FLOAT)0.0; vrtx = (m3dv_t *)M3D_REALLOC(vrtx, ((*numvrtx) + 1) * sizeof(m3dv_t));
vrtx = (m3dv_t*)M3D_REALLOC(vrtx, ((*numvrtx) + 1) * sizeof(m3dv_t));
memcpy(&vrtx[*numvrtx], v, sizeof(m3dv_t)); memcpy(&vrtx[*numvrtx], v, sizeof(m3dv_t));
*idx = *numvrtx; *idx = *numvrtx;
(*numvrtx)++; (*numvrtx)++;
@ -187,9 +101,8 @@ m3dv_t *M3DExporter::AddVrtx(m3dv_t *vrtx, uint32_t *numvrtx, m3dv_t *v, uint32_
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// helper to add a tmap (private to NodeWalk) // helper to add a tmap (private to NodeWalk)
m3dti_t *M3DExporter::AddTmap(m3dti_t *tmap, uint32_t *numtmap, m3dti_t *ti, uint32_t *idx) m3dti_t *AddTmap(m3dti_t *tmap, uint32_t *numtmap, m3dti_t *ti, uint32_t *idx) {
{ tmap = (m3dti_t *)M3D_REALLOC(tmap, ((*numtmap) + 1) * sizeof(m3dti_t));
tmap = (m3dti_t*)M3D_REALLOC(tmap, ((*numtmap) + 1) * sizeof(m3dti_t));
memcpy(&tmap[*numtmap], ti, sizeof(m3dti_t)); memcpy(&tmap[*numtmap], ti, sizeof(m3dti_t));
*idx = *numtmap; *idx = *numtmap;
(*numtmap)++; (*numtmap)++;
@ -197,154 +110,84 @@ m3dti_t *M3DExporter::AddTmap(m3dti_t *tmap, uint32_t *numtmap, m3dti_t *ti, uin
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// recursive node walker // convert aiColor4D into uint32_t
void M3DExporter::NodeWalk(const aiNode* pNode, aiMatrix4x4 m) uint32_t mkColor(aiColor4D *c) {
{ return ((uint8_t)(c->a * 255) << 24L) |
aiMatrix4x4 nm = m * pNode->mTransformation; ((uint8_t)(c->b * 255) << 16L) |
((uint8_t)(c->g * 255) << 8L) |
for(unsigned int i = 0; i < pNode->mNumMeshes; i++) { ((uint8_t)(c->r * 255) << 0L);
const aiMesh *mesh = mScene->mMeshes[pNode->mMeshes[i]];
unsigned int mi = (M3D_INDEX)-1U;
if(mScene->mMaterials) {
// get the material for this mesh
mi = addMaterial(mScene->mMaterials[mesh->mMaterialIndex]);
}
// iterate through the mesh faces
for(unsigned int j = 0; j < mesh->mNumFaces; j++) {
unsigned int n;
const aiFace* face = &(mesh->mFaces[j]);
// only triangle meshes supported for now
if(face->mNumIndices != 3) {
throw DeadlyExportError( "use aiProcess_Triangulate before export" );
}
// add triangle to the output
n = m3d->numface++;
m3d->face = (m3df_t*)M3D_REALLOC(m3d->face,
m3d->numface * sizeof(m3df_t));
if(!m3d->face) {
throw DeadlyExportError( "memory allocation error" );
}
/* set all index to -1 by default */
m3d->face[n].vertex[0] = m3d->face[n].vertex[1] = m3d->face[n].vertex[2] =
m3d->face[n].normal[0] = m3d->face[n].normal[1] = m3d->face[n].normal[2] =
m3d->face[n].texcoord[0] = m3d->face[n].texcoord[1] = m3d->face[n].texcoord[2] = -1U;
m3d->face[n].materialid = mi;
for(unsigned int k = 0; k < face->mNumIndices; k++) {
// get the vertex's index
unsigned int l = face->mIndices[k];
unsigned int idx;
m3dv_t vertex;
m3dti_t ti;
// multiply the position vector by the transformation matrix
aiVector3D v = mesh->mVertices[l];
v *= nm;
vertex.x = v.x;
vertex.y = v.y;
vertex.z = v.z;
vertex.w = 1.0;
vertex.color = 0;
vertex.skinid = -1U;
// add color if defined
if(mesh->HasVertexColors(0))
vertex.color = mkColor(&mesh->mColors[0][l]);
// save the vertex to the output
m3d->vertex = AddVrtx(m3d->vertex, &m3d->numvertex,
&vertex, &idx);
m3d->face[n].vertex[k] = (M3D_INDEX)idx;
// do we have texture coordinates?
if(mesh->HasTextureCoords(0)) {
ti.u = mesh->mTextureCoords[0][l].x;
ti.v = mesh->mTextureCoords[0][l].y;
m3d->tmap = AddTmap(m3d->tmap, &m3d->numtmap, &ti, &idx);
m3d->face[n].texcoord[k] = (M3D_INDEX)idx;
}
// do we have normal vectors?
if(mesh->HasNormals()) {
vertex.x = mesh->mNormals[l].x;
vertex.y = mesh->mNormals[l].y;
vertex.z = mesh->mNormals[l].z;
vertex.color = 0;
m3d->vertex = AddVrtx(m3d->vertex, &m3d->numvertex, &vertex, &idx);
m3d->face[n].normal[k] = (M3D_INDEX)idx;
}
}
}
}
// repeat for the children nodes
for (unsigned int i = 0; i < pNode->mNumChildren; i++) {
NodeWalk(pNode->mChildren[i], nm);
}
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// convert aiColor4D into uint32_t // add a material property to the output
uint32_t M3DExporter::mkColor(aiColor4D* c) void addProp(m3dm_t *m, uint8_t type, uint32_t value) {
{ unsigned int i;
return ((uint8_t)(c->a*255) << 24L) | i = m->numprop++;
((uint8_t)(c->b*255) << 16L) | m->prop = (m3dp_t *)M3D_REALLOC(m->prop, m->numprop * sizeof(m3dp_t));
((uint8_t)(c->g*255) << 8L) | if (!m->prop) {
((uint8_t)(c->r*255) << 0L); throw DeadlyExportError("memory allocation error");
}
m->prop[i].type = type;
m->prop[i].value.num = value;
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// add a material to the output // add a material to the output
M3D_INDEX M3DExporter::addMaterial(const aiMaterial *mat) M3D_INDEX addMaterial(const Assimp::M3DWrapper &m3d, const aiMaterial *mat) {
{
unsigned int mi = -1U; unsigned int mi = -1U;
aiColor4D c; aiColor4D c;
aiString name; aiString name;
ai_real f; ai_real f;
char *fn; char *fn;
if(mat && mat->Get(AI_MATKEY_NAME, name) == AI_SUCCESS && name.length && if (mat && mat->Get(AI_MATKEY_NAME, name) == AI_SUCCESS && name.length &&
strcmp((char*)&name.data, AI_DEFAULT_MATERIAL_NAME)) { strcmp((char *)&name.data, AI_DEFAULT_MATERIAL_NAME)) {
// check if we have saved a material by this name. This has to be done // check if we have saved a material by this name. This has to be done
// because only the referenced materials should be added to the output // because only the referenced materials should be added to the output
for(unsigned int i = 0; i < m3d->nummaterial; i++) for (unsigned int i = 0; i < m3d->nummaterial; i++)
if(!strcmp((char*)&name.data, m3d->material[i].name)) { if (!strcmp((char *)&name.data, m3d->material[i].name)) {
mi = i; mi = i;
break; break;
} }
// if not found, add the material to the output // if not found, add the material to the output
if(mi == -1U) { if (mi == -1U) {
unsigned int k; unsigned int k;
mi = m3d->nummaterial++; mi = m3d->nummaterial++;
m3d->material = (m3dm_t*)M3D_REALLOC(m3d->material, m3d->nummaterial m3d->material = (m3dm_t *)M3D_REALLOC(m3d->material, m3d->nummaterial * sizeof(m3dm_t));
* sizeof(m3dm_t)); if (!m3d->material) {
if(!m3d->material) { throw DeadlyExportError("memory allocation error");
throw DeadlyExportError( "memory allocation error" );
} }
m3d->material[mi].name = _m3d_safestr((char*)&name.data, 0); m3d->material[mi].name = _m3d_safestr((char *)&name.data, 0);
m3d->material[mi].numprop = 0; m3d->material[mi].numprop = 0;
m3d->material[mi].prop = NULL; m3d->material[mi].prop = NULL;
// iterate through the material property table and see what we got // iterate through the material property table and see what we got
for(k = 0; k < 15; k++) { for (k = 0; k < 15; k++) {
unsigned int j; unsigned int j;
if(m3d_propertytypes[k].format == m3dpf_map) if (m3d_propertytypes[k].format == m3dpf_map)
continue; continue;
if(aiProps[k].pKey) { if (aiProps[k].pKey) {
switch(m3d_propertytypes[k].format) { switch (m3d_propertytypes[k].format) {
case m3dpf_color: case m3dpf_color:
if(mat->Get(aiProps[k].pKey, aiProps[k].type, if (mat->Get(aiProps[k].pKey, aiProps[k].type,
aiProps[k].index, c) == AI_SUCCESS) aiProps[k].index, c) == AI_SUCCESS)
addProp(&m3d->material[mi], addProp(&m3d->material[mi],
m3d_propertytypes[k].id, mkColor(&c)); m3d_propertytypes[k].id, mkColor(&c));
break; break;
case m3dpf_float: case m3dpf_float:
if(mat->Get(aiProps[k].pKey, aiProps[k].type, if (mat->Get(aiProps[k].pKey, aiProps[k].type,
aiProps[k].index, f) == AI_SUCCESS) aiProps[k].index, f) == AI_SUCCESS)
addProp(&m3d->material[mi], addProp(&m3d->material[mi],
m3d_propertytypes[k].id, m3d_propertytypes[k].id,
/* not (uint32_t)f, because we don't want to convert /* not (uint32_t)f, because we don't want to convert
* it, we want to see it as 32 bits of memory */ * it, we want to see it as 32 bits of memory */
*((uint32_t*)&f)); *((uint32_t *)&f));
break; break;
case m3dpf_uint8: case m3dpf_uint8:
if(mat->Get(aiProps[k].pKey, aiProps[k].type, if (mat->Get(aiProps[k].pKey, aiProps[k].type,
aiProps[k].index, j) == AI_SUCCESS) { aiProps[k].index, j) == AI_SUCCESS) {
// special conversion for illumination model property // special conversion for illumination model property
if(m3d_propertytypes[k].id == m3dp_il) { if (m3d_propertytypes[k].id == m3dp_il) {
switch(j) { switch (j) {
case aiShadingMode_NoShading: j = 0; break; case aiShadingMode_NoShading: j = 0; break;
case aiShadingMode_Phong: j = 2; break; case aiShadingMode_Phong: j = 2; break;
default: j = 1; break; default: j = 1; break;
@ -355,39 +198,40 @@ M3D_INDEX M3DExporter::addMaterial(const aiMaterial *mat)
} }
break; break;
default: default:
if(mat->Get(aiProps[k].pKey, aiProps[k].type, if (mat->Get(aiProps[k].pKey, aiProps[k].type,
aiProps[k].index, j) == AI_SUCCESS) aiProps[k].index, j) == AI_SUCCESS)
addProp(&m3d->material[mi], addProp(&m3d->material[mi],
m3d_propertytypes[k].id, j); m3d_propertytypes[k].id, j);
break; break;
} }
} }
if(aiTxProps[k].pKey && if (aiTxProps[k].pKey &&
mat->GetTexture((aiTextureType)aiTxProps[k].type, mat->GetTexture((aiTextureType)aiTxProps[k].type,
aiTxProps[k].index, &name, NULL, NULL, NULL, aiTxProps[k].index, &name, NULL, NULL, NULL,
NULL, NULL) == AI_SUCCESS) { NULL, NULL) == AI_SUCCESS) {
unsigned int i; unsigned int i;
for(j = name.length-1; j > 0 && name.data[j]!='.'; j++); for (j = name.length - 1; j > 0 && name.data[j] != '.'; j++)
if(j && name.data[j]=='.' && ;
(name.data[j+1]=='p' || name.data[j+1]=='P') && if (j && name.data[j] == '.' &&
(name.data[j+1]=='n' || name.data[j+1]=='N') && (name.data[j + 1] == 'p' || name.data[j + 1] == 'P') &&
(name.data[j+1]=='g' || name.data[j+1]=='G')) (name.data[j + 1] == 'n' || name.data[j + 1] == 'N') &&
name.data[j]=0; (name.data[j + 1] == 'g' || name.data[j + 1] == 'G'))
name.data[j] = 0;
// do we have this texture saved already? // do we have this texture saved already?
fn = _m3d_safestr((char*)&name.data, 0); fn = _m3d_safestr((char *)&name.data, 0);
for(j = 0, i = -1U; j < m3d->numtexture; j++) for (j = 0, i = -1U; j < m3d->numtexture; j++)
if(!strcmp(fn, m3d->texture[j].name)) { if (!strcmp(fn, m3d->texture[j].name)) {
i = j; i = j;
free(fn); free(fn);
break; break;
} }
if(i == -1U) { if (i == -1U) {
i = m3d->numtexture++; i = m3d->numtexture++;
m3d->texture = (m3dtx_t*)M3D_REALLOC( m3d->texture = (m3dtx_t *)M3D_REALLOC(
m3d->texture, m3d->texture,
m3d->numtexture * sizeof(m3dtx_t)); m3d->numtexture * sizeof(m3dtx_t));
if(!m3d->texture) { if (!m3d->texture) {
throw DeadlyExportError( "memory allocation error" ); throw DeadlyExportError("memory allocation error");
} }
// we don't need the texture itself, only its name // we don't need the texture itself, only its name
m3d->texture[i].name = fn; m3d->texture[i].name = fn;
@ -404,17 +248,165 @@ M3D_INDEX M3DExporter::addMaterial(const aiMaterial *mat)
return mi; return mi;
} }
// ------------------------------------------------------------------------------------------------ namespace Assimp {
// add a material property to the output
void M3DExporter::addProp(m3dm_t *m, uint8_t type, uint32_t value) // ---------------------------------------------------------------------
{ // Worker function for exporting a scene to binary M3D.
unsigned int i; // Prototyped and registered in Exporter.cpp
i = m->numprop++; void ExportSceneM3D(
m->prop = (m3dp_t*)M3D_REALLOC(m->prop, m->numprop * sizeof(m3dp_t)); const char *pFile,
if(!m->prop) { throw DeadlyExportError( "memory allocation error" ); } IOSystem *pIOSystem,
m->prop[i].type = type; const aiScene *pScene,
m->prop[i].value.num = value; const ExportProperties *pProperties) {
// initialize the exporter
M3DExporter exporter(pScene, pProperties);
// perform binary export
exporter.doExport(pFile, pIOSystem, false);
} }
// ---------------------------------------------------------------------
// Worker function for exporting a scene to ASCII A3D.
// Prototyped and registered in Exporter.cpp
void ExportSceneA3D(
const char *pFile,
IOSystem *pIOSystem,
const aiScene *pScene,
const ExportProperties *pProperties
) {
// initialize the exporter
M3DExporter exporter(pScene, pProperties);
// perform ascii export
exporter.doExport(pFile, pIOSystem, true);
}
// ------------------------------------------------------------------------------------------------
M3DExporter::M3DExporter(const aiScene *pScene, const ExportProperties *pProperties) :
mScene(pScene),
mProperties(pProperties),
outfile() {}
// ------------------------------------------------------------------------------------------------
void M3DExporter::doExport(
const char *pFile,
IOSystem *pIOSystem,
bool toAscii) {
// TODO: convert mProperties into M3D_EXP_* flags
(void)mProperties;
// open the indicated file for writing (in binary / ASCII mode)
outfile.reset(pIOSystem->Open(pFile, toAscii ? "wt" : "wb"));
if (!outfile) {
throw DeadlyExportError("could not open output .m3d file: " + std::string(pFile));
}
M3DWrapper m3d;
if (!m3d) {
throw DeadlyExportError("memory allocation error");
}
m3d->name = _m3d_safestr((char *)&mScene->mRootNode->mName.data, 2);
// Create a model from assimp structures
aiMatrix4x4 m;
NodeWalk(m3d, mScene->mRootNode, m);
// serialize the structures
unsigned int size;
unsigned char *output = m3d.Save(M3D_EXP_FLOAT, M3D_EXP_EXTRA | (toAscii ? M3D_EXP_ASCII : 0), size);
if (!output || size < 8) {
throw DeadlyExportError("unable to serialize into Model 3D");
}
// Write out serialized model
outfile->Write(output, size, 1);
// explicitly release file pointer,
// so we don't have to rely on class destruction.
outfile.reset();
}
// ------------------------------------------------------------------------------------------------
// recursive node walker
void M3DExporter::NodeWalk(const M3DWrapper &m3d, const aiNode *pNode, aiMatrix4x4 m) {
aiMatrix4x4 nm = m * pNode->mTransformation;
for (unsigned int i = 0; i < pNode->mNumMeshes; i++) {
const aiMesh *mesh = mScene->mMeshes[pNode->mMeshes[i]];
unsigned int mi = (M3D_INDEX)-1U;
if (mScene->mMaterials) {
// get the material for this mesh
mi = addMaterial(m3d, mScene->mMaterials[mesh->mMaterialIndex]);
}
// iterate through the mesh faces
for (unsigned int j = 0; j < mesh->mNumFaces; j++) {
unsigned int n;
const aiFace *face = &(mesh->mFaces[j]);
// only triangle meshes supported for now
if (face->mNumIndices != 3) {
throw DeadlyExportError("use aiProcess_Triangulate before export");
}
// add triangle to the output
n = m3d->numface++;
m3d->face = (m3df_t *)M3D_REALLOC(m3d->face,
m3d->numface * sizeof(m3df_t));
if (!m3d->face) {
throw DeadlyExportError("memory allocation error");
}
/* set all index to -1 by default */
m3d->face[n].vertex[0] = m3d->face[n].vertex[1] = m3d->face[n].vertex[2] =
m3d->face[n].normal[0] = m3d->face[n].normal[1] = m3d->face[n].normal[2] =
m3d->face[n].texcoord[0] = m3d->face[n].texcoord[1] = m3d->face[n].texcoord[2] = -1U;
m3d->face[n].materialid = mi;
for (unsigned int k = 0; k < face->mNumIndices; k++) {
// get the vertex's index
unsigned int l = face->mIndices[k];
unsigned int idx;
m3dv_t vertex;
m3dti_t ti;
// multiply the position vector by the transformation matrix
aiVector3D v = mesh->mVertices[l];
v *= nm;
vertex.x = v.x;
vertex.y = v.y;
vertex.z = v.z;
vertex.w = 1.0;
vertex.color = 0;
vertex.skinid = -1U;
// add color if defined
if (mesh->HasVertexColors(0))
vertex.color = mkColor(&mesh->mColors[0][l]);
// save the vertex to the output
m3d->vertex = AddVrtx(m3d->vertex, &m3d->numvertex,
&vertex, &idx);
m3d->face[n].vertex[k] = (M3D_INDEX)idx;
// do we have texture coordinates?
if (mesh->HasTextureCoords(0)) {
ti.u = mesh->mTextureCoords[0][l].x;
ti.v = mesh->mTextureCoords[0][l].y;
m3d->tmap = AddTmap(m3d->tmap, &m3d->numtmap, &ti, &idx);
m3d->face[n].texcoord[k] = (M3D_INDEX)idx;
}
// do we have normal vectors?
if (mesh->HasNormals()) {
vertex.x = mesh->mNormals[l].x;
vertex.y = mesh->mNormals[l].y;
vertex.z = mesh->mNormals[l].z;
vertex.color = 0;
m3d->vertex = AddVrtx(m3d->vertex, &m3d->numvertex, &vertex, &idx);
m3d->face[n].normal[k] = (M3D_INDEX)idx;
}
}
}
}
// repeat for the children nodes
for (unsigned int i = 0; i < pNode->mNumChildren; i++) {
NodeWalk(m3d, pNode->mChildren[i], nm);
}
}
} // namespace Assimp
#endif // ASSIMP_BUILD_NO_M3D_EXPORTER #endif // ASSIMP_BUILD_NO_M3D_EXPORTER
#endif // ASSIMP_BUILD_NO_EXPORT #endif // ASSIMP_BUILD_NO_EXPORT

View File

@ -48,8 +48,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef ASSIMP_BUILD_NO_M3D_EXPORTER #ifndef ASSIMP_BUILD_NO_M3D_EXPORTER
#include "m3d.h"
#include <assimp/types.h> #include <assimp/types.h>
//#include <assimp/material.h> //#include <assimp/material.h>
#include <assimp/StreamWriter.h> // StreamWriterLE #include <assimp/StreamWriter.h> // StreamWriterLE
@ -68,6 +66,8 @@ namespace Assimp
class IOStream; class IOStream;
class ExportProperties; class ExportProperties;
class M3DWrapper;
// --------------------------------------------------------------------- // ---------------------------------------------------------------------
/** Helper class to export a given scene to an M3D file. */ /** Helper class to export a given scene to an M3D file. */
// --------------------------------------------------------------------- // ---------------------------------------------------------------------
@ -83,15 +83,9 @@ namespace Assimp
const aiScene* mScene; // the scene to export const aiScene* mScene; // the scene to export
const ExportProperties* mProperties; // currently unused const ExportProperties* mProperties; // currently unused
std::shared_ptr<IOStream> outfile; // file to write to std::shared_ptr<IOStream> outfile; // file to write to
m3d_t *m3d; // model for the C library to convert to
// helper to do the recursive walking // helper to do the recursive walking
void NodeWalk(const aiNode* pNode, aiMatrix4x4 m); void NodeWalk(const M3DWrapper &m3d, const aiNode* pNode, aiMatrix4x4 m);
m3dv_t *AddVrtx(m3dv_t *vrtx, uint32_t *numvrtx, m3dv_t *v, uint32_t *idx);
m3dti_t *AddTmap(m3dti_t *tmap, uint32_t *numtmap, m3dti_t *ti, uint32_t *idx);
uint32_t mkColor(aiColor4D* c);
M3D_INDEX addMaterial(const aiMaterial *mat);
void addProp(m3dm_t *m, uint8_t type, uint32_t value);
}; };
} }

View File

@ -48,16 +48,18 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#define M3D_NOWEIGHTS #define M3D_NOWEIGHTS
#define M3D_NOANIMATION #define M3D_NOANIMATION
#include <assimp/IOStreamBuffer.h>
#include <memory>
#include <assimp/DefaultIOSystem.h> #include <assimp/DefaultIOSystem.h>
#include <assimp/Importer.hpp> #include <assimp/IOStreamBuffer.h>
#include <assimp/scene.h>
#include <assimp/ai_assert.h> #include <assimp/ai_assert.h>
#include <assimp/DefaultLogger.hpp>
#include <assimp/importerdesc.h> #include <assimp/importerdesc.h>
#include <assimp/scene.h>
#include <assimp/DefaultLogger.hpp>
#include <assimp/Importer.hpp>
#include <memory>
#include "M3DImporter.h" #include "M3DImporter.h"
#include "M3DMaterials.h" #include "M3DMaterials.h"
#include "M3DWrapper.h"
// RESOURCES: // RESOURCES:
// https://gitlab.com/bztsrc/model3d/blob/master/docs/m3d_format.md // https://gitlab.com/bztsrc/model3d/blob/master/docs/m3d_format.md
@ -89,7 +91,7 @@ static const aiImporterDesc desc = {
"", "",
"", "",
"", "",
aiImporterFlags_SupportBinaryFlavour, aiImporterFlags_SupportTextFlavour | aiImporterFlags_SupportBinaryFlavour,
0, 0,
0, 0,
0, 0,
@ -97,53 +99,18 @@ static const aiImporterDesc desc = {
"m3d a3d" "m3d a3d"
}; };
// workaround: the SDK expects a C callback, but we want to use Assimp::IOSystem to implement that
extern "C" {
void* m3dimporter_pIOHandler;
unsigned char *m3dimporter_readfile(char *fn, unsigned int *size) {
ai_assert( nullptr != fn );
ai_assert( nullptr != size );
std::string file(fn);
std::unique_ptr<Assimp::IOStream> pStream(
(reinterpret_cast<Assimp::IOSystem*>(m3dimporter_pIOHandler))->Open( file, "rb"));
size_t fileSize = 0;
unsigned char *data = NULL;
// sometimes pStream is nullptr for some reason (should be an empty object returning nothing I guess)
if(pStream) {
fileSize = pStream->FileSize();
// should be allocated with malloc(), because the library will call free() to deallocate
data = (unsigned char*)malloc(fileSize);
if( !data || !pStream.get() || !fileSize || fileSize != pStream->Read(data,1,fileSize)) {
pStream.reset();
*size = 0;
// don't throw a deadly exception, it's not fatal if we can't read an external asset
return nullptr;
}
pStream.reset();
}
*size = (int)fileSize;
return data;
}
}
namespace Assimp { namespace Assimp {
using namespace std; using namespace std;
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Default constructor // Default constructor
M3DImporter::M3DImporter() M3DImporter::M3DImporter() :
: mScene(nullptr) mScene(nullptr) {}
, m3d(nullptr) { }
// ------------------------------------------------------------------------------------------------
// Destructor.
M3DImporter::~M3DImporter() {}
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Returns true, if file is a binary or ASCII Model 3D file. // Returns true, if file is a binary or ASCII Model 3D file.
bool M3DImporter::CanRead(const std::string& pFile, IOSystem* pIOHandler , bool checkSig) const { bool M3DImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool checkSig) const {
const std::string extension = GetExtension(pFile); const std::string extension = GetExtension(pFile);
if (extension == "m3d" || extension == "a3d") if (extension == "m3d" || extension == "a3d")
@ -159,9 +126,9 @@ bool M3DImporter::CanRead(const std::string& pFile, IOSystem* pIOHandler , bool
const char* tokens[] = {"3DMO", "3dmo"}; const char* tokens[] = {"3DMO", "3dmo"};
return CheckMagicToken(pIOHandler,pFile,tokens,2,0,4); return CheckMagicToken(pIOHandler,pFile,tokens,2,0,4);
*/ */
std::unique_ptr<IOStream> pStream (pIOHandler->Open(pFile, "rb")); std::unique_ptr<IOStream> pStream(pIOHandler->Open(pFile, "rb"));
unsigned char data[4]; unsigned char data[4];
if(4 != pStream->Read(data,1,4)) { if (4 != pStream->Read(data, 1, 4)) {
return false; return false;
} }
return !memcmp(data, "3DMO", 4) /* bin */ || !memcmp(data, "3dmo", 4) /* ASCII */; return !memcmp(data, "3DMO", 4) /* bin */ || !memcmp(data, "3dmo", 4) /* ASCII */;
@ -170,81 +137,75 @@ bool M3DImporter::CanRead(const std::string& pFile, IOSystem* pIOHandler , bool
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
const aiImporterDesc* M3DImporter::GetInfo() const { const aiImporterDesc *M3DImporter::GetInfo() const {
return &desc; return &desc;
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Model 3D import implementation // Model 3D import implementation
void M3DImporter::InternReadFile( const std::string &file, aiScene* pScene, IOSystem* pIOHandler) { void M3DImporter::InternReadFile(const std::string &file, aiScene *pScene, IOSystem *pIOHandler) {
// Read file into memory // Read file into memory
std::unique_ptr<IOStream> pStream( pIOHandler->Open( file, "rb")); std::unique_ptr<IOStream> pStream(pIOHandler->Open(file, "rb"));
if( !pStream.get() ) { if (!pStream.get()) {
throw DeadlyImportError( "Failed to open file " + file + "." ); throw DeadlyImportError("Failed to open file " + file + ".");
} }
// Get the file-size and validate it, throwing an exception when fails // Get the file-size and validate it, throwing an exception when fails
size_t fileSize = pStream->FileSize(); size_t fileSize = pStream->FileSize();
if( fileSize < 8 ) { if (fileSize < 8) {
throw DeadlyImportError( "M3D-file " + file + " is too small." ); throw DeadlyImportError("M3D-file " + file + " is too small.");
} }
std::unique_ptr<unsigned char[]> _buffer (new unsigned char[fileSize]); std::vector<unsigned char> buffer(fileSize);
unsigned char *data( _buffer.get() ); if (fileSize != pStream->Read(buffer.data(), 1, fileSize)) {
if(fileSize != pStream->Read(data,1,fileSize)) { throw DeadlyImportError("Failed to read the file " + file + ".");
throw DeadlyImportError( "Failed to read the file " + file + "." );
} }
// Get the path for external assets // Get the path for external assets
std::string folderName( "./" ); std::string folderName("./");
std::string::size_type pos = file.find_last_of( "\\/" ); std::string::size_type pos = file.find_last_of("\\/");
if ( pos != std::string::npos ) { if (pos != std::string::npos) {
folderName = file.substr( 0, pos ); folderName = file.substr(0, pos);
if ( !folderName.empty() ) { if (!folderName.empty()) {
pIOHandler->PushDirectory( folderName ); pIOHandler->PushDirectory(folderName);
} }
} }
// pass this IOHandler to the C callback
m3dimporter_pIOHandler = pIOHandler;
//DefaultLogger::create("/dev/stderr", Logger::VERBOSE); //DefaultLogger::create("/dev/stderr", Logger::VERBOSE);
ASSIMP_LOG_DEBUG_F("M3D: loading ", file); ASSIMP_LOG_DEBUG_F("M3D: loading ", file);
// let the C SDK do the hard work for us // let the C SDK do the hard work for us
m3d = m3d_load(&data[0], m3dimporter_readfile, free, nullptr); M3DWrapper m3d(pIOHandler, buffer);
m3dimporter_pIOHandler = nullptr;
if( !m3d ) {
throw DeadlyImportError( "Unable to parse " + file + " as M3D." ); if (!m3d) {
throw DeadlyImportError("Unable to parse " + file + " as M3D.");
} }
// create the root node // create the root node
pScene->mRootNode = new aiNode; pScene->mRootNode = new aiNode;
pScene->mRootNode->mName = aiString(std::string(std::string(m3d->name))); pScene->mRootNode->mName = aiString(m3d.Name());
pScene->mRootNode->mTransformation = aiMatrix4x4(); pScene->mRootNode->mTransformation = aiMatrix4x4();
pScene->mRootNode->mNumChildren = 0; pScene->mRootNode->mNumChildren = 0;
mScene = pScene; mScene = pScene;
ASSIMP_LOG_DEBUG("M3D: root node " + std::string(m3d->name)); ASSIMP_LOG_DEBUG("M3D: root node " + m3d.Name());
// now we just have to fill up the Assimp structures in pScene // now we just have to fill up the Assimp structures in pScene
importMaterials(); importMaterials(m3d);
importTextures(); importTextures(m3d);
importBones(-1U, pScene->mRootNode); importBones(m3d, -1U, pScene->mRootNode);
importMeshes(); importMeshes(m3d);
importAnimations(); importAnimations(m3d);
// we don't need the SDK's version any more
m3d_free(m3d);
// Pop directory stack // Pop directory stack
if ( pIOHandler->StackSize() > 0 ) { if (pIOHandler->StackSize() > 0) {
pIOHandler->PopDirectory(); pIOHandler->PopDirectory();
} }
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// convert materials. properties are converted using a static table in M3DMaterials.h // convert materials. properties are converted using a static table in M3DMaterials.h
void M3DImporter::importMaterials() void M3DImporter::importMaterials(const M3DWrapper &m3d_wrap) {
{
unsigned int i, j, k, l, n; unsigned int i, j, k, l, n;
m3dm_t *m; m3dm_t *m;
aiString name = aiString(AI_DEFAULT_MATERIAL_NAME); aiString name = aiString(AI_DEFAULT_MATERIAL_NAME);
@ -252,42 +213,43 @@ void M3DImporter::importMaterials()
ai_real f; ai_real f;
ai_assert(mScene != nullptr); ai_assert(mScene != nullptr);
ai_assert(m3d != nullptr); ai_assert(m3d_wrap);
mScene->mNumMaterials = m3d->nummaterial + 1; mScene->mNumMaterials = m3d_wrap->nummaterial + 1;
mScene->mMaterials = new aiMaterial*[ m3d->nummaterial + 1 ]; mScene->mMaterials = new aiMaterial *[mScene->mNumMaterials];
ASSIMP_LOG_DEBUG_F("M3D: importMaterials ", mScene->mNumMaterials); ASSIMP_LOG_DEBUG_F("M3D: importMaterials ", mScene->mNumMaterials);
// add a default material as first // add a default material as first
aiMaterial* mat = new aiMaterial; aiMaterial *mat = new aiMaterial;
mat->AddProperty( &name, AI_MATKEY_NAME ); mat->AddProperty(&name, AI_MATKEY_NAME);
c.a = 1.0; c.b = c.g = c.r = 0.6; c.a = 1.0f;
mat->AddProperty( &c, 1, AI_MATKEY_COLOR_DIFFUSE); c.b = c.g = c.r = 0.6f;
mat->AddProperty(&c, 1, AI_MATKEY_COLOR_DIFFUSE);
mScene->mMaterials[0] = mat; mScene->mMaterials[0] = mat;
for(i = 0; i < m3d->nummaterial; i++) { for (i = 0; i < m3d_wrap->nummaterial; i++) {
m = &m3d->material[i]; m = &m3d_wrap->material[i];
aiMaterial* mat = new aiMaterial; aiMaterial *mat = new aiMaterial;
name.Set(std::string(m->name)); name.Set(std::string(m->name));
mat->AddProperty( &name, AI_MATKEY_NAME ); mat->AddProperty(&name, AI_MATKEY_NAME);
for(j = 0; j < m->numprop; j++) { for (j = 0; j < m->numprop; j++) {
// look up property type // look up property type
// 0 - 127 scalar values, // 0 - 127 scalar values,
// 128 - 255 the same properties but for texture maps // 128 - 255 the same properties but for texture maps
k = 256; k = 256;
for(l = 0; l < sizeof(m3d_propertytypes)/sizeof(m3d_propertytypes[0]); l++) for (l = 0; l < sizeof(m3d_propertytypes) / sizeof(m3d_propertytypes[0]); l++)
if(m->prop[j].type == m3d_propertytypes[l].id || if (m->prop[j].type == m3d_propertytypes[l].id ||
m->prop[j].type == m3d_propertytypes[l].id + 128) { m->prop[j].type == m3d_propertytypes[l].id + 128) {
k = l; k = l;
break; break;
} }
// should never happen, but be safe than sorry // should never happen, but be safe than sorry
if(k == 256) continue; if (k == 256) continue;
// scalar properties // scalar properties
if(m->prop[j].type < 128 && aiProps[k].pKey) { if (m->prop[j].type < 128 && aiProps[k].pKey) {
switch(m3d_propertytypes[k].format) { switch (m3d_propertytypes[k].format) {
case m3dpf_color: case m3dpf_color:
c = mkColor(m->prop[j].value.color); c = mkColor(m->prop[j].value.color);
mat->AddProperty(&c, 1, aiProps[k].pKey, aiProps[k].type, aiProps[k].index); mat->AddProperty(&c, 1, aiProps[k].pKey, aiProps[k].type, aiProps[k].index);
@ -298,8 +260,8 @@ void M3DImporter::importMaterials()
break; break;
default: default:
n = m->prop[j].value.num; n = m->prop[j].value.num;
if(m->prop[j].type == m3dp_il) { if (m->prop[j].type == m3dp_il) {
switch(n) { switch (n) {
case 0: n = aiShadingMode_NoShading; break; case 0: n = aiShadingMode_NoShading; break;
case 2: n = aiShadingMode_Phong; break; case 2: n = aiShadingMode_Phong; break;
default: n = aiShadingMode_Gouraud; break; default: n = aiShadingMode_Gouraud; break;
@ -310,11 +272,11 @@ void M3DImporter::importMaterials()
} }
} }
// texture map properties // texture map properties
if(m->prop[j].type >= 128 && aiTxProps[k].pKey && if (m->prop[j].type >= 128 && aiTxProps[k].pKey &&
// extra check, should never happen, do we have the refered texture? // extra check, should never happen, do we have the refered texture?
m->prop[j].value.textureid < m3d->numtexture && m->prop[j].value.textureid < m3d_wrap->numtexture &&
m3d->texture[m->prop[j].value.textureid].name) { m3d_wrap->texture[m->prop[j].value.textureid].name) {
name.Set(std::string(std::string(m3d->texture[m->prop[j].value.textureid].name) + ".png")); name.Set(std::string(std::string(m3d_wrap->texture[m->prop[j].value.textureid].name) + ".png"));
mat->AddProperty(&name, aiTxProps[k].pKey, aiTxProps[k].type, aiTxProps[k].index); mat->AddProperty(&name, aiTxProps[k].pKey, aiTxProps[k].type, aiTxProps[k].index);
n = 0; n = 0;
mat->AddProperty(&n, 1, _AI_MATKEY_UVWSRC_BASE, aiProps[k].type, aiProps[k].index); mat->AddProperty(&n, 1, _AI_MATKEY_UVWSRC_BASE, aiProps[k].type, aiProps[k].index);
@ -326,43 +288,49 @@ void M3DImporter::importMaterials()
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// import textures, this is the simplest of all // import textures, this is the simplest of all
void M3DImporter::importTextures() void M3DImporter::importTextures(const M3DWrapper &m3d) {
{
unsigned int i; unsigned int i;
const char *formatHint[] = { "rgba0800", "rgba0808", "rgba8880", "rgba8888" }; const char *formatHint[] = { "rgba0800", "rgba0808", "rgba8880", "rgba8888" };
m3dtx_t *t; m3dtx_t *t;
ai_assert(mScene != nullptr); ai_assert(mScene != nullptr);
ai_assert(m3d != nullptr); ai_assert(m3d);
mScene->mNumTextures = m3d->numtexture; mScene->mNumTextures = m3d->numtexture;
ASSIMP_LOG_DEBUG_F("M3D: importTextures ", mScene->mNumTextures); ASSIMP_LOG_DEBUG_F("M3D: importTextures ", mScene->mNumTextures);
if(!m3d->numtexture) if (!m3d->numtexture)
return; return;
mScene->mTextures = new aiTexture*[m3d->numtexture]; mScene->mTextures = new aiTexture *[m3d->numtexture];
for(i = 0; i < m3d->numtexture; i++) { for (i = 0; i < m3d->numtexture; i++) {
unsigned int j, k; unsigned int j, k;
t = &m3d->texture[i]; t = &m3d->texture[i];
if(!t->w || !t->h || !t->f || !t->d) continue; if (!t->w || !t->h || !t->f || !t->d) continue;
aiTexture *tx = new aiTexture; aiTexture *tx = new aiTexture;
strcpy(tx->achFormatHint, formatHint[t->f - 1]); strcpy(tx->achFormatHint, formatHint[t->f - 1]);
tx->mFilename = aiString(std::string(t->name) + ".png"); tx->mFilename = aiString(std::string(t->name) + ".png");
tx->mWidth = t->w; tx->mWidth = t->w;
tx->mHeight = t->h; tx->mHeight = t->h;
tx->pcData = new aiTexel[ tx->mWidth*tx->mHeight ]; tx->pcData = new aiTexel[tx->mWidth * tx->mHeight];
for(j = k = 0; j < tx->mWidth*tx->mHeight; j++) { for (j = k = 0; j < tx->mWidth * tx->mHeight; j++) {
switch(t->f) { switch (t->f) {
case 1: tx->pcData[j].g = t->d[k++]; break; 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 2:
tx->pcData[j].g = t->d[k++];
tx->pcData[j].a = t->d[k++];
break;
case 3: case 3:
tx->pcData[j].r = t->d[k++]; tx->pcData[j].g = t->d[k++]; tx->pcData[j].r = t->d[k++];
tx->pcData[j].b = t->d[k++]; tx->pcData[j].a = 255; tx->pcData[j].g = t->d[k++];
tx->pcData[j].b = t->d[k++];
tx->pcData[j].a = 255;
break; break;
case 4: case 4:
tx->pcData[j].r = t->d[k++]; tx->pcData[j].g = t->d[k++]; tx->pcData[j].r = t->d[k++];
tx->pcData[j].b = t->d[k++]; tx->pcData[j].a = 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; break;
} }
} }
@ -374,10 +342,9 @@ void M3DImporter::importTextures()
// this is tricky. M3D has a global vertex and UV list, and faces are indexing them // 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 // individually. In assimp there're per mesh vertex and UV lists, and they must be
// indexed simultaneously. // indexed simultaneously.
void M3DImporter::importMeshes() void M3DImporter::importMeshes(const M3DWrapper &m3d) {
{
unsigned int i, j, k, l, numpoly = 3, lastMat = -2U; unsigned int i, j, k, l, numpoly = 3, lastMat = -2U;
std::vector<aiMesh*> *meshes = new std::vector<aiMesh*>(); std::vector<aiMesh *> *meshes = new std::vector<aiMesh *>();
std::vector<aiFace> *faces = nullptr; std::vector<aiFace> *faces = nullptr;
std::vector<aiVector3D> *vertices = nullptr; std::vector<aiVector3D> *vertices = nullptr;
std::vector<aiVector3D> *normals = nullptr; std::vector<aiVector3D> *normals = nullptr;
@ -387,17 +354,17 @@ void M3DImporter::importMeshes()
aiMesh *pMesh = nullptr; aiMesh *pMesh = nullptr;
ai_assert(mScene != nullptr); ai_assert(mScene != nullptr);
ai_assert(m3d != nullptr); ai_assert(m3d);
ai_assert(mScene->mRootNode != nullptr); ai_assert(mScene->mRootNode != nullptr);
ASSIMP_LOG_DEBUG_F("M3D: importMeshes ", m3d->numface); ASSIMP_LOG_DEBUG_F("M3D: importMeshes ", m3d->numface);
for(i = 0; i < m3d->numface; i++) { for (i = 0; i < m3d->numface; i++) {
// we must switch mesh if material changes // we must switch mesh if material changes
if(lastMat != m3d->face[i].materialid) { if (lastMat != m3d->face[i].materialid) {
lastMat = m3d->face[i].materialid; lastMat = m3d->face[i].materialid;
if(pMesh && vertices && vertices->size() && faces && faces->size()) { if (pMesh && vertices && vertices->size() && faces && faces->size()) {
populateMesh(pMesh, faces, vertices, normals, texcoords, colors, vertexids); populateMesh(m3d, pMesh, faces, vertices, normals, texcoords, colors, vertexids);
meshes->push_back(pMesh); meshes->push_back(pMesh);
delete faces; delete faces;
delete vertices; delete vertices;
@ -420,9 +387,9 @@ void M3DImporter::importMeshes()
aiFace *pFace = new aiFace; aiFace *pFace = new aiFace;
pFace->mNumIndices = numpoly; pFace->mNumIndices = numpoly;
pFace->mIndices = new unsigned int[numpoly]; pFace->mIndices = new unsigned int[numpoly];
for(j = 0; j < numpoly; j++) { for (j = 0; j < numpoly; j++) {
aiVector3D pos, uv, norm; aiVector3D pos, uv, norm;
k = vertices->size(); k = static_cast<unsigned int>(vertices->size());
pFace->mIndices[j] = k; pFace->mIndices[j] = k;
l = m3d->face[i].vertex[j]; l = m3d->face[i].vertex[j];
pos.x = m3d->vertex[l].x; pos.x = m3d->vertex[l].x;
@ -431,20 +398,20 @@ void M3DImporter::importMeshes()
vertices->push_back(pos); vertices->push_back(pos);
colors->push_back(mkColor(m3d->vertex[l].color)); colors->push_back(mkColor(m3d->vertex[l].color));
// add a bone to temporary vector // add a bone to temporary vector
if(m3d->vertex[l].skinid != -1U &&m3d->vertex[l].skinid != -2U && m3d->skin && m3d->bone) { if (m3d->vertex[l].skinid != -1U && m3d->vertex[l].skinid != -2U && m3d->skin && m3d->bone) {
// this is complicated, because M3D stores a list of bone id / weight pairs per // 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 // vertex but assimp uses lists of local vertex id/weight pairs per local bone list
vertexids->push_back(l); vertexids->push_back(l);
} }
l = m3d->face[i].texcoord[j]; l = m3d->face[i].texcoord[j];
if(l != -1U) { if (l != -1U) {
uv.x = m3d->tmap[l].u; uv.x = m3d->tmap[l].u;
uv.y = m3d->tmap[l].v; uv.y = m3d->tmap[l].v;
uv.z = 0.0; uv.z = 0.0;
texcoords->push_back(uv); texcoords->push_back(uv);
} }
l = m3d->face[i].normal[j]; l = m3d->face[i].normal[j];
if(l != -1U) { if (l != -1U) {
norm.x = m3d->vertex[l].x; norm.x = m3d->vertex[l].x;
norm.y = m3d->vertex[l].y; norm.y = m3d->vertex[l].y;
norm.z = m3d->vertex[l].z; norm.z = m3d->vertex[l].z;
@ -455,58 +422,57 @@ void M3DImporter::importMeshes()
delete pFace; delete pFace;
} }
// if there's data left in the temporary vectors, flush them // if there's data left in the temporary vectors, flush them
if(pMesh && vertices->size() && faces->size()) { if (pMesh && vertices->size() && faces->size()) {
populateMesh(pMesh, faces, vertices, normals, texcoords, colors, vertexids); populateMesh(m3d, pMesh, faces, vertices, normals, texcoords, colors, vertexids);
meshes->push_back(pMesh); meshes->push_back(pMesh);
} }
// create global mesh list in scene // create global mesh list in scene
mScene->mNumMeshes = meshes->size(); mScene->mNumMeshes = static_cast<unsigned int>(meshes->size());
mScene->mMeshes = new aiMesh*[mScene->mNumMeshes]; mScene->mMeshes = new aiMesh *[mScene->mNumMeshes];
std::copy(meshes->begin(), meshes->end(), mScene->mMeshes); std::copy(meshes->begin(), meshes->end(), mScene->mMeshes);
// create mesh indeces in root node // create mesh indeces in root node
mScene->mRootNode->mNumMeshes = meshes->size(); mScene->mRootNode->mNumMeshes = static_cast<unsigned int>(meshes->size());
mScene->mRootNode->mMeshes = new unsigned int[meshes->size()]; mScene->mRootNode->mMeshes = new unsigned int[meshes->size()];
for(i = 0; i < meshes->size(); i++) { for (i = 0; i < meshes->size(); i++) {
mScene->mRootNode->mMeshes[i] = i; mScene->mRootNode->mMeshes[i] = i;
} }
delete meshes; delete meshes;
if(faces) delete faces; if (faces) delete faces;
if(vertices) delete vertices; if (vertices) delete vertices;
if(normals) delete normals; if (normals) delete normals;
if(texcoords) delete texcoords; if (texcoords) delete texcoords;
if(colors) delete colors; if (colors) delete colors;
if(vertexids) delete vertexids; if (vertexids) delete vertexids;
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// a reentrant node parser. Otherwise this is simple // a reentrant node parser. Otherwise this is simple
void M3DImporter::importBones(unsigned int parentid, aiNode *pParent) void M3DImporter::importBones(const M3DWrapper &m3d, unsigned int parentid, aiNode *pParent) {
{
unsigned int i, n; unsigned int i, n;
ai_assert(pParent != nullptr); ai_assert(pParent != nullptr);
ai_assert(mScene != nullptr); ai_assert(mScene != nullptr);
ai_assert(m3d != nullptr); ai_assert(m3d);
ASSIMP_LOG_DEBUG_F("M3D: importBones ", m3d->numbone, " parentid ", (int)parentid); ASSIMP_LOG_DEBUG_F("M3D: importBones ", m3d->numbone, " parentid ", (int)parentid);
for(n = 0, i = parentid + 1; i < m3d->numbone; i++) for (n = 0, i = parentid + 1; i < m3d->numbone; i++)
if(m3d->bone[i].parent == parentid) n++; if (m3d->bone[i].parent == parentid) n++;
pParent->mChildren = new aiNode*[n]; pParent->mChildren = new aiNode *[n];
for(i = parentid + 1; i < m3d->numbone; i++) { for (i = parentid + 1; i < m3d->numbone; i++) {
if(m3d->bone[i].parent == parentid) { if (m3d->bone[i].parent == parentid) {
aiNode *pChild = new aiNode; aiNode *pChild = new aiNode;
pChild->mParent = pParent; pChild->mParent = pParent;
pChild->mName = aiString(std::string(m3d->bone[i].name)); pChild->mName = aiString(std::string(m3d->bone[i].name));
convertPose(&pChild->mTransformation, m3d->bone[i].pos, m3d->bone[i].ori); convertPose(m3d, &pChild->mTransformation, m3d->bone[i].pos, m3d->bone[i].ori);
pChild->mNumChildren = 0; pChild->mNumChildren = 0;
pParent->mChildren[pParent->mNumChildren] = pChild; pParent->mChildren[pParent->mNumChildren] = pChild;
pParent->mNumChildren++; pParent->mNumChildren++;
importBones(i, pChild); importBones(m3d, i, pChild);
} }
} }
} }
@ -515,24 +481,23 @@ void M3DImporter::importBones(unsigned int parentid, aiNode *pParent)
// this is another headache. M3D stores list of changed bone id/position/orientation triplets and // 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 // 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 // bone, so we have to convert between the two conceptually different representation forms
void M3DImporter::importAnimations() void M3DImporter::importAnimations(const M3DWrapper &m3d) {
{
unsigned int i, j, k, l, pos, ori; unsigned int i, j, k, l, pos, ori;
double t; double t;
m3da_t *a; m3da_t *a;
ai_assert(mScene != nullptr); ai_assert(mScene != nullptr);
ai_assert(m3d != nullptr); ai_assert(m3d);
mScene->mNumAnimations = m3d->numaction; mScene->mNumAnimations = m3d->numaction;
ASSIMP_LOG_DEBUG_F("M3D: importAnimations ", mScene->mNumAnimations); ASSIMP_LOG_DEBUG_F("M3D: importAnimations ", mScene->mNumAnimations);
if(!m3d->numaction || !m3d->numbone) if (!m3d->numaction || !m3d->numbone)
return; return;
mScene->mAnimations = new aiAnimation*[m3d->numaction]; mScene->mAnimations = new aiAnimation *[m3d->numaction];
for(i = 0; i < m3d->numaction; i++) { for (i = 0; i < m3d->numaction; i++) {
a = &m3d->action[i]; a = &m3d->action[i];
aiAnimation *pAnim = new aiAnimation; aiAnimation *pAnim = new aiAnimation;
pAnim->mName = aiString(std::string(a->name)); pAnim->mName = aiString(std::string(a->name));
@ -540,8 +505,8 @@ void M3DImporter::importAnimations()
pAnim->mTicksPerSecond = 100; pAnim->mTicksPerSecond = 100;
// now we know how many bones are referenced in this animation // now we know how many bones are referenced in this animation
pAnim->mNumChannels = m3d->numbone; pAnim->mNumChannels = m3d->numbone;
pAnim->mChannels = new aiNodeAnim*[pAnim->mNumChannels]; pAnim->mChannels = new aiNodeAnim *[pAnim->mNumChannels];
for(l = 0; l < m3d->numbone; l++) { for (l = 0; l < m3d->numbone; l++) {
unsigned int n; unsigned int n;
pAnim->mChannels[l] = new aiNodeAnim; pAnim->mChannels[l] = new aiNodeAnim;
pAnim->mChannels[l]->mNodeName = aiString(std::string(m3d->bone[l].name)); pAnim->mChannels[l]->mNodeName = aiString(std::string(m3d->bone[l].name));
@ -551,10 +516,10 @@ void M3DImporter::importAnimations()
pAnim->mChannels[l]->mRotationKeys = new aiQuatKey[a->numframe]; pAnim->mChannels[l]->mRotationKeys = new aiQuatKey[a->numframe];
pos = m3d->bone[l].pos; pos = m3d->bone[l].pos;
ori = m3d->bone[l].ori; ori = m3d->bone[l].ori;
for(j = n = 0; j < a->numframe; j++) { for (j = n = 0; j < a->numframe; j++) {
t = ((double)a->frame[j].msec) / 10; t = ((double)a->frame[j].msec) / 10;
for(k = 0; k < a->frame[j].numtransform; k++) { for (k = 0; k < a->frame[j].numtransform; k++) {
if(a->frame[j].transform[k].boneid == l) { if (a->frame[j].transform[k].boneid == l) {
pos = a->frame[j].transform[k].pos; pos = a->frame[j].transform[k].pos;
ori = a->frame[j].transform[k].ori; ori = a->frame[j].transform[k].ori;
} }
@ -570,8 +535,8 @@ void M3DImporter::importAnimations()
pAnim->mChannels[l]->mRotationKeys[j].mValue.x = q->x; pAnim->mChannels[l]->mRotationKeys[j].mValue.x = q->x;
pAnim->mChannels[l]->mRotationKeys[j].mValue.y = q->y; pAnim->mChannels[l]->mRotationKeys[j].mValue.y = q->y;
pAnim->mChannels[l]->mRotationKeys[j].mValue.z = q->z; pAnim->mChannels[l]->mRotationKeys[j].mValue.z = q->z;
}// foreach frame } // foreach frame
}// foreach bones } // foreach bones
mScene->mAnimations[i] = pAnim; mScene->mAnimations[i] = pAnim;
} }
} }
@ -580,72 +545,83 @@ void M3DImporter::importAnimations()
// convert uint32_t into aiColor4D // convert uint32_t into aiColor4D
aiColor4D M3DImporter::mkColor(uint32_t c) { aiColor4D M3DImporter::mkColor(uint32_t c) {
aiColor4D color; aiColor4D color;
color.a = ((float)((c >> 24)&0xff)) / 255; color.a = ((float)((c >> 24) & 0xff)) / 255;
color.b = ((float)((c >> 16)&0xff)) / 255; color.b = ((float)((c >> 16) & 0xff)) / 255;
color.g = ((float)((c >> 8)&0xff)) / 255; color.g = ((float)((c >> 8) & 0xff)) / 255;
color.r = ((float)((c >> 0)&0xff)) / 255; color.r = ((float)((c >> 0) & 0xff)) / 255;
return color; return color;
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// convert a position id and orientation id into a 4 x 4 transformation matrix // convert a position id and orientation id into a 4 x 4 transformation matrix
void M3DImporter::convertPose(aiMatrix4x4 *m, unsigned int posid, unsigned int orientid) void M3DImporter::convertPose(const M3DWrapper &m3d, aiMatrix4x4 *m, unsigned int posid, unsigned int orientid) {
{
ai_assert(m != nullptr); ai_assert(m != nullptr);
ai_assert(m3d != nullptr); ai_assert(m3d);
ai_assert(posid != -1U && posid < m3d->numvertex); ai_assert(posid != -1U && posid < m3d->numvertex);
ai_assert(orientid != -1U && orientid < m3d->numvertex); ai_assert(orientid != -1U && orientid < m3d->numvertex);
m3dv_t *p = &m3d->vertex[posid]; m3dv_t *p = &m3d->vertex[posid];
m3dv_t *q = &m3d->vertex[orientid]; m3dv_t *q = &m3d->vertex[orientid];
/* quaternion to matrix. Do NOT use aiQuaternion to aiMatrix3x3, gives bad results */ /* 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) { 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->a2 = m->a3 = m->b1 = m->b3 = m->c1 = m->c2 = 0.0;
m->a1 = m->b2 = m->c3 = -1.0; m->a1 = m->b2 = m->c3 = -1.0;
} else { } 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->a1 = 1 - 2 * (q->y * q->y + q->z * q->z);
m->a2 = 2 * (q->x * q->y - q->z * q->w); if(m->a2 > -M3D_EPSILON && m->a2 < M3D_EPSILON) m->a2 = 0.0; if (m->a1 > -M3D_EPSILON && m->a1 < M3D_EPSILON) m->a1 = 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->a2 = 2 * (q->x * q->y - q->z * q->w);
m->b1 = 2 * (q->x * q->y + q->z * q->w); if(m->b1 > -M3D_EPSILON && m->b1 < M3D_EPSILON) m->b1 = 0.0; if (m->a2 > -M3D_EPSILON && m->a2 < M3D_EPSILON) m->a2 = 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->a3 = 2 * (q->x * q->z + q->y * q->w);
m->b3 = 2 * (q->y * q->z - q->x * q->w); if(m->b3 > -M3D_EPSILON && m->b3 < M3D_EPSILON) m->b3 = 0.0; if (m->a3 > -M3D_EPSILON && m->a3 < M3D_EPSILON) m->a3 = 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->b1 = 2 * (q->x * q->y + q->z * q->w);
m->c2 = 2 * (q->y * q->z + q->x * q->w); if(m->c2 > -M3D_EPSILON && m->c2 < M3D_EPSILON) m->c2 = 0.0; if (m->b1 > -M3D_EPSILON && m->b1 < M3D_EPSILON) m->b1 = 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; 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 */ /* set translation */
m->a4 = p->x; m->b4 = p->y; m->c4 = p->z; m->a4 = p->x;
m->b4 = p->y;
m->c4 = p->z;
m->d1 = 0; m->d2 = 0; m->d3 = 0; m->d4 = 1; m->d1 = 0;
m->d2 = 0;
m->d3 = 0;
m->d4 = 1;
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// find a node by name // find a node by name
aiNode *M3DImporter::findNode(aiNode *pNode, aiString name) aiNode *M3DImporter::findNode(aiNode *pNode, aiString name) {
{
unsigned int i; unsigned int i;
ai_assert(pNode != nullptr); ai_assert(pNode != nullptr);
ai_assert(mScene != nullptr); ai_assert(mScene != nullptr);
if(pNode->mName == name) if (pNode->mName == name)
return pNode; return pNode;
for(i = 0; i < pNode->mNumChildren; i++) { for (i = 0; i < pNode->mNumChildren; i++) {
aiNode *pChild = findNode(pNode->mChildren[i], name); aiNode *pChild = findNode(pNode->mChildren[i], name);
if(pChild) return pChild; if (pChild) return pChild;
} }
return nullptr; return nullptr;
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// fills up offsetmatrix in mBones // fills up offsetmatrix in mBones
void M3DImporter::calculateOffsetMatrix(aiNode *pNode, aiMatrix4x4 *m) void M3DImporter::calculateOffsetMatrix(aiNode *pNode, aiMatrix4x4 *m) {
{
ai_assert(pNode != nullptr); ai_assert(pNode != nullptr);
ai_assert(mScene != nullptr); ai_assert(mScene != nullptr);
if(pNode->mParent) { if (pNode->mParent) {
calculateOffsetMatrix(pNode->mParent, m); calculateOffsetMatrix(pNode->mParent, m);
*m *= pNode->mTransformation; *m *= pNode->mTransformation;
} else { } else {
@ -657,7 +633,7 @@ void M3DImporter::calculateOffsetMatrix(aiNode *pNode, aiMatrix4x4 *m)
// because M3D has a global mesh, global vertex ids and stores materialid on the face, we need // 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 indeces // temporary lists to collect data for an aiMesh, which requires local arrays and local indeces
// this function fills up an aiMesh with those temporary lists // this function fills up an aiMesh with those temporary lists
void M3DImporter::populateMesh(aiMesh *pMesh, std::vector<aiFace> *faces, std::vector<aiVector3D> *vertices, 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<aiVector3D> *normals, std::vector<aiVector3D> *texcoords, std::vector<aiColor4D> *colors,
std::vector<unsigned int> *vertexids) { std::vector<unsigned int> *vertexids) {
@ -668,28 +644,28 @@ void M3DImporter::populateMesh(aiMesh *pMesh, std::vector<aiFace> *faces, std::v
ai_assert(texcoords != nullptr); ai_assert(texcoords != nullptr);
ai_assert(colors != nullptr); ai_assert(colors != nullptr);
ai_assert(vertexids != nullptr); ai_assert(vertexids != nullptr);
ai_assert(m3d != nullptr); ai_assert(m3d);
ASSIMP_LOG_DEBUG_F("M3D: populateMesh numvertices ", vertices->size(), " numfaces ", faces->size(), ASSIMP_LOG_DEBUG_F("M3D: populateMesh numvertices ", vertices->size(), " numfaces ", faces->size(),
" numnormals ", normals->size(), " numtexcoord ", texcoords->size(), " numbones ", m3d->numbone); " numnormals ", normals->size(), " numtexcoord ", texcoords->size(), " numbones ", m3d->numbone);
if(vertices->size() && faces->size()) { if (vertices->size() && faces->size()) {
pMesh->mNumFaces = faces->size(); pMesh->mNumFaces = static_cast<unsigned int>(faces->size());
pMesh->mFaces = new aiFace[pMesh->mNumFaces]; pMesh->mFaces = new aiFace[pMesh->mNumFaces];
std::copy(faces->begin(), faces->end(), pMesh->mFaces); std::copy(faces->begin(), faces->end(), pMesh->mFaces);
pMesh->mNumVertices = vertices->size(); pMesh->mNumVertices = static_cast<unsigned int>(vertices->size());
pMesh->mVertices = new aiVector3D[pMesh->mNumVertices]; pMesh->mVertices = new aiVector3D[pMesh->mNumVertices];
std::copy(vertices->begin(), vertices->end(), pMesh->mVertices); std::copy(vertices->begin(), vertices->end(), pMesh->mVertices);
if(normals->size() == vertices->size()) { if (normals->size() == vertices->size()) {
pMesh->mNormals = new aiVector3D[pMesh->mNumVertices]; pMesh->mNormals = new aiVector3D[pMesh->mNumVertices];
std::copy(normals->begin(), normals->end(), pMesh->mNormals); std::copy(normals->begin(), normals->end(), pMesh->mNormals);
} }
if(texcoords->size() == vertices->size()) { if (texcoords->size() == vertices->size()) {
pMesh->mTextureCoords[0] = new aiVector3D[pMesh->mNumVertices]; pMesh->mTextureCoords[0] = new aiVector3D[pMesh->mNumVertices];
std::copy(texcoords->begin(), texcoords->end(), pMesh->mTextureCoords[0]); std::copy(texcoords->begin(), texcoords->end(), pMesh->mTextureCoords[0]);
pMesh->mNumUVComponents[0] = 2; pMesh->mNumUVComponents[0] = 2;
} }
if(colors->size() == vertices->size()) { if (colors->size() == vertices->size()) {
pMesh->mColors[0] = new aiColor4D[pMesh->mNumVertices]; pMesh->mColors[0] = new aiColor4D[pMesh->mNumVertices];
std::copy(colors->begin(), colors->end(), pMesh->mColors[0]); std::copy(colors->begin(), colors->end(), pMesh->mColors[0]);
} }
@ -697,30 +673,30 @@ void M3DImporter::populateMesh(aiMesh *pMesh, std::vector<aiFace> *faces, std::v
// vertex but assimp uses lists of local vertex id/weight pairs per local bone list // vertex but assimp uses lists of local vertex id/weight pairs per local bone list
pMesh->mNumBones = m3d->numbone; pMesh->mNumBones = m3d->numbone;
/* we need aiBone with mOffsetMatrix for bones without weights as well */ /* we need aiBone with mOffsetMatrix for bones without weights as well */
if(pMesh->mNumBones) { if (pMesh->mNumBones) {
pMesh->mBones = new aiBone*[pMesh->mNumBones]; pMesh->mBones = new aiBone *[pMesh->mNumBones];
for(unsigned int i = 0; i < m3d->numbone; i++) { for (unsigned int i = 0; i < m3d->numbone; i++) {
aiNode *pNode; aiNode *pNode;
pMesh->mBones[i] = new aiBone; pMesh->mBones[i] = new aiBone;
pMesh->mBones[i]->mName = aiString(std::string(m3d->bone[i].name)); pMesh->mBones[i]->mName = aiString(std::string(m3d->bone[i].name));
pMesh->mBones[i]->mNumWeights = 0; pMesh->mBones[i]->mNumWeights = 0;
pNode = findNode(mScene->mRootNode, pMesh->mBones[i]->mName); pNode = findNode(mScene->mRootNode, pMesh->mBones[i]->mName);
if(pNode) { if (pNode) {
calculateOffsetMatrix(pNode, &pMesh->mBones[i]->mOffsetMatrix); calculateOffsetMatrix(pNode, &pMesh->mBones[i]->mOffsetMatrix);
pMesh->mBones[i]->mOffsetMatrix.Inverse(); pMesh->mBones[i]->mOffsetMatrix.Inverse();
} else } else
pMesh->mBones[i]->mOffsetMatrix = aiMatrix4x4(); pMesh->mBones[i]->mOffsetMatrix = aiMatrix4x4();
} }
if(vertexids->size()) { if (vertexids->size()) {
unsigned int i, j; unsigned int i, j;
// first count how many vertices we have per bone // first count how many vertices we have per bone
for(i = 0; i < vertexids->size(); i++) { for (i = 0; i < vertexids->size(); i++) {
unsigned int s = m3d->vertex[vertexids->at(i)].skinid; unsigned int s = m3d->vertex[vertexids->at(i)].skinid;
if(s != -1U && s!= -2U) { if (s != -1U && s != -2U) {
for(unsigned int k = 0; k < M3D_NUMBONE && m3d->skin[s].weight[k] > 0.0; k++) { 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)); aiString name = aiString(std::string(m3d->bone[m3d->skin[s].boneid[k]].name));
for(j = 0; j < pMesh->mNumBones; j++) { for (j = 0; j < pMesh->mNumBones; j++) {
if(pMesh->mBones[j]->mName == name) { if (pMesh->mBones[j]->mName == name) {
pMesh->mBones[j]->mNumWeights++; pMesh->mBones[j]->mNumWeights++;
break; break;
} }
@ -729,21 +705,21 @@ void M3DImporter::populateMesh(aiMesh *pMesh, std::vector<aiFace> *faces, std::v
} }
} }
// allocate mWeights // allocate mWeights
for(j = 0; j < pMesh->mNumBones; j++) { for (j = 0; j < pMesh->mNumBones; j++) {
aiBone *pBone = pMesh->mBones[j]; aiBone *pBone = pMesh->mBones[j];
if(pBone->mNumWeights) { if (pBone->mNumWeights) {
pBone->mWeights = new aiVertexWeight[pBone->mNumWeights]; pBone->mWeights = new aiVertexWeight[pBone->mNumWeights];
pBone->mNumWeights = 0; pBone->mNumWeights = 0;
} }
} }
// fill up with data // fill up with data
for(i = 0; i < vertexids->size(); i++) { for (i = 0; i < vertexids->size(); i++) {
unsigned int s = m3d->vertex[vertexids->at(i)].skinid; unsigned int s = m3d->vertex[vertexids->at(i)].skinid;
if(s != -1U && s!= -2U) { if (s != -1U && s != -2U) {
for(unsigned int k = 0; k < M3D_NUMBONE && m3d->skin[s].weight[k] > 0.0; k++) { 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)); aiString name = aiString(std::string(m3d->bone[m3d->skin[s].boneid[k]].name));
for(j = 0; j < pMesh->mNumBones; j++) { for (j = 0; j < pMesh->mNumBones; j++) {
if(pMesh->mBones[j]->mName == name) { if (pMesh->mBones[j]->mName == name) {
aiBone *pBone = pMesh->mBones[j]; aiBone *pBone = pMesh->mBones[j];
pBone->mWeights[pBone->mNumWeights].mVertexId = i; pBone->mWeights[pBone->mNumWeights].mVertexId = i;
pBone->mWeights[pBone->mNumWeights].mWeight = m3d->skin[s].weight[k]; pBone->mWeights[pBone->mNumWeights].mWeight = m3d->skin[s].weight[k];

View File

@ -48,7 +48,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef ASSIMP_BUILD_NO_M3D_IMPORTER #ifndef ASSIMP_BUILD_NO_M3D_IMPORTER
#include "m3d.h"
#include <assimp/BaseImporter.h> #include <assimp/BaseImporter.h>
#include <assimp/material.h> #include <assimp/material.h>
#include <vector> #include <vector>
@ -60,41 +59,39 @@ struct aiFace;
namespace Assimp { namespace Assimp {
class M3DWrapper;
class M3DImporter : public BaseImporter { class M3DImporter : public BaseImporter {
public: public:
/// \brief Default constructor /// \brief Default constructor
M3DImporter(); M3DImporter();
/// \brief Destructor
~M3DImporter();
public: public:
/// \brief Returns whether the class can handle the format of the given file. /// \brief Returns whether the class can handle the format of the given file.
/// \remark See BaseImporter::CanRead() for details. /// \remark See BaseImporter::CanRead() for details.
bool CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const; bool CanRead(const std::string &pFile, IOSystem *pIOHandler, bool checkSig) const;
private: private:
aiScene* mScene; // the scene to import to aiScene *mScene = nullptr; // the scene to import to
m3d_t *m3d; // model for the C library to convert from
//! \brief Appends the supported extension. //! \brief Appends the supported extension.
const aiImporterDesc* GetInfo () const; const aiImporterDesc *GetInfo() const;
//! \brief File import implementation. //! \brief File import implementation.
void InternReadFile(const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler); void InternReadFile(const std::string &pFile, aiScene *pScene, IOSystem *pIOHandler);
void importMaterials(); void importMaterials(const M3DWrapper &m3d);
void importTextures(); void importTextures(const M3DWrapper &m3d);
void importMeshes(); void importMeshes(const M3DWrapper &m3d);
void importBones(unsigned int parentid, aiNode *pParent); void importBones(const M3DWrapper &m3d, unsigned int parentid, aiNode *pParent);
void importAnimations(); void importAnimations(const M3DWrapper &m3d);
// helper functions // helper functions
aiColor4D mkColor(uint32_t c); aiColor4D mkColor(uint32_t c);
void convertPose(aiMatrix4x4 *m, unsigned int posid, unsigned int orientid); void convertPose(const M3DWrapper &m3d, aiMatrix4x4 *m, unsigned int posid, unsigned int orientid);
aiNode *findNode(aiNode *pNode, aiString name); aiNode *findNode(aiNode *pNode, aiString name);
void calculateOffsetMatrix(aiNode *pNode, aiMatrix4x4 *m); void calculateOffsetMatrix(aiNode *pNode, aiMatrix4x4 *m);
void populateMesh(aiMesh *pMesh, std::vector<aiFace> *faces, std::vector<aiVector3D> *verteces, void populateMesh(const M3DWrapper &m3d, aiMesh *pMesh, std::vector<aiFace> *faces, std::vector<aiVector3D> *verteces,
std::vector<aiVector3D> *normals, std::vector<aiVector3D> *texcoords, std::vector<aiColor4D> *colors, std::vector<aiVector3D> *normals, std::vector<aiVector3D> *texcoords, std::vector<aiColor4D> *colors,
std::vector<unsigned int> *vertexids); std::vector<unsigned int> *vertexids);
}; };

View File

@ -0,0 +1,136 @@
/*
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2019, 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.
----------------------------------------------------------------------
*/
#if !(ASSIMP_BUILD_NO_EXPORT || ASSIMP_BUILD_NO_M3D_EXPORTER) && !ASSIMP_BUILD_NO_M3D_IMPORTER
#include "M3DWrapper.h"
#include <assimp/DefaultIOSystem.h>
#include <assimp/IOStreamBuffer.h>
#include <assimp/ai_assert.h>
#if (__cplusplus >= 201103L) || (_MSC_VER >= 1915) || defined(AI_M3D_USE_STDMUTEX) // C++11 and MSVC that mostly supports it
#define AI_M3D_USE_STDMUTEX
#include <mutex>
#endif
// workaround: the M3D SDK expects a C callback, but we want to use Assimp::IOSystem to implement that
// This makes it non-rentrant so lock a mutex (requires C++11)
std::mutex file_mutex;
extern "C" {
void *m3dimporter_pIOHandler;
unsigned char *m3dimporter_readfile(char *fn, unsigned int *size) {
ai_assert(nullptr != fn);
ai_assert(nullptr != size);
std::string file(fn);
std::unique_ptr<Assimp::IOStream> pStream(
(reinterpret_cast<Assimp::IOSystem *>(m3dimporter_pIOHandler))->Open(file, "rb"));
size_t fileSize = 0;
unsigned char *data = NULL;
// sometimes pStream is nullptr for some reason (should be an empty object returning nothing I guess)
if (pStream) {
fileSize = pStream->FileSize();
// should be allocated with malloc(), because the library will call free() to deallocate
data = (unsigned char *)malloc(fileSize);
if (!data || !pStream.get() || !fileSize || fileSize != pStream->Read(data, 1, fileSize)) {
pStream.reset();
*size = 0;
// don't throw a deadly exception, it's not fatal if we can't read an external asset
return nullptr;
}
pStream.reset();
}
*size = (int)fileSize;
return data;
}
}
namespace Assimp {
M3DWrapper::M3DWrapper() {
// use malloc() here because m3d_free() will call free()
m3d_ = (m3d_t *)calloc(1, sizeof(m3d_t));
}
M3DWrapper::M3DWrapper(IOSystem *pIOHandler, const std::vector<unsigned char> &buffer) {
#ifdef AI_M3D_USE_STDMUTEX
// M3D is NOT thread-safe, so lock the global mutex
const std::lock_guard<std::mutex> lock(file_mutex);
#endif
// pass this IOHandler to the C callback
m3dimporter_pIOHandler = pIOHandler;
m3d_ = m3d_load(const_cast<unsigned char *>(buffer.data()), m3dimporter_readfile, free, nullptr);
// Clear the C callback
m3dimporter_pIOHandler = nullptr;
}
M3DWrapper::~M3DWrapper() {
reset();
}
void M3DWrapper::reset() {
ClearSave();
if (m3d_)
m3d_free(m3d_);
m3d_ = nullptr;
}
unsigned char *M3DWrapper::Save(int quality, int flags, unsigned int &size) {
#if (!(ASSIMP_BUILD_NO_EXPORT || ASSIMP_BUILD_NO_M3D_EXPORTER))
ClearSave();
saved_output_ = m3d_save(m3d_, quality, flags, &size);
return saved_output_;
#else
return nullptr;
#endif
}
void M3DWrapper::ClearSave() {
if (saved_output_)
M3D_FREE(saved_output_);
saved_output_ = nullptr;
}
} // namespace Assimp
#endif

View File

@ -0,0 +1,96 @@
#pragma once
/*
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2019, 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.
----------------------------------------------------------------------
*/
/** @file M3DWrapper.h
* @brief Declares a class to wrap the C m3d SDK
*/
#ifndef AI_M3DWRAPPER_H_INC
#define AI_M3DWRAPPER_H_INC
#if !(ASSIMP_BUILD_NO_EXPORT || ASSIMP_BUILD_NO_M3D_EXPORTER) && !ASSIMP_BUILD_NO_M3D_IMPORTER
#include <memory>
#include <vector>
#include "m3d.h"
namespace Assimp {
class IOSystem;
class M3DWrapper {
m3d_t *m3d_ = nullptr;
unsigned char *saved_output_ = nullptr;
public:
// Construct an empty M3D model
explicit M3DWrapper();
// Construct an M3D model from provided buffer
// NOTE: The m3d.h SDK function does not mark the data as const. Have assumed it does not write.
// BUG: SECURITY: The m3d.h SDK cannot be informed of the buffer size. BUFFER OVERFLOW IS CERTAIN
explicit M3DWrapper(IOSystem *pIOHandler, const std::vector<unsigned char> &buffer);
~M3DWrapper();
void reset();
// Name
inline std::string Name() const {
if (m3d_) return std::string(m3d_->name);
return std::string();
}
// Execute a save
unsigned char *Save(int quality, int flags, unsigned int &size);
void ClearSave();
inline explicit operator bool() const { return m3d_ != nullptr; }
// Allow direct access to M3D API
inline m3d_t *operator->() const { return m3d_; }
inline m3d_t *M3D() const { return m3d_; }
};
} // namespace Assimp
#endif
#endif // AI_M3DWRAPPER_H_INC