Pārlūkot izejas kodu

Merge branch 'master' into update_ai_texture_type_max

Kim Kulling 3 gadi atpakaļ
vecāks
revīzija
5e69517742

+ 32 - 93
code/AssetLib/FBX/FBXDocument.cpp

@@ -4,7 +4,6 @@ Open Asset Import Library (assimp)
 
 Copyright (c) 2006-2022, assimp team
 
-
 All rights reserved.
 
 Redistribution and use of this software in source and binary forms,
@@ -68,23 +67,13 @@ namespace FBX {
 using namespace Util;
 
 // ------------------------------------------------------------------------------------------------
-LazyObject::LazyObject(uint64_t id, const Element& element, const Document& doc)
-: doc(doc)
-, element(element)
-, id(id)
-, flags() {
-    // empty
-}
-
-// ------------------------------------------------------------------------------------------------
-LazyObject::~LazyObject()
-{
+LazyObject::LazyObject(uint64_t id, const Element& element, const Document& doc) : 
+        doc(doc), element(element), id(id), flags() {
     // empty
 }
 
 // ------------------------------------------------------------------------------------------------
-const Object* LazyObject::Get(bool dieOnError)
-{
+const Object* LazyObject::Get(bool dieOnError) {
     if(IsBeingConstructed() || FailedToConstruct()) {
         return nullptr;
     }
@@ -234,17 +223,8 @@ const Object* LazyObject::Get(bool dieOnError)
 }
 
 // ------------------------------------------------------------------------------------------------
-Object::Object(uint64_t id, const Element& element, const std::string& name)
-: element(element)
-, name(name)
-, id(id)
-{
-    // empty
-}
-
-// ------------------------------------------------------------------------------------------------
-Object::~Object()
-{
+Object::Object(uint64_t id, const Element& element, const std::string& name) :
+        element(element), name(name), id(id) {
     // empty
 }
 
@@ -255,16 +235,8 @@ FileGlobalSettings::FileGlobalSettings(const Document &doc, std::shared_ptr<cons
 }
 
 // ------------------------------------------------------------------------------------------------
-FileGlobalSettings::~FileGlobalSettings()
-{
-    // empty
-}
-
-// ------------------------------------------------------------------------------------------------
-Document::Document(const Parser& parser, const ImportSettings& settings)
-: settings(settings)
-, parser(parser)
-{
+Document::Document(const Parser& parser, const ImportSettings& settings) :
+     settings(settings), parser(parser) {
 	ASSIMP_LOG_DEBUG("Creating FBX Document");
 
     // Cannot use array default initialization syntax because vc8 fails on it
@@ -285,8 +257,7 @@ Document::Document(const Parser& parser, const ImportSettings& settings)
 }
 
 // ------------------------------------------------------------------------------------------------
-Document::~Document()
-{
+Document::~Document() {
     for(ObjectMap::value_type& v : objects) {
         delete v.second;
     }
@@ -348,8 +319,7 @@ void Document::ReadHeader() {
 }
 
 // ------------------------------------------------------------------------------------------------
-void Document::ReadGlobalSettings()
-{
+void Document::ReadGlobalSettings() {
     const Scope& sc = parser.GetRootScope();
     const Element* const ehead = sc["GlobalSettings"];
     if ( nullptr == ehead || !ehead->Compound() ) {
@@ -370,8 +340,7 @@ void Document::ReadGlobalSettings()
 }
 
 // ------------------------------------------------------------------------------------------------
-void Document::ReadObjects()
-{
+void Document::ReadObjects() {
     // read ID objects from "Objects" section
     const Scope& sc = parser.GetRootScope();
     const Element* const eobjects = sc["Objects"];
@@ -418,8 +387,7 @@ void Document::ReadObjects()
 }
 
 // ------------------------------------------------------------------------------------------------
-void Document::ReadPropertyTemplates()
-{
+void Document::ReadPropertyTemplates() {
     const Scope& sc = parser.GetRootScope();
     // read property templates from "Definitions" section
     const Element* const edefs = sc["Definitions"];
@@ -476,8 +444,7 @@ void Document::ReadPropertyTemplates()
 }
 
 // ------------------------------------------------------------------------------------------------
-void Document::ReadConnections()
-{
+void Document::ReadConnections() {
     const Scope& sc = parser.GetRootScope();
     // read property templates from "Definitions" section
     const Element* const econns = sc["Connections"];
@@ -524,8 +491,7 @@ void Document::ReadConnections()
 }
 
 // ------------------------------------------------------------------------------------------------
-const std::vector<const AnimationStack*>& Document::AnimationStacks() const
-{
+const std::vector<const AnimationStack*>& Document::AnimationStacks() const {
     if (!animationStacksResolved.empty() || animationStacks.empty()) {
         return animationStacksResolved;
     }
@@ -545,17 +511,15 @@ const std::vector<const AnimationStack*>& Document::AnimationStacks() const
 }
 
 // ------------------------------------------------------------------------------------------------
-LazyObject* Document::GetObject(uint64_t id) const
-{
+LazyObject* Document::GetObject(uint64_t id) const {
     ObjectMap::const_iterator it = objects.find(id);
     return it == objects.end() ? nullptr : (*it).second;
 }
 
-#define MAX_CLASSNAMES 6
+constexpr size_t MAX_CLASSNAMES  = 6;
 
 // ------------------------------------------------------------------------------------------------
-std::vector<const Connection*> Document::GetConnectionsSequenced(uint64_t id, const ConnectionMap& conns) const
-{
+std::vector<const Connection*> Document::GetConnectionsSequenced(uint64_t id, const ConnectionMap& conns) const {
     std::vector<const Connection*> temp;
 
     const std::pair<ConnectionMap::const_iterator,ConnectionMap::const_iterator> range =
@@ -573,11 +537,9 @@ std::vector<const Connection*> Document::GetConnectionsSequenced(uint64_t id, co
 
 // ------------------------------------------------------------------------------------------------
 std::vector<const Connection*> Document::GetConnectionsSequenced(uint64_t id, bool is_src,
-    const ConnectionMap& conns,
-    const char* const* classnames,
-    size_t count) const
-
-{
+        const ConnectionMap& conns,
+        const char* const* classnames,
+        size_t count) const {
     ai_assert(classnames);
     ai_assert( count != 0 );
     ai_assert( count <= MAX_CLASSNAMES);
@@ -622,95 +584,72 @@ std::vector<const Connection*> Document::GetConnectionsSequenced(uint64_t id, bo
 }
 
 // ------------------------------------------------------------------------------------------------
-std::vector<const Connection*> Document::GetConnectionsBySourceSequenced(uint64_t source) const
-{
+std::vector<const Connection*> Document::GetConnectionsBySourceSequenced(uint64_t source) const {
     return GetConnectionsSequenced(source, ConnectionsBySource());
 }
 
 // ------------------------------------------------------------------------------------------------
-std::vector<const Connection*> Document::GetConnectionsBySourceSequenced(uint64_t src, const char* classname) const
-{
+std::vector<const Connection*> Document::GetConnectionsBySourceSequenced(uint64_t src, const char* classname) const {
     const char* arr[] = {classname};
     return GetConnectionsBySourceSequenced(src, arr,1);
 }
 
 // ------------------------------------------------------------------------------------------------
 std::vector<const Connection*> Document::GetConnectionsBySourceSequenced(uint64_t source,
-        const char* const* classnames, size_t count) const
-{
+        const char* const* classnames, size_t count) const {
     return GetConnectionsSequenced(source, true, ConnectionsBySource(),classnames, count);
 }
 
 // ------------------------------------------------------------------------------------------------
 std::vector<const Connection*> Document::GetConnectionsByDestinationSequenced(uint64_t dest,
-        const char* classname) const
-{
+        const char* classname) const {
     const char* arr[] = {classname};
     return GetConnectionsByDestinationSequenced(dest, arr,1);
 }
 
 // ------------------------------------------------------------------------------------------------
-std::vector<const Connection*> Document::GetConnectionsByDestinationSequenced(uint64_t dest) const
-{
+std::vector<const Connection*> Document::GetConnectionsByDestinationSequenced(uint64_t dest) const {
     return GetConnectionsSequenced(dest, ConnectionsByDestination());
 }
 
 // ------------------------------------------------------------------------------------------------
 std::vector<const Connection*> Document::GetConnectionsByDestinationSequenced(uint64_t dest,
-    const char* const* classnames, size_t count) const
-
-{
+        const char* const* classnames, size_t count) const {
     return GetConnectionsSequenced(dest, false, ConnectionsByDestination(),classnames, count);
 }
 
 // ------------------------------------------------------------------------------------------------
 Connection::Connection(uint64_t insertionOrder,  uint64_t src, uint64_t dest, const std::string& prop,
-        const Document& doc)
-
-: insertionOrder(insertionOrder)
-, prop(prop)
-, src(src)
-, dest(dest)
-, doc(doc)
-{
+            const Document& doc) :
+            insertionOrder(insertionOrder), prop(prop), src(src), dest(dest), doc(doc) {
     ai_assert(doc.Objects().find(src) != doc.Objects().end());
     // dest may be 0 (root node)
     ai_assert(!dest || doc.Objects().find(dest) != doc.Objects().end());
 }
 
 // ------------------------------------------------------------------------------------------------
-Connection::~Connection()
-{
-    // empty
-}
-
-// ------------------------------------------------------------------------------------------------
-LazyObject& Connection::LazySourceObject() const
-{
+LazyObject& Connection::LazySourceObject() const {
     LazyObject* const lazy = doc.GetObject(src);
     ai_assert(lazy);
     return *lazy;
 }
 
 // ------------------------------------------------------------------------------------------------
-LazyObject& Connection::LazyDestinationObject() const
-{
+LazyObject& Connection::LazyDestinationObject() const {
     LazyObject* const lazy = doc.GetObject(dest);
     ai_assert(lazy);
     return *lazy;
 }
 
 // ------------------------------------------------------------------------------------------------
-const Object* Connection::SourceObject() const
-{
+const Object* Connection::SourceObject() const {
     LazyObject* const lazy = doc.GetObject(src);
     ai_assert(lazy);
     return lazy->Get();
 }
 
 // ------------------------------------------------------------------------------------------------
-const Object* Connection::DestinationObject() const
-{
+const Object* Connection::DestinationObject() const {
     LazyObject* const lazy = doc.GetObject(dest);
     ai_assert(lazy);
     return lazy->Get();
@@ -719,4 +658,4 @@ const Object* Connection::DestinationObject() const
 } // !FBX
 } // !Assimp
 
-#endif
+#endif // ASSIMP_BUILD_NO_FBX_IMPORTER

+ 23 - 31
code/AssetLib/FBX/FBXDocument.h

@@ -4,7 +4,6 @@ Open Asset Import Library (assimp)
 
 Copyright (c) 2006-2022, assimp team
 
-
 All rights reserved.
 
 Redistribution and use of this software in source and binary forms,
@@ -89,7 +88,7 @@ class LazyObject {
 public:
     LazyObject(uint64_t id, const Element& element, const Document& doc);
 
-    ~LazyObject();
+    ~LazyObject() = default;
 
     const Object* Get(bool dieOnError = false);
 
@@ -139,7 +138,7 @@ class Object {
 public:
     Object(uint64_t id, const Element& element, const std::string& name);
 
-    virtual ~Object();
+    virtual ~Object() = default;
 
     const Element& SourceElement() const {
         return element;
@@ -267,8 +266,7 @@ public:
     Light(uint64_t id, const Element& element, const Document& doc, const std::string& name);
     virtual ~Light();
 
-    enum Type
-    {
+    enum Type {
         Type_Point,
         Type_Directional,
         Type_Spot,
@@ -278,8 +276,7 @@ public:
         Type_MAX // end-of-enum sentinel
     };
 
-    enum Decay
-    {
+    enum Decay {
         Decay_None,
         Decay_Linear,
         Decay_Quadratic,
@@ -347,7 +344,7 @@ public:
 
     Model(uint64_t id, const Element& element, const Document& doc, const std::string& name);
 
-    virtual ~Model();
+    virtual ~Model() = default;
 
     fbx_simple_property(QuaternionInterpolate, int, 0)
 
@@ -578,31 +575,27 @@ public:
         BlendMode_BlendModeCount
     };
 
-    const Texture* getTexture(int index=0) const
-    {
+    const Texture* getTexture(int index=0) const {
 		return textures[index];
-
     }
 	int textureCount() const {
 		return static_cast<int>(textures.size());
 	}
-    BlendMode GetBlendMode() const
-    {
+    BlendMode GetBlendMode() const {
         return blendMode;
     }
-    float Alpha()
-    {
+    float Alpha() {
         return alpha;
     }
+
 private:
 	std::vector<const Texture*> textures;
     BlendMode blendMode;
     float alpha;
 };
 
-typedef std::fbx_unordered_map<std::string, const Texture*> TextureMap;
-typedef std::fbx_unordered_map<std::string, const LayeredTexture*> LayeredTextureMap;
-
+using TextureMap = std::fbx_unordered_map<std::string, const Texture*>;
+using LayeredTextureMap = std::fbx_unordered_map<std::string, const LayeredTexture*>;
 
 /** DOM class for generic FBX videos */
 class Video : public Object {
@@ -690,8 +683,8 @@ private:
     LayeredTextureMap layeredTextures;
 };
 
-typedef std::vector<int64_t> KeyTimeList;
-typedef std::vector<float> KeyValueList;
+using KeyTimeList = std::vector<int64_t>;
+using KeyValueList = std::vector<float>;
 
 /** Represents a FBX animation curve (i.e. a 1-dimensional set of keyframes and values therefore) */
 class AnimationCurve : public Object {
@@ -727,7 +720,7 @@ private:
 };
 
 // property-name -> animation curve
-typedef std::map<std::string, const AnimationCurve*> AnimationCurveMap;
+using AnimationCurveMap = std::map<std::string, const AnimationCurve*>;
 
 /** Represents a FBX animation curve (i.e. a mapping from single animation curves to nodes) */
 class AnimationCurveNode : public Object {
@@ -777,7 +770,7 @@ private:
     const Document& doc;
 };
 
-typedef std::vector<const AnimationCurveNode*> AnimationCurveNodeList;
+using AnimationCurveNodeList = std::vector<const AnimationCurveNode*>;
 
 /** Represents a FBX animation layer (i.e. a list of node animations) */
 class AnimationLayer : public Object {
@@ -800,7 +793,7 @@ private:
     const Document& doc;
 };
 
-typedef std::vector<const AnimationLayer*> AnimationLayerList;
+using AnimationLayerList = std::vector<const AnimationLayer*>;
 
 /** Represents a FBX animation stack (i.e. a list of animation layers) */
 class AnimationStack : public Object {
@@ -843,8 +836,8 @@ private:
     std::shared_ptr<const PropertyTable> props;
 };
 
-typedef std::vector<float> WeightArray;
-typedef std::vector<unsigned int> WeightIndexArray;
+using WeightArray = std::vector<float>;
+using WeightIndexArray = std::vector<unsigned int>;
 
 
 /** DOM class for BlendShapeChannel deformers */
@@ -956,7 +949,7 @@ class Connection {
 public:
     Connection(uint64_t insertionOrder,  uint64_t src, uint64_t dest, const std::string& prop, const Document& doc);
 
-    ~Connection();
+    ~Connection() = default;
 
     // note: a connection ensures that the source and dest objects exist, but
     // not that they have DOM representations, so the return value of one of
@@ -1011,10 +1004,9 @@ public:
 // during their entire lifetime (Document). FBX files have
 // up to many thousands of objects (most of which we never use),
 // so the memory overhead for them should be kept at a minimum.
-typedef std::fbx_unordered_map<uint64_t, LazyObject*> ObjectMap;
-typedef std::fbx_unordered_map<std::string, std::shared_ptr<const PropertyTable> > PropertyTemplateMap;
-
-typedef std::fbx_unordered_multimap<uint64_t, const Connection*> ConnectionMap;
+using ObjectMap = std::fbx_unordered_map<uint64_t, LazyObject*> ;
+using PropertyTemplateMap = std::fbx_unordered_map<std::string, std::shared_ptr<const PropertyTable> > ;
+using ConnectionMap = std::fbx_unordered_multimap<uint64_t, const Connection*>;
 
 /** DOM class for global document settings, a single instance per document can
  *  be accessed via Document.Globals(). */
@@ -1022,7 +1014,7 @@ class FileGlobalSettings {
 public:
     FileGlobalSettings(const Document& doc, std::shared_ptr<const PropertyTable> props);
 
-    ~FileGlobalSettings();
+    ~FileGlobalSettings() = default;
 
     const PropertyTable& Props() const {
         ai_assert(props.get());

+ 22 - 1
code/AssetLib/FBX/FBXMaterial.cpp

@@ -140,11 +140,32 @@ Material::~Material() {
     // empty
 }
 
+    aiVector2D uvTrans;
+    aiVector2D uvScaling;
+    ai_real    uvRotation;
+
+    std::string type;
+    std::string relativeFileName;
+    std::string fileName;
+    std::string alphaSource;
+    std::shared_ptr<const PropertyTable> props;
+
+    unsigned int crop[4]{};
+
+    const Video* media;
+
 // ------------------------------------------------------------------------------------------------
 Texture::Texture(uint64_t id, const Element& element, const Document& doc, const std::string& name) :
         Object(id,element,name),
+        uvTrans(0.0f, 0.0f),
         uvScaling(1.0f,1.0f),
-        media(0) {
+        uvRotation(0.0f),
+        type(),
+        relativeFileName(),
+        fileName(),
+        alphaSource(),
+        props(),
+        media(nullptr) {
     const Scope& sc = GetRequiredScope(element);
 
     const Element* const Type = sc["Type"];

+ 0 - 5
code/AssetLib/FBX/FBXModel.cpp

@@ -4,7 +4,6 @@ Open Asset Import Library (assimp)
 
 Copyright (c) 2006-2022, assimp team
 
-
 All rights reserved.
 
 Redistribution and use of this software in source and binary forms,
@@ -76,10 +75,6 @@ Model::Model(uint64_t id, const Element &element, const Document &doc, const std
     ResolveLinks(element, doc);
 }
 
-// ------------------------------------------------------------------------------------------------
-Model::~Model() {
-}
-
 // ------------------------------------------------------------------------------------------------
 void Model::ResolveLinks(const Element&, const Document &doc) {
     const char *const arr[] = { "Geometry", "Material", "NodeAttribute" };

+ 77 - 0
code/AssetLib/LWO/LWOFileData.h

@@ -64,6 +64,7 @@ namespace LWO {
 
 #define AI_LWO_FOURCC_LWOB AI_IFF_FOURCC('L', 'W', 'O', 'B')
 #define AI_LWO_FOURCC_LWO2 AI_IFF_FOURCC('L', 'W', 'O', '2')
+#define AI_LWO_FOURCC_LWO3 AI_IFF_FOURCC('L', 'W', 'O', '3')
 #define AI_LWO_FOURCC_LXOB AI_IFF_FOURCC('L', 'X', 'O', 'B')
 
 // chunks specific to the LWOB format
@@ -248,6 +249,57 @@ namespace LWO {
 #define AI_LWO_SPOT AI_IFF_FOURCC('S', 'P', 'O', 'T')
 #define AI_LWO_PICK AI_IFF_FOURCC('P', 'I', 'C', 'K')
 
+// Surface Part
+#define AI_LWO_NODS AI_IFF_FOURCC('N', 'O', 'D', 'S')
+#define AI_LWO_NNDS AI_IFF_FOURCC('N', 'N', 'D', 'S')
+#define AI_LWO_NTAG AI_IFF_FOURCC('N', 'T', 'A', 'G')
+#define AI_LWO_NRNM AI_IFF_FOURCC('N', 'R', 'N', 'M')
+#define AI_LWO_NRME AI_IFF_FOURCC('N', 'R', 'M', 'E')
+#define AI_LWO_NDTA AI_IFF_FOURCC('N', 'D', 'T', 'A')
+#define AI_LWO_ATTR AI_IFF_FOURCC('A', 'T', 'T', 'R')
+#define AI_LWO_VERS AI_IFF_FOURCC('V', 'E', 'R', 'S')
+#define AI_LWO_ENUM AI_IFF_FOURCC('E', 'N', 'U', 'M')
+#define AI_LWO_ENTR AI_IFF_FOURCC('E', 'N', 'T', 'R')
+#define AI_LWO_NAME AI_IFF_FOURCC('N', 'A', 'M', 'E')
+#define AI_LWO_FLAG AI_IFF_FOURCC('F', 'L', 'A', 'G')
+#define AI_LWO_TAG AI_IFF_FOURCC('T', 'A', 'G', ' ')
+#define AI_LWO_VALU AI_IFF_FOURCC('V', 'A', 'L', 'U')
+#define AI_LWO_IBGC AI_IFF_FOURCC('I', 'B', 'G', 'C')
+#define AI_LWO_IOPC AI_IFF_FOURCC('I', 'O', 'P', 'C')
+#define AI_LWO_IIMG AI_IFF_FOURCC('I', 'I', 'M', 'G')
+#define AI_LWO_TXTR AI_IFF_FOURCC('T', 'X', 'T', 'R')
+
+#define AI_LWO_IFAL AI_IFF_FOURCC('I', 'F', 'A', 'L')
+#define AI_LWO_ISCL AI_IFF_FOURCC('I', 'S', 'C', 'L')
+#define AI_LWO_IPOS AI_IFF_FOURCC('I', 'P', 'O', 'S')
+#define AI_LWO_IROT AI_IFF_FOURCC('I', 'R', 'O', 'T')
+#define AI_LWO_IBMP AI_IFF_FOURCC('I', 'B', 'M', 'P')
+#define AI_LWO_IUTD AI_IFF_FOURCC('I', 'U', 'T', 'D')
+#define AI_LWO_IVTD AI_IFF_FOURCC('I', 'V', 'T', 'D')
+
+#define AI_LWO_IPIX AI_IFF_FOURCC('I', 'P', 'I', 'X')
+#define AI_LWO_IMIP AI_IFF_FOURCC('I', 'M', 'I', 'P')
+#define AI_LWO_IMOD AI_IFF_FOURCC('I', 'M', 'O', 'D')
+#define AI_LWO_AMOD AI_IFF_FOURCC('A', 'M', 'O', 'D')
+#define AI_LWO_IINV AI_IFF_FOURCC('I', 'I', 'N', 'V')
+#define AI_LWO_INCR AI_IFF_FOURCC('I', 'N', 'C', 'R')
+#define AI_LWO_IAXS AI_IFF_FOURCC('I', 'A', 'X', 'S')
+#define AI_LWO_IFOT AI_IFF_FOURCC('I', 'F', 'O', 'T')
+#define AI_LWO_ITIM AI_IFF_FOURCC('I', 'T', 'I', 'M')
+#define AI_LWO_IWRL AI_IFF_FOURCC('I', 'W', 'R', 'L')
+#define AI_LWO_IUTI AI_IFF_FOURCC('I', 'U', 'T', 'I')
+#define AI_LWO_IINX AI_IFF_FOURCC('I', 'I', 'N', 'X')
+#define AI_LWO_IINY AI_IFF_FOURCC('I', 'I', 'N', 'Y')
+#define AI_LWO_IINZ AI_IFF_FOURCC('I', 'I', 'N', 'Z')
+#define AI_LWO_IREF AI_IFF_FOURCC('I', 'R', 'E', 'F')
+#define AI_LWO_IMST AI_IFF_FOURCC('I', 'M', 'S', 'T')
+#define AI_LWO_VPVL AI_IFF_FOURCC('V', 'P', 'V', 'L')
+#define AI_LWO_VPRM AI_IFF_FOURCC('V', 'P', 'R', 'M')
+#define AI_LWO_IMAP AI_IFF_FOURCC('I', 'M', 'A', 'P')
+#define AI_LWO_IUVI AI_IFF_FOURCC('I', 'U', 'V', 'I')
+#define AI_LWO_IUTL AI_IFF_FOURCC('I', 'U', 'T', 'L')
+#define AI_LWO_IVTL AI_IFF_FOURCC('I', 'V', 'T', 'L')
+
 // MODO extension - per-vertex normal vectors
 #define AI_LWO_MODO_NORM AI_IFF_FOURCC('N', 'O', 'R', 'M')
 
@@ -555,6 +607,31 @@ struct Surface {
     float mAdditiveTransparency;
 };
 
+// ---------------------------------------------------------------------------
+/** \brief Data structure for a LWO node
+ */
+struct Node {
+    // Name of node
+    std::string mName;
+
+    // RefName of node
+    std::string mRefName;
+
+    // Ref FileName
+    std::string fileName;
+};
+
+struct NodeAttribute {
+    //! Color of the surface
+    aiColor3D mColor;
+
+    //! true for two-sided materials
+    bool bDoubleSided;
+
+    //! Various material parameters
+    float mDiffuseValue, mSpecularValue, mTransparency, mGlossiness, mLuminosity, mColorHighlights;
+};
+
 // ---------------------------------------------------------------------------
 #define AI_LWO_VALIDATE_CHUNK_LENGTH(length, name, size)              \
     if (length < size) {                                              \

+ 209 - 13
code/AssetLib/LWO/LWOLoader.cpp

@@ -83,6 +83,7 @@ static const aiImporterDesc desc = {
 LWOImporter::LWOImporter() :
         mIsLWO2(),
         mIsLXOB(),
+        mIsLWO3(),
         mLayers(),
         mCurLayer(),
         mTags(),
@@ -182,16 +183,19 @@ void LWOImporter::InternReadFile(const std::string &pFile,
     mCurLayer->mIndex = (uint16_t) -1;
 
     // old lightwave file format (prior to v6)
+    mIsLWO2 = false;
+    mIsLWO3 = false;
+    mIsLXOB = false;
+
     if (AI_LWO_FOURCC_LWOB == fileType) {
         ASSIMP_LOG_INFO("LWO file format: LWOB (<= LightWave 5.5)");
 
-        mIsLWO2 = false;
-        mIsLXOB = false;
         LoadLWOBFile();
     } else if (AI_LWO_FOURCC_LWO2 == fileType) {
         // New lightwave format
-        mIsLXOB = false;
         ASSIMP_LOG_INFO("LWO file format: LWO2 (>= LightWave 6)");
+    } else if ( AI_LWO_FOURCC_LWO3 == fileType ) {
+        ASSIMP_LOG_INFO("LWO file format: LWO3 (>= LightWave 2018)");
     } else if (AI_LWO_FOURCC_LXOB == fileType) {
         // MODO file format
         mIsLXOB = true;
@@ -207,8 +211,13 @@ void LWOImporter::InternReadFile(const std::string &pFile,
         throw DeadlyImportError("Unknown LWO sub format: ", szBuff);
     }
 
-    if (AI_LWO_FOURCC_LWOB != fileType) {
-        mIsLWO2 = true;
+    if (AI_LWO_FOURCC_LWOB != fileType) {   //
+        if( AI_LWO_FOURCC_LWO3 == fileType ) {
+            mIsLWO3 = true;
+        } else {
+            mIsLWO2 = true;
+        }
+        
         LoadLWO2File();
 
         // The newer lightwave format allows the user to configure the
@@ -442,6 +451,7 @@ void LWOImporter::InternReadFile(const std::string &pFile,
 
     // The RemoveRedundantMaterials step will clean this up later
     pScene->mMaterials = new aiMaterial *[pScene->mNumMaterials = (unsigned int)mSurfaces->size()];
+
     for (unsigned int mat = 0; mat < pScene->mNumMaterials; ++mat) {
         aiMaterial *pcMat = new aiMaterial();
         pScene->mMaterials[mat] = pcMat;
@@ -687,7 +697,7 @@ void LWOImporter::ResolveClips() {
 // ------------------------------------------------------------------------------------------------
 void LWOImporter::AdjustTexturePath(std::string &out) {
     // --- this function is used for both LWO2 and LWOB
-    if (!mIsLWO2 && ::strstr(out.c_str(), "(sequence)")) {
+    if (!mIsLWO2 && !mIsLWO3 && ::strstr(out.c_str(), "(sequence)")) {
 
         // remove the (sequence) and append 000
         ASSIMP_LOG_INFO("LWOB: Sequence of animated texture found. It will be ignored");
@@ -730,7 +740,7 @@ void LWOImporter::LoadLWOPoints(unsigned int length) {
         throw DeadlyImportError("LWO2: Points chunk length is not multiple of vertexLen (12)");
     }
     unsigned int regularSize = (unsigned int)mCurLayer->mTempPoints.size() + length / 12;
-    if (mIsLWO2) {
+    if (mIsLWO2 || mIsLWO3) {
         mCurLayer->mTempPoints.reserve(regularSize + (regularSize >> 2u));
         mCurLayer->mTempPoints.resize(regularSize);
 
@@ -1155,6 +1165,76 @@ void LWOImporter::LoadLWO2Clip(unsigned int length) {
     }
 }
 
+void LWOImporter::LoadLWO3Clip(unsigned int length) {
+    AI_LWO_VALIDATE_CHUNK_LENGTH(length, CLIP, 12);
+
+    mClips.push_back(LWO::Clip());
+    LWO::Clip &clip = mClips.back();
+
+    // first - get the index of the clip
+    clip.idx = GetU4();
+
+    IFF::ChunkHeader head = IFF::LoadChunk(mFileBuffer);
+    switch (head.type) {
+        case AI_LWO_STIL:
+            AI_LWO_VALIDATE_CHUNK_LENGTH(head.length, STIL, 1);
+
+            // "Normal" texture
+            GetS0(clip.path, head.length);
+            clip.type = Clip::STILL;
+            break;
+
+        case AI_LWO_ISEQ:
+            AI_LWO_VALIDATE_CHUNK_LENGTH(head.length, ISEQ, 16);
+            // Image sequence. We'll later take the first.
+            {
+                uint8_t digits = GetU1();
+                mFileBuffer++;
+                int16_t offset = GetU2();
+                mFileBuffer += 4;
+                int16_t start = GetU2();
+                mFileBuffer += 4;
+
+                std::string s;
+                std::ostringstream ss;
+                GetS0(s, head.length);
+
+                head.length -= (uint16_t)s.length() + 1;
+                ss << s;
+                ss << std::setw(digits) << offset + start;
+                GetS0(s, head.length);
+                ss << s;
+                clip.path = ss.str();
+                clip.type = Clip::SEQ;
+            }
+            break;
+
+        case AI_LWO_STCC:
+            ASSIMP_LOG_WARN("LWO3: Color shifted images are not supported");
+            break;
+
+        case AI_LWO_ANIM:
+            ASSIMP_LOG_WARN("LWO3: Animated textures are not supported");
+            break;
+
+        case AI_LWO_XREF:
+            AI_LWO_VALIDATE_CHUNK_LENGTH(head.length, XREF, 4);
+
+            // Just a cross-reference to another CLIp
+            clip.type = Clip::REF;
+            clip.clipRef = GetU4();
+            break;
+
+        case AI_LWO_NEGA:
+            AI_LWO_VALIDATE_CHUNK_LENGTH(head.length, NEGA, 2);
+            clip.negate = (0 != GetU2());
+            break;
+
+        default:
+            ASSIMP_LOG_WARN("LWO3: Encountered unknown CLIP sub-chunk");
+    }
+}
+
 // ------------------------------------------------------------------------------------------------
 // Load envelope description
 void LWOImporter::LoadLWO2Envelope(unsigned int length) {
@@ -1265,6 +1345,104 @@ void LWOImporter::LoadLWO2Envelope(unsigned int length) {
     }
 }
 
+void LWOImporter::LoadLWO3Envelope(unsigned int length) {
+    LE_NCONST uint8_t *const end = mFileBuffer + length;
+    AI_LWO_VALIDATE_CHUNK_LENGTH(length, ENVL, 4);
+
+    mEnvelopes.push_back(LWO::Envelope());
+    LWO::Envelope &envelope = mEnvelopes.back();
+
+    // Get the index of the envelope
+    envelope.index = ReadVSizedIntLWO2(mFileBuffer);
+
+    // ... and read all blocks
+    while (true) {
+        if (mFileBuffer + 8 >= end) break;
+        LE_NCONST IFF::ChunkHeader head = IFF::LoadChunk(mFileBuffer);
+
+        if (mFileBuffer + head.length > end)
+            throw DeadlyImportError("LWO3: Invalid envelope chunk length");
+
+        uint8_t *const next = mFileBuffer + head.length;
+        switch (head.type) {
+                // Type & representation of the envelope
+            case AI_LWO_TYPE:
+                AI_LWO_VALIDATE_CHUNK_LENGTH(head.length, TYPE, 4);
+                mFileBuffer++; // skip user format
+
+                // Determine type of envelope
+                envelope.type = (LWO::EnvelopeType)*mFileBuffer;
+                ++mFileBuffer;
+                break;
+
+                // precondition
+            case AI_LWO_PRE:
+                AI_LWO_VALIDATE_CHUNK_LENGTH(head.length, PRE, 4);
+                envelope.pre = (LWO::PrePostBehaviour)GetU2();
+                break;
+
+                // postcondition
+            case AI_LWO_POST:
+                AI_LWO_VALIDATE_CHUNK_LENGTH(head.length, POST, 4);
+                envelope.post = (LWO::PrePostBehaviour)GetU2();
+                break;
+
+                // keyframe
+            case AI_LWO_KEY: {
+                AI_LWO_VALIDATE_CHUNK_LENGTH(head.length, KEY, 10);
+
+                envelope.keys.push_back(LWO::Key());
+                LWO::Key &key = envelope.keys.back();
+
+                key.time = GetF4();
+                key.value = GetF4();
+                break;
+            }
+
+                // interval interpolation
+            case AI_LWO_SPAN: {
+                AI_LWO_VALIDATE_CHUNK_LENGTH(head.length, SPAN, 6);
+                if (envelope.keys.size() < 2)
+                    ASSIMP_LOG_WARN("LWO3: Unexpected SPAN chunk");
+                else {
+                    LWO::Key &key = envelope.keys.back();
+                    switch (GetU4()) {
+                        case AI_LWO_STEP:
+                            key.inter = LWO::IT_STEP;
+                            break;
+                        case AI_LWO_LINE:
+                            key.inter = LWO::IT_LINE;
+                            break;
+                        case AI_LWO_TCB:
+                            key.inter = LWO::IT_TCB;
+                            break;
+                        case AI_LWO_HERM:
+                            key.inter = LWO::IT_HERM;
+                            break;
+                        case AI_LWO_BEZI:
+                            key.inter = LWO::IT_BEZI;
+                            break;
+                        case AI_LWO_BEZ2:
+                            key.inter = LWO::IT_BEZ2;
+                            break;
+                        default:
+                            ASSIMP_LOG_WARN("LWO3: Unknown interval interpolation mode");
+                    };
+
+                    // todo ... read params
+                }
+                break;
+            }
+
+            default:
+                ASSIMP_LOG_WARN("LWO3: Encountered unknown ENVL subchunk");
+                break;
+        }
+        // regardless how much we did actually read, go to the next chunk
+        mFileBuffer = next;
+    }
+}
+
 // ------------------------------------------------------------------------------------------------
 // Load file - master function
 void LWOImporter::LoadLWO2File() {
@@ -1272,16 +1450,25 @@ void LWOImporter::LoadLWO2File() {
 
     LE_NCONST uint8_t *const end = mFileBuffer + fileSize;
     unsigned int iUnnamed = 0;
+
     while (true) {
         if (mFileBuffer + sizeof(IFF::ChunkHeader) > end) break;
-        const IFF::ChunkHeader head = IFF::LoadChunk(mFileBuffer);
+
+        IFF::ChunkHeader head = IFF::LoadChunk(mFileBuffer);
+
+        int bufOffset = 0;
+        if( head.type == AI_IFF_FOURCC_FORM ) { // not chunk, it's a form
+            mFileBuffer -= 8;
+            head = IFF::LoadForm(mFileBuffer);
+            bufOffset = 4;
+        }
 
         if (mFileBuffer + head.length > end) {
             throw DeadlyImportError("LWO2: Chunk length points behind the file");
             break;
         }
         uint8_t *const next = mFileBuffer + head.length;
-
+        mFileBuffer += bufOffset;
         if (!head.length) {
             mFileBuffer = next;
             continue;
@@ -1337,7 +1524,6 @@ void LWOImporter::LoadLWO2File() {
 
                 break;
             }
-
                 // vertex list
             case AI_LWO_PNTS: {
                 if (skip)
@@ -1399,19 +1585,29 @@ void LWOImporter::LoadLWO2File() {
 
                 // surface chunk
             case AI_LWO_SURF: {
-                LoadLWO2Surface(head.length);
+                if( mIsLWO3 )
+                    LoadLWO3Surface(head.length);
+                else
+                    LoadLWO2Surface(head.length);
+
                 break;
             }
 
                 // clip chunk
             case AI_LWO_CLIP: {
-                LoadLWO2Clip(head.length);
+                if( mIsLWO3 )
+                    LoadLWO3Clip(head.length);
+                else
+                    LoadLWO2Clip(head.length);
                 break;
             }
 
                 // envelope chunk
             case AI_LWO_ENVL: {
-                LoadLWO2Envelope(head.length);
+                if( mIsLWO3 )
+                    LoadLWO3Envelope(head.length);
+                else
+                    LoadLWO2Envelope(head.length);
                 break;
             }
         }

+ 32 - 0
code/AssetLib/LWO/LWOLoader.h

@@ -116,6 +116,8 @@ private:
     */
     inline void GetS0(std::string &out, unsigned int max);
     inline float GetF4();
+    inline float GetF8();
+    inline uint64_t GetU8();
     inline uint32_t GetU4();
     inline uint16_t GetU2();
     inline uint8_t GetU1();
@@ -131,6 +133,7 @@ private:
      *  @param size Maximum size to be read, in bytes.
      */
     void LoadLWO2Surface(unsigned int size);
+    void LoadLWO3Surface(unsigned int size);
 
     // -------------------------------------------------------------------
     /** Loads a texture block from a LWO2 file.
@@ -197,12 +200,23 @@ private:
      *  @param length Size of the chunk
     */
     void LoadLWO2Clip(unsigned int length);
+    void LoadLWO3Clip(unsigned int length);
 
     // -------------------------------------------------------------------
     /** Load an envelope from an EVL chunk
      *  @param length Size of the chunk
     */
     void LoadLWO2Envelope(unsigned int length);
+    void LoadLWO3Envelope(unsigned int length);
+
+    // -------------------------------------------------------------------
+    /** Load an nodal blocks from surface form
+     *  @param length Size of the chunk
+    */
+    void LoadNodalBlocks(unsigned int length);
+    void LoadNodes(unsigned int length);
+    void LoadNodeTag(unsigned int length);
+    void LoadNodeData(unsigned int length);
 
     // -------------------------------------------------------------------
     /** Count vertices and faces in a LWOB/LWO2 file
@@ -347,6 +361,8 @@ protected:
     /** true if the file is a LXOB file*/
     bool mIsLXOB;
 
+    bool mIsLWO3;
+
     /** Temporary list of layers from the file */
     LayerList *mLayers;
 
@@ -400,6 +416,22 @@ inline float LWOImporter::GetF4() {
     return f;
 }
 
+inline float LWOImporter::GetF8() {
+    double f;
+    ::memcpy(&f, mFileBuffer, 8);
+    mFileBuffer += 8;
+    AI_LSWAP8(f);
+    return (float)f;
+}
+
+inline uint64_t LWOImporter::GetU8() {
+    uint64_t f;
+    ::memcpy(&f, mFileBuffer, 8);
+    mFileBuffer += 8;
+    AI_LSWAP8(f);
+    return f;
+}
+
 // ------------------------------------------------------------------------------------------------
 inline uint32_t LWOImporter::GetU4() {
     uint32_t f;

+ 313 - 2
code/AssetLib/LWO/LWOMaterial.cpp

@@ -159,7 +159,7 @@ bool LWOImporter::HandleTextures(aiMaterial *pcMat, const TextureList &in, aiTex
 
         // The older LWOB format does not use indirect references to clips.
         // The file name of a texture is directly specified in the tex chunk.
-        if (mIsLWO2) {
+        if (mIsLWO2 || mIsLWO3) {
             // find the corresponding clip (take the last one if multiple
             // share the same index)
             ClipList::iterator end = mClips.end(), candidate = end;
@@ -270,7 +270,7 @@ void LWOImporter::ConvertMaterial(const LWO::Surface &surf, aiMaterial *pcMat) {
     aiShadingMode m;
     if (surf.mSpecularValue && surf.mGlossiness) {
         float fGloss;
-        if (mIsLWO2) {
+        if (mIsLWO2 || mIsLWO3) {
             fGloss = std::pow(surf.mGlossiness * ai_real(10.0) + ai_real(2.0), ai_real(2.0));
         } else {
             if (16.0 >= surf.mGlossiness)
@@ -688,6 +688,252 @@ void LWOImporter::LoadLWO2ShaderBlock(LE_NCONST IFF::SubChunkHeader * /*head*/,
     surf.mShaders.push_back(shader);
 }
 
+// ------------------------------------------------------------------------------------------------
+void LWOImporter::LoadNodalBlocks(unsigned int size) {
+    LE_NCONST uint8_t *const end = mFileBuffer + size;
+
+    while (true) {
+        if (mFileBuffer + 8 >= end)
+            break;
+
+        IFF::ChunkHeader head = IFF::LoadChunk(mFileBuffer);
+        int bufOffset = 0;
+        if (head.type == AI_IFF_FOURCC_FORM) { // not chunk, it's a form
+            mFileBuffer -= 8;
+            head = IFF::LoadForm(mFileBuffer);
+            bufOffset = 4;
+        }
+
+        if (mFileBuffer + head.length > end) {
+            throw DeadlyImportError("LWO3: cannot read length; LoadNodalBlocks");
+        }
+        int node_idx = 0;
+        uint8_t *const next = mFileBuffer + head.length;
+        mFileBuffer += bufOffset;
+        switch (head.type) {
+        case AI_LWO_NNDS:
+            node_idx++;
+            LoadNodes(head.length);
+            break;
+        }
+
+        mFileBuffer = next;
+    }
+}
+
+// ------------------------------------------------------------------------------------------------
+void LWOImporter::LoadNodes(unsigned int size) {
+    LE_NCONST uint8_t *const end = mFileBuffer + size;
+
+    while (true) {
+        if (mFileBuffer + 8 >= end)
+            break;
+
+        IFF::ChunkHeader head = IFF::LoadChunk(mFileBuffer);
+        int bufOffset = 0;
+        if (head.type == AI_IFF_FOURCC_FORM) { // not chunk, it's a form
+            mFileBuffer -= 8;
+            head = IFF::LoadForm(mFileBuffer);
+            bufOffset = 4;
+        }
+
+        if (mFileBuffer + head.length > end) {
+            throw DeadlyImportError("LWO3: cannot read length; LoadNodes");
+        }
+
+        uint8_t *const next = mFileBuffer + head.length;
+        mFileBuffer += bufOffset;
+        switch (head.type) {
+        case AI_LWO_NTAG:
+            LoadNodeTag(head.length);
+            break;
+        }
+
+        mFileBuffer = next;
+    }
+}
+
+// ------------------------------------------------------------------------------------------------
+void LWOImporter::LoadNodeTag(unsigned int size) {
+    LE_NCONST uint8_t *const end = mFileBuffer + size;
+
+    while (true) {
+        if (mFileBuffer + 8 >= end)
+            break;
+
+        IFF::ChunkHeader head = IFF::LoadChunk(mFileBuffer);
+        int bufOffset = 0;
+        if (head.type == AI_IFF_FOURCC_FORM) { // not chunk, it's a form
+            mFileBuffer -= 8;
+            head = IFF::LoadForm(mFileBuffer);
+            bufOffset = 4;
+        }
+
+        if (mFileBuffer + head.length > end) {
+            throw DeadlyImportError("LWO3: cannot read length; LoadNodeTag");
+        }
+
+        uint8_t *const next = mFileBuffer + head.length;
+        mFileBuffer += bufOffset;
+
+        switch (head.type) {
+        case AI_LWO_NDTA:
+            LoadNodeData(head.length);
+            break;
+        }
+
+        mFileBuffer = next;
+    }
+}
+
+// ------------------------------------------------------------------------------------------------
+void LWOImporter::LoadNodeData(unsigned int size) {
+    LE_NCONST uint8_t *const end = mFileBuffer + size;
+
+    LWO::Surface &surf = mSurfaces->back();
+
+    while (true) {
+        if (mFileBuffer + 8 >= end)
+            break;
+
+        IFF::ChunkHeader head = IFF::LoadChunk(mFileBuffer);
+        int bufOffset = 0;
+        if (head.type == AI_IFF_FOURCC_FORM) { // not chunk, it's a form
+            mFileBuffer -= 8;
+            head = IFF::LoadForm(mFileBuffer);
+            bufOffset = 4;
+        }
+
+        if (mFileBuffer + head.length > end) {
+            throw DeadlyImportError("LWO3: INVALID LENGTH; LoadNodeData");
+        }
+
+        uint8_t *const next = mFileBuffer + head.length;
+        mFileBuffer += bufOffset;
+        switch (head.type) {
+            case AI_LWO_VERS:
+            case AI_LWO_ENUM:
+            case AI_LWO_IBGC:
+            case AI_LWO_IOPC:
+            case AI_LWO_IIMG:
+            case AI_LWO_TXTR:
+            case AI_LWO_IFAL:
+            case AI_LWO_ISCL:
+            case AI_LWO_IPOS:
+            case AI_LWO_IROT:
+            case AI_LWO_IBMP:
+            case AI_LWO_IUTD:
+            case AI_LWO_IVTD:
+
+            case AI_LWO_IPIX:
+            case AI_LWO_IMIP:
+            case AI_LWO_IMOD:
+            case AI_LWO_AMOD:
+            case AI_LWO_IINV:
+            case AI_LWO_INCR:
+            case AI_LWO_IAXS:
+            case AI_LWO_IFOT:
+            case AI_LWO_ITIM:
+            case AI_LWO_IWRL:
+            case AI_LWO_IUTI:
+            case AI_LWO_IUVI:
+            case AI_LWO_IINX:
+            case AI_LWO_IINY:
+            case AI_LWO_IINZ:
+            case AI_LWO_IREF:
+            case AI_LWO_IMST:
+            case AI_LWO_IMAP:
+            case AI_LWO_IUTL:
+            case AI_LWO_IVTL:
+            case AI_LWO_VPVL:
+            case AI_LWO_VPRM:
+                mFileBuffer = next;
+                break;
+            case AI_LWO_ENTR:
+                std::string attrName;
+
+                while (true) {
+                    if (mFileBuffer + 8 >= next)
+                        break;
+
+                    IFF::ChunkHeader head1 = IFF::LoadChunk(mFileBuffer);
+                    int bufOffset1 = 0;
+                    if (head1.type == AI_IFF_FOURCC_FORM) { // not chunk, it's a form
+                        mFileBuffer -= 8;
+                        head1 = IFF::LoadForm(mFileBuffer);
+                        bufOffset1 = 4;
+                    }
+
+                    if (mFileBuffer + head1.length > end) {
+                        throw DeadlyImportError("LWO3: cannot read length;");
+                    }
+                    uint8_t *const next1 = mFileBuffer + head1.length;
+                    mFileBuffer += bufOffset1;
+
+                    switch (head1.type) {
+                        case AI_LWO_FLAG:
+                        case AI_LWO_TAG:
+                            mFileBuffer = next1;
+                            break;
+                        case AI_LWO_NAME:
+                            GetS0(attrName, head1.length);
+                            break;
+                        case AI_LWO_VALU:
+                            mFileBuffer += 8;
+
+                            std::string valueType;
+                            GetS0(valueType, 8);
+
+                            if (valueType == "int") {
+                                static_cast<void>(GetU4());
+                            } else if (valueType == "double") {
+                                static_cast<void>(GetU8());
+                            } else if (valueType == "vparam") {
+                                mFileBuffer += 24;
+
+                                float value = GetF8();
+                                if (attrName == "Diffuse") {
+                                    surf.mDiffuseValue = value;
+                                } else if (attrName == "Specular") {
+                                    surf.mSpecularValue = value;
+                                } else if (attrName == "Transparency") {
+                                    surf.mTransparency = value;
+                                } else if (attrName == "Glossiness") {
+                                    surf.mGlossiness = value;
+                                } else if (attrName == "Luminosity") {
+                                    surf.mLuminosity = value;
+                                } else if (attrName == "Color Highlight") {
+                                    surf.mColorHighlights = value;
+                                } else if (attrName == "Refraction Index") {
+                                    surf.mIOR = value;
+                                } else if (attrName == "Bump Height") {
+                                    surf.mBumpIntensity = value;
+                                }
+                            } else if (valueType == "vparam3") {
+                                mFileBuffer += 24;
+
+                                float value1, value2, value3;
+                                value1 = GetF8();
+                                value2 = GetF8();
+                                value3 = GetF8();
+
+                                if (attrName == "Color") {
+                                    surf.mColor.r = value1;
+                                    surf.mColor.g = value2;
+                                    surf.mColor.b = value3;
+                                }
+                            }
+
+                            mFileBuffer = next1;
+                            break;
+                    }
+                }
+
+                break;
+        }
+    }
+}
+
 // ------------------------------------------------------------------------------------------------
 void LWOImporter::LoadLWO2Surface(unsigned int size) {
     LE_NCONST uint8_t *const end = mFileBuffer + size;
@@ -841,4 +1087,69 @@ void LWOImporter::LoadLWO2Surface(unsigned int size) {
     }
 }
 
+void LWOImporter::LoadLWO3Surface(unsigned int size) {
+    mFileBuffer += 8;
+    LE_NCONST uint8_t *const end = mFileBuffer + size - 12;
+
+    mSurfaces->push_back(LWO::Surface());
+    LWO::Surface &surf = mSurfaces->back();
+
+    GetS0(surf.mName, size);
+
+    // check whether this surface was derived from any other surface
+    std::string derived;
+    GetS0(derived, (unsigned int)(end - mFileBuffer));
+    if (derived.length()) {
+        // yes, find this surface
+        for (SurfaceList::iterator it = mSurfaces->begin(), itEnd = mSurfaces->end() - 1; it != itEnd; ++it) {
+            if ((*it).mName == derived) {
+                // we have it ...
+                surf = *it;
+                derived.clear();
+                break;
+            }
+        }
+        if (derived.size()) {
+            ASSIMP_LOG_WARN("LWO3: Unable to find source surface: ", derived);
+        }
+    }
+    while (true) {
+        if (mFileBuffer + 8 >= end)
+            break;
+
+        IFF::ChunkHeader head = IFF::LoadChunk(mFileBuffer);
+        int bufOffset = 0;
+        if( head.type == AI_IFF_FOURCC_FORM ) { // not chunk, it's a form
+            mFileBuffer -= 8;
+            head = IFF::LoadForm(mFileBuffer);
+            bufOffset = 4;
+        }
+
+        if (mFileBuffer + head.length > end) {
+            throw DeadlyImportError("LWO3: cannot read length; LoadLWO3Surface");
+        }
+
+        uint8_t *const next = mFileBuffer + head.length;
+        mFileBuffer += bufOffset;
+        switch (head.type) {
+            case AI_LWO_NODS:
+                LoadNodalBlocks(head.length);
+                break;
+                // polygon sidedness
+            case AI_LWO_SIDE: {
+                AI_LWO_VALIDATE_CHUNK_LENGTH(head.length, SIDE, 2);
+                surf.bDoubleSided = (3 == GetU2());
+                break;
+            }
+                // maximum smoothing angle
+            case AI_LWO_SMAN: {
+                AI_LWO_VALIDATE_CHUNK_LENGTH(head.length, SMAN, 4);
+                surf.mMaximumSmoothAngle = std::fabs(GetF4());
+                break;
+            }
+        }
+        mFileBuffer = next;
+    }
+}
+
 #endif // !! ASSIMP_BUILD_NO_X_IMPORTER

+ 21 - 0
code/AssetLib/Obj/ObjFileParser.cpp

@@ -117,6 +117,7 @@ void ObjFileParser::parseFile(IOStreamBuffer<char> &streamBuffer) {
     unsigned int processed = 0;
     size_t lastFilePos(0);
 
+    bool insideCstype = false;
     std::vector<char> buffer;
     while (streamBuffer.getNextDataLine(buffer, '\\')) {
         m_DataIt = buffer.begin();
@@ -131,6 +132,18 @@ void ObjFileParser::parseFile(IOStreamBuffer<char> &streamBuffer) {
             m_progress->UpdateFileRead(processed, progressTotal);
         }
 
+        // handle cstype section end (http://paulbourke.net/dataformats/obj/)
+        if (insideCstype) {
+            switch (*m_DataIt) {
+            case 'e': {
+                std::string name;
+                getNameNoSpace(m_DataIt, m_DataItEnd, name);
+                insideCstype = name != "end";
+            } break;
+            }
+            goto pf_skip_line;
+        }
+
         // parse line
         switch (*m_DataIt) {
         case 'v': // Parse a vertex texture coordinate
@@ -219,6 +232,14 @@ void ObjFileParser::parseFile(IOStreamBuffer<char> &streamBuffer) {
             getObjectName();
         } break;
 
+        case 'c': // handle cstype section start
+        {
+            std::string name;
+            getNameNoSpace(m_DataIt, m_DataItEnd, name);
+            insideCstype = name == "cstype";
+            goto pf_skip_line;
+        } break;
+
         default: {
         pf_skip_line:
             m_DataIt = skipLine<DataArrayIt>(m_DataIt, m_DataItEnd, m_uiLine);

+ 29 - 0
code/Common/IFF.h

@@ -35,6 +35,17 @@ struct SubChunkHeader
     uint16_t length;
 };
 
+/////////////////////////////////////////////////////////////////////////////////
+//! Describes an IFF form header
+/////////////////////////////////////////////////////////////////////////////////
+struct FormHeader
+{
+    //! Length of the chunk data, in bytes
+    uint32_t length;
+
+    //! Type of the chunk header - FourCC
+    uint32_t type;
+};
 
 #define AI_IFF_FOURCC(a,b,c,d) ((uint32_t) (((uint8_t)a << 24u) | \
     ((uint8_t)b << 16u) | ((uint8_t)c << 8u) | ((uint8_t)d)))
@@ -77,6 +88,24 @@ inline SubChunkHeader LoadSubChunk(uint8_t*& outFile)
     return head;
 }
 
+/////////////////////////////////////////////////////////////////////////////////
+//! Load a chunk header
+//! @param outFile Pointer to the file data - points to the chunk data afterwards
+//! @return Copy of the chunk header
+/////////////////////////////////////////////////////////////////////////////////
+inline ChunkHeader LoadForm(uint8_t*& outFile)
+{
+    ChunkHeader head;
+    outFile += 4;
+    ::memcpy(&head.length, outFile, 4);
+    outFile += 4;
+    ::memcpy(&head.type, outFile, 4);
+
+    AI_LSWAP4(head.length);
+    AI_LSWAP4(head.type);
+    return head;
+}
+
 /////////////////////////////////////////////////////////////////////////////////
 //! Read the file header and return the type of the file and its size
 //! @param outFile Pointer to the file data. The buffer must at

+ 3 - 3
code/Common/ZipArchiveIOSystem.cpp

@@ -122,15 +122,15 @@ voidpf IOSystem2Unzip::open(voidpf opaque, const char *filename, int mode) {
 voidpf IOSystem2Unzip::opendisk(voidpf opaque, voidpf stream, uint32_t number_disk, int mode) {
     ZipFile *io_stream = (ZipFile *)stream;
     voidpf ret = NULL;
-    size_t i;
+    int i;
 
     char *disk_filename = (char*)malloc(io_stream->m_Filename.length() + 1);
     strncpy(disk_filename, io_stream->m_Filename.c_str(), io_stream->m_Filename.length() + 1);
-    for (i = io_stream->m_Filename.length() - 1; i >= 0; i -= 1)
+    for (i = (int)io_stream->m_Filename.length() - 1; i >= 0; i -= 1)
     {
         if (disk_filename[i] != '.')
             continue;
-        snprintf(&disk_filename[i], io_stream->m_Filename.length() - i, ".z%02u", number_disk + 1);
+        snprintf(&disk_filename[i], io_stream->m_Filename.length() - size_t(i), ".z%02u", number_disk + 1);
         break;
     }
 

+ 48 - 1
include/assimp/material.inl

@@ -130,7 +130,54 @@ AI_FORCE_INLINE aiReturn aiMaterial::Get(const char* pKey,unsigned int type,
 }
 
 // ---------------------------------------------------------------------------
-AI_FORCE_INLINE aiReturn aiMaterial::Get(const char* pKey,unsigned int type,
+// Specialisation for a single bool.
+// Casts floating point and integer to bool
+template <>
+AI_FORCE_INLINE
+        aiReturn
+        aiMaterial::Get(const char *pKey, unsigned int type,
+                unsigned int idx, bool &pOut) const {
+    const aiMaterialProperty *prop;
+    const aiReturn ret = ::aiGetMaterialProperty(this, pKey, type, idx,
+            (const aiMaterialProperty **)&prop);
+    if (AI_SUCCESS == ret) {
+
+        switch (prop->mType) {
+            // Type cannot be converted
+        default: return AI_FAILURE;
+
+        case aiPTI_Buffer: {
+            // Native bool value storage
+            if (prop->mDataLength < sizeof(bool)) {
+                return AI_FAILURE;
+            }
+            ::memcpy(&pOut, prop->mData, sizeof(bool));
+        } break;
+
+        case aiPTI_Float:
+        case aiPTI_Double: {
+            // Read as float and cast to bool
+            float value = 0.0f;
+            if (AI_SUCCESS == ::aiGetMaterialFloat(this, pKey, type, idx, &value)) {
+                pOut = static_cast<bool>(value);
+                return AI_SUCCESS;
+            }
+            return AI_FAILURE;
+        }
+        case aiPTI_Integer: {
+            // Cast to bool
+            const int value = static_cast<int>(*prop->mData);
+            pOut = static_cast<bool>(value);
+            return AI_SUCCESS;
+        }
+        }
+    }
+    return ret;
+}
+
+// ---------------------------------------------------------------------------
+AI_FORCE_INLINE
+aiReturn aiMaterial::Get(const char* pKey,unsigned int type,
         unsigned int idx,ai_real* pOut,
         unsigned int* pMax) const {
     return ::aiGetMaterialFloatArray(this,pKey,type,idx,pOut,pMax);

+ 1 - 1
include/assimp/pbrmaterial.h

@@ -61,7 +61,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #define AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLIC_FACTOR AI_MATKEY_METALLIC_FACTOR
 #define AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_ROUGHNESS_FACTOR AI_MATKEY_ROUGHNESS_FACTOR
 
-//#define AI_MATKEY_GLTF_PBRSPECULARGLOSSINESS "$mat.gltf.pbrSpecularGlossiness", 0, 0
+#define AI_MATKEY_GLTF_PBRSPECULARGLOSSINESS AI_MATKEY_GLOSSINESS_FACTOR
 #define AI_MATKEY_GLTF_PBRSPECULARGLOSSINESS_GLOSSINESS_FACTOR AI_MATKEY_GLOSSINESS_FACTOR
 
 // Use AI_MATKEY_SHADING_MODEL == aiShadingMode_Unlit instead

+ 87 - 6
test/unit/utMaterialSystem.cpp

@@ -125,15 +125,96 @@ TEST_F(MaterialSystemTest, testStringProperty) {
 }
 
 // ------------------------------------------------------------------------------------------------
-TEST_F(MaterialSystemTest, testMaterialNameAccess) {
-    aiMaterial *mat = new aiMaterial();
-    EXPECT_NE(nullptr, mat);
-
-    aiString name = mat->GetName();
+TEST_F(MaterialSystemTest, testDefaultMaterialName) {
+    aiString name = pcMat->GetName();
     const int retValue(strncmp(name.C_Str(), AI_DEFAULT_MATERIAL_NAME, name.length));
     EXPECT_EQ(0, retValue);
+}
+
+// ------------------------------------------------------------------------------------------------
+TEST_F(MaterialSystemTest, testBoolProperty) {
+    const bool valTrue = true;
+    const bool valFalse = false;
+    EXPECT_EQ(AI_SUCCESS, pcMat->AddProperty(&valTrue, 1, "bool_true"));
+    EXPECT_EQ(AI_SUCCESS, pcMat->AddProperty(&valFalse, 1, "bool_false"));
+
+    bool read = false;
+    EXPECT_EQ(AI_SUCCESS, pcMat->Get("bool_true", 0, 0, read));
+    EXPECT_TRUE(read) << "read true bool";
+    EXPECT_EQ(AI_SUCCESS, pcMat->Get("bool_false", 0, 0, read));
+    EXPECT_FALSE(read) << "read false bool";
+}
+
+// ------------------------------------------------------------------------------------------------
+TEST_F(MaterialSystemTest, testCastIntProperty) {
+    int value = 10;
+    EXPECT_EQ(AI_SUCCESS, pcMat->AddProperty(&value, 1, "integer"));
+    value = 0;
+    EXPECT_EQ(AI_SUCCESS, pcMat->AddProperty(&value, 1, "zero"));
+    value = -1;
+    EXPECT_EQ(AI_SUCCESS, pcMat->AddProperty(&value, 1, "negative"));
+
+    // To float
+    float valFloat = 0.0f;
+    EXPECT_EQ(AI_SUCCESS, pcMat->Get("integer", 0, 0, valFloat));
+    EXPECT_EQ(10.0f, valFloat);
+    EXPECT_EQ(AI_SUCCESS, pcMat->Get("zero", 0, 0, valFloat));
+    EXPECT_EQ(0.0f, valFloat);
+    EXPECT_EQ(AI_SUCCESS, pcMat->Get("negative", 0, 0, valFloat));
+    EXPECT_EQ(-1.0f, valFloat);
+
+    // To bool
+    bool valBool = false;
+    EXPECT_EQ(AI_SUCCESS, pcMat->Get("integer", 0, 0, valBool));
+    EXPECT_EQ(true, valBool);
+    EXPECT_EQ(AI_SUCCESS, pcMat->Get("zero", 0, 0, valBool));
+    EXPECT_EQ(false, valBool);
+    EXPECT_EQ(AI_SUCCESS, pcMat->Get("negative", 0, 0, valBool));
+    EXPECT_EQ(true, valBool);
+}
+
+// ------------------------------------------------------------------------------------------------
+TEST_F(MaterialSystemTest, testCastFloatProperty) {
+    float value = 150392.63f;
+    EXPECT_EQ(AI_SUCCESS, pcMat->AddProperty(&value, 1, "float"));
+    value = 0;
+    EXPECT_EQ(AI_SUCCESS, pcMat->AddProperty(&value, 1, "zero"));
+
+    // To int
+    int valInt = 0.0f;
+    EXPECT_EQ(AI_SUCCESS, pcMat->Get("float", 0, 0, valInt));
+    EXPECT_EQ(150392, valInt);
+    EXPECT_EQ(AI_SUCCESS, pcMat->Get("zero", 0, 0, valInt));
+    EXPECT_EQ(0, valInt);
+
+    // To bool
+    bool valBool = false;
+    EXPECT_EQ(AI_SUCCESS, pcMat->Get("float", 0, 0, valBool));
+    EXPECT_EQ(true, valBool);
+    EXPECT_EQ(AI_SUCCESS, pcMat->Get("zero", 0, 0, valBool));
+    EXPECT_EQ(false, valBool);
+}
+
+// ------------------------------------------------------------------------------------------------
+TEST_F(MaterialSystemTest, testCastSmallFloatProperty) {
+    float value = 0.0078125f;
+    EXPECT_EQ(AI_SUCCESS, pcMat->AddProperty(&value, 1, "float"));
+    value = 0;
+    EXPECT_EQ(AI_SUCCESS, pcMat->AddProperty(&value, 1, "zero"));
+
+    // To int
+    int valInt = 0.0f;
+    EXPECT_EQ(AI_SUCCESS, pcMat->Get("float", 0, 0, valInt));
+    EXPECT_EQ(0, valInt);
+    EXPECT_EQ(AI_SUCCESS, pcMat->Get("zero", 0, 0, valInt));
+    EXPECT_EQ(0, valInt);
 
-    delete mat;
+    // To bool
+    bool valBool = false;
+    EXPECT_EQ(AI_SUCCESS, pcMat->Get("float", 0, 0, valBool));
+    EXPECT_EQ(true, valBool);
+    EXPECT_EQ(AI_SUCCESS, pcMat->Get("zero", 0, 0, valBool));
+    EXPECT_EQ(false, valBool);
 }
 
 // ------------------------------------------------------------------------------------------------