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. */
)
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
)
#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
--- /dev/null
+/*
+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
--- /dev/null
+/*
+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++] = '=';
+ }
+ }
+ }
+
+}
+
+}
+
+
----------------------------------------------------------------------
*/
-#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
--- /dev/null
+/*
+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);
+ }
+
+
+}
+
+
#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 {
-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!");
- }
-}
#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
{
class IOStream;
class ExportProperties;
-
// ------------------------------------------------------------------------------------------------
/** Helper class to export a given scene to an glTF file. */
// ------------------------------------------------------------------------------------------------
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();
};
}
+++ /dev/null
-/*
-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
-
#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];
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];
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;
#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();
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
+++ /dev/null
-/*
-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++] = '=';
- }
- }
-}