issue_1973

added support for CustomData(Layer) to support multiple (texture) UV mappings
added unittest with sample model
pull/2005/head
CwTCwT 2018-06-09 15:45:09 +02:00
parent cd0fe21464
commit 85b0026c92
12 changed files with 707 additions and 17 deletions

View File

@ -0,0 +1,212 @@
#pragma once
#include "BlenderCustomData.h"
#include <array>
namespace Assimp {
namespace Blender
{
/**
* @brief read/convert of Structure array to memory
*/
template<typename T>
bool read(const Structure &s, T *p, const size_t cnt, const FileDatabase &db) {
for (size_t i = 0; i < cnt; ++i) {
T read;
s.Convert(read, db);
*p = read;
p++;
}
return true;
}
/**
* @brief pointer to function read memory for n CustomData types
*/
typedef bool(*PRead)(void *pOut, const size_t cnt, const FileDatabase &db);
/**
* @brief pointer to function read memory for cnt CustomData types
*/
typedef void *(*PAlloc)(const size_t cnt);
/**
* @brief helper macro to define Structure specific read function
* for ex: when used like
*
* IMPL_STRUCT_READ(MLoop)
*
* following function is implemented
*
* bool readMLoop(void *v, const size_t cnt, const FileDatabase &db) {
* return read<MLoop>(db.dna["MLoop"], static_cast<MLoop *>(v), cnt, db);
* }
*/
#define IMPL_STRUCT_READ(ty) \
bool read##ty(void *v, const size_t cnt, const FileDatabase &db) { \
return read<ty>(db.dna[#ty], static_cast<ty *>(v), cnt, db); \
}
/**
* @brief helper macro to define Structure specific alloc function
* for ex: when used like
*
* IMPL_STRUCT_ALLOC(MLoop)
*
* following function is implemented
*
* void * allocMLoop(const size_t cnt) {
* return new uint8_t[cnt * sizeof MLoop];
* }
*/
#define IMPL_STRUCT_ALLOC(ty) \
void *alloc##ty(const size_t cnt) { \
return new uint8_t[cnt * sizeof ty]; \
}
/**
* @brief helper macro to define Structure functions
*/
#define IMPL_STRUCT(ty) \
IMPL_STRUCT_ALLOC(ty) \
IMPL_STRUCT_READ(ty)
// supported structures for CustomData
IMPL_STRUCT(MVert)
IMPL_STRUCT(MEdge)
IMPL_STRUCT(MFace)
IMPL_STRUCT(MTFace)
IMPL_STRUCT(MTexPoly)
IMPL_STRUCT(MLoopUV)
IMPL_STRUCT(MLoopCol)
IMPL_STRUCT(MPoly)
IMPL_STRUCT(MLoop)
/**
* @brief describes the size of data and the read function to be used for single CustomerData.type
*/
struct CustomDataTypeDescription
{
PRead Read; ///< function to read one CustomData type element
PAlloc Alloc; ///< function to allocate n type elements
};
/**
* @brief shortcut for array of CustomDataTypeDescription
*/
typedef std::array<CustomDataTypeDescription, CD_NUMTYPES> CustomDataTypeDescriptions;
/**
* @brief helper macro to define Structure type specific CustomDataTypeDescription
* @note IMPL_STRUCT_READ for same ty must be used earlier to implement the typespecific read function
*/
#define DECL_STRUCT_CUSTOMDATATYPEDESCRIPTION(ty) \
CustomDataTypeDescription{ &read##ty, &alloc##ty }
/**
* @brief helper macro to define CustomDataTypeDescription for UNSUPPORTED type
*/
#define DECL_UNSUPPORTED_CUSTOMDATATYPEDESCRIPTION \
CustomDataTypeDescription{ nullptr, nullptr }
/**
* @brief descriptors for data pointed to from CustomDataLayer.data
* @note some of the CustomData uses already well defined Structures
* other (like CD_ORCO, ...) uses arrays of rawtypes or even arrays of Structures
* use a special readfunction for that cases
*/
CustomDataTypeDescriptions customDataTypeDescriptions =
{
DECL_STRUCT_CUSTOMDATATYPEDESCRIPTION(MVert),
DECL_UNSUPPORTED_CUSTOMDATATYPEDESCRIPTION,
DECL_UNSUPPORTED_CUSTOMDATATYPEDESCRIPTION,
DECL_STRUCT_CUSTOMDATATYPEDESCRIPTION(MEdge),
DECL_STRUCT_CUSTOMDATATYPEDESCRIPTION(MFace),
DECL_STRUCT_CUSTOMDATATYPEDESCRIPTION(MTFace),
DECL_UNSUPPORTED_CUSTOMDATATYPEDESCRIPTION,
DECL_UNSUPPORTED_CUSTOMDATATYPEDESCRIPTION,
DECL_UNSUPPORTED_CUSTOMDATATYPEDESCRIPTION,
DECL_UNSUPPORTED_CUSTOMDATATYPEDESCRIPTION,
DECL_UNSUPPORTED_CUSTOMDATATYPEDESCRIPTION,
DECL_UNSUPPORTED_CUSTOMDATATYPEDESCRIPTION,
DECL_UNSUPPORTED_CUSTOMDATATYPEDESCRIPTION,
DECL_UNSUPPORTED_CUSTOMDATATYPEDESCRIPTION,
DECL_UNSUPPORTED_CUSTOMDATATYPEDESCRIPTION,
DECL_STRUCT_CUSTOMDATATYPEDESCRIPTION(MTexPoly),
DECL_STRUCT_CUSTOMDATATYPEDESCRIPTION(MLoopUV),
DECL_STRUCT_CUSTOMDATATYPEDESCRIPTION(MLoopCol),
DECL_UNSUPPORTED_CUSTOMDATATYPEDESCRIPTION,
DECL_UNSUPPORTED_CUSTOMDATATYPEDESCRIPTION,
DECL_UNSUPPORTED_CUSTOMDATATYPEDESCRIPTION,
DECL_UNSUPPORTED_CUSTOMDATATYPEDESCRIPTION,
DECL_UNSUPPORTED_CUSTOMDATATYPEDESCRIPTION,
DECL_UNSUPPORTED_CUSTOMDATATYPEDESCRIPTION,
DECL_UNSUPPORTED_CUSTOMDATATYPEDESCRIPTION,
DECL_STRUCT_CUSTOMDATATYPEDESCRIPTION(MPoly),
DECL_STRUCT_CUSTOMDATATYPEDESCRIPTION(MLoop),
DECL_UNSUPPORTED_CUSTOMDATATYPEDESCRIPTION,
DECL_UNSUPPORTED_CUSTOMDATATYPEDESCRIPTION,
DECL_UNSUPPORTED_CUSTOMDATATYPEDESCRIPTION,
DECL_UNSUPPORTED_CUSTOMDATATYPEDESCRIPTION,
DECL_UNSUPPORTED_CUSTOMDATATYPEDESCRIPTION,
DECL_UNSUPPORTED_CUSTOMDATATYPEDESCRIPTION,
DECL_UNSUPPORTED_CUSTOMDATATYPEDESCRIPTION,
DECL_UNSUPPORTED_CUSTOMDATATYPEDESCRIPTION,
DECL_UNSUPPORTED_CUSTOMDATATYPEDESCRIPTION,
DECL_UNSUPPORTED_CUSTOMDATATYPEDESCRIPTION,
DECL_UNSUPPORTED_CUSTOMDATATYPEDESCRIPTION,
DECL_UNSUPPORTED_CUSTOMDATATYPEDESCRIPTION,
DECL_UNSUPPORTED_CUSTOMDATATYPEDESCRIPTION,
DECL_UNSUPPORTED_CUSTOMDATATYPEDESCRIPTION,
DECL_UNSUPPORTED_CUSTOMDATATYPEDESCRIPTION
};
bool isValidCustomDataType(const int cdtype)
{
return cdtype >= 0 && cdtype < CD_NUMTYPES;
}
bool readCustomData(std::shared_ptr<void> &out, const CustomDataType cdtype, const size_t cnt, const FileDatabase &db)
{
if (!isValidCustomDataType(cdtype))
{
throw Error((Formatter::format(), "CustomData.type ", cdtype, " out of index"));
}
const CustomDataTypeDescription cdtd = customDataTypeDescriptions[cdtype];
if (cdtd.Read && cdtd.Alloc)
{
// allocate cnt elements and parse them from file
out.reset(cdtd.Alloc(cnt));
return cdtd.Read(out.get(), cnt, db);
}
return false;
}
std::shared_ptr<CustomDataLayer> getCustomDataLayer(const CustomData &customdata, const CustomDataType cdtype, const std::string &name)
{
for (auto it = customdata.layers.begin(); it != customdata.layers.end(); ++it)
{
if (it->get()->type == cdtype && name == it->get()->name)
{
return *it;
}
}
return nullptr;
}
const void * getCustomDataLayerData(const CustomData &customdata, const CustomDataType cdtype, const std::string &name)
{
const std::shared_ptr<CustomDataLayer> pLayer = getCustomDataLayer(customdata, cdtype, name);
if (pLayer && pLayer->data)
{
return pLayer->data.get();
}
return nullptr;
}
}
}

View File

@ -0,0 +1,101 @@
#pragma once
#include "BlenderDNA.h"
#include "BlenderScene.h"
#include <memory>
namespace Assimp {
namespace Blender
{
/* CustomData.type from Blender (2.79b) */
enum CustomDataType
{
CD_AUTO_FROM_NAME = -1,
CD_MVERT = 0,
#ifdef DNA_DEPRECATED
CD_MSTICKY = 1, /* DEPRECATED */
#endif
CD_MDEFORMVERT = 2,
CD_MEDGE = 3,
CD_MFACE = 4,
CD_MTFACE = 5,
CD_MCOL = 6,
CD_ORIGINDEX = 7,
CD_NORMAL = 8,
/* CD_POLYINDEX = 9, */
CD_PROP_FLT = 10,
CD_PROP_INT = 11,
CD_PROP_STR = 12,
CD_ORIGSPACE = 13, /* for modifier stack face location mapping */
CD_ORCO = 14,
CD_MTEXPOLY = 15,
CD_MLOOPUV = 16,
CD_MLOOPCOL = 17,
CD_TANGENT = 18,
CD_MDISPS = 19,
CD_PREVIEW_MCOL = 20, /* for displaying weightpaint colors */
/* CD_ID_MCOL = 21, */
CD_TEXTURE_MLOOPCOL = 22,
CD_CLOTH_ORCO = 23,
CD_RECAST = 24,
/* BMESH ONLY START */
CD_MPOLY = 25,
CD_MLOOP = 26,
CD_SHAPE_KEYINDEX = 27,
CD_SHAPEKEY = 28,
CD_BWEIGHT = 29,
CD_CREASE = 30,
CD_ORIGSPACE_MLOOP = 31,
CD_PREVIEW_MLOOPCOL = 32,
CD_BM_ELEM_PYPTR = 33,
/* BMESH ONLY END */
CD_PAINT_MASK = 34,
CD_GRID_PAINT_MASK = 35,
CD_MVERT_SKIN = 36,
CD_FREESTYLE_EDGE = 37,
CD_FREESTYLE_FACE = 38,
CD_MLOOPTANGENT = 39,
CD_TESSLOOPNORMAL = 40,
CD_CUSTOMLOOPNORMAL = 41,
CD_NUMTYPES = 42
};
/**
* @brief check if given cdtype is valid (ie >= 0 and < CD_NUMTYPES)
* @param[in] cdtype to check
* @return true when valid
*/
bool isValidCustomDataType(const int cdtype);
/**
* @brief read CustomData's data to ptr to mem
* @param[out] out memory ptr to set
* @param[in] cdtype to read
* @param[in] cnt cnt of elements to read
* @param[in] db to read elements from
* @return true when ok
*/
bool readCustomData(std::shared_ptr<void> &out, CustomDataType cdtype, size_t cnt, const FileDatabase &db);
/**
* @brief returns CustomDataLayer ptr for given cdtype and name
* @param[in] customdata CustomData to search for wanted layer
* @param[in] cdtype to search for
* @param[in] name to search for
* @return CustomDataLayer * or nullptr if not found
*/
std::shared_ptr<CustomDataLayer> getCustomDataLayer(const CustomData &customdata, CustomDataType cdtype, const std::string &name);
/**
* @brief returns CustomDataLayer data ptr for given cdtype and name
* @param[in] customdata CustomData to search for wanted layer
* @param[in] cdtype to search for
* @param[in] name to search for
* @return CustomDataLayer * or nullptr if not found
*/
const void * getCustomDataLayerData(const CustomData &customdata, CustomDataType cdtype, const std::string &name);
}
}

View File

@ -309,6 +309,28 @@ public:
void ReadField(T& out, const char* name, void ReadField(T& out, const char* name,
const FileDatabase& db) const; const FileDatabase& db) const;
// --------------------------------------------------------
/**
* @brief field parsing for dynamic vectors
* @param[in] out vector of struct to be filled
* @param[in] name of field
* @param[in] db to access the file, dna, ...
* @return true when read was successful
*/
template <int error_policy, template <typename> class TOUT, typename T>
bool ReadFieldPtrVector(vector<TOUT<T>>&out, const char* name, const FileDatabase& db) const;
/**
* @brief parses raw customdata
* @param[in] out shared_ptr to be filled
* @param[in] cdtype customdata type to read
* @param[in] name of field ptr
* @param[in] db to access the file, dna, ...
* @return true when read was successful
*/
template <int error_policy>
bool ReadCustomDataPtr(std::shared_ptr<void>&out, int cdtype, const char* name, const FileDatabase& db) const;
private: private:
// -------------------------------------------------------- // --------------------------------------------------------

View File

@ -307,6 +307,124 @@ void Structure :: ReadField(T& out, const char* name, const FileDatabase& db) co
} }
//--------------------------------------------------------------------------------
// field parsing for raw untyped data (like CustomDataLayer.data)
template <int error_policy>
bool Structure::ReadCustomDataPtr(std::shared_ptr<void>&out, int cdtype, const char* name, const FileDatabase& db) const
{
if (!isValidCustomDataType(cdtype))
{
ASSIMP_LOG_ERROR("given rawtype out of index");
return false;
}
const StreamReaderAny::pos old = db.reader->GetCurrentPos();
Pointer ptrval;
const Field* f;
try
{
f = &(*this)[name];
// sanity check, should never happen if the genblenddna script is right
if (!(f->flags & FieldFlag_Pointer))
{
throw Error((Formatter::format(), "Field `", name, "` of structure `",
this->name, "` ought to be a pointer"));
}
db.reader->IncPtr(f->offset);
Convert(ptrval, db);
// actually it is meaningless on which Structure the Convert is called
// because the `Pointer` argument triggers a special implementation.
}
catch (const Error& e)
{
_defaultInitializer<error_policy>()(out, e.what());
out.reset();
}
bool readOk = true;
if (ptrval.val)
{
// get block for ptr
const FileBlockHead* block = LocateFileBlockForAddress(ptrval, db);
db.reader->SetCurrentPos(block->start + static_cast<size_t>((ptrval.val - block->address.val)));
// read block->num instances of given type to out
readOk = readCustomData(out, static_cast<CustomDataType>(cdtype), block->num, db);
}
// and recover the previous stream position
db.reader->SetCurrentPos(old);
#ifndef ASSIMP_BUILD_BLENDER_NO_STATS
++db.stats().fields_read;
#endif
return readOk;
}
//--------------------------------------------------------------------------------
template <int error_policy, template <typename> class TOUT, typename T>
bool Structure::ReadFieldPtrVector(vector<TOUT<T>>&out, const char* name, const FileDatabase& db) const
{
out.clear();
const StreamReaderAny::pos old = db.reader->GetCurrentPos();
Pointer ptrval;
const Field* f;
try
{
f = &(*this)[name];
// sanity check, should never happen if the genblenddna script is right
if (!(f->flags & FieldFlag_Pointer))
{
throw Error((Formatter::format(), "Field `", name, "` of structure `",
this->name, "` ought to be a pointer"));
}
db.reader->IncPtr(f->offset);
Convert(ptrval, db);
// actually it is meaningless on which Structure the Convert is called
// because the `Pointer` argument triggers a special implementation.
}
catch (const Error& e)
{
_defaultInitializer<error_policy>()(out, e.what());
out.clear();
return false;
}
if (ptrval.val)
{
// find the file block the pointer is pointing to
const FileBlockHead* block = LocateFileBlockForAddress(ptrval, db);
db.reader->SetCurrentPos(block->start + static_cast<size_t>((ptrval.val - block->address.val)));
// FIXME: basically, this could cause problems with 64 bit pointers on 32 bit systems.
// I really ought to improve StreamReader to work with 64 bit indices exclusively.
const Structure& s = db.dna[f->type];
for (size_t i = 0; i < block->num; ++i)
{
TOUT<T> p(new T);
s.Convert(*p, db);
out.push_back(p);
}
}
db.reader->SetCurrentPos(old);
#ifndef ASSIMP_BUILD_BLENDER_NO_STATS
++db.stats().fields_read;
#endif
return false;
}
//-------------------------------------------------------------------------------- //--------------------------------------------------------------------------------
template <template <typename> class TOUT, typename T> template <template <typename> class TOUT, typename T>
bool Structure :: ResolvePointer(TOUT<T>& out, const Pointer & ptrval, const FileDatabase& db, bool Structure :: ResolvePointer(TOUT<T>& out, const Pointer & ptrval, const FileDatabase& db,

View File

@ -54,6 +54,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "BlenderIntermediate.h" #include "BlenderIntermediate.h"
#include "BlenderModifier.h" #include "BlenderModifier.h"
#include "BlenderBMesh.h" #include "BlenderBMesh.h"
#include "BlenderCustomData.h"
#include <assimp/StringUtils.h> #include <assimp/StringUtils.h>
#include <assimp/scene.h> #include <assimp/scene.h>
#include <assimp/importerdesc.h> #include <assimp/importerdesc.h>
@ -1021,6 +1022,36 @@ void BlenderImporter::ConvertMesh(const Scene& /*in*/, const Object* /*obj*/, co
} }
} }
// TODO should we create the TextureUVMapping map in Convert<Material> to prevent redundant processing?
// create texture <-> uvname mapping for all materials
// key is texture number, value is data *
typedef std::map<uint32_t, const MLoopUV *> TextureUVMapping;
// key is material number, value is the TextureUVMapping for the material
typedef std::map<uint32_t, TextureUVMapping> MaterialTextureUVMappings;
MaterialTextureUVMappings matTexUvMappings;
const uint32_t maxMat = static_cast<const uint32_t>(mesh->mat.size());
for (uint32_t m = 0; m < maxMat; ++m)
{
// get material by index
const std::shared_ptr<Material> pMat = mesh->mat[m];
TextureUVMapping texuv;
const uint32_t maxTex = sizeof(pMat->mtex) / sizeof(pMat->mtex[0]);
for (uint32_t t = 0; t < maxTex; ++t)
{
if (pMat->mtex[t] && pMat->mtex[t]->uvname[0])
{
// get the CustomData layer for given uvname and correct type
const MLoopUV *pLoop = static_cast<const MLoopUV*>(getCustomDataLayerData(mesh->ldata, CD_MLOOPUV, pMat->mtex[t]->uvname));
if (pLoop)
{
texuv.insert(std::make_pair(t, pLoop));
}
}
}
matTexUvMappings.insert(std::make_pair(m, texuv));
}
// collect texture coordinates, they're stored in a separate per-face buffer // collect texture coordinates, they're stored in a separate per-face buffer
if (mesh->mtface || mesh->mloopuv) { if (mesh->mtface || mesh->mloopuv) {
if (mesh->totface > static_cast<int> ( mesh->mtface.size())) { if (mesh->totface > static_cast<int> ( mesh->mtface.size())) {
@ -1028,8 +1059,17 @@ void BlenderImporter::ConvertMesh(const Scene& /*in*/, const Object* /*obj*/, co
} }
for (std::vector<aiMesh*>::iterator it = temp->begin()+old; it != temp->end(); ++it) { for (std::vector<aiMesh*>::iterator it = temp->begin()+old; it != temp->end(); ++it) {
ai_assert((*it)->mNumVertices && (*it)->mNumFaces); ai_assert((*it)->mNumVertices && (*it)->mNumFaces);
const auto itMatTexUvMapping = matTexUvMappings.find((*it)->mMaterialIndex);
if (itMatTexUvMapping == matTexUvMappings.end()) {
// default behaviour like before
(*it)->mTextureCoords[0] = new aiVector3D[(*it)->mNumVertices]; (*it)->mTextureCoords[0] = new aiVector3D[(*it)->mNumVertices];
}
else {
// create texture coords for every mapped tex
for (uint32_t i = 0; i < itMatTexUvMapping->second.size(); ++i) {
(*it)->mTextureCoords[i] = new aiVector3D[(*it)->mNumVertices];
}
}
(*it)->mNumFaces = (*it)->mNumVertices = 0; (*it)->mNumFaces = (*it)->mNumVertices = 0;
} }
@ -1051,13 +1091,35 @@ void BlenderImporter::ConvertMesh(const Scene& /*in*/, const Object* /*obj*/, co
aiMesh* const out = temp[ mat_num_to_mesh_idx[ v.mat_nr ] ]; aiMesh* const out = temp[ mat_num_to_mesh_idx[ v.mat_nr ] ];
const aiFace& f = out->mFaces[out->mNumFaces++]; const aiFace& f = out->mFaces[out->mNumFaces++];
const auto itMatTexUvMapping = matTexUvMappings.find(v.mat_nr);
if (itMatTexUvMapping == matTexUvMappings.end()) {
// old behavior
aiVector3D* vo = &out->mTextureCoords[0][out->mNumVertices]; aiVector3D* vo = &out->mTextureCoords[0][out->mNumVertices];
for (unsigned int j = 0; j < f.mNumIndices; ++j,++vo,++out->mNumVertices) { for (unsigned int j = 0; j < f.mNumIndices; ++j, ++vo, ++out->mNumVertices) {
const MLoopUV& uv = mesh->mloopuv[v.loopstart + j]; const MLoopUV& uv = mesh->mloopuv[v.loopstart + j];
vo->x = uv.uv[0]; vo->x = uv.uv[0];
vo->y = uv.uv[1]; vo->y = uv.uv[1];
} }
}
else {
// create textureCoords for every mapped tex
for (uint32_t m = 0; m < itMatTexUvMapping->second.size(); ++m) {
const MLoopUV *tm = itMatTexUvMapping->second[m];
aiVector3D* vo = &out->mTextureCoords[m][out->mNumVertices];
uint32_t j = 0;
for (; j < f.mNumIndices; ++j, ++vo) {
const MLoopUV& uv = tm[v.loopstart + j];
vo->x = uv.uv[0];
vo->y = uv.uv[1];
}
// only update written mNumVertices in last loop
// TODO why must the numVertices be incremented here?
if (m == itMatTexUvMapping->second.size() - 1)
{
out->mNumVertices += j;
}
}
}
} }
} }

View File

@ -47,6 +47,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "BlenderScene.h" #include "BlenderScene.h"
#include "BlenderSceneGen.h" #include "BlenderSceneGen.h"
#include "BlenderDNA.h" #include "BlenderDNA.h"
#include "BlenderCustomData.h"
using namespace Assimp; using namespace Assimp;
using namespace Assimp::Blender; using namespace Assimp::Blender;
@ -481,6 +482,12 @@ template <> void Structure :: Convert<Mesh> (
ReadFieldPtr<ErrorPolicy_Igno>(dest.mcol,"*mcol",db); ReadFieldPtr<ErrorPolicy_Igno>(dest.mcol,"*mcol",db);
ReadFieldPtr<ErrorPolicy_Fail>(dest.mat,"**mat",db); ReadFieldPtr<ErrorPolicy_Fail>(dest.mat,"**mat",db);
ReadField<ErrorPolicy_Igno>(dest.vdata, "vdata", db);
ReadField<ErrorPolicy_Igno>(dest.edata, "edata", db);
ReadField<ErrorPolicy_Igno>(dest.fdata, "fdata", db);
ReadField<ErrorPolicy_Igno>(dest.pdata, "pdata", db);
ReadField<ErrorPolicy_Warn>(dest.ldata, "ldata", db);
db.reader->IncPtr(size); db.reader->IncPtr(size);
} }
@ -786,6 +793,42 @@ template <> void Structure :: Convert<Image> (
db.reader->IncPtr(size); db.reader->IncPtr(size);
} }
//--------------------------------------------------------------------------------
template <> void Structure::Convert<CustomData>(
CustomData& dest,
const FileDatabase& db
) const
{
ReadFieldArray<ErrorPolicy_Warn>(dest.typemap, "typemap", db);
ReadField<ErrorPolicy_Igno>(dest.pad_i1, "pad_i1", db);
ReadField<ErrorPolicy_Warn>(dest.totlayer, "totlayer", db);
ReadField<ErrorPolicy_Warn>(dest.maxlayer, "maxlayer", db);
ReadField<ErrorPolicy_Warn>(dest.totsize, "totsize", db);
ReadFieldPtrVector<ErrorPolicy_Warn>(dest.layers, "*layers", db);
db.reader->IncPtr(size);
}
//--------------------------------------------------------------------------------
template <> void Structure::Convert<CustomDataLayer>(
CustomDataLayer& dest,
const FileDatabase& db
) const
{
ReadField<ErrorPolicy_Fail>(dest.type, "type", db);
ReadField<ErrorPolicy_Fail>(dest.offset, "offset", db);
ReadField<ErrorPolicy_Fail>(dest.flag, "flag", db);
ReadField<ErrorPolicy_Fail>(dest.active, "active", db);
ReadField<ErrorPolicy_Fail>(dest.active_rnd, "active_rnd", db);
ReadField<ErrorPolicy_Fail>(dest.active_clone, "active_clone", db);
ReadField<ErrorPolicy_Fail>(dest.active_mask, "active_mask", db);
ReadField<ErrorPolicy_Fail>(dest.uid, "uid", db);
ReadFieldArray<ErrorPolicy_Warn>(dest.name, "name", db);
ReadCustomDataPtr<ErrorPolicy_Fail>(dest.data, dest.type, "*data", db);
db.reader->IncPtr(size);
}
//-------------------------------------------------------------------------------- //--------------------------------------------------------------------------------
void DNA::RegisterConverters() { void DNA::RegisterConverters() {
@ -822,6 +865,8 @@ void DNA::RegisterConverters() {
converters["Camera"] = DNA::FactoryPair( &Structure::Allocate<Camera>, &Structure::Convert<Camera> ); converters["Camera"] = DNA::FactoryPair( &Structure::Allocate<Camera>, &Structure::Convert<Camera> );
converters["MirrorModifierData"] = DNA::FactoryPair( &Structure::Allocate<MirrorModifierData>, &Structure::Convert<MirrorModifierData> ); converters["MirrorModifierData"] = DNA::FactoryPair( &Structure::Allocate<MirrorModifierData>, &Structure::Convert<MirrorModifierData> );
converters["Image"] = DNA::FactoryPair( &Structure::Allocate<Image>, &Structure::Convert<Image> ); converters["Image"] = DNA::FactoryPair( &Structure::Allocate<Image>, &Structure::Convert<Image> );
converters["CustomData"] = DNA::FactoryPair(&Structure::Allocate<CustomData>, &Structure::Convert<CustomData>);
converters["CustomDataLayer"] = DNA::FactoryPair(&Structure::Allocate<CustomDataLayer>, &Structure::Convert<CustomDataLayer>);
} }
#endif // ASSIMP_BUILD_NO_BLEND_IMPORTER #endif // ASSIMP_BUILD_NO_BLEND_IMPORTER

View File

@ -157,10 +157,16 @@ struct World : ElemBase {
// ------------------------------------------------------------------------------- // -------------------------------------------------------------------------------
struct MVert : ElemBase { struct MVert : ElemBase {
float co[3] FAIL; float co[3] FAIL;
float no[3] FAIL; float no[3] FAIL; // readed as short and divided through / 32767.f
char flag; char flag;
int mat_nr WARN; int mat_nr WARN;
int bweight; int bweight;
MVert() : ElemBase()
, mat_nr(0)
, flag(0)
, bweight(0)
{}
}; };
// ------------------------------------------------------------------------------- // -------------------------------------------------------------------------------
@ -369,6 +375,75 @@ struct Material : ElemBase {
std::shared_ptr<MTex> mtex[18]; std::shared_ptr<MTex> mtex[18];
}; };
/*
CustomDataLayer 104
int type 0 4
int offset 4 4
int flag 8 4
int active 12 4
int active_rnd 16 4
int active_clone 20 4
int active_mask 24 4
int uid 28 4
char name 32 64
void *data 96 8
*/
struct CustomDataLayer : ElemBase
{
int type;
int offset;
int flag;
int active;
int active_rnd;
int active_clone;
int active_mask;
int uid;
char name[64];
std::shared_ptr<void> data; // must be converted to real type according type member
CustomDataLayer()
: ElemBase()
, type(0)
, offset(0)
, flag(0)
, active(0)
, active_rnd(0)
, active_clone(0)
, active_mask(0)
, uid(0)
, data(nullptr)
{
memset(name, 0, sizeof name);
}
};
/*
CustomData 208
CustomDataLayer *layers 0 8
int typemap 8 168
int pad_i1 176 4
int totlayer 180 4
int maxlayer 184 4
int totsize 188 4
BLI_mempool *pool 192 8
CustomDataExternal *external 200 8
*/
struct CustomData : ElemBase
{
vector<std::shared_ptr<struct CustomDataLayer> > layers;
int typemap[42]; // CD_NUMTYPES
int pad_i1;
int totlayer;
int maxlayer;
int totsize;
/*
std::shared_ptr<BLI_mempool> pool;
std::shared_ptr<CustomDataExternal> external;
*/
};
// ------------------------------------------------------------------------------- // -------------------------------------------------------------------------------
struct Mesh : ElemBase { struct Mesh : ElemBase {
ID id FAIL; ID id FAIL;
@ -398,6 +473,12 @@ struct Mesh : ElemBase {
vector<MCol> mcol; vector<MCol> mcol;
vector< std::shared_ptr<Material> > mat FAIL; vector< std::shared_ptr<Material> > mat FAIL;
struct CustomData vdata;
struct CustomData edata;
struct CustomData fdata;
struct CustomData pdata;
struct CustomData ldata;
}; };
// ------------------------------------------------------------------------------- // -------------------------------------------------------------------------------

View File

@ -248,6 +248,17 @@ template <> void Structure :: Convert<Image> (
) const ) const
; ;
template <> void Structure::Convert<CustomData>(
CustomData& dest,
const FileDatabase& db
) const
;
template <> void Structure::Convert<CustomDataLayer>(
CustomDataLayer& dest,
const FileDatabase& db
) const
;
} }
} }

View File

@ -469,6 +469,8 @@ ADD_ASSIMP_IMPORTER( BLEND
BlenderBMesh.cpp BlenderBMesh.cpp
BlenderTessellator.h BlenderTessellator.h
BlenderTessellator.cpp BlenderTessellator.cpp
BlenderCustomData.h
BlenderCustomData.cpp
) )
ADD_ASSIMP_IMPORTER( IFC ADD_ASSIMP_IMPORTER( IFC

View File

@ -362,14 +362,23 @@ void STLImporter::LoadASCIIFile( aiNode *root ) {
pMesh->mNumFaces = 0; pMesh->mNumFaces = 0;
throw DeadlyImportError("Normal buffer size does not match position buffer size"); throw DeadlyImportError("Normal buffer size does not match position buffer size");
} }
// only process positionbuffer when filled, else exception when accessing with index operator
// see line 353: only warning is triggered
// see line 373(now): access to empty positionbuffer with index operator forced exception
if (!positionBuffer.empty()) {
pMesh->mNumFaces = static_cast<unsigned int>(positionBuffer.size() / 3); pMesh->mNumFaces = static_cast<unsigned int>(positionBuffer.size() / 3);
pMesh->mNumVertices = static_cast<unsigned int>(positionBuffer.size()); pMesh->mNumVertices = static_cast<unsigned int>(positionBuffer.size());
pMesh->mVertices = new aiVector3D[pMesh->mNumVertices]; pMesh->mVertices = new aiVector3D[pMesh->mNumVertices];
memcpy(pMesh->mVertices, positionBuffer.data(), pMesh->mNumVertices * sizeof(aiVector3D)); memcpy(pMesh->mVertices, &positionBuffer[0].x, pMesh->mNumVertices * sizeof(aiVector3D));
positionBuffer.clear(); positionBuffer.clear();
}
// also only process normalBuffer when filled, else exception when accessing with index operator
if (!normalBuffer.empty()) {
pMesh->mNormals = new aiVector3D[pMesh->mNumVertices]; pMesh->mNormals = new aiVector3D[pMesh->mNumVertices];
memcpy(pMesh->mNormals, normalBuffer.data(), pMesh->mNumVertices * sizeof(aiVector3D)); memcpy(pMesh->mNormals, &normalBuffer[0].x, pMesh->mNumVertices * sizeof(aiVector3D));
normalBuffer.clear(); normalBuffer.clear();
}
// now copy faces // now copy faces
addFacesToMesh(pMesh); addFacesToMesh(pMesh);

View File

@ -125,3 +125,30 @@ TEST_F(BlendImportMaterials, testImportMaterial)
ASSERT_PROPERTY_EQ(61, "mirror.glossSamples", mirrorGlossSamples); ASSERT_PROPERTY_EQ(61, "mirror.glossSamples", mirrorGlossSamples);
ASSERT_PROPERTY_FLOAT_EQ(0.87f, "mirror.glossAnisotropic", mirrorGlossAnisotropic); ASSERT_PROPERTY_FLOAT_EQ(0.87f, "mirror.glossAnisotropic", mirrorGlossAnisotropic);
} }
TEST_F(BlendImportMaterials, testImportMaterialwith2texturesAnd2TexCoordMappings)
{
const aiScene* pTest = im->ReadFile(ASSIMP_TEST_MODELS_DIR "/BLEND/plane_2_textures_2_texcoords_279.blend", aiProcess_ValidateDataStructure);
ASSERT_TRUE(pTest != NULL);
// material has 2 diffuse textures
ASSERT_TRUE(pTest->HasMaterials());
EXPECT_EQ(1, pTest->mNumMaterials);
const aiMaterial *pMat = pTest->mMaterials[0];
ASSERT_TRUE(nullptr != pMat);
ASSERT_EQ(2, pMat->GetTextureCount(aiTextureType_DIFFUSE));
aiString aPath;
aiTextureMapping tm = aiTextureMapping::aiTextureMapping_OTHER;
aiReturn result = pMat->GetTexture(aiTextureType_DIFFUSE, 0, &aPath, &tm);
ASSERT_EQ(aiReturn_SUCCESS, result);
result = pMat->GetTexture(aiTextureType_DIFFUSE, 1, &aPath, &tm);
ASSERT_EQ(aiReturn_SUCCESS, result);
// mesh has 2 texturecoord sets
ASSERT_TRUE(pTest->HasMeshes());
EXPECT_EQ(1, pTest->mNumMeshes);
const aiMesh *pMesh = pTest->mMeshes[0];
ASSERT_TRUE(nullptr != pMesh);
ASSERT_TRUE(pMesh->HasTextureCoords(0));
ASSERT_TRUE(pMesh->HasTextureCoords(1));
}