Merge pull request #1423 from Matter-and-Form/feature/gltf2

glTF 2 Importer + glTF 2 Spec Conformity
pull/1431/head
Kim Kulling 2017-09-13 20:01:38 +02:00 committed by GitHub
commit d139b4d180
33 changed files with 2453 additions and 1229 deletions

View File

@ -52,7 +52,8 @@ __Importers__:
- DXF
- ENFF
- FBX
- GLB/GLTF
- glTF 1.0 + GLB
- glTF 2.0
- HMB
- IFC-STEP
- IRR / IRRMESH
@ -106,8 +107,8 @@ __Exporters__:
- JSON (for WebGl, via https://github.com/acgessler/assimp2json)
- ASSBIN
- STEP
- glTF (partial)
- glTF2.0
- glTF 1.0 (partial)
- glTF 2.0 (partial)
### Building ###
Take a look into the `INSTALL` file. Our build system is CMake, if you used CMake before there is a good chance you know what to do.

View File

@ -666,6 +666,8 @@ ADD_ASSIMP_IMPORTER( GLTF
glTF2Asset.inl
glTF2AssetWriter.h
glTF2AssetWriter.inl
glTF2Importer.cpp
glTF2Importer.h
glTF2Exporter.h
glTF2Exporter.cpp
)

View File

@ -119,7 +119,7 @@ bool ColladaLoader::CanRead( const std::string& pFile, IOSystem* pIOHandler, boo
* might be NULL and it's our duty to return true here.
*/
if (!pIOHandler)return true;
const char* tokens[] = {"collada"};
const char* tokens[] = {"<collada"};
return SearchFileHeaderForToken(pIOHandler,pFile,tokens,1);
}
return false;

View File

@ -182,6 +182,7 @@ corresponding preprocessor flag to selectively disable formats.
#endif
#ifndef ASSIMP_BUILD_NO_GLTF_IMPORTER
# include "glTFImporter.h"
# include "glTF2Importer.h"
#endif
#ifndef ASSIMP_BUILD_NO_C4D_IMPORTER
# include "C4DImporter.h"
@ -336,6 +337,7 @@ void GetImporterInstanceList(std::vector< BaseImporter* >& out)
#endif
#if ( !defined ASSIMP_BUILD_NO_GLTF_IMPORTER )
out.push_back( new glTFImporter() );
out.push_back( new glTF2Importer() );
#endif
#if ( !defined ASSIMP_BUILD_NO_C4D_IMPORTER )
out.push_back( new C4DImporter() );

View File

@ -94,7 +94,7 @@ bool OFFImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool
{
if (!pIOHandler)return true;
const char* tokens[] = {"off"};
return SearchFileHeaderForToken(pIOHandler,pFile,tokens,1);
return SearchFileHeaderForToken(pIOHandler,pFile,tokens,1,3);
}
return false;
}

View File

@ -43,8 +43,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* Declares a glTF class to handle gltf/glb files
*
* glTF Extensions Support:
* KHR_binary_glTF: full
* KHR_materials_common: full
* KHR_materials_pbrSpecularGlossiness full
*/
#ifndef GLTF2ASSET_H_INC
#define GLTF2ASSET_H_INC
@ -130,15 +129,12 @@ namespace glTF2
struct BufferView; // here due to cross-reference
struct Texture;
struct Light;
struct Skin;
// Vec/matrix types, as raw float arrays
typedef float (vec3)[3];
typedef float (vec4)[4];
typedef float (mat4)[16];
typedef float (mat4)[16];
namespace Util
{
@ -166,23 +162,36 @@ namespace glTF2
//! Magic number for GLB files
#define AI_GLB_MAGIC_NUMBER "glTF"
#define AI_GLB_MAGIC_NUMBER "glTF"
#define AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLIC_FACTOR "$mat.gltf.pbrMetallicRoughness.metallicFactor", 0, 0
#define AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_ROUGHNESS_FACTOR "$mat.gltf.pbrMetallicRoughness.roughnessFactor", 0, 0
#define AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLICROUGHNESS_TEXTURE aiTextureType_UNKNOWN, 0
#define AI_MATKEY_GLTF_ALPHAMODE "$mat.gltf.alphaMode", 0, 0
#define AI_MATKEY_GLTF_ALPHACUTOFF "$mat.gltf.alphaCutoff", 0, 0
#define AI_MATKEY_GLTF_PBRSPECULARGLOSSINESS "$mat.gltf.pbrSpecularGlossiness", 0, 0
#define AI_MATKEY_GLTF_PBRSPECULARGLOSSINESS_DIFFUSE_FACTOR "$clr.diffuse", 0, 1
#define AI_MATKEY_GLTF_PBRSPECULARGLOSSINESS_SPECULAR_FACTOR "$clr.specular", 0, 1
#define AI_MATKEY_GLTF_PBRSPECULARGLOSSINESS_GLOSSINESS_FACTOR "$mat.gltf.pbrMetallicRoughness.glossinessFactor", 0, 0
#define AI_MATKEY_GLTF_PBRSPECULARGLOSSINESS_DIFFUSE_TEXTURE aiTextureType_DIFFUSE, 1
#define AI_MATKEY_GLTF_PBRSPECULARGLOSSINESS_SPECULARGLOSSINESS_TEXTURE aiTextureType_UNKNOWN, 1
#define _AI_MATKEY_GLTF_TEXTURE_TEXCOORD_BASE "$tex.file.texCoord"
#define _AI_MATKEY_GLTF_MAPPINGNAME_BASE "$tex.mappingname"
#define _AI_MATKEY_GLTF_MAPPINGID_BASE "$tex.mappingid"
#define _AI_MATKEY_GLTF_MAPPINGFILTER_MAG_BASE "$tex.mappingfiltermag"
#define _AI_MATKEY_GLTF_MAPPINGFILTER_MIN_BASE "$tex.mappingfiltermin"
#define AI_MATKEY_GLTF_TEXTURE_TEXCOORD _AI_MATKEY_GLTF_TEXTURE_TEXCOORD_BASE, type, N
#define AI_MATKEY_GLTF_MAPPINGNAME(type, N) _AI_MATKEY_GLTF_MAPPINGNAME_BASE, type, N
#define AI_MATKEY_GLTF_MAPPINGID(type, N) _AI_MATKEY_GLTF_MAPPINGID_BASE, type, N
#define AI_MATKEY_GLTF_MAPPINGFILTER_MAG(type, N) _AI_MATKEY_GLTF_MAPPINGFILTER_MAG_BASE, type, N
#define AI_MATKEY_GLTF_MAPPINGFILTER_MIN(type, N) _AI_MATKEY_GLTF_MAPPINGFILTER_MIN_BASE, type, N
#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
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
@ -232,7 +241,7 @@ namespace glTF2
case ComponentType_UNSIGNED_BYTE:
return 1;
default:
throw DeadlyImportError("GLTF: Unsupported Component Type "+t);
throw DeadlyImportError("GLTF: Unsupported Component Type " + std::to_string(t));
}
}
@ -244,15 +253,17 @@ namespace glTF2
};
//! Values for the Sampler::magFilter field
enum SamplerMagFilter
enum class SamplerMagFilter: unsigned int
{
UNSET = 0,
SamplerMagFilter_Nearest = 9728,
SamplerMagFilter_Linear = 9729
};
//! Values for the Sampler::minFilter field
enum SamplerMinFilter
enum class SamplerMinFilter: unsigned int
{
UNSET = 0,
SamplerMinFilter_Nearest = 9728,
SamplerMinFilter_Linear = 9729,
SamplerMinFilter_Nearest_Mipmap_Nearest = 9984,
@ -262,11 +273,12 @@ namespace glTF2
};
//! Values for the Sampler::wrapS and Sampler::wrapT field
enum SamplerWrap
enum class SamplerWrap: unsigned int
{
SamplerWrap_Clamp_To_Edge = 33071,
SamplerWrap_Mirrored_Repeat = 33648,
SamplerWrap_Repeat = 10497
UNSET = 0,
Clamp_To_Edge = 33071,
Mirrored_Repeat = 33648,
Repeat = 10497
};
//! Values for the Texture::format and Texture::internalFormat fields
@ -382,6 +394,7 @@ namespace glTF2
struct Object
{
int index; //!< The index of this object within its property container
int oIndex; //!< The original index of this object defined in the JSON
std::string id; //!< The globally unique ID used to reference this object
std::string name; //!< The user-defined name of this object
@ -629,7 +642,7 @@ namespace glTF2
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;
};
} cameraProperties;
Camera() {}
void Read(Value& obj, Asset& r);
@ -668,43 +681,68 @@ namespace glTF2
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
const vec4 defaultBaseColor = {1, 1, 1, 1};
const vec3 defaultEmissiveFactor = {0, 0, 0};
const vec4 defaultDiffuseFactor = {1, 1, 1, 1};
const vec3 defaultSpecularFactor = {1, 1, 1};
struct TextureInfo
{
Ref<Texture> texture;
vec4 color;
unsigned int index;
unsigned int texCoord = 0;
};
struct NormalTextureInfo : TextureInfo
{
float scale = 1;
};
struct OcclusionTextureInfo : TextureInfo
{
float strength = 1;
};
struct PbrMetallicRoughness
{
vec4 baseColorFactor;
TextureInfo baseColorTexture;
TextureInfo metallicRoughnessTexture;
float metallicFactor;
float roughnessFactor;
};
struct PbrSpecularGlossiness
{
vec4 diffuseFactor;
vec3 specularFactor;
float glossinessFactor;
TextureInfo diffuseTexture;
TextureInfo specularGlossinessTexture;
PbrSpecularGlossiness() { SetDefaults(); }
void SetDefaults();
};
//! 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;
Ref<Texture> normal;
//PBR metallic roughness properties
PbrMetallicRoughness pbrMetallicRoughness;
//other basic material properties
NormalTextureInfo normalTexture;
OcclusionTextureInfo occlusionTexture;
TextureInfo emissiveTexture;
vec3 emissiveFactor;
std::string alphaMode;
float alphaCutoff;
bool doubleSided;
bool transparent;
float transparency;
float shininess;
Technique technique;
//extension: KHR_materials_pbrSpecularGlossiness
Nullable<PbrSpecularGlossiness> pbrSpecularGlossiness;
Material() { SetDefaults(); }
void Read(Value& obj, Asset& r);
@ -729,92 +767,21 @@ namespace glTF2
Ref<Material> material;
};
/// \struct SExtension
/// Extension used for mesh.
struct SExtension
{
/// \enum EType
/// Type of extension.
enum EType
{
#ifdef ASSIMP_IMPORTER_GLTF_USE_OPEN3DGC
Compression_Open3DGC,///< Compression of mesh data using Open3DGC algorithm.
#endif
Unknown
};
EType Type;///< Type of extension.
/// \fn SExtension
/// Constructor.
/// \param [in] pType - type of extension.
SExtension(const EType pType)
: Type(pType)
{}
virtual ~SExtension() {
// empty
}
};
#ifdef ASSIMP_IMPORTER_GLTF_USE_OPEN3DGC
/// \struct SCompression_Open3DGC
/// Compression of mesh data using Open3DGC algorithm.
struct SCompression_Open3DGC : public SExtension
{
using SExtension::Type;
std::string Buffer;///< ID of "buffer" used for storing compressed data.
size_t Offset;///< Offset in "bufferView" where compressed data are stored.
size_t Count;///< Count of elements in compressed data. Is always equivalent to size in bytes: look comments for "Type" and "Component_Type".
bool Binary;///< If true then "binary" mode is used for coding, if false - "ascii" mode.
size_t IndicesCount;///< Count of indices in mesh.
size_t VerticesCount;///< Count of vertices in mesh.
// AttribType::Value Type;///< Is always "SCALAR".
// ComponentType Component_Type;///< Is always "ComponentType_UNSIGNED_BYTE" (5121).
/// \fn SCompression_Open3DGC
/// Constructor.
SCompression_Open3DGC()
: SExtension(Compression_Open3DGC) {
// empty
}
virtual ~SCompression_Open3DGC() {
// empty
}
};
#endif
std::vector<Primitive> primitives;
std::list<SExtension*> Extension;///< List of extensions used in mesh.
Mesh() {}
/// \fn ~Mesh()
/// Destructor.
~Mesh() { for(std::list<SExtension*>::iterator it = Extension.begin(), it_end = Extension.end(); it != it_end; it++) { delete *it; }; }
/// \fn void Read(Value& pJSON_Object, Asset& pAsset_Root)
/// Get mesh data from JSON-object and place them to root asset.
/// \param [in] pJSON_Object - reference to pJSON-object from which data are read.
/// \param [out] pAsset_Root - reference to root assed where data will be stored.
void Read(Value& pJSON_Object, Asset& pAsset_Root);
#ifdef ASSIMP_IMPORTER_GLTF_USE_OPEN3DGC
/// \fn void Decode_O3DGC(const SCompression_Open3DGC& pCompression_Open3DGC, Asset& pAsset_Root)
/// Decode part of "buffer" which encoded with Open3DGC algorithm.
/// \param [in] pCompression_Open3DGC - reference to structure which describe encoded region.
/// \param [out] pAsset_Root - reference to root assed where data will be stored.
void Decode_O3DGC(const SCompression_Open3DGC& pCompression_Open3DGC, Asset& pAsset_Root);
#endif
};
struct Node : public Object
{
std::vector< Ref<Node> > children;
std::vector< Ref<Mesh> > meshes;
Ref<Mesh> mesh;
Nullable<mat4> matrix;
Nullable<vec3> translation;
@ -822,10 +789,9 @@ namespace glTF2
Nullable<vec3> scale;
Ref<Camera> camera;
Ref<Light> light;
std::vector< Ref<Node> > skeletons; //!< The ID of skeleton nodes. Each of which is the root of a node hierarchy.
Ref<Skin> skin; //!< The ID of the skin referenced by this node.
Ref<Skin> skin; //!< The ID of the skin referenced by this node.
std::string jointName; //!< Name used when this node is a joint in a skin.
Ref<Node> parent; //!< This is not part of the glTF specification. Used as a helper.
@ -843,12 +809,12 @@ namespace glTF2
struct Sampler : public Object
{
SamplerMagFilter magFilter; //!< The texture magnification filter. (required)
SamplerMinFilter minFilter; //!< The texture minification filter. (required)
SamplerWrap wrapS; //!< The texture wrapping in the S direction. (required)
SamplerWrap wrapT; //!< The texture wrapping in the T direction. (required)
SamplerMagFilter magFilter; //!< The texture magnification filter.
SamplerMinFilter minFilter; //!< The texture minification filter.
SamplerWrap wrapS; //!< The texture wrapping in the S direction.
SamplerWrap wrapT; //!< The texture wrapping in the T direction.
Sampler() {}
Sampler() { SetDefaults(); }
void Read(Value& obj, Asset& r);
void SetDefaults();
};
@ -878,27 +844,6 @@ namespace glTF2
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
{
@ -915,35 +860,6 @@ namespace glTF2
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();
};
struct Animation : public Object
{
struct AnimSampler {
@ -1025,14 +941,16 @@ namespace glTF2
friend class Asset;
friend class AssetWriter;
typedef typename std::gltf_unordered_map< std::string, unsigned int > Dict;
typedef typename std::gltf_unordered_map< unsigned int, unsigned int > Dict;
typedef typename std::gltf_unordered_map< std::string, unsigned int > IdDict;
std::vector<T*> mObjs; //! The read objects
Dict mObjsById; //! The read objects accessible 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
std::vector<T*> mObjs; //! The read objects
Dict mObjsByOIndex; //! The read objects accessible by original index
IdDict mObjsById; //! The read objects accessible 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();
@ -1046,9 +964,10 @@ namespace glTF2
LazyDict(Asset& asset, const char* dictId, const char* extId = 0);
~LazyDict();
Ref<T> Get(const char* id);
Ref<T> Retrieve(unsigned int i);
Ref<T> Get(unsigned int i);
Ref<T> Get(const std::string& pID) { return Get(pID.c_str()); }
Ref<T> Get(const char* id);
Ref<T> Create(const char* id);
Ref<T> Create(const std::string& id)
@ -1067,22 +986,17 @@ namespace glTF2
{
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: {})
float version; //!< The glTF format version
std::string version; //!< The glTF format version
void Read(Document& doc);
AssetMetadata()
: premultipliedAlpha(false)
, version(0)
{
}
AssetMetadata() : version("") {}
};
//
@ -1123,8 +1037,7 @@ namespace glTF2
//! Keeps info about the enabled extensions
struct Extensions
{
bool KHR_binary_glTF;
bool KHR_materials_common;
bool KHR_materials_pbrSpecularGlossiness;
} extensionsUsed;
@ -1142,16 +1055,11 @@ namespace glTF2
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<Skin> skins;
LazyDict<Texture> textures;
LazyDict<Light> lights; // KHR_materials_common ext
Ref<Scene> scene;
public:
@ -1167,23 +1075,16 @@ namespace glTF2
, 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")
, skins (*this, "skins")
, textures (*this, "textures")
, lights (*this, "lights", "KHR_materials_common")
{
memset(&extensionsUsed, 0, sizeof(extensionsUsed));
}
//! Main function
void Load(const std::string& file, bool isBinary = false);
//! Enables the "KHR_binary_glTF" extension on the asset
void SetAsBinary();
void Load(const std::string& file);
//! Search for an available name, starting from the given strings
std::string FindUniqueID(const std::string& str, const char* suffix);
@ -1192,11 +1093,8 @@ namespace glTF2
{ return mBodyBuffer; }
private:
void ReadBinaryHeader(IOStream& stream);
void ReadExtensionsUsed(Document& doc);
IOStream* OpenFile(std::string path, const char* mode, bool absolute = false);
};

View File

@ -1,4 +1,4 @@
/*
/*
Open Asset Import Library (assimp)
----------------------------------------------------------------------
@ -44,11 +44,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Header files, Assimp
#include <assimp/DefaultLogger.hpp>
#ifdef ASSIMP_IMPORTER_GLTF_USE_OPEN3DGC
// Header files, Open3DGC.
# include <Open3DGC/o3dgcSC3DMCDecoder.h>
#endif
using namespace Assimp;
namespace glTF2 {
@ -128,6 +123,18 @@ namespace {
return (it != val.MemberEnd() && it->value.IsString()) ? &it->value : 0;
}
inline Value* FindNumber(Value& val, const char* id)
{
Value::MemberIterator it = val.FindMember(id);
return (it != val.MemberEnd() && it->value.IsNumber()) ? &it->value : 0;
}
inline Value* FindUInt(Value& val, const char* id)
{
Value::MemberIterator it = val.FindMember(id);
return (it != val.MemberEnd() && it->value.IsUint()) ? &it->value : 0;
}
inline Value* FindArray(Value& val, const char* id)
{
Value::MemberIterator it = val.FindMember(id);
@ -176,7 +183,7 @@ inline void LazyDict<T>::AttachToDocument(Document& doc)
}
if (container) {
mDict = FindObject(*container, mDictId);
mDict = FindArray(*container, mDictId);
}
}
@ -187,18 +194,11 @@ inline void LazyDict<T>::DetachFromDocument()
}
template<class T>
Ref<T> LazyDict<T>::Get(unsigned int i)
Ref<T> LazyDict<T>::Retrieve(unsigned int i)
{
return Ref<T>(mObjs, i);
}
template<class T>
Ref<T> LazyDict<T>::Get(const char* id)
{
id = T::TranslateId(mAsset, id);
typename Dict::iterator it = mObjsById.find(id);
if (it != mObjsById.end()) { // already created?
typename Dict::iterator it = mObjsByOIndex.find(i);
if (it != mObjsByOIndex.end()) {// already created?
return Ref<T>(mObjs, it->second);
}
@ -207,27 +207,52 @@ Ref<T> LazyDict<T>::Get(const char* id)
throw DeadlyImportError("GLTF: Missing section \"" + std::string(mDictId) + "\"");
}
Value::MemberIterator obj = mDict->FindMember(id);
if (obj == mDict->MemberEnd()) {
throw DeadlyImportError("GLTF: Missing object with id \"" + std::string(id) + "\" in \"" + mDictId + "\"");
}
if (!obj->value.IsObject()) {
throw DeadlyImportError("GLTF: Object with id \"" + std::string(id) + "\" is not a JSON object");
if (!mDict->IsArray()) {
throw DeadlyImportError("GLTF: Field is not an array \"" + std::string(mDictId) + "\"");
}
Value &obj = (*mDict)[i];
if (!obj.IsObject()) {
throw DeadlyImportError("GLTF: Object at index \"" + std::to_string(i) + "\" 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);
inst->id = std::string(mDictId) + "_" + std::to_string(i);
inst->oIndex = i;
ReadMember(obj, "name", inst->name);
inst->Read(obj, mAsset);
return Add(inst);
}
template<class T>
Ref<T> LazyDict<T>::Get(unsigned int i)
{
return Ref<T>(mObjs, i);
}
template<class T>
Ref<T> LazyDict<T>::Get(const char* id)
{
id = T::TranslateId(mAsset, id);
typename IdDict::iterator it = mObjsById.find(id);
if (it != mObjsById.end()) { // already created?
return Ref<T>(mObjs, it->second);
}
return Ref<T>();
}
template<class T>
Ref<T> LazyDict<T>::Add(T* obj)
{
unsigned int idx = unsigned(mObjs.size());
mObjs.push_back(obj);
mObjsByOIndex[obj->oIndex] = idx;
mObjsById[obj->id] = idx;
mAsset.mUsedIds[obj->id] = true;
return Ref<T>(mObjs, idx);
@ -241,8 +266,10 @@ Ref<T> LazyDict<T>::Create(const char* id)
throw DeadlyImportError("GLTF: two objects with the same ID exist");
}
T* inst = new T();
unsigned int idx = unsigned(mObjs.size());
inst->id = id;
inst->index = static_cast<int>(mObjs.size());
inst->index = idx;
inst->oIndex = idx;
return Add(inst);
}
@ -263,11 +290,6 @@ inline Buffer::~Buffer()
inline const char* Buffer::TranslateId(Asset& r, const char* id)
{
// Compatibility with old spec
if (r.extensionsUsed.KHR_binary_glTF && strcmp(id, "KHR_binary_glTF") == 0) {
return "binary_glTF";
}
return id;
}
@ -310,7 +332,9 @@ inline void Buffer::Read(Value& obj, Asset& r)
}
else { // Local file
if (byteLength > 0) {
IOStream* file = r.OpenFile(uri, "rb");
std::string dir = !r.mCurrentAssetDir.empty() ? (r.mCurrentAssetDir + "/") : "";
IOStream* file = r.OpenFile(dir + uri, "rb");
if (file) {
bool ok = LoadFromStream(*file, byteLength);
delete file;
@ -437,9 +461,9 @@ inline void Buffer::Grow(size_t 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);
if (Value* bufferVal = FindUInt(obj, "buffer")) {
buffer = r.buffers.Retrieve(bufferVal->GetUint());
}
byteOffset = MemberOrDefault(obj, "byteOffset", 0u);
@ -452,9 +476,9 @@ inline void BufferView::Read(Value& obj, Asset& r)
inline void Accessor::Read(Value& obj, Asset& r)
{
const char* bufferViewId = MemberOrDefault<const char*>(obj, "bufferView", 0);
if (bufferViewId) {
bufferView = r.bufferViews.Get(bufferViewId);
if (Value* bufferViewVal = FindUInt(obj, "bufferView")) {
bufferView = r.bufferViews.Retrieve(bufferViewVal->GetUint());
}
byteOffset = MemberOrDefault(obj, "byteOffset", 0u);
@ -601,29 +625,6 @@ inline Image::Image()
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")) {
width = MemberOrDefault(*ext, "width", 0);
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();
@ -671,6 +672,7 @@ inline void Sampler::Read(Value& obj, Asset& r)
{
SetDefaults();
ReadMember(obj, "name", name);
ReadMember(obj, "magFilter", magFilter);
ReadMember(obj, "minFilter", minFilter);
ReadMember(obj, "wrapS", wrapS);
@ -679,34 +681,61 @@ inline void Sampler::Read(Value& obj, Asset& r)
inline void Sampler::SetDefaults()
{
magFilter = SamplerMagFilter_Linear;
minFilter = SamplerMinFilter_Linear;
wrapS = SamplerWrap_Repeat;
wrapT = SamplerWrap_Repeat;
//only wrapping modes have defaults
wrapS = SamplerWrap::Repeat;
wrapT = SamplerWrap::Repeat;
magFilter = SamplerMagFilter::UNSET;
minFilter = SamplerMinFilter::UNSET;
}
inline void Texture::Read(Value& obj, Asset& r)
{
const char* sourcestr;
if (ReadMember(obj, "source", sourcestr)) {
source = r.images.Get(sourcestr);
if (Value* sourceVal = FindUInt(obj, "source")) {
source = r.images.Retrieve(sourceVal->GetUint());
}
const char* samplerstr;
if (ReadMember(obj, "sampler", samplerstr)) {
sampler = r.samplers.Get(samplerstr);
if (Value* samplerVal = FindUInt(obj, "sampler")) {
sampler = r.samplers.Retrieve(samplerVal->GetUint());
}
}
namespace {
inline void ReadMaterialProperty(Asset& r, Value& vals, const char* propName, TexProperty& out)
inline void SetTextureProperties(Asset& r, Value* prop, TextureInfo& out)
{
if (Value* index = FindUInt(*prop, "index")) {
out.texture = r.textures.Retrieve(index->GetUint());
}
if (Value* texcoord = FindUInt(*prop, "texCoord")) {
out.texCoord = texcoord->GetUint();
}
}
inline void ReadTextureProperty(Asset& r, Value& vals, const char* propName, TextureInfo& out)
{
if (Value* prop = FindMember(vals, propName)) {
if (prop->IsString()) {
out.texture = r.textures.Get(prop->GetString());
SetTextureProperties(r, prop, out);
}
}
inline void ReadTextureProperty(Asset& r, Value& vals, const char* propName, NormalTextureInfo& out)
{
if (Value* prop = FindMember(vals, propName)) {
SetTextureProperties(r, prop, out);
if (Value* scale = FindNumber(*prop, "scale")) {
out.scale = scale->GetDouble();
}
else {
ReadValue(*prop, out.color);
}
}
inline void ReadTextureProperty(Asset& r, Value& vals, const char* propName, OcclusionTextureInfo& out)
{
if (Value* prop = FindMember(vals, propName)) {
SetTextureProperties(r, prop, out);
if (Value* strength = FindNumber(*prop, "strength")) {
out.strength = strength->GetDouble();
}
}
}
@ -716,59 +745,67 @@ 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, "transparency", transparency);
ReadMember(*values, "shininess", shininess);
if (Value* pbrMetallicRoughness = FindObject(material, "pbrMetallicRoughness")) {
ReadMember(*pbrMetallicRoughness, "baseColorFactor", this->pbrMetallicRoughness.baseColorFactor);
ReadTextureProperty(r, *pbrMetallicRoughness, "baseColorTexture", this->pbrMetallicRoughness.baseColorTexture);
ReadTextureProperty(r, *pbrMetallicRoughness, "metallicRoughnessTexture", this->pbrMetallicRoughness.metallicRoughnessTexture);
ReadMember(*pbrMetallicRoughness, "metallicFactor", this->pbrMetallicRoughness.metallicFactor);
ReadMember(*pbrMetallicRoughness, "roughnessFactor", this->pbrMetallicRoughness.roughnessFactor);
}
ReadTextureProperty(r, material, "normalTexture", this->normalTexture);
ReadTextureProperty(r, material, "occlusionTexture", this->occlusionTexture);
ReadTextureProperty(r, material, "emissiveTexture", this->emissiveTexture);
ReadMember(material, "emissiveFactor", this->emissiveFactor);
ReadMember(material, "doubleSided", this->doubleSided);
ReadMember(material, "alphaMode", this->alphaMode);
ReadMember(material, "alphaCutoff", this->alphaCutoff);
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;
}
if (r.extensionsUsed.KHR_materials_pbrSpecularGlossiness) {
if (Value* pbrSpecularGlossiness = FindObject(*extensions, "KHR_materials_pbrSpecularGlossiness")) {
PbrSpecularGlossiness pbrSG;
if (Value* values = FindObject(*ext, "values")) {
ReadMaterialProperty(r, *values, "ambient", this->ambient);
ReadMaterialProperty(r, *values, "diffuse", this->diffuse);
ReadMaterialProperty(r, *values, "specular", this->specular);
ReadMember(*pbrSpecularGlossiness, "diffuseFactor", pbrSG.diffuseFactor);
ReadTextureProperty(r, *pbrSpecularGlossiness, "diffuseTexture", pbrSG.diffuseTexture);
ReadTextureProperty(r, *pbrSpecularGlossiness, "specularGlossinessTexture", pbrSG.specularGlossinessTexture);
ReadMember(*pbrSpecularGlossiness, "specularFactor", pbrSG.specularFactor);
ReadMember(*pbrSpecularGlossiness, "glossinessFactor", pbrSG.glossinessFactor);
ReadMember(*values, "doubleSided", doubleSided);
ReadMember(*values, "transparent", transparent);
ReadMember(*values, "transparency", transparency);
ReadMember(*values, "shininess", shininess);
}
this->pbrSpecularGlossiness = Nullable<PbrSpecularGlossiness>(pbrSG);
}
}
}
}
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; }
void SetVector(vec4& v, const float(&in)[4])
{ v[0] = in[0]; v[1] = in[1]; v[2] = in[2]; v[3] = in[3]; }
void SetVector(vec3& v, const float(&in)[3])
{ v[0] = in[0]; v[1] = in[1]; v[2] = in[2]; }
}
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);
//pbr materials
SetVector(pbrMetallicRoughness.baseColorFactor, defaultBaseColor);
pbrMetallicRoughness.metallicFactor = 1.0;
pbrMetallicRoughness.roughnessFactor = 1.0;
SetVector(emissiveFactor, defaultEmissiveFactor);
alphaMode = "OPAQUE";
alphaCutoff = 0.5;
doubleSided = false;
transparent = false;
transparency = 1.0;
shininess = 0.0;
}
technique = Technique_undefined;
inline void PbrSpecularGlossiness::SetDefaults()
{
//pbrSpecularGlossiness properties
SetVector(diffuseFactor, defaultDiffuseFactor);
SetVector(specularFactor, defaultSpecularFactor);
glossinessFactor = 1.0;
}
namespace {
@ -808,6 +845,10 @@ namespace {
inline void Mesh::Read(Value& pJSON_Object, Asset& pAsset_Root)
{
if (Value* name = FindMember(pJSON_Object, "name")) {
this->name = name->GetString();
}
/****************** Mesh primitives ******************/
if (Value* primitives = FindArray(pJSON_Object, "primitives")) {
this->primitives.resize(primitives->Size());
@ -819,7 +860,7 @@ inline void Mesh::Read(Value& pJSON_Object, Asset& pAsset_Root)
if (Value* attrs = FindObject(primitive, "attributes")) {
for (Value::MemberIterator it = attrs->MemberBegin(); it != attrs->MemberEnd(); ++it) {
if (!it->value.IsString()) continue;
if (!it->value.IsUint()) 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.
@ -829,262 +870,22 @@ inline void Mesh::Read(Value& pJSON_Object, Asset& pAsset_Root)
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] = pAsset_Root.accessors.Get(it->value.GetString());
(*vec)[idx] = pAsset_Root.accessors.Retrieve(it->value.GetUint());
}
}
}
if (Value* indices = FindString(primitive, "indices")) {
prim.indices = pAsset_Root.accessors.Get(indices->GetString());
if (Value* indices = FindUInt(primitive, "indices")) {
prim.indices = pAsset_Root.accessors.Retrieve(indices->GetUint());
}
if (Value* material = FindString(primitive, "material")) {
prim.material = pAsset_Root.materials.Get(material->GetString());
if (Value* material = FindUInt(primitive, "material")) {
prim.material = pAsset_Root.materials.Retrieve(material->GetUint());
}
}
}
/****************** Mesh extensions ******************/
Value* json_extensions = FindObject(pJSON_Object, "extensions");
if(json_extensions == nullptr) goto mr_skip_extensions;
for(Value::MemberIterator it_memb = json_extensions->MemberBegin(); it_memb != json_extensions->MemberEnd(); it_memb++)
{
#ifdef ASSIMP_IMPORTER_GLTF_USE_OPEN3DGC
if(it_memb->name.GetString() == std::string("Open3DGC-compression"))
{
// Search for compressed data.
// Compressed data contain description of part of "buffer" which is encoded. This part must be decoded and
// new data will replace old encoded part by request. In fact \"compressedData\" is kind of "accessor" structure.
Value* comp_data = FindObject(it_memb->value, "compressedData");
if(comp_data == nullptr) throw DeadlyImportError("GLTF: \"Open3DGC-compression\" must has \"compressedData\".");
DefaultLogger::get()->info("GLTF: Decompressing Open3DGC data.");
/************** Read data from JSON-document **************/
#define MESH_READ_COMPRESSEDDATA_MEMBER(pFieldName, pOut) \
if(!ReadMember(*comp_data, pFieldName, pOut)) \
{ \
throw DeadlyImportError(std::string("GLTF: \"compressedData\" must has \"") + pFieldName + "\"."); \
}
const char* mode_str;
const char* type_str;
ComponentType component_type;
SCompression_Open3DGC* ext_o3dgc = new SCompression_Open3DGC;
MESH_READ_COMPRESSEDDATA_MEMBER("buffer", ext_o3dgc->Buffer);
MESH_READ_COMPRESSEDDATA_MEMBER("byteOffset", ext_o3dgc->Offset);
MESH_READ_COMPRESSEDDATA_MEMBER("componentType", component_type);
MESH_READ_COMPRESSEDDATA_MEMBER("type", type_str);
MESH_READ_COMPRESSEDDATA_MEMBER("count", ext_o3dgc->Count);
MESH_READ_COMPRESSEDDATA_MEMBER("mode", mode_str);
MESH_READ_COMPRESSEDDATA_MEMBER("indicesCount", ext_o3dgc->IndicesCount);
MESH_READ_COMPRESSEDDATA_MEMBER("verticesCount", ext_o3dgc->VerticesCount);
#undef MESH_READ_COMPRESSEDDATA_MEMBER
// Check some values
if(strcmp(type_str, "SCALAR")) throw DeadlyImportError("GLTF: only \"SCALAR\" type is supported for compressed data.");
if(component_type != ComponentType_UNSIGNED_BYTE) throw DeadlyImportError("GLTF: only \"UNSIGNED_BYTE\" component type is supported for compressed data.");
// Set read/write data mode.
if(strcmp(mode_str, "binary") == 0)
ext_o3dgc->Binary = true;
else if(strcmp(mode_str, "ascii") == 0)
ext_o3dgc->Binary = false;
else
throw DeadlyImportError(std::string("GLTF: for compressed data supported modes is: \"ascii\", \"binary\". Not the: \"") + mode_str + "\".");
/************************ Decoding ************************/
Decode_O3DGC(*ext_o3dgc, pAsset_Root);
Extension.push_back(ext_o3dgc);// store info in mesh extensions list.
}// if(it_memb->name.GetString() == "Open3DGC-compression")
else
#endif
{
throw DeadlyImportError(std::string("GLTF: Unknown mesh extension: \"") + it_memb->name.GetString() + "\".");
}
}// for(Value::MemberIterator it_memb = json_extensions->MemberBegin(); it_memb != json_extensions->MemberEnd(); json_extensions++)
mr_skip_extensions:
return;// After label some operators must be present.
}
#ifdef ASSIMP_IMPORTER_GLTF_USE_OPEN3DGC
inline void Mesh::Decode_O3DGC(const SCompression_Open3DGC& pCompression_Open3DGC, Asset& pAsset_Root)
{
typedef unsigned short IndicesType;///< \sa glTFExporter::ExportMeshes.
o3dgc::SC3DMCDecoder<IndicesType> decoder;
o3dgc::IndexedFaceSet<IndicesType> ifs;
o3dgc::BinaryStream bstream;
uint8_t* decoded_data;
size_t decoded_data_size = 0;
Ref<Buffer> buf = pAsset_Root.buffers.Get(pCompression_Open3DGC.Buffer);
// Read data from buffer and place it in BinaryStream for decoder.
// Just "Count" because always is used type equivalent to uint8_t.
bstream.LoadFromBuffer(&buf->GetPointer()[pCompression_Open3DGC.Offset], static_cast<unsigned long>(pCompression_Open3DGC.Count));
// After decoding header we can get size of primitives.
if(decoder.DecodeHeader(ifs, bstream) != o3dgc::O3DGC_OK) throw DeadlyImportError("GLTF: can not decode Open3DGC header.");
/****************** Get sizes of arrays and check sizes ******************/
// Note. See "Limitations for meshes when using Open3DGC-compression".
// Indices
size_t size_coordindex = ifs.GetNCoordIndex() * 3;// See float attributes note.
if(primitives[0].indices->count != size_coordindex)
throw DeadlyImportError("GLTF: Open3DGC. Compressed indices count (" + std::to_string(size_coordindex) +
") not equal to uncompressed (" + std::to_string(primitives[0].indices->count) + ").");
size_coordindex *= sizeof(IndicesType);
// Coordinates
size_t size_coord = ifs.GetNCoord();// See float attributes note.
if(primitives[0].attributes.position[0]->count != size_coord)
throw DeadlyImportError("GLTF: Open3DGC. Compressed positions count (" + std::to_string(size_coord) +
") not equal to uncompressed (" + std::to_string(primitives[0].attributes.position[0]->count) + ").");
size_coord *= 3 * sizeof(float);
// Normals
size_t size_normal = ifs.GetNNormal();// See float attributes note.
if(primitives[0].attributes.normal[0]->count != size_normal)
throw DeadlyImportError("GLTF: Open3DGC. Compressed normals count (" + std::to_string(size_normal) +
") not equal to uncompressed (" + std::to_string(primitives[0].attributes.normal[0]->count) + ").");
size_normal *= 3 * sizeof(float);
// Additional attributes.
std::vector<size_t> size_floatattr;
std::vector<size_t> size_intattr;
size_floatattr.resize(ifs.GetNumFloatAttributes());
size_intattr.resize(ifs.GetNumIntAttributes());
decoded_data_size = size_coordindex + size_coord + size_normal;
for(size_t idx = 0, idx_end = size_floatattr.size(), idx_texcoord = 0; idx < idx_end; idx++)
{
// size = number_of_elements * components_per_element * size_of_component.
// Note. But as you can see above, at first we are use this variable in meaning "count". After checking count of objects...
size_t tval = ifs.GetNFloatAttribute(static_cast<unsigned long>(idx));
switch(ifs.GetFloatAttributeType(static_cast<unsigned long>(idx)))
{
case o3dgc::O3DGC_IFS_FLOAT_ATTRIBUTE_TYPE_TEXCOORD:
// Check situation when encoded data contain texture coordinates but primitive not.
if(idx_texcoord < primitives[0].attributes.texcoord.size())
{
if(primitives[0].attributes.texcoord[idx]->count != tval)
throw DeadlyImportError("GLTF: Open3DGC. Compressed texture coordinates count (" + std::to_string(tval) +
") not equal to uncompressed (" + std::to_string(primitives[0].attributes.texcoord[idx]->count) + ").");
idx_texcoord++;
}
else
{
ifs.SetNFloatAttribute(static_cast<unsigned long>(idx), 0ul);// Disable decoding this attribute.
}
break;
default:
throw DeadlyImportError("GLTF: Open3DGC. Unsupported type of float attribute: " + to_string(ifs.GetFloatAttributeType(static_cast<unsigned long>(idx))));
}
tval *= ifs.GetFloatAttributeDim(static_cast<unsigned long>(idx)) * sizeof(o3dgc::Real);// After checking count of objects we can get size of array.
size_floatattr[idx] = tval;
decoded_data_size += tval;
}
for(size_t idx = 0, idx_end = size_intattr.size(); idx < idx_end; idx++)
{
// size = number_of_elements * components_per_element * size_of_component. See float attributes note.
size_t tval = ifs.GetNIntAttribute(static_cast<unsigned long>(idx));
switch( ifs.GetIntAttributeType(static_cast<unsigned long>(idx) ) )
{
case o3dgc::O3DGC_IFS_INT_ATTRIBUTE_TYPE_UNKOWN:
case o3dgc::O3DGC_IFS_INT_ATTRIBUTE_TYPE_INDEX:
case o3dgc::O3DGC_IFS_INT_ATTRIBUTE_TYPE_JOINT_ID:
case o3dgc::O3DGC_IFS_INT_ATTRIBUTE_TYPE_INDEX_BUFFER_ID:
break;
default:
throw DeadlyImportError("GLTF: Open3DGC. Unsupported type of int attribute: " + to_string(ifs.GetIntAttributeType(static_cast<unsigned long>(idx))));
}
tval *= ifs.GetIntAttributeDim(static_cast<unsigned long>(idx)) * sizeof(long);// See float attributes note.
size_intattr[idx] = tval;
decoded_data_size += tval;
}
// Create array for decoded data.
decoded_data = new uint8_t[decoded_data_size];
/****************** Set right array regions for decoder ******************/
auto get_buf_offset = [](Ref<Accessor>& pAccessor) -> size_t { return pAccessor->byteOffset + pAccessor->bufferView->byteOffset; };
// Indices
ifs.SetCoordIndex((IndicesType* const)(decoded_data + get_buf_offset(primitives[0].indices)));
// Coordinates
ifs.SetCoord((o3dgc::Real* const)(decoded_data + get_buf_offset(primitives[0].attributes.position[0])));
// Normals
if(size_normal)
{
ifs.SetNormal((o3dgc::Real* const)(decoded_data + get_buf_offset(primitives[0].attributes.normal[0])));
}
for(size_t idx = 0, idx_end = size_floatattr.size(), idx_texcoord = 0; idx < idx_end; idx++)
{
switch(ifs.GetFloatAttributeType(static_cast<unsigned long>(idx)))
{
case o3dgc::O3DGC_IFS_FLOAT_ATTRIBUTE_TYPE_TEXCOORD:
if(idx_texcoord < primitives[0].attributes.texcoord.size())
{
// See above about absent attributes.
ifs.SetFloatAttribute(static_cast<unsigned long>(idx), (o3dgc::Real* const)(decoded_data + get_buf_offset(primitives[0].attributes.texcoord[idx])));
idx_texcoord++;
}
break;
default:
throw DeadlyImportError("GLTF: Open3DGC. Unsupported type of float attribute: " + to_string(ifs.GetFloatAttributeType(static_cast<unsigned long>(idx))));
}
}
for(size_t idx = 0, idx_end = size_intattr.size(); idx < idx_end; idx++) {
switch(ifs.GetIntAttributeType(static_cast<unsigned int>(idx))) {
case o3dgc::O3DGC_IFS_INT_ATTRIBUTE_TYPE_UNKOWN:
case o3dgc::O3DGC_IFS_INT_ATTRIBUTE_TYPE_INDEX:
case o3dgc::O3DGC_IFS_INT_ATTRIBUTE_TYPE_JOINT_ID:
case o3dgc::O3DGC_IFS_INT_ATTRIBUTE_TYPE_INDEX_BUFFER_ID:
break;
// ifs.SetIntAttribute(idx, (long* const)(decoded_data + get_buf_offset(primitives[0].attributes.joint)));
default:
throw DeadlyImportError("GLTF: Open3DGC. Unsupported type of int attribute: " + to_string(ifs.GetIntAttributeType(static_cast<unsigned long>(idx))));
}
}
//
// Decode data
//
if ( decoder.DecodePayload( ifs, bstream ) != o3dgc::O3DGC_OK ) {
throw DeadlyImportError( "GLTF: can not decode Open3DGC data." );
}
// Set encoded region for "buffer".
buf->EncodedRegion_Mark(pCompression_Open3DGC.Offset, pCompression_Open3DGC.Count, decoded_data, decoded_data_size, id);
// No. Do not delete "output_data". After calling "EncodedRegion_Mark" bufferView is owner of "output_data".
// "delete [] output_data;"
}
#endif
inline void Camera::Read(Value& obj, Asset& r)
{
type = MemberOrDefault(obj, "type", Camera::Perspective);
@ -1095,80 +896,34 @@ inline void Camera::Read(Value& obj, Asset& r)
if (!it) throw DeadlyImportError("GLTF: 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);
cameraProperties.perspective.aspectRatio = MemberOrDefault(*it, "aspectRatio", 0.f);
cameraProperties.perspective.yfov = MemberOrDefault(*it, "yfov", 3.1415f/2.f);
cameraProperties.perspective.zfar = MemberOrDefault(*it, "zfar", 100.f);
cameraProperties.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);
cameraProperties.ortographic.xmag = MemberOrDefault(obj, "xmag", 1.f);
cameraProperties.ortographic.ymag = MemberOrDefault(obj, "ymag", 1.f);
cameraProperties.ortographic.zfar = MemberOrDefault(obj, "zfar", 100.f);
cameraProperties.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()) {
if (child.IsUint()) {
// get/create the child node
Ref<Node> chn = r.nodes.Get(child.GetString());
Ref<Node> chn = r.nodes.Retrieve(child.GetUint());
if (chn) this->children.push_back(chn);
}
}
}
if (Value* matrix = FindArray(obj, "matrix")) {
ReadValue(*matrix, this->matrix);
}
@ -1178,64 +933,46 @@ inline void Node::Read(Value& obj, Asset& r)
ReadMember(obj, "rotation", rotation);
}
if (Value* meshes = FindArray(obj, "meshes")) {
unsigned numMeshes = (unsigned)meshes->Size();
if (Value* mesh = FindUInt(obj, "mesh")) {
Ref<Mesh> meshRef = r.meshes.Retrieve((*mesh).GetUint());
std::vector<unsigned int> meshList;
this->meshes.reserve(numMeshes);
for (unsigned 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 (meshRef) this->mesh = meshRef;
}
if (Value* camera = FindString(obj, "camera")) {
this->camera = r.cameras.Get(camera->GetString());
if (Value* camera = FindUInt(obj, "camera")) {
this->camera = r.cameras.Retrieve(camera->GetUint());
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 (!(*array)[i].IsUint()) continue;
Ref<Node> node = r.nodes.Retrieve((*array)[i].GetUint());
if (node)
this->nodes.push_back(node);
}
}
}
inline void AssetMetadata::Read(Document& doc)
{
// read the version, etc.
float 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* versionString = FindString(*obj, "version")) {
version = versionString->GetString();
} else if (Value* versionNumber = FindNumber (*obj, "version")) {
char buf[4];
ai_snprintf(buf, 4, "%.1f", versionNumber->GetDouble());
version = buf;
}
if (Value* profile = FindObject(*obj, "profile")) {
ReadMember(*profile, "api", this->profile.api);
@ -1243,60 +980,16 @@ inline void AssetMetadata::Read(Document& doc)
}
}
version = std::max(statedVersion, version);
if (version == 0) {
// if missing version, we'll assume version 1.0...
version = 1;
}
if (version != 1) {
char msg[128];
ai_snprintf(msg, 128, "GLTF: Unsupported glTF version: %.1f", version);
throw DeadlyImportError(msg);
if (version.empty() || version[0] != '2') {
throw DeadlyImportError("GLTF: Unsupported glTF version: " + version);
}
}
//
// Asset methods implementation
//
inline void Asset::ReadBinaryHeader(IOStream& stream)
{
GLB_Header header;
if (stream.Read(&header, sizeof(header), 1) != 1) {
throw DeadlyImportError("GLTF: Unable to read the file header");
}
if (strncmp((char*)header.magic, AI_GLB_MAGIC_NUMBER, sizeof(header.magic)) != 0) {
throw DeadlyImportError("GLTF: Invalid binary glTF file");
}
AI_SWAP4(header.version);
asset.version = header.version;
if (header.version != 1) {
throw DeadlyImportError("GLTF: Unsupported binary glTF version");
}
AI_SWAP4(header.sceneFormat);
if (header.sceneFormat != SceneFormat_JSON) {
throw DeadlyImportError("GLTF: 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)
inline void Asset::Load(const std::string& pFile)
{
mCurrentAssetDir.clear();
int pos = std::max(int(pFile.rfind('/')), int(pFile.rfind('\\')));
@ -1307,16 +1000,8 @@ inline void Asset::Load(const std::string& pFile, bool isBinary)
throw DeadlyImportError("GLTF: 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;
}
mSceneLength = stream->FileSize();
mBodyLength = 0;
// read the scene data
@ -1361,12 +1046,14 @@ inline void Asset::Load(const std::string& pFile, bool isBinary)
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());
if (Value* scene = FindUInt(doc, "scene")) {
unsigned int sceneIndex = scene->GetUint();
Ref<Scene> s = scenes.Retrieve(sceneIndex);
this->scene = s;
}
// Clean up
@ -1375,16 +1062,6 @@ inline void Asset::Load(const std::string& pFile, bool isBinary)
}
}
inline void Asset::SetAsBinary()
{
if (!extensionsUsed.KHR_binary_glTF) {
extensionsUsed.KHR_binary_glTF = true;
mBodyBuffer = buffers.Create("binary_glTF");
mBodyBuffer->MarkAsSpecial();
}
}
inline void Asset::ReadExtensionsUsed(Document& doc)
{
Value* extsUsed = FindArray(doc, "extensionsUsed");
@ -1401,8 +1078,7 @@ inline void Asset::ReadExtensionsUsed(Document& doc)
#define CHECK_EXT(EXT) \
if (exts.find(#EXT) != exts.end()) extensionsUsed.EXT = true;
CHECK_EXT(KHR_binary_glTF);
CHECK_EXT(KHR_materials_common);
CHECK_EXT(KHR_materials_pbrSpecularGlossiness);
#undef CHECK_EXT
}

View File

@ -43,8 +43,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* Declares a class to write gltf/glb files
*
* glTF Extensions Support:
* KHR_binary_glTF: full
* KHR_materials_common: full
* KHR_materials_pbrSpecularGlossiness: full
*/
#ifndef GLTF2ASSETWRITER_H_INC
#define GLTF2ASSETWRITER_H_INC
@ -82,7 +81,6 @@ public:
AssetWriter(Asset& asset);
void WriteFile(const char* path);
void WriteGLBFile(const char* path);
};
}

View File

@ -72,6 +72,12 @@ namespace glTF2 {
return val;
}
inline Value& MakeValue(Value& val, float r, MemoryPoolAllocator<>& al) {
val.SetDouble(r);
return val;
}
template<class T>
inline void AddRefsVector(Value& obj, const char* fieldId, std::vector< Ref<T> >& v, MemoryPoolAllocator<>& al) {
if (v.empty()) return;
@ -91,7 +97,11 @@ namespace glTF2 {
{
obj.AddMember("bufferView", a.bufferView->index, w.mAl);
obj.AddMember("byteOffset", a.byteOffset, w.mAl);
obj.AddMember("byteStride", a.byteStride, w.mAl);
if (a.byteStride != 0) {
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);
@ -169,21 +179,7 @@ namespace glTF2 {
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", img.bufferView->index, 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()) {
if (img.HasData()) {
uri = "data:" + (img.mimeType.empty() ? "application/octet-stream" : img.mimeType);
uri += ";base64,";
Util::EncodeBase64(img.GetData(), img.GetDataLength(), uri);
@ -196,51 +192,149 @@ namespace glTF2 {
}
namespace {
inline void WriteTex(Value& obj, Ref<Texture> texture, const char* propName, MemoryPoolAllocator<>& al)
inline void SetTexBasic(TextureInfo t, Value& tex, MemoryPoolAllocator<>& al)
{
if (texture) {
tex.SetObject();
tex.AddMember("index", t.texture->index, al);
if (t.texCoord != 0) {
tex.AddMember("texCoord", t.texCoord, al);
}
}
inline void WriteTex(Value& obj, TextureInfo t, const char* propName, MemoryPoolAllocator<>& al)
{
if (t.texture) {
Value tex;
tex.SetObject();
tex.AddMember("index", texture->index, al);
SetTexBasic(t, tex, al);
obj.AddMember(StringRef(propName), tex, al);
}
}
inline void WriteColorOrTex(Value& obj, TexProperty& prop, const char* propName, MemoryPoolAllocator<>& al)
inline void WriteTex(Value& obj, NormalTextureInfo t, const char* propName, MemoryPoolAllocator<>& al)
{
WriteTex(obj, prop.texture, propName, al);
if (!prop.texture) {
Value col;
obj.AddMember(StringRef(propName), MakeValue(col, prop.color, al), al);
if (t.texture) {
Value tex;
SetTexBasic(t, tex, al);
if (t.scale != 1) {
tex.AddMember("scale", t.scale, al);
}
obj.AddMember(StringRef(propName), tex, al);
}
}
inline void WriteTex(Value& obj, OcclusionTextureInfo t, const char* propName, MemoryPoolAllocator<>& al)
{
if (t.texture) {
Value tex;
SetTexBasic(t, tex, al);
if (t.strength != 1) {
tex.AddMember("strength", t.strength, al);
}
obj.AddMember(StringRef(propName), tex, al);
}
}
template<size_t N>
inline void WriteVec(Value& obj, float(&prop)[N], const char* propName, MemoryPoolAllocator<>& al)
{
Value arr;
obj.AddMember(StringRef(propName), MakeValue(arr, prop, al), al);
}
template<size_t N>
inline void WriteVec(Value& obj, float(&prop)[N], const char* propName, const float(&defaultVal)[N], MemoryPoolAllocator<>& al)
{
if (!std::equal(std::begin(prop), std::end(prop), std::begin(defaultVal))) {
WriteVec(obj, prop, propName, al);
}
}
inline void WriteFloat(Value& obj, float prop, const char* propName, MemoryPoolAllocator<>& al)
{
Value num;
obj.AddMember(StringRef(propName), MakeValue(num, prop, al), al);
}
}
inline void Write(Value& obj, Material& m, AssetWriter& w)
{
if (m.transparent) {
obj.AddMember("alphaMode", "BLEND", w.mAl);
}
Value v;
v.SetObject();
Value pbrMetallicRoughness;
pbrMetallicRoughness.SetObject();
{
if (m.transparent && !m.diffuse.texture) {
m.diffuse.color[3] = m.transparency;
}
WriteColorOrTex(v, m.ambient, m.ambient.texture ? "ambientTexture" : "ambientFactor", w.mAl);
WriteColorOrTex(v, m.diffuse, m.diffuse.texture ? "diffuseTexture" : "diffuseFactor", w.mAl);
WriteColorOrTex(v, m.specular, m.specular.texture ? "specularTexture" : "specularFactor", w.mAl);
WriteColorOrTex(v, m.emission, m.emission.texture ? "emissionTexture" : "emissionFactor", w.mAl);
v.AddMember("shininessFactor", m.shininess, w.mAl);
}
v.AddMember("type", "commonPhong", w.mAl);
Value ext;
ext.SetObject();
ext.AddMember("KHR_materials_common", v, w.mAl);
obj.AddMember("extensions", ext, w.mAl);
WriteTex(pbrMetallicRoughness, m.pbrMetallicRoughness.baseColorTexture, "baseColorTexture", w.mAl);
WriteTex(pbrMetallicRoughness, m.pbrMetallicRoughness.metallicRoughnessTexture, "metallicRoughnessTexture", w.mAl);
WriteVec(pbrMetallicRoughness, m.pbrMetallicRoughness.baseColorFactor, "baseColorFactor", defaultBaseColor, w.mAl);
WriteTex(obj, m.normal, "normalTexture", w.mAl);
if (m.pbrMetallicRoughness.metallicFactor != 1) {
WriteFloat(pbrMetallicRoughness, m.pbrMetallicRoughness.metallicFactor, "metallicFactor", w.mAl);
}
if (m.pbrMetallicRoughness.roughnessFactor != 1) {
WriteFloat(pbrMetallicRoughness, m.pbrMetallicRoughness.roughnessFactor, "roughnessFactor", w.mAl);
}
}
if (!pbrMetallicRoughness.ObjectEmpty()) {
obj.AddMember("pbrMetallicRoughness", pbrMetallicRoughness, w.mAl);
}
WriteTex(obj, m.normalTexture, "normalTexture", w.mAl);
WriteTex(obj, m.emissiveTexture, "emissiveTexture", w.mAl);
WriteTex(obj, m.occlusionTexture, "occlusionTexture", w.mAl);
WriteVec(obj, m.emissiveFactor, "emissiveFactor", defaultEmissiveFactor, w.mAl);
if (m.alphaCutoff != 0.5) {
WriteFloat(obj, m.alphaCutoff, "alphaCutoff", w.mAl);
}
if (m.alphaMode != "OPAQUE") {
obj.AddMember("alphaMode", Value(m.alphaMode, w.mAl).Move(), w.mAl);
}
if (m.doubleSided) {
obj.AddMember("doubleSided", m.doubleSided, w.mAl);
}
Value exts;
exts.SetObject();
if (m.pbrSpecularGlossiness.isPresent) {
Value pbrSpecularGlossiness;
pbrSpecularGlossiness.SetObject();
PbrSpecularGlossiness &pbrSG = m.pbrSpecularGlossiness.value;
//pbrSpecularGlossiness
WriteVec(pbrSpecularGlossiness, pbrSG.diffuseFactor, "diffuseFactor", defaultDiffuseFactor, w.mAl);
WriteVec(pbrSpecularGlossiness, pbrSG.specularFactor, "specularFactor", defaultSpecularFactor, w.mAl);
if (pbrSG.glossinessFactor != 1) {
WriteFloat(obj, pbrSG.glossinessFactor, "glossinessFactor", w.mAl);
}
WriteTex(pbrSpecularGlossiness, pbrSG.diffuseTexture, "diffuseTexture", w.mAl);
WriteTex(pbrSpecularGlossiness, pbrSG.specularGlossinessTexture, "specularGlossinessTexture", w.mAl);
if (!pbrSpecularGlossiness.ObjectEmpty()) {
exts.AddMember("KHR_materials_pbrSpecularGlossiness", pbrSpecularGlossiness, w.mAl);
}
}
if (!exts.ObjectEmpty()) {
obj.AddMember("extensions", exts, w.mAl);
}
}
namespace {
@ -263,59 +357,6 @@ namespace glTF2 {
inline void Write(Value& obj, Mesh& m, AssetWriter& w)
{
/********************* Name **********************/
obj.AddMember("name", m.name, w.mAl);
/**************** Mesh extensions ****************/
if(m.Extension.size() > 0)
{
Value json_extensions;
json_extensions.SetObject();
for(Mesh::SExtension* ptr_ext : m.Extension)
{
switch(ptr_ext->Type)
{
#ifdef ASSIMP_IMPORTER_GLTF_USE_OPEN3DGC
case Mesh::SExtension::EType::Compression_Open3DGC:
{
Value json_comp_data;
Mesh::SCompression_Open3DGC* ptr_ext_comp = (Mesh::SCompression_Open3DGC*)ptr_ext;
// filling object "compressedData"
json_comp_data.SetObject();
json_comp_data.AddMember("buffer", ptr_ext_comp->Buffer, w.mAl);
json_comp_data.AddMember("byteOffset", ptr_ext_comp->Offset, w.mAl);
json_comp_data.AddMember("componentType", 5121, w.mAl);
json_comp_data.AddMember("type", "SCALAR", w.mAl);
json_comp_data.AddMember("count", ptr_ext_comp->Count, w.mAl);
if(ptr_ext_comp->Binary)
json_comp_data.AddMember("mode", "binary", w.mAl);
else
json_comp_data.AddMember("mode", "ascii", w.mAl);
json_comp_data.AddMember("indicesCount", ptr_ext_comp->IndicesCount, w.mAl);
json_comp_data.AddMember("verticesCount", ptr_ext_comp->VerticesCount, w.mAl);
// filling object "Open3DGC-compression"
Value json_o3dgc;
json_o3dgc.SetObject();
json_o3dgc.AddMember("compressedData", json_comp_data, w.mAl);
// add member to object "extensions"
json_extensions.AddMember("Open3DGC-compression", json_o3dgc, w.mAl);
}
break;
#endif
default:
throw DeadlyImportError("GLTF: Can not write mesh: unknown mesh extension, only Open3DGC is supported.");
}// switch(ptr_ext->Type)
}// for(Mesh::SExtension* ptr_ext : m.Extension)
// Add extensions to mesh
obj.AddMember("extensions", json_extensions, w.mAl);
}// if(m.Extension.size() > 0)
/****************** Primitives *******************/
Value primitives;
primitives.SetArray();
@ -340,10 +381,9 @@ namespace glTF2 {
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");
WriteAttrs(w, attrs, p.attributes.color, "COLOR", true);
WriteAttrs(w, attrs, p.attributes.joint, "JOINTS", true);
WriteAttrs(w, attrs, p.attributes.weight, "WEIGHTS", true);
}
prim.AddMember("attributes", attrs, w.mAl);
}
@ -377,7 +417,9 @@ namespace glTF2 {
AddRefsVector(obj, "children", n.children, w.mAl);
AddRefsVector(obj, "meshes", n.meshes, w.mAl);
if (n.mesh) {
obj.AddMember("mesh", n.mesh->index, w.mAl);
}
AddRefsVector(obj, "skeletons", n.skeletons, w.mAl);
@ -397,17 +439,24 @@ namespace glTF2 {
inline void Write(Value& obj, Sampler& b, AssetWriter& w)
{
if (b.wrapS) {
obj.AddMember("wrapS", b.wrapS, w.mAl);
if (!b.name.empty()) {
obj.AddMember("name", b.name, w.mAl);
}
if (b.wrapT) {
obj.AddMember("wrapT", b.wrapT, w.mAl);
if (b.wrapS != SamplerWrap::UNSET && b.wrapS != SamplerWrap::Repeat) {
obj.AddMember("wrapS", static_cast<unsigned int>(b.wrapS), w.mAl);
}
if (b.magFilter) {
obj.AddMember("magFilter", b.magFilter, w.mAl);
if (b.wrapT != SamplerWrap::UNSET && b.wrapT != SamplerWrap::Repeat) {
obj.AddMember("wrapT", static_cast<unsigned int>(b.wrapT), w.mAl);
}
if (b.minFilter) {
obj.AddMember("minFilter", b.minFilter, w.mAl);
if (b.magFilter != SamplerMagFilter::UNSET) {
obj.AddMember("magFilter", static_cast<unsigned int>(b.magFilter), w.mAl);
}
if (b.minFilter != SamplerMinFilter::UNSET) {
obj.AddMember("minFilter", static_cast<unsigned int>(b.minFilter), w.mAl);
}
}
@ -444,11 +493,6 @@ namespace glTF2 {
}
inline void Write(Value& obj, Technique& b, AssetWriter& w)
{
}
inline void Write(Value& obj, Texture& tex, AssetWriter& w)
{
if (tex.source) {
@ -459,11 +503,6 @@ namespace glTF2 {
}
}
inline void Write(Value& obj, Light& b, AssetWriter& w)
{
}
inline AssetWriter::AssetWriter(Asset& a)
: mDoc()
@ -523,88 +562,12 @@ namespace glTF2 {
}
}
inline void AssetWriter::WriteGLBFile(const char* path)
{
std::unique_ptr<IOStream> outfile(mAsset.OpenFile(path, "wb", true));
if (outfile == 0) {
throw DeadlyExportError("Could not open output file: " + std::string(path));
}
// we will write the header later, skip its size
outfile->Seek(sizeof(GLB_Header), aiOrigin_SET);
StringBuffer docBuffer;
Writer<StringBuffer> writer(docBuffer);
mDoc.Accept(writer);
if (outfile->Write(docBuffer.GetString(), docBuffer.GetSize(), 1) != 1) {
throw DeadlyExportError("Failed to write scene data!");
}
WriteBinaryData(outfile.get(), docBuffer.GetSize());
}
inline 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 = 2;
AI_SWAP4(header.version);
header.length = uint32_t(sizeof(header) + sceneLength + bodyLength);
AI_SWAP4(header.length);
header.sceneLength = uint32_t(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!");
}
}
inline void AssetWriter::WriteMetadata()
{
Value asset;
asset.SetObject();
{
char versionChar[10];
ai_snprintf(versionChar, sizeof(versionChar), "%.1f", mAsset.asset.version);
asset.AddMember("version", Value(versionChar, mAl).Move(), mAl);
asset.AddMember("generator", Value(mAsset.asset.generator, mAl).Move(), mAl);
}
asset.AddMember("version", Value(mAsset.asset.version, mAl).Move(), mAl);
asset.AddMember("generator", Value(mAsset.asset.generator, mAl).Move(), mAl);
mDoc.AddMember("asset", asset, mAl);
}
@ -613,11 +576,10 @@ namespace glTF2 {
Value exts;
exts.SetArray();
{
if (false)
exts.PushBack(StringRef("KHR_binary_glTF"), mAl);
// This is used to export common materials with GLTF 2.
exts.PushBack(StringRef("KHR_materials_common"), mAl);
// This is used to export pbrSpecularGlossiness materials with GLTF 2.
if (this->mAsset.extensionsUsed.KHR_materials_pbrSpecularGlossiness) {
exts.PushBack(StringRef("KHR_materials_pbrSpecularGlossiness"), mAl);
}
}
if (!exts.Empty())

View File

@ -62,11 +62,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "glTF2AssetWriter.h"
#ifdef ASSIMP_IMPORTER_GLTF_USE_OPEN3DGC
// Header files, Open3DGC.
# include <Open3DGC/o3dgcSC3DMCEncoder.h>
#endif
using namespace rapidjson;
using namespace Assimp;
@ -106,16 +101,8 @@ glTF2Exporter::glTF2Exporter(const char* filename, IOSystem* pIOSystem, const ai
mAsset.reset( new Asset( pIOSystem ) );
if (isBinary) {
mAsset->SetAsBinary();
}
ExportMetadata();
//for (unsigned int i = 0; i < pScene->mNumCameras; ++i) {}
//for (unsigned int i = 0; i < pScene->mNumLights; ++i) {}
ExportMaterials();
if (mScene->mRootNode) {
@ -124,19 +111,13 @@ glTF2Exporter::glTF2Exporter(const char* filename, IOSystem* pIOSystem, const ai
ExportMeshes();
//for (unsigned int i = 0; i < pScene->mNumTextures; ++i) {}
ExportScene();
ExportAnimations();
AssetWriter writer(*mAsset);
if (isBinary) {
writer.WriteGLBFile(filename);
} else {
writer.WriteFile(filename);
}
writer.WriteFile(filename);
}
/*
@ -234,63 +215,86 @@ inline Ref<Accessor> ExportData(Asset& a, std::string& meshName, Ref<Buffer>& bu
return acc;
}
namespace {
void GetMatScalar(const aiMaterial* mat, float& val, const char* propName, int type, int idx) {
if (mat->Get(propName, type, idx, val) == AI_SUCCESS) {}
inline void SetSamplerWrap(SamplerWrap& wrap, aiTextureMapMode map)
{
switch (map) {
case aiTextureMapMode_Clamp:
wrap = SamplerWrap::Clamp_To_Edge;
break;
case aiTextureMapMode_Mirror:
wrap = SamplerWrap::Mirrored_Repeat;
break;
case aiTextureMapMode_Wrap:
case aiTextureMapMode_Decal:
default:
wrap = SamplerWrap::Repeat;
break;
};
}
void glTF2Exporter::GetTexSampler(const aiMaterial* mat, Ref<Texture> texture, aiTextureType tt, unsigned int slot)
{
aiString aId;
std::string id;
if (aiGetMaterialString(mat, AI_MATKEY_GLTF_MAPPINGID(tt, slot), &aId) == AI_SUCCESS) {
id = aId.C_Str();
}
if (Ref<Sampler> ref = mAsset->samplers.Get(id.c_str())) {
texture->sampler = ref;
} else {
id = mAsset->FindUniqueID(id, "sampler");
texture->sampler = mAsset->samplers.Create(id.c_str());
aiTextureMapMode mapU, mapV;
SamplerMagFilter filterMag;
SamplerMinFilter filterMin;
if (aiGetMaterialInteger(mat, AI_MATKEY_MAPPINGMODE_U(tt, slot), (int*)&mapU) == AI_SUCCESS) {
SetSamplerWrap(texture->sampler->wrapS, mapU);
}
if (aiGetMaterialInteger(mat, AI_MATKEY_MAPPINGMODE_V(tt, slot), (int*)&mapV) == AI_SUCCESS) {
SetSamplerWrap(texture->sampler->wrapT, mapV);
}
if (aiGetMaterialInteger(mat, AI_MATKEY_GLTF_MAPPINGFILTER_MAG(tt, slot), (int*)&filterMag) == AI_SUCCESS) {
texture->sampler->magFilter = filterMag;
}
if (aiGetMaterialInteger(mat, AI_MATKEY_GLTF_MAPPINGFILTER_MIN(tt, slot), (int*)&filterMin) == AI_SUCCESS) {
texture->sampler->minFilter = filterMin;
}
aiString name;
if (aiGetMaterialString(mat, AI_MATKEY_GLTF_MAPPINGNAME(tt, slot), &name) == AI_SUCCESS) {
texture->sampler->name = name.C_Str();
}
}
}
void glTF2Exporter::GetTexSampler(const aiMaterial* mat, Ref<Texture> texture)
void glTF2Exporter::GetMatTexProp(const aiMaterial* mat, unsigned int& prop, const char* propName, aiTextureType tt, unsigned int slot)
{
std::string samplerId = mAsset->FindUniqueID("", "sampler");
texture->sampler = mAsset->samplers.Create(samplerId);
std::string textureKey = std::string(_AI_MATKEY_TEXTURE_BASE) + "." + propName;
aiTextureMapMode mapU, mapV;
aiGetMaterialInteger(mat,AI_MATKEY_MAPPINGMODE_U_DIFFUSE(0),(int*)&mapU);
aiGetMaterialInteger(mat,AI_MATKEY_MAPPINGMODE_V_DIFFUSE(0),(int*)&mapV);
switch (mapU) {
case aiTextureMapMode_Wrap:
texture->sampler->wrapS = SamplerWrap_Repeat;
break;
case aiTextureMapMode_Clamp:
texture->sampler->wrapS = SamplerWrap_Clamp_To_Edge;
break;
case aiTextureMapMode_Mirror:
texture->sampler->wrapS = SamplerWrap_Mirrored_Repeat;
break;
case aiTextureMapMode_Decal:
default:
texture->sampler->wrapS = SamplerWrap_Repeat;
break;
};
switch (mapV) {
case aiTextureMapMode_Wrap:
texture->sampler->wrapT = SamplerWrap_Repeat;
break;
case aiTextureMapMode_Clamp:
texture->sampler->wrapT = SamplerWrap_Clamp_To_Edge;
break;
case aiTextureMapMode_Mirror:
texture->sampler->wrapT = SamplerWrap_Mirrored_Repeat;
break;
case aiTextureMapMode_Decal:
default:
texture->sampler->wrapT = SamplerWrap_Repeat;
break;
};
// Hard coded Texture filtering options because I do not know where to find them in the aiMaterial.
texture->sampler->magFilter = SamplerMagFilter_Linear;
texture->sampler->minFilter = SamplerMinFilter_Linear_Mipmap_Linear;
mat->Get(textureKey.c_str(), tt, slot, prop);
}
void glTF2Exporter::GetMatTex(const aiMaterial* mat, Ref<Texture>& texture, aiTextureType tt)
void glTF2Exporter::GetMatTexProp(const aiMaterial* mat, float& prop, const char* propName, aiTextureType tt, unsigned int slot)
{
aiString tex;
std::string textureKey = std::string(_AI_MATKEY_TEXTURE_BASE) + "." + propName;
mat->Get(textureKey.c_str(), tt, slot, prop);
}
void glTF2Exporter::GetMatTex(const aiMaterial* mat, Ref<Texture>& texture, aiTextureType tt, unsigned int slot = 0)
{
if (mat->GetTextureCount(tt) > 0) {
if (mat->Get(AI_MATKEY_TEXTURE(tt, 0), tex) == AI_SUCCESS) {
aiString tex;
if (mat->Get(AI_MATKEY_TEXTURE(tt, slot), tex) == AI_SUCCESS) {
std::string path = tex.C_Str();
if (path.size() > 0) {
@ -325,22 +329,63 @@ void glTF2Exporter::GetMatTex(const aiMaterial* mat, Ref<Texture>& texture, aiTe
texture->source->uri = path;
}
GetTexSampler(mat, texture);
GetTexSampler(mat, texture, tt, slot);
}
}
}
}
}
void glTF2Exporter::GetMatColorOrTex(const aiMaterial* mat, TexProperty& prop, const char* propName, int type, int idx, aiTextureType tt)
void glTF2Exporter::GetMatTex(const aiMaterial* mat, TextureInfo& prop, aiTextureType tt, unsigned int slot = 0)
{
Ref<Texture>& texture = prop.texture;
GetMatTex(mat, texture, tt, slot);
if (texture) {
GetMatTexProp(mat, prop.texCoord, "texCoord", tt, slot);
}
}
void glTF2Exporter::GetMatTex(const aiMaterial* mat, NormalTextureInfo& prop, aiTextureType tt, unsigned int slot = 0)
{
Ref<Texture>& texture = prop.texture;
GetMatTex(mat, texture, tt, slot);
if (texture) {
GetMatTexProp(mat, prop.texCoord, "texCoord", tt, slot);
GetMatTexProp(mat, prop.scale, "scale", tt, slot);
}
}
void glTF2Exporter::GetMatTex(const aiMaterial* mat, OcclusionTextureInfo& prop, aiTextureType tt, unsigned int slot = 0)
{
Ref<Texture>& texture = prop.texture;
GetMatTex(mat, texture, tt, slot);
if (texture) {
GetMatTexProp(mat, prop.texCoord, "texCoord", tt, slot);
GetMatTexProp(mat, prop.strength, "strength", tt, slot);
}
}
void glTF2Exporter::GetMatColor(const aiMaterial* mat, vec4& prop, const char* propName, int type, int idx)
{
aiColor4D col;
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;
prop[0] = col.r; prop[1] = col.g; prop[2] = col.b; prop[3] = col.a;
}
GetMatTex(mat, prop.texture, tt);
}
void glTF2Exporter::GetMatColor(const aiMaterial* mat, vec3& prop, const char* propName, int type, int idx)
{
aiColor3D col;
if (mat->Get(propName, type, idx, col) == AI_SUCCESS) {
prop[0] = col.r; prop[1] = col.g; prop[2] = col.b;
}
}
void glTF2Exporter::ExportMaterials()
{
@ -348,6 +393,9 @@ void glTF2Exporter::ExportMaterials()
for (unsigned int i = 0; i < mScene->mNumMaterials; ++i) {
const aiMaterial* mat = mScene->mMaterials[i];
std::string id = "material_" + std::to_string(i);
Ref<Material> m = mAsset->materials.Create(id);
std::string name;
if (mat->Get(AI_MATKEY_NAME, aiName) == AI_SUCCESS) {
@ -355,17 +403,70 @@ void glTF2Exporter::ExportMaterials()
}
name = mAsset->FindUniqueID(name, "material");
Ref<Material> m = mAsset->materials.Create(name);
m->name = name;
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);
GetMatTex(mat, m->normal, aiTextureType_NORMALS);
GetMatTex(mat, m->pbrMetallicRoughness.baseColorTexture, aiTextureType_DIFFUSE);
GetMatTex(mat, m->pbrMetallicRoughness.metallicRoughnessTexture, AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLICROUGHNESS_TEXTURE);
GetMatColor(mat, m->pbrMetallicRoughness.baseColorFactor, AI_MATKEY_COLOR_DIFFUSE);
m->transparent = mat->Get(AI_MATKEY_OPACITY, m->transparency) == aiReturn_SUCCESS && m->transparency != 1.0;
if (mat->Get(AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLIC_FACTOR, m->pbrMetallicRoughness.metallicFactor) != AI_SUCCESS) {
//if metallicFactor wasn't defined, then the source is likely not a PBR file, and the metallicFactor should be 0
m->pbrMetallicRoughness.metallicFactor = 0;
}
GetMatScalar(mat, m->shininess, AI_MATKEY_SHININESS);
mat->Get(AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_ROUGHNESS_FACTOR, m->pbrMetallicRoughness.roughnessFactor);
GetMatTex(mat, m->normalTexture, aiTextureType_NORMALS);
GetMatTex(mat, m->occlusionTexture, aiTextureType_LIGHTMAP);
GetMatTex(mat, m->emissiveTexture, aiTextureType_EMISSIVE);
GetMatColor(mat, m->emissiveFactor, AI_MATKEY_COLOR_EMISSIVE);
mat->Get(AI_MATKEY_TWOSIDED, m->doubleSided);
mat->Get(AI_MATKEY_GLTF_ALPHACUTOFF, m->alphaCutoff);
bool foundAlphaMode = false;
for (size_t i = 0; i < mat->mNumProperties; ++i) {
aiMaterialProperty *prop = mat->mProperties[i];
if (prop->mKey != aiString("$mat.gltf.alphaMode"))
continue;
std::string alphaMode;
for (size_t c = 0; c < prop->mDataLength; ++c)
alphaMode += prop->mData[c];
m->alphaMode = alphaMode;
foundAlphaMode = true;
}
if (!foundAlphaMode) {
float opacity;
if (mat->Get(AI_MATKEY_OPACITY, opacity) == AI_SUCCESS) {
if (opacity < 1) {
m->alphaMode = "MASK";
m->pbrMetallicRoughness.baseColorFactor[3] *= opacity;
}
}
}
bool hasPbrSpecularGlossiness = false;
mat->Get(AI_MATKEY_GLTF_PBRSPECULARGLOSSINESS, hasPbrSpecularGlossiness);
if (hasPbrSpecularGlossiness) {
if (!mAsset->extensionsUsed.KHR_materials_pbrSpecularGlossiness) {
mAsset->extensionsUsed.KHR_materials_pbrSpecularGlossiness = true;
}
PbrSpecularGlossiness pbrSG;
GetMatColor(mat, pbrSG.diffuseFactor, AI_MATKEY_GLTF_PBRSPECULARGLOSSINESS_DIFFUSE_FACTOR);
GetMatColor(mat, pbrSG.specularFactor, AI_MATKEY_GLTF_PBRSPECULARGLOSSINESS_SPECULAR_FACTOR);
mat->Get(AI_MATKEY_GLTF_PBRSPECULARGLOSSINESS_GLOSSINESS_FACTOR, pbrSG.glossinessFactor);
GetMatTex(mat, pbrSG.diffuseTexture, AI_MATKEY_GLTF_PBRSPECULARGLOSSINESS_DIFFUSE_TEXTURE);
GetMatTex(mat, pbrSG.specularGlossinessTexture, AI_MATKEY_GLTF_PBRSPECULARGLOSSINESS_SPECULARGLOSSINESS_TEXTURE);
m->pbrSpecularGlossiness = Nullable<PbrSpecularGlossiness>(pbrSG);
}
}
}
@ -375,16 +476,15 @@ void glTF2Exporter::ExportMaterials()
*/
bool FindMeshNode(Ref<Node>& nodeIn, Ref<Node>& meshNode, std::string meshID)
{
for (unsigned int i = 0; i < nodeIn->meshes.size(); ++i) {
if (meshID.compare(nodeIn->meshes[i]->id) == 0) {
meshNode = nodeIn;
return true;
}
if (nodeIn->mesh && meshID.compare(nodeIn->mesh->id) == 0) {
meshNode = nodeIn;
return true;
}
for (unsigned int i = 0; i < nodeIn->children.size(); ++i) {
if(FindMeshNode(nodeIn->children[i], meshNode, meshID)) {
return true;
return true;
}
}
@ -502,15 +602,6 @@ void glTF2Exporter::ExportMeshes()
// because "ComponentType_UNSIGNED_SHORT" used for indices. And it's a maximal type according to glTF specification.
typedef unsigned short IndicesType;
// Variables needed for compression. BEGIN.
// Indices, not pointers - because pointer to buffer is changing while writing to it.
size_t idx_srcdata_begin;// Index of buffer before writing mesh data. Also, index of begin of coordinates array in buffer.
size_t idx_srcdata_normal = SIZE_MAX;// Index of begin of normals array in buffer. SIZE_MAX - mean that mesh has no normals.
std::vector<size_t> idx_srcdata_tc;// Array of indices. Every index point to begin of texture coordinates array in buffer.
size_t idx_srcdata_ind;// Index of begin of coordinates indices array in buffer.
bool comp_allow;// Point that data of current mesh can be compressed.
// Variables needed for compression. END.
std::string fname = std::string(mFilename);
std::string bufferIdPrefix = fname.substr(0, fname.rfind(".gltf"));
std::string bufferId = mAsset->FindUniqueID("", bufferIdPrefix.c_str());
@ -543,48 +634,22 @@ void glTF2Exporter::ExportMeshes()
for (unsigned int idx_mesh = 0; idx_mesh < mScene->mNumMeshes; ++idx_mesh) {
const aiMesh* aim = mScene->mMeshes[idx_mesh];
// Check if compressing requested and mesh can be encoded.
#ifdef ASSIMP_IMPORTER_GLTF_USE_OPEN3DGC
comp_allow = mProperties->GetPropertyBool("extensions.Open3DGC.use", false);
#else
comp_allow = false;
#endif
std::string name = aim->mName.C_Str();
if(comp_allow && (aim->mPrimitiveTypes == aiPrimitiveType_TRIANGLE) && (aim->mNumVertices > 0) && (aim->mNumFaces > 0))
{
idx_srcdata_tc.clear();
idx_srcdata_tc.reserve(AI_MAX_NUMBER_OF_TEXTURECOORDS);
}
else
{
std::string msg;
if(aim->mPrimitiveTypes != aiPrimitiveType_TRIANGLE)
msg = "all primitives of the mesh must be a triangles.";
else
msg = "mesh must has vertices and faces.";
DefaultLogger::get()->warn("GLTF: can not use Open3DGC-compression: " + msg);
comp_allow = false;
}
std::string meshId = mAsset->FindUniqueID(aim->mName.C_Str(), "mesh");
std::string meshId = mAsset->FindUniqueID(name, "mesh");
Ref<Mesh> m = mAsset->meshes.Create(meshId);
m->primitives.resize(1);
Mesh::Primitive& p = m->primitives.back();
m->name = name;
p.material = mAsset->materials.Get(aim->mMaterialIndex);
/******************* Vertices ********************/
// If compression is used then you need parameters of uncompressed region: begin and size. At this step "begin" is stored.
if(comp_allow) idx_srcdata_begin = b->byteLength;
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);
/******************** Normals ********************/
if(comp_allow && (aim->mNormals != 0)) idx_srcdata_normal = b->byteLength;// Store index of normals array.
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);
@ -600,16 +665,12 @@ void glTF2Exporter::ExportMeshes()
if (aim->mNumUVComponents[i] > 0) {
AttribType::Value type = (aim->mNumUVComponents[i] == 2) ? AttribType::VEC2 : AttribType::VEC3;
if(comp_allow) idx_srcdata_tc.push_back(b->byteLength);// Store index of texture coordinates array.
Ref<Accessor> tc = ExportData(*mAsset, meshId, b, aim->mNumVertices, aim->mTextureCoords[i], AttribType::VEC3, type, ComponentType_FLOAT, false);
if (tc) p.attributes.texcoord.push_back(tc);
}
}
/*************** Vertices indices ****************/
idx_srcdata_ind = b->byteLength;// Store index of indices array.
if (aim->mNumFaces > 0) {
std::vector<IndicesType> indices;
unsigned int nIndicesPerFace = aim->mFaces[0].mNumIndices;
@ -634,104 +695,12 @@ void glTF2Exporter::ExportMeshes()
p.mode = PrimitiveMode_TRIANGLES;
}
/*************** Skins ****************/
if(aim->HasBones()) {
ExportSkin(*mAsset, aim, m, b, skinRef, inverseBindMatricesData);
/*************** Skins ****************/
if(aim->HasBones()) {
ExportSkin(*mAsset, aim, m, b, skinRef, inverseBindMatricesData);
}
}
/****************** Compression ******************/
///TODO: animation: weights, joints.
if(comp_allow)
{
#ifdef ASSIMP_IMPORTER_GLTF_USE_OPEN3DGC
// Only one type of compression supported at now - Open3DGC.
//
o3dgc::BinaryStream bs;
o3dgc::SC3DMCEncoder<IndicesType> encoder;
o3dgc::IndexedFaceSet<IndicesType> comp_o3dgc_ifs;
o3dgc::SC3DMCEncodeParams comp_o3dgc_params;
//
// Fill data for encoder.
//
// Quantization
unsigned quant_coord = mProperties->GetPropertyInteger("extensions.Open3DGC.quantization.POSITION", 12);
unsigned quant_normal = mProperties->GetPropertyInteger("extensions.Open3DGC.quantization.NORMAL", 10);
unsigned quant_texcoord = mProperties->GetPropertyInteger("extensions.Open3DGC.quantization.TEXCOORD", 10);
// Prediction
o3dgc::O3DGCSC3DMCPredictionMode prediction_position = o3dgc::O3DGC_SC3DMC_PARALLELOGRAM_PREDICTION;
o3dgc::O3DGCSC3DMCPredictionMode prediction_normal = o3dgc::O3DGC_SC3DMC_SURF_NORMALS_PREDICTION;
o3dgc::O3DGCSC3DMCPredictionMode prediction_texcoord = o3dgc::O3DGC_SC3DMC_PARALLELOGRAM_PREDICTION;
// IndexedFacesSet: "Crease angle", "solid", "convex" are set to default.
comp_o3dgc_ifs.SetCCW(true);
comp_o3dgc_ifs.SetIsTriangularMesh(true);
comp_o3dgc_ifs.SetNumFloatAttributes(0);
// Coordinates
comp_o3dgc_params.SetCoordQuantBits(quant_coord);
comp_o3dgc_params.SetCoordPredMode(prediction_position);
comp_o3dgc_ifs.SetNCoord(aim->mNumVertices);
comp_o3dgc_ifs.SetCoord((o3dgc::Real* const)&b->GetPointer()[idx_srcdata_begin]);
// Normals
if(idx_srcdata_normal != SIZE_MAX)
{
comp_o3dgc_params.SetNormalQuantBits(quant_normal);
comp_o3dgc_params.SetNormalPredMode(prediction_normal);
comp_o3dgc_ifs.SetNNormal(aim->mNumVertices);
comp_o3dgc_ifs.SetNormal((o3dgc::Real* const)&b->GetPointer()[idx_srcdata_normal]);
}
// Texture coordinates
for(size_t num_tc = 0; num_tc < idx_srcdata_tc.size(); num_tc++)
{
size_t num = comp_o3dgc_ifs.GetNumFloatAttributes();
comp_o3dgc_params.SetFloatAttributeQuantBits(static_cast<unsigned long>(num), quant_texcoord);
comp_o3dgc_params.SetFloatAttributePredMode(static_cast<unsigned long>(num), prediction_texcoord);
comp_o3dgc_ifs.SetNFloatAttribute(static_cast<unsigned long>(num), aim->mNumVertices);// number of elements.
comp_o3dgc_ifs.SetFloatAttributeDim(static_cast<unsigned long>(num), aim->mNumUVComponents[num_tc]);// components per element: aiVector3D => x * float
comp_o3dgc_ifs.SetFloatAttributeType(static_cast<unsigned long>(num), o3dgc::O3DGC_IFS_FLOAT_ATTRIBUTE_TYPE_TEXCOORD);
comp_o3dgc_ifs.SetFloatAttribute(static_cast<unsigned long>(num), (o3dgc::Real* const)&b->GetPointer()[idx_srcdata_tc[num_tc]]);
comp_o3dgc_ifs.SetNumFloatAttributes(static_cast<unsigned long>(num + 1));
}
// Coordinates indices
comp_o3dgc_ifs.SetNCoordIndex(aim->mNumFaces);
comp_o3dgc_ifs.SetCoordIndex((IndicesType* const)&b->GetPointer()[idx_srcdata_ind]);
// Prepare to enconding
comp_o3dgc_params.SetNumFloatAttributes(comp_o3dgc_ifs.GetNumFloatAttributes());
if(mProperties->GetPropertyBool("extensions.Open3DGC.binary", true))
comp_o3dgc_params.SetStreamType(o3dgc::O3DGC_STREAM_TYPE_BINARY);
else
comp_o3dgc_params.SetStreamType(o3dgc::O3DGC_STREAM_TYPE_ASCII);
comp_o3dgc_ifs.ComputeMinMax(o3dgc::O3DGC_SC3DMC_MAX_ALL_DIMS);
//
// Encoding
//
encoder.Encode(comp_o3dgc_params, comp_o3dgc_ifs, bs);
// Replace data in buffer.
b->ReplaceData(idx_srcdata_begin, b->byteLength - idx_srcdata_begin, bs.GetBuffer(), bs.GetSize());
//
// Add information about extension to mesh.
//
// Create extension structure.
Mesh::SCompression_Open3DGC* ext = new Mesh::SCompression_Open3DGC;
// Fill it.
ext->Buffer = b->id;
ext->Offset = idx_srcdata_begin;
ext->Count = b->byteLength - idx_srcdata_begin;
ext->Binary = mProperties->GetPropertyBool("extensions.Open3DGC.binary");
ext->IndicesCount = comp_o3dgc_ifs.GetNCoordIndex() * 3;
ext->VerticesCount = comp_o3dgc_ifs.GetNCoord();
// And assign to mesh.
m->Extension.push_back(ext);
#endif
}// if(comp_allow)
}// for (unsigned int i = 0; i < mScene->mNumMeshes; ++i)
//----------------------------------------
// Finish the skin
// Create the Accessor for skinRef->inverseBindMatrices
@ -786,8 +755,8 @@ unsigned int glTF2Exporter::ExportNodeHierarchy(const aiNode* n)
CopyValue(n->mTransformation, node->matrix.value);
}
for (unsigned int i = 0; i < n->mNumMeshes; ++i) {
node->meshes.push_back(mAsset->meshes.Get(n->mMeshes[i]));
if (n->mNumMeshes > 0) {
node->mesh = mAsset->meshes.Get(n->mMeshes[0]);
}
for (unsigned int i = 0; i < n->mNumChildren; ++i) {
@ -815,8 +784,8 @@ unsigned int glTF2Exporter::ExportNode(const aiNode* n, Ref<Node>& parent)
CopyValue(n->mTransformation, node->matrix.value);
}
for (unsigned int i = 0; i < n->mNumMeshes; ++i) {
node->meshes.push_back(mAsset->meshes.Get(n->mMeshes[i]));
if (n->mNumMeshes > 0) {
node->mesh = mAsset->meshes.Get(n->mMeshes[0]);
}
for (unsigned int i = 0; i < n->mNumChildren; ++i) {
@ -845,7 +814,7 @@ void glTF2Exporter::ExportScene()
void glTF2Exporter::ExportMetadata()
{
AssetMetadata& asset = mAsset->asset;
asset.version = 2;
asset.version = "2.0";
char buffer[256];
ai_snprintf(buffer, 256, "Open Asset Import Library (assimp v%d.%d.%d)",
@ -963,7 +932,7 @@ void glTF2Exporter::ExportAnimations()
name = mAsset->FindUniqueID(name, "animation");
Ref<Animation> animRef = mAsset->animations.Create(name);
/******************* Parameters ********************/
// Parameters
ExtractAnimationData(*mAsset, name, animRef, bufferRef, nodeChannel, anim->mTicksPerSecond);
for (unsigned int j = 0; j < 3; ++j) {

View File

@ -66,8 +66,15 @@ namespace glTF2
class Asset;
struct TexProperty;
struct TextureInfo;
struct NormalTextureInfo;
struct OcclusionTextureInfo;
struct Node;
struct Texture;
// Vec/matrix types, as raw float arrays
typedef float (vec3)[3];
typedef float (vec4)[4];
}
namespace Assimp
@ -101,9 +108,15 @@ namespace Assimp
void WriteBinaryData(IOStream* outfile, std::size_t sceneLength);
void GetTexSampler(const aiMaterial* mat, glTF2::Ref<glTF2::Texture> texture);
void GetMatTex(const aiMaterial* mat, glTF2::Ref<glTF2::Texture>& texture, aiTextureType tt);
void GetMatColorOrTex(const aiMaterial* mat, glTF2::TexProperty& prop, const char* propName, int type, int idx, aiTextureType tt);
void GetTexSampler(const aiMaterial* mat, glTF2::Ref<glTF2::Texture> texture, aiTextureType tt, unsigned int slot);
void GetMatTexProp(const aiMaterial* mat, unsigned int& prop, const char* propName, aiTextureType tt, unsigned int idx);
void GetMatTexProp(const aiMaterial* mat, float& prop, const char* propName, aiTextureType tt, unsigned int idx);
void GetMatTex(const aiMaterial* mat, glTF2::Ref<glTF2::Texture>& texture, aiTextureType tt, unsigned int slot);
void GetMatTex(const aiMaterial* mat, glTF2::TextureInfo& prop, aiTextureType tt, unsigned int slot);
void GetMatTex(const aiMaterial* mat, glTF2::NormalTextureInfo& prop, aiTextureType tt, unsigned int slot);
void GetMatTex(const aiMaterial* mat, glTF2::OcclusionTextureInfo& prop, aiTextureType tt, unsigned int slot);
void GetMatColor(const aiMaterial* mat, glTF2::vec4& prop, const char* propName, int type, int idx);
void GetMatColor(const aiMaterial* mat, glTF2::vec3& prop, const char* propName, int type, int idx);
void ExportMetadata();
void ExportMaterials();
void ExportMeshes();

View File

@ -0,0 +1,645 @@
/*
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2017, 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 ASSIMP_BUILD_NO_GLTF_IMPORTER
#include "glTF2Importer.h"
#include "StringComparison.h"
#include "StringUtils.h"
#include <assimp/Importer.hpp>
#include <assimp/scene.h>
#include <assimp/ai_assert.h>
#include <assimp/DefaultLogger.hpp>
#include <assimp/importerdesc.h>
#include <memory>
#include "MakeVerboseFormat.h"
#include "glTF2Asset.h"
// This is included here so WriteLazyDict<T>'s definition is found.
#include "glTF2AssetWriter.h"
#include <rapidjson/document.h>
#include <rapidjson/rapidjson.h>
using namespace Assimp;
using namespace glTF2;
//
// glTF2Importer
//
static const aiImporterDesc desc = {
"glTF2 Importer",
"",
"",
"",
aiImporterFlags_SupportTextFlavour | aiImporterFlags_LimitedSupport | aiImporterFlags_Experimental,
0,
0,
0,
0,
"gltf glb"
};
glTF2Importer::glTF2Importer()
: BaseImporter()
, meshOffsets()
, embeddedTexIdxs()
, mScene( NULL ) {
// empty
}
glTF2Importer::~glTF2Importer() {
// empty
}
const aiImporterDesc* glTF2Importer::GetInfo() const
{
return &desc;
}
bool glTF2Importer::CanRead(const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const
{
const std::string &extension = GetExtension(pFile);
if (extension != "gltf") // We currently can't read glTF2 binary files (.glb), yet
return false;
if (checkSig && pIOHandler) {
glTF2::Asset asset(pIOHandler);
try {
asset.Load(pFile);
std::string version = asset.asset.version;
return !version.empty() && version[0] == '2';
} catch (...) {
return false;
}
}
return false;
}
//static void CopyValue(const glTF2::vec3& v, aiColor3D& out)
//{
// out.r = v[0]; out.g = v[1]; out.b = v[2];
//}
static void CopyValue(const glTF2::vec4& v, aiColor4D& out)
{
out.r = v[0]; out.g = v[1]; out.b = v[2]; out.a = v[3];
}
/*static void CopyValue(const glTF2::vec4& v, aiColor3D& out)
{
out.r = v[0]; out.g = v[1]; out.b = v[2];
}*/
static void CopyValue(const glTF2::vec3& v, aiColor4D& out)
{
out.r = v[0]; out.g = v[1]; out.b = v[2]; out.a = 1.0;
}
static void CopyValue(const glTF2::vec3& v, aiVector3D& out)
{
out.x = v[0]; out.y = v[1]; out.z = v[2];
}
static void CopyValue(const glTF2::vec4& v, aiQuaternion& out)
{
out.x = v[0]; out.y = v[1]; out.z = v[2]; out.w = v[3];
}
static void CopyValue(const glTF2::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];
}
inline void SetMaterialColorProperty(Asset& r, vec4& prop, aiMaterial* mat, const char* pKey, unsigned int type, unsigned int idx)
{
aiColor4D col;
CopyValue(prop, col);
mat->AddProperty(&col, 1, pKey, type, idx);
}
inline void SetMaterialColorProperty(Asset& r, vec3& prop, aiMaterial* mat, const char* pKey, unsigned int type, unsigned int idx)
{
aiColor4D col;
CopyValue(prop, col);
mat->AddProperty(&col, 1, pKey, type, idx);
}
inline void SetMaterialTextureProperty(std::vector<int>& embeddedTexIdxs, Asset& r, glTF2::TextureInfo prop, aiMaterial* mat, aiTextureType texType, unsigned int texSlot = 0)
{
if (prop.texture && 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);
}
mat->AddProperty(&uri, AI_MATKEY_TEXTURE(texType, texSlot));
mat->AddProperty(&prop.texCoord, 1, _AI_MATKEY_GLTF_TEXTURE_TEXCOORD_BASE, texType, texSlot);
if (prop.texture->sampler) {
Ref<Sampler> sampler = prop.texture->sampler;
aiString name(sampler->name);
aiString id(sampler->id);
mat->AddProperty(&name, AI_MATKEY_GLTF_MAPPINGNAME(texType, texSlot));
mat->AddProperty(&id, AI_MATKEY_GLTF_MAPPINGID(texType, texSlot));
mat->AddProperty(&sampler->wrapS, 1, AI_MATKEY_MAPPINGMODE_U(texType, texSlot));
mat->AddProperty(&sampler->wrapT, 1, AI_MATKEY_MAPPINGMODE_V(texType, texSlot));
if (sampler->magFilter != SamplerMagFilter::UNSET) {
mat->AddProperty(&sampler->magFilter, 1, AI_MATKEY_GLTF_MAPPINGFILTER_MAG(texType, texSlot));
}
if (sampler->minFilter != SamplerMinFilter::UNSET) {
mat->AddProperty(&sampler->minFilter, 1, AI_MATKEY_GLTF_MAPPINGFILTER_MIN(texType, texSlot));
}
}
}
}
void glTF2Importer::ImportMaterials(glTF2::Asset& r)
{
mScene->mNumMaterials = unsigned(r.materials.Size());
mScene->mMaterials = new aiMaterial*[mScene->mNumMaterials];
for (unsigned int i = 0; i < mScene->mNumMaterials; ++i) {
aiMaterial* aimat = mScene->mMaterials[i] = new aiMaterial();
Material& mat = r.materials[i];
if (!mat.name.empty()) {
aiString str(mat.name);
aimat->AddProperty(&str, AI_MATKEY_NAME);
}
SetMaterialColorProperty(r, mat.pbrMetallicRoughness.baseColorFactor, aimat, AI_MATKEY_COLOR_DIFFUSE);
SetMaterialTextureProperty(embeddedTexIdxs, r, mat.pbrMetallicRoughness.baseColorTexture, aimat, aiTextureType_DIFFUSE);
SetMaterialTextureProperty(embeddedTexIdxs, r, mat.pbrMetallicRoughness.metallicRoughnessTexture, aimat, AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLICROUGHNESS_TEXTURE);
aimat->AddProperty(&mat.pbrMetallicRoughness.metallicFactor, 1, AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLIC_FACTOR);
aimat->AddProperty(&mat.pbrMetallicRoughness.roughnessFactor, 1, AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_ROUGHNESS_FACTOR);
SetMaterialTextureProperty(embeddedTexIdxs, r, mat.normalTexture, aimat, aiTextureType_NORMALS);
SetMaterialTextureProperty(embeddedTexIdxs, r, mat.occlusionTexture, aimat, aiTextureType_LIGHTMAP);
SetMaterialTextureProperty(embeddedTexIdxs, r, mat.emissiveTexture, aimat, aiTextureType_EMISSIVE);
SetMaterialColorProperty(r, mat.emissiveFactor, aimat, AI_MATKEY_COLOR_EMISSIVE);
aimat->AddProperty(&mat.doubleSided, 1, AI_MATKEY_TWOSIDED);
aimat->AddProperty(&mat.alphaMode, 1, AI_MATKEY_GLTF_ALPHAMODE);
aimat->AddProperty(&mat.alphaCutoff, 1, AI_MATKEY_GLTF_ALPHACUTOFF);
//pbrSpecularGlossiness
if (mat.pbrSpecularGlossiness.isPresent) {
PbrSpecularGlossiness &pbrSG = mat.pbrSpecularGlossiness.value;
aimat->AddProperty(&mat.pbrSpecularGlossiness.isPresent, 1, AI_MATKEY_GLTF_PBRSPECULARGLOSSINESS);
SetMaterialColorProperty(r, pbrSG.diffuseFactor, aimat, AI_MATKEY_GLTF_PBRSPECULARGLOSSINESS_DIFFUSE_FACTOR);
SetMaterialColorProperty(r, pbrSG.specularFactor, aimat, AI_MATKEY_GLTF_PBRSPECULARGLOSSINESS_SPECULAR_FACTOR);
aimat->AddProperty(&pbrSG.glossinessFactor, 1, AI_MATKEY_GLTF_PBRSPECULARGLOSSINESS_GLOSSINESS_FACTOR);
SetMaterialTextureProperty(embeddedTexIdxs, r, pbrSG.diffuseTexture, aimat, AI_MATKEY_GLTF_PBRSPECULARGLOSSINESS_DIFFUSE_TEXTURE);
SetMaterialTextureProperty(embeddedTexIdxs, r, pbrSG.specularGlossinessTexture, aimat, AI_MATKEY_GLTF_PBRSPECULARGLOSSINESS_SPECULARGLOSSINESS_TEXTURE);
}
}
}
static 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)
{
face.mNumIndices = 2;
face.mIndices = new unsigned int[2];
face.mIndices[0] = a;
face.mIndices[1] = b;
}
static inline void SetFace(aiFace& face, int a, int b, int c)
{
face.mNumIndices = 3;
face.mIndices = new unsigned int[3];
face.mIndices[0] = a;
face.mIndices[1] = b;
face.mIndices[2] = c;
}
static inline bool CheckValidFacesIndices(aiFace* faces, unsigned nFaces, unsigned nVerts)
{
for (unsigned i = 0; i < nFaces; ++i) {
for (unsigned j = 0; j < faces[i].mNumIndices; ++j) {
unsigned idx = faces[i].mIndices[j];
if (idx >= nVerts)
return false;
}
}
return true;
}
void glTF2Importer::ImportMeshes(glTF2::Asset& r)
{
std::vector<aiMesh*> meshes;
unsigned int k = 0;
for (unsigned int m = 0; m < r.meshes.Size(); ++m) {
Mesh& mesh = r.meshes[m];
meshOffsets.push_back(k);
k += unsigned(mesh.primitives.size());
for (unsigned int p = 0; p < mesh.primitives.size(); ++p) {
Mesh::Primitive& prim = mesh.primitives[p];
aiMesh* aim = new aiMesh();
meshes.push_back(aim);
aim->mName = mesh.name.empty() ? mesh.id : mesh.name;
if (mesh.primitives.size() > 1) {
size_t& len = aim->mName.length;
aim->mName.data[len] = '-';
len += 1 + ASSIMP_itoa10(aim->mName.data + len + 1, unsigned(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;
}
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);
}
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();
aiVector3D* values = aim->mTextureCoords[tc];
for (unsigned int i = 0; i < aim->mNumVertices; ++i) {
values[i].y = 1 - values[i].y; // Flip Y coords
}
}
if (prim.indices) {
aiFace* faces = 0;
unsigned int nFaces = 0;
unsigned int count = prim.indices->count;
Accessor::Indexer data = prim.indices->GetIndexer();
ai_assert(data.IsValid());
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;
}
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;
}
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;
}
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;
}
if (faces) {
aim->mFaces = faces;
aim->mNumFaces = nFaces;
ai_assert(CheckValidFacesIndices(faces, nFaces, aim->mNumVertices));
}
}
if (prim.material) {
aim->mMaterialIndex = prim.material.GetIndex();
}
}
}
meshOffsets.push_back(k);
CopyVector(meshes, mScene->mMeshes, mScene->mNumMeshes);
}
void glTF2Importer::ImportCameras(glTF2::Asset& r)
{
if (!r.cameras.Size()) return;
mScene->mNumCameras = r.cameras.Size();
mScene->mCameras = new aiCamera*[r.cameras.Size()];
for (size_t i = 0; i < r.cameras.Size(); ++i) {
Camera& cam = r.cameras[i];
aiCamera* aicam = mScene->mCameras[i] = new aiCamera();
if (cam.type == Camera::Perspective) {
aicam->mAspect = cam.cameraProperties.perspective.aspectRatio;
aicam->mHorizontalFOV = cam.cameraProperties.perspective.yfov * aicam->mAspect;
aicam->mClipPlaneFar = cam.cameraProperties.perspective.zfar;
aicam->mClipPlaneNear = cam.cameraProperties.perspective.znear;
}
else {
// assimp does not support orthographic cameras
}
}
}
aiNode* ImportNode(aiScene* pScene, glTF2::Asset& r, std::vector<unsigned int>& meshOffsets, glTF2::Ref<glTF2::Node>& ptr)
{
Node& node = *ptr;
aiNode* ainode = new aiNode(node.id);
if (!node.children.empty()) {
ainode->mNumChildren = unsigned(node.children.size());
ainode->mChildren = new aiNode*[ainode->mNumChildren];
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& matrix = ainode->mTransformation;
if (node.matrix.isPresent) {
CopyValue(node.matrix.value, matrix);
}
else {
if (node.translation.isPresent) {
aiVector3D trans;
CopyValue(node.translation.value, trans);
aiMatrix4x4 t;
aiMatrix4x4::Translation(trans, t);
matrix = t * matrix;
}
if (node.scale.isPresent) {
aiVector3D scal(1.f);
CopyValue(node.scale.value, scal);
aiMatrix4x4 s;
aiMatrix4x4::Scaling(scal, s);
matrix = s * matrix;
}
if (node.rotation.isPresent) {
aiQuaternion rot;
CopyValue(node.rotation.value, rot);
matrix = aiMatrix4x4(rot.GetMatrix()) * matrix;
}
}
if (node.mesh) {
ainode->mNumMeshes = 1;
ainode->mMeshes = new unsigned int[1];
int k = 0;
int idx = node.mesh.GetIndex();
for (unsigned int j = meshOffsets[idx]; j < meshOffsets[idx + 1]; ++j, ++k) {
ainode->mMeshes[k] = j;
}
}
if (node.camera) {
pScene->mCameras[node.camera.GetIndex()]->mName = ainode->mName;
}
return ainode;
}
void glTF2Importer::ImportNodes(glTF2::Asset& r)
{
if (!r.scene) return;
std::vector< Ref<Node> > rootNodes = r.scene->nodes;
// The root nodes
unsigned int numRootNodes = unsigned(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;
}
//if (!mScene->mRootNode) {
// mScene->mRootNode = new aiNode("EMPTY");
//}
}
void glTF2Importer::ImportEmbeddedTextures(glTF2::Asset& r)
{
embeddedTexIdxs.resize(r.images.Size(), -1);
int numEmbeddedTexs = 0;
for (size_t i = 0; i < r.images.Size(); ++i) {
if (r.images[i].HasData())
numEmbeddedTexs += 1;
}
if (numEmbeddedTexs == 0)
return;
mScene->mTextures = new aiTexture*[numEmbeddedTexs];
// Add the embedded textures
for (size_t i = 0; i < r.images.Size(); ++i) {
Image img = r.images[i];
if (!img.HasData()) continue;
int idx = mScene->mNumTextures++;
embeddedTexIdxs[i] = idx;
aiTexture* tex = mScene->mTextures[idx] = new aiTexture();
size_t length = img.GetDataLength();
void* data = img.StealData();
tex->mWidth = static_cast<unsigned int>(length);
tex->mHeight = 0;
tex->pcData = reinterpret_cast<aiTexel*>(data);
if (!img.mimeType.empty()) {
const char* ext = strchr(img.mimeType.c_str(), '/') + 1;
if (ext) {
if (strcmp(ext, "jpeg") == 0) ext = "jpg";
size_t len = strlen(ext);
if (len <= 3) {
strcpy(tex->achFormatHint, ext);
}
}
}
}
}
void glTF2Importer::InternReadFile(const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler) {
this->mScene = pScene;
// read the asset file
glTF2::Asset asset(pIOHandler);
asset.Load(pFile);
//
// Copy the data out
//
ImportEmbeddedTextures(asset);
ImportMaterials(asset);
ImportMeshes(asset);
ImportCameras(asset);
ImportNodes(asset);
// TODO: it does not split the loaded vertices, should it?
//pScene->mFlags |= AI_SCENE_FLAGS_NON_VERBOSE_FORMAT;
MakeVerboseFormatProcess process;
process.Execute(pScene);
if (pScene->mNumMeshes == 0) {
pScene->mFlags |= AI_SCENE_FLAGS_INCOMPLETE;
}
}
#endif // ASSIMP_BUILD_NO_GLTF_IMPORTER

View File

@ -0,0 +1,91 @@
/*
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2017, 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_GLTF2IMPORTER_H_INC
#define AI_GLTF2IMPORTER_H_INC
#include "BaseImporter.h"
#include <assimp/DefaultIOSystem.h>
struct aiNode;
namespace glTF2
{
class Asset;
}
namespace Assimp {
/**
* Load the glTF2 format.
* https://github.com/KhronosGroup/glTF/tree/master/specification
*/
class glTF2Importer : public BaseImporter{
public:
glTF2Importer();
virtual ~glTF2Importer();
virtual bool CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig ) const;
protected:
virtual const aiImporterDesc* GetInfo() const;
virtual void InternReadFile( const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler );
private:
std::vector<unsigned int> meshOffsets;
std::vector<int> embeddedTexIdxs;
aiScene* mScene;
void ImportEmbeddedTextures(glTF2::Asset& a);
void ImportMaterials(glTF2::Asset& a);
void ImportMeshes(glTF2::Asset& a);
void ImportCameras(glTF2::Asset& a);
void ImportLights(glTF2::Asset& a);
void ImportNodes(glTF2::Asset& a);
};
} // Namespace assimp
#endif // AI_GLTF2IMPORTER_H_INC

View File

@ -1058,13 +1058,13 @@ namespace glTF
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: {})
float version; //!< The glTF format version (should be 1.0)
std::string version; //!< The glTF format version (should be 1.0)
void Read(Document& doc);
AssetMetadata()
: premultipliedAlpha(false)
, version(0)
, version("")
{
}
};

View File

@ -40,6 +40,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "StringUtils.h"
#include <iomanip>
// Header files, Assimp
#include <assimp/DefaultLogger.hpp>
@ -128,6 +129,12 @@ namespace {
return (it != val.MemberEnd() && it->value.IsString()) ? &it->value : 0;
}
inline Value* FindNumber(Value& val, const char* id)
{
Value::MemberIterator it = val.FindMember(id);
return (it != val.MemberEnd() && it->value.IsNumber()) ? &it->value : 0;
}
inline Value* FindArray(Value& val, const char* id)
{
Value::MemberIterator it = val.FindMember(id);
@ -309,7 +316,9 @@ inline void Buffer::Read(Value& obj, Asset& r)
}
else { // Local file
if (byteLength > 0) {
IOStream* file = r.OpenFile(uri, "rb");
std::string dir = !r.mCurrentAssetDir.empty() ? (r.mCurrentAssetDir + "/") : "";
IOStream* file = r.OpenFile(dir + uri, "rb");
if (file) {
bool ok = LoadFromStream(*file, byteLength);
delete file;
@ -1228,13 +1237,21 @@ inline void Scene::Read(Value& obj, Asset& r)
inline void AssetMetadata::Read(Document& doc)
{
// read the version, etc.
float 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* versionString = FindString(*obj, "version")) {
version = versionString->GetString();
} else if (Value* versionNumber = FindNumber (*obj, "version")) {
char buf[4];
ai_snprintf(buf, 4, "%.1f", versionNumber->GetDouble());
version = buf;
}
if (Value* profile = FindObject(*obj, "profile")) {
ReadMember(*profile, "api", this->profile.api);
@ -1242,16 +1259,8 @@ inline void AssetMetadata::Read(Document& doc)
}
}
version = std::max(statedVersion, version);
if (version == 0) {
// if missing version, we'll assume version 1...
version = 1;
}
if (version != 1) {
char msg[128];
ai_snprintf(msg, 128, "GLTF: Unsupported glTF version: %.0f", version);
throw DeadlyImportError(msg);
if (version.empty() || version[0] != '1') {
throw DeadlyImportError("GLTF: Unsupported glTF version: " + version);
}
}
@ -1273,7 +1282,7 @@ inline void Asset::ReadBinaryHeader(IOStream& stream)
}
AI_SWAP4(header.version);
asset.version = header.version;
asset.version = std::to_string(header.version);
if (header.version != 1) {
throw DeadlyImportError("GLTF: Unsupported binary glTF version");
}

View File

@ -606,13 +606,8 @@ namespace glTF {
{
Value asset;
asset.SetObject();
{
char versionChar[10];
ai_snprintf(versionChar, sizeof(versionChar), "%.0f", mAsset.asset.version);
asset.AddMember("version", Value(versionChar, mAl).Move(), mAl);
asset.AddMember("generator", Value(mAsset.asset.generator, mAl).Move(), mAl);
}
asset.AddMember("version", Value(mAsset.asset.version, mAl).Move(), mAl);
asset.AddMember("generator", Value(mAsset.asset.generator, mAl).Move(), mAl);
mDoc.AddMember("asset", asset, mAl);
}

View File

@ -100,24 +100,19 @@ const aiImporterDesc* glTFImporter::GetInfo() const
bool glTFImporter::CanRead(const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const
{
const std::string& extension = GetExtension(pFile);
const std::string &extension = GetExtension(pFile);
if (extension == "gltf" || extension == "glb")
return true;
if (extension != "gltf" && extension != "glb")
return false;
if ((checkSig || !extension.length()) && pIOHandler) {
char buffer[4];
std::unique_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 (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 (checkSig && pIOHandler) {
glTF::Asset asset(pIOHandler);
try {
asset.Load(pFile, extension == "glb");
std::string version = asset.asset.version;
return !version.empty() && version[0] == '1';
} catch (...) {
return false;
}
}

View File

@ -65,7 +65,7 @@ SET( TEST_SRCS
unit/ut3DSImportExport.cpp
unit/utACImportExport.cpp
unit/utAMFImportExport.cpp
unit/utASEImportExport.cpp
unit/utASEImportExport.cpp
unit/utAnim.cpp
unit/AssimpAPITest.cpp
unit/utB3DImportExport.cpp
@ -88,6 +88,7 @@ SET( TEST_SRCS
unit/utFixInfacingNormals.cpp
unit/utGenNormals.cpp
unit/utglTFImportExport.cpp
unit/utglTF2ImportExport.cpp
unit/utHMPImportExport.cpp
unit/utIFCImportExport.cpp
unit/utImporter.cpp
@ -146,7 +147,7 @@ add_executable( unit
)
add_definitions(-DASSIMP_TEST_MODELS_DIR="${CMAKE_CURRENT_LIST_DIR}/models")
SET_PROPERTY( TARGET assimp PROPERTY DEBUG_POSTFIX ${CMAKE_DEBUG_POSTFIX} )
IF( WIN32 )

View File

@ -0,0 +1,181 @@
{
"asset": {
"generator": "COLLADA2GLTF",
"version": "2.0"
},
"scene": 0,
"scenes": [
{
"nodes": [
0
]
}
],
"nodes": [
{
"children": [
1
],
"matrix": [
1.0,
0.0,
0.0,
0.0,
0.0,
0.0,
-1.0,
0.0,
0.0,
1.0,
0.0,
0.0,
0.0,
0.0,
0.0,
1.0
]
},
{
"mesh": 0
}
],
"meshes": [
{
"primitives": [
{
"attributes": {
"NORMAL": 1,
"POSITION": 2,
"TEXCOORD_0": 3
},
"indices": 0,
"mode": 4,
"material": 0
}
],
"name": "Mesh"
}
],
"accessors": [
{
"bufferView": 0,
"byteOffset": 0,
"componentType": 5123,
"count": 36,
"max": [
23
],
"min": [
0
],
"type": "SCALAR"
},
{
"bufferView": 1,
"byteOffset": 0,
"componentType": 5126,
"count": 24,
"max": [
1.0,
1.0,
1.0
],
"min": [
-1.0,
-1.0,
-1.0
],
"type": "VEC3"
},
{
"bufferView": 1,
"byteOffset": 288,
"componentType": 5126,
"count": 24,
"max": [
0.5,
0.5,
0.5
],
"min": [
-0.5,
-0.5,
-0.5
],
"type": "VEC3"
},
{
"bufferView": 2,
"byteOffset": 0,
"componentType": 5126,
"count": 24,
"max": [
6.0,
1.0
],
"min": [
0.0,
0.0
],
"type": "VEC2"
}
],
"materials": [
{
"pbrMetallicRoughness": {
"baseColorTexture": {
"index": 0
},
"metallicFactor": 0.0
},
"name": "Texture"
}
],
"textures": [
{
"sampler": 0,
"source": 0
}
],
"images": [
{
"uri": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAANMAAADTCAMAAAAs2dbrAAAAsVBMVEXc3Nz8/f3+//9chyf///+VsHP4+/thiy3Z29dyl0R8nlH09vJeiCqzxZ6ft4GLqWatwZXw9vyEpFynvYxmjjTS3cNrkjvu8urn8vre7fjY6ffL08K6yqbEz7XT183K4vTQ5fW62fC/zq3J17fn7d/g6NbC3fLb5c/l5eVsrd+wzOF/t+OWw+WGu+TE099vr+CmyON5tOJ0seG50OHL1t7S2d7Z29yOv+Wdx+emzuyx1O6AHuoWAAAIi0lEQVR4XuzUS3LEIAxF0WznIYk/xun9LyxDRqnqGANq4rODW4L39d88Ho/H43HkZPzJVYgoxhiIpDJ7k2z5wJpizSkBv4vCJn1OWTZMeE9gY/Xfx3DE30Q2WfGBvOAa8lZnEKFH0JZ1GEE/MnpGw54R93Cs41ip4k6SjuWPjnC3YJZWtaJdqhJhlJDWLINgJJm/FoUxGpfJ0xAxnvMTv1UWzEF21pG8wzT+2OZIjeTxScZhLmdGz13FfHXoAGbCCmHgVCSHNVwaleSxjh8z4Yxmj09VBGtRGbAOq1G+OSlgvZC3SwLijVE2Qodot7lS4+xGSS1quyS4O/5UIegSSm/SUaENHZ1NDH1qX5SHRmdPUoJOqWPyInS6Pn6HoNlkJxh68T6fqTFXkkrERPL9en0L3ueufKkf6uxmt1EYiAN4sP7i0pNPABeItEIdeV0vJMTA+z/YaqWo3WSS1gn2lP4ewFHEfNqexLi5hAIAvTgKZZ+4myQxpsSZUtomiD75yPM5Pqg8uDK5cbORZzT+p3JDgfxma16LK3WaceLUkxQPJjj6hnGbo2sHpkkxzB4didFgcgp23OJQ5BS4Pn6TeiM5PThYCva2vQmCHDgYCmZXfCbJfMpc9PXQk6QGTE0PMNv7TDSBmaM/IXqSVeKKdsSsy6gjCfMZLijWSVaXvomkzbiwRF/jR0fS3JThXTbTw8aAVVCcbXFW2wRv8oa+hVmaumoWS88YTgKFXNpBYHrd1GXfaaCfyI0CK7u0g0DoSfMCoSduTBF6Zto3bV033ewdiel/L/9+te3G6HOR35d4p/JmcjI9rVI4e9nd0z9XSStc08tAidkmw4cs6khuK3BK6ZlS6huFC6/xZj23z3BHbSiZOceVIto2aCrcp1N1BteByXe3DfQgq/EZNVMKQwUui5ROPscXFoqvL3HLa5R0sjkg/6eGEjcVMQajXuNraqK4XA3ufoey9AhXIURmUzyDhCbUya0/nFN6SHIbyK0vERMCqZZW4ikcXCQOcQ7nlgT1gVPFjvuz+vCXX4UCl/mEd+tAXhSsSDxT9rq/tJjbasJAFEWJm2QwF6NJVNra0hptoFVsERrp/39YXwTrbDWZOcf1GrLDhrMyF45elUMAVTjlR5NvrctNIsniFDB5HVzstJWEB+MUR/I5PdVRahtQcF3hSNxEkoMGhw+SAidmU1bqLjIFI5wwS670IwhPYvzHNCRweweZBiHOeBH8yik8KnBOtWal9GUqcc4T38D6h49gM0zoNLXTlqmGBQ/fXhDOhKyUskyrChYZ78ol4cyCpn+jKlMUw6bkTpJwxjgqJZ93jHgbIQt3Ukou0zOYkDv5rUw1rpDmtE9685VpYn91ZXp1OjiFs0xMpqXUa795z7mTRnjH0jvxuh77pXkP0a/TRh7erdSDh1ItybTERQruJA5nNJTia4+16dvp4C4Th+srxfOeDKHVaccyUTiRNnan6FNNpm6fvhTCmVlil3JTqh3wvDt1ksvE5PTeu5ZM3evTh0o4MxZcY+4eO2QSdeobzpgpK6UjU/d+b68TzszmvqvUhmRa4Aal05mwdQhnClbKU6bG4AYZn91dwv94t+MepYEgCuDTzhSudIFSgKIHKtwlaIBEe5cTv/8XM8ZEIrPtGxzw9zd5TcO83SbbgjKhSsnJXyZtRZfeHOFAmKut7fgF2siFdMyd1qQ0/nB7pZbN9WUacrcPpOwd4bev1B6USeuTdnA0FZqCF67h6Rae95y0r45wTO9SR0+ZtFCR9uIIx2agUv55H5H26gnHqp7906395W/TXWBkGD/7xOGiw/2VwvNeLxhaU8Qeh6eFCrd7ugxLjsbTrWTM2IPtTbeNIdxu9myr1EnNe8lYThFvL65wLAyilcLzPg+MZRTz6gzHRoaTgWYZKxNWUlRzZbi/UpKc4MNlUrHFI0Udrg73Vyo5oKP10pZseCexeR8J9ztXKvrNfvNR/nHeK2px3jC+T2zh/kql6ebPxnhcCph3VCett/zx7dfYHT6lINxTKWWy2aurqnnHdYqrRdLJu2UiAsId+s8S0XJVmbJNTm0+iwLCPZXC0jkbZdQqkRYq3CETq3rGRltqVUhUqsI9wlYAdbqFzB4IDp8O99Ln19iIHaMHhi/N+KbCohaDFZutqUPhWB9864RnP1yQAoav5NurCrjk9dlsRJ16KnzK9zAAN/XUZ7tH6nQ56cWY7yPvWiiSku3CgAD5yy7newmrRFrUFV9jSEAhZ8Uo8P2EQfyvSoYzVtDmZFwlihJku4VM31Vvm/N1pgQVv6PnWZ//g2pby1m6my5+cls3ubHCQBCASypZbiPbIGDYAF7wXpScYO5/tCxZBgb8g78btKpdbZ6kA/708/Vah35kKrbxy+v7/9frX9dOPE/hb2KYi+V5dsYBjk+icIjhc+gZhyz1xQRp+BQ6ALUF5XFYz2eYBIfNmo/gcILnEzQ4QwzLZ2ecstVTELuWpTOCk8LEwq04batm83Zvz5I1gg+IeczftY7L2+FDHUul8DFVzWPaSc8SjTMuCIblsRt2lfSEw0WLZVmsx2Wu5Mqro9FbwR0GlqMX3OLtyztM16n6RgJ8PYu36+qph52zzEwJ7rZoZjUggnViPtohitAwl2lFLIp5NAHxdDZTO8S0GqY2OkQmqp69220T07EDkggtU2lmpOLSRKUHJCTeMro2IK25Z1xmRXqLYTxjJ8jh7QzjGAdBLhJlKu0DcpLO8F7TEJDd1lrexjhBCd7Bj7yDVivKIe56WL/t2jEKwCAMheEsYirEBgNdohl6CO9/tM6FgrRdFPJNWX/I+BIbTMao/MhKYjAlo/3LE8bCChND5RLfLXxPhNl1wMYhxXHOtktDWInWHMr2mHakkEkRVoXaaiUWyVmEqTY1hOX1+z3mnHPOXX/B0DoXjBYdAAAAAElFTkSuQmCC"
}
],
"samplers": [
{
"magFilter": 9729,
"minFilter": 9986,
"wrapS": 10497,
"wrapT": 10497
}
],
"bufferViews": [
{
"buffer": 0,
"byteOffset": 768,
"byteLength": 72,
"target": 34963
},
{
"buffer": 0,
"byteOffset": 0,
"byteLength": 576,
"byteStride": 12,
"target": 34962
},
{
"buffer": 0,
"byteOffset": 576,
"byteLength": 192,
"byteStride": 8,
"target": 34962
}
],
"buffers": [
{
"byteLength": 840,
"uri": "data:application/octet-stream;base64,AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAvwAAAL8AAAA/AAAAPwAAAL8AAAA/AAAAvwAAAD8AAAA/AAAAPwAAAD8AAAA/AAAAPwAAAD8AAAA/AAAAPwAAAL8AAAA/AAAAPwAAAD8AAAC/AAAAPwAAAL8AAAC/AAAAvwAAAD8AAAA/AAAAPwAAAD8AAAA/AAAAvwAAAD8AAAC/AAAAPwAAAD8AAAC/AAAAPwAAAL8AAAA/AAAAvwAAAL8AAAA/AAAAPwAAAL8AAAC/AAAAvwAAAL8AAAC/AAAAvwAAAL8AAAA/AAAAvwAAAD8AAAA/AAAAvwAAAL8AAAC/AAAAvwAAAD8AAAC/AAAAvwAAAL8AAAC/AAAAvwAAAD8AAAC/AAAAPwAAAL8AAAC/AAAAPwAAAD8AAAC/AADAQAAAAAAAAKBAAAAAAAAAwED+/38/AACgQP7/fz8AAIBAAAAAAAAAoEAAAAAAAACAQAAAgD8AAKBAAACAPwAAAEAAAAAAAACAPwAAAAAAAABAAACAPwAAgD8AAIA/AABAQAAAAAAAAIBAAAAAAAAAQEAAAIA/AACAQAAAgD8AAEBAAAAAAAAAAEAAAAAAAABAQAAAgD8AAABAAACAPwAAAAAAAAAAAAAAAP7/fz8AAIA/AAAAAAAAgD/+/38/AAABAAIAAwACAAEABAAFAAYABwAGAAUACAAJAAoACwAKAAkADAANAA4ADwAOAA0AEAARABIAEwASABEAFAAVABYAFwAWABUA"
}
]
}

View File

@ -0,0 +1,197 @@
{
"asset": {
"generator": "COLLADA2GLTF",
"version": "2.0"
},
"scene": 0,
"scenes": [
{
"nodes": [
0
]
}
],
"nodes": [
{
"children": [
1
],
"matrix": [
1.0,
0.0,
0.0,
0.0,
0.0,
0.0,
-1.0,
0.0,
0.0,
1.0,
0.0,
0.0,
0.0,
0.0,
0.0,
1.0
]
},
{
"mesh": 0
}
],
"meshes": [
{
"primitives": [
{
"attributes": {
"NORMAL": 1,
"POSITION": 2,
"TEXCOORD_0": 3
},
"indices": 0,
"mode": 4,
"material": 0
}
],
"name": "Mesh"
}
],
"accessors": [
{
"bufferView": 0,
"byteOffset": 0,
"componentType": 5123,
"count": 36,
"max": [
23
],
"min": [
0
],
"type": "SCALAR"
},
{
"bufferView": 1,
"byteOffset": 0,
"componentType": 5126,
"count": 24,
"max": [
1.0,
1.0,
1.0
],
"min": [
-1.0,
-1.0,
-1.0
],
"type": "VEC3"
},
{
"bufferView": 1,
"byteOffset": 288,
"componentType": 5126,
"count": 24,
"max": [
0.5,
0.5,
0.5
],
"min": [
-0.5,
-0.5,
-0.5
],
"type": "VEC3"
},
{
"bufferView": 2,
"byteOffset": 0,
"componentType": 5126,
"count": 24,
"max": [
6.0,
1.0
],
"min": [
0.0,
0.0
],
"type": "VEC2"
}
],
"materials": [
{
"pbrMetallicRoughness": {
"baseColorTexture": {
"index": 0
},
"metallicFactor": 0.0
},
"extensions": {
"KHR_materials_pbrSpecularGlossiness": {
"diffuseTexture": {
"index": 0
},
"specularFactor": [
0.20000000298023225,
0.20000000298023225,
0.20000000298023225
],
"glossinessFactor": 1.0
}
},
"name": "Texture"
}
],
"textures": [
{
"sampler": 0,
"source": 0
}
],
"images": [
{
"uri": "CesiumLogoFlat.png"
}
],
"samplers": [
{
"magFilter": 9729,
"minFilter": 9986,
"wrapS": 10497,
"wrapT": 10497
}
],
"bufferViews": [
{
"buffer": 0,
"byteOffset": 768,
"byteLength": 72,
"target": 34963
},
{
"buffer": 0,
"byteOffset": 0,
"byteLength": 576,
"byteStride": 12,
"target": 34962
},
{
"buffer": 0,
"byteOffset": 576,
"byteLength": 192,
"byteStride": 8,
"target": 34962
}
],
"buffers": [
{
"byteLength": 840,
"uri": "BoxTextured0.bin"
}
],
"extensionsUsed": [
"KHR_materials_pbrSpecularGlossiness"
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

View File

@ -0,0 +1,282 @@
{
"asset": {
"generator": "COLLADA2GLTF",
"version": "2.0"
},
"scene": 0,
"scenes": [
{
"nodes": [
0
]
}
],
"nodes": [
{
"children": [
1
],
"matrix": [
1.0,
0.0,
0.0,
0.0,
0.0,
0.0,
-1.0,
0.0,
0.0,
1.0,
0.0,
0.0,
0.0,
0.0,
0.0,
1.0
]
},
{
"mesh": 0
}
],
"meshes": [
{
"primitives": [
{
"attributes": {
"NORMAL": 1,
"POSITION": 2,
"TEXCOORD_0": 3
},
"indices": 0,
"mode": 4,
"material": 0
}
],
"name": "Mesh"
}
],
"accessors": [
{
"bufferView": 0,
"byteOffset": 0,
"componentType": 5123,
"count": 36,
"max": [
23
],
"min": [
0
],
"type": "SCALAR"
},
{
"bufferView": 1,
"byteOffset": 0,
"componentType": 5126,
"count": 24,
"max": [
1.0,
1.0,
1.0
],
"min": [
-1.0,
-1.0,
-1.0
],
"type": "VEC3"
},
{
"bufferView": 1,
"byteOffset": 288,
"componentType": 5126,
"count": 24,
"max": [
0.5,
0.5,
0.5
],
"min": [
-0.5,
-0.5,
-0.5
],
"type": "VEC3"
},
{
"bufferView": 2,
"byteOffset": 0,
"componentType": 5126,
"count": 24,
"max": [
6.0,
1.0
],
"min": [
0.0,
0.0
],
"type": "VEC2"
}
],
"materials": [
{
"values": {
"diffuse": [
0
],
"specular": [
0.20000000298023225,
0.20000000298023225,
0.20000000298023225,
1.0
],
"shininess": [
256.0
],
"transparency": [
1.0
]
},
"technique": 0
}
],
"textures": [
{
"sampler": 0,
"source": 0
}
],
"images": [
{
"uri": "CesiumLogoFlat.png"
}
],
"samplers": [
{
"magFilter": 9729,
"minFilter": 9986,
"wrapS": 10497,
"wrapT": 10497
}
],
"techniques": [
{
"attributes": {
"a_normal": "normal",
"a_position": "position",
"a_texcoord0": "texcoord0"
},
"parameters": {
"diffuse": {
"type": 35678
},
"modelViewMatrix": {
"semantic": "MODELVIEW",
"type": 35676
},
"normal": {
"semantic": "NORMAL",
"type": 35665
},
"normalMatrix": {
"semantic": "MODELVIEWINVERSETRANSPOSE",
"type": 35675
},
"position": {
"semantic": "POSITION",
"type": 35665
},
"projectionMatrix": {
"semantic": "PROJECTION",
"type": 35676
},
"shininess": {
"type": 5126
},
"specular": {
"type": 35666
},
"texcoord0": {
"semantic": "TEXCOORD_0",
"type": 35665
},
"transparency": {
"type": 5126
}
},
"program": 0,
"states": {
"enable": [
2884,
2929
]
},
"uniforms": {
"u_diffuse": "diffuse",
"u_modelViewMatrix": "modelViewMatrix",
"u_normalMatrix": "normalMatrix",
"u_projectionMatrix": "projectionMatrix",
"u_shininess": "shininess",
"u_specular": "specular",
"u_transparency": "transparency"
}
}
],
"programs": [
{
"attributes": [
"a_normal",
"a_position",
"a_texcoord0"
],
"fragmentShader": 1,
"vertexShader": 0
}
],
"shaders": [
{
"type": 35633,
"uri": "BoxTextured0.vert"
},
{
"type": 35632,
"uri": "BoxTextured1.frag"
}
],
"bufferViews": [
{
"buffer": 0,
"byteOffset": 768,
"byteLength": 72,
"target": 34963
},
{
"buffer": 0,
"byteOffset": 0,
"byteLength": 576,
"byteStride": 12,
"target": 34962
},
{
"buffer": 0,
"byteOffset": 576,
"byteLength": 192,
"byteStride": 8,
"target": 34962
}
],
"buffers": [
{
"byteLength": 840,
"uri": "BoxTextured0.bin"
}
],
"extensionsRequired": [
"KHR_technique_webgl"
],
"extensionsUsed": [
"KHR_technique_webgl"
]
}

View File

@ -0,0 +1,17 @@
precision highp float;
uniform mat4 u_modelViewMatrix;
uniform mat4 u_projectionMatrix;
uniform mat3 u_normalMatrix;
attribute vec3 a_position;
varying vec3 v_position;
attribute vec3 a_normal;
varying vec3 v_normal;
attribute vec2 a_texcoord0;
varying vec2 v_texcoord0;
void main(void) {
vec4 pos = u_modelViewMatrix * vec4(a_position,1.0);
v_position = pos.xyz;
gl_Position = u_projectionMatrix * pos;
v_normal = u_normalMatrix * a_normal;
v_texcoord0 = a_texcoord0;
}

View File

@ -0,0 +1,29 @@
precision highp float;
uniform sampler2D u_diffuse;
uniform vec4 u_specular;
uniform float u_shininess;
uniform float u_transparency;
varying vec3 v_position;
varying vec3 v_normal;
varying vec2 v_texcoord0;
void main(void) {
vec3 normal = normalize(v_normal);
vec4 diffuse = texture2D(u_diffuse, v_texcoord0);
vec3 diffuseLight = vec3(0.0, 0.0, 0.0);
vec3 specular = u_specular.rgb;
vec3 specularLight = vec3(0.0, 0.0, 0.0);
vec3 ambient = diffuse.rgb;
vec3 viewDir = -normalize(v_position);
vec3 ambientLight = vec3(0.0, 0.0, 0.0);
ambientLight += vec3(0.2, 0.2, 0.2);
vec3 l = vec3(0.0, 0.0, 1.0);
diffuseLight += vec3(1.0, 1.0, 1.0) * max(dot(normal, l), 0.);
vec3 reflectDir = reflect(-l, normal);
float specularIntensity = max(0., pow(max(dot(reflectDir, viewDir), 0.), u_shininess));
specularLight += vec3(1.0, 1.0, 1.0) * specularIntensity;
vec3 color = vec3(0.0, 0.0, 0.0);
color += diffuse.rgb * diffuseLight;
color += specular * specularLight;
color += ambient * ambientLight;
gl_FragColor = vec4(color * diffuse.a * u_transparency, diffuse.a * u_transparency);
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

View File

@ -0,0 +1,181 @@
{
"asset": {
"generator": "COLLADA2GLTF",
"version": "2.0"
},
"scene": 0,
"scenes": [
{
"nodes": [
0
]
}
],
"nodes": [
{
"children": [
1
],
"matrix": [
1.0,
0.0,
0.0,
0.0,
0.0,
0.0,
-1.0,
0.0,
0.0,
1.0,
0.0,
0.0,
0.0,
0.0,
0.0,
1.0
]
},
{
"mesh": 0
}
],
"meshes": [
{
"primitives": [
{
"attributes": {
"NORMAL": 1,
"POSITION": 2,
"TEXCOORD_0": 3
},
"indices": 0,
"mode": 4,
"material": 0
}
],
"name": "Mesh"
}
],
"accessors": [
{
"bufferView": 0,
"byteOffset": 0,
"componentType": 5123,
"count": 36,
"max": [
23
],
"min": [
0
],
"type": "SCALAR"
},
{
"bufferView": 1,
"byteOffset": 0,
"componentType": 5126,
"count": 24,
"max": [
1.0,
1.0,
1.0
],
"min": [
-1.0,
-1.0,
-1.0
],
"type": "VEC3"
},
{
"bufferView": 1,
"byteOffset": 288,
"componentType": 5126,
"count": 24,
"max": [
0.5,
0.5,
0.5
],
"min": [
-0.5,
-0.5,
-0.5
],
"type": "VEC3"
},
{
"bufferView": 2,
"byteOffset": 0,
"componentType": 5126,
"count": 24,
"max": [
6.0,
1.0
],
"min": [
0.0,
0.0
],
"type": "VEC2"
}
],
"materials": [
{
"pbrMetallicRoughness": {
"baseColorTexture": {
"index": 0
},
"metallicFactor": 0.0
},
"name": "Texture"
}
],
"textures": [
{
"sampler": 0,
"source": 0
}
],
"images": [
{
"uri": "CesiumLogoFlat.png"
}
],
"samplers": [
{
"magFilter": 9729,
"minFilter": 9986,
"wrapS": 10497,
"wrapT": 10497
}
],
"bufferViews": [
{
"buffer": 0,
"byteOffset": 768,
"byteLength": 72,
"target": 34963
},
{
"buffer": 0,
"byteOffset": 0,
"byteLength": 576,
"byteStride": 12,
"target": 34962
},
{
"buffer": 0,
"byteOffset": 576,
"byteLength": 192,
"byteStride": 8,
"target": 34962
}
],
"buffers": [
{
"byteLength": 840,
"uri": "BoxTextured0.bin"
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

View File

@ -0,0 +1,80 @@
/*
---------------------------------------------------------------------------
Open Asset Import Library (assimp)
---------------------------------------------------------------------------
Copyright (c) 2006-2017, 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 "UnitTestPCH.h"
#include "AbstractImportExportBase.h"
#include <assimp/Importer.hpp>
#include <assimp/Exporter.hpp>
using namespace Assimp;
class utglTF2ImportExport : public AbstractImportExportBase {
public:
virtual bool importerTest() {
Assimp::Importer importer;
const aiScene *scene = importer.ReadFile( ASSIMP_TEST_MODELS_DIR "/glTF2/BoxTextured-glTF/BoxTextured.gltf", 0);
return nullptr != scene;
}
#ifndef ASSIMP_BUILD_NO_EXPORT
virtual bool exporterTest() {
Assimp::Importer importer;
Assimp::Exporter exporter;
const aiScene *scene = importer.ReadFile( ASSIMP_TEST_MODELS_DIR "/glTF2/BoxTextured-glTF/BoxTextured.gltf", 0 );
EXPECT_NE( nullptr, scene );
EXPECT_EQ( aiReturn_SUCCESS, exporter.Export( scene, "gltf2", ASSIMP_TEST_MODELS_DIR "/glTF2/BoxTextured-glTF/BoxTextured_out.gltf" ) );
return true;
}
#endif // ASSIMP_BUILD_NO_EXPORT
};
TEST_F( utglTF2ImportExport, importglTF2FromFileTest ) {
EXPECT_TRUE( importerTest() );
}
#ifndef ASSIMP_BUILD_NO_EXPORT
TEST_F( utglTF2ImportExport, exportglTF2FromFileTest ) {
EXPECT_TRUE( exporterTest() );
}
#endif // ASSIMP_BUILD_NO_EXPORT

View File

@ -55,6 +55,6 @@ public:
}
};
TEST_F( utglTFImportExport, importglTFromFileTest ) {
TEST_F( utglTFImportExport, importglTFFromFileTest ) {
EXPECT_TRUE( importerTest() );
}