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,7 +82,171 @@ 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;
// ------------------------------------------------------------------------------------------------
// Conversion functions
// ------------------------------------------------------------------------------------------------
// helper to add a vertex (private to NodeWalk)
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->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->w == (M3D_FLOAT)-0.0) v->w = (M3D_FLOAT)0.0;
vrtx = (m3dv_t *)M3D_REALLOC(vrtx, ((*numvrtx) + 1) * sizeof(m3dv_t));
memcpy(&vrtx[*numvrtx], v, sizeof(m3dv_t));
*idx = *numvrtx;
(*numvrtx)++;
return vrtx;
}
// ------------------------------------------------------------------------------------------------
// helper to add a tmap (private to NodeWalk)
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));
memcpy(&tmap[*numtmap], ti, sizeof(m3dti_t));
*idx = *numtmap;
(*numtmap)++;
return tmap;
}
// ------------------------------------------------------------------------------------------------
// convert aiColor4D into uint32_t
uint32_t mkColor(aiColor4D *c) {
return ((uint8_t)(c->a * 255) << 24L) |
((uint8_t)(c->b * 255) << 16L) |
((uint8_t)(c->g * 255) << 8L) |
((uint8_t)(c->r * 255) << 0L);
}
// ------------------------------------------------------------------------------------------------
// add a material property to the output
void addProp(m3dm_t *m, uint8_t type, uint32_t value) {
unsigned int i;
i = m->numprop++;
m->prop = (m3dp_t *)M3D_REALLOC(m->prop, m->numprop * sizeof(m3dp_t));
if (!m->prop) {
throw DeadlyExportError("memory allocation error");
}
m->prop[i].type = type;
m->prop[i].value.num = value;
}
// ------------------------------------------------------------------------------------------------
// add a material to the output
M3D_INDEX addMaterial(const Assimp::M3DWrapper &m3d, const aiMaterial *mat) {
unsigned int mi = -1U;
aiColor4D c;
aiString name;
ai_real f;
char *fn;
if (mat && mat->Get(AI_MATKEY_NAME, name) == AI_SUCCESS && name.length &&
strcmp((char *)&name.data, AI_DEFAULT_MATERIAL_NAME)) {
// 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
for (unsigned int i = 0; i < m3d->nummaterial; i++)
if (!strcmp((char *)&name.data, m3d->material[i].name)) {
mi = i;
break;
}
// if not found, add the material to the output
if (mi == -1U) {
unsigned int k;
mi = m3d->nummaterial++;
m3d->material = (m3dm_t *)M3D_REALLOC(m3d->material, m3d->nummaterial * sizeof(m3dm_t));
if (!m3d->material) {
throw DeadlyExportError("memory allocation error");
}
m3d->material[mi].name = _m3d_safestr((char *)&name.data, 0);
m3d->material[mi].numprop = 0;
m3d->material[mi].prop = NULL;
// iterate through the material property table and see what we got
for (k = 0; k < 15; k++) {
unsigned int j;
if (m3d_propertytypes[k].format == m3dpf_map)
continue;
if (aiProps[k].pKey) {
switch (m3d_propertytypes[k].format) {
case m3dpf_color:
if (mat->Get(aiProps[k].pKey, aiProps[k].type,
aiProps[k].index, c) == AI_SUCCESS)
addProp(&m3d->material[mi],
m3d_propertytypes[k].id, mkColor(&c));
break;
case m3dpf_float:
if (mat->Get(aiProps[k].pKey, aiProps[k].type,
aiProps[k].index, f) == AI_SUCCESS)
addProp(&m3d->material[mi],
m3d_propertytypes[k].id,
/* not (uint32_t)f, because we don't want to convert
* it, we want to see it as 32 bits of memory */
*((uint32_t *)&f));
break;
case m3dpf_uint8:
if (mat->Get(aiProps[k].pKey, aiProps[k].type,
aiProps[k].index, j) == AI_SUCCESS) {
// special conversion for illumination model property
if (m3d_propertytypes[k].id == m3dp_il) {
switch (j) {
case aiShadingMode_NoShading: j = 0; break;
case aiShadingMode_Phong: j = 2; break;
default: j = 1; break;
}
}
addProp(&m3d->material[mi],
m3d_propertytypes[k].id, j);
}
break;
default:
if (mat->Get(aiProps[k].pKey, aiProps[k].type,
aiProps[k].index, j) == AI_SUCCESS)
addProp(&m3d->material[mi],
m3d_propertytypes[k].id, j);
break;
}
}
if (aiTxProps[k].pKey &&
mat->GetTexture((aiTextureType)aiTxProps[k].type,
aiTxProps[k].index, &name, NULL, NULL, NULL,
NULL, NULL) == AI_SUCCESS) {
unsigned int i;
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') &&
(name.data[j + 1] == 'n' || name.data[j + 1] == 'N') &&
(name.data[j + 1] == 'g' || name.data[j + 1] == 'G'))
name.data[j] = 0;
// do we have this texture saved already?
fn = _m3d_safestr((char *)&name.data, 0);
for (j = 0, i = -1U; j < m3d->numtexture; j++)
if (!strcmp(fn, m3d->texture[j].name)) {
i = j;
free(fn);
break;
}
if (i == -1U) {
i = m3d->numtexture++;
m3d->texture = (m3dtx_t *)M3D_REALLOC(
m3d->texture,
m3d->numtexture * sizeof(m3dtx_t));
if (!m3d->texture) {
throw DeadlyExportError("memory allocation error");
}
// we don't need the texture itself, only its name
m3d->texture[i].name = fn;
m3d->texture[i].w = 0;
m3d->texture[i].h = 0;
m3d->texture[i].d = NULL;
}
addProp(&m3d->material[mi],
m3d_propertytypes[k].id + 128, i);
}
}
}
}
return mi;
}
namespace Assimp { namespace Assimp {
@ -91,8 +257,7 @@ namespace Assimp {
const char *pFile, const char *pFile,
IOSystem *pIOSystem, IOSystem *pIOSystem,
const aiScene *pScene, const aiScene *pScene,
const ExportProperties* pProperties const ExportProperties *pProperties) {
){
// initialize the exporter // initialize the exporter
M3DExporter exporter(pScene, pProperties); M3DExporter exporter(pScene, pProperties);
@ -117,21 +282,17 @@ namespace Assimp {
exporter.doExport(pFile, pIOSystem, true); exporter.doExport(pFile, pIOSystem, true);
} }
} // end of namespace Assimp
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
M3DExporter::M3DExporter ( const aiScene* pScene, const ExportProperties* pProperties ) M3DExporter::M3DExporter(const aiScene *pScene, const ExportProperties *pProperties) :
: mScene(pScene) mScene(pScene),
, mProperties(pProperties) mProperties(pProperties),
, outfile() outfile() {}
, m3d(nullptr) { }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
void M3DExporter::doExport( void M3DExporter::doExport(
const char *pFile, const char *pFile,
IOSystem *pIOSystem, IOSystem *pIOSystem,
bool toAscii bool toAscii) {
){
// TODO: convert mProperties into M3D_EXP_* flags // TODO: convert mProperties into M3D_EXP_* flags
(void)mProperties; (void)mProperties;
@ -141,8 +302,7 @@ void M3DExporter::doExport (
throw DeadlyExportError("could not open output .m3d file: " + std::string(pFile)); throw DeadlyExportError("could not open output .m3d file: " + std::string(pFile));
} }
// use malloc() here because m3d_free() will call free() M3DWrapper m3d;
m3d = (m3d_t*)calloc(1, sizeof(m3d_t));
if (!m3d) { if (!m3d) {
throw DeadlyExportError("memory allocation error"); throw DeadlyExportError("memory allocation error");
} }
@ -150,13 +310,12 @@ void M3DExporter::doExport (
// Create a model from assimp structures // Create a model from assimp structures
aiMatrix4x4 m; aiMatrix4x4 m;
NodeWalk(mScene->mRootNode, m); NodeWalk(m3d, mScene->mRootNode, m);
// serialize the structures // serialize the structures
unsigned int size; unsigned int size;
unsigned char *output = m3d_save(m3d, M3D_EXP_FLOAT, unsigned char *output = m3d.Save(M3D_EXP_FLOAT, M3D_EXP_EXTRA | (toAscii ? M3D_EXP_ASCII : 0), size);
M3D_EXP_EXTRA | (toAscii ? M3D_EXP_ASCII : 0), &size);
m3d_free(m3d);
if (!output || size < 8) { if (!output || size < 8) {
throw DeadlyExportError("unable to serialize into Model 3D"); throw DeadlyExportError("unable to serialize into Model 3D");
} }
@ -169,37 +328,9 @@ void M3DExporter::doExport (
outfile.reset(); outfile.reset();
} }
// ------------------------------------------------------------------------------------------------
// helper to add a vertex (private to NodeWalk)
m3dv_t *M3DExporter::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->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->w == (M3D_FLOAT)-0.0) v->w = (M3D_FLOAT)0.0;
vrtx = (m3dv_t*)M3D_REALLOC(vrtx, ((*numvrtx) + 1) * sizeof(m3dv_t));
memcpy(&vrtx[*numvrtx], v, sizeof(m3dv_t));
*idx = *numvrtx;
(*numvrtx)++;
return vrtx;
}
// ------------------------------------------------------------------------------------------------
// helper to add a tmap (private to NodeWalk)
m3dti_t *M3DExporter::AddTmap(m3dti_t *tmap, uint32_t *numtmap, m3dti_t *ti, uint32_t *idx)
{
tmap = (m3dti_t*)M3D_REALLOC(tmap, ((*numtmap) + 1) * sizeof(m3dti_t));
memcpy(&tmap[*numtmap], ti, sizeof(m3dti_t));
*idx = *numtmap;
(*numtmap)++;
return tmap;
}
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// recursive node walker // recursive node walker
void M3DExporter::NodeWalk(const aiNode* pNode, aiMatrix4x4 m) void M3DExporter::NodeWalk(const M3DWrapper &m3d, const aiNode *pNode, aiMatrix4x4 m) {
{
aiMatrix4x4 nm = m * pNode->mTransformation; aiMatrix4x4 nm = m * pNode->mTransformation;
for (unsigned int i = 0; i < pNode->mNumMeshes; i++) { for (unsigned int i = 0; i < pNode->mNumMeshes; i++) {
@ -207,7 +338,7 @@ void M3DExporter::NodeWalk(const aiNode* pNode, aiMatrix4x4 m)
unsigned int mi = (M3D_INDEX)-1U; unsigned int mi = (M3D_INDEX)-1U;
if (mScene->mMaterials) { if (mScene->mMaterials) {
// get the material for this mesh // get the material for this mesh
mi = addMaterial(mScene->mMaterials[mesh->mMaterialIndex]); mi = addMaterial(m3d, mScene->mMaterials[mesh->mMaterialIndex]);
} }
// iterate through the mesh faces // iterate through the mesh faces
for (unsigned int j = 0; j < mesh->mNumFaces; j++) { for (unsigned int j = 0; j < mesh->mNumFaces; j++) {
@ -272,149 +403,10 @@ void M3DExporter::NodeWalk(const aiNode* pNode, aiMatrix4x4 m)
} }
// repeat for the children nodes // repeat for the children nodes
for (unsigned int i = 0; i < pNode->mNumChildren; i++) { for (unsigned int i = 0; i < pNode->mNumChildren; i++) {
NodeWalk(pNode->mChildren[i], nm); NodeWalk(m3d, pNode->mChildren[i], nm);
} }
} }
} // namespace Assimp
// ------------------------------------------------------------------------------------------------
// convert aiColor4D into uint32_t
uint32_t M3DExporter::mkColor(aiColor4D* c)
{
return ((uint8_t)(c->a*255) << 24L) |
((uint8_t)(c->b*255) << 16L) |
((uint8_t)(c->g*255) << 8L) |
((uint8_t)(c->r*255) << 0L);
}
// ------------------------------------------------------------------------------------------------
// add a material to the output
M3D_INDEX M3DExporter::addMaterial(const aiMaterial *mat)
{
unsigned int mi = -1U;
aiColor4D c;
aiString name;
ai_real f;
char *fn;
if(mat && mat->Get(AI_MATKEY_NAME, name) == AI_SUCCESS && name.length &&
strcmp((char*)&name.data, AI_DEFAULT_MATERIAL_NAME)) {
// 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
for(unsigned int i = 0; i < m3d->nummaterial; i++)
if(!strcmp((char*)&name.data, m3d->material[i].name)) {
mi = i;
break;
}
// if not found, add the material to the output
if(mi == -1U) {
unsigned int k;
mi = m3d->nummaterial++;
m3d->material = (m3dm_t*)M3D_REALLOC(m3d->material, m3d->nummaterial
* sizeof(m3dm_t));
if(!m3d->material) {
throw DeadlyExportError( "memory allocation error" );
}
m3d->material[mi].name = _m3d_safestr((char*)&name.data, 0);
m3d->material[mi].numprop = 0;
m3d->material[mi].prop = NULL;
// iterate through the material property table and see what we got
for(k = 0; k < 15; k++) {
unsigned int j;
if(m3d_propertytypes[k].format == m3dpf_map)
continue;
if(aiProps[k].pKey) {
switch(m3d_propertytypes[k].format) {
case m3dpf_color:
if(mat->Get(aiProps[k].pKey, aiProps[k].type,
aiProps[k].index, c) == AI_SUCCESS)
addProp(&m3d->material[mi],
m3d_propertytypes[k].id, mkColor(&c));
break;
case m3dpf_float:
if(mat->Get(aiProps[k].pKey, aiProps[k].type,
aiProps[k].index, f) == AI_SUCCESS)
addProp(&m3d->material[mi],
m3d_propertytypes[k].id,
/* not (uint32_t)f, because we don't want to convert
* it, we want to see it as 32 bits of memory */
*((uint32_t*)&f));
break;
case m3dpf_uint8:
if(mat->Get(aiProps[k].pKey, aiProps[k].type,
aiProps[k].index, j) == AI_SUCCESS) {
// special conversion for illumination model property
if(m3d_propertytypes[k].id == m3dp_il) {
switch(j) {
case aiShadingMode_NoShading: j = 0; break;
case aiShadingMode_Phong: j = 2; break;
default: j = 1; break;
}
}
addProp(&m3d->material[mi],
m3d_propertytypes[k].id, j);
}
break;
default:
if(mat->Get(aiProps[k].pKey, aiProps[k].type,
aiProps[k].index, j) == AI_SUCCESS)
addProp(&m3d->material[mi],
m3d_propertytypes[k].id, j);
break;
}
}
if(aiTxProps[k].pKey &&
mat->GetTexture((aiTextureType)aiTxProps[k].type,
aiTxProps[k].index, &name, NULL, NULL, NULL,
NULL, NULL) == AI_SUCCESS) {
unsigned int i;
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') &&
(name.data[j+1]=='n' || name.data[j+1]=='N') &&
(name.data[j+1]=='g' || name.data[j+1]=='G'))
name.data[j]=0;
// do we have this texture saved already?
fn = _m3d_safestr((char*)&name.data, 0);
for(j = 0, i = -1U; j < m3d->numtexture; j++)
if(!strcmp(fn, m3d->texture[j].name)) {
i = j;
free(fn);
break;
}
if(i == -1U) {
i = m3d->numtexture++;
m3d->texture = (m3dtx_t*)M3D_REALLOC(
m3d->texture,
m3d->numtexture * sizeof(m3dtx_t));
if(!m3d->texture) {
throw DeadlyExportError( "memory allocation error" );
}
// we don't need the texture itself, only its name
m3d->texture[i].name = fn;
m3d->texture[i].w = 0;
m3d->texture[i].h = 0;
m3d->texture[i].d = NULL;
}
addProp(&m3d->material[mi],
m3d_propertytypes[k].id + 128, i);
}
}
}
}
return mi;
}
// ------------------------------------------------------------------------------------------------
// add a material property to the output
void M3DExporter::addProp(m3dm_t *m, uint8_t type, uint32_t value)
{
unsigned int i;
i = m->numprop++;
m->prop = (m3dp_t*)M3D_REALLOC(m->prop, m->numprop * sizeof(m3dp_t));
if(!m->prop) { throw DeadlyExportError( "memory allocation error" ); }
m->prop[i].type = type;
m->prop[i].value.num = value;
}
#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,49 +99,14 @@ 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.
@ -188,9 +155,8 @@ void M3DImporter::InternReadFile( const std::string &file, aiScene* pScene, IOSy
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 + ".");
} }
@ -203,37 +169,33 @@ void M3DImporter::InternReadFile( const std::string &file, aiScene* pScene, IOSy
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) { if (!m3d) {
throw DeadlyImportError("Unable to parse " + file + " as 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) {
@ -243,8 +205,7 @@ void M3DImporter::InternReadFile( const std::string &file, aiScene* pScene, IOSy
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// 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,22 +213,23 @@ 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;
c.b = c.g = c.r = 0.6f;
mat->AddProperty(&c, 1, AI_MATKEY_COLOR_DIFFUSE); 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);
@ -312,9 +274,9 @@ 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,14 +288,13 @@ 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);
@ -355,14 +316,21 @@ void M3DImporter::importTextures()
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,8 +342,7 @@ 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;
@ -387,7 +354,7 @@ 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);
@ -397,7 +364,7 @@ void M3DImporter::importMeshes()
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;
@ -422,7 +389,7 @@ void M3DImporter::importMeshes()
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;
@ -456,17 +423,17 @@ void M3DImporter::importMeshes()
} }
// 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;
@ -483,13 +450,12 @@ void M3DImporter::importMeshes()
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// 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);
@ -502,11 +468,11 @@ void M3DImporter::importBones(unsigned int parentid, aiNode *pParent)
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,14 +481,13 @@ 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;
@ -589,10 +554,9 @@ aiColor4D M3DImporter::mkColor(uint32_t c) {
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// 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];
@ -603,27 +567,40 @@ void M3DImporter::convertPose(aiMatrix4x4 *m, unsigned int posid, unsigned int o
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);
@ -640,8 +617,7 @@ aiNode *M3DImporter::findNode(aiNode *pNode, aiString name)
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// 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);
@ -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,16 +644,16 @@ 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()) {

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,22 +59,20 @@ 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;
@ -83,18 +80,18 @@ private:
//! \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