text: //
  // Copyright (c) Autodesk, Inc. All rights reserved 
  //
  // C++ glTF FBX importer/exporter plug-in
  // by Cyrille Fauvel - Autodesk Developer Network (ADN)
  // January 2015
  //
  // Permission to use, copy, modify, and distribute this software in
  // object code form for any purpose and without fee is hereby granted, 
  // provided that the above copyright notice appears in all copies and 
  // that both that copyright notice and the limited warranty and
  // restricted rights notice below appear in all supporting 
  // documentation.
  //
  // AUTODESK PROVIDES THIS PROGRAM "AS IS" AND WITH ALL FAULTS. 
  // AUTODESK SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTY OF
  // MERCHANTABILITY OR FITNESS FOR A PARTICULAR USE.  AUTODESK, INC. 
  // DOES NOT WARRANT THAT THE OPERATION OF THE PROGRAM WILL BE
  // UNINTERRUPTED OR ERROR FREE.
  //
  #pragma once
  
  #define _USE_MATH_DEFINES
  #include <math.h>
  
  #define FBX_GLTF_EXPORTER U("FBX GLTF Exporter v1.0")
  #define PROFILE_API U("WebGL")
  #define PROFILE_VERSION U("1.0.3")
  #define GLTF_VERSION U("1.0")
  
  #define DEG2RAD(a) a * M_PI / 180.0
  #define MergeJsonObjects_(a, b) \
  	for ( const auto &iter : b.as_object () ) \
  		a [iter.first] =iter.second
  //#define MergeJsonFromRetObject(a, b) \
  //	{ \
  //	web::json::value b2 =b ; \
  //	for ( const auto &iter : b2.as_object () ) \
  //		a [iter.first] =iter.second ; \
  //	}
  
  #define GLTF_ANGLE(a) \
  	GetIOSettings ()->GetBoolProp (IOSN_FBX_GLTF_ANGLEINDEGREE, false) ? a : DEG2RAD(a)
  
  namespace _IOglTF_NS_ {
  
  web::json::value &MergeJsonObjects (web::json::value &a, const web::json::value &b) ;
  utility::string_t GetJsonObjectKeyAt (web::json::value &a, int i =0) ;
  #define GetJsonFirstKey(a) GetJsonObjectKeyAt(a)
  
  class gltfWriter : public FbxWriter {
  private:
  	utility::string_t _fileName ;
  	utility::ofstream_t _gltf ;
  	memoryStream<uint8_t> _bin ;
  
  	web::json::value _json ;
  
  	bool _writeDefaults ;
  	double _samplingPeriod ;
  	std::map<FbxUInt64, utility::string_t> _IDs ;
  	std::vector<utility::string_t> _registeredNames ;
  	std::map<utility::string_t, utility::string_t> _uvSets ;
  #ifdef _DEBUG
  	std::vector<utility::string_t> _path ;
  #endif
  
  public:
  	gltfWriter (FbxManager &pManager, int id) ;
  	virtual ~gltfWriter () ;
  
  	virtual bool FileCreate (char *pFileName) ;
  	virtual bool FileClose () ;
  	virtual bool IsFileOpen () ;
  	virtual void GetWriteOptions () ;
  	virtual bool Write (FbxDocument *pDocument) ;
  	virtual bool PreprocessScene (FbxScene &scene) ;
  	virtual bool PostprocessScene (FbxScene &scene) ;
  
  	static FbxWriter *Create_gltfWriter (FbxManager &manager, FbxExporter &exporter, int subID, int pluginID) ;
  	static void *gltfFormatInfo (FbxWriter::EInfoRequest request, int id) ;
  	static void FillIOSettings (FbxIOSettings &ios) ;
  
  protected:
  	void PrepareForSerialization () ;
  
  protected:
  	bool recordId (FbxUInt64 uniqid, utility::string_t id) ;
  	bool isKnownId (FbxUInt64 uniqid) ;
  	bool isKnownId (utility::string_t id) ;
  public:
  	utility::string_t nodeId (FbxNode *pNode, bool bNodeAttribute =false, bool bRecord =false) ;
  
  protected:
  	utility::string_t registerName (utility::string_t name) ;
  	bool isNameRegistered (utility::string_t id) ;
  public:
  	utility::string_t createUniqueName (utility::string_t type, FbxUInt64 id) ;
  
  	inline utility::string_t createSamplerName (FbxString &szname) { return (U ("sampler_") + utility::conversions::to_string_t (szname.Buffer ())) ; }
  	inline utility::string_t createSamplerName (const char *pszName) { return (U ("sampler_") + utility::conversions::to_string_t (pszName)) ; }
  	inline utility::string_t createTextureName (FbxString &szname) { return (U("texture_") + utility::conversions::to_string_t (szname.Buffer ())) ; }
  	inline utility::string_t createTextureName (const char *pszName) { return (U("texture_") + utility::conversions::to_string_t (pszName)) ; }
  
  protected:
  	web::json::value WriteSceneNodeRecursive (FbxNode *pNode, FbxPose *pPose =nullptr, bool bRoot =false) ;
  	web::json::value WriteSceneNode (FbxNode *pNode, FbxPose *pPose =nullptr) ;
  	FbxNodeAttribute::EType nodeType (FbxNode *pNode) ;
  
  	bool IsGeometryNode (FbxNode *pNode) ;
  	bool CheckMaterials (FbxNode *pNode) ;
  	void PreprocessNodeRecursive (FbxNode *pNode) ;
  	web::json::value WriteNode (FbxNode *pNode) ;
  	web::json::value GetTransform (FbxNode *pNode) ;
  
  	// The following list is json nodes generated by other json nodes
  	// accessor / bufferView
  
  	// asset
  	bool WriteAsset (FbxDocumentInfo *pSceneInfo) ;
  	// buffer
  	bool WriteBuffer () ;
  	// camera
  	double cameraYFOV (FbxCamera *pCamera) ;
  	web::json::value WriteCamera (FbxNode *pNode) ;
  	// light
  	void lightAttenuation (FbxLight *pLight, web::json::value &lightDef) ;
  	web::json::value WriteLight (FbxNode *pNode) ;
  	web::json::value WriteAmbientLight (FbxScene &pScene) ;
  	// material
  	utility::string_t LighthingModel (FbxSurfaceMaterial *pMaterial) ;
  	web::json::value WriteMaterialTransparencyParameter (const utility::char_t *pszName, FbxPropertyT<FbxDouble> &property, FbxPropertyT<FbxDouble3> &propertyColor, FbxProperty &propertyOpaque, web::json::value &values, web::json::value &techniqueParameters) ;
  	//web::json::value WriteMaterialTransparencyParameter (const utility::char_t *pszName, FbxPropertyT<FbxDouble> &property, web::json::value &values, web::json::value &techniqueParameters) ;
  	web::json::value WriteMaterialParameter (const utility::char_t *pszName, FbxPropertyT<FbxDouble3> &property, double factor, web::json::value &values, web::json::value &techniqueParameters) ;
  	web::json::value WriteMaterialParameter (const utility::char_t *pszName, FbxPropertyT<FbxDouble> &property, web::json::value &values, web::json::value &techniqueParameters) ;
  	web::json::value WriteMaterialParameter (const utility::char_t *pszName, FbxSurfaceMaterial *pMaterial, const char *propertyName, const char *factorName, web::json::value &values, web::json::value &techniqueParameters) ;
  	web::json::value WriteMaterial (FbxNode *pNode, FbxSurfaceMaterial *pMaterial) ;
  	web::json::value WriteDefaultMaterial (FbxNode *pNode) ;
  	web::json::value WritePhongMaterial (FbxNode *pNode, FbxSurfaceMaterial *pMaterial) ;
  	web::json::value WriteLambertMaterial (FbxNode *pNode, FbxSurfaceMaterial *pMaterial) ;
  	web::json::value WriteConstantShadingModelMaterial (FbxNode *pNode, FbxSurfaceMaterial *pMaterial) ;
  	web::json::value WriteBlinnShadingModelMaterial (FbxNode *pNode, FbxSurfaceMaterial *pMaterial) ;
  	web::json::value WriteDefaultShadingModelWithCGFXMaterial (FbxNode *pNode, FbxSurfaceMaterial *pMaterial) ;
  	web::json::value WriteDefaultShadingModelMaterial (FbxNode *pNode, FbxSurfaceMaterial *pMaterial) ;
  	web::json::value WriteDefaultShadingModelMaterial (FbxNode *pNode) ;
  	// mesh
  	web::json::value WriteMesh (FbxNode *pNode) ;
  	// line
  	//web::json::value WriteLine (FbxNode *pNode) ;
  	// null
  	web::json::value WriteNull (FbxNode *pNode) ;
  	// program
  	web::json::value WriteProgram (FbxNode *pNode, FbxSurfaceMaterial *pMaterial, utility::string_t programName, web::json::value &attributes) ;
  	// scenes / scene
  	bool WriteScene (FbxScene *pScene, int poseIndex =-1) ;
  	// shaders
  	web::json::value WriteShaders (FbxNode *pNode, web::json::value &program) ;
  	// technique
  	void AdditionalTechniqueParameters (FbxNode *pNode, web::json::value &techniqueParameters, bool bHasNormals =false) ;
  	void TechniqueParameters (FbxNode *pNode, web::json::value &techniqueParameters, web::json::value &attributes, web::json::value &accessors, bool bHasMaterial =true) ;
  	web::json::value WriteTechnique (FbxNode *pNode, FbxSurfaceMaterial *pMaterial, web::json::value &techniqueParameters) ;
  	// textures
  	web::json::value WriteTextureBindings (FbxMesh *pMesh, FbxSurfaceMaterial *pMaterial, web::json::value &params) ;
  	web::json::value WriteTexture (FbxTexture *pTexture) ;
  
  	// buffer
  	bool WriteShaders () ;
  
  private:
  	typedef web::json::value (gltfWriter::*ExporterRouteFct) (FbxNode *pNode) ;
  	typedef std::map<FbxNodeAttribute::EType, ExporterRouteFct> ExporterRoutes ;
  	static ExporterRoutes _routes ;
  
  	template<class Type /*, const utility::char_t *Type*/>
  	web::json::value WriteArray (std::vector<Type> &data, int size, FbxNode *pNode, const utility::char_t *suffix) ;
  	template<class T, class Type /*, const utility::char_t *Type*/>
  	web::json::value WriteArray (FbxArray<T> &data, FbxNode *pNode, const utility::char_t *suffix) ;
  	template<class T, class Type /*, const utility::char_t *Type*/>
  	web::json::value WriteArray (std::vector<T> &data, FbxNode *pNode, const utility::char_t *suffix) ;
  
  	template<class T, class Type /*, const utility::char_t *Type*/>
  	web::json::value WriteArrayWithMinMax (FbxArray<T> &data, FbxNode *pNode, const utility::char_t *suffix) ;
  	template<class T, class Type /*, const utility::char_t *Type*/>
  	web::json::value WriteArrayWithMinMax (std::vector<T> &data, FbxNode *pNode, const utility::char_t *suffix) ;
  	template<class T, class Type /*, const utility::char_t *Type*/>
  	web::json::value WriteArrayWithMinMax (FbxArray<T> &data, T bMin, T bMax, FbxNode *pNode, const utility::char_t *suffix) ;
  	template<class T, class Type /*, const utility::char_t *Type*/>
  	web::json::value WriteArrayWithMinMax (std::vector<T> &data, T bMin, T bMax, FbxNode *pNode, const utility::char_t *suffix) ;
  
  } ;
  
  //-----------------------------------------------------------------------------
  template<class Type>
  web::json::value gltfWriter::WriteArray (std::vector<Type> &data, int size, FbxNode *pNode, const utility::char_t *suffix) {
  	std::ofstream::pos_type offset =_bin.tellg () ;
  	//std::copy (data.begin (), data.end (), std::ostream_iterator<Type> (_bin)) ;
  #if defined(_WIN32) || defined(_WIN64)
  	for ( std::vector<Type>::iterator iter =data.begin () ; iter != data.end () ; iter++ )
  		_bin.write ((uint8_t *)&(*iter), sizeof (Type)) ;
  #else
  	typedef typename std::vector<Type>::iterator iteratorType ;
  	for ( iteratorType iter =data.begin () ; iter != data.end () ; iter++ )
  		_bin.write ((uint8_t *)&(*iter), sizeof (Type)) ;
  #endif
  	// bufferView
  	web::json::value viewDef =web::json::value::object () ;
  	FbxString filename =FbxPathUtils::GetFileName (utility::conversions::to_utf8string (_fileName).c_str (), false) ;
  	viewDef [U("buffer")] =web::json::value::string (utility::conversions::to_string_t (filename.Buffer ())) ;
  	size_t nb =data.size () / size ;
  	viewDef [U("byteLength")] =web::json::value::number ((int)(sizeof (Type) * nb * size)) ;
  	viewDef [U("byteOffset")] =web::json::value::number ((int)offset) ;
  	// Array buffers (ARRAY_BUFFER) : These buffers contain vertex attributes, such as vertex coordinates, texture coordinate data,
  	// per vertex - color data, and normals.They can be interleaved (using the stride parameter) or sequential, with one array after
  	// another (write 1, 000 vertices, then 1, 000 normals, and so on).glVertexPointer and glNormalPointer each point to the appropriate offsets.
  	// Element array buffers (ELEMENT_ARRAY_BUFFER) : This type of buffer is used mainly for the element pointer in glDraw [Range]Elements ().
  	// It contains only indices of elements.
  	viewDef [U("target")] =size == 1 ? IOglTF::ELEMENT_ARRAY_BUFFER : IOglTF::ARRAY_BUFFER ; // Valid values are 34962 (ARRAY_BUFFER) or 34963 (ELEMENT_ARRAY_BUFFER)
  	viewDef [U("name")] =web::json::value::string (nodeId (pNode, true) + suffix + U("_Buffer")) ;
  	web::json::value view =web::json::value::object ({ {
  		nodeId (pNode, true) + suffix + U("_Buffer"),
  		viewDef
  	} }) ;
  
  	// Accessor
  	web::json::value accDef =web::json::value::object () ;
  	accDef [U("bufferView")] =web::json::value::string (nodeId (pNode, true) + suffix + U("_Buffer")) ;
  	accDef [U("byteOffset")] =web::json::value::number ((int)0) ;
  	accDef [U("byteStride")] =web::json::value::number (/*size == 1 ? 0 :*/ (int)sizeof (Type) * size) ;
  	accDef [U("componentType")] =web::json::value::number ((int)IOglTF::accessorComponentType<Type> ()) ;
  	accDef [U("count")] =web::json::value::number ((int)nb) ;
  	//accDef [U("min")] =web::json::value::array ({ { (float)bMin.Buffer () [0], (float)bMin.Buffer () [1], (float)bMin.Buffer () [2] } }) ;
  	//accDef [U("max")] =web::json::value::array ({ { (float)bMax.Buffer () [0], (float)bMax.Buffer () [1], (float)bMax.Buffer () [2] } }) ;
  	accDef [U("type")] =web::json::value::string (IOglTF::accessorType<Type> (size, 1)) ;
  	accDef [U("name")] =web::json::value::string (nodeId (pNode, true) + suffix) ;
  	web::json::value acc =web::json::value::object ({ {
  		nodeId (pNode, true) + suffix,
  		accDef
  	} }) ;
  
  	return (web::json::value::object ({ { U("accessors"), acc }, { U("bufferViews"), view } })) ;
  }
  
  template<class T, class Type>
  web::json::value gltfWriter::WriteArray (FbxArray<T> &data, FbxNode *pNode, const utility::char_t *suffix) {
  	// glTF/Collada do not support double, convert to float (or Type)
  	int size =sizeof (decltype(std::declval<T> ().mData)) / sizeof (decltype(std::declval<T> ().mData [0])) ;
  	int nb =data.GetCount () ;
  	std::vector<Type> fdata (nb * size) ;
  	for ( int i =0 ; i < nb ; i++ )
  		for ( int j =0 ; j < size ; j++ )
  			fdata [i * size + j] =(Type)data [i].Buffer () [j] ;
  
  	return (WriteArray<Type> (fdata, size, pNode, suffix)) ;
  }
  
  template<class T, class Type>
  web::json::value gltfWriter::WriteArray (std::vector<T> &data, FbxNode *pNode, const utility::char_t *suffix) {
  	// glTF/Collada do not support double, convert to float (or Type)
  	int size =sizeof (decltype(std::declval<T> ().mData)) / sizeof (decltype(std::declval<T> ().mData [0])) ;
  	int nb =(int)data.size () ;
  	std::vector<Type> fdata (nb * size) ;
  	for ( int i =0 ; i < nb ; i++ )
  		for ( int j =0 ; j < size ; j++ )
  			fdata [i * size + j] =(Type)data [i] [j] ;
  	return (WriteArray<Type> (fdata, size, pNode, suffix)) ;
  }
  
  template<class T, class Type>
  web::json::value gltfWriter::WriteArrayWithMinMax (FbxArray<T> &data, FbxNode *pNode, const utility::char_t *suffix) {
  	int nb =data.GetCount () ;
  	std::vector<T> fdata (nb) ;
  	for ( int i =0 ; i < nb ; i++ )
  		fdata [i] =data [i] ;
  	return (WriteArrayWithMinMax<T, Type> (fdata, pNode, suffix)) ;
  }
  
  template<class T, class Type>
  web::json::value gltfWriter::WriteArrayWithMinMax (std::vector<T> &data, FbxNode *pNode, const utility::char_t *suffix) {
  	int size =sizeof (decltype(std::declval<T> ().mData)) / sizeof (decltype(std::declval<T> ().mData [0])) ;
  #pragma push_macro ("min")
  #undef min
  #pragma push_macro ("max")
  #undef max
  	T bMin ;
  	for ( int i =0 ; i < size ; i++ )
  		bMin [i] =std::numeric_limits<double>::max () ; // DBL_MAX
  	T bMax ;
  	for ( int i =0 ; i < size ; i++ )
  		bMax [i] =std::numeric_limits<double>::min () ; // DBL_MIN
  	int nb =(int)data.size () ;
  	for ( int i =0 ; i < nb ; i++ ) {
  		for ( int j =0 ; j < size ; j++ ) {
  			bMin [j] =std::min (bMin [j], data [i] [j]) ;
  			bMax [j] =std::max (bMax [j], data [i] [j]) ;
  		}
  	}
  #pragma pop_macro ("min")
  #pragma pop_macro ("max")
  	return (WriteArrayWithMinMax<T, Type> (data, bMin, bMax, pNode, suffix)) ;
  }
  
  template<class T, class Type>
  web::json::value gltfWriter::WriteArrayWithMinMax (FbxArray<T> &data, T bMin, T bMax, FbxNode *pNode, const utility::char_t *suffix) {
  	int nb =data.GetCount () ;
  	std::vector<T> fdata (nb) ;
  	for ( int i =0 ; i < nb ; i++ )
  		fdata [i] =data [i] ;
  	return (WriteArrayWithMinMax<T, Type> (fdata, bMin, bMax, pNode, suffix)) ;
  }
  
  template<class T, class Type>
  web::json::value gltfWriter::WriteArrayWithMinMax (std::vector<T> &data, T bMin, T bMax, FbxNode *pNode, const utility::char_t *suffix) {
  	web::json::value ret =WriteArray<T, Type> (data, pNode, suffix) ;
  	web::json::value accDef =ret [U ("accessors")] [GetJsonFirstKey (ret [U ("accessors")])] ;
  	//accDef [U("min")] =web::json::value::array ({ { (float)bMin.Buffer () [0], (float)bMin.Buffer () [1], (float)bMin.Buffer () [2] } }) ;
  	//accDef [U("max")] =web::json::value::array ({ { (float)bMax.Buffer () [0], (float)bMax.Buffer () [1], (float)bMax.Buffer () [2] } }) ;
  	int size =sizeof (decltype(std::declval<T> ().mData)) / sizeof (decltype(std::declval<T> ().mData [0])) ;
  	accDef [U ("min")] =web::json::value::array () ;
  	for ( int j =0 ; j < size ; j++ )
  		accDef [U ("min")] [j] =(Type)bMin.Buffer () [j] ;
  	accDef [U ("max")] =web::json::value::array () ;
  	for ( int j =0 ; j < size ; j++ )
  		accDef [U ("max")] [j] =(Type)bMax.Buffer () [j] ;
  	ret [U ("accessors")] [GetJsonFirstKey (ret [U ("accessors")])] =accDef ;
  	return (ret) ;
  }
  
  }
  