Browse Source

Merge branch 'master' into master

Kim Kulling 7 years ago
parent
commit
5f3aa142b4

+ 9 - 10
CONTRIBUTING.md

@@ -1,11 +1,10 @@
-#How to contribute
+# How to contribute
 
 
-If you want to contribute you can follow these setps:
-- Fist create your own clone of assimp
-- When you want to fix a bug or add a new feature create a branch on your own fork ( just follow https://help.github.com/articles/creating-a-pull-request-from-a-fork/ )
-- Push it to the repo and open a pull request
-- A pull request will start our CI-service, which checks if the build works for linux and windows. 
-  It will check for memory leaks, compiler warnings and memory alignment issues. If any of these tests fails: fix it and the tests will be reastarted automatically
-  - At the end we will perform a code review and merge your branch to the master branch.
-  
-  
+If you want to contribute, follow these steps:
+
+- First, create your own clone of assimp.
+- When you want to fix a bug or add a new feature, create a branch on your own fork following [these instructions](https://help.github.com/articles/creating-a-pull-request-from-a-fork/).
+- Push it to your fork of the repository and open a pull request.
+- A pull request will start our continuous integration service, which checks if the build works for Linux and Windows.
+  It will check for memory leaks, compiler warnings and memory alignment issues. If any of these tests fail, fix it and the tests will be restarted automatically.
+  - At the end, we will perform a code review and merge your branch to the master branch.

+ 4 - 0
appveyor.yml

@@ -32,6 +32,9 @@ install:
   - if "%APPVEYOR_BUILD_WORKER_IMAGE%"=="Visual Studio 2017" set CMAKE_GENERATOR_NAME=Visual Studio 15 2017
   - if "%APPVEYOR_BUILD_WORKER_IMAGE%"=="Visual Studio 2017" set CMAKE_GENERATOR_NAME=Visual Studio 15 2017
   - if "%platform%"=="x64" set CMAKE_GENERATOR_NAME=%CMAKE_GENERATOR_NAME% Win64
   - if "%platform%"=="x64" set CMAKE_GENERATOR_NAME=%CMAKE_GENERATOR_NAME% Win64
   - cmake %CMAKE_DEFINES% -G "%CMAKE_GENERATOR_NAME%"
   - cmake %CMAKE_DEFINES% -G "%CMAKE_GENERATOR_NAME%"
+  - set PATH=%PATH%;"C:\\Program Files (x86)\\Inno Setup 5"
+  - ps: Invoke-WebRequest -Uri https://download.microsoft.com/download/5/7/b/57b2947c-7221-4f33-b35e-2fc78cb10df4/vc_redist.x64.exe -OutFile .\packaging\windows-innosetup\vc_redist.x64.exe
+  - ps: Invoke-WebRequest -Uri https://download.microsoft.com/download/1/d/8/1d8137db-b5bb-4925-8c5d-927424a2e4de/vc_redist.x86.exe -OutFile .\packaging\windows-innosetup\vc_redist.x86.exe
   
   
 cache:
 cache:
   - code\assimp.dir\%CONFIGURATION%
   - code\assimp.dir\%CONFIGURATION%
@@ -50,6 +53,7 @@ build:
   project: Assimp.sln
   project: Assimp.sln
   
   
 after_build:
 after_build:
+  - if "%APPVEYOR_BUILD_WORKER_IMAGE%"=="Visual Studio 2017" iscc packaging\windows-innosetup\script.iss
   - 7z a assimp.7z bin\%CONFIGURATION%\* lib\%CONFIGURATION%\*
   - 7z a assimp.7z bin\%CONFIGURATION%\* lib\%CONFIGURATION%\*
 
 
 test_script:
 test_script:

+ 185 - 0
code/BlenderCustomData.cpp

@@ -0,0 +1,185 @@
+#include "BlenderCustomData.h"
+#include "BlenderDNA.h"
+#include <array>
+#include <functional>
+
+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)(ElemBase *pOut, const size_t cnt, const FileDatabase &db);
+        typedef ElemBase *  (*PCreate)(const size_t cnt);
+        typedef void(*PDestroy)(ElemBase *);
+
+#define IMPL_STRUCT_READ(ty)                                                    \
+        bool read##ty(ElemBase *v, const size_t cnt, const FileDatabase &db) {  \
+            return read<ty>(db.dna[#ty], dynamic_cast<ty *>(v), cnt, db);       \
+        }
+
+#define IMPL_STRUCT_CREATE(ty)                                                  \
+        ElemBase *create##ty(const size_t cnt) {                                \
+            return new ty[cnt];                                                 \
+        }
+
+#define IMPL_STRUCT_DESTROY(ty)                                                 \
+        void destroy##ty(ElemBase *pE) {                                        \
+            ty *p = dynamic_cast<ty *>(pE);                                     \
+            delete[]p;                                                          \
+        }
+
+        /**
+        *   @brief  helper macro to define Structure functions
+        */
+#define IMPL_STRUCT(ty)                                                         \
+        IMPL_STRUCT_READ(ty)                                                    \
+        IMPL_STRUCT_CREATE(ty)                                                  \
+        IMPL_STRUCT_DESTROY(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
+            PCreate Create;                       ///< function to allocate n type elements
+            PDestroy Destroy;
+
+            CustomDataTypeDescription(PRead read, PCreate create, PDestroy destroy)
+                : Read(read)
+                , Create(create)
+                , Destroy(destroy)
+            {}
+        };
+
+
+        /**
+        *   @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, &create##ty, &destroy##ty}
+
+        /**
+        *   @brief  helper macro to define CustomDataTypeDescription for UNSUPPORTED type
+        */
+#define DECL_UNSUPPORTED_CUSTOMDATATYPEDESCRIPTION          \
+        CustomDataTypeDescription{nullptr, 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
+        */
+        std::array<CustomDataTypeDescription, CD_NUMTYPES> 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<ElemBase> &out, const int 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.Create && cdtd.Destroy && cnt > 0) {
+                // allocate cnt elements and parse them from file
+                out.reset(cdtd.Create(cnt), cdtd.Destroy);
+                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 ElemBase * 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;
+        }
+    }
+}

+ 89 - 0
code/BlenderCustomData.h

@@ -0,0 +1,89 @@
+#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  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 * to struct data or nullptr if not found
+        */
+        const ElemBase * getCustomDataLayerData(const CustomData &customdata, CustomDataType cdtype, const std::string &name);
+    }
+}

+ 33 - 0
code/BlenderDNA.h

@@ -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<ElemBase>&out, int cdtype, const char* name, const FileDatabase& db) const;
+
 private:
 private:
 
 
     // --------------------------------------------------------
     // --------------------------------------------------------
@@ -803,6 +825,17 @@ private:
     FileDatabase& db;
     FileDatabase& db;
 };
 };
 
 
+/**
+*   @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<ElemBase> &out, int cdtype, size_t cnt, const FileDatabase &db);
+
+
     } // end Blend
     } // end Blend
 } // end Assimp
 } // end Assimp
 
 

+ 102 - 0
code/BlenderDNA.inl

@@ -307,6 +307,108 @@ 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<ElemBase>&out, int cdtype, const char* name, const FileDatabase& db) const {
+
+	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, 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,

+ 65 - 8
code/BlenderLoader.cpp

@@ -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,32 @@ 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 ElemBase *pLoop = getCustomDataLayerData(mesh->ldata, CD_MLOOPUV, pMat->mtex[t]->uvname);
+                if (pLoop) {
+                    texuv.insert(std::make_pair(t, dynamic_cast<const MLoopUV *>(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 +1055,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);
-
-            (*it)->mTextureCoords[0] = new aiVector3D[(*it)->mNumVertices];
+            const auto itMatTexUvMapping = matTexUvMappings.find((*it)->mMaterialIndex);
+            if (itMatTexUvMapping == matTexUvMappings.end()) {
+                // default behaviour like before
+                (*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 +1087,34 @@ 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++];
 
 
-            aiVector3D* vo = &out->mTextureCoords[0][out->mNumVertices];
-            for (unsigned int j = 0; j < f.mNumIndices; ++j,++vo,++out->mNumVertices) {
-                const MLoopUV& uv = mesh->mloopuv[v.loopstart + j];
-                vo->x = uv.uv[0];
-                vo->y = uv.uv[1];
+            const auto itMatTexUvMapping = matTexUvMappings.find(v.mat_nr);
+            if (itMatTexUvMapping == matTexUvMappings.end()) {
+                // old behavior
+                aiVector3D* vo = &out->mTextureCoords[0][out->mNumVertices];
+                for (unsigned int j = 0; j < f.mNumIndices; ++j, ++vo, ++out->mNumVertices) {
+                    const MLoopUV& uv = mesh->mloopuv[v.loopstart + j];
+                    vo->x = uv.uv[0];
+                    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;
+                    }
+                }
             }
             }
-
         }
         }
     }
     }
 
 

+ 45 - 0
code/BlenderScene.cpp

@@ -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

+ 80 - 1
code/BlenderScene.h

@@ -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()
+        , flag(0)
+        , mat_nr(0)
+        , bweight(0)
+    {}
 };
 };
 
 
 // -------------------------------------------------------------------------------
 // -------------------------------------------------------------------------------
@@ -369,6 +375,73 @@ 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<ElemBase> 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 +471,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;
 };
 };
 
 
 // -------------------------------------------------------------------------------
 // -------------------------------------------------------------------------------

+ 11 - 0
code/BlenderSceneGen.h

@@ -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
+    ;
 
 
     }
     }
 }
 }

+ 2 - 0
code/CMakeLists.txt

@@ -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

+ 12 - 17
code/ObjFileImporter.cpp

@@ -76,41 +76,36 @@ using namespace std;
 
 
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
 //  Default constructor
 //  Default constructor
-ObjFileImporter::ObjFileImporter() :
-    m_Buffer(),
-    m_pRootObject( NULL ),
-    m_strAbsPath( "" )
-{
+ObjFileImporter::ObjFileImporter()
+: m_Buffer()
+, m_pRootObject( nullptr )
+, m_strAbsPath( "" ) {
     DefaultIOSystem io;
     DefaultIOSystem io;
     m_strAbsPath = io.getOsSeparator();
     m_strAbsPath = io.getOsSeparator();
 }
 }
 
 
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
 //  Destructor.
 //  Destructor.
-ObjFileImporter::~ObjFileImporter()
-{
+ObjFileImporter::~ObjFileImporter() {
     delete m_pRootObject;
     delete m_pRootObject;
-    m_pRootObject = NULL;
+    m_pRootObject = nullptr;
 }
 }
 
 
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
 //  Returns true, if file is an obj file.
 //  Returns true, if file is an obj file.
-bool ObjFileImporter::CanRead( const std::string& pFile, IOSystem*  pIOHandler , bool checkSig ) const
-{
-    if(!checkSig) //Check File Extension
-    {
+bool ObjFileImporter::CanRead( const std::string& pFile, IOSystem*  pIOHandler , bool checkSig ) const {
+    if(!checkSig)  {
+        //Check File Extension
         return SimpleExtensionCheck(pFile,"obj");
         return SimpleExtensionCheck(pFile,"obj");
-    }
-    else //Check file Header
-    {
+    } else {
+        // Check file Header
         static const char *pTokens[] = { "mtllib", "usemtl", "v ", "vt ", "vn ", "o ", "g ", "s ", "f " };
         static const char *pTokens[] = { "mtllib", "usemtl", "v ", "vt ", "vn ", "o ", "g ", "s ", "f " };
         return BaseImporter::SearchFileHeaderForToken(pIOHandler, pFile, pTokens, 9 );
         return BaseImporter::SearchFileHeaderForToken(pIOHandler, pFile, pTokens, 9 );
     }
     }
 }
 }
 
 
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
-const aiImporterDesc* ObjFileImporter::GetInfo () const
-{
+const aiImporterDesc* ObjFileImporter::GetInfo () const {
     return &desc;
     return &desc;
 }
 }
 
 

+ 17 - 8
code/STLLoader.cpp

@@ -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");
         }
         }
-        pMesh->mNumFaces = static_cast<unsigned int>(positionBuffer.size() / 3);
-        pMesh->mNumVertices = static_cast<unsigned int>(positionBuffer.size());
-        pMesh->mVertices = new aiVector3D[pMesh->mNumVertices];
-        memcpy(pMesh->mVertices, positionBuffer.data(), pMesh->mNumVertices * sizeof(aiVector3D));
-        positionBuffer.clear();
-        pMesh->mNormals = new aiVector3D[pMesh->mNumVertices];
-        memcpy(pMesh->mNormals, normalBuffer.data(), pMesh->mNumVertices * sizeof(aiVector3D));
-        normalBuffer.clear();
+
+        // 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->mNumVertices = static_cast<unsigned int>(positionBuffer.size());
+            pMesh->mVertices = new aiVector3D[pMesh->mNumVertices];
+            memcpy(pMesh->mVertices, &positionBuffer[0].x, pMesh->mNumVertices * sizeof(aiVector3D));
+            positionBuffer.clear();
+        }
+        // also only process normalBuffer when filled, else exception when accessing with index operator
+        if (!normalBuffer.empty()) {
+            pMesh->mNormals = new aiVector3D[pMesh->mNumVertices];
+            memcpy(pMesh->mNormals, &normalBuffer[0].x, pMesh->mNumVertices * sizeof(aiVector3D));
+            normalBuffer.clear();
+        }
 
 
         // now copy faces
         // now copy faces
         addFacesToMesh(pMesh);
         addFacesToMesh(pMesh);

+ 26 - 36
packaging/windows-innosetup/script.iss

@@ -2,7 +2,7 @@
 
 
 [Setup]
 [Setup]
 AppName=Open Asset Import Library - SDK
 AppName=Open Asset Import Library - SDK
-AppVerName=Open Asset Import Library - SDK (v3.3.1)
+AppVerName=Open Asset Import Library - SDK (v4.1.0)
 DefaultDirName={pf}\Assimp
 DefaultDirName={pf}\Assimp
 DefaultGroupName=Assimp
 DefaultGroupName=Assimp
 UninstallDisplayIcon={app}\bin\x86\assimp.exe
 UninstallDisplayIcon={app}\bin\x86\assimp.exe
@@ -12,9 +12,9 @@ SetupIconFile=..\..\tools\shared\assimp_tools_icon.ico
 WizardImageFile=compiler:WizModernImage-IS.BMP
 WizardImageFile=compiler:WizModernImage-IS.BMP
 WizardSmallImageFile=compiler:WizModernSmallImage-IS.BMP
 WizardSmallImageFile=compiler:WizModernSmallImage-IS.BMP
 LicenseFile=License.rtf
 LicenseFile=License.rtf
-OutputBaseFileName=assimp-sdk-3.3.1-setup
-VersionInfoVersion=3.3.1.0
-VersionInfoTextVersion=3.3.1
+OutputBaseFileName=assimp-sdk-4.1.0-setup
+VersionInfoVersion=4.1.0.0
+VersionInfoTextVersion=4.1.0
 VersionInfoCompany=Assimp Development Team
 VersionInfoCompany=Assimp Development Team
 ArchitecturesInstallIn64BitMode=x64
 ArchitecturesInstallIn64BitMode=x64
 
 
@@ -30,20 +30,19 @@ Name: "help";        Description: "Help Files"; Types: full compact
 Name: "samples";     Description: "Samples"; Types: full
 Name: "samples";     Description: "Samples"; Types: full
 Name: "test";        Description: "Test Models (BSD-licensed)"; Types: full
 Name: "test";        Description: "Test Models (BSD-licensed)"; Types: full
 Name: "test_nonbsd"; Description: "Test Models (other (free) licenses)"; Types: full
 Name: "test_nonbsd"; Description: "Test Models (other (free) licenses)"; Types: full
-Name: "pyassimp";    Description: "Python Bindings"; Types: full
-Name: "dassimp";     Description: "D Bindings"; Types: full
-Name: "assimp_net";  Description: "C#/.NET Bindings"; Types: full
+;Name: "pyassimp";    Description: "Python Bindings"; Types: full
+;Name: "dassimp";     Description: "D Bindings"; Types: full
+;Name: "assimp_net";  Description: "C#/.NET Bindings"; Types: full
 
 
 [Run]
 [Run]
-Filename: "{app}\stub\vc_redist.x86.exe"; Parameters: "/qb"; StatusMsg: "Installing VS2015 redistributable package (32 Bit)"; Check: not IsWin64
-Filename: "{app}\stub\vc_redist.x64.exe"; Parameters: "/qb"; StatusMsg: "Installing VS2015 redistributable package (64 Bit)"; Check: IsWin64
+;Filename: "{app}\stub\vc_redist.x86.exe"; Parameters: "/qb"; StatusMsg: "Installing VS2017 redistributable package (32 Bit)"; Check: not IsWin64
+Filename: "{app}\stub\vc_redist.x64.exe"; Parameters: "/qb"; StatusMsg: "Installing VS2017 redistributable package (64 Bit)"; Check: IsWin64
 
 
 [Files]
 [Files]
-
 Source: "readme_installer.txt"; DestDir: "{app}"; Flags: isreadme
 Source: "readme_installer.txt"; DestDir: "{app}"; Flags: isreadme
 
 
 ; Installer stub
 ; Installer stub
-Source: "vc_redist.x86.exe"; DestDir: "{app}\stub\"; Check: not IsWin64
+;Source: "vc_redist.x86.exe"; DestDir: "{app}\stub\"; Check: not IsWin64
 Source: "vc_redist.x64.exe"; DestDir: "{app}\stub\"; Check: IsWin64
 Source: "vc_redist.x64.exe"; DestDir: "{app}\stub\"; Check: IsWin64
 
 
 ; Common stuff
 ; Common stuff
@@ -55,27 +54,27 @@ Source: "WEB"; DestDir: "{app}"
 Source: "..\..\scripts\*"; DestDir: "{app}\scripts"; Flags: recursesubdirs
 Source: "..\..\scripts\*"; DestDir: "{app}\scripts"; Flags: recursesubdirs
 
 
 ; x86 binaries
 ; x86 binaries
-Source: "..\..\bin\release\x86\assimp-vc140-mt.dll";  DestDir: "{app}\bin\x86"
-Source: "..\..\bin\release\x86\assimp_viewer.exe";      DestDir: "{app}\bin\x86"; Components: tools
-Source: "D3DCompiler_42.dll";                         DestDir: "{app}\bin\x86"; Components: tools
-Source: "D3DX9_42.dll";                               DestDir: "{app}\bin\x86"; Components: tools
-Source: "..\..\bin\release\x86\assimp.exe";           DestDir: "{app}\bin\x86"; Components: tools
+;Source: "..\..\bin\release\x86\assimp-vc140-mt.dll";  DestDir: "{app}\bin\x86"
+;Source: "..\..\bin\release\x86\assimp_viewer.exe";    DestDir: "{app}\bin\x86"; Components: tools
+;Source: "C:\Windows\SysWOW64\D3DCompiler_42.dll";     DestDir: "{app}\bin\x86"; Components: tools
+;Source: "C:\Windows\SysWOW64\D3DX9_42.dll";           DestDir: "{app}\bin\x86"; Components: tools
+;Source: "..\..\bin\release\x86\assimp.exe";           DestDir: "{app}\bin\x86"; Components: tools
 
 
 ; x64 binaries
 ; x64 binaries
-Source: "..\..\bin\release\x64\assimp-vc140-mt.dll";  DestDir: "{app}\bin\x64"
-Source: "..\..\bin\release\x64\assimp_viewer.exe";      DestDir: "{app}\bin\x64"; Components: tools
-Source: "D3DCompiler_42_x64.dll";                     DestDir: "{app}\bin\x64"; DestName: "D3DCompiler_42.dll"; Components: tools
-Source: "D3DX9_42_x64.dll";                           DestDir: "{app}\bin\x64"; DestName: "D3DX9_42.dll"; Components: tools
-Source: "..\..\bin\release\x64\assimp.exe";           DestDir: "{app}\bin\x64"; Components: tools
+Source: "..\..\bin\release\assimp-vc140-mt.dll";  DestDir: "{app}\bin\x64"
+Source: "..\..\bin\release\assimp_viewer.exe";    DestDir: "{app}\bin\x64"; Components: tools
+Source: "C:\Windows\SysWOW64\D3DCompiler_42.dll"; DestDir: "{app}\bin\x64"; DestName: "D3DCompiler_42.dll"; Components: tools
+Source: "C:\Windows\SysWOW64\D3DX9_42.dll";       DestDir: "{app}\bin\x64"; DestName: "D3DX9_42.dll"; Components: tools
+Source: "..\..\bin\release\assimp.exe";           DestDir: "{app}\bin\x64"; Components: tools
 
 
 ; Documentation
 ; Documentation
-Source: "..\..\doc\AssimpDoc_Html\AssimpDoc.chm"; DestDir: "{app}\doc"; Components: help
-Source: "..\..\doc\AssimpCmdDoc_Html\AssimpCmdDoc.chm"; DestDir: "{app}\doc"; Components: help
-Source: "..\..\doc\datastructure.xml"; DestDir: "{app}\doc"; Components: help
+;Source: "..\..\doc\AssimpDoc_Html\AssimpDoc.chm"; DestDir: "{app}\doc"; Components: help
+;Source: "..\..\doc\AssimpCmdDoc_Html\AssimpCmdDoc.chm"; DestDir: "{app}\doc"; Components: help
+;Source: "..\..\doc\datastructure.xml"; DestDir: "{app}\doc"; Components: help
 
 
 ; Import libraries
 ; Import libraries
-Source: "..\..\lib\release\x86\assimp.lib"; DestDir: "{app}\lib\x86"
-Source: "..\..\lib\release\x64\assimp.lib"; DestDir: "{app}\lib\x64"
+;Source: "..\..\lib\release\x86\assimp.lib"; DestDir: "{app}\lib\x86"
+Source: "..\..\lib\release\assimp-vc140-mt.lib"; DestDir: "{app}\lib\x64"
 
 
 ; Samples
 ; Samples
 Source: "..\..\samples\*"; DestDir: "{app}\samples"; Flags: recursesubdirs; Components: samples
 Source: "..\..\samples\*"; DestDir: "{app}\samples"; Flags: recursesubdirs; Components: samples
@@ -84,7 +83,7 @@ Source: "..\..\samples\*"; DestDir: "{app}\samples"; Flags: recursesubdirs; Comp
 Source: "..\..\include\*"; DestDir: "{app}\include"; Flags: recursesubdirs
 Source: "..\..\include\*"; DestDir: "{app}\include"; Flags: recursesubdirs
 
 
 ; dAssimp
 ; dAssimp
-Source: "..\..\port\dAssimp\*"; DestDir: "{app}\port\D"; Flags: recursesubdirs; Components: dassimp
+;Source: "..\..\port\dAssimp\*"; DestDir: "{app}\port\D"; Flags: recursesubdirs; Components: dassimp
 
 
 ; Assimp.NET
 ; Assimp.NET
 ;Source: "..\..\port\Assimp.NET\*"; DestDir: "{app}\port\C#"; Flags: recursesubdirs; Components: assimp_net
 ;Source: "..\..\port\Assimp.NET\*"; DestDir: "{app}\port\C#"; Flags: recursesubdirs; Components: assimp_net
@@ -97,15 +96,6 @@ Source: "..\..\port\dAssimp\*"; DestDir: "{app}\port\D"; Flags: recursesubdirs;
 ;Source: "..\..\test\regression\*"; DestDir: "{app}\test\regression"; Flags: recursesubdirs; Components: test
 ;Source: "..\..\test\regression\*"; DestDir: "{app}\test\regression"; Flags: recursesubdirs; Components: test
 ;Source: "..\..\test\models-nonbsd\*"; DestDir: "{app}\test\models-nonbsd"; Flags: recursesubdirs; Components: test_nonbsd
 ;Source: "..\..\test\models-nonbsd\*"; DestDir: "{app}\test\models-nonbsd"; Flags: recursesubdirs; Components: test_nonbsd
 
 
-; Source Code & Workspaces
-;Source: "..\..\code\*"; Excludes: "*.o"; DestDir: "{app}\code"; Flags: recursesubdirs; Components: wsource
-;Source: "..\..\workspaces\vc8\*.sln"; DestDir: "{app}\workspaces\vc8"; Components: wsource and vc8
-;Source: "..\..\workspaces\vc8\*.vcproj"; DestDir: "{app}\workspaces\vc8"; Components: wsource and vc8
-;Source: "..\..\workspaces\vc9\*.sln"; DestDir: "{app}\workspaces\vc9"; Components: wsource and vc9
-;Source: "..\..\workspaces\vc9\*.vcproj"; DestDir: "{app}\workspaces\vc9"; Components: wsource and vc9
-
-; Source: "Readme.txt"; DestDir: "{app}"; Flags: isreadme
-
 [Icons]
 [Icons]
 Name: "{group}\Assimp Manual"; Filename: "{app}\doc\AssimpDoc.chm" ; Components: help
 Name: "{group}\Assimp Manual"; Filename: "{app}\doc\AssimpDoc.chm" ; Components: help
 Name: "{group}\Assimp Command Line Manual"; Filename: "{app}\doc\AssimpCmdDoc.chm"; Components: help
 Name: "{group}\Assimp Command Line Manual"; Filename: "{app}\doc\AssimpCmdDoc.chm"; Components: help

BIN
test/models/BLEND/plane_2_textures_2_texcoords_279.blend


+ 27 - 0
test/unit/utBlendImportMaterials.cpp

@@ -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));
+}

+ 57 - 0
tools/make/build_env_win32.bat

@@ -0,0 +1,57 @@
+@echo off
+set "initialdir=%cd%"
+goto:main
+
+:exitsucc
+cd /d "%initialdir%"
+set initialdir=
+
+set MSBUILD_15="C:\Program Files (x86)\Microsoft Visual Studio\2018\Professional\MSBuild\15.0\Bin\msbuild.exe"
+set MSBUILD_14="C:\Program Files (x86)\MSBuild\14.0\Bin\msbuild.exe"
+
+if not "%VS150%"=="" set MSBUILD_15="%VS150%\MSBuild\15.0\Bin\msbuild.exe"
+
+if /i %VS_VERSION%==2017 (
+	set MS_BUILD_EXE=%MSBUILD_15%
+	set PLATFORM_VER=v141
+) else (
+	set MS_BUILD_EXE=%MSBUILD_14%
+	set PLATFORM_VER=v140
+)
+
+set MSBUILD=%MS_BUILD_EXE%
+
+exit /b 0
+
+:main
+if not defined PLATFORM set "PLATFORM=x64"
+
+::my work here is done?
+
+set PATH_VSWHERE=C:\Program Files (x86)\Microsoft Visual Studio\Installer\
+REM set PATH_STUDIO="C:\Program Files (x86)\Microsoft Visual Studio\2017\Professional\"
+
+for /f "usebackq tokens=*" %%i in (`"%PATH_VSWHERE%vswhere" -latest -products * -requires Microsoft.Component.MSBuild -property installationPath`) do (
+  set InstallDir=%%i
+)
+
+IF EXIST "%InstallDir%\VC\Auxiliary\Build\vcvarsall.bat" set VS150=%InstallDir%\
+
+set "CMAKE_GENERATOR=Visual Studio 15 2017 Win64"
+if not "%VS150%"=="" call "%VS150%\VC\Auxiliary\Build\vcvarsall.bat" %PLATFORM% && echo found VS 2o17 && set PLATFORM_VER=v141 && set VS_VERSION=2017 && goto:exitsucc
+
+set "CMAKE_GENERATOR=Visual Studio 14 2015 Win64"
+if defined VS140COMNTOOLS call "%VS140COMNTOOLS%..\..\VC\vcvarsall.bat" %PLATFORM% && echo found VS 2o15 && set PLATFORM_VER=v140 && set VS_VERSION=2015 && goto:exitsucc
+
+if defined VS130COMNTOOLS echo call ghostbusters... found lost VS version
+
+set "CMAKE_GENERATOR=Visual Studio 12 2013 Win64"
+if defined VS120COMNTOOLS call "%VS120COMNTOOLS%..\..\VC\vcvarsall.bat" %PLATFORM% && echo found VS 2o13 && set PLATFORM_VER=v120 && set VS_VERSION=2013 && goto:exitsucc
+
+set "CMAKE_GENERATOR=Visual Studio 11 2012 Win64"
+if defined VS110COMNTOOLS call "%VS110COMNTOOLS%..\..\VC\vcvarsall.bat" %PLATFORM% && echo found VS 2o12 && set PLATFORM_VER=v110 && set VS_VERSION=2012 && goto:exitsucc
+
+set "CMAKE_GENERATOR=Visual Studio 10 2010 Win64"
+if defined VS100COMNTOOLS call "%VS100COMNTOOLS%..\..\VC\vcvarsall.bat" %PLATFORM% && echo found VS 2o1o && set PLATFORM_VER=v100 && set VS_VERSION=2010 && goto:exitsucc
+
+goto:exitsucc

+ 18 - 0
tools/make/make_all_win32_x64.bat

@@ -0,0 +1,18 @@
+rem @echo off
+setlocal
+call build_env_win32.bat
+
+set BUILD_CONFIG=release
+set PLATFORM_CONFIG=x64
+set MAX_CPU_CONFIG=4
+
+set CONFIG_PARAMETER=/p:Configuration="%BUILD_CONFIG%"
+set PLATFORM_PARAMETER=/p:Platform="%PLATFORM_CONFIG%"
+set CPU_PARAMETER=/maxcpucount:%MAX_CPU_CONFIG%
+set PLATFORM_TOOLSET=/p:PlatformToolset=%PLATFORM_VER%
+
+pushd ..\..\
+cmake CMakeLists.txt -G "Visual Studio 15 2017 Win64"
+%MSBUILD% assimp.sln %CONFIG_PARAMETER% %PLATFORM_PARAMETER% %CPU_PARAMETER% %PLATFORM_TOOLSET%
+popd
+endlocal