Implemented basic glTF exporter and improved the importer (plus some code refactor)
authorOtger <otgerrogla@gmail.com>
Sun, 29 Nov 2015 18:13:51 +0000 (19:13 +0100)
committerOtger <otgerrogla@gmail.com>
Sun, 29 Nov 2015 18:13:51 +0000 (19:13 +0100)
13 files changed:
code/BaseImporter.h
code/CMakeLists.txt
code/Exporter.cpp
code/glTFAsset.h [new file with mode: 0644]
code/glTFAsset.inl [new file with mode: 0644]
code/glTFAssetWriter.h [moved from code/glTFUtil.h with 62% similarity]
code/glTFAssetWriter.inl [new file with mode: 0644]
code/glTFExporter.cpp
code/glTFExporter.h
code/glTFFileData.h [deleted file]
code/glTFImporter.cpp
code/glTFImporter.h
code/glTFUtil.cpp [deleted file]

index cb6394e..7c34ac5 100644 (file)
@@ -359,6 +359,27 @@ public: // static utilities
         IOStream* stream,
         std::vector<char>& data);
 
+    // -------------------------------------------------------------------
+    /** Utility function to move a std::vector into a aiScene array
+    *  @param vec The vector to be moved
+    *  @param out The output pointer to the allocated array.
+    *  @param numOut The output count of elements copied. */
+    template<typename T>
+    AI_FORCE_INLINE
+    static void CopyVector(
+        std::vector<T>& vec,
+        T*& out,
+        unsigned int& outLength)
+    {
+        outLength = vec.size();
+        if (outLength) {
+            out = new T[outLength];
+            std::swap_ranges(vec.begin(), vec.end(), out);
+        }
+    }
+
+    
+
 protected:
 
     /** Error description in case there was one. */
index 69ee3d7..dffb3b4 100644 (file)
@@ -563,12 +563,14 @@ ADD_ASSIMP_IMPORTER(X
 )
 
 ADD_ASSIMP_IMPORTER(GLTF
+  glTFAsset.h
+  glTFAsset.inl
+  glTFAssetWriter.h
+  glTFAssetWriter.inl
+
   glTFImporter.cpp
   glTFImporter.h
-  glTFUtil.cpp
-  glTFUtil.h
-  glTFFileData.h
-
+  
   glTFExporter.h
   glTFExporter.cpp
 )
index 1d84e0d..215fa67 100644 (file)
@@ -139,9 +139,9 @@ Exporter::ExportFormatEntry gExporters[] =
 
 #ifndef ASSIMP_BUILD_NO_GLTF_EXPORTER
     Exporter::ExportFormatEntry( "gltf", "GL Transmission Format", "gltf", &ExportSceneGLTF,
-        aiProcess_JoinIdenticalVertices),
+        aiProcess_JoinIdenticalVertices | aiProcess_SortByPType),
     Exporter::ExportFormatEntry( "glb", "GL Transmission Format (binary)", "glb", &ExportSceneGLB,
-        aiProcess_JoinIdenticalVertices),
+        aiProcess_JoinIdenticalVertices | aiProcess_SortByPType),
 #endif
 
 #ifndef ASSIMP_BUILD_NO_ASSBIN_EXPORTER
diff --git a/code/glTFAsset.h b/code/glTFAsset.h
new file mode 100644 (file)
index 0000000..4f6c37a
--- /dev/null
@@ -0,0 +1,933 @@
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2015, assimp team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+copyright notice, this list of conditions and the
+following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the
+following disclaimer in the documentation and/or other
+materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+contributors may be used to endorse or promote products
+derived from this software without specific prior
+written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file glTFAsset.h
+ * Declares a glTF class to handle gltf/glb files
+ *
+ * glTF Extensions Support:
+ *   KHR_binary_glTF: full
+ *   KHR_materials_common: full
+ */
+#ifndef glTFAsset_H_INC
+#define glTFAsset_H_INC
+
+#include <map>
+#include <string>
+#include <vector>
+#include <algorithm>
+#include <stdexcept>
+
+#define RAPIDJSON_HAS_STDSTRING 1
+#include <rapidjson/rapidjson.h>
+#include <rapidjson/document.h>
+#include <rapidjson/error/en.h>
+
+#ifdef ASSIMP_API
+#   include "boost/shared_ptr.hpp"
+#   include "DefaultIOSystem.h"
+#   include "ByteSwapper.h"
+#else
+#   include <memory>
+#   define AI_SWAP4(p)
+#   define ai_assert
+#endif
+
+
+#if _MSC_VER > 1500 || (defined __GNUC___)
+#       define ASSIMP_GLTF_USE_UNORDERED_MULTIMAP
+#   else
+#       define gltf_unordered_map map
+#endif
+
+#ifdef ASSIMP_GLTF_USE_UNORDERED_MULTIMAP
+#   include <unordered_map>
+#   if _MSC_VER > 1600
+#       define gltf_unordered_map unordered_map
+#   else
+#       define gltf_unordered_map tr1::unordered_map
+#   endif
+#endif
+
+namespace glTF
+{
+#ifdef ASSIMP_API
+    using Assimp::IOStream;
+    using Assimp::IOSystem;
+    using boost::shared_ptr;
+#else
+    using std::shared_ptr;
+
+    typedef std::runtime_error DeadlyImportError;
+    typedef std::runtime_error DeadlyExportError;
+
+    enum aiOrigin { aiOrigin_SET = 0, aiOrigin_CUR = 1, aiOrigin_END = 2 };
+    class IOSystem;
+    class IOStream
+    {
+        FILE* f;
+    public:
+        IOStream(FILE* file) : f(file) {}
+        ~IOStream() { fclose(f); f = 0; }
+
+        size_t Read(void* b, size_t sz, size_t n) { return fread(b, sz, n, f); }
+        size_t Write(const void* b, size_t sz, size_t n) { return fwrite(b, sz, n, f); }
+        int    Seek(size_t off, aiOrigin orig) { return fseek(f, off, int(orig)); }
+        size_t Tell() const { return ftell(f); }
+
+        size_t FileSize() {
+            long p = Tell(), len = (Seek(0, aiOrigin_END), Tell());
+            return size_t((Seek(p, aiOrigin_SET), len));
+        }
+    };
+#endif
+
+    using rapidjson::Value;
+    using rapidjson::Document;
+
+    class Asset;
+    class AssetWriter;
+
+    struct BufferView; // here due to cross-reference
+    struct Texture;
+    struct Light;
+
+
+    // Vec/matrix types, as raw float arrays
+    typedef float (vec3)[3];
+    typedef float (vec4)[4];
+    typedef float (mat4)[16];
+
+
+    namespace Util
+    {
+        void EncodeBase64(const uint8_t* in, size_t inLength, std::string& out);
+
+        size_t DecodeBase64(const char* in, size_t inLength, uint8_t*& out);
+
+        inline size_t DecodeBase64(const char* in, uint8_t*& out)
+        {
+            return DecodeBase64(in, strlen(in), out);
+        }
+
+        struct DataURI
+        {
+            const char* mediaType;
+            const char* charset;
+            bool base64;
+            const char* data;
+            size_t dataLength;
+        };
+
+        //! Check if a uri is a data URI
+        inline bool ParseDataURI(const char* uri, size_t uriLen, DataURI& out);
+    }
+
+
+    //! Magic number for GLB files
+    #define AI_GLB_MAGIC_NUMBER "glTF"
+
+    #ifdef ASSIMP_API
+        #include "./../include/assimp/Compiler/pushpack1.h"
+    #endif
+
+    //! For the KHR_binary_glTF extension (binary .glb file)
+    //! 20-byte header (+ the JSON + a "body" data section)
+    struct GLB_Header
+    {
+        uint8_t magic[4];     //!< Magic number: "glTF"
+        uint32_t version;     //!< Version number (always 1 as of the last update)
+        uint32_t length;      //!< Total length of the Binary glTF, including header, scene, and body, in bytes
+        uint32_t sceneLength; //!< Length, in bytes, of the glTF scene
+        uint32_t sceneFormat; //!< Specifies the format of the glTF scene (see the SceneFormat enum)
+    } PACK_STRUCT;
+
+    #ifdef ASSIMP_API
+        #include "./../include/assimp/Compiler/poppack1.h"
+    #endif
+
+
+    //! Values for the GLB_Header::sceneFormat field
+    enum SceneFormat
+    {
+        SceneFormat_JSON = 0
+    };
+
+    //! Values for the mesh primitive modes
+    enum PrimitiveMode
+    {
+        PrimitiveMode_POINTS = 0,
+        PrimitiveMode_LINES = 1,
+        PrimitiveMode_LINE_LOOP = 2,
+        PrimitiveMode_LINE_STRIP = 3,
+        PrimitiveMode_TRIANGLES = 4,
+        PrimitiveMode_TRIANGLE_STRIP = 5,
+        PrimitiveMode_TRIANGLE_FAN = 6
+    };
+
+    //! Values for the Accessor::componentType field
+    enum ComponentType
+    {
+        ComponentType_BYTE = 5120,
+        ComponentType_UNSIGNED_BYTE = 5121,
+        ComponentType_SHORT = 5122,
+        ComponentType_UNSIGNED_SHORT = 5123,
+        ComponentType_FLOAT = 5126
+    };
+
+    inline size_t ComponentTypeSize(ComponentType t)
+    {
+        switch (t) {
+            case ComponentType_SHORT:
+            case ComponentType_UNSIGNED_SHORT:
+                return 2;
+
+            case ComponentType_FLOAT:
+                return 4;
+
+            //case Accessor::ComponentType_BYTE:
+            //case Accessor::ComponentType_UNSIGNED_BYTE:
+            default:
+                return 1;
+        }
+    }
+
+    //! Values for the BufferView::target field
+    enum BufferViewTarget
+    {
+        BufferViewTarget_ARRAY_BUFFER = 34962,
+        BufferViewTarget_ELEMENT_ARRAY_BUFFER = 34963
+    };
+
+    //! Values for the Texture::format and Texture::internalFormat fields
+    enum TextureFormat
+    {
+        TextureFormat_ALPHA = 6406,
+        TextureFormat_RGB = 6407,
+        TextureFormat_RGBA = 6408,
+        TextureFormat_LUMINANCE = 6409,
+        TextureFormat_LUMINANCE_ALPHA = 6410
+    };
+
+    //! Values for the Texture::target field
+    enum TextureTarget
+    {
+        TextureTarget_TEXTURE_2D = 3553
+    };
+
+    //! Values for the Texture::type field
+    enum TextureType
+    {
+        TextureType_UNSIGNED_BYTE = 5121,
+        TextureType_UNSIGNED_SHORT_5_6_5 = 33635,
+        TextureType_UNSIGNED_SHORT_4_4_4_4 = 32819,
+        TextureType_UNSIGNED_SHORT_5_5_5_1 = 32820
+    };
+
+
+    //! Values for the Accessor::type field (helper class)
+    class AttribType
+    {
+    public:
+        enum Value
+            { SCALAR, VEC2, VEC3, VEC4, MAT2, MAT3, MAT4 };
+
+    private:
+        static const size_t NUM_VALUES = static_cast<size_t>(MAT4)+1;
+
+        struct Info
+            { const char* name; unsigned int numComponents; };
+
+        template<int N> struct data
+            { static const Info infos[NUM_VALUES]; };
+
+    public:
+        inline static Value FromString(const char* str)
+        {
+            for (size_t i = 0; i < NUM_VALUES; ++i) {
+                if (strcmp(data<0>::infos[i].name, str) == 0) {
+                    return static_cast<Value>(i);
+                }
+            }
+            return SCALAR;
+        }
+
+        inline static const char* ToString(Value type)
+        {
+            return data<0>::infos[static_cast<size_t>(type)].name;
+        }
+
+        inline static unsigned int GetNumComponents(Value type)
+        {
+            return data<0>::infos[static_cast<size_t>(type)].numComponents;
+        }
+    };
+
+    // must match the order of the AttribTypeTraits::Value enum!
+    template<int N> const AttribType::Info
+    AttribType::data<N>::infos[AttribType::NUM_VALUES] = {
+        { "SCALAR", 1 }, { "VEC2", 2 }, { "VEC3", 3 }, { "VEC4", 4 }, { "MAT2", 4 }, { "MAT3", 9 }, { "MAT4", 16 }
+    };
+
+
+
+    //! A reference to one top-level object, which is valid
+    //! until the Asset instance is destroyed
+    template<class T>
+    class Ref
+    {
+        std::vector<T*>* vector;
+        int index;
+
+    public:
+        Ref() : vector(0), index(0) {}
+        Ref(std::vector<T*>& vec, int idx) : vector(&vec), index(idx) {}
+
+        inline size_t GetIndex() const
+            { return index; }
+
+        operator bool() const
+            { return vector != 0; }
+
+        T* operator->()
+            { return (*vector)[index]; }
+
+        T& operator*()
+            { return *((*vector)[index]); }
+    };
+
+    //! Helper struct to represent values that might not be present
+    template<class T>
+    struct Nullable
+    {
+        T value;
+        bool isPresent;
+
+        Nullable() : isPresent(false) {}
+        Nullable(T& val) : value(val), isPresent(true) {}
+    };
+
+
+    //! Base classe for all glTF top-level objects
+    struct Object
+    {
+        std::string id;   //!< The globally unique ID used to reference this object
+        std::string name; //!< The user-defined name of this object
+
+        //! Objects marked as special are not exported (used to emulate the binary body buffer)
+        virtual bool IsSpecial() const
+            { return false; }
+    };
+
+
+
+    //
+    // Classes for each glTF top-level object type
+    //
+
+    //! A typed view into a BufferView. A BufferView contains raw binary data.
+    //! An accessor provides a typed view into a BufferView or a subset of a BufferView
+    // !similar to how WebGL's vertexAttribPointer() defines an attribute in a buffer.
+    struct Accessor : public Object
+    {
+        Ref<BufferView> bufferView;  //!< The ID of the bufferView. (required)
+        unsigned int byteOffset;     //!< The offset relative to the start of the bufferView in bytes. (required)
+        unsigned int byteStride;     //!< The stride, in bytes, between attributes referenced by this accessor. (default: 0)
+        ComponentType componentType; //!< The datatype of components in the attribute. (required)
+        unsigned int count;          //!< The number of attributes referenced by this accessor. (required)
+        AttribType::Value type;      //!< Specifies if the attribute is a scalar, vector, or matrix. (required)
+        //std::vector<float> max;    //!< Maximum value of each component in this attribute.
+        //std::vector<float> min;    //!< Minimum value of each component in this attribute.
+
+        unsigned int GetNumComponents();
+        unsigned int GetBytesPerComponent();
+        unsigned int GetElementSize();
+
+        inline uint8_t* GetPointer();
+
+        template<class T>
+        void ExtractData(T*& outData);
+
+        void WriteData(size_t count, const void* src_buffer, size_t src_stride);
+
+        //! Helper class to iterate the data
+        class Indexer
+        {
+            friend struct Accessor;
+
+            uint8_t* data;
+            Accessor& accessor;
+            size_t elemSize, stride;
+
+            Indexer(Accessor& acc);
+
+        public:
+
+            //! Accesses the i-th value as defined by the accessor
+            template<class T>
+            T GetValue(int i);
+
+            //! Accesses the i-th value as defined by the accessor
+            inline unsigned int GetUInt(int i)
+            {
+                return GetValue<unsigned int>(i);
+            }
+        };
+
+        inline Indexer GetIndexer()
+        {
+            return Indexer(*this);
+        }
+
+        Accessor() {}
+        void Read(Value& obj, Asset& r);
+    };
+
+
+    struct Animation : public Object
+    {
+        struct Channel
+        {
+
+        };
+        
+        struct Target
+        {
+
+        };
+        
+        struct Sampler
+        {
+
+        };
+    };
+
+    //! A buffer points to binary geometry, animation, or skins.
+    struct Buffer : public Object
+    {
+    public:
+
+        enum Type
+        {
+            Type_arraybuffer,
+            Type_text
+        };
+
+        //std::string uri; //!< The uri of the buffer. Can be a filepath, a data uri, etc. (required)
+        size_t byteLength; //!< The length of the buffer in bytes. (default: 0)
+        //std::string type; //!< XMLHttpRequest responseType (default: "arraybuffer")
+
+        Type type;
+
+    private:
+        shared_ptr<uint8_t> mData; //!< Pointer to the data
+        bool mIsSpecial; //!< Set to true for special cases (e.g. the body buffer)
+
+    public:
+        Buffer();
+
+        void Read(Value& obj, Asset& r);
+
+        void LoadFromStream(IOStream& stream, size_t length = 0, size_t baseOffset = 0);
+        
+        size_t AppendData(uint8_t* data, size_t length);
+        void Grow(size_t amount);
+
+        uint8_t* GetPointer()
+            { return mData.get(); }
+
+        void MarkAsSpecial()
+            { mIsSpecial = true; }
+        
+        bool IsSpecial() const
+            { return mIsSpecial; }
+    };
+
+
+    //! A view into a buffer generally representing a subset of the buffer.
+    struct BufferView : public Object
+    {
+        Ref<Buffer> buffer; //! The ID of the buffer. (required)
+        size_t byteOffset; //! The offset into the buffer in bytes. (required)
+        size_t byteLength; //! The length of the bufferView in bytes. (default: 0)
+
+        BufferViewTarget target; //! The target that the WebGL buffer should be bound to.
+
+        BufferView() {}
+        void Read(Value& obj, Asset& r);
+    };
+
+
+    struct Camera : public Object
+    {
+        enum Type
+        {
+            Perspective,
+            Orthographic
+        };
+
+        Type type;
+
+        union
+        {
+            struct {
+                float aspectRatio; //!<The floating - point aspect ratio of the field of view. (0 = undefined = use the canvas one)
+                float yfov;  //!<The floating - point vertical field of view in radians. (required)
+                float zfar;  //!<The floating - point distance to the far clipping plane. (required)
+                float znear; //!< The floating - point distance to the near clipping plane. (required)
+            } perspective;
+
+            struct {
+                float xmag;  //! The floating-point horizontal magnification of the view. (required)
+                float ymag;  //! The floating-point vertical magnification of the view. (required)
+                float zfar;  //! The floating-point distance to the far clipping plane. (required)
+                float znear; //! The floating-point distance to the near clipping plane. (required)
+            } ortographic;
+        };
+
+        Camera() {}
+        void Read(Value& obj, Asset& r);
+    };
+
+
+    //! Image data used to create a texture.
+    struct Image : public Object
+    {
+        std::string uri; //! The uri of the image, that can be a file path, a data URI, etc.. (required)
+
+        Ref<BufferView> bufferView;
+
+        std::string mimeType;
+
+    private:
+        uint8_t* mData;
+        size_t mDataLength;
+
+    public:
+
+        Image();
+        void Read(Value& obj, Asset& r);
+
+        inline bool HasData() const
+            { return mDataLength > 0; }
+
+        inline size_t GetDataLength() const
+            { return mDataLength; }
+        
+        inline const uint8_t* GetData() const
+            { return mData; }
+
+        inline uint8_t* StealData();
+
+        inline void SetData(uint8_t* data, size_t length, Asset& r);
+    };
+
+    //! Holds a material property that can be a texture or a color
+    struct TexProperty
+    {
+        Ref<Texture> texture;
+        vec4 color;
+    };
+
+    //! The material appearance of a primitive.
+    struct Material : public Object
+    {
+        //Ref<Sampler> source; //!< The ID of the technique.
+        //std::gltf_unordered_map<std::string, std::string> values; //!< A dictionary object of parameter values.
+
+        //! Techniques defined by KHR_materials_common
+        enum Technique
+        {
+            Technique_undefined = 0,
+            Technique_BLINN,
+            Technique_PHONG,
+            Technique_LAMBERT,
+            Technique_CONSTANT
+        };
+
+        TexProperty ambient;
+        TexProperty diffuse;
+        TexProperty specular;
+        TexProperty emission;
+
+        bool doubleSided;
+        bool transparent;
+        float transparency;
+        float shininess;
+
+        Technique technique;
+
+        Material() { SetDefaults(); }
+        void Read(Value& obj, Asset& r);
+        void SetDefaults();
+    };
+
+    //! A set of primitives to be rendered. A node can contain one or more meshes. A node's transform places the mesh in the scene.
+    struct Mesh : public Object
+    {
+        typedef std::vector< Ref<Accessor> > AccessorList;
+
+        struct Primitive
+        {
+            PrimitiveMode mode;
+
+            struct Attributes {
+                AccessorList position, normal, texcoord, color, joint, jointmatrix, weight;
+            } attributes;
+
+            Ref<Accessor> indices;
+
+            Ref<Material> material;
+        };
+
+        std::vector<Primitive> primitives;
+
+        Mesh() {}
+        void Read(Value& obj, Asset& r);
+    };
+
+    struct Node : public Object
+    {
+        std::vector< Ref<Node> > children;
+        std::vector< Ref<Mesh> > meshes;
+
+        Nullable<mat4> matrix;
+        Nullable<vec3> translation;
+        Nullable<vec4> rotation;
+        Nullable<vec3> scale;
+
+        Ref<Camera> camera;
+        Ref<Light>  light;
+
+        Node() {}
+        void Read(Value& obj, Asset& r);
+    };
+
+    struct Program : public Object
+    {
+        Program() {}
+        void Read(Value& obj, Asset& r);
+    };
+
+
+    struct Sampler : public Object
+    {
+        Sampler() {}
+        void Read(Value& obj, Asset& r);
+    };
+
+    struct Scene : public Object
+    {
+        std::vector< Ref<Node> > nodes;
+
+        Scene() {}
+        void Read(Value& obj, Asset& r);
+    };
+
+    struct Shader : public Object
+    {
+        Shader() {}
+        void Read(Value& obj, Asset& r);
+    };
+
+    struct Skin : public Object
+    {
+        Skin() {}
+        void Read(Value& obj, Asset& r);
+    };
+
+    struct Technique : public Object
+    {
+        struct Parameters
+        {
+
+        };
+
+        struct States
+        {
+
+        };
+
+        struct Functions
+        {
+
+        };
+
+        Technique() {}
+        void Read(Value& obj, Asset& r);
+    };
+
+    //! A texture and its sampler.
+    struct Texture : public Object
+    {
+        //Ref<Sampler> source; //!< The ID of the sampler used by this texture. (required)
+        Ref<Image> source; //!< The ID of the image used by this texture. (required)
+
+        //TextureFormat format; //!< The texture's format. (default: TextureFormat_RGBA)
+        //TextureFormat internalFormat; //!< The texture's internal format. (default: TextureFormat_RGBA)
+
+        //TextureTarget target; //!< The target that the WebGL texture should be bound to. (default: TextureTarget_TEXTURE_2D)
+        //TextureType type; //!< Texel datatype. (default: TextureType_UNSIGNED_BYTE)
+
+        Texture() {}
+        void Read(Value& obj, Asset& r);
+    };
+
+
+    //! A light (from KHR_materials_common extension)
+    struct Light : public Object
+    {
+        enum Type
+        {
+            Type_undefined,
+            Type_ambient,
+            Type_directional,
+            Type_point,
+            Type_spot
+        };
+
+        Type type;
+
+        vec4 color;
+        float distance;
+        float constantAttenuation;
+        float linearAttenuation;
+        float quadraticAttenuation;
+        float falloffAngle;
+        float falloffExponent;
+
+        Light() {}
+        void Read(Value& obj, Asset& r);
+
+        void SetDefaults();
+    };
+
+    //! Base class for LazyDict that acts as an interface
+    class LazyDictBase
+    {
+    public:
+        virtual ~LazyDictBase() {}
+
+        virtual void AttachToDocument(Document& doc) = 0;
+        virtual void DetachFromDocument() = 0;
+
+        virtual void WriteObjects(AssetWriter& writer) = 0;
+    };
+
+    //! Manages lazy loading of the glTF top-level objects, and keeps a reference to them by ID
+    //! It is the owner the loaded objects, so when it is destroyed it also deletes them
+    template<class T>
+    class LazyDict : public LazyDictBase
+    {
+        friend class Asset;
+        friend class AssetWriter;
+
+        typedef typename std::gltf_unordered_map< std::string, size_t > Dict;
+
+        std::vector<T*>  mObjs;      //! The read objects
+        Dict             mObjsById;  //! The read objects accesible by id
+        const char*      mDictId;    //! ID of the dictionary object
+        const char*      mExtId;     //! ID of the extension defining the dictionary
+        Value*           mDict;      //! JSON dictionary object
+        Asset&           mAsset;     //! The asset instance
+
+        void AttachToDocument(Document& doc);
+        void DetachFromDocument();
+
+        void WriteObjects(AssetWriter& writer);
+
+        Ref<T> Add(T* obj);
+
+    public:
+        LazyDict(Asset& asset, const char* dictId, const char* extId = 0);
+        ~LazyDict();
+
+        Ref<T> Get(const char* id);
+        Ref<T> Get(size_t i);
+
+        Ref<T> Create(const char* id);
+        Ref<T> Create(const std::string& id)
+            { return Create(id.c_str()); }
+
+        inline size_t Size() const
+            { return mObjs.size(); }
+
+        inline T& operator[](size_t i)
+            { return *mObjs[i]; }
+
+    };
+
+
+    struct AssetMetadata
+    {
+        std::string copyright; //!< A copyright message suitable for display to credit the content creator.
+        std::string generator; //!< Tool that generated this glTF model.Useful for debugging.
+        bool premultipliedAlpha; //!< Specifies if the shaders were generated with premultiplied alpha. (default: false)
+
+        struct {
+            std::string api;     //!< Specifies the target rendering API (default: "WebGL")
+            std::string version; //!< Specifies the target rendering API (default: "1.0.3")
+        } profile; //!< Specifies the target rendering API and version, e.g., WebGL 1.0.3. (default: {})
+
+        int version; //!< The glTF format version (should be 1)
+
+        void Read(Document& doc);
+    };
+
+    //
+    // glTF Asset class
+    //
+
+    //! Root object for a glTF asset
+    class Asset
+    {
+        typedef std::gltf_unordered_map<std::string, int> IdMap;
+
+        template<class T>
+        friend class LazyDict;
+
+        friend struct Buffer; // To access OpenFile
+
+        friend class AssetWriter;
+
+    private:
+        IOSystem* mIOSystem;
+
+        std::string mCurrentAssetDir;
+
+        size_t mSceneLength;
+        size_t mBodyOffset, mBodyLength;
+
+        std::vector<LazyDictBase*> mDicts;
+
+        IdMap mUsedIds;
+
+        Ref<Buffer> mBodyBuffer;
+
+        Asset(Asset&);
+        Asset& operator=(const Asset&);
+
+    public:
+
+        //! Keeps info about the enabled extensions
+        struct Extensions
+        {
+            bool KHR_binary_glTF;
+            bool KHR_materials_common;
+
+        } extensionsUsed;
+
+        AssetMetadata asset;
+
+
+        // Dictionaries for each type of object
+
+        LazyDict<Accessor>    accessors;
+        LazyDict<Animation>   animations;
+        LazyDict<Buffer>      buffers;
+        LazyDict<BufferView>  bufferViews;
+        LazyDict<Camera>      cameras;
+        LazyDict<Image>       images;
+        LazyDict<Material>    materials;
+        LazyDict<Mesh>        meshes;
+        LazyDict<Node>        nodes;
+        //LazyDict<Program>   programs;
+        //LazyDict<Sampler>   samplers;
+        LazyDict<Scene>       scenes;
+        //LazyDict<Shader>    shaders;
+        //LazyDict<Skin>      skins;
+        //LazyDict<Technique> techniques;
+        LazyDict<Texture>     textures;
+
+        LazyDict<Light>       lights; // KHR_materials_common ext
+
+        Ref<Scene> scene;
+
+    public:
+        Asset(IOSystem* io = 0)
+            : mIOSystem(io)
+            , accessors     (*this, "accessors")
+            , animations    (*this, "animations")
+            , buffers       (*this, "buffers")
+            , bufferViews   (*this, "bufferViews")
+            , cameras       (*this, "cameras")
+            , images        (*this, "images")
+            , materials     (*this, "materials")
+            , meshes        (*this, "meshes")
+            , nodes         (*this, "nodes")
+            //, programs    (*this, "programs")
+            //, samplers    (*this, "samplers")
+            , scenes        (*this, "scenes")
+            //, shaders     (*this, "shaders")
+            //, skins       (*this, "skins")
+            //, techniques  (*this, "techniques")
+            , textures      (*this, "textures")
+            , lights        (*this, "lights", "KHR_materials_common")
+        {
+            memset(&extensionsUsed, 0, sizeof(extensionsUsed));
+            memset(&asset, 0, sizeof(asset));
+        }
+
+        //! Main function
+        void Load(const std::string& file, bool isBinary = false);
+
+        //! Enables the "KHR_binary_glTF" extension on the asset
+        void SetAsBinary();
+
+        //! Search for an available name, starting from the given strings
+        std::string FindUniqueID(const std::string& str, const char* suffix);
+
+        Ref<Buffer> GetBodyBuffer()
+            { return mBodyBuffer; }
+
+    private:
+        void ReadBinaryHeader(IOStream& stream);
+
+        void ReadExtensionsUsed(Document& doc);
+
+
+        IOStream* OpenFile(std::string path, const char* mode, bool absolute = false);
+    };
+
+}
+
+// Include the implementation of the methods
+#include "glTFAsset.inl"
+
+#endif
diff --git a/code/glTFAsset.inl b/code/glTFAsset.inl
new file mode 100644 (file)
index 0000000..82f7a92
--- /dev/null
@@ -0,0 +1,1212 @@
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2015, assimp team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+copyright notice, this list of conditions and the
+following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the
+following disclaimer in the documentation and/or other
+materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+contributors may be used to endorse or promote products
+derived from this software without specific prior
+written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+namespace glTF {
+
+namespace {
+    
+    //
+    // JSON Value reading helpers
+    //
+
+    template<class T>
+    struct ReadHelper { static bool Read(Value& val, T& out) {
+        return val.IsInt() ? out = static_cast<T>(val.GetInt()), true : false;
+    }};
+    
+    template<> struct ReadHelper<bool> { static bool Read(Value& val, bool& out) {
+        return val.IsBool() ? out = val.GetBool(), true : false;
+    }};
+
+    template<> struct ReadHelper<float> { static bool Read(Value& val, float& out) {
+        return val.IsNumber() ? out = static_cast<float>(val.GetDouble()), true : false;
+    }};
+    
+    template<size_t N> struct ReadHelper<float[N]> { static bool Read(Value& val, float (&out)[N]) {
+        if (!val.IsArray() || val.Size() != N) return false;
+        for (int i = 0; i < N; ++i) {
+            if (val[i].IsNumber())
+                out[i] = static_cast<float>(val[i].GetDouble());
+        }
+        return true;
+    }};
+
+    template<> struct ReadHelper<const char*> { static bool Read(Value& val, const char*& out) {
+        return val.IsString() ? out = val.GetString(), true : false;
+    }};
+
+    template<> struct ReadHelper<std::string> { static bool Read(Value& val, std::string& out) {
+        return val.IsString() ? out = val.GetString(), true : false;
+    }};
+
+    template<class T> struct ReadHelper< Nullable<T> > { static bool Read(Value& val, Nullable<T>& out) {
+        return out.isPresent = ReadHelper<T>::Read(val, out.value);
+    }};
+
+    template<class T>
+    inline static bool ReadValue(Value& val, T& out)
+    {
+        return ReadHelper<T>::Read(val, out);
+    }
+
+    template<class T>
+    inline static bool ReadMember(Value& obj, const char* id, T& out)
+    {
+        Value::MemberIterator it = obj.FindMember(id);
+        if (it != obj.MemberEnd()) {
+            return ReadHelper<T>::Read(it->value, out);
+        }
+        return false;
+    }
+
+    template<class T>
+    inline static T MemberOrDefault(Value& obj, const char* id, T defaultValue)
+    {
+        T out;
+        return ReadMember(obj, id, out) ? out : defaultValue;
+    }
+
+    inline Value* FindMember(Value& val, const char* id)
+    {
+        Value::MemberIterator it = val.FindMember(id);
+        return (it != val.MemberEnd()) ? &it->value : 0;
+    }
+
+    inline Value* FindString(Value& val, const char* id)
+    {
+        Value::MemberIterator it = val.FindMember(id);
+        return (it != val.MemberEnd() && it->value.IsString()) ? &it->value : 0;
+    }
+
+    inline Value* FindArray(Value& val, const char* id)
+    {
+        Value::MemberIterator it = val.FindMember(id);
+        return (it != val.MemberEnd() && it->value.IsArray()) ? &it->value : 0;
+    }
+
+    inline Value* FindObject(Value& val, const char* id)
+    {
+        Value::MemberIterator it = val.FindMember(id);
+        return (it != val.MemberEnd() && it->value.IsObject()) ? &it->value : 0;
+    }
+}
+
+//
+// LazyDict methods
+//
+
+template<class T>
+inline LazyDict<T>::LazyDict(Asset& asset, const char* dictId, const char* extId)
+    : mDictId(dictId), mExtId(extId), mDict(0), mAsset(asset)
+{
+    asset.mDicts.push_back(this); // register to the list of dictionaries
+}
+
+template<class T>
+inline LazyDict<T>::~LazyDict()
+{
+    for (size_t i = 0; i < mObjs.size(); ++i) {
+        delete mObjs[i];
+    }
+}
+
+
+template<class T>
+inline void LazyDict<T>::AttachToDocument(Document& doc)
+{
+    Value* container = 0;
+
+    if (mExtId) {
+        if (Value* exts = FindObject(doc, "extensions")) {
+            container = FindObject(*exts, mExtId);
+        }
+    }
+    else {
+        container = &doc;
+    }
+
+    if (container) {
+        mDict = FindObject(*container, mDictId);
+    }
+}
+
+template<class T>
+inline void LazyDict<T>::DetachFromDocument()
+{
+    mDict = 0;
+}
+
+template<class T>
+Ref<T> LazyDict<T>::Get(size_t i)
+{
+    return Ref<T>(mObjs, i);
+}
+
+template<class T>
+Ref<T> LazyDict<T>::Get(const char* id)
+{
+    typename Dict::iterator it = mObjsById.find(id);
+    if (it != mObjsById.end()) { // already created?
+        return Ref<T>(mObjs, it->second);
+    }
+
+    // read it from the JSON object
+    if (!mDict) {
+        return Ref<T>(); // section is missing
+    }
+
+    Value::MemberIterator obj = mDict->FindMember(id);
+    if (obj == mDict->MemberEnd()) {
+        throw DeadlyImportError("Missing object with id \"" + std::string(id) + "\" in \"" + mDictId + "\"");
+    }
+    if (!obj->value.IsObject()) {
+        throw DeadlyImportError("Object with id \"" + std::string(id) + "\" is not a JSON object!");
+    }
+
+    // create an instance of the given type
+    T* inst = new T();
+    inst->id = id;
+    ReadMember(obj->value, "name", inst->name);
+    inst->Read(obj->value, mAsset);
+    return Add(inst);
+}
+
+template<class T>
+Ref<T> LazyDict<T>::Add(T* obj)
+{
+    size_t idx = mObjs.size();
+    mObjs.push_back(obj);
+    mObjsById[obj->id] = idx;
+    mAsset.mUsedIds[obj->id] = true;
+    return Ref<T>(mObjs, idx);
+}
+
+template<class T>
+Ref<T> LazyDict<T>::Create(const char* id)
+{
+    Asset::IdMap::iterator it = mAsset.mUsedIds.find(id);
+    if (it != mAsset.mUsedIds.end()) {
+        throw DeadlyImportError("Two objects with the same ID exist!");
+    }
+    T* inst = new T();
+    inst->id = id;
+    return Add(inst);
+}
+
+
+//
+// glTF dictionary objects methods
+//
+
+
+inline Buffer::Buffer()
+: byteLength(0), type(Type_arraybuffer), mIsSpecial(false)
+{ }
+
+
+inline void Buffer::Read(Value& obj, Asset& r)
+{
+    size_t statedLength = MemberOrDefault<size_t>(obj, "byteLength", 0);
+    byteLength = statedLength;
+
+    Value* it = FindString(obj, "uri");
+    if (!it) return;
+
+    const char* uri = it->GetString();
+
+    Util::DataURI dataURI;
+    if (ParseDataURI(uri, it->GetStringLength(), dataURI)) {
+        if (dataURI.base64) {
+            uint8_t* data = 0;
+            this->byteLength = Util::DecodeBase64(dataURI.data, dataURI.dataLength, data);
+            this->mData.reset(data);
+
+            if (statedLength > 0 && this->byteLength != statedLength) {
+                // error?
+            }
+        }
+    }
+    else { // Local file
+        if (byteLength > 0) {
+            IOStream* file = r.OpenFile(uri, "rb");
+            if (file) {
+                LoadFromStream(*file, byteLength);
+                delete file;
+            }
+        }
+    }
+}
+
+inline void Buffer::LoadFromStream(IOStream& stream, size_t length, size_t baseOffset)
+{
+    byteLength = length ? length : stream.FileSize();
+
+    if (baseOffset) {
+        stream.Seek(baseOffset, aiOrigin_SET);
+    }
+
+    mData.reset(new uint8_t[byteLength]);
+
+    if (stream.Read(mData.get(), byteLength, 1) != 1) {
+        throw DeadlyImportError("Unable to load buffer from file!");
+    }
+}
+
+inline size_t Buffer::AppendData(uint8_t* data, size_t length)
+{
+    size_t offset = this->byteLength;
+    Grow(length);
+    memcpy(mData.get() + offset, data, length);
+    return offset;
+}
+
+inline void Buffer::Grow(size_t amount)
+{
+    if (amount <= 0) return;
+    uint8_t* b = new uint8_t[byteLength + amount];
+    if (mData) memcpy(b, mData.get(), byteLength);
+    mData.reset(b);
+    byteLength += amount;
+}
+
+
+inline void BufferView::Read(Value& obj, Asset& r)
+{
+    const char* bufferId = MemberOrDefault<const char*>(obj, "buffer", 0);
+    if (bufferId) {
+        buffer = r.buffers.Get(bufferId);
+    }
+        
+    byteOffset = MemberOrDefault(obj, "byteOffset", 0u);
+    byteLength = MemberOrDefault(obj, "byteLength", 0u);
+}
+
+
+
+inline void Accessor::Read(Value& obj, Asset& r)
+{
+    const char* bufferViewId = MemberOrDefault<const char*>(obj, "bufferView", 0);
+    if (bufferViewId) {
+        bufferView = r.bufferViews.Get(bufferViewId);
+    }
+
+    byteOffset = MemberOrDefault(obj, "byteOffset", 0u);
+    byteStride = MemberOrDefault(obj, "byteStride", 0u);
+    componentType = MemberOrDefault(obj, "componentType", ComponentType_BYTE);
+    count = MemberOrDefault(obj, "count", 0u);
+
+    const char* typestr;
+    type = ReadMember(obj, "type", typestr) ? AttribType::FromString(typestr) : AttribType::SCALAR;
+}
+
+inline unsigned int Accessor::GetNumComponents()
+{
+    return AttribType::GetNumComponents(type);
+}
+
+inline unsigned int Accessor::GetBytesPerComponent()
+{
+    return ComponentTypeSize(componentType);
+}
+
+inline unsigned int Accessor::GetElementSize()
+{
+    return GetNumComponents() * GetBytesPerComponent();
+}
+
+inline uint8_t* Accessor::GetPointer()
+{
+    if (!bufferView || !bufferView->buffer) return 0;
+
+    size_t offset = byteOffset + bufferView->byteOffset;
+    return bufferView->buffer->GetPointer() + offset;
+}
+
+namespace {
+    inline void CopyData(size_t count,
+            const uint8_t* src, size_t src_stride,
+                  uint8_t* dst, size_t dst_stride)
+    {
+        if (src_stride == dst_stride) {
+            memcpy(dst, src, count * src_stride);
+        }
+        else {
+            size_t sz = std::min(src_stride, dst_stride);
+            for (size_t i = 0; i < count; ++i) {
+                memcpy(dst, src, sz);
+                if (sz < dst_stride) {
+                    memset(dst + sz, 0, dst_stride - sz);
+                }
+                src += src_stride;
+                dst += dst_stride;
+            }
+        }
+    }
+}
+
+template<class T>
+void Accessor::ExtractData(T*& outData)
+{
+    uint8_t* data = GetPointer();
+    ai_assert(data);
+
+    const size_t elemSize = GetElementSize();
+    const size_t totalSize = elemSize * count;
+
+    const size_t stride = byteStride ? byteStride : elemSize;
+
+    const size_t targetElemSize = sizeof(T);
+    ai_assert(elemSize <= targetElemSize);
+
+    ai_assert(count*stride <= bufferView->byteLength);
+
+    outData = new T[count];
+    if (stride == elemSize && targetElemSize == elemSize) {
+        memcpy(outData, data, totalSize);
+    }
+    else {
+        for (size_t i = 0; i < count; ++i) {
+            memcpy(outData + i, data + i*stride, elemSize);
+        }
+    }
+}
+
+inline void Accessor::WriteData(size_t count, const void* src_buffer, size_t src_stride)
+{
+    uint8_t* buffer_ptr = bufferView->buffer->GetPointer();
+    size_t offset = byteOffset + bufferView->byteOffset;
+
+    size_t dst_stride = GetNumComponents() * GetBytesPerComponent();
+
+    const uint8_t* src = reinterpret_cast<const uint8_t*>(src_buffer);
+    uint8_t*       dst = reinterpret_cast<      uint8_t*>(buffer_ptr + offset);
+
+    ai_assert(dst + count*dst_stride <= buffer_ptr + bufferView->buffer->byteLength);
+    CopyData(count, src, src_stride, dst, dst_stride);
+}
+
+
+
+inline Accessor::Indexer::Indexer(Accessor& acc)
+    : accessor(acc)
+    , data(acc.GetPointer())
+    , elemSize(acc.GetElementSize())
+    , stride(acc.byteStride ? acc.byteStride : elemSize)
+{
+
+}
+
+//! Accesses the i-th value as defined by the accessor
+template<class T>
+T Accessor::Indexer::GetValue(int i)
+{
+    ai_assert(data);
+    ai_assert(i*stride < accessor.bufferView->byteLength);
+    T value = T();
+    memcpy(&value, data + i*stride, elemSize);
+    //value >>= 8 * (sizeof(T) - elemSize);
+    return value;
+}
+
+inline Image::Image()
+    : mData(0)
+    , mDataLength(0)
+{
+
+}
+
+inline void Image::Read(Value& obj, Asset& r)
+{
+    // Check for extensions first (to detect binary embedded data) 
+    if (Value* extensions = FindObject(obj, "extensions")) {
+        if (r.extensionsUsed.KHR_binary_glTF) {
+            if (Value* ext = FindObject(*extensions, "KHR_binary_glTF")) {
+
+                int width  = MemberOrDefault(*ext, "width", 0);
+                int height = MemberOrDefault(*ext, "height", 0);
+
+                ReadMember(*ext, "mimeType", mimeType);
+
+                const char* bufferViewId;
+                if (ReadMember(*ext, "bufferView", bufferViewId)) {
+                    Ref<BufferView> bv = r.bufferViews.Get(bufferViewId);
+                    if (bv) {
+                        mDataLength = bv->byteLength;
+                        mData = new uint8_t[mDataLength];
+                        memcpy(mData, bv->buffer->GetPointer() + bv->byteOffset, mDataLength);
+                    }
+                }
+            }
+        }
+    }
+
+    if (!mDataLength) {
+        if (Value* uri = FindString(obj, "uri")) {
+            const char* uristr = uri->GetString();
+
+            Util::DataURI dataURI;
+            if (ParseDataURI(uristr, uri->GetStringLength(), dataURI)) {
+                mimeType = dataURI.mediaType;
+                if (dataURI.base64) {
+                    mDataLength = Util::DecodeBase64(dataURI.data, dataURI.dataLength, mData);
+                }
+            }
+            else {
+                this->uri = uristr;
+            }
+        }
+    }
+}
+
+inline uint8_t* Image::StealData()
+{
+    uint8_t* data = mData;
+    mDataLength = 0;
+    mData = 0;
+    return data;
+}
+
+inline void Image::SetData(uint8_t* data, size_t length, Asset& r)
+{
+    Ref<Buffer> b = r.GetBodyBuffer();
+    if (b) { // binary file: append to body
+        std::string bvId = r.FindUniqueID(this->id, "imgdata");
+        bufferView = r.bufferViews.Create(bvId);
+
+        bufferView->buffer = b;
+        bufferView->byteLength = length;
+        bufferView->byteOffset = b->AppendData(data, length);
+    }
+    else { // text file: will be stored as a data uri
+        this->mData = data;
+        this->mDataLength = length;
+    }
+}
+
+inline void Texture::Read(Value& obj, Asset& r)
+{
+    const char* sourcestr;
+    if (ReadMember(obj, "source", sourcestr)) {
+        source = r.images.Get(sourcestr);
+    }
+}
+
+namespace {
+    inline void ReadMaterialProperty(Asset& r, Value& vals, const char* propName, TexProperty& out)
+    {
+        if (Value* prop = FindMember(vals, propName)) {
+            if (prop->IsString()) {
+                out.texture = r.textures.Get(prop->GetString());
+            }
+            else {
+                ReadValue(*prop, out.color);
+            }
+        }
+    }
+}
+
+inline void Material::Read(Value& material, Asset& r)
+{
+    SetDefaults();
+
+    if (Value* values = FindObject(material, "values")) {
+        ReadMaterialProperty(r, *values, "ambient", this->ambient);
+        ReadMaterialProperty(r, *values, "diffuse", this->diffuse);
+        ReadMaterialProperty(r, *values, "specular", this->specular);
+
+        ReadMember(*values, "shininess", shininess);
+    }
+
+    if (Value* extensions = FindObject(material, "extensions")) {
+        if (r.extensionsUsed.KHR_materials_common) {
+            if (Value* ext = FindObject(*extensions, "KHR_materials_common")) {
+                if (Value* tnq = FindString(*ext, "technique")) {
+                    const char* t = tnq->GetString();
+                    if      (strcmp(t, "BLINN") == 0)    technique = Technique_BLINN;
+                    else if (strcmp(t, "PHONG") == 0)    technique = Technique_PHONG;
+                    else if (strcmp(t, "LAMBERT") == 0)  technique = Technique_LAMBERT;
+                    else if (strcmp(t, "CONSTANT") == 0) technique = Technique_CONSTANT;
+                }
+
+                ReadMaterialProperty(r, *ext, "ambient", this->ambient);
+                ReadMaterialProperty(r, *ext, "diffuse", this->diffuse);
+                ReadMaterialProperty(r, *ext, "specular", this->specular);
+
+                ReadMember(*ext, "doubleSided", doubleSided);
+                ReadMember(*ext, "transparent", transparent);
+                ReadMember(*ext, "transparency", transparency);
+                ReadMember(*ext, "shininess", shininess);
+            }
+        }
+    }
+}
+
+namespace {
+    void SetVector(vec4& v, float x, float y, float z, float w)
+        { v[0] = x; v[1] = y; v[2] = z; v[3] = w; }
+}
+
+inline void Material::SetDefaults()
+{
+    SetVector(ambient.color, 0, 0, 0, 1);
+    SetVector(diffuse.color, 0, 0, 0, 1);
+    SetVector(specular.color, 0, 0, 0, 1);
+    SetVector(emission.color, 0, 0, 0, 1);
+
+    doubleSided = false;
+    transparent = false;
+    transparency = 1.0;
+    shininess = 0.0;
+
+    technique = Technique_undefined;
+}
+
+namespace {
+
+    template<int N>
+    inline int Compare(const char* attr, const char (&str)[N]) {
+        return (strncmp(attr, str, N - 1) == 0) ? N - 1 : 0;
+    }
+
+    inline bool GetAttribVector(Mesh::Primitive& p, const char* attr, Mesh::AccessorList*& v, int& pos)
+    {
+        if (pos = Compare(attr, "POSITION")) {
+            v = &(p.attributes.position);
+        }
+        else if (pos = Compare(attr, "NORMAL")) {
+            v = &(p.attributes.normal);
+        }
+        else if (pos = Compare(attr, "TEXCOORD")) {
+            v = &(p.attributes.texcoord);
+        }
+        else if (pos = Compare(attr, "COLOR")) {
+            v = &(p.attributes.color);
+        }
+        else if (pos = Compare(attr, "JOINT")) {
+            v = &(p.attributes.joint);
+        }
+        else if (pos = Compare(attr, "JOINTMATRIX")) {
+            v = &(p.attributes.jointmatrix);
+        }
+        else if (pos = Compare(attr, "WEIGHT")) {
+            v = &(p.attributes.weight);
+        }
+        else return false;
+        return true;
+    }
+}
+
+inline void Mesh::Read(Value& obj, Asset& r)
+{  
+    if (Value* primitives = FindArray(obj, "primitives")) {
+        this->primitives.resize(primitives->Size());
+        for (unsigned int i = 0; i < primitives->Size(); ++i) {
+            Value& primitive = (*primitives)[i];
+
+            Primitive& prim = this->primitives[i];
+            prim.mode = MemberOrDefault(primitive, "mode", PrimitiveMode_TRIANGLES);
+
+            if (Value* attrs = FindObject(primitive, "attributes")) {
+                for (Value::MemberIterator it = attrs->MemberBegin(); it != attrs->MemberEnd(); ++it) {
+                    if (!it->value.IsString()) continue;
+                    const char* attr = it->name.GetString();
+                    // Valid attribute semantics include POSITION, NORMAL, TEXCOORD, COLOR, JOINT, JOINTMATRIX, and WEIGHT.Attribute semantics can be of the form[semantic]_[set_index], e.g., TEXCOORD_0, TEXCOORD_1, etc.
+
+                    int undPos = 0;
+                    Mesh::AccessorList* vec = 0;
+                    if (GetAttribVector(prim, attr, vec, undPos)) {
+                        size_t idx = (attr[undPos] == '_') ? atoi(attr + undPos + 1) : 0;
+                        if ((*vec).size() <= idx) (*vec).resize(idx + 1);
+                        (*vec)[idx] = r.accessors.Get(it->value.GetString());
+                    }
+                }
+            }
+
+            if (Value* indices = FindString(primitive, "indices")) {
+                prim.indices = r.accessors.Get(indices->GetString());
+            }
+
+            if (Value* material = FindString(primitive, "material")) {
+                prim.material = r.materials.Get(material->GetString());
+            }
+        }
+    }
+}
+
+
+inline void Camera::Read(Value& obj, Asset& r)
+{
+    type = MemberOrDefault(obj, "type", Camera::Perspective);
+
+    const char* subobjId = (type == Camera::Orthographic) ? "ortographic" : "perspective";
+
+    Value* it = FindObject(obj, subobjId);
+    if (!it) throw DeadlyImportError("Camera missing its parameters!");
+
+    if (type == Camera::Perspective) {
+        perspective.aspectRatio = MemberOrDefault(*it, "aspectRatio", 0.f);
+        perspective.yfov        = MemberOrDefault(*it, "yfov", 3.1415f/2.f);
+        perspective.zfar        = MemberOrDefault(*it, "zfar", 100.f);
+        perspective.znear       = MemberOrDefault(*it, "znear", 0.01f);
+    }
+    else {
+        ortographic.xmag  = MemberOrDefault(obj, "xmag", 1.f);
+        ortographic.ymag  = MemberOrDefault(obj, "ymag", 1.f);
+        ortographic.zfar  = MemberOrDefault(obj, "zfar", 100.f);
+        ortographic.znear = MemberOrDefault(obj, "znear", 0.01f);
+    }
+}
+
+inline void Light::Read(Value& obj, Asset& r)
+{
+    SetDefaults();
+
+    if (Value* type = FindString(obj, "type")) {
+        const char* t = type->GetString();
+        if      (strcmp(t, "ambient") == 0)     this->type = Type_ambient;
+        else if (strcmp(t, "directional") == 0) this->type = Type_directional;
+        else if (strcmp(t, "point") == 0)       this->type = Type_point;
+        else if (strcmp(t, "spot") == 0)        this->type = Type_spot;
+
+        if (this->type != Type_undefined) {
+            if (Value* vals = FindString(obj, t)) {
+                ReadMember(*vals, "color", color);
+
+                ReadMember(*vals, "constantAttenuation", constantAttenuation);
+                ReadMember(*vals, "linearAttenuation", linearAttenuation);
+                ReadMember(*vals, "quadraticAttenuation", quadraticAttenuation);
+                ReadMember(*vals, "distance", distance);
+
+                ReadMember(*vals, "falloffAngle", falloffAngle);
+                ReadMember(*vals, "falloffExponent", falloffExponent);
+            }
+        }
+    }
+}
+
+inline void Light::SetDefaults()
+{
+    #ifndef M_PI
+        const float M_PI = 3.14159265358979323846f;
+    #endif
+
+    type = Type_undefined;
+
+    SetVector(color, 0.f, 0.f, 0.f, 1.f);
+
+    constantAttenuation = 0.f;
+    linearAttenuation = 1.f;
+    quadraticAttenuation = 1.f;
+    distance = 0.f;
+
+    falloffAngle = static_cast<float>(M_PI / 2.f);
+    falloffExponent = 0.f;
+}
+
+inline void Node::Read(Value& obj, Asset& r)
+{
+    if (Value* children = FindArray(obj, "children")) {
+        this->children.reserve(children->Size());
+        for (unsigned int i = 0; i < children->Size(); ++i) {
+            Value& child = (*children)[i];
+            if (child.IsString()) {
+                // get/create the child node
+                Ref<Node> chn = r.nodes.Get(child.GetString());
+                if (chn) this->children.push_back(chn);
+            }
+        }
+    }
+
+    
+    if (Value* matrix = FindArray(obj, "matrix")) {
+        ReadValue(*matrix, this->matrix);
+    }
+    else {
+        ReadMember(obj, "translation", translation);
+        ReadMember(obj, "scale", scale);
+        ReadMember(obj, "rotation", rotation);
+    }
+
+    if (Value* meshes = FindArray(obj, "meshes")) {
+        size_t numMeshes = (size_t)meshes->Size();
+
+        std::vector<unsigned int> meshList;
+
+        this->meshes.reserve(numMeshes);
+        for (size_t i = 0; i < numMeshes; ++i) {
+            if ((*meshes)[i].IsString()) {
+                Ref<Mesh> mesh = r.meshes.Get((*meshes)[i].GetString());
+                if (mesh) this->meshes.push_back(mesh);
+            }
+        }
+    }
+
+    if (Value* camera = FindString(obj, "camera")) {
+        this->camera = r.cameras.Get(camera->GetString());
+        if (this->camera)
+            this->camera->id = this->id;
+    }
+
+    // TODO load "skeletons", "skin", "jointName"
+
+    if (Value* extensions = FindObject(obj, "extensions")) {
+        if (r.extensionsUsed.KHR_materials_common) {
+
+            if (Value* ext = FindObject(*extensions, "KHR_materials_common")) {
+                if (Value* light = FindString(*ext, "light")) {
+                    this->light = r.lights.Get(light->GetString());
+                }
+            }
+
+        }
+    }
+}
+
+inline void Scene::Read(Value& obj, Asset& r)
+{
+    if (Value* array = FindArray(obj, "nodes")) {
+        for (unsigned int i = 0; i < array->Size(); ++i) {
+            if (!(*array)[i].IsString()) continue;
+            Ref<Node> node = r.nodes.Get((*array)[i].GetString());
+            if (node)
+                this->nodes.push_back(node);
+        }
+    }
+}
+
+
+inline void AssetMetadata::Read(Document& doc)
+{
+    // read the version, etc.
+    int statedVersion = 0;
+    if (Value* obj = FindObject(doc, "asset")) {
+        ReadMember(*obj, "copyright", copyright);
+        ReadMember(*obj, "generator", generator);
+
+        premultipliedAlpha = MemberOrDefault(*obj, "premultipliedAlpha", false);
+        statedVersion = MemberOrDefault(*obj, "version", 0);
+
+        if (Value* profile = FindObject(*obj, "profile")) {
+            ReadMember(*profile, "api",     this->profile.api);
+            ReadMember(*profile, "version", this->profile.version);
+        }
+    }
+
+    version = std::max(statedVersion, version);
+    if (version == 0) {
+        // if missing version, we'll assume version 1...
+        version = 1;
+    }
+
+    if (version != 1) {
+        char msg[128];
+        sprintf(msg, "Unsupported glTF version: %d", version);
+        throw DeadlyImportError(msg);
+    }
+}
+
+
+
+//
+// Asset methods implementation
+//
+
+inline void Asset::ReadBinaryHeader(IOStream& stream)
+{
+    GLB_Header header;
+    if (stream.Read(&header, sizeof(header), 1) != 1) {
+        throw DeadlyImportError("Unable to read the file header");
+    }
+
+    if (strncmp((char*)header.magic, AI_GLB_MAGIC_NUMBER, sizeof(header.magic)) != 0) {
+        throw DeadlyImportError("Invalid binary glTF file");
+    }
+
+    AI_SWAP4(header.version);
+    asset.version = header.version;
+    if (header.version != 1) {
+        throw DeadlyImportError("Unsupported binary glTF version");
+    }
+
+    AI_SWAP4(header.sceneFormat);
+    if (header.sceneFormat != SceneFormat_JSON) {
+        throw DeadlyImportError("Unsupported binary glTF scene format");
+    }
+
+    AI_SWAP4(header.length);
+    AI_SWAP4(header.sceneLength);
+
+    mSceneLength = static_cast<size_t>(header.sceneLength);
+
+    mBodyOffset = sizeof(header)+mSceneLength;
+    mBodyOffset = (mBodyOffset + 3) & ~3; // Round up to next multiple of 4
+
+    mBodyLength = header.length - mBodyOffset;
+}
+
+inline void Asset::Load(const std::string& pFile, bool isBinary)
+{
+    mCurrentAssetDir.clear();
+    int pos = std::max(int(pFile.rfind('/')), int(pFile.rfind('\\')));
+    if (pos != int(std::string::npos)) mCurrentAssetDir = pFile.substr(0, pos + 1);
+
+    shared_ptr<IOStream> stream(OpenFile(pFile.c_str(), "rb", true));
+    if (!stream) {
+        throw DeadlyImportError("Could not open file for reading");
+    }
+
+    // is binary? then read the header
+    if (isBinary) {
+        SetAsBinary(); // also creates the body buffer
+        ReadBinaryHeader(*stream);
+    }
+    else {
+        mSceneLength = stream->FileSize();
+        mBodyLength = 0;
+    }
+
+
+    // read the scene data
+
+    std::vector<char> sceneData(mSceneLength + 1);
+    sceneData[mSceneLength] = '\0';
+
+    if (stream->Read(&sceneData[0], 1, mSceneLength) != mSceneLength) {
+        throw DeadlyImportError("Could not read the file contents");
+    }
+
+
+    // parse the JSON document
+
+    Document doc;
+    doc.ParseInsitu(&sceneData[0]);
+
+    if (doc.HasParseError()) {
+        char buffer[32];
+        sprintf(buffer, "%u", doc.GetErrorOffset());
+        throw DeadlyImportError(std::string("JSON parse error, offset ") + buffer + ": "
+            + GetParseError_En(doc.GetParseError()));
+    }
+
+    if (!doc.IsObject()) {
+        throw DeadlyImportError("gltf file must be a JSON object!");
+    }
+
+    // Fill the buffer instance for the current file embedded contents
+    if (mBodyLength > 0) {
+        mBodyBuffer->LoadFromStream(*stream, mBodyLength, mBodyOffset);
+    }
+
+
+    // Load the metadata
+    asset.Read(doc);
+    ReadExtensionsUsed(doc);
+
+    // Prepare the dictionaries
+    for (size_t i = 0; i < mDicts.size(); ++i) {
+        mDicts[i]->AttachToDocument(doc);
+    }
+
+
+
+    // Read the "scene" property, which specifies which scene to load
+    // and recursively load everything referenced by it
+    if (Value* scene = FindString(doc, "scene")) {
+        this->scene = scenes.Get(scene->GetString());
+    }
+
+    // Clean up
+    for (size_t i = 0; i < mDicts.size(); ++i) {
+        mDicts[i]->DetachFromDocument();
+    }
+}
+
+inline void Asset::SetAsBinary()
+{
+    if (!extensionsUsed.KHR_binary_glTF) {
+        extensionsUsed.KHR_binary_glTF = true;
+        mBodyBuffer = buffers.Create("KHR_binary_glTF");
+        mBodyBuffer->MarkAsSpecial();
+    }
+}
+
+
+inline void Asset::ReadExtensionsUsed(Document& doc)
+{
+    Value* extsUsed = FindArray(doc, "extensionsUsed");
+    if (!extsUsed) return;
+
+    std::gltf_unordered_map<std::string, bool> exts;
+
+    for (unsigned int i = 0; i < extsUsed->Size(); ++i) {
+        if ((*extsUsed)[i].IsString()) {
+            exts[(*extsUsed)[i].GetString()] = true;
+        }
+    }
+
+    #define CHECK_EXT(EXT) \
+        if (exts.find(#EXT) != exts.end()) extensionsUsed.EXT = true;
+
+    CHECK_EXT(KHR_binary_glTF);
+    CHECK_EXT(KHR_materials_common);
+
+    #undef CHECK_EXT
+}
+
+inline IOStream* Asset::OpenFile(std::string path, const char* mode, bool absolute)
+{
+    #ifdef ASSIMP_API
+        return mIOSystem->Open(path, mode);
+    #else
+        if (path.size() < 2) return 0;
+        if (!absolute && path[1] != ':' && path[0] != '/') { // relative?
+            path = mCurrentAssetDir + path;
+        }
+        FILE* f = fopen(path.c_str(), mode);
+        return f ? new IOStream(f) : 0;
+    #endif
+}
+
+inline std::string Asset::FindUniqueID(const std::string& str, const char* suffix)
+{
+    std::string id = str;
+
+    Asset::IdMap::iterator it;
+
+    do {
+        if (!id.empty()) {
+            it = mUsedIds.find(id);
+            if (it == mUsedIds.end()) break;
+
+            id += "_";
+        }
+
+        id += suffix;
+
+        it = mUsedIds.find(id);
+        if (it == mUsedIds.end()) break;
+
+        char buffer[256];
+        int offset = sprintf(buffer, "%s_", id.c_str());
+        for (int i = 0; it != mUsedIds.end(); ++i) {
+            sprintf(buffer + offset, "%d", i);
+
+            id = buffer;
+            it = mUsedIds.find(id);
+        }
+    } while (false); // fake loop to allow using "break"
+    
+    return id;
+}
+
+namespace Util
+{
+
+    inline bool ParseDataURI(const char* const_uri, size_t uriLen, DataURI& out)
+    {
+        if (const_uri[0] != 0x10) { // we already parsed this uri?
+            if (strncmp(const_uri, "data:", 5) != 0) // not a data uri?
+                return false;
+        }
+
+        // set defaults
+        out.mediaType = "text/plain";
+        out.charset = "US-ASCII";
+        out.base64 = false;
+
+        char* uri = const_cast<char*>(const_uri);
+        if (uri[0] != 0x10) {
+            uri[0] = 0x10;
+            uri[1] = uri[2] = uri[3] = uri[4] = 0;
+
+            size_t i = 5, j;
+            if (uri[i] != ';' && uri[i] != ',') { // has media type?
+                uri[1] = i;
+                for (; uri[i] != ';' && uri[i] != ',' && i < uriLen; ++i) {}
+            }
+            while (uri[i] == ';' && i < uriLen) {
+                uri[i++] = '\0';
+                for (j = i; uri[i] != ';' && uri[i] != ',' && i < uriLen; ++i) {}
+
+                if (strncmp(uri + j, "charset=", 8) == 0) uri[2] = j + 8;
+                else if (strncmp(uri + j, "base64", 6) == 0) uri[3] = j;
+            }
+            if (i < uriLen) {
+                uri[i++] = '\0';
+                uri[4] = i;
+            }
+            else {
+                uri[1] = uri[2] = uri[3] = 0;
+                uri[4] = 5;
+            }
+        }
+
+        if (uri[1] != 0) out.mediaType = uri + uri[1];
+        if (uri[2] != 0) out.charset = uri + uri[2];
+        if (uri[3] != 0) out.base64 = true;
+        out.data = uri + uri[4];
+        out.dataLength = (uri + uriLen) - out.data;
+
+        return true;
+    }
+
+    template<bool B>
+    struct DATA
+    {
+        static const uint8_t tableDecodeBase64[128];
+    };
+
+    template<bool B>
+    const uint8_t DATA<B>::tableDecodeBase64[128] = {
+         0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+         0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+         0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, 62,  0,  0,  0, 63,
+        52, 53, 54, 55, 56, 57, 58, 59, 60, 61,  0,  0,  0, 64,  0,  0,
+         0,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14,
+        15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,  0,  0,  0,  0,  0,
+         0, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
+        41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51,  0,  0,  0,  0,  0
+    };
+
+    inline char EncodeCharBase64(uint8_t b)
+    {
+        return "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="[b];
+    }
+
+    inline uint8_t DecodeCharBase64(char c)
+    {
+        return DATA<true>::tableDecodeBase64[c]; // TODO faster with lookup table or ifs?
+        /*if (c >= 'A' && c <= 'Z') return c - 'A';
+        if (c >= 'a' && c <= 'z') return c - 'a' + 26;
+        if (c >= '0' && c <= '9') return c - '0' + 52;
+        if (c == '+') return 62;
+        if (c == '/') return 63;
+        return 64; // '-' */
+    }
+
+    inline size_t DecodeBase64(const char* in, size_t inLength, uint8_t*& out)
+    {
+        ai_assert(inLength % 4 == 0);
+
+        if (inLength < 4) {
+            out = 0;
+            return 0;
+        }
+
+        int nEquals = int(in[inLength - 1] == '=') +
+                      int(in[inLength - 2] == '=');
+
+        size_t outLength = (inLength * 3) / 4 - nEquals;
+        out = new uint8_t[outLength];
+        memset(out, 0, outLength);
+
+        size_t i, j = 0;
+
+        for (i = 0; i + 4 < inLength; i += 4) {
+            uint8_t b0 = DecodeCharBase64(in[i]);
+            uint8_t b1 = DecodeCharBase64(in[i + 1]);
+            uint8_t b2 = DecodeCharBase64(in[i + 2]);
+            uint8_t b3 = DecodeCharBase64(in[i + 3]);
+
+            out[j++] = (uint8_t)((b0 << 2) | (b1 >> 4));
+            out[j++] = (uint8_t)((b1 << 4) | (b2 >> 2));
+            out[j++] = (uint8_t)((b2 << 6) | b3);
+        }
+
+        {
+            uint8_t b0 = DecodeCharBase64(in[i]);
+            uint8_t b1 = DecodeCharBase64(in[i + 1]);
+            uint8_t b2 = DecodeCharBase64(in[i + 2]);
+            uint8_t b3 = DecodeCharBase64(in[i + 3]);
+
+            out[j++] = (uint8_t)((b0 << 2) | (b1 >> 4));
+            if (b2 < 64) out[j++] = (uint8_t)((b1 << 4) | (b2 >> 2));
+            if (b3 < 64) out[j++] = (uint8_t)((b2 << 6) | b3);
+        }
+
+        return outLength;
+    }
+
+
+
+    inline void EncodeBase64(
+        const uint8_t* in, size_t inLength,
+        std::string& out)
+    {
+        size_t outLength = ((inLength + 2) / 3) * 4;
+
+        size_t j = out.size();
+        out.resize(j + outLength);
+
+        for (size_t i = 0; i <  inLength; i += 3) {
+            uint8_t b = (in[i] & 0xFC) >> 2;
+            out[j++] = EncodeCharBase64(b);
+
+            b = (in[i] & 0x03) << 4;
+            if (i + 1 < inLength) {
+                b |= (in[i + 1] & 0xF0) >> 4;
+                out[j++] = EncodeCharBase64(b);
+
+                b = (in[i + 1] & 0x0F) << 2;
+                if (i + 2 < inLength) {
+                    b |= (in[i + 2] & 0xC0) >> 6;
+                    out[j++] = EncodeCharBase64(b);
+
+                    b = in[i + 2] & 0x3F;
+                    out[j++] = EncodeCharBase64(b);
+                }
+                else {
+                    out[j++] = EncodeCharBase64(b);
+                    out[j++] = '=';
+                }
+            }
+            else {
+                out[j++] = EncodeCharBase64(b);
+                out[j++] = '=';
+                out[j++] = '=';
+            }
+        }
+    }
+
+}
+
+}
+
+
similarity index 62%
rename from code/glTFUtil.h
rename to code/glTFAssetWriter.h
index 089632c..65d0532 100644 (file)
@@ -37,49 +37,47 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 ----------------------------------------------------------------------
 */
-#ifndef AI_GLTFUTIL_H_INC
-#define AI_GLTFUTIL_H_INC
-
-//#include "StreamReader.h"
-//#include "MemoryIOWrapper.h"
-#include "StringComparison.h"
-
-#if _MSC_VER > 1500 || (defined __GNUC___)
-#   define ASSIMP_GLTF_USE_UNORDERED_MULTIMAP
-#   else
-#   define gltf_unordered_map map
-#   define gltf_unordered_multimap multimap
-#endif
 
-#ifdef ASSIMP_GLTF_USE_UNORDERED_MULTIMAP
-#   include <unordered_map>
-#   if _MSC_VER > 1600
-#       define gltf_unordered_map unordered_map
-#       define gltf_unordered_multimap unordered_multimap
-#   else
-#       define gltf_unordered_map tr1::unordered_map
-#       define gltf_unordered_multimap tr1::unordered_multimap
-#   endif
-#endif
+/** @file glTFWriter.h
+ * Declares a class to write gltf/glb files
+ *
+ * glTF Extensions Support:
+ *   KHR_binary_glTF: full
+ *   KHR_materials_common: full
+ */
+#ifndef glTFAssetWriter_H_INC
+#define glTFAssetWriter_H_INC
 
-namespace Assimp {
-namespace glTF {
+#include "glTFAsset.h"
 
-    //
-    // Misc
-    //
+namespace glTF
+{
 
-    std::size_t DecodeBase64(const char* in, uint8_t*& out);
-    std::size_t DecodeBase64(const char* in, std::size_t inLength, uint8_t*& out);
+using rapidjson::MemoryPoolAllocator;
 
-    void EncodeBase64(const uint8_t* in, std::size_t inLength, std::string& out);
+class AssetWriter
+{
+private:
 
+    void WriteBinaryData(IOStream* outfile, size_t sceneLength);
 
-    bool IsDataURI(const char* uri);
+    void WriteMetadata();
+    void WriteExtensionsUsed();
 
-}
-}
+public:
+    Document mDoc;
+    Asset& mAsset;
+
+    MemoryPoolAllocator<>& mAl;
 
+    AssetWriter(Asset& asset);
 
-#endif // AI_GLTFUTIL_H_INC
+    void WriteFile(const char* path);
+};
 
+}
+
+// Include the implementation of the methods
+#include "glTFAssetWriter.inl"
+
+#endif
diff --git a/code/glTFAssetWriter.inl b/code/glTFAssetWriter.inl
new file mode 100644 (file)
index 0000000..d6e313f
--- /dev/null
@@ -0,0 +1,490 @@
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2015, assimp team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+copyright notice, this list of conditions and the
+following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the
+following disclaimer in the documentation and/or other
+materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+contributors may be used to endorse or promote products
+derived from this software without specific prior
+written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+#include <rapidjson/stringbuffer.h>
+#include <rapidjson/writer.h>
+#include <rapidjson/prettywriter.h>
+
+namespace glTF {
+
+    using rapidjson::StringBuffer;
+    using rapidjson::PrettyWriter;
+    using rapidjson::Writer;
+    using rapidjson::StringRef;
+    using rapidjson::StringRef;
+
+    namespace {
+
+        template<size_t N>
+        inline Value& MakeValue(Value& val, float(&r)[N], MemoryPoolAllocator<>& al) {
+            val.SetArray();
+            val.Reserve(N, al);
+            for (int i = 0; i < N; ++i) {
+                val.PushBack(r[i], al);
+            }
+            return val;
+        };
+
+        template<class T>
+        inline void AddRefsVector(Value& obj, const char* fieldId, std::vector< Ref<T> >& v, MemoryPoolAllocator<>& al) {
+            if (v.empty()) return;
+            Value lst;
+            lst.SetArray();
+            lst.Reserve(v.size(), al);
+            for (size_t i = 0; i < v.size(); ++i) {
+                lst.PushBack(StringRef(v[i]->id), al);
+            }
+            obj.AddMember(StringRef(fieldId), lst, al);
+        };
+
+
+    }
+
+    inline void Write(Value& obj, Accessor& a, AssetWriter& w)
+    {
+        obj.AddMember("bufferView", Value(a.bufferView->id, w.mAl).Move(), w.mAl);
+        obj.AddMember("byteOffset", a.byteOffset, w.mAl);
+        obj.AddMember("byteStride", a.byteStride, w.mAl);
+        obj.AddMember("componentType", int(a.componentType), w.mAl);
+        obj.AddMember("count", a.count, w.mAl);
+        obj.AddMember("type", StringRef(AttribType::ToString(a.type)), w.mAl);
+    }
+
+    inline void Write(Value& obj, Animation& a, AssetWriter& w)
+    {
+
+    }
+
+    inline void Write(Value& obj, Buffer& b, AssetWriter& w)
+    {
+        std::string dataURI = "data:application/octet-stream;base64,";
+        Util::EncodeBase64(b.GetPointer(), b.byteLength, dataURI);
+
+        const char* type;
+        switch (b.type) {
+            case Buffer::Type_text:
+                type = "text"; break;
+            default:
+                type = "arraybuffer";
+        }
+
+        obj.AddMember("byteLength", b.byteLength, w.mAl);
+        obj.AddMember("type", StringRef(type), w.mAl);
+        obj.AddMember("uri", Value(dataURI, w.mAl).Move(), w.mAl);
+    }
+
+    inline void Write(Value& obj, BufferView& bv, AssetWriter& w)
+    {
+        obj.AddMember("buffer", Value(bv.buffer->id, w.mAl).Move(), w.mAl);
+        obj.AddMember("byteOffset", bv.byteOffset, w.mAl);
+        obj.AddMember("byteLength", bv.byteLength, w.mAl);
+        obj.AddMember("target", int(bv.target), w.mAl);
+    }
+
+    inline void Write(Value& obj, Camera& c, AssetWriter& w)
+    {
+
+    }
+
+    inline void Write(Value& obj, Image& img, AssetWriter& w)
+    {
+        std::string uri;
+        if (w.mAsset.extensionsUsed.KHR_binary_glTF && img.bufferView) {
+            Value exts, ext;
+            exts.SetObject();
+            ext.SetObject();
+
+            ext.AddMember("bufferView", StringRef(img.bufferView->id), w.mAl);
+
+            if (!img.mimeType.empty())
+                ext.AddMember("mimeType", StringRef(img.mimeType), w.mAl);
+
+            exts.AddMember("KHR_binary_glTF", ext, w.mAl);
+            obj.AddMember("extensions", exts, w.mAl);
+            return;
+        }
+        else if (img.HasData()) {
+            uri = "data:" + (img.mimeType.empty() ? "application/octet-stream" : img.mimeType);
+            uri += ";base64,";
+            Util::EncodeBase64(img.GetData(), img.GetDataLength(), uri);
+        }
+        else {
+            uri = img.uri;
+        }
+
+        obj.AddMember("uri", Value(uri, w.mAl).Move(), w.mAl);
+    }
+
+    namespace {
+        inline void WriteColorOrTex(Value& obj, TexProperty& prop, const char* propName, MemoryPoolAllocator<>& al)
+        {
+            if (prop.texture)
+                obj.AddMember(StringRef(propName), Value(prop.texture->id, al).Move(), al);
+            else {
+                Value col;
+                obj.AddMember(StringRef(propName), MakeValue(col, prop.color, al), al);
+            }
+        }
+    }
+
+    inline void Write(Value& obj, Material& m, AssetWriter& w)
+    {
+        Value v;
+        v.SetObject();
+        {
+            WriteColorOrTex(v, m.ambient, "ambient", w.mAl);
+            WriteColorOrTex(v, m.diffuse, "diffuse", w.mAl);
+            WriteColorOrTex(v, m.specular, "specular", w.mAl);
+            WriteColorOrTex(v, m.emission, "emission", w.mAl);
+
+            v.AddMember("shininess", m.shininess, w.mAl);
+        }
+        obj.AddMember("values", v, w.mAl);
+    }
+
+    namespace {
+        inline void WriteAttrs(AssetWriter& w, Value& attrs, Mesh::AccessorList& lst,
+            const char* semantic, bool forceNumber = false)
+        {
+            if (lst.empty()) return;
+            if (lst.size() == 1 && !forceNumber) {
+                attrs.AddMember(StringRef(semantic), Value(lst[0]->id, w.mAl).Move(), w.mAl);
+            }
+            else {
+                for (size_t i = 0; i < lst.size(); ++i) {
+                    char buffer[32];
+                    sprintf(buffer, "%s_%d", semantic, i);
+                    attrs.AddMember(Value(buffer, w.mAl).Move(), Value(lst[i]->id, w.mAl).Move(), w.mAl);
+                }
+            }
+        }
+    }
+
+    inline void Write(Value& obj, Mesh& m, AssetWriter& w)
+    {
+        Value primitives;
+        primitives.SetArray();
+        primitives.Reserve(m.primitives.size(), w.mAl);
+
+        for (size_t i = 0; i < m.primitives.size(); ++i) {
+            Mesh::Primitive& p = m.primitives[i];
+            Value prim;
+            prim.SetObject();
+            {
+                prim.AddMember("mode", Value(int(p.mode)).Move(), w.mAl);
+
+                if (p.material)
+                    prim.AddMember("material", p.material->id, w.mAl);
+
+                if (p.indices)
+                    prim.AddMember("indices", Value(p.indices->id, w.mAl).Move(), w.mAl);
+
+                Value attrs;
+                attrs.SetObject();
+                {
+                    WriteAttrs(w, attrs, p.attributes.position, "POSITION");
+                    WriteAttrs(w, attrs, p.attributes.normal, "NORMAL");
+                    WriteAttrs(w, attrs, p.attributes.texcoord, "TEXCOORD", true);
+                    WriteAttrs(w, attrs, p.attributes.color, "COLOR");
+                    WriteAttrs(w, attrs, p.attributes.joint, "JOINT");
+                    WriteAttrs(w, attrs, p.attributes.jointmatrix, "JOINTMATRIX");
+                    WriteAttrs(w, attrs, p.attributes.weight, "WEIGHT");
+                }
+                prim.AddMember("attributes", attrs, w.mAl);
+            }
+            primitives.PushBack(prim, w.mAl);
+        }
+    
+        obj.AddMember("primitives", primitives, w.mAl);
+    }
+
+    inline void Write(Value& obj, Node& n, AssetWriter& w)
+    {
+
+        if (n.matrix.isPresent) {
+            Value val;
+            obj.AddMember("matrix", MakeValue(val, n.matrix.value, w.mAl).Move(), w.mAl);
+        }
+
+        if (n.translation.isPresent) {
+            Value val;
+            obj.AddMember("translation", MakeValue(val, n.translation.value, w.mAl).Move(), w.mAl);
+        }
+
+        if (n.scale.isPresent) {
+            Value val;
+            obj.AddMember("scale", MakeValue(val, n.scale.value, w.mAl).Move(), w.mAl);
+        }
+        if (n.rotation.isPresent) {
+            Value val;
+            obj.AddMember("rotation", MakeValue(val, n.rotation.value, w.mAl).Move(), w.mAl);
+        }
+
+        AddRefsVector(obj, "children", n.children, w.mAl);
+
+        AddRefsVector(obj, "meshes", n.meshes, w.mAl);
+    }
+
+    inline void Write(Value& obj, Program& b, AssetWriter& w)
+    {
+
+    }
+
+    inline void Write(Value& obj, Sampler& b, AssetWriter& w)
+    {
+
+    }
+
+    inline void Write(Value& scene, Scene& s, AssetWriter& w)
+    {
+        AddRefsVector(scene, "nodes", s.nodes, w.mAl);
+    }
+
+    inline void Write(Value& obj, Shader& b, AssetWriter& w)
+    {
+
+    }
+
+    inline void Write(Value& obj, Skin& b, AssetWriter& w)
+    {
+
+    }
+
+    inline void Write(Value& obj, Technique& b, AssetWriter& w)
+    {
+
+    }
+
+    inline void Write(Value& obj, Texture& tex, AssetWriter& w)
+    {
+        if (tex.source) {
+            obj.AddMember("source", Value(tex.source->id, w.mAl).Move(), w.mAl);
+        }
+    }
+
+    inline void Write(Value& obj, Light& b, AssetWriter& w)
+    {
+
+    }
+
+    template<class T>
+    void LazyDict<T>::WriteObjects(AssetWriter& w)
+    {
+        if (mObjs.empty()) return;
+
+        Value* container = &w.mDoc;
+
+        if (mExtId) {
+            Value* exts = FindObject(w.mDoc, "extensions");
+            if (!exts) {
+                w.mDoc.AddMember("extensions", Value().SetObject().Move(), w.mDoc.GetAllocator());
+                exts = FindObject(w.mDoc, "extensions");
+            }
+
+            if (!(container = FindObject(*exts, mExtId))) {
+                exts->AddMember(StringRef(mExtId), Value().SetObject().Move(), w.mDoc.GetAllocator());
+                container = FindObject(*exts, mExtId);
+            }
+        }
+
+        Value* dict;
+        if (!(dict = FindObject(*container, mDictId))) {
+            container->AddMember(StringRef(mDictId), Value().SetObject().Move(), w.mDoc.GetAllocator());
+            dict = FindObject(*container, mDictId);
+        }
+
+        for (size_t i = 0; i < mObjs.size(); ++i) {
+            if (mObjs[i]->IsSpecial()) continue;
+
+            Value obj;
+            obj.SetObject();
+
+            if (!mObjs[i]->name.empty()) {
+                obj.AddMember("name", StringRef(mObjs[i]->name.c_str()), w.mAl);
+            }
+
+            Write(obj, *mObjs[i], w);
+
+            dict->AddMember(StringRef(mObjs[i]->id), obj, w.mAl);
+        }
+    }
+
+
+    AssetWriter::AssetWriter(Asset& a)
+        : mAsset(a)
+        , mDoc()
+        , mAl(mDoc.GetAllocator())
+    {
+        mDoc.SetObject();
+
+        WriteMetadata();
+        WriteExtensionsUsed();
+
+        // Dump the contents of the dictionaries
+        for (size_t i = 0; i < a.mDicts.size(); ++i) {
+            a.mDicts[i]->WriteObjects(*this);
+        }
+
+        // Add the target scene field
+        if (mAsset.scene) {
+            mDoc.AddMember("scene", StringRef(mAsset.scene->id), mAl);
+        }
+    }
+
+
+    void AssetWriter::WriteFile(const char* path)
+    {
+        bool isBinary = mAsset.extensionsUsed.KHR_binary_glTF;
+
+        boost::scoped_ptr<IOStream> outfile
+            (mAsset.OpenFile(path, isBinary ? "wb" : "wt", true));
+
+        if (outfile == 0) {
+            throw DeadlyExportError("Could not open output file: " + std::string(path));
+        }
+
+        if (isBinary) {
+            // we will write the header later, skip its size
+            outfile->Seek(sizeof(GLB_Header), aiOrigin_SET);
+        }
+
+        StringBuffer docBuffer;
+
+        bool pretty = true; 
+        if (!isBinary && pretty) {
+            PrettyWriter<StringBuffer> writer(docBuffer);
+            mDoc.Accept(writer);
+        }
+        else {
+            Writer<StringBuffer> writer(docBuffer);
+            mDoc.Accept(writer);
+        }
+
+        if (outfile->Write(docBuffer.GetString(), docBuffer.GetSize(), 1) != 1) {
+            throw DeadlyExportError("Failed to write scene data!"); 
+        }
+
+        if (isBinary) {
+            WriteBinaryData(outfile.get(), docBuffer.GetSize());
+        }
+    }
+
+    void AssetWriter::WriteBinaryData(IOStream* outfile, size_t sceneLength)
+    {
+        //
+        // write the body data
+        //
+
+        size_t bodyLength = 0;
+        if (Ref<Buffer> b = mAsset.GetBodyBuffer()) {
+            bodyLength = b->byteLength;
+
+            if (bodyLength > 0) {
+                size_t bodyOffset = sizeof(GLB_Header) + sceneLength;
+                bodyOffset = (bodyOffset + 3) & ~3; // Round up to next multiple of 4
+
+                outfile->Seek(bodyOffset, aiOrigin_SET);
+
+                if (outfile->Write(b->GetPointer(), b->byteLength, 1) != 1) {
+                    throw DeadlyExportError("Failed to write body data!");
+                }
+            }
+        }
+
+
+        //
+        // write the header
+        //
+
+        GLB_Header header;
+        memcpy(header.magic, AI_GLB_MAGIC_NUMBER, sizeof(header.magic));
+
+        header.version = 1;
+        AI_SWAP4(header.version);
+
+        header.length = sizeof(header) + sceneLength + bodyLength;
+        AI_SWAP4(header.length);
+
+        header.sceneLength = sceneLength;
+        AI_SWAP4(header.sceneLength);
+
+        header.sceneFormat = SceneFormat_JSON;
+        AI_SWAP4(header.sceneFormat);
+
+        outfile->Seek(0, aiOrigin_SET);
+
+        if (outfile->Write(&header, 1, sizeof(header)) != sizeof(header)) {
+            throw DeadlyExportError("Failed to write the header!");
+        }
+    }
+
+    
+    void AssetWriter::WriteMetadata()
+    {
+        Value asset;
+        asset.SetObject();
+        {
+            asset.AddMember("version", mAsset.asset.version, mAl);
+
+            asset.AddMember("generator", Value(mAsset.asset.generator, mAl).Move(), mAl);
+        }
+        mDoc.AddMember("asset", asset, mAl);
+    }
+
+    void AssetWriter::WriteExtensionsUsed()
+    {
+        Value exts;
+        exts.SetArray();
+        {
+            if (false)
+                exts.PushBack(StringRef("KHR_binary_glTF"), mAl);
+
+            if (false)
+                exts.PushBack(StringRef("KHR_materials_common"), mAl);
+        }
+
+        if (!exts.Empty())
+            mDoc.AddMember("extensionsUsed", exts, mAl);
+    }
+
+
+}
+
+
index eb133cc..b47e0da 100644 (file)
@@ -56,20 +56,14 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 #include <boost/foreach.hpp>
 #include <boost/scoped_ptr.hpp>
+#include <boost/shared_ptr.hpp>
 
-#define RAPIDJSON_HAS_STDSTRING 1
-#include <rapidjson/document.h>
-#include <rapidjson/writer.h>
-#include <rapidjson/prettywriter.h>
-#include <rapidjson/stringbuffer.h>
-
-#include "glTFFileData.h"
-#include "glTFUtil.h"
+#include "glTFAssetWriter.h"
 
 using namespace rapidjson;
 
 using namespace Assimp;
-using namespace Assimp::glTF;
+using namespace glTF;
 
 namespace Assimp {
 
@@ -93,306 +87,278 @@ namespace Assimp {
 
 
 
-class glTFSceneExporter
+glTFExporter::glTFExporter(const char* filename, IOSystem* pIOSystem, const aiScene* pScene,
+                           const ExportProperties* pProperties, bool isBinary)
+    : mFilename(filename)
+    , mIOSystem(pIOSystem)
+    , mScene(pScene)
+    , mProperties(pProperties)
 {
-    typedef std::gltf_unordered_map<std::string, int> IdMap;
+    boost::scoped_ptr<Asset> asset(new glTF::Asset(pIOSystem));
+    mAsset = asset.get();
 
-    Document& mDoc;
-    MemoryPoolAllocator<>& mAl;
-
-    const aiScene* mScene;
-
-    std::string mRootNodeId;
-
-    std::vector<std::string> mMeshIds;
-
-    IdMap mUsedIds;
-
-public:
-    glTFSceneExporter(Document& doc, const aiScene* pScene)
-        : mDoc(doc)
-        , mAl(doc.GetAllocator())
-        , mScene(pScene)
-    {
-        doc.SetObject();
-
-        for (unsigned int i = 0; i < pScene->mNumAnimations; ++i) {
-
-        }
+    if (isBinary) {
+        asset->SetAsBinary();
+    }
 
-        for (unsigned int i = 0; i < pScene->mNumCameras; ++i) {
+    ExportMetadata();
 
-        }
+    //for (unsigned int i = 0; i < pScene->mNumAnimations; ++i) {}
 
-        for (unsigned int i = 0; i < pScene->mNumLights; ++i) {
+    //for (unsigned int i = 0; i < pScene->mNumCameras; ++i) {}
 
-        }
+    //for (unsigned int i = 0; i < pScene->mNumLights; ++i) {}
 
-        for (unsigned int i = 0; i < pScene->mNumLights; ++i) {
 
-        }
+    ExportMaterials();
 
-        for (unsigned int i = 0; i < pScene->mNumMaterials; ++i) {
+    ExportMeshes();
 
-        }
+    //for (unsigned int i = 0; i < pScene->mNumTextures; ++i) {}
 
 
-        AddMeshes();
+    if (mScene->mRootNode) {
+        ExportNode(mScene->mRootNode);
+    }
 
-        for (unsigned int i = 0; i < pScene->mNumTextures; ++i) {
+    ExportScene();
 
-        }
 
-        AddNodes();
+    glTF::AssetWriter writer(*mAsset);
+    writer.WriteFile(filename);
+}
 
-        CreateScene();
-    }
 
-    inline void Pushf(Value& val, float f)
-    {
-        val.PushBack(Value(f).Move(), mAl);
-    }
+static void CopyValue(const aiMatrix4x4& v, glTF::mat4& o)
+{ 
+    o[ 0] = v.a1; o[ 1] = v.b1; o[ 2] = v.c1; o[ 3] = v.d1;
+    o[ 4] = v.a2; o[ 5] = v.b2; o[ 6] = v.c2; o[ 7] = v.d2;
+    o[ 8] = v.a3; o[ 9] = v.b3; o[10] = v.c3; o[11] = v.d3;
+    o[12] = v.a4; o[13] = v.b4; o[14] = v.c4; o[15] = v.d4;
+}
 
-    inline void SetMatrix(Value& v, const aiMatrix4x4& m)
-    {
-        v.SetArray();
-        v.Reserve(16, mAl);
+inline Ref<Accessor> ExportData(Asset& a, std::string& meshName, Ref<Buffer>& buffer,
+    unsigned int count, void* data, AttribType::Value typeIn, AttribType::Value typeOut, ComponentType compType, bool isIndices = false)
+{
+    if (!count || !data) return Ref<Accessor>();
+
+    unsigned int numCompsIn = AttribType::GetNumComponents(typeIn);
+    unsigned int numCompsOut = AttribType::GetNumComponents(typeOut);
+    unsigned int bytesPerComp = ComponentTypeSize(compType);
+
+    size_t offset = buffer->byteLength;
+    size_t length = count * numCompsOut * bytesPerComp;
+    buffer->Grow(length);
+
+    // bufferView
+    Ref<BufferView> bv = a.bufferViews.Create(a.FindUniqueID(meshName, "view"));
+    bv->buffer = buffer;
+    bv->byteOffset = 0;
+    bv->byteLength = length; //! The target that the WebGL buffer should be bound to.
+    bv->target = isIndices ? BufferViewTarget_ELEMENT_ARRAY_BUFFER : BufferViewTarget_ARRAY_BUFFER;
+
+    // accessor
+    Ref<Accessor> acc = a.accessors.Create(a.FindUniqueID(meshName, "accessor"));
+    acc->bufferView = bv;
+    acc->byteOffset = offset;
+    acc->byteStride = 0;
+    acc->componentType = compType;
+    acc->count = count;
+    acc->type = typeOut;
+
+    // copy the data
+    acc->WriteData(count, data, numCompsIn*bytesPerComp);
+
+    return acc;
+}
 
-        Pushf(v, m.a1); Pushf(v, m.b1); Pushf(v, m.c1); Pushf(v, m.d1);
-        Pushf(v, m.a2); Pushf(v, m.b2); Pushf(v, m.c2); Pushf(v, m.d2);
-        Pushf(v, m.a3); Pushf(v, m.b3); Pushf(v, m.c3); Pushf(v, m.d3);
-        Pushf(v, m.a4); Pushf(v, m.b4); Pushf(v, m.c4); Pushf(v, m.d4);
+namespace {
+    void GetMatScalar(const aiMaterial* mat, float& val, const char* propName, int type, int idx) {
+        if (mat->Get(propName, type, idx, val) == AI_SUCCESS) {}
     }
+}
 
-    void AddMeshes()
-    {
-        if (mScene->mNumMeshes == 0) return;
-
-        Value meshes;
-        meshes.SetObject();
-
-        mMeshIds.reserve(mScene->mNumMeshes);
-        for (unsigned int i = 0; i < mScene->mNumMeshes; ++i) {
-            aiMesh* m = mScene->mMeshes[i];
-            std::string meshId = FindID(m->mName, "mesh");
-            mMeshIds.push_back(meshId);
-
-            Value mesh;
-            mesh.SetObject();
-            {
-                Value primitives;
-                primitives.SetObject();
+void glTFExporter::GetMatColorOrTex(const aiMaterial* mat, glTF::TexProperty& prop, const char* propName, int type, int idx, aiTextureType tt)
+{
+    aiString tex;
+    aiColor4D col;
+    if (mat->GetTextureCount(tt) > 0) {
+        if (mat->Get(AI_MATKEY_TEXTURE(tt, 0), tex) == AI_SUCCESS) {
+            std::string path = tex.C_Str();
+
+            if (path.size() > 0) {
+                if (path[0] != '*') {
+                    std::map<std::string, size_t>::iterator it = mTexturesByPath.find(path);
+                    if (it != mTexturesByPath.end()) {
+                        prop.texture = mAsset->textures.Get(it->second);
+                    }
+                }
 
-                
-                mesh.AddMember("primitives", primitives, mAl);
+                if (!prop.texture) {
+                    std::string texId = mAsset->FindUniqueID("", "texture");
+                    prop.texture = mAsset->textures.Create(texId);
+                    mTexturesByPath[path] = prop.texture.GetIndex();
+
+                    std::string imgId = mAsset->FindUniqueID("", "image");
+                    prop.texture->source = mAsset->images.Create(imgId);
+
+                    if (path[0] == '*') { // embedded
+                        aiTexture* tex = mScene->mTextures[atoi(&path[1])];
+
+                        uint8_t* data = reinterpret_cast<uint8_t*>(tex->pcData);
+                        prop.texture->source->SetData(data, tex->mWidth, *mAsset);
+
+                        if (tex->achFormatHint[0]) {
+                            std::string mimeType = "image/";
+                            mimeType += (memcmp(tex->achFormatHint, "jpg", 3) == 0) ? "jpeg" : tex->achFormatHint;
+                            prop.texture->source->mimeType = mimeType;
+                        }
+                    }
+                    else {
+                        prop.texture->source->uri = path;
+                    }
+                }
             }
-
-            meshes.AddMember(StringRef(mMeshIds.back()), mesh, mAl);
         }
-
-        mDoc.AddMember("meshes", meshes, mAl);
     }
 
-    void AddNodes()
-    {
-        if (!mScene->mRootNode) return;
-
-        Value nodes;
-        nodes.SetObject();
-
-        mRootNodeId = AddNode(nodes, mScene->mRootNode);
-
-        mDoc.AddMember("nodes", nodes, mAl);
+    if (mat->Get(propName, type, idx, col) == AI_SUCCESS) {
+        prop.color[0] = col.r; prop.color[1] = col.g; prop.color[2] = col.b; prop.color[3] = col.a;
     }
+}
 
-
-    std::string AddNode(Value& nodes, const aiNode* n)
-    {
-        std::string nodeId = FindID(n->mName, "node");
-
-        Value node;
-        node.SetObject();
-
-        if (!n->mTransformation.IsIdentity()) {
-            Value matrix;
-            SetMatrix(matrix, n->mTransformation);
-            node.AddMember("matrix", matrix, mAl);
-        }
-
-        if (n->mNumMeshes > 0) {
-            Value meshes;
-            meshes.SetArray();
-            for (unsigned int i = 0; i < n->mNumMeshes; ++i) {
-                meshes.PushBack(StringRef(mMeshIds[n->mMeshes[i]]), mAl);
-            }
-            node.AddMember("meshes", meshes, mAl);
+void glTFExporter::ExportMaterials()
+{
+    aiString aiName;
+    for (unsigned int i = 0; i < mScene->mNumMaterials; ++i) {
+        const aiMaterial* mat = mScene->mMaterials[i];
+
+        
+        std::string name;
+        if (mat->Get(AI_MATKEY_NAME, aiName) == AI_SUCCESS) {
+            name = aiName.C_Str();
         }
+        name = mAsset->FindUniqueID(name, "material");
 
-        if (n->mNumChildren > 0) {
-            Value children;
-            children.SetArray();
-            for (unsigned int i = 0; i < n->mNumChildren; ++i) {
-                std::string id = AddNode(nodes, n->mChildren[i]);
-                children.PushBack(Value(id, mAl).Move(), mAl);
-            }
-            node.AddMember("children", children, mAl);
-        }
+        Ref<Material> m = mAsset->materials.Create(name);
 
-        nodes.AddMember(Value(nodeId, mAl).Move(), node, mAl);
+        GetMatColorOrTex(mat, m->ambient, AI_MATKEY_COLOR_AMBIENT, aiTextureType_AMBIENT);
+        GetMatColorOrTex(mat, m->diffuse, AI_MATKEY_COLOR_DIFFUSE, aiTextureType_DIFFUSE);
+        GetMatColorOrTex(mat, m->specular, AI_MATKEY_COLOR_SPECULAR, aiTextureType_SPECULAR);
+        GetMatColorOrTex(mat, m->emission, AI_MATKEY_COLOR_EMISSIVE, aiTextureType_EMISSIVE);
 
-        return nodeId;
+        GetMatScalar(mat, m->shininess, AI_MATKEY_SHININESS);
     }
+}
 
-    void CreateScene()
-    {
-        const char* sceneName = "defaultScene";
+void glTFExporter::ExportMeshes()
+{
+    for (unsigned int i = 0; i < mScene->mNumMeshes; ++i) {
+        const aiMesh* aim = mScene->mMeshes[i];
 
-        mDoc.AddMember("scene", Value(sceneName, mAl).Move(), mAl);
+        std::string meshId = mAsset->FindUniqueID(aim->mName.C_Str(), "mesh");
+        Ref<Mesh> m = mAsset->meshes.Create(meshId);
+        m->primitives.resize(1);
+        Mesh::Primitive& p = m->primitives.back();
 
-        Value scenes;
-        scenes.SetObject();
-        {
-            Value scene;
-            scene.SetObject();
-            {
-                Value nodes;
-                nodes.SetArray();
+        p.material = mAsset->materials.Get(aim->mMaterialIndex);
 
-                if (!mRootNodeId.empty()) {
-                    nodes.PushBack(StringRef(mRootNodeId), mAl);
-                }
+        std::string bufferId = mAsset->FindUniqueID(meshId, "buffer");
 
-                scene.AddMember("nodes", nodes, mAl);
-            }
-            scenes.AddMember(Value(sceneName, mAl).Move(), scene, mAl);
+        Ref<Buffer> b = mAsset->GetBodyBuffer();
+        if (!b) {
+            b = mAsset->buffers.Create(bufferId);
         }
-        mDoc.AddMember("scenes", scenes, mAl);
-    }
 
-    std::string FindID(const aiString& str, const char* suffix)
-    {
-        std::string id = str.C_Str();
-
-        IdMap::iterator it;
+        Ref<Accessor> v = ExportData(*mAsset, meshId, b, aim->mNumVertices, aim->mVertices, AttribType::VEC3, AttribType::VEC3, ComponentType_FLOAT);
+        if (v) p.attributes.position.push_back(v);
 
-        do {
-            if (!id.empty()) {
-                it = mUsedIds.find(id);
-                if (it == mUsedIds.end()) break;
+        Ref<Accessor> n = ExportData(*mAsset, meshId, b, aim->mNumVertices, aim->mNormals, AttribType::VEC3, AttribType::VEC3, ComponentType_FLOAT);
+        if (n) p.attributes.normal.push_back(n);
 
-                id += "-";
+        for (int i = 0; i < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++i) {
+            if (aim->mNumUVComponents[i] > 0) {
+                AttribType::Value type = (aim->mNumUVComponents[i] == 2) ? AttribType::VEC2 : AttribType::VEC3;
+                Ref<Accessor> tc = ExportData(*mAsset, meshId, b, aim->mNumVertices, aim->mTextureCoords[i], AttribType::VEC3, type, ComponentType_FLOAT, true);
+                if (tc) p.attributes.texcoord.push_back(tc);
             }
+        }
 
-            id += suffix;
-
-            it = mUsedIds.find(id);
-            if (it == mUsedIds.end()) break;
-
-            char buffer[256];
-            int offset = sprintf(buffer, "%s-", id.c_str());
-            for (int i = 0; it == mUsedIds.end(); ++i) {
-                ASSIMP_itoa10(buffer + offset, sizeof(buffer), i);
-
-                id = buffer;
-                it = mUsedIds.find(id);
+        if (aim->mNumFaces > 0) {
+            unsigned int nIndicesPerFace = aim->mFaces[0].mNumIndices;
+            std::vector<uint16_t> indices;
+            indices.resize(aim->mNumFaces * nIndicesPerFace);
+            for (size_t i = 0; i < aim->mNumFaces; ++i) {
+                for (size_t j = 0; j < nIndicesPerFace; ++j) {
+                    indices[i*nIndicesPerFace + j] = uint16_t(aim->mFaces[i].mIndices[j]);
+                }
             }
-        } while (false); // fake loop to allow using "break"
+            p.indices = ExportData(*mAsset, meshId, b, indices.size(), &indices[0], AttribType::SCALAR, AttribType::SCALAR, ComponentType_UNSIGNED_SHORT);
+        }
 
-        mUsedIds[id] = true;
-        return id;
+        switch (aim->mPrimitiveTypes) {
+            case aiPrimitiveType_POLYGON:
+                p.mode = PrimitiveMode_TRIANGLES; break; // TODO implement this
+            case aiPrimitiveType_LINE:
+                p.mode = PrimitiveMode_LINES; break;
+            case aiPrimitiveType_POINT:
+                p.mode = PrimitiveMode_POINTS; break;
+            default: // aiPrimitiveType_TRIANGLE
+                p.mode = PrimitiveMode_TRIANGLES;
+        }
     }
-};
-
-
+}
 
-glTFExporter::glTFExporter(const char* filename, IOSystem* pIOSystem, const aiScene* pScene,
-                           const ExportProperties* pProperties, bool isBinary)
-    : mFilename(filename)
-    , mIOSystem(pIOSystem)
-    , mScene(pScene)
-    , mProperties(pProperties)
-    , mIsBinary(isBinary)
+size_t glTFExporter::ExportNode(const aiNode* n)
 {
-    boost::scoped_ptr<IOStream> outfile(pIOSystem->Open(mFilename, "wt"));
-    if (outfile == 0) {
-        throw DeadlyExportError("Could not open output file: " + std::string(mFilename));
-    }
+    Ref<Node> node = mAsset->nodes.Create(mAsset->FindUniqueID(n->mName.C_Str(), "node"));
 
-    if (isBinary) {
-        // we will write the header later, skip its size
-        outfile->Seek(sizeof(GLB_Header), aiOrigin_SET);
+    if (!n->mTransformation.IsIdentity()) {
+        node->matrix.isPresent = true;
+        CopyValue(n->mTransformation, node->matrix.value);
     }
 
-
-    Document doc;
-    StringBuffer docBuffer;
-    {
-        glTFSceneExporter exportScene(doc, mScene);
-
-        bool pretty = true; 
-        if (!isBinary && pretty) {
-            PrettyWriter<StringBuffer> writer(docBuffer);
-            doc.Accept(writer);
-        }
-        else {
-            Writer<StringBuffer> writer(docBuffer);
-            doc.Accept(writer);
-        }
+    for (unsigned int i = 0; i < n->mNumMeshes; ++i) {
+        node->meshes.push_back(mAsset->meshes.Get(n->mMeshes[i]));
     }
 
-    if (outfile->Write(docBuffer.GetString(), docBuffer.GetSize(), 1) != 1) {
-        throw DeadlyExportError("Failed to write scene data!"); 
+    for (unsigned int i = 0; i < n->mNumChildren; ++i) {
+        size_t idx = ExportNode(n->mChildren[i]);
+        node->children.push_back(mAsset->nodes.Get(idx));
     }
 
-    if (isBinary) {
-        WriteBinaryData(outfile.get(), docBuffer.GetSize());
-    }
+    return node.GetIndex();
 }
 
 
-void glTFExporter::WriteBinaryData(IOStream* outfile, std::size_t sceneLength)
+void glTFExporter::ExportScene()
 {
-    //
-    // write the body data
-    //
-
-    if (!mBodyData.empty()) {
-        std::size_t bodyOffset = sizeof(GLB_Header) + sceneLength;
-        bodyOffset = (bodyOffset + 3) & ~3; // Round up to next multiple of 4
+    const char* sceneName = "defaultScene";
+    Ref<Scene> scene = mAsset->scenes.Create(sceneName);
 
-        outfile->Seek(bodyOffset, aiOrigin_SET);
-
-        if (outfile->Write(&mBodyData[0], mBodyData.size(), 1) != 1) {
-            throw DeadlyExportError("Failed to write body data!");
-        }
+    // root node will be the first one exported (idx 0)
+    if (mAsset->nodes.Size() > 0) {
+        scene->nodes.push_back(mAsset->nodes.Get(0u));
     }
 
+    // set as the default scene
+    mAsset->scene = scene;
+}
 
-    //
-    // write the header
-    //
-
-    outfile->Seek(0, aiOrigin_SET);
-
-    GLB_Header header;
-    memcpy(header.magic, AI_GLB_MAGIC_NUMBER, sizeof(header.magic));
+void glTFExporter::ExportMetadata()
+{
+    glTF::AssetMetadata& asset = mAsset->asset;
+    asset.version = 1;
 
-    header.version = 1;
-    AI_SWAP4(header.version);
+    char buffer[256];
+    sprintf(buffer, "Open Asset Import Library (assimp v%d.%d.%d)",
+        aiGetVersionMajor(), aiGetVersionMinor(), aiGetVersionRevision());
 
-    header.length = sizeof(header) + sceneLength + mBodyData.size();
-    AI_SWAP4(header.length);
+    asset.generator = buffer;
+}
 
-    header.sceneLength = sceneLength;
-    AI_SWAP4(header.sceneLength);
 
-    header.sceneFormat = SceneFormat_JSON;
-    AI_SWAP4(header.sceneFormat);
 
-    if (outfile->Write(&header, sizeof(header), 1) != 1) {
-        throw DeadlyExportError("Failed to write the header!");
-    }
-}
 
 
 
index 39dbf04..4b916cc 100644 (file)
@@ -45,11 +45,24 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #define AI_GLTFEXPORTER_H_INC
 
 #include <assimp/types.h>
+#include <assimp/material.h>
 #include <sstream>
 #include <vector>
 #include <map>
 
+#include "boost/scoped_ptr.hpp"
+
+
 struct aiScene;
+struct aiNode;
+struct aiMaterial;
+
+namespace glTF
+{
+    class Asset;
+
+    struct TexProperty;
+}
 
 namespace Assimp
 {
@@ -57,7 +70,6 @@ namespace Assimp
     class IOStream;
     class ExportProperties;
 
-
     // ------------------------------------------------------------------------------------------------
     /** Helper class to export a given scene to an glTF file. */
     // ------------------------------------------------------------------------------------------------
@@ -74,12 +86,21 @@ namespace Assimp
         IOSystem* mIOSystem;
         const aiScene* mScene;
         const ExportProperties* mProperties;
-        bool mIsBinary;
+
+        std::map<std::string, size_t> mTexturesByPath;
+
+        glTF::Asset* mAsset;
 
         std::vector<unsigned char> mBodyData;
 
         void WriteBinaryData(IOStream* outfile, std::size_t sceneLength);
 
+        void GetMatColorOrTex(const aiMaterial* mat, glTF::TexProperty& prop, const char* propName, int type, int idx, aiTextureType tt);
+        void ExportMetadata();
+        void ExportMaterials();
+        void ExportMeshes();
+        size_t ExportNode(const aiNode* node);
+        void ExportScene();
     };
 
 }
diff --git a/code/glTFFileData.h b/code/glTFFileData.h
deleted file mode 100644 (file)
index 2a124d3..0000000
+++ /dev/null
@@ -1,122 +0,0 @@
-/*
-Open Asset Import Library (assimp)
-----------------------------------------------------------------------
-
-Copyright (c) 2006-2015, assimp team
-All rights reserved.
-
-Redistribution and use of this software in source and binary forms,
-with or without modification, are permitted provided that the
-following conditions are met:
-
-* Redistributions of source code must retain the above
-copyright notice, this list of conditions and the
-following disclaimer.
-
-* Redistributions in binary form must reproduce the above
-copyright notice, this list of conditions and the
-following disclaimer in the documentation and/or other
-materials provided with the distribution.
-
-* Neither the name of the assimp team, nor the names of its
-contributors may be used to endorse or promote products
-derived from this software without specific prior
-written permission of the assimp team.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-----------------------------------------------------------------------
-*/
-#ifndef AI_GLTFFILEDATA_H_INC
-#define AI_GLTFFILEDATA_H_INC
-
-#include <stdint.h>
-
-namespace Assimp {
-namespace glTF {
-
-
-//! Magic number for GLB files
-#define AI_GLB_MAGIC_NUMBER "glTF"
-
-
-#include "./../include/assimp/Compiler/pushpack1.h"
-
-// KHR_binary_glTF (binary .glb file)
-// 20-byte header (+ the JSON + a "body" data section)
-struct GLB_Header
-{
-    //! Magic number: "glTF"
-    unsigned char magic[4];  // "glTF"
-
-    //! Version number (always 1 as of the last update)
-    uint32_t version;
-
-    //! Total length of the Binary glTF, including header, scene, and body, in bytes
-    uint32_t length;
-
-    //! Length, in bytes, of the glTF scene
-    uint32_t sceneLength;
-
-    //! Specifies the format of the glTF scene (see the SceneFormat enum)
-    uint32_t sceneFormat;
-} PACK_STRUCT;
-
-#include "./../include/assimp/Compiler/poppack1.h"
-
-
-
-//! Values for the GLB_Header::sceneFormat field
-enum SceneFormat
-{
-    SceneFormat_JSON = 0
-};
-
-
-//! Values for the mesh primitive modes
-enum PrimitiveMode
-{
-    PrimitiveMode_POINTS         = 0,
-    PrimitiveMode_LINES          = 1,
-    PrimitiveMode_LINE_LOOP      = 2,
-    PrimitiveMode_LINE_STRIP     = 3,
-    PrimitiveMode_TRIANGLES      = 4,
-    PrimitiveMode_TRIANGLE_STRIP = 5,
-    PrimitiveMode_TRIANGLE_FAN   = 6
-};
-
-
-//! Values for the accessors component type field
-enum ComponentType
-{
-    ComponentType_BYTE           = 5120,
-    ComponentType_UNSIGNED_BYTE  = 5121,
-    ComponentType_SHORT          = 5122,
-    ComponentType_UNSIGNED_SHORT = 5123,
-    ComponentType_FLOAT          = 5126
-};
-
-
-//! Will hold the enabled extensions
-struct Extensions
-{
-    bool KHR_binary_glTF;
-};
-
-
-} // end namespaces
-}
-
-
-#endif // AI_GLTFFILEDATA_H_INC
-
index 2df1ea4..d622718 100644 (file)
@@ -41,621 +41,184 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #ifndef ASSIMP_BUILD_NO_GLTF_IMPORTER
 
 #include "glTFImporter.h"
-#include "StreamReader.h"
-#include "DefaultIOSystem.h"
 
-#include <boost/scoped_ptr.hpp>
+#include "StringComparison.h"
+
+#include "boost/scoped_ptr.hpp"
 
 #include <assimp/Importer.hpp>
 #include <assimp/scene.h>
 #include <assimp/ai_assert.h>
 #include <assimp/DefaultLogger.hpp>
+#include <boost/shared_ptr.hpp>
 
-#include "glTFFileData.h"
-#include "glTFUtil.h"
-
-#define RAPIDJSON_HAS_STDSTRING 1
-#include <rapidjson/rapidjson.h>
-#include <rapidjson/document.h>
-#include <rapidjson/error/en.h>
-
-using namespace rapidjson;
+#include "glTFAsset.h"
 
 using namespace Assimp;
-using namespace Assimp::glTF;
-
-
-using boost::shared_ptr;
-using boost::scoped_ptr;
-
-// (cannot typedef' templated classes, and "using" is c++11)
-#define Ptr shared_ptr
-
-// (used everywhere, and cannot use "auto")
-typedef rapidjson::Value::MemberIterator MemIt;
+using namespace glTF;
 
 
 //
-// JSON Value reading helpers
+// glTFImporter
 //
 
+static const aiImporterDesc desc = {
+    "glTF Importer",
+    "",
+    "",
+    "",
+    aiImporterFlags_SupportTextFlavour | aiImporterFlags_SupportBinaryFlavour | aiImporterFlags_SupportCompressedFlavour
+        | aiImporterFlags_LimitedSupport | aiImporterFlags_Experimental,
+    0,
+    0,
+    0,
+    0,
+    "gltf glb"
+};
 
-#define GETF(VAL, OUT) { if ((VAL).IsNumber()) (OUT) = static_cast<float>((VAL).GetDouble()); }
-
-template<class T>
-struct ReadHelper { };
-
-template<> struct ReadHelper<int> { static bool Read(Value& val, int& out) {
-    return val.IsInt() ? val.GetInt(), true : false;
-}};
-
-template<> struct ReadHelper<unsigned int> { static bool Read(Value& val, unsigned int& out) {
-    return val.IsInt() ? out = static_cast<unsigned int>(val.GetInt()), true : false;
-}};
-
-template<> struct ReadHelper<float> { static bool Read(Value& val, float& out) {
-    return val.IsNumber() ? out = static_cast<float>(val.GetDouble()), true : false;
-}};
-
-template<> struct ReadHelper<const char*> { static bool Read(Value& val, const char*& out) {
-    return val.IsString() ? out = val.GetString(), true : false;
-}};
-
-template<> struct ReadHelper<std::string> { static bool Read(Value& val, std::string& out) {
-    return val.IsString() ? out = val.GetString(), true : false;
-}};
-
-template<> struct ReadHelper<aiColor3D> { static bool Read(Value& v, aiColor3D& out) {
-    if (!v.IsArray() || v.Size() < 3) return false;
-    GETF(v[0], out.r); GETF(v[1], out.g); GETF(v[2], out.b);
-    return true;
-}};
-
-template<> struct ReadHelper<aiVector3D> { static bool Read(Value& v, aiVector3D& out) {
-    if (!v.IsArray() || v.Size() != 3) return false;
-    GETF(v[0], out.x); GETF(v[1], out.y); GETF(v[2], out.z);
-    return true;
-}};
-
-template<> struct ReadHelper<aiQuaternion> { static bool Read(Value& v, aiQuaternion& out) {
-    if (!v.IsArray() || v.Size() != 4) return false;
-    GETF(v[0], out.x); GETF(v[1], out.y); GETF(v[2], out.z); GETF(v[3], out.w);
-    return true;
-}};
-
-template<> struct ReadHelper<aiMatrix4x4> { static bool Read(Value& v, aiMatrix4x4& o) {
-    if (!v.IsArray() || v.Size() != 16) return false;
-    GETF(v[ 0], o.a1); GETF(v[ 1], o.b1); GETF(v[ 2], o.c1); GETF(v[ 3], o.d1);
-    GETF(v[ 4], o.a2); GETF(v[ 5], o.b2); GETF(v[ 6], o.c2); GETF(v[ 7], o.d2);
-    GETF(v[ 8], o.a3); GETF(v[ 9], o.b3); GETF(v[10], o.c3); GETF(v[11], o.d3);
-    GETF(v[12], o.a4); GETF(v[13], o.b4); GETF(v[14], o.c4); GETF(v[15], o.d4);
-    return true;
-}};
-
-template<class T>
-inline static bool Read(Value& val, T& out)
+glTFImporter::glTFImporter() 
+: BaseImporter()
 {
-    return ReadHelper<T>::Read(val, out);
+
 }
 
-template<class T>
-inline static bool ReadMember(Value& obj, const char* id, T& out)
+glTFImporter::~glTFImporter()
 {
-    MemIt it = obj.FindMember(id);
-    if (it != obj.MemberEnd()) {
-        return ReadHelper<T>::Read(it->value, out);
-    }
-    return false;
+
 }
 
-template<class T>
-inline static T TryReadMember(Value& obj, const char* id, T defaultValue)
+const aiImporterDesc* glTFImporter::GetInfo() const
 {
-    T out;
-    return ReadMember(obj, id, out) ? out : defaultValue;
+    return &desc;
 }
 
-
-//! References a sequence of loaded elements (e.g. meshes)
-typedef std::pair<unsigned int, unsigned int> Range;
-
-
-
-//
-// glTFReader class
-//
-
-//! Manages lazy loading of the glTF top-level objects, and keeps a reference to them by ID
-template< class T, class INST, T(INST::*FACTORY_FN)(const char*, Value&)>
-class LazyDict
+bool glTFImporter::CanRead(const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const
 {
-    typedef typename std::gltf_unordered_map<std::string, T> Map;
-
-    Value*      mDict;     //! JSON dictionary object
-    const char* mDictId;   //! ID of the dictionary object
-    INST&       mInstance; //! The reader object instance
-    Map         mReadObjs; //! The read objects
-
-public:
-    LazyDict(INST& instance, const char* dictId)
-        : mDictId(dictId), mInstance(instance)
-    {
-        Document& doc = mInstance.GetDocument();
-
-        MemIt it = doc.FindMember(dictId);
-        mDict = (it != doc.MemberEnd() && it->value.IsObject()) ? &it->value : 0;
-    }
-
-    T Get(const char* id)
-    {
-        if (!mDict) return T(); // section was missing
-
-        typename Map::iterator it = mReadObjs.find(id);
-        if (it != mReadObjs.end()) { // already created?
-            return it->second;
-        }
-
-        // read it from the JSON object
-        MemIt obj = mDict->FindMember(id);
-        if (obj == mDict->MemberEnd()) {
-            throw DeadlyImportError("Missing object with id \"" + std::string(id) + "\" in \"" + mDictId + "\"");
-        }
-
-        // create an instance of the given type
-        T val = (mInstance.*FACTORY_FN)(id, obj->value);
-        mReadObjs[id] = val;
-        return val;
-    }
-};
+    const std::string& extension = GetExtension(pFile);
 
-struct Buffer;
-struct BufferView;
-struct Accessor;
+    if (extension == "gltf" || extension == "glb")
+        return true;
 
-struct Image;
-struct Texture;
+    if ((checkSig || !extension.length()) && pIOHandler) {
+        char buffer[4];
 
-//! Handles the reading of the glTF JSON document
-class glTFReader
-{
-    aiScene* mScene;
-    Document& mDoc;
-    IOSystem& mIO;
-
-    // Vectors of imported objects, will be copied to mScene
-    std::vector<aiMaterial*> mImpMaterials;
-    std::vector<aiMesh*>     mImpMeshes;
-    std::vector<aiTexture*>  mImpTextures;
-
-    Extensions mExtensions;
-
-    Ptr<Buffer> mBodyBuffer; //! Special buffer containing the body data
-
-
-    Ptr<Buffer>     LoadBuffer(const char* id, Value& obj);
-    Ptr<BufferView> LoadBufferView(const char* id, Value& obj);
-    Ptr<Accessor>   LoadAccessor(const char* id, Value& obj);
-
-    Ptr<Image>      LoadImage(const char* id, Value& obj);
-    Ptr<Texture>    LoadTexture(const char* id, Value& obj);
-
-    aiNode*         LoadNode(const char* id, Value& node);
-    Range           LoadMesh(const char* id, Value& mesh);
-    unsigned int    LoadMaterial(const char* id, Value& material);
-
-    typedef glTFReader T; // (to shorten next declarations)
-
-    LazyDict<Ptr<Accessor>,   T, &T::LoadAccessor>   mAccessors;
-    //LazyDict<Animation*,    T, &T::LoadAnimation>  mAnimations;
-    //LazyDict<Asset*,        T, &T::LoadAsset>      mAssets;
-    LazyDict<Ptr<Buffer>,     T, &T::LoadBuffer>     mBuffers;
-    LazyDict<Ptr<BufferView>, T, &T::LoadBufferView> mBufferViews;
-    //LazyDict<Camera*,       T, &T::LoadCamera>     mCameras;
-    LazyDict<Ptr<Image>,      T, &T::LoadImage>      mImages;
-    LazyDict<unsigned int,    T, &T::LoadMaterial>   mMaterials;
-    LazyDict<Range,           T, &T::LoadMesh>       mMeshes;
-    LazyDict<aiNode*,         T, &T::LoadNode>       mNodes;
-    //LazyDict<Ptr<Program>,  T, &T::LoadProgram>    mPrograms;
-    //LazyDict<Ptr<Sampler>,  T, &T::LoadSampler>    mSamplers;
-    //LazyDict<Ptr<Shader>,   T, &T::LoadShader>     mShaders;
-    //LazyDict<Ptr<Skin>,     T, &T::LoadSkin>       mSkins;
-    //LazyDict<Ptr<Technique>,T, &T::LoadTechnique>  mTechniques;
-    LazyDict<Ptr<Texture>,    T, &T::LoadTexture>    mTextures;
-
-
-    void LoadScene(Value& scene)
-    {
-        MemIt nodesm = scene.FindMember("nodes");
-        if (nodesm != scene.MemberEnd() && nodesm->value.IsArray()) {
-            Value& nodes = nodesm->value;
-
-            unsigned int numRootNodes = nodes.Size();
-            if (numRootNodes == 1) { // a single root node: use it
-                if (nodes[0].IsString()) {
-                    mScene->mRootNode = mNodes.Get(nodes[0].GetString());
-                }
+        boost::scoped_ptr<IOStream> pStream(pIOHandler->Open(pFile));
+        if (pStream && pStream->Read(buffer, sizeof(buffer), 1) == 1) {
+            if (memcmp(buffer, AI_GLB_MAGIC_NUMBER, sizeof(buffer)) == 0) {
+                return true; // Has GLB header
             }
-            else if (numRootNodes > 1) { // more than one root node: create a fake root
-                aiNode* root = new aiNode("ROOT");
-                root->mChildren = new aiNode*[numRootNodes];
-                for (unsigned int i = 0; i < numRootNodes; ++i) {
-                    if (nodes[i].IsString()) {
-                        aiNode* node = mNodes.Get(nodes[i].GetString());
-                        if (node) {
-                            node->mParent = root;
-                            root->mChildren[root->mNumChildren++] = node;
-                        }
-                    }
-                }
-                mScene->mRootNode = root;
+            else if (memcmp(buffer, "{\r\n ", sizeof(buffer)) == 0
+                    || memcmp(buffer, "{\n  ", sizeof(buffer)) == 0) {
+                // seems a JSON file, and we're the only format that can read them
+                return true;
             }
         }
-
-        //if (!mScene->mRootNode) {
-            //mScene->mRootNode = new aiNode("EMPTY");
-        //}
     }
 
-    void SetMaterialColorProperty(aiMaterial* mat, Value& vals, const char* propName, aiTextureType texType,
-        const char* pKey, unsigned int type, unsigned int idx);
-
-    void CopyData()
-    {
-        // TODO: it does not split the loaded vertices, should it?
-        mScene->mFlags |= AI_SCENE_FLAGS_NON_VERBOSE_FORMAT;
-
-        if (mImpMaterials.empty()) {
-            mImpMaterials.push_back(new aiMaterial());
-        }
-
-        if (mImpMaterials.size()) {
-            mScene->mNumMaterials = mImpMaterials.size();
-            mScene->mMaterials = new aiMaterial*[mImpMaterials.size()];
-            std::swap_ranges(mImpMaterials.begin(), mImpMaterials.end(), mScene->mMaterials);
-        }
-
-        if (mImpMeshes.size()) {
-            mScene->mNumMeshes = mImpMeshes.size();
-            mScene->mMeshes = new aiMesh*[mImpMeshes.size()];
-            std::swap_ranges(mImpMeshes.begin(), mImpMeshes.end(), mScene->mMeshes);
-        }
-
-        if (mImpTextures.size()) {
-            mScene->mNumTextures = mImpTextures.size();
-            mScene->mTextures = new aiTexture*[mImpTextures.size()];
-            std::swap_ranges(mImpTextures.begin(), mImpTextures.end(), mScene->mTextures);
-        }
-    }
-
-public:
-    glTFReader(aiScene* scene, Document& document, IOSystem& iohandler, shared_ptr<Buffer>& bodyBuff) :
-        mScene(scene),
-        mDoc(document),
-        mIO(iohandler),
-        mBodyBuffer(bodyBuff),
-        mAccessors(*this, "accessors"),
-        //mAnimations(*this, "animations"),
-        //mAssets(*this, "assets"),
-        mBuffers(*this, "buffers"),
-        mBufferViews(*this, "bufferViews"),
-        //mCameras(*this, "cameras"),
-        mImages(*this, "images"),
-        mMaterials(*this, "materials"),
-        mMeshes(*this, "meshes"),
-        mNodes(*this, "nodes"),
-        //mPrograms(*this, "programs"),
-        //mSamplers(*this, "samplers"),
-        //mShaders(*this, "shaders"),
-        //mSkins(*this, "skins"),
-        //mTechniques(*this, "techniques"),
-        mTextures(*this, "textures")
-    {
-        memset(&mExtensions, 0, sizeof(mExtensions));
-    }
-
-    Document& GetDocument()
-    {
-        return mDoc;
-    }
-
-    //! Main function
-    void Load()
-    {
-        // read the used extensions
-        MemIt extensionsUsed = mDoc.FindMember("extensionsUsed");
-        if (extensionsUsed != mDoc.MemberEnd() && extensionsUsed->value.IsArray()) {
-            std::gltf_unordered_map<std::string, bool> exts;
-
-            for (unsigned int i = 0; i < extensionsUsed->value.Size(); ++i) {
-                if (extensionsUsed->value[i].IsString()) {
-                    exts[extensionsUsed->value[i].GetString()] = true;
-                }
-            }
-
-            if (exts.find("KHR_binary_glTF") != exts.end()) {
-                mExtensions.KHR_binary_glTF = true;
-            }
-        }
-
-
-        const char* sceneId = 0;
-
-        // the "scene" property specifies which scene to load
-        {
-            MemIt scene = mDoc.FindMember("scene");
-            if (scene != mDoc.MemberEnd() && scene->value.IsString()) {
-                sceneId = scene->value.GetString();
-            }
-        }
-
-        MemIt scene;
-
-        MemIt scenes = mDoc.FindMember("scenes");
-        if (scenes != mDoc.MemberEnd() && scenes->value.IsObject()) {
-            if (sceneId) {
-                scene = scenes->value.FindMember(sceneId);
-                if (scene == scenes->value.MemberEnd()) {
-                    //ThrowException("Missing scene!");
-                }
-            }
-            else { // if not specified, use the first one
-                scene = scenes->value.MemberBegin();
-            }
-        }
+    return false;
+}
 
-        if (scene != scenes->value.MemberEnd()) {
-            LoadScene(scene->value);
-        }
 
-        CopyData();
-    }
-};
 
-struct Buffer
+static void CopyValue(const glTF::vec3& v, aiColor3D& out)
 {
-private:
-    std::size_t byteLength;
-    shared_ptr<uint8_t> data;
-
-public:
-    Buffer(shared_ptr<uint8_t>& d, std::size_t length)
-        : data(d), byteLength(length)
-    { }
-
-    std::size_t GetLength() const
-    {
-        return byteLength;
-    }
-
-    uint8_t* GetPointer()
-    {
-        return data.get();
-    }
-
-    static Buffer* FromStream(IOStream& stream, std::size_t length = 0, std::size_t baseOffset = 0)
-    {
-        if (!length) {
-            length = stream.FileSize();
-        }
-
-        if (baseOffset) {
-            stream.Seek(baseOffset, aiOrigin_SET);
-        }
-
-        shared_ptr<uint8_t> data(new uint8_t[length]);
-
-
-        if (stream.Read(data.get(), length, 1) != 1) {
-            throw DeadlyImportError("Unable to load buffer from file!");
-        }
-
-        return new Buffer(data, length);
-    }
-};
-
+    out.r = v[0]; out.g = v[1]; out.b = v[2];
+}
 
-Ptr<Buffer> glTFReader::LoadBuffer(const char* id, Value& obj)
+static void CopyValue(const glTF::vec4& v, aiColor4D& out)
 {
-    if (!obj.IsObject()) return Ptr<Buffer>();
-
-    if (mExtensions.KHR_binary_glTF && strcmp(id, "KHR_binary_glTF") == 0) {
-        return mBodyBuffer;
-    }
-    else {
-        const char* uri = TryReadMember<const char*>(obj, "uri", 0);
-
-        Buffer* b = 0;
-        if (IsDataURI(uri)) {
-            const char* comma = strchr(uri, ',');
-            *const_cast<char*>(comma) = '\0';
-            
-            bool isBase64 = (strstr(uri, "base64") != 0);
-            if (isBase64) {
-                uint8_t* data;
-                std::size_t dataLen = DecodeBase64(comma + 1, data);
-                shared_ptr<uint8_t> dataptr(data);
-                b = new Buffer(dataptr, dataLen);
-            }
-        }
-        else if (uri) { // Local file
-            unsigned int byteLength = TryReadMember(obj, "byteLength", 0u);
-
-            scoped_ptr<IOStream> file(mIO.Open(uri));
-            b = Buffer::FromStream(*file.get(), byteLength);
-        }
-        return Ptr<Buffer>(b);
-    }
+    out.r = v[0]; out.g = v[1]; out.b = v[2]; out.a = v[3];
 }
 
-
-struct BufferView
+static void CopyValue(const glTF::vec4& v, aiColor3D& out)
 {
-    Ptr<Buffer> buffer;
-    unsigned int byteOffset;
-    unsigned int byteLength;
-
-    BufferView() {}
-
-    BufferView(Value& obj)
-    {
-        Read(obj);
-    }
-
-    void Read(Value& obj)
-    {
-        if (!obj.IsObject()) return;
-
-    }
-
-};
+    out.r = v[0]; out.g = v[1]; out.b = v[2];
+}
 
-Ptr<BufferView> glTFReader::LoadBufferView(const char* id, Value& obj)
+static void CopyValue(const glTF::vec3& v, aiVector3D& out)
 {
-    if (!obj.IsObject()) return Ptr<BufferView>();
-
-
-    const char* bufferId = TryReadMember<const char*>(obj, "buffer", 0);
-    if (!bufferId) return Ptr<BufferView>();
-
-    BufferView* bv = new BufferView();
-
-    bv->buffer = mBuffers.Get(bufferId);
-    bv->byteOffset = TryReadMember(obj, "byteOffset", 0u);
-    bv->byteLength = TryReadMember(obj, "byteLength", 0u);
-
-    return Ptr<BufferView>(bv);
+    out.x = v[0]; out.y = v[1]; out.z = v[2];
 }
 
-
-struct Accessor
+static void CopyValue(const glTF::vec4& v, aiQuaternion& out)
 {
-    Ptr<BufferView> bufferView;
-    unsigned int byteOffset;
-    unsigned int byteStride;
-    ComponentType componentType;
-    unsigned int count;
-    std::string type; // "SCALAR", "VEC2", "VEC3", "VEC4", "MAT2", "MAT3", "MAT4"
-    //unsigned int max;
-    ///unsigned int min;
-
-    unsigned int numComponents;
-    unsigned int bytesPerComponent;
-    unsigned int elemSize;
-
-    uint8_t* data;
-
-    inline uint8_t* GetPointer()
-    {
-        if (!bufferView || !bufferView->buffer) return 0;
-
-        std::size_t offset = byteOffset + bufferView->byteOffset;
-        return bufferView->buffer->GetPointer() + offset;
-    }
-
-    template<class T>
-    void ExtractData(T*& outData, unsigned int* outCount = 0, unsigned int* outComponents = 0)
-    {
-        ai_assert(data);
-
-        const std::size_t totalSize = elemSize * count;
-
-        const std::size_t targetElemSize = sizeof(T);
-        ai_assert(elemSize <= targetElemSize);
+    out.x = v[0]; out.y = v[1]; out.z = v[2]; out.w = v[3];
+}
 
-        ai_assert(count*byteStride <= bufferView->byteLength);
+static void CopyValue(const glTF::mat4& v, aiMatrix4x4& o)
+{
+    o.a1 = v[ 0]; o.b1 = v[ 1]; o.c1 = v[ 2]; o.d1 = v[ 3];
+    o.a2 = v[ 4]; o.b2 = v[ 5]; o.c2 = v[ 6]; o.d2 = v[ 7];
+    o.a3 = v[ 8]; o.b3 = v[ 9]; o.c3 = v[10]; o.d3 = v[11];
+    o.a4 = v[12]; o.b4 = v[13]; o.c4 = v[14]; o.d4 = v[15];
+}
 
-        outData = new T[count];
-        if (byteStride == elemSize && targetElemSize == elemSize) {
-            memcpy(outData, data, totalSize);
-        }
-        else {
-            for (std::size_t i = 0; i < count; ++i) {
-                memcpy(outData + i, data + i*byteStride, elemSize);
+inline void SetMaterialColorProperty(std::vector<int>& embeddedTexIdxs, Asset& r, glTF::TexProperty prop, aiMaterial* mat,
+    aiTextureType texType, const char* pKey, unsigned int type, unsigned int idx)
+{
+    if (prop.texture) {
+        if (prop.texture->source) {
+            aiString uri(prop.texture->source->uri);
+
+            int texIdx = embeddedTexIdxs[prop.texture->source.GetIndex()];
+            if (texIdx != -1) { // embedded
+                // setup texture reference string (copied from ColladaLoader::FindFilenameForEffectTexture)
+                uri.data[0] = '*';
+                uri.length = 1 + ASSIMP_itoa10(uri.data + 1, MAXLEN - 1, texIdx);
             }
-        }
-
-        if (outCount) *outCount = count;
-        if (outComponents) *outComponents = numComponents;
-    }
 
-    //! Gets the i-th value as defined by the accessor
-    template<class T>
-    T GetValue(int i)
-    {
-        ai_assert(data);
-        ai_assert(i*byteStride < bufferView->byteLength);
-        T value = T();
-        memcpy(&value, data + i*byteStride, elemSize);
-        //value >>= 8 * (sizeof(T) - elemSize);
-        return value;
+            mat->AddProperty(&uri, _AI_MATKEY_TEXTURE_BASE, texType, 0);
+        }
     }
-
-    //! Gets the i-th value as defined by the accessor
-    unsigned int GetUInt(int i)
-    {
-        return GetValue<unsigned int>(i);
+    else {
+        aiColor4D col;
+        CopyValue(prop.color, col);
+        if (col.r != 1.f || col.g != 1.f || col.b != 1.f || col.a != 1.f) {
+            mat->AddProperty(&col, 1, pKey, type, idx);
+        }
     }
-};
+}
 
-Ptr<Accessor> glTFReader::LoadAccessor(const char* id, Value& obj)
+void glTFImporter::ImportMaterials(glTF::Asset& r)
 {
-    if (!obj.IsObject()) return Ptr<Accessor>();
+    mScene->mNumMaterials = r.materials.Size();
+    mScene->mMaterials = new aiMaterial*[mScene->mNumMaterials];
 
-    Accessor* a = new Accessor();
+    for (unsigned int i = 0; i < mScene->mNumMaterials; ++i) {
+        aiMaterial* aimat = mScene->mMaterials[i] = new aiMaterial();
 
-    const char* bufferViewId = TryReadMember<const char*>(obj, "bufferView", 0);
-    if (bufferViewId) {
-        a->bufferView = mBufferViews.Get(bufferViewId);
-    }
+        Material& mat = r.materials[i];
 
-    int compType = TryReadMember(obj, "componentType", unsigned(ComponentType_BYTE));
-
-    a->byteOffset = TryReadMember(obj, "byteOffset", 0u);
-    a->byteStride = TryReadMember(obj, "byteStride", 0u);
-    a->componentType = static_cast<ComponentType>(compType);
-    a->count = TryReadMember(obj, "count", 0u);
-    a->type = TryReadMember(obj, "type", "");
-
-    a->numComponents = 1; // "SCALAR"
-    if (a->type == "VEC2") a->numComponents = 2;
-    else if (a->type == "VEC3") a->numComponents = 3;
-    else if (a->type == "VEC4") a->numComponents = 4;
-    else if (a->type == "MAT2") a->numComponents = 4;
-    else if (a->type == "MAT3") a->numComponents = 9;
-    else if (a->type == "MAT4") a->numComponents = 16;
-
-    switch (a->componentType) {
-        case ComponentType_SHORT:
-        case ComponentType_UNSIGNED_SHORT:
-            a->bytesPerComponent = 2;
-            break;
-
-        case ComponentType_FLOAT:
-            a->bytesPerComponent = 4;
-            break;
-
-        //case Accessor::ComponentType_BYTE:
-        //case Accessor::ComponentType_UNSIGNED_BYTE:
-        default:
-            a->bytesPerComponent = 1;
-    }
+        /*if (!mat.name.empty())*/ {
+            aiString str(mat.id /*mat.name*/);
+            aimat->AddProperty(&str, AI_MATKEY_NAME);
+        }
 
-    a->elemSize = a->numComponents * a->bytesPerComponent;
-    if (!a->byteStride) a->byteStride = a->elemSize;
+        SetMaterialColorProperty(embeddedTexIdxs, r, mat.diffuse, aimat, aiTextureType_DIFFUSE, AI_MATKEY_COLOR_DIFFUSE);
+        SetMaterialColorProperty(embeddedTexIdxs, r, mat.specular, aimat, aiTextureType_SPECULAR, AI_MATKEY_COLOR_SPECULAR);
+        SetMaterialColorProperty(embeddedTexIdxs, r, mat.ambient, aimat, aiTextureType_AMBIENT, AI_MATKEY_COLOR_AMBIENT);
 
-    a->data = a->GetPointer();
+        if (mat.shininess > 0.f) {
+            aimat->AddProperty(&mat.shininess, 1, AI_MATKEY_SHININESS);
+        }
+    }
 
-    return Ptr<Accessor>(a);
+    if (mScene->mNumMaterials == 0) {
+        mScene->mNumMaterials = 1;
+        mScene->mMaterials = new aiMaterial*[1];
+        mScene->mMaterials[0] = new aiMaterial();
+    }
 }
 
-static inline void setFace(aiFace& face, int a)
+
+inline void SetFace(aiFace& face, int a)
 {
     face.mNumIndices = 1;
     face.mIndices = new unsigned int[1];
     face.mIndices[0] = a;
 }
 
-static inline void setFace(aiFace& face, int a, int b)
+inline void SetFace(aiFace& face, int a, int b)
 {
     face.mNumIndices = 2;
     face.mIndices = new unsigned int[2];
@@ -663,7 +226,7 @@ static inline void setFace(aiFace& face, int a, int b)
     face.mIndices[1] = b;
 }
 
-static inline void setFace(aiFace& face, int a, int b, int c)
+inline void SetFace(aiFace& face, int a, int b, int c)
 {
     face.mNumIndices = 3;
     face.mIndices = new unsigned int[3];
@@ -672,541 +235,390 @@ static inline void setFace(aiFace& face, int a, int b, int c)
     face.mIndices[2] = c;
 }
 
-Range glTFReader::LoadMesh(const char* id, Value& mesh)
-{  
-    Range range;
-    range.first = mImpMeshes.size();
-    range.second = mImpMeshes.size();
-
-    MemIt primitives = mesh.FindMember("primitives");
-    if (primitives != mesh.MemberEnd() && primitives->value.IsArray()) {
-        for (unsigned int i = 0; i < primitives->value.Size(); ++i) {
-            Value& primitive = primitives->value[i];
-
-            aiMesh* aimesh = new aiMesh();
-            mImpMeshes.push_back(aimesh);
-            ++range.second;
-
-            MemIt mode = primitive.FindMember("mode");
-            if (mode != primitive.MemberEnd() && mode->value.IsInt()) {
-                switch (mode->value.GetInt()) {
-                    case PrimitiveMode_POINTS:
-                        aimesh->mPrimitiveTypes |= aiPrimitiveType_POINT;
-                        break;
-                            
-                    case PrimitiveMode_LINES:
-                    case PrimitiveMode_LINE_LOOP:
-                    case PrimitiveMode_LINE_STRIP:
-                        aimesh->mPrimitiveTypes |= aiPrimitiveType_LINE;
-                        break;
-
-                    case PrimitiveMode_TRIANGLES:
-                    case PrimitiveMode_TRIANGLE_STRIP:
-                    case PrimitiveMode_TRIANGLE_FAN:
-                        aimesh->mPrimitiveTypes |= aiPrimitiveType_TRIANGLE;
-                        break;
-                }
-            }
+void glTFImporter::ImportMeshes(glTF::Asset& r)
+{
+    std::vector<aiMesh*> meshes;
 
-            MemIt attrs = primitive.FindMember("attributes");
-            if (attrs != primitive.MemberEnd() && attrs->value.IsObject()) {
-                for (MemIt it = attrs->value.MemberBegin(); it != attrs->value.MemberEnd(); ++it) {
-                    if (!it->value.IsString()) continue;
-                    const char* attr = it->name.GetString();
-                    const char* accessorId = it->value.GetString();
+    unsigned int k = 0;
 
-                    Ptr<Accessor> accessor = mAccessors.Get(accessorId);
-                    if (!accessor) continue;
+    for (unsigned int m = 0; m < r.meshes.Size(); ++m) {
+        Mesh& mesh = r.meshes[m];
 
-                    if (strcmp(attr, "POSITION") == 0) {
-                        accessor->ExtractData(aimesh->mVertices, &aimesh->mNumVertices);
-                    }
-                    else if (strcmp(attr, "NORMAL") == 0) {
-                        accessor->ExtractData(aimesh->mNormals);
-                    }
-                   else if (strncmp(attr, "TEXCOORD_", 9) == 0) {
-                        int idx = attr[9] - '0';
-                        if (idx >= 0 && idx <= AI_MAX_NUMBER_OF_TEXTURECOORDS) {
-                            accessor->ExtractData(aimesh->mTextureCoords[idx], 0, &aimesh->mNumUVComponents[idx]);
-                        }
-                    }
-                }
-            }
+        meshOffsets.push_back(k);
+        k += mesh.primitives.size();
 
-            MemIt indices = primitive.FindMember("indices");
-            if (indices != primitive.MemberEnd() && indices->value.IsString()) {
-                Ptr<Accessor> acc = mAccessors.Get(indices->value.GetString());
-                if (acc) {
-                    aiFace* faces = 0;
-                    std::size_t nFaces = 0;
-
-                    int primitiveMode = mode->value.GetInt();
-                    switch (primitiveMode) {
-                        case PrimitiveMode_POINTS: {
-                            nFaces = acc->count;
-                            faces = new aiFace[nFaces];
-                            for (unsigned int i = 0; i < acc->count; ++i) {
-                                setFace(faces[i], acc->GetUInt(i));
-                            }
-                            break;
-                        }
+        for (unsigned int p = 0; p < mesh.primitives.size(); ++p) {
+            Mesh::Primitive& prim = mesh.primitives[p];
 
-                        case PrimitiveMode_LINES: {
-                            nFaces = acc->count / 2;
-                            faces = new aiFace[nFaces];
-                            for (unsigned int i = 0; i < acc->count; i += 2) {
-                                setFace(faces[i / 2], acc->GetUInt(i), acc->GetUInt(i + 1));
-                            }
-                            break;
-                        }
-
-                        case PrimitiveMode_LINE_LOOP:
-                        case PrimitiveMode_LINE_STRIP: {
-                            nFaces = acc->count - ((primitiveMode == PrimitiveMode_LINE_STRIP) ? 1 : 0);
-                            faces = new aiFace[nFaces];
-                            setFace(faces[0], acc->GetUInt(0), acc->GetUInt(1));
-                            for (unsigned int i = 2; i < acc->count; ++i) {
-                                setFace(faces[i - 1], faces[i - 2].mIndices[1], acc->GetUInt(i));
-                            }
-                            if (primitiveMode == PrimitiveMode_LINE_LOOP) { // close the loop
-                                setFace(faces[acc->count - 1], faces[acc->count - 2].mIndices[1], faces[0].mIndices[0]);
-                            }
-                            break;
-                        }
-
-                        case PrimitiveMode_TRIANGLES: {
-                            nFaces = acc->count / 3;
-                            faces = new aiFace[nFaces];
-                            for (unsigned int i = 0; i < acc->count; i += 3) {
-                                setFace(faces[i / 3], acc->GetUInt(i), acc->GetUInt(i + 1), acc->GetUInt(i + 2));
-                            }
-                            break;
-                        }
-                        case PrimitiveMode_TRIANGLE_STRIP: {
-                            nFaces = acc->count - 2;
-                            faces = new aiFace[nFaces];
-                            setFace(faces[0], acc->GetUInt(0), acc->GetUInt(1), acc->GetUInt(2));
-                            for (unsigned int i = 3; i < acc->count; ++i) {
-                                setFace(faces[i - 2], faces[i - 1].mIndices[1], faces[i - 1].mIndices[2], acc->GetUInt(i));
-                            }
-                            break;
-                        }
-                        case PrimitiveMode_TRIANGLE_FAN:
-                            nFaces = acc->count - 2;
-                            faces = new aiFace[nFaces];
-                            setFace(faces[0], acc->GetUInt(0), acc->GetUInt(1), acc->GetUInt(2));
-                            for (unsigned int i = 3; i < acc->count; ++i) {
-                                setFace(faces[i - 2], faces[0].mIndices[0], faces[i - 1].mIndices[2], acc->GetUInt(i));
-                            }
-                            break;
-                    }
+            aiMesh* aim = new aiMesh();
+            meshes.push_back(aim);
 
-                    if (faces) {
-                        aimesh->mFaces = faces;
-                        aimesh->mNumFaces = nFaces;
-                    }
-                }
+            aim->mName = mesh.id;
+            if (mesh.primitives.size() > 1) {
+                size_t& len = aim->mName.length;
+                aim->mName.data[len] = '-';
+                len += 1 + ASSIMP_itoa10(aim->mName.data + len + 1, MAXLEN - len - 1, p);
             }
 
+            switch (prim.mode) {
+                case PrimitiveMode_POINTS:
+                    aim->mPrimitiveTypes |= aiPrimitiveType_POINT;
+                    break;
+
+                case PrimitiveMode_LINES:
+                case PrimitiveMode_LINE_LOOP:
+                case PrimitiveMode_LINE_STRIP:
+                    aim->mPrimitiveTypes |= aiPrimitiveType_LINE;
+                    break;
+
+                case PrimitiveMode_TRIANGLES:
+                case PrimitiveMode_TRIANGLE_STRIP:
+                case PrimitiveMode_TRIANGLE_FAN:
+                    aim->mPrimitiveTypes |= aiPrimitiveType_TRIANGLE;
+                    break;
+            }
 
-            MemIt material = primitive.FindMember("material");
-            if (material != primitive.MemberEnd() && material->value.IsString()) {
-                aimesh->mMaterialIndex = mMaterials.Get(material->value.GetString());
+            Mesh::Primitive::Attributes& attr = prim.attributes;
+            if (attr.position.size() > 0 && attr.position[0]) {
+                aim->mNumVertices = attr.position[0]->count;
+                attr.position[0]->ExtractData(aim->mVertices);
             }
-        }
-    }
 
-    return range;
-}
+            if (attr.normal.size() > 0 && attr.normal[0]) {
+                attr.normal[0]->ExtractData(aim->mNormals);
+            }
 
+            for (size_t tc = 0; tc < attr.texcoord.size() && tc <= AI_MAX_NUMBER_OF_TEXTURECOORDS; ++tc) {
+                attr.texcoord[tc]->ExtractData(aim->mTextureCoords[tc]);
+                aim->mNumUVComponents[tc] = attr.texcoord[tc]->GetNumComponents();
+            }
 
-struct Image
-{
-    aiString uri;
-};
 
-struct Texture
-{
-    Ptr<Image> source;
-};
+            if (prim.indices) {
+                aiFace* faces = 0;
+                size_t nFaces = 0;
 
-Ptr<Image> glTFReader::LoadImage(const char* id, Value& obj)
-{
+                unsigned int count = prim.indices->count;
 
-    Image* img = new Image();
+                Accessor::Indexer data = prim.indices->GetIndexer();
 
-    std::size_t embeddedDataLen = 0;
-    uint8_t* embeddedData = 0;
-    const char* mimeType = 0;
+                switch (prim.mode) {
+                    case PrimitiveMode_POINTS: {
+                        nFaces = count;
+                        faces = new aiFace[nFaces];
+                        for (unsigned int i = 0; i < count; ++i) {
+                            SetFace(faces[i], data.GetUInt(i));
+                        }
+                        break;
+                    }
 
-    // Check for extensions first (to detect binary embedded data) 
-    MemIt extensions = obj.FindMember("extensions");
-    if (extensions != obj.MemberEnd()) {
-        Value& exts = extensions->value;
+                    case PrimitiveMode_LINES: {
+                        nFaces = count / 2;
+                        faces = new aiFace[nFaces];
+                        for (unsigned int i = 0; i < count; i += 2) {
+                            SetFace(faces[i / 2], data.GetUInt(i), data.GetUInt(i + 1));
+                        }
+                        break;
+                    }
 
-        MemIt KHR_binary_glTF = exts.FindMember("KHR_binary_glTF");
-        if (KHR_binary_glTF != exts.MemberEnd() && KHR_binary_glTF->value.IsObject()) {
+                    case PrimitiveMode_LINE_LOOP:
+                    case PrimitiveMode_LINE_STRIP: {
+                        nFaces = count - ((prim.mode == PrimitiveMode_LINE_STRIP) ? 1 : 0);
+                        faces = new aiFace[nFaces];
+                        SetFace(faces[0], data.GetUInt(0), data.GetUInt(1));
+                        for (unsigned int i = 2; i < count; ++i) {
+                            SetFace(faces[i - 1], faces[i - 2].mIndices[1], data.GetUInt(i));
+                        }
+                        if (prim.mode == PrimitiveMode_LINE_LOOP) { // close the loop
+                            SetFace(faces[count - 1], faces[count - 2].mIndices[1], faces[0].mIndices[0]);
+                        }
+                        break;
+                    }
 
-            int width  = TryReadMember(KHR_binary_glTF->value, "width", 0);
-            int height = TryReadMember(KHR_binary_glTF->value, "height", 0);
-            
-            ReadMember(KHR_binary_glTF->value, "mimeType", mimeType);
-
-            const char* bufferViewId;
-            if (ReadMember(KHR_binary_glTF->value, "bufferView", bufferViewId)) {
-                Ptr<BufferView> bv = mBufferViews.Get(bufferViewId);
-                if (bv) {
-                    embeddedDataLen = bv->byteLength;
-                    embeddedData = new uint8_t[embeddedDataLen];
-                    memcpy(embeddedData, bv->buffer->GetPointer() + bv->byteOffset, embeddedDataLen);
-                }
-            }
-        }
-    }
-    
-    if (!embeddedDataLen) {
-        const char* uri;
-        if (ReadMember(obj, "uri", uri)) {
-            if (IsDataURI(uri)) {
-                const char* comma = strchr(uri, ',');
-                *const_cast<char*>(comma) = '\0';
-
-                bool isBase64 = (strstr(uri, "base64") != 0);
-                if (isBase64) {
-                    embeddedDataLen = DecodeBase64(comma + 1, embeddedData);
+                    case PrimitiveMode_TRIANGLES: {
+                        nFaces = count / 3;
+                        faces = new aiFace[nFaces];
+                        for (unsigned int i = 0; i < count; i += 3) {
+                            SetFace(faces[i / 3], data.GetUInt(i), data.GetUInt(i + 1), data.GetUInt(i + 2));
+                        }
+                        break;
+                    }
+                    case PrimitiveMode_TRIANGLE_STRIP: {
+                        nFaces = count - 2;
+                        faces = new aiFace[nFaces];
+                        SetFace(faces[0], data.GetUInt(0), data.GetUInt(1), data.GetUInt(2));
+                        for (unsigned int i = 3; i < count; ++i) {
+                            SetFace(faces[i - 2], faces[i - 1].mIndices[1], faces[i - 1].mIndices[2], data.GetUInt(i));
+                        }
+                        break;
+                    }
+                    case PrimitiveMode_TRIANGLE_FAN:
+                        nFaces = count - 2;
+                        faces = new aiFace[nFaces];
+                        SetFace(faces[0], data.GetUInt(0), data.GetUInt(1), data.GetUInt(2));
+                        for (unsigned int i = 3; i < count; ++i) {
+                            SetFace(faces[i - 2], faces[0].mIndices[0], faces[i - 1].mIndices[2], data.GetUInt(i));
+                        }
+                        break;
                 }
 
-                const char* sc = strchr(uri, ';');
-                if (sc != 0) {
-                    *const_cast<char*>(sc) = '\0';
-                    mimeType = uri;
+                if (faces) {
+                    aim->mFaces = faces;
+                    aim->mNumFaces = nFaces;
                 }
             }
-            else {
-                img->uri = uri;
-            }
-        }
-    }
-
-    // Add the embedded texture
-    if (embeddedDataLen > 0) {
-        aiTexture* tex = new aiTexture();
-        mImpTextures.push_back(tex);
 
-        tex->mWidth = static_cast<unsigned int>(embeddedDataLen);
-        tex->mHeight = 0;
-        tex->pcData = reinterpret_cast<aiTexel*>(embeddedData);
 
-        if (mimeType) {
-            const char* ext = strchr(mimeType, '/') + 1;
-            if (ext) {
-                if (strcmp(ext, "jpeg") == 0) ext = "jpg";
-            
-                std::size_t len = strlen(ext);
-                if (len <= 3) {
-                    strcpy(tex->achFormatHint, ext);
-                }
+            if (prim.material) {
+                aim->mMaterialIndex = prim.material.GetIndex();
             }
         }
-
-        // setup texture reference string (copied from ColladaLoader::FindFilenameForEffectTexture)
-        img->uri.data[0] = '*';
-        img->uri.length = 1 + ASSIMP_itoa10(img->uri.data + 1, MAXLEN - 1, mImpTextures.size() - 1);
     }
 
-    return Ptr<Image>(img);
+    meshOffsets.push_back(k);
+    
+    CopyVector(meshes, mScene->mMeshes, mScene->mNumMeshes);
 }
 
-Ptr<Texture> glTFReader::LoadTexture(const char* id, Value& obj)
+void glTFImporter::ImportCameras(glTF::Asset& r)
 {
-    Texture* tex = new Texture();
+    if (!r.cameras.Size()) return;
 
-    const char* source;
-    if (ReadMember(obj, "source", source)) {
-        tex->source = mImages.Get(source);
-    }
+    mScene->mNumCameras = r.cameras.Size();
+    mScene->mCameras = new aiCamera*[r.cameras.Size()];
 
-    return Ptr<Texture>(tex);
-}
+    for (size_t i = 0; i < r.cameras.Size(); ++i) {
+        Camera& cam = r.cameras[i];
 
-void glTFReader::SetMaterialColorProperty(aiMaterial* mat, Value& vals, const char* propName, aiTextureType texType, const char* pKey, unsigned int type, unsigned int idx)
-{
-    MemIt prop = vals.FindMember(propName);
-    if (prop != vals.MemberEnd()) {
-        aiColor3D col;
-        if (Read(prop->value, col)) {
-            mat->AddProperty(&col, 1, pKey, type, idx);
+        aiCamera* aicam = mScene->mCameras[i] = new aiCamera();
+
+        if (cam.type == Camera::Perspective) {
+            
+            aicam->mAspect        = cam.perspective.aspectRatio;
+            aicam->mHorizontalFOV = cam.perspective.yfov * aicam->mAspect;
+            aicam->mClipPlaneFar  = cam.perspective.zfar;
+            aicam->mClipPlaneNear = cam.perspective.znear;
         }
-        else if (prop->value.IsString()) {
-            Ptr<Texture> tex = mTextures.Get(prop->value.GetString());
-            if (tex && tex->source) {
-                mat->AddProperty(&tex->source->uri, _AI_MATKEY_TEXTURE_BASE, texType, 0);
-            }
+        else {
+            // assimp does not support orthographic cameras
         }
     }
 }
 
-unsigned int glTFReader::LoadMaterial(const char* id, Value& material)
+void glTFImporter::ImportLights(glTF::Asset& r)
 {
-    aiMaterial* mat = new aiMaterial();
-    mImpMaterials.push_back(mat);
+    if (!r.lights.Size()) return;
 
-    const char* name;
-    if (ReadMember(material, "name", name)) {
-        aiString str(name);
-        mat->AddProperty(&str, AI_MATKEY_NAME);
-    }
+    mScene->mNumLights = r.lights.Size();
+    mScene->mLights = new aiLight*[r.lights.Size()];
 
-    MemIt values = material.FindMember("values");
-    if (values != material.MemberEnd() && values->value.IsObject()) {
-        Value& vals = values->value;
+    for (size_t i = 0; i < r.lights.Size(); ++i) {
+        Light& l = r.lights[i];
 
-        SetMaterialColorProperty(mat, vals, "diffuse",  aiTextureType_DIFFUSE,  AI_MATKEY_COLOR_DIFFUSE);
-        SetMaterialColorProperty(mat, vals, "specular", aiTextureType_SPECULAR, AI_MATKEY_COLOR_SPECULAR);
-        SetMaterialColorProperty(mat, vals, "ambient",  aiTextureType_AMBIENT,  AI_MATKEY_COLOR_AMBIENT);
+        aiLight* ail = mScene->mLights[i] = new aiLight();
 
-        float shininess;
-        if (ReadMember(vals, "shininess", shininess)) {
-            mat->AddProperty(&shininess, 1, AI_MATKEY_SHININESS);
-        }
-    }
+        switch (l.type) {
+            case Light::Type_directional:
+                ail->mType = aiLightSource_DIRECTIONAL; break;
 
-    MemIt extensions = material.FindMember("values");
-    if (extensions != material.MemberEnd() && extensions->value.IsObject()) {
-        Value& exts = extensions->value;
+            case Light::Type_point:
+                ail->mType = aiLightSource_POINT; break;
 
-        MemIt KHR_materials_common = exts.FindMember("KHR_materials_common");
-        if (KHR_materials_common != exts.MemberEnd() && KHR_materials_common->value.IsObject()) {
-            // TODO: support KHR_materials_common (https://github.com/KhronosGroup/glTF/tree/master/extensions/Khronos/KHR_materials_common)
+            case Light::Type_spot:
+                ail->mType = aiLightSource_SPOT; break;
+
+            case Light::Type_ambient:
+                ail->mType = aiLightSource_AMBIENT; break;
         }
-    }
 
-    return static_cast<unsigned int>(mImpMaterials.size() - 1);
+        CopyValue(l.color, ail->mColorAmbient);
+        CopyValue(l.color, ail->mColorDiffuse);
+        CopyValue(l.color, ail->mColorSpecular);
+        
+        ail->mAngleOuterCone = l.falloffAngle;
+        ail->mAngleInnerCone = l.falloffExponent; // TODO fix this, it does not look right at all
+
+        ail->mAttenuationConstant  = l.constantAttenuation;
+        ail->mAttenuationLinear    = l.linearAttenuation;
+        ail->mAttenuationQuadratic = l.quadraticAttenuation;
+    }
 }
 
-aiNode* glTFReader::LoadNode(const char* id, Value& node)
-{
-    aiNode* ainode = new aiNode(id);
 
-    //MemIt name = node.FindMember("name");
-    //if (name != node.MemberEnd() && name->value.IsString()) {
-    //    strcpy(ainode->mName.data, name->value.GetString());
-    //}
+aiNode* ImportNode(aiScene* pScene, glTF::Asset& r, std::vector<unsigned int>& meshOffsets, glTF::Ref<glTF::Node>& ptr)
+{
+    Node& node = *ptr;
 
-    MemIt children = node.FindMember("children");
-    if (children != node.MemberEnd() && children->value.IsArray()) {
+    aiNode* ainode = new aiNode(node.id);
 
-        ainode->mChildren = new aiNode*[children->value.Size()];
-        //ainode->mNumChildren = 0;
+    if (!node.children.empty()) {
+        ainode->mNumChildren = node.children.size();
+        ainode->mChildren = new aiNode*[ainode->mNumChildren];
 
-        for (unsigned int i = 0; i < children->value.Size(); ++i) {
-            Value& child = children->value[i];
-            if (child.IsString()) {
-                // get/create the child node
-                aiNode* aichild = mNodes.Get(child.GetString());
-                if (aichild) {
-                    aichild->mParent = ainode;
-                    ainode->mChildren[ainode->mNumChildren++] = aichild;
-                }
-            }
+        for (unsigned int i = 0; i < ainode->mNumChildren; ++i) {
+            aiNode* child = ImportNode(pScene, r, meshOffsets, node.children[i]);
+            child->mParent = ainode;
+            ainode->mChildren[i] = child;
         }
     }
 
-    aiMatrix4x4& transf = ainode->mTransformation;
-
-    MemIt matrix = node.FindMember("matrix");
-    if (matrix != node.MemberEnd()) {
-        Read(matrix->value, transf);
+    aiMatrix4x4 matrix = ainode->mTransformation;
+    if (node.matrix.isPresent) {
+        CopyValue(node.matrix.value, matrix);
     }
     else {
-        MemIt translation = node.FindMember("translation");
-        if (translation != node.MemberEnd()) {
+        if (node.translation.isPresent) {
             aiVector3D trans;
-            if (Read(matrix->value, trans)) {
-                aiMatrix4x4 m;
-                aiMatrix4x4::Translation(trans, m);
-                transf = m * transf;
-            }
+            CopyValue(node.translation.value, trans);
+            aiMatrix4x4 t;
+            aiMatrix4x4::Translation(trans, t);
+            matrix = t * matrix;
         }
 
-        MemIt scale = node.FindMember("scale");
-        if (scale != node.MemberEnd()) {
+        if (node.scale.isPresent) {
             aiVector3D scal(1.f);
-            if (Read(matrix->value, scal)) {
-                aiMatrix4x4 m;
-                aiMatrix4x4::Scaling(scal, m);
-                transf = m * transf;
-            }
+            CopyValue(node.scale.value, scal);
+            aiMatrix4x4 s;
+            aiMatrix4x4::Scaling(scal, s);
+            matrix = s * matrix;
         }
 
-        MemIt rotation = node.FindMember("rotation");
-        if (rotation != node.MemberEnd()) {
+
+        if (node.rotation.isPresent) {
             aiQuaternion rot;
-            if (Read(matrix->value, rot)) {
-                transf = aiMatrix4x4(rot.GetMatrix()) * transf;
-            }
+            CopyValue(node.rotation.value, rot);
+            matrix = aiMatrix4x4(rot.GetMatrix()) * matrix;
         }
     }
 
-    MemIt meshes = node.FindMember("meshes");
-    if (meshes != node.MemberEnd() && meshes->value.IsArray()) {
-        std::size_t numMeshes = (std::size_t)meshes->value.Size();
+    if (!node.meshes.empty()) {
+        int count = 0;
+        for (size_t i = 0; i < node.meshes.size(); ++i) {
+            int idx = node.meshes[i].GetIndex();
+            count += meshOffsets[idx + 1] - meshOffsets[idx];
+        }
 
-        std::vector<unsigned int> meshList;
+        ainode->mNumMeshes = count;
+        ainode->mMeshes = new unsigned int[count];
 
-        for (std::size_t i = 0; i < numMeshes; ++i) {
-            if (meshes->value[i].IsString()) {
-                Range range = mMeshes.Get(meshes->value[i].GetString());
-                for (unsigned int m = range.first; m < range.second; ++m) {
-                    meshList.push_back(m);
-                }                
+        int k = 0;
+        for (size_t i = 0; i < node.meshes.size(); ++i) {
+            int idx = node.meshes[i].GetIndex();
+            for (size_t j = meshOffsets[idx]; j < meshOffsets[idx + 1]; ++j, ++k) {
+                ainode->mMeshes[k] = j;
             }
         }
+    }
 
-        if (meshList.size()) {
-            ainode->mNumMeshes = meshList.size();
-            ainode->mMeshes = new unsigned int[meshList.size()];
-            std::swap_ranges(meshList.begin(), meshList.end(), ainode->mMeshes);
-        }
+    if (node.camera) {
+        pScene->mCameras[node.camera.GetIndex()]->mName = ainode->mName;
     }
 
-    // TODO load "skeletons", "skin", "jointName", "camera"
+    if (node.light) {
+        pScene->mLights[node.light.GetIndex()]->mName = ainode->mName;
+    }
 
     return ainode;
 }
 
-
-
-
-
-//
-// glTFImporter methods
-//
-
-template<> const std::string LogFunctions<glTFImporter>::log_prefix = "glTF: ";
-
-
-
-static const aiImporterDesc desc = {
-    "glTF Importer",
-    "",
-    "",
-    "",
-    aiImporterFlags_SupportTextFlavour | aiImporterFlags_SupportBinaryFlavour | aiImporterFlags_SupportCompressedFlavour
-    | aiImporterFlags_LimitedSupport | aiImporterFlags_Experimental,
-    0,
-    0,
-    0,
-    0,
-    "gltf glb"
-};
-
-glTFImporter::glTFImporter() 
-: BaseImporter()
+void glTFImporter::ImportNodes(glTF::Asset& r)
 {
+    if (!r.scene) return;
 
-}
-
-glTFImporter::~glTFImporter() {
+    std::vector< Ref<Node> > rootNodes = r.scene->nodes;
 
-}
-
-bool glTFImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig ) const {
-    const std::string& extension = GetExtension(pFile);
-    if (extension == "gltf" || extension == "glb") {
-        return true;
+    // The root nodes
+    unsigned int numRootNodes = rootNodes.size();
+    if (numRootNodes == 1) { // a single root node: use it
+        mScene->mRootNode = ImportNode(mScene, r, meshOffsets, rootNodes[0]);
+    }
+    else if (numRootNodes > 1) { // more than one root node: create a fake root
+        aiNode* root = new aiNode("ROOT");
+        root->mChildren = new aiNode*[numRootNodes];
+        for (unsigned int i = 0; i < numRootNodes; ++i) {
+            aiNode* node = ImportNode(mScene, r, meshOffsets, rootNodes[i]);
+            node->mParent = root;
+            root->mChildren[root->mNumChildren++] = node;
+        }
+        mScene->mRootNode = root;
     }
-    return false;
-}
 
-const aiImporterDesc* glTFImporter::GetInfo() const {
-    return &desc;
+    //if (!mScene->mRootNode) {
+    //  mScene->mRootNode = new aiNode("EMPTY");
+    //}
 }
 
-void glTFImporter::ReadBinaryHeader(IOStream& stream)
+void glTFImporter::ImportEmbeddedTextures(glTF::Asset& r)
 {
-    GLB_Header header;
-    if (stream.Read(&header, sizeof(header), 1) != 1) {
-        ThrowException("Unable to read the file header");
-    }
-
-    if (strncmp((char*)header.magic, AI_GLB_MAGIC_NUMBER, sizeof(header.magic)) != 0) {
-        ThrowException("Invalid binary glTF file");
-    }
-
-    AI_SWAP4(header.version);
-    if (header.version != 1) {
-        ThrowException("Unsupported binary glTF version");
-    }
+    embeddedTexIdxs.resize(r.images.Size(), -1);
 
-    AI_SWAP4(header.sceneFormat);
-    if (header.sceneFormat != SceneFormat_JSON) {
-        ThrowException("Unsupported binary glTF scene format");
+    int numEmbeddedTexs = 0;
+    for (size_t i = 0; i < r.images.Size(); ++i) {
+        if (r.images[i].HasData())
+            numEmbeddedTexs += 1;
     }
 
-    AI_SWAP4(header.length);
-    AI_SWAP4(header.sceneLength);
+    if (numEmbeddedTexs == 0)
+        return;
 
-    mSceneLength = static_cast<std::size_t>(header.sceneLength);
-        
-    mBodyOffset = sizeof(header) + mSceneLength;
-    mBodyOffset = (mBodyOffset + 3) & ~3; // Round up to next multiple of 4
+    mScene->mTextures = new aiTexture*[numEmbeddedTexs];
 
-    mBodyLength = header.length - mBodyOffset;
-}
+    // Add the embedded textures
+    for (size_t i = 0; i < r.images.Size(); ++i) {
+        Image img = r.images[i];
+        if (!img.HasData()) continue;
 
-void glTFImporter::InternReadFile( const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler ) {
-    scoped_ptr<IOStream> stream(pIOHandler->Open(pFile, "rb"));
-    if (!stream) {
-        ThrowException("Could not open file for reading");
-    }
+        int idx = mScene->mNumTextures++;
+        embeddedTexIdxs[i] = idx;
 
-    // is binary? then read the header
-    if (GetExtension(pFile) == "glb") {
-        ReadBinaryHeader(*stream);
-    }
-    else {
-        mSceneLength = stream->FileSize();
-        mBodyLength = 0;
-    }
+        aiTexture* tex = mScene->mTextures[idx] = new aiTexture();
 
+        size_t length = img.GetDataLength();
+        void* data = img.StealData();
 
-    // read the scene data
+        tex->mWidth = static_cast<unsigned int>(length);
+        tex->mHeight = 0;
+        tex->pcData = reinterpret_cast<aiTexel*>(data);
 
-    scoped_ptr<char> sceneData = new char[mSceneLength + 1];
-    sceneData[mSceneLength] = '\0';
+        if (!img.mimeType.empty()) {
+            const char* ext = strchr(img.mimeType.c_str(), '/') + 1;
+            if (ext) {
+                if (strcmp(ext, "jpeg") == 0) ext = "jpg";
 
-    if (stream->Read(sceneData, 1, mSceneLength) != mSceneLength) {
-        ThrowException("Could not read the file contents");
+                size_t len = strlen(ext);
+                if (len <= 3) {
+                    strcpy(tex->achFormatHint, ext);
+                }
+            }
+        }
     }
+}
 
+void glTFImporter::InternReadFile(const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler) {
 
-    // parse the JSON document
+    this->mScene = pScene;
+    
+    // read the asset file
+    glTF::Asset asset(pIOHandler);
+    asset.Load(pFile, GetExtension(pFile) == "glb");
 
-    Document doc;
-    doc.ParseInsitu(sceneData);
 
-    if (doc.HasParseError()) {
-        char buffer[32];
-        ASSIMP_itoa10(buffer, doc.GetErrorOffset());
-        ThrowException(std::string("JSON parse error, offset ") + buffer + ": "
-            + GetParseError_En(doc.GetParseError()));
-    }
+    //
+    // Copy the data out
+    //
 
-    if (!doc.IsObject()) {
-        ThrowException("gltf file must be a JSON object!");
-    }
+    ImportEmbeddedTextures(asset);
+    ImportMaterials(asset);
 
+    ImportMeshes(asset);
 
-    // Buffer instance for the current file embedded contents
-    shared_ptr<Buffer> bodyBuffer;
-    if (mBodyLength > 0) {
-        bodyBuffer.reset(Buffer::FromStream(*stream, mBodyLength, mBodyOffset));
-    }
+    ImportCameras(asset);
+    ImportLights(asset);
+
+    ImportNodes(asset);
 
-    // import the data
-    glTFReader reader(pScene, doc, *pIOHandler, bodyBuffer);
-    reader.Load();
+    // TODO: it does not split the loaded vertices, should it?
+    pScene->mFlags |= AI_SCENE_FLAGS_NON_VERBOSE_FORMAT;
 
     if (pScene->mNumMeshes == 0) {
         pScene->mFlags |= AI_SCENE_FLAGS_INCOMPLETE;
index 3de8999..b0b20c0 100644 (file)
@@ -41,16 +41,23 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #define AI_GLTFIMPORTER_H_INC
 
 #include "BaseImporter.h"
-#include "LogAux.h"
 #include "DefaultIOSystem.h"
 
+struct aiNode;
+
+
+namespace glTF
+{
+    class Asset;
+}
+
 namespace Assimp {
 
 /**
  * Load the glTF format.
  * https://github.com/KhronosGroup/glTF/tree/master/specification
  */
-class glTFImporter : public BaseImporter, public LogFunctions<glTFImporter> {
+class glTFImporter : public BaseImporter{
 public:
     glTFImporter();
     virtual ~glTFImporter();
@@ -61,10 +68,20 @@ protected:
     virtual void InternReadFile( const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler );
 
 private:
-    void ReadBinaryHeader(IOStream& stream);
 
-    std::size_t mSceneLength;
-    std::size_t mBodyOffset, mBodyLength;
+    std::vector<unsigned int> meshOffsets;
+
+    std::vector<int> embeddedTexIdxs;
+
+    aiScene* mScene;
+
+    void ImportEmbeddedTextures(glTF::Asset& a);
+    void ImportMaterials(glTF::Asset& a);
+    void ImportMeshes(glTF::Asset& a);
+    void ImportCameras(glTF::Asset& a);
+    void ImportLights(glTF::Asset& a);
+    void ImportNodes(glTF::Asset& a);
+
 };
 
 } // Namespace assimp
diff --git a/code/glTFUtil.cpp b/code/glTFUtil.cpp
deleted file mode 100644 (file)
index 46ef32a..0000000
+++ /dev/null
@@ -1,164 +0,0 @@
-/*
-Open Asset Import Library (assimp)
-----------------------------------------------------------------------
-
-Copyright (c) 2006-2015, assimp team
-All rights reserved.
-
-Redistribution and use of this software in source and binary forms,
-with or without modification, are permitted provided that the
-following conditions are met:
-
-* Redistributions of source code must retain the above
-copyright notice, this list of conditions and the
-following disclaimer.
-
-* Redistributions in binary form must reproduce the above
-copyright notice, this list of conditions and the
-following disclaimer in the documentation and/or other
-materials provided with the distribution.
-
-* Neither the name of the assimp team, nor the names of its
-contributors may be used to endorse or promote products
-derived from this software without specific prior
-written permission of the assimp team.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-----------------------------------------------------------------------
-*/
-#include "glTFUtil.h"
-
-
-using namespace Assimp;
-using namespace Assimp::glTF;
-
-
-bool Assimp::glTF::IsDataURI(const char* uri)
-{
-    return strncmp(uri, "data:", 5) == 0;
-}
-
-
-static const uint8_t tableDecodeBase64[128] = {
-    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-    0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x3F,
-    0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B,
-    0x3C, 0x3D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-    0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
-    0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E,
-    0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16,
-    0x17, 0x18, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00,
-    0x00, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20,
-    0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
-    0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, 0x30,
-    0x31, 0x32, 0x33, 0x00, 0x00, 0x00, 0x00, 0x00
-};
-
-static inline char EncodeCharBase64(uint8_t b)
-{
-    return "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="[b];
-}
-
-static inline uint8_t DecodeCharBase64(char c)
-{
-    return tableDecodeBase64[c]; // TODO faster with lookup table or ifs?
-    /*if (c >= 'A' && c <= 'Z') return c - 'A';
-    if (c >= 'a' && c <= 'z') return c - 'a' + 26;
-    if (c >= '0' && c <= '9') return c - '0' + 52;
-    return c == '+' ? 62 : 63;*/
-}
-
-std::size_t Assimp::glTF::DecodeBase64(
-    const char* in, uint8_t*& out)
-{
-    return DecodeBase64(in, strlen(in), out);
-}
-
-std::size_t Assimp::glTF::DecodeBase64(
-    const char* in, std::size_t inLength, uint8_t*& out)
-{
-    ai_assert(dataLen % 4 == 0);
-
-    if (inLength < 4) {
-        out = 0;
-        return 0;
-    }
-
-    int nEquals = int(in[inLength - 1] == '=') +
-                  int(in[inLength - 2] == '=');
-
-    std::size_t outLength = (inLength * 3) / 4 - nEquals;
-    out = new uint8_t[outLength];
-    memset(out, 0, outLength);
-
-    std::size_t j = 0;
-
-    for (std::size_t i = 0; i < inLength; i += 4) {
-        uint8_t b0 = DecodeCharBase64(in[i]);
-        uint8_t b1 = DecodeCharBase64(in[i + 1]);
-        uint8_t b2 = DecodeCharBase64(in[i + 2]);
-        uint8_t b3 = DecodeCharBase64(in[i + 3]);
-
-        out[j++] = (uint8_t)((b0 << 2) | (b1 >> 4));
-        out[j++] = (uint8_t)((b1 << 4) | (b2 >> 2));
-        out[j++] = (uint8_t)((b2 << 6) | b3);
-    }
-
-    return outLength;
-}
-
-
-
-void Assimp::glTF::EncodeBase64(
-    const uint8_t* in, std::size_t inLength,
-    std::string& out)
-{
-    std::size_t outLength = ((inLength + 2) / 3) * 4;
-
-    out.resize(outLength);
-
-    std::size_t j = 0;
-    for (std::size_t i = 0; i <  inLength; i += 3) {
-        uint8_t b = (in[i] & 0xFC) >> 2;
-        out[j++] = EncodeCharBase64(b);
-
-        b = (in[i] & 0x03) << 4;
-        if (i + 1 < inLength) {
-            b |= (in[i + 1] & 0xF0) >> 4;
-            out[j++] = EncodeCharBase64(b);
-
-            b = (in[i + 1] & 0x0F) << 2;
-            if (i + 2 < inLength) {
-                b |= (in[i + 2] & 0xC0) >> 6;
-                out[j++] = EncodeCharBase64(b);
-
-                b = in[i + 2] & 0x3F;
-                out[j++] = EncodeCharBase64(b);
-            }
-            else {
-                out[j++] = EncodeCharBase64(b);
-                out[j++] = '=';
-            }
-        }
-        else {
-            out[j++] = EncodeCharBase64(b);
-            out[j++] = '=';
-            out[j++] = '=';
-        }
-    }
-}