diff --git a/code/M3D/M3DExporter.cpp b/code/M3D/M3DExporter.cpp index faf5a9585..a3988db0b 100644 --- a/code/M3D/M3DExporter.cpp +++ b/code/M3D/M3DExporter.cpp @@ -45,7 +45,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #define M3D_IMPLEMENTATION #define M3D_NOIMPORTER #define M3D_EXPORTER -#define M3D_ASCII #ifndef ASSIMP_BUILD_NO_M3D_IMPORTER #define M3D_NODUP #endif @@ -65,9 +64,9 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include +#include "M3DWrapper.h" #include "M3DExporter.h" #include "M3DMaterials.h" -#include "M3DWrapper.h" // RESOURCES: // https://gitlab.com/bztsrc/model3d/blob/master/docs/m3d_format.md @@ -131,6 +130,28 @@ void addProp(m3dm_t *m, uint8_t type, uint32_t value) { m->prop[i].value.num = value; } +// ------------------------------------------------------------------------------------------------ +// convert aiString to identifier safe C string. This is a duplication of _m3d_safestr +char *SafeStr(aiString str, bool isStrict) +{ + char *s = (char *)&str.data; + char *d, *ret; + int i, len; + + for(len = str.length + 1; *s && (*s == ' ' || *s == '\t'); s++, len--); + if(len > 255) len = 255; + ret = (char *)M3D_MALLOC(len + 1); + if (!ret) { + throw DeadlyExportError("memory allocation error"); + } + for(i = 0, d = ret; i < len && *s && *s != '\r' && *s != '\n'; s++, d++, i++) { + *d = isStrict && (*s == ' ' || *s == '\t' || *s == '/' || *s == '\\') ? '_' : (*s == '\t' ? ' ' : *s); + } + for(; d > ret && (*(d-1) == ' ' || *(d-1) == '\t'); d--); + *d = 0; + return ret; +} + // ------------------------------------------------------------------------------------------------ // add a material to the output M3D_INDEX addMaterial(const Assimp::M3DWrapper &m3d, const aiMaterial *mat) { @@ -157,7 +178,7 @@ M3D_INDEX addMaterial(const Assimp::M3DWrapper &m3d, const aiMaterial *mat) { if (!m3d->material) { throw DeadlyExportError("memory allocation error"); } - m3d->material[mi].name = _m3d_safestr((char *)&name.data, 0); + m3d->material[mi].name = SafeStr(name, true); m3d->material[mi].numprop = 0; m3d->material[mi].prop = NULL; // iterate through the material property table and see what we got @@ -218,7 +239,7 @@ M3D_INDEX addMaterial(const Assimp::M3DWrapper &m3d, const aiMaterial *mat) { (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); + fn = SafeStr(name, true); for (j = 0, i = M3D_NOTDEFINED; j < m3d->numtexture; j++) if (!strcmp(fn, m3d->texture[j].name)) { i = j; @@ -275,11 +296,15 @@ void ExportSceneA3D( const ExportProperties *pProperties ) { +#ifdef M3D_ASCII // initialize the exporter M3DExporter exporter(pScene, pProperties); // perform ascii export exporter.doExport(pFile, pIOSystem, true); +#else + throw DeadlyExportError("Assimp configured without M3D_ASCII support"); +#endif } // ------------------------------------------------------------------------------------------------ @@ -306,7 +331,7 @@ void M3DExporter::doExport( if (!m3d) { throw DeadlyExportError("memory allocation error"); } - m3d->name = _m3d_safestr((char *)&mScene->mRootNode->mName.data, 2); + m3d->name = SafeStr(mScene->mRootNode->mName, false); // Create a model from assimp structures aiMatrix4x4 m; diff --git a/code/M3D/M3DImporter.cpp b/code/M3D/M3DImporter.cpp index a183f8bda..a77e75a27 100644 --- a/code/M3D/M3DImporter.cpp +++ b/code/M3D/M3DImporter.cpp @@ -43,7 +43,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #ifndef ASSIMP_BUILD_NO_M3D_IMPORTER #define M3D_IMPLEMENTATION -#define M3D_ASCII #define M3D_NONORMALS /* leave the post-processing to Assimp */ #define M3D_NOWEIGHTS #define M3D_NOANIMATION @@ -57,9 +56,9 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include +#include "M3DWrapper.h" #include "M3DImporter.h" #include "M3DMaterials.h" -#include "M3DWrapper.h" // RESOURCES: // https://gitlab.com/bztsrc/model3d/blob/master/docs/m3d_format.md @@ -96,7 +95,11 @@ static const aiImporterDesc desc = { 0, 0, 0, +#ifdef M3D_ASCII "m3d a3d" +#else + "m3d" +#endif }; namespace Assimp { @@ -113,7 +116,11 @@ M3DImporter::M3DImporter() : bool M3DImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool checkSig) const { const std::string extension = GetExtension(pFile); - if (extension == "m3d" || extension == "a3d") + if (extension == "m3d" +#ifdef M3D_ASCII + || extension == "a3d" +#endif + ) return true; else if (!extension.length() || checkSig) { if (!pIOHandler) { @@ -131,7 +138,11 @@ bool M3DImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool c if (4 != pStream->Read(data, 1, 4)) { return false; } - return !memcmp(data, "3DMO", 4) /* bin */ || !memcmp(data, "3dmo", 4) /* ASCII */; + return !memcmp(data, "3DMO", 4) /* bin */ +#ifdef M3D_ASCII + || !memcmp(data, "3dmo", 4) /* ASCII */ +#endif + ; } return false; } @@ -163,6 +174,12 @@ void M3DImporter::InternReadFile(const std::string &file, aiScene *pScene, IOSys if(!memcmp(buffer.data(), "3DMO", 4) && memcmp(buffer.data() + 4, &fileSize, 4)) { throw DeadlyImportError("Bad binary header in file " + file + "."); } +#ifdef M3D_ASCII + // make sure there's a terminator zero character, as input must be ASCIIZ + if(!memcmp(buffer.data(), "3dmo", 4)) { + buffer.push_back(0); + } +#endif // Get the path for external assets std::string folderName("./"); @@ -180,7 +197,6 @@ void M3DImporter::InternReadFile(const std::string &file, aiScene *pScene, IOSys // let the C SDK do the hard work for us M3DWrapper m3d(pIOHandler, buffer); - if (!m3d) { throw DeadlyImportError("Unable to parse " + file + " as M3D."); } @@ -310,32 +326,40 @@ void M3DImporter::importTextures(const M3DWrapper &m3d) { for (i = 0; i < m3d->numtexture; i++) { unsigned int j, k; t = &m3d->texture[i]; - if (!t->w || !t->h || !t->f || !t->d) continue; aiTexture *tx = new aiTexture; - strcpy(tx->achFormatHint, formatHint[t->f - 1]); tx->mFilename = aiString(std::string(t->name) + ".png"); - tx->mWidth = t->w; - tx->mHeight = t->h; - tx->pcData = new aiTexel[tx->mWidth * tx->mHeight]; - for (j = k = 0; j < tx->mWidth * tx->mHeight; j++) { - switch (t->f) { - case 1: tx->pcData[j].g = t->d[k++]; break; - case 2: - tx->pcData[j].g = t->d[k++]; - tx->pcData[j].a = t->d[k++]; - break; - case 3: - tx->pcData[j].r = t->d[k++]; - tx->pcData[j].g = t->d[k++]; - tx->pcData[j].b = t->d[k++]; - tx->pcData[j].a = 255; - break; - case 4: - tx->pcData[j].r = t->d[k++]; - tx->pcData[j].g = t->d[k++]; - tx->pcData[j].b = t->d[k++]; - tx->pcData[j].a = t->d[k++]; - break; + if (!t->w || !t->h || !t->f || !t->d) { + /* without ASSIMP_USE_M3D_READFILECB, we only have the filename, but no texture data ever */ + tx->mWidth = 0; + tx->mHeight = 0; + memcpy(tx->achFormatHint, "png\000", 4); + tx->pcData = nullptr; + } else { + /* if we have the texture loaded, set format hint and pcData too */ + tx->mWidth = t->w; + tx->mHeight = t->h; + strcpy(tx->achFormatHint, formatHint[t->f - 1]); + tx->pcData = new aiTexel[tx->mWidth * tx->mHeight]; + for (j = k = 0; j < tx->mWidth * tx->mHeight; j++) { + switch (t->f) { + case 1: tx->pcData[j].g = t->d[k++]; break; + case 2: + tx->pcData[j].g = t->d[k++]; + tx->pcData[j].a = t->d[k++]; + break; + case 3: + tx->pcData[j].r = t->d[k++]; + tx->pcData[j].g = t->d[k++]; + tx->pcData[j].b = t->d[k++]; + tx->pcData[j].a = 255; + break; + case 4: + tx->pcData[j].r = t->d[k++]; + tx->pcData[j].g = t->d[k++]; + tx->pcData[j].b = t->d[k++]; + tx->pcData[j].a = t->d[k++]; + break; + } } } mScene->mTextures[i] = tx; diff --git a/code/M3D/M3DWrapper.cpp b/code/M3D/M3DWrapper.cpp index c0de1cf31..28eda845f 100644 --- a/code/M3D/M3DWrapper.cpp +++ b/code/M3D/M3DWrapper.cpp @@ -48,15 +48,17 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include -#if (__cplusplus >= 201103L) || !defined(_MSC_VER) || (_MSC_VER >= 1900) // C++11 and MSVC 2015 onwards -# define threadlocal thread_local -#else -# if defined(_MSC_VER) && (_MSC_VER >= 1800) // there's an alternative for MSVC 2013 -# define threadlocal __declspec(thread) +#ifdef ASSIMP_USE_M3D_READFILECB + +# if (__cplusplus >= 201103L) || !defined(_MSC_VER) || (_MSC_VER >= 1900) // C++11 and MSVC 2015 onwards +# define threadlocal thread_local # else -# define threadlocal +# if defined(_MSC_VER) && (_MSC_VER >= 1800) // there's an alternative for MSVC 2013 +# define threadlocal __declspec(thread) +# else +# define threadlocal +# endif # endif -#endif extern "C" { @@ -89,6 +91,7 @@ unsigned char *m3dimporter_readfile(char *fn, unsigned int *size) { return data; } } +#endif namespace Assimp { M3DWrapper::M3DWrapper() { @@ -97,11 +100,15 @@ M3DWrapper::M3DWrapper() { } M3DWrapper::M3DWrapper(IOSystem *pIOHandler, const std::vector &buffer) { +#ifdef ASSIMP_USE_M3D_READFILECB // pass this IOHandler to the C callback in a thread-local pointer m3dimporter_pIOHandler = pIOHandler; m3d_ = m3d_load(const_cast(buffer.data()), m3dimporter_readfile, free, nullptr); // Clear the C callback m3dimporter_pIOHandler = nullptr; +#else + m3d_ = m3d_load(const_cast(buffer.data()), nullptr, nullptr, nullptr); +#endif } M3DWrapper::~M3DWrapper() { diff --git a/code/M3D/M3DWrapper.h b/code/M3D/M3DWrapper.h index f404d3d4b..4ceac12ad 100644 --- a/code/M3D/M3DWrapper.h +++ b/code/M3D/M3DWrapper.h @@ -52,6 +52,10 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include +// Assimp specific M3D configuration. Comment out these defines to remove functionality +#define ASSIMP_USE_M3D_READFILECB +#define M3D_ASCII + #include "m3d.h" namespace Assimp { diff --git a/code/M3D/m3d.h b/code/M3D/m3d.h index 5ab9f14c0..edf5d9623 100644 --- a/code/M3D/m3d.h +++ b/code/M3D/m3d.h @@ -91,11 +91,7 @@ typedef uint16_t M3D_INDEX; #else #define _inline #define _pack -# ifdef __cplusplus /* only for c++ code */ -# define _unused __pragma(warning(suppress:4100)) -# else -# define _unused (void) -# endif +#define _unused __pragma(warning(suppress:4100)) #endif #ifndef __cplusplus #define _register register @@ -2064,7 +2060,7 @@ static char *_m3d_getfloat(char *s, M3D_FLOAT *ret) return _m3d_findarg(e); } #endif -#if !defined(M3D_NODUP) && (defined(M3D_ASCII) || defined(M3D_EXPORTER)) +#if !defined(M3D_NODUP) && (!defined(M3D_NOIMPORTER) || defined(M3D_ASCII) || defined(M3D_EXPORTER)) /* helper function to create safe strings */ char *_m3d_safestr(char *in, int morelines) { @@ -2139,45 +2135,48 @@ M3D_INDEX _m3d_gettx(m3d_t *model, m3dread_t readfilecb, m3dfree_t freecb, char buff = (*readfilecb)(fn2, &len); M3D_FREE(fn2); } - if(!buff) + if(!buff) { buff = (*readfilecb)(fn, &len); + if(!buff) return M3D_UNDEF; + } } - if(!buff) return M3D_UNDEF; /* add to textures array */ i = model->numtexture++; model->texture = (m3dtx_t*)M3D_REALLOC(model->texture, model->numtexture * sizeof(m3dtx_t)); if(!model->texture) { - if(freecb) (*freecb)(buff); + if(buff && freecb) (*freecb)(buff); model->errcode = M3D_ERR_ALLOC; return M3D_UNDEF; } model->texture[i].name = fn; model->texture[i].w = model->texture[i].h = 0; model->texture[i].d = NULL; - if(buff[0] == 0x89 && buff[1] == 'P' && buff[2] == 'N' && buff[3] == 'G') { + if(buff) { + if(buff[0] == 0x89 && buff[1] == 'P' && buff[2] == 'N' && buff[3] == 'G') { #ifdef STBI__PNG_TYPE - s.read_from_callbacks = 0; - s.img_buffer = s.img_buffer_original = (unsigned char *) buff; - s.img_buffer_end = s.img_buffer_original_end = (unsigned char *) buff+len; - /* don't use model->texture[i].w directly, it's a uint16_t */ - w = h = len = 0; - ri.bits_per_channel = 8; - model->texture[i].d = (uint8_t*)stbi__png_load(&s, (int*)&w, (int*)&h, (int*)&len, 0, &ri); - model->texture[i].w = w; - model->texture[i].h = h; - model->texture[i].f = (uint8_t)len; + s.read_from_callbacks = 0; + s.img_buffer = s.img_buffer_original = (unsigned char *) buff; + s.img_buffer_end = s.img_buffer_original_end = (unsigned char *) buff+len; + /* don't use model->texture[i].w directly, it's a uint16_t */ + w = h = len = 0; + ri.bits_per_channel = 8; + model->texture[i].d = (uint8_t*)stbi__png_load(&s, (int*)&w, (int*)&h, (int*)&len, 0, &ri); + model->texture[i].w = w; + model->texture[i].h = h; + model->texture[i].f = (uint8_t)len; #endif - } else { + } else { #ifdef M3D_TX_INTERP - if((model->errcode = M3D_TX_INTERP(fn, buff, len, &model->texture[i])) != M3D_SUCCESS) { - M3D_LOG("Unable to generate texture"); - M3D_LOG(fn); - } + if((model->errcode = M3D_TX_INTERP(fn, buff, len, &model->texture[i])) != M3D_SUCCESS) { + M3D_LOG("Unable to generate texture"); + M3D_LOG(fn); + } #else - M3D_LOG("Unimplemented interpreter"); - M3D_LOG(fn); + M3D_LOG("Unimplemented interpreter"); + M3D_LOG(fn); #endif + } + if(freecb) (*freecb)(buff); } - if(freecb) (*freecb)(buff); if(!model->texture[i].d) model->errcode = M3D_ERR_UNKIMG; return i; @@ -2589,6 +2588,7 @@ m3d_t *m3d_load(unsigned char *data, m3dread_t readfilecb, m3dfree_t freecb, m3d if(!pe || !*pe) goto asciiend; m->prop[j].value.textureid = _m3d_gettx(model, readfilecb, freecb, pe); if(model->errcode == M3D_ERR_ALLOC) { M3D_FREE(pe); goto memerr; } + /* this error code only returned if readfilecb was specified */ if(m->prop[j].value.textureid == M3D_UNDEF) { M3D_LOG("Texture not found"); M3D_LOG(pe); @@ -3254,6 +3254,7 @@ memerr: M3D_LOG("Out of memory"); M3D_GETSTR(name); m->prop[i].value.textureid = _m3d_gettx(model, readfilecb, freecb, name); if(model->errcode == M3D_ERR_ALLOC) goto memerr; + /* this error code only returned if readfilecb was specified */ if(m->prop[i].value.textureid == M3D_UNDEF) { M3D_LOG("Texture not found"); M3D_LOG(m->name);