diff --git a/base/physics_test.blend b/base/physics_test.blend new file mode 100644 index 00000000..a6e020b1 Binary files /dev/null and b/base/physics_test.blend differ diff --git a/base/physics_test.glb b/base/physics_test.glb new file mode 100644 index 00000000..1375b140 Binary files /dev/null and b/base/physics_test.glb differ diff --git a/neo/framework/CmdSystem.h b/neo/framework/CmdSystem.h index ac3b05bc..76f51537 100644 --- a/neo/framework/CmdSystem.h +++ b/neo/framework/CmdSystem.h @@ -243,7 +243,7 @@ ID_INLINE void idCmdSystem::ArgCompletion_FileName( const idCmdArgs& args, void( ID_INLINE void idCmdSystem::ArgCompletion_MapName( const idCmdArgs& args, void( *callback )( const char* s ) ) { - cmdSystem->ArgCompletion_FolderExtension( args, callback, "maps/", true, ".map", ".json", NULL ); + cmdSystem->ArgCompletion_FolderExtension( args, callback, "maps/", true, ".map", ".json",".gltf",".glb", NULL ); } ID_INLINE void idCmdSystem::ArgCompletion_MapNameNoJson( const idCmdArgs& args, void( *callback )( const char* s ) ) diff --git a/neo/idlib/Lexer.cpp b/neo/idlib/Lexer.cpp index 58063167..677fc43d 100644 --- a/neo/idlib/Lexer.cpp +++ b/neo/idlib/Lexer.cpp @@ -1507,31 +1507,31 @@ Skips until a matching close brace is found. Internal brace depths are properly skipped. ================= */ -int idLexer::SkipBracedSection( bool parseFirstBrace ) -{ +int idLexer::SkipBracedSection( bool parseFirstBrace, braceSkipMode_t skipMode/* = BRSKIP_BRACE */, int * skipped /*= nullptr*/) { idToken token; int depth; + idStr openTokens[2] = { "{" , "[" }; + idStr closeTokens[2] = { "}" , "]" }; + if ( skipped != nullptr ) + *skipped = 0; + + int scopeCount = 0; depth = parseFirstBrace ? 0 : 1; - do - { - if( !ReadToken( &token ) ) - { + do { + if ( !ReadToken( &token ) ) { return false; } - if( token.type == TT_PUNCTUATION ) - { - if( token == "{" ) - { + if ( token.type == TT_PUNCTUATION ) { + if ( token == openTokens[skipMode] ) { depth++; - } - else if( token == "}" ) - { + if ( skipped != nullptr ) + (*skipped)++; + } else if ( token == closeTokens[skipMode] ) { depth--; } } - } - while( depth ); + } while( depth ); return true; } @@ -2083,8 +2083,9 @@ void idLexer::Reset() // set if there's a token available in idLexer::token idLexer::tokenavailable = 0; - idLexer::line = 1; - idLexer::lastline = 1; + idLexer::line = intialLine; + idLexer::lastline = intialLine; + // clear the saved token idLexer::token = ""; } @@ -2166,6 +2167,7 @@ int idLexer::LoadFile( const char* filename, bool OSPath ) idLexer::tokenavailable = 0; idLexer::line = 1; + idLexer::line = 1; idLexer::lastline = 1; idLexer::allocated = true; idLexer::loaded = true; @@ -2199,6 +2201,7 @@ int idLexer::LoadMemory( const char* ptr, int length, const char* name, int star idLexer::tokenavailable = 0; idLexer::line = startLine; idLexer::lastline = startLine; + idLexer::intialLine = startLine; idLexer::allocated = false; idLexer::loaded = true; diff --git a/neo/idlib/Lexer.h b/neo/idlib/Lexer.h index b037e229..a1172fb0 100644 --- a/neo/idlib/Lexer.h +++ b/neo/idlib/Lexer.h @@ -64,6 +64,12 @@ typedef enum LEXFL_ONLYSTRINGS = BIT( 13 ) // parse as whitespace deliminated strings (quoted strings keep quotes) } lexerFlags_t; +typedef enum { + BRSKIP_BRACES, + BRSKIP_BRACKET +} braceSkipMode_t; + + // punctuation ids #define P_RSHIFT_ASSIGN 1 #define P_LSHIFT_ASSIGN 2 @@ -182,7 +188,7 @@ public: // skip the rest of the current line int SkipRestOfLine(); // skip the braced section - int SkipBracedSection( bool parseFirstBrace = true ); + int SkipBracedSection( bool parseFirstBrace = true , braceSkipMode_t skipMode = BRSKIP_BRACES,int * skipped = nullptr); // skips spaces, tabs, C-like comments etc. Returns false if there is no token left to read. bool SkipWhiteSpace( bool currentLine ); // unread the given token @@ -267,6 +273,7 @@ private: int length; // length of the script in bytes int line; // current line in script int lastline; // line before reading token + int intialLine; // line that was set on load as starting line int tokenavailable; // set by unreadToken int flags; // several script flags const punctuation_t* punctuations; // the punctuations used in the script diff --git a/neo/idlib/MapFile.cpp b/neo/idlib/MapFile.cpp index f00138b9..fb5edb13 100644 --- a/neo/idlib/MapFile.cpp +++ b/neo/idlib/MapFile.cpp @@ -1553,6 +1553,7 @@ bool idMapFile::Parse( const char* filename, bool ignoreRegion, bool osPath ) idMapEntity* mapEnt; int i, j, k; + idStr extension; name = filename; name.StripFileExtension(); name.StripFileExtension(); // RB: there might be .map.map @@ -1572,6 +1573,18 @@ bool idMapFile::Parse( const char* filename, bool ignoreRegion, bool osPath ) } } + bool isGTLF = false; + if ( !src.IsLoaded( ) ) { + // HVG: try loading a .gltf/glb second + fullName.SetFileExtension( "glb" ); + isGTLF = src.LoadFile( fullName, osPath ); + if ( !isGTLF ) + { + fullName.SetFileExtension( "gltf" ); + isGTLF = src.LoadFile( fullName, osPath ); + } + } + if( !src.IsLoaded() ) { // now try a .map file @@ -1588,17 +1601,11 @@ bool idMapFile::Parse( const char* filename, bool ignoreRegion, bool osPath ) fileTime = src.GetFileTime(); entities.DeleteContents( true ); - if( !src.ReadToken( &token ) ) + if(!isGTLF && !src.ReadToken( &token ) ) { return false; } - // RB: TODO check for JSON in another way - //if( token == "{" ) - //{ - // isJSON = true; - //} - if( isJSON ) { while( true ) @@ -1661,7 +1668,11 @@ bool idMapFile::Parse( const char* filename, bool ignoreRegion, bool osPath ) } } } - else + else if ( isGTLF ) + { + gltfParser->Load( fullName ); + idMapEntity::GetEntities(gltfParser->currentAsset,entities,0); + }else { if( token == "Version" ) { diff --git a/neo/idlib/MapFile.h b/neo/idlib/MapFile.h index a89b2510..46ba3794 100644 --- a/neo/idlib/MapFile.h +++ b/neo/idlib/MapFile.h @@ -29,6 +29,7 @@ If you have questions concerning this license or the applicable additional terms #ifndef __MAPFILE_H__ #define __MAPFILE_H__ +#include "gltfProperties.h" /* =============================================================================== @@ -348,7 +349,7 @@ public: void ConvertFromBrush( const idMapBrush* brush, int entityNum, int primitiveNum ); void ConvertFromPatch( const idMapPatch* patch, int entityNum, int primitiveNum ); - + void ConvertFromMeshGltf( const gltfMesh * mesh , gltfData * data ); static MapPolygonMesh* Parse( idLexer& src, const idVec3& origin, float version = CURRENT_MAP_VERSION ); bool Write( idFile* fp, int primitiveNum, const idVec3& origin ) const; @@ -367,6 +368,9 @@ public: return verts.Append( v ); } + int AddVertices( const idList &v ) { + return verts.Append( v ); + } int GetNumPolygons() const { @@ -422,6 +426,10 @@ protected: class idMapEntity { + typedef idList EntityList; + typedef idList &EntityListRef; + typedef idList *EntityListPtr; + friend class idMapFile; public: @@ -437,8 +445,12 @@ public: { primitives.DeleteContents( true ); } + // HVG check gltf scene for entities + static int GetEntities( gltfData * data, EntityListRef entities, int scene = 0 ); static idMapEntity* Parse( idLexer& src, bool worldSpawn = false, float version = CURRENT_MAP_VERSION ); bool Write( idFile* fp, int entityNum, bool valve220 ) const; + + // HVG NOTE: this is not compatible with gltf (extra) json! // RB begin static idMapEntity* ParseJSON( idLexer& src ); bool WriteJSON( idFile* fp, int entityNum, int numEntities ) const; @@ -538,7 +550,7 @@ protected: float version; ID_TIME_T fileTime; unsigned int geometryCRC; - idList entities; + idMapEntity::EntityList entities; idStr name; bool hasPrimitiveData; bool valve220Format; // RB: for TrenchBroom support diff --git a/neo/idlib/containers/List.h b/neo/idlib/containers/List.h index f533e9b2..4bc0f967 100644 --- a/neo/idlib/containers/List.h +++ b/neo/idlib/containers/List.h @@ -203,29 +203,21 @@ public: memTag = ( byte )tag_; }; - // Begin/End methods for range-based for loops. - _type_* begin() - { - if( num > 0 ) - { - return &list[0]; + struct Iterator { + _type_ *p; + _type_ &operator*( ) { return *p; } + bool operator != ( const Iterator &rhs ) { + return p != rhs.p; } - else - { - return nullptr; - } - } - _type_* end() - { - if( num > 0 ) - { - return &list[num - 1]; - } - else - { - return nullptr; - } - } + void operator ++( ) { ++p; } + }; + + auto begin( ) const { // const version + return Iterator{list}; + }; + auto end( ) const { // const version + return Iterator{list + Num( )}; + }; private: int num; diff --git a/neo/idlib/gltfExtras.cpp b/neo/idlib/gltfExtras.cpp new file mode 100644 index 00000000..3f9ee909 --- /dev/null +++ b/neo/idlib/gltfExtras.cpp @@ -0,0 +1,35 @@ +#include "precompiled.h" +#pragma hdrstop +#include "gltfExtras.h" + +extern idCVar gltf_parseVerbose; + +void gltfExtra_Scatter::parse( idToken &token, idLexer * parser ) { + + parser->UnreadToken( &token ); + + gltfItemArray scatterInfo; + GLTFARRAYITEM( scatterInfo, emitter, gltfObject ); + scatterInfo.Parse( parser,true ); +} + +void gltfExtra_cvar::parse( idToken &token, idLexer *parser ) { + + parser->UnreadToken( &token ); + gltfItemArray cvarInfo; + idStr n,t,v,d; + GLTFARRAYITEMREF( cvarInfo, name, gltfItem , n); + GLTFARRAYITEMREF( cvarInfo, type, gltfItem , t); + GLTFARRAYITEMREF( cvarInfo, value, gltfItem, v ); + GLTFARRAYITEMREF( cvarInfo, desc, gltfItem , d); + int total = cvarInfo.Parse( parser ); + assert( total == 3 ); + idCVar * gltExtra_cvar = new idCVar( + n.c_str(), + v.c_str(), + CVAR_SYSTEM | CVAR_BOOL, + d.c_str() + ); + + cvarSystem->Register(gltExtra_cvar); +} \ No newline at end of file diff --git a/neo/idlib/gltfExtras.h b/neo/idlib/gltfExtras.h new file mode 100644 index 00000000..69c785f7 --- /dev/null +++ b/neo/idlib/gltfExtras.h @@ -0,0 +1,35 @@ +#include "gltfParser.h" + +#ifndef gltfExtraParser +#define gltfExtraParser(className,ptype) \ + class gltfExtra_##className : public parsable, public parseType \ + {public: \ + gltfExtra_##className( idStr Name ) : name( Name ){ item = nullptr; } \ + virtual void parse( idToken &token ){parse(token,nullptr);} \ + virtual void parse( idToken &token , idLexer * parser ); \ + virtual idStr &Name( ) { return name; } \ + private: \ + idStr name;} +#pragma endregion +#endif + +//Helper macros for gltf data deserialize +#define GLTFARRAYITEM(target,name,type) auto * name = new type (#name); target.AddItemDef((parsable*)name) +#define GLTFARRAYITEMREF(target,name,type,ref) auto * name = new type (#name); target.AddItemDef((parsable*)name); name->Set(&ref) +#ifndef GLTF_EXTRAS_H +#define GLTF_EXTRAS_H + +class test { +public: + test(){ } +}; + + +gltfExtraParser( Scatter, test ); + +gltfExtraParser( cvar, idCVar ); +#endif // GLTF_EXTRAS_H + +#ifndef gltfExternalParser +#undef gltfExtraParser +#endif \ No newline at end of file diff --git a/neo/idlib/gltfParser.cpp b/neo/idlib/gltfParser.cpp new file mode 100644 index 00000000..05ab3aa9 --- /dev/null +++ b/neo/idlib/gltfParser.cpp @@ -0,0 +1,2111 @@ +#include "precompiled.h" +#pragma hdrstop + +/// +/// Clean up registerd gltfItem_Extra's +/// Clean up loaded gltfData; +/// + +static const unsigned int gltfChunk_Type_JSON = 0x4E4F534A; //1313821514 +static const unsigned int gltfChunk_Type_BIN = 0x004E4942; //5130562 + +idCVar gltfParser_PrefixNodeWithID( "gltfParser_PrefixNodeWithID", "0", CVAR_SYSTEM | CVAR_BOOL, "The node's id is prefixed to the node's name during load" ); +// +//gltf_sampler_wrap_type_map s_samplerWrapTypeMap[] = { +// //33071 CLAMP_TO_EDGE +// 33071, BGFX_SAMPLER_U_CLAMP, BGFX_SAMPLER_V_CLAMP, +// //33648 MIRRORED_REPEAT +// 33648, BGFX_SAMPLER_U_MIRROR, BGFX_SAMPLER_V_MIRROR, +// //10497 REPEAT +// 10497, BGFX_SAMPLER_NONE , BGFX_SAMPLER_NONE , +// 0,0,0 +//}; +////todo +//gltf_sampler_mag_type_map s_samplerMagTypeMap[] = { +// //9728 NEAREST //mag/min +// 9728 , BGFX_SAMPLER_MIN_ANISOTROPIC +// //9729 LINEAR // mag/min +// // +// //9984 NEAREST_MIPMAP_NEAREST //min +// // +// //9985 LINEAR_MIPMAP_NEAREST //min +// // +// //9986 NEAREST_MIPMAP_LINEAR //min +// // +// //9987 LINEAR_MIPMAP_LINEAR //min +//}; + +//uint64_t GetSamplerFlags( gltfSampler * sampler) { +// // Ignore the sampling options for filter -- always use mag: LINEAR and min: LINEAR_MIPMAP_LINEAR +// uint64_t flags ;//= BGFX_TEXTURE_NONE | BGFX_SAMPLER_MIN_ANISOTROPIC ; +// int i = -1; +// while ( s_samplerWrapTypeMap[++i].id != 0 ) +// { +// if ( s_samplerWrapTypeMap[i].id == sampler->wrapS) +// flags |= s_samplerWrapTypeMap[i].bgfxFlagU; +// if ( s_samplerWrapTypeMap[i].id == sampler->wrapT ) +// flags |= s_samplerWrapTypeMap[i].bgfxFlagV; +// } +// return flags; +//} + + +gltf_mesh_attribute_map s_meshAttributeMap[] = { + "POSITION", gltfMesh_Primitive_Attribute::Type::Position, 3, + "NORMAL", gltfMesh_Primitive_Attribute::Type::Normal, 3, + "TANGENT", gltfMesh_Primitive_Attribute::Type::Tangent, 3, + "TEXCOORD_0", gltfMesh_Primitive_Attribute::Type::TexCoord0, 2, + "TEXCOORD_1", gltfMesh_Primitive_Attribute::Type::TexCoord1, 2, + "TEXCOORD_2", gltfMesh_Primitive_Attribute::Type::TexCoord2, 2, + "TEXCOORD_3", gltfMesh_Primitive_Attribute::Type::TexCoord3, 2, + "TEXCOORD_4", gltfMesh_Primitive_Attribute::Type::TexCoord4, 2, + "TEXCOORD_5", gltfMesh_Primitive_Attribute::Type::TexCoord5, 2, + "TEXCOORD_6", gltfMesh_Primitive_Attribute::Type::TexCoord6, 2, + "TEXCOORD_7", gltfMesh_Primitive_Attribute::Type::TexCoord7, 2, + "COLOR_0", gltfMesh_Primitive_Attribute::Type::Color0, 4, + "COLOR_1", gltfMesh_Primitive_Attribute::Type::Color1, 4, + "COLOR_2", gltfMesh_Primitive_Attribute::Type::Color2, 4, + "COLOR_3", gltfMesh_Primitive_Attribute::Type::Color3, 4, + "WEIGHTS_0", gltfMesh_Primitive_Attribute::Type::Weight, 4, + "JOINTS_0", gltfMesh_Primitive_Attribute::Type::Indices, 4, + "", gltfMesh_Primitive_Attribute::Type::Count +}; + +gltfMesh_Primitive_Attribute::Type GetAttributeEnum( const char *str , uint * elementSize = nullptr) { + int i = -1; + while ( s_meshAttributeMap[++i].attib != gltfMesh_Primitive_Attribute::Type::Count ) + if ( !idStr::Icmp( s_meshAttributeMap[i].stringID, str ) ) + { + if (elementSize != nullptr) + *elementSize = s_meshAttributeMap[i].elementSize; + return s_meshAttributeMap[i].attib; + } + + return gltfMesh_Primitive_Attribute::Type::Count; +} + +//https://github.com/KhronosGroup/glTF/issues/832 +//gltf_accessor_component_type_map s_bgfxComponentTypeMap[] = { +// "signed byte", 5120, bgfx::AttribType::Count, 1 , +// "unsigned byte", 5121, bgfx::AttribType::Uint8, 1 , +// "signed short", 5122, bgfx::AttribType::Int16, 2 , +// "unsigned short", 5123, bgfx::AttribType::Count, 2 , +// "unsigned int", 5125, bgfx::AttribType::Count, 4 , +// "float", 5126, bgfx::AttribType::Float, 4 , +// "double", 5130, bgfx::AttribType::Float, 8 , +// "", 0, bgfx::AttribType::Count, 0 +//}; +// +gltf_accessor_component_type_map s_nativeComponentTypeMap[] = { + "signed byte", 5120, gltf_accessor_component::Type::_byte, 1 , + "unsigned byte", 5121, gltf_accessor_component::Type::_uByte, 1 , + "signed short", 5122, gltf_accessor_component::Type::_short, 2 , + "unsigned short", 5123, gltf_accessor_component::Type::_uShort, 2 , + "unsigned int", 5125, gltf_accessor_component::Type::_uInt, 4 , + "float", 5126, gltf_accessor_component::Type::_float, 4 , + "double", 5130, gltf_accessor_component::Type::_double, 8 , + "", 0, gltf_accessor_component::Type::Count, 0 +}; +// +gltf_accessor_component::Type GetComponentTypeEnum( int id , uint * sizeInBytes = nullptr) { + int i = -1; + while ( s_nativeComponentTypeMap[++i].id != 0) + if ( s_nativeComponentTypeMap[i].id == id ) { + if (sizeInBytes != nullptr ) + *sizeInBytes = s_nativeComponentTypeMap[i].sizeInBytes; + + return s_nativeComponentTypeMap[i].type; + } + + return gltf_accessor_component::Type::Count; +} + +//some arbitrary amount for now. +#define GLTF_MAX_CHUNKS 32 + +idList gltfData::dataList; +idHashIndex gltfData::fileDataHash; +gltfItemArray* gltfItem_Extra::items = new gltfItemArray(); + +idCVar gltf_parseVerbose( "gltf_parseVerbose", "0", CVAR_RENDERER | CVAR_ARCHIVE | CVAR_BOOL, "print gltf json data while parsing" ); + +//Helper macros for gltf data deserialize +//NOTE: gltfItems that deviate from the default SET(T*) function cannot be handled with itemref macro. +// target must be an gltfArrayItem. +// type with name will be added to the array. +#define GLTFARRAYITEM(target,name,type) auto * name = new type (#name); target.AddItemDef((parsable*)name) +// name must point to an existing valid entry +// name->Set (&target->name); +#define GLTFARRAYITEMREF(target,name) name->Set(&target->name) + +void gltfPropertyArray::Iterator::operator ++( ) { + + //check if by modification, we are iterating again. + //custom not AOS things can do this since it is not nicely guarded by braces + if ( array->dirty && (!array->iterating && !array->isArrayOfStructs) ) { + array->iterating = array->parser->PeekTokenString( "," ); + if ( array->iterating ) { + array->properties.AssureSizeAlloc( array->properties.Num( ) + 1, idListNewElement ); + array->parser->ExpectTokenString( "," ); + } + } + + if ( array->iterating ) + { + p = array->properties[array->properties.Num( ) - 1]; + p->array = array; + if (array->isArrayOfStructs ) + array->parser->ParseBracedSection( p->item ); + else + { + idToken token; + array->parser->ExpectAnyToken( &token ); + p->item = token; + } + array->iterating = array->parser->PeekTokenString( "," ); + if ( array->iterating ) { + array->properties.AssureSizeAlloc( array->properties.Num( ) + 1, idListNewElement ); + array->parser->ExpectTokenString( "," ); + } + }else + { + if ( array->dirty ){ + p = array->endPtr; + array->dirty = false; + } + else if ( array->index + 1 < array->properties.Num() ) + p = array->properties[++array->index]; + else + p = array->endPtr; + } +} +gltfPropertyArray::~gltfPropertyArray() +{ + delete endPtr; + properties.DeleteContents(true); +} +gltfPropertyArray::gltfPropertyArray( idLexer *Parser, bool AoS/* = true */) + : parser( Parser ), iterating( true ), dirty( true ), index( 0 ), isArrayOfStructs(AoS) +{ + properties.AssureSizeAlloc(32, idListNewElement ); + properties.SetNum(0); + endPtr = new gltfPropertyItem(); + endPtr->array = this; +} + +gltfPropertyArray::Iterator gltfPropertyArray::begin( ) { + if ( iterating ) + { + if ( isArrayOfStructs && !parser->PeekTokenString( "{" ) ) { + if ( !parser->ExpectTokenString( "[" ) && parser->PeekTokenString( "{" ) ) + common->FatalError( "Malformed gltf array" ); + }else if ( !isArrayOfStructs && !parser->ExpectTokenString( "[") ) + common->FatalError( "Malformed gltf array" ); + + properties.AssureSizeAlloc( properties.Num() + 1,idListNewElement ); + gltfPropertyItem *start = properties[0]; + start->array = this; + if (isArrayOfStructs ) + parser->ParseBracedSection( start->item ); + else + { + idToken token; + parser->ExpectAnyToken(&token); + start->item = token; + } + iterating = parser->PeekTokenString( "," ); + if ( iterating ) + { + properties.AssureSizeAlloc( properties.Num() + 1,idListNewElement ); + parser->ExpectTokenString( "," ); + } + + return Iterator{ this , start }; + } + index = 0; + return Iterator{ this ,properties[index]}; +} +gltfPropertyArray::Iterator gltfPropertyArray::end( ) { + return Iterator{ this , endPtr}; +} +int gltfItemArray::Fill( idLexer *lexer, idDict *strPairs ) { + idToken token; + bool parsing = true; + int parseCount = 0; + lexer->ExpectTokenString( "{" ); + while ( parsing && !lexer->PeekTokenString( "}" ) && lexer->ExpectAnyToken( &token ) ) { + lexer->ExpectTokenString( ":" ); + idStr key = token; + idStr value; + key.StripTrailingWhitespace( ); + if ( lexer->PeekTokenString( "{" )) + { + lexer->ParseBracedSectionExact(value); + value.StripTrailingWhitespace( ); + strPairs->Set( key, value ); + }else + { + lexer->ExpectAnyToken( &token ); + value = token; + value.StripTrailingWhitespace( ); + key.StripTrailingWhitespace( ); + strPairs->Set( key, token ); + } + + parseCount++; + parsing = lexer->PeekTokenString( "," ); + if ( parsing ) + lexer->ExpectTokenString( "," ); + } + lexer->ExpectTokenString( "}" ); + return parseCount; +} +int gltfItemArray::Parse(idLexer * lexer, bool forwardLexer/* = false*/) { + idToken token; + bool parsing = true; + int parseCount = 0; + lexer->ExpectTokenString( "{" ); + while ( parsing && !lexer->PeekTokenString( "}" ) && lexer->ExpectAnyToken( &token ) ) + { + lexer->ExpectTokenString( ":" ); + bool parsed = false; + for ( auto item : items ) + { + if ( item->Name( ) == token ) + { + lexer->ExpectAnyToken( &token ); + if ( forwardLexer ) + item->parse( token , lexer ); + else + item->parse( token ); + parsed = true; + break; + } + } + if (!parsed) + lexer->SkipBracedSection(); + else + parseCount++; + + parsing = lexer->PeekTokenString( "," ); + if ( parsing ) + lexer->ExpectTokenString( "," ); + } + lexer->ExpectTokenString( "}" ); + return parseCount; +} + +byte * gltfData::AddData(int size, int * bufferID/*=nullptr*/) +{ + if (totalChunks == -1 ) + { + json = (byte*) Mem_ClearedAlloc(size,TAG_IDLIB_GLTF); + totalChunks++; + jsonDataLength = size; + return json; + } + + int id = totalChunks; + + if (data == nullptr ) + data = ( byte ** ) Mem_ClearedAlloc( GLTF_MAX_CHUNKS * sizeof( byte * ),TAG_IDLIB_GLTF ); + data[totalChunks++] = (byte*) Mem_ClearedAlloc(size,TAG_IDLIB_GLTF); + + if ( bufferID ) + *bufferID = id; + + return data[id]; +} + +bool gltfItem_uri::Convert( ) { + //HVG_TODO + // uri cache. + //read data + int length = fileSystem->ReadFile( item->c_str( ), NULL ); + idFile *file = fileSystem->OpenFileRead( item->c_str() ); + + //create buffer + gltfBuffer *buffer = data->Buffer( ); + buffer->parent = data; + buffer->name = item->c_str( ); + buffer->byteLength = length; + int bufferID = -1; + byte * dest = data->AddData( length, &bufferID ); + + if (file->Read( dest, length ) != length) + common->FatalError("Could not read %s",item->c_str() ); + + if (gltf_parseVerbose.GetBool() ) + common->Warning("gltf Uri %s loaded into buffer[ %i ]",buffer->name.c_str(),bufferID ); + + //create bufferview + //if bufferview is not set, this is an buffer.uri. + //A bufferview should aready be defined if the buffer is used. + if (bufferView != nullptr ) + { + *bufferView = data->BufferViewList().Num(); + gltfBufferView * newBufferView = data->BufferView( ); + newBufferView->buffer = bufferID; + newBufferView->byteLength = length; + newBufferView->parent = data; + } + + fileSystem->CloseFile( file ); + + return false; +} + +void gltfItem_Extra::parse( idToken &token ) { + parser->UnreadToken( &token ); + parser->ParseBracedSectionExact(item->json); + + idLexer lexer( LEXFL_ALLOWPATHNAMES | LEXFL_ALLOWMULTICHARLITERALS | LEXFL_NOSTRINGESCAPECHARS | LEXFL_ALLOWPATHNAMES ); + lexer.LoadMemory( item->json, item->json.Size( ), "gltfItem_Extra", 0 ); + items->Fill(&lexer,&item->strPairs); + lexer.Reset(); + items->Parse( &lexer , true ); + + if ( gltf_parseVerbose.GetBool( ) ) + common->Printf( "%s", item->json.c_str( ) ); +} + +void gltfItem_Extra::Register( parsable *extra ) { + common->DPrintf("...Registering gltf Extra \"%s\" total(%i)\n",extra->Name().c_str(),items->Num() ); + items->AddItemDef( extra ); +} + +void gltfItem_animation_sampler::parse( idToken &token ) { + gltfItemArray animSampler; + GLTFARRAYITEM( animSampler, input, gltfItem_integer); + GLTFARRAYITEM( animSampler, interpolation, gltfItem ); + GLTFARRAYITEM( animSampler, output, gltfItem_integer); + GLTFARRAYITEM( animSampler, extensions, gltfItem ); + GLTFARRAYITEM( animSampler, extras, gltfItem_Extra); + + gltfPropertyArray array = gltfPropertyArray( parser ); + for ( auto &prop : array ) { + idLexer lexer( LEXFL_ALLOWPATHNAMES | LEXFL_ALLOWMULTICHARLITERALS | LEXFL_NOSTRINGESCAPECHARS | LEXFL_ALLOWPATHNAMES ); + lexer.LoadMemory( prop.item.c_str( ), prop.item.Size( ), "gltfAnimation_Sampler", 0 ); + + item->AssureSizeAlloc( item->Num( ) + 1, idListNewElement ); + gltfAnimation_Sampler *gltfAnimSampler = ( *item )[item->Num( ) - 1]; + + GLTFARRAYITEMREF( gltfAnimSampler, input ); + GLTFARRAYITEMREF( gltfAnimSampler, interpolation ); + GLTFARRAYITEMREF( gltfAnimSampler, output ); + GLTFARRAYITEMREF( gltfAnimSampler, extensions ); + extras->Set ( &gltfAnimSampler->extras, &lexer ); + animSampler.Parse( &lexer ); + if ( gltf_parseVerbose.GetBool( ) ) + common->Printf( "%s", prop.item.c_str( ) ); + + gltfAnimSampler->intType = gltfAnimation_Sampler::resolveType(gltfAnimSampler->interpolation); + } + parser->ExpectTokenString( "]" ); +} + +void gltfItem_animation_channel_target::parse( idToken &token ) { + parser->UnreadToken( &token ); + gltfItemArray animChannelTarget; + GLTFARRAYITEM( animChannelTarget, node, gltfItem_integer); + GLTFARRAYITEM( animChannelTarget, path, gltfItem ); + GLTFARRAYITEM( animChannelTarget, extensions, gltfItem ); + GLTFARRAYITEM( animChannelTarget, extras, gltfItem_Extra ); + + GLTFARRAYITEMREF( item, node ); + GLTFARRAYITEMREF( item, path ); + GLTFARRAYITEMREF( item, extensions ); + extras->Set ( &item->extras, parser ); + animChannelTarget.Parse( parser ); + + if ( gltf_parseVerbose.GetBool( ) ) + common->Printf( "%s", token.c_str( ) ); + + item->TRS = gltfAnimation_Channel_Target::resolveType(item->path); +} + +void gltfItem_animation_channel::parse( idToken &token ) { + //parser->UnreadToken( &token ); + gltfItemArray anim; + GLTFARRAYITEM( anim, sampler, gltfItem_integer ); + GLTFARRAYITEM( anim, target, gltfItem_animation_channel_target ); + GLTFARRAYITEM( anim, extensions, gltfItem ); + GLTFARRAYITEM( anim, extras, gltfItem_Extra ); + + gltfPropertyArray array = gltfPropertyArray( parser ); + for ( auto &prop : array ) { + idLexer lexer( LEXFL_ALLOWPATHNAMES | LEXFL_ALLOWMULTICHARLITERALS | LEXFL_NOSTRINGESCAPECHARS | LEXFL_ALLOWPATHNAMES ); + lexer.LoadMemory( prop.item.c_str( ), prop.item.Size( ), "gltfAnimation_Channel", 0 ); + + + item->AssureSizeAlloc( item->Num( ) + 1, idListNewElement ); + gltfAnimation_Channel *gltfAnimationChannel = ( *item )[item->Num( ) - 1]; + + GLTFARRAYITEMREF( gltfAnimationChannel, sampler ); + target->Set (&gltfAnimationChannel->target,&lexer ); + GLTFARRAYITEMREF( gltfAnimationChannel, extensions ); + extras->Set (&gltfAnimationChannel->extras,&lexer); + anim.Parse( &lexer ); + if ( gltf_parseVerbose.GetBool( ) ) + common->Printf( "%s", token.c_str( ) ); + + } + parser->ExpectTokenString( "]" ); +} + +void gltfItem_mesh_primitive::parse( idToken &token ) +{ + gltfItemArray prim; + GLTFARRAYITEM( prim, attributes, gltfItem_mesh_primitive_attribute ); + GLTFARRAYITEM( prim, indices, gltfItem_integer ); + GLTFARRAYITEM( prim, material, gltfItem_integer ); + GLTFARRAYITEM( prim, mode, gltfItem_integer ); + GLTFARRAYITEM( prim, target, gltfItem ); + GLTFARRAYITEM( prim, extensions, gltfItem ); + GLTFARRAYITEM( prim, extras, gltfItem_Extra ); + + gltfPropertyArray array = gltfPropertyArray( parser ); + for ( auto &prop : array ) { + idLexer lexer( LEXFL_ALLOWPATHNAMES | LEXFL_ALLOWMULTICHARLITERALS | LEXFL_NOSTRINGESCAPECHARS | LEXFL_ALLOWPATHNAMES ); + lexer.LoadMemory( prop.item.c_str( ), prop.item.Size( ), "gltfItem_mesh_primitiveB", 0 ); + + + item->AssureSizeAlloc(item->Num() + 1,idListNewElement); + gltfMesh_Primitive *gltfMeshPrim =(*item)[item->Num() - 1]; + + attributes->Set ( &gltfMeshPrim->attributes, &lexer ); + GLTFARRAYITEMREF( gltfMeshPrim, indices ); + GLTFARRAYITEMREF( gltfMeshPrim, material ); + GLTFARRAYITEMREF( gltfMeshPrim, mode ); + GLTFARRAYITEMREF( gltfMeshPrim, target ); + GLTFARRAYITEMREF( gltfMeshPrim, extensions ); + extras->Set ( &gltfMeshPrim->extras, &lexer ); + prim.Parse( &lexer ); + if ( gltf_parseVerbose.GetBool( ) ) + common->Printf( "%s", prop.item.c_str( ) ); + } + parser->ExpectTokenString( "]" ); +} + +void gltfItem_mesh_primitive_attribute::parse( idToken &token ) { + bool parsing = true; + while ( parsing && parser->ExpectAnyToken( &token ) ) { + + item->AssureSizeAlloc( item->Num( ) + 1, idListNewElement ); + gltfMesh_Primitive_Attribute *attr = ( *item )[item->Num( ) - 1]; + parser->ExpectTokenString( ":" ); + attr->attributeSemantic = token; + attr->type = GetAttributeEnum( attr->attributeSemantic.c_str(), &attr->elementSize ); + parser->ExpectAnyToken( &token ); + attr->accessorIndex = token.GetIntValue(); + parsing = parser->PeekTokenString( "," ); + if ( parsing ) + parser->ExpectTokenString( "," ); + } + parser->ExpectTokenString( "}" ); + + if ( gltf_parseVerbose.GetBool( ) ) + common->Printf( "%s", token.c_str( ) ); +} + +void gltfItem_integer_array::parse( idToken &token ) { + + parser->UnreadToken( &token ); + gltfPropertyArray array = gltfPropertyArray( parser, false ); + for ( auto &prop : array ) { + idStr neg; + int &value = item->Alloc( ); + value = prop.item.GetIntValue(); + + if ( prop.item.type == TT_PUNCTUATION && prop.item == "-" ) { + parser->ExpectTokenType( TT_NUMBER, 0, &prop.item ); + value = -( prop.item.GetIntValue( ) ); + neg = "-"; + } else if ( prop.item.type == TT_NUMBER ) { + value = prop.item.GetIntValue( ); + } else + common->FatalError( "parse error" ); + + if ( gltf_parseVerbose.GetBool( ) ) + common->Printf( "%s%s", neg.c_str( ), prop.item.c_str( ) ); + } + parser->ExpectTokenString( "]" ); +} + +void gltfItem_number_array::parse( idToken &token ) { + + parser->UnreadToken(&token); + gltfPropertyArray array = gltfPropertyArray( parser, false ); + for ( auto &prop : array ) { + idStr neg; + double& value = item->Alloc( ); + if ( prop.item.type == TT_PUNCTUATION && prop.item == "-" ) { + parser->ExpectTokenType( TT_NUMBER, 0, &prop.item ); + value = -(prop.item.GetDoubleValue()); + neg = "-"; + } else if ( prop.item.type == TT_NUMBER ) { + value = prop.item.GetDoubleValue( ); + }else + common->FatalError("parse error" ); + + if ( gltf_parseVerbose.GetBool( ) ) + common->Printf( "%s%s", neg.c_str(),prop.item.c_str( ) ); + } + parser->ExpectTokenString( "]" ); +} + +void gltfItem_vec4::parse( idToken &token ) { + + auto *numbers = new gltfItem_number_array( "" ); + idList numberarray; + numbers->Set( &numberarray, parser ); + numbers->parse( token ); + if ( numbers->item->Num( ) != 4 ) + common->FatalError( "gltfItem_vec4 : missing arguments, expected 4, got %i", numbers->item->Num( ) ); + + double *val = numbers->item->Ptr( ); + *item = idVec4( val[0], val[1], val[2], val[3] ); +} + +void gltfItem_vec3::parse( idToken &token ) { + auto *numbers = new gltfItem_number_array( "" ); + idList numberarray; + numbers->Set( &numberarray, parser ); + numbers->parse( token ); + if ( numbers->item->Num( ) != 3 ) + common->FatalError( "gltfItem_vec3 : missing arguments, expected 3, got %i", numbers->item->Num( ) ); + + double *val = numbers->item->Ptr( ); + *item = idVec3( val[0], val[1], val[2] ); +} + +void gltfItem_vec2::parse( idToken &token ) { + auto *numbers = new gltfItem_number_array( "" ); + idList numberarray; + numbers->Set( &numberarray, parser ); + numbers->parse( token ); + if ( numbers->item->Num( ) != 2 ) + common->FatalError( "gltfItem_vec3 : missing arguments, expected 2, got %i", numbers->item->Num( ) ); + + double *val = numbers->item->Ptr( ); + *item = idVec2( val[0], val[1] ); +} + +void gltfItem_quat::parse( idToken &token ) { + auto * numbers = new gltfItem_number_array(""); + idList numberarray; + numbers->Set( &numberarray, parser ); + numbers->parse( token ); + if (numbers->item->Num() != 4 ) + common->FatalError("gltfItem_quat : missing arguments, expectd 4, got %i", numbers->item->Num( ) ); + + double * val = numbers->item->Ptr(); + *item = idQuat( val[0] , val[1] , val[2] , val[3] ); +} + +void gltfItem_mat4::parse( idToken &token ) { + auto *numbers = new gltfItem_number_array( "" ); + idList numberarray; + numbers->Set( &numberarray, parser ); + numbers->parse( token ); + if ( numbers->item->Num( ) != 16 ) + common->FatalError( "gltfItem_mat4 : missing arguments, expectd 16, got %i", numbers->item->Num( ) ); + + double *val = numbers->item->Ptr( ); + *item = idMat4( + val[0], val[1], val[2], val[3], + val[4], val[5], val[6], val[7], + val[8], val[9], val[10], val[11], + val[12], val[13], val[14], val[15] + ); +} + + +void gltfItem_accessor_sparse::parse( idToken &token ) { + parser->Warning("%s is untested!", "gltfItem_accessor_sparse" ); + + gltfItemArray sparse; + GLTFARRAYITEM( sparse, count, gltfItem_integer ); + GLTFARRAYITEM( sparse, indices, gltfItem_accessor_sparse_indices); + GLTFARRAYITEM( sparse, values, gltfItem_accessor_sparse_values ); + GLTFARRAYITEM( sparse, extensions, gltfItem ); + GLTFARRAYITEM( sparse, extras, gltfItem_Extra ); + + GLTFARRAYITEMREF( item, count ); + indices->Set ( &item->indices, parser ); + values->Set ( &item->values, parser ); + GLTFARRAYITEMREF( item, extensions ); + extras->Set ( &item->extras, parser ); + sparse.Parse( parser ); + + if ( gltf_parseVerbose.GetBool( ) ) + common->Printf( "%s", token.c_str( ) ); +} + +void gltfItem_accessor_sparse_values::parse( idToken &token ) { + parser->Warning( "%s is untested!", "gltfItem_accessor_sparse_values" ); + + gltfItemArray values; + GLTFARRAYITEM( values, bufferView, gltfItem_integer ); + GLTFARRAYITEM( values, byteOffset, gltfItem_integer ); + GLTFARRAYITEM( values, extensions, gltfItem ); + GLTFARRAYITEM( values, extras, gltfItem_Extra ); + + GLTFARRAYITEMREF( item, bufferView ); + GLTFARRAYITEMREF( item, byteOffset ); + GLTFARRAYITEMREF( item, extensions ); + extras->Set ( &item->extras, parser ); + + if ( gltf_parseVerbose.GetBool( ) ) + common->Printf( "%s", token.c_str( ) ); +} + +void gltfItem_accessor_sparse_indices::parse( idToken &token ) { + parser->Warning( "%s is untested!", "gltfItem_accessor_sparse_indices" ); + + gltfItemArray indices; + GLTFARRAYITEM( indices, bufferView, gltfItem_integer ); + GLTFARRAYITEM( indices, byteOffset, gltfItem_integer ); + GLTFARRAYITEM( indices, componentType, gltfItem_integer ); + GLTFARRAYITEM( indices, extensions, gltfItem ); + GLTFARRAYITEM( indices, extras, gltfItem_Extra ); + + GLTFARRAYITEMREF( item, bufferView ); + GLTFARRAYITEMREF( item, byteOffset ); + GLTFARRAYITEMREF( item, componentType ); + GLTFARRAYITEMREF( item, extensions ); + extras->Set ( &item->extras, parser ); + indices.Parse( parser ); + + if ( gltf_parseVerbose.GetBool( ) ) + common->Printf( "%s", token.c_str( ) ); +} + +void gltfItem_camera_orthographic::parse( idToken &token ) +{ + gltfPropertyArray array = gltfPropertyArray( parser ); + for ( auto &prop : array ) + common->Printf( "%s", prop.item.c_str( ) ); + parser->ExpectTokenString( "]" ); +} + +void gltfItem_camera_perspective::parse( idToken &token ) +{ + parser->UnreadToken( &token ); + gltfItemArray cameraPerspective; + GLTFARRAYITEM( cameraPerspective, aspectRatio, gltfItem_number ); + GLTFARRAYITEM( cameraPerspective, yfov, gltfItem_number ); + GLTFARRAYITEM( cameraPerspective, zfar, gltfItem_number ); + GLTFARRAYITEM( cameraPerspective, znear, gltfItem_number ); + GLTFARRAYITEM( cameraPerspective, extensions, gltfItem ); + GLTFARRAYITEM( cameraPerspective, extras, gltfItem_Extra ); + + GLTFARRAYITEMREF( item, aspectRatio ); + GLTFARRAYITEMREF( item, yfov ); + GLTFARRAYITEMREF( item, zfar ); + GLTFARRAYITEMREF( item, znear ); + GLTFARRAYITEMREF( item, extensions ); + extras->Set ( &item->extras, parser ); + cameraPerspective.Parse( parser ); + + if ( gltf_parseVerbose.GetBool( ) ) + common->Printf( "%s", token.c_str( ) ); +} + +void gltfItem_occlusion_texture::parse( idToken &token ) { + parser->UnreadToken( &token ); + gltfItemArray textureInfo; + GLTFARRAYITEM( textureInfo, index, gltfItem_integer); + GLTFARRAYITEM( textureInfo, texCoord, gltfItem_integer ); + GLTFARRAYITEM( textureInfo, strength, gltfItem_number); + GLTFARRAYITEM( textureInfo, extensions, gltfItem_texture_info_extensions ); + GLTFARRAYITEM( textureInfo, extras, gltfItem_Extra ); + + GLTFARRAYITEMREF( item, index ); + GLTFARRAYITEMREF( item, texCoord ); + GLTFARRAYITEMREF( item, strength ); + extensions->Set ( &item->extensions, parser ); + extras->Set ( &item->extras, parser ); + textureInfo.Parse( parser ); + + if ( gltf_parseVerbose.GetBool( ) ) + common->Printf( "%s", token.c_str( ) ); +} + +void gltfItem_normal_texture::parse( idToken &token ) { + parser->UnreadToken( &token ); + gltfItemArray textureInfo; + GLTFARRAYITEM( textureInfo, index, gltfItem_integer); + GLTFARRAYITEM( textureInfo, texCoord, gltfItem_integer ); + GLTFARRAYITEM( textureInfo, scale, gltfItem_number); + GLTFARRAYITEM( textureInfo, extensions, gltfItem_texture_info_extensions ); + GLTFARRAYITEM( textureInfo, extras, gltfItem_Extra ); + + GLTFARRAYITEMREF( item, index ); + GLTFARRAYITEMREF( item, texCoord ); + GLTFARRAYITEMREF( item, scale ); + extensions->Set ( &item->extensions, parser ); + extras->Set ( &item->extras, parser ); + textureInfo.Parse( parser ); + + if ( gltf_parseVerbose.GetBool( ) ) + common->Printf( "%s", token.c_str( ) ); +} + +void gltfItem_texture_info::parse( idToken &token ) { + parser->UnreadToken( &token ); + gltfItemArray textureInfo; + GLTFARRAYITEM( textureInfo, index, gltfItem_integer); + GLTFARRAYITEM( textureInfo, texCoord, gltfItem_integer ); + GLTFARRAYITEM( textureInfo, extensions, gltfItem_texture_info_extensions ); + GLTFARRAYITEM( textureInfo, extras, gltfItem_Extra ); + + GLTFARRAYITEMREF( item, index ); + GLTFARRAYITEMREF( item, texCoord ); + extensions->Set ( &item->extensions, parser ); + extras->Set ( &item->extras, parser ); + textureInfo.Parse( parser ); + + if ( gltf_parseVerbose.GetBool( ) ) + common->Printf( "%s", token.c_str( ) ); +} + +void gltfItem_pbrMetallicRoughness::parse( idToken &token ) +{ + parser->UnreadToken( &token ); + gltfItemArray pbrMetallicRoughness; + GLTFARRAYITEM( pbrMetallicRoughness, baseColorFactor, gltfItem_vec4); + GLTFARRAYITEM( pbrMetallicRoughness, baseColorTexture, gltfItem_texture_info); + GLTFARRAYITEM( pbrMetallicRoughness, metallicFactor, gltfItem_number ); + GLTFARRAYITEM( pbrMetallicRoughness, roughnessFactor, gltfItem_number ); + GLTFARRAYITEM( pbrMetallicRoughness, metallicRoughnessTexture, gltfItem_texture_info ); + GLTFARRAYITEM( pbrMetallicRoughness, extensions, gltfItem ); + GLTFARRAYITEM( pbrMetallicRoughness, extras, gltfItem_Extra ); + + baseColorFactor->Set ( &item->baseColorFactor, parser ); + baseColorTexture->Set ( &item->baseColorTexture, parser ); + GLTFARRAYITEMREF ( item, metallicFactor ); + GLTFARRAYITEMREF ( item, roughnessFactor ); + metallicRoughnessTexture->Set( &item->metallicRoughnessTexture, parser ); + GLTFARRAYITEMREF ( item, extensions ); + extras->Set ( &item->extras, parser ); + pbrMetallicRoughness.Parse( parser ); + + if ( gltf_parseVerbose.GetBool( ) ) + common->Printf( "%s", token.c_str( ) ); +} + +void gltfItem_TextureInfo_KHR_texture_transform::parse( idToken &token ) { + parser->UnreadToken( &token ); + gltfItemArray texureTransform; + GLTFARRAYITEM( texureTransform, offset, gltfItem_vec2 ); + GLTFARRAYITEM( texureTransform, rotation, gltfItem_number); + GLTFARRAYITEM( texureTransform, scale, gltfItem_vec2 ); + GLTFARRAYITEM( texureTransform, texCoord, gltfItem_integer ); + + item->KHR_texture_transform = new gltfExt_KHR_texture_transform( ); + + offset->Set ( &item->KHR_texture_transform->offset, parser ); + GLTFARRAYITEMREF ( item->KHR_texture_transform, rotation ); + scale->Set ( &item->KHR_texture_transform->scale, parser ); + GLTFARRAYITEMREF ( item->KHR_texture_transform, texCoord ); + texureTransform.Parse( parser ); + + if ( gltf_parseVerbose.GetBool( ) ) + common->Printf( "%s", token.c_str( ) ); +} + +void gltfItem_Material_KHR_materials_pbrSpecularGlossiness::parse( idToken &token ) +{ + parser->UnreadToken( &token ); + gltfItemArray khrPbr; + GLTFARRAYITEM( khrPbr, diffuseFactor, gltfItem_vec4 ); + GLTFARRAYITEM( khrPbr, diffuseTexture, gltfItem_texture_info ); + GLTFARRAYITEM( khrPbr, specularFactor, gltfItem_vec3 ); + GLTFARRAYITEM( khrPbr, glossinessFactor, gltfItem_number ); + GLTFARRAYITEM( khrPbr, specularGlossinessTexture, gltfItem_texture_info ); + GLTFARRAYITEM( khrPbr, extensions, gltfItem); + GLTFARRAYITEM( khrPbr, extras, gltfItem_Extra ); + + item->KHR_materials_pbrSpecularGlossiness = new gltfExt_KHR_materials_pbrSpecularGlossiness( ); + + diffuseFactor->Set ( &item->KHR_materials_pbrSpecularGlossiness->diffuseFactor, parser ); + diffuseTexture->Set ( &item->KHR_materials_pbrSpecularGlossiness->diffuseTexture, parser ); + specularFactor->Set ( &item->KHR_materials_pbrSpecularGlossiness->specularFactor, parser ); + GLTFARRAYITEMREF ( item->KHR_materials_pbrSpecularGlossiness, glossinessFactor); + specularGlossinessTexture->Set ( &item->KHR_materials_pbrSpecularGlossiness->specularGlossinessTexture, parser ); + GLTFARRAYITEMREF ( item->KHR_materials_pbrSpecularGlossiness, extensions ); + extras->Set ( &item->KHR_materials_pbrSpecularGlossiness->extras, parser ); + khrPbr.Parse( parser ); + + if ( gltf_parseVerbose.GetBool( ) ) + common->Printf( "%s", token.c_str( ) ); +} + +void gltfItem_Node_KHR_lights_punctual::parse( idToken &token ) { + + parser->UnreadToken( &token ); + gltfItemArray xlight; + GLTFARRAYITEM( xlight, light, gltfItem_integer ); + item->KHR_lights_punctual = new gltfNode_KHR_lights_punctual( ); + light->Set( &item->KHR_lights_punctual->light ); + xlight.Parse( parser ); + + if ( gltf_parseVerbose.GetBool( ) ) + common->Printf( "%s", token.c_str( ) ); +} + +void gltfItem_KHR_lights_punctual::parse( idToken &token ) { + idToken localToken; + parser->ExpectTokenString( "lights" ); + parser->ExpectTokenString( ":" ); + + gltfItemArray light; + GLTFARRAYITEM( light, color, gltfItem_vec3 ); + GLTFARRAYITEM( light, intensity, gltfItem_number ); + //GLTFARRAYITEM( light, spot, gltfItem ); + GLTFARRAYITEM( light, type, gltfItem ); + GLTFARRAYITEM( light, range, gltfItem_number ); + GLTFARRAYITEM( light, name, gltfItem ); + GLTFARRAYITEM( light, extensions, gltfItem ); + GLTFARRAYITEM( light, extras, gltfItem_Extra ); + + gltfPropertyArray array = gltfPropertyArray( parser ); + for ( auto &prop : array ) { + idLexer lexer( LEXFL_ALLOWPATHNAMES | LEXFL_ALLOWMULTICHARLITERALS | LEXFL_NOSTRINGESCAPECHARS | LEXFL_ALLOWPATHNAMES ); + lexer.LoadMemory( prop.item.c_str( ), prop.item.Size( ), "gltfNode_light", 0 ); + + item->KHR_lights_punctual.AssureSizeAlloc( + item->KHR_lights_punctual.Num( ) + 1, + idListNewElement ); + + gltfExt_KHR_lights_punctual *gltfLight = + item->KHR_lights_punctual[item->KHR_lights_punctual.Num( ) - 1]; + + color->Set (&gltfLight->color,&lexer ); + GLTFARRAYITEMREF (gltfLight, intensity ); + //GLTFARRAYITEMREF (gltfLight, spot ); + GLTFARRAYITEMREF (gltfLight, type ); + GLTFARRAYITEMREF (gltfLight, range ); + GLTFARRAYITEMREF (gltfLight, name ); + GLTFARRAYITEMREF (gltfLight, extensions ); + extras->Set (&gltfLight->extras,&lexer ); + + light.Parse( &lexer ); + + gltfLight->intType = gltfExt_KHR_lights_punctual::resolveType(gltfLight->type); + + if ( gltf_parseVerbose.GetBool( ) ) + common->Printf( "%s", prop.item.c_str( ) ); + } + + parser->ExpectTokenString( "]" ); + parser->ExpectTokenString( "}" ); +} + +void gltfItem_node_extensions::parse( idToken &token ) +{ + parser->UnreadToken( &token ); + gltfItemArray extensions; + GLTFARRAYITEM( extensions, KHR_lights_punctual, gltfItem_Node_KHR_lights_punctual ); + + KHR_lights_punctual->Set( item, parser); + extensions.Parse( parser ); + + if ( gltf_parseVerbose.GetBool( ) ) + common->Printf( "%s", token.c_str( ) ); + +} + +void gltfItem_material_extensions::parse( idToken &token ) { + parser->UnreadToken( &token ); + + gltfItemArray extensions; + GLTFARRAYITEM( extensions, KHR_materials_pbrSpecularGlossiness, gltfItem_Material_KHR_materials_pbrSpecularGlossiness ); + + KHR_materials_pbrSpecularGlossiness->Set( item, parser ); + extensions.Parse( parser ); + + if ( gltf_parseVerbose.GetBool( ) ) + common->Printf( "%s", token.c_str( ) ); +} + +void gltfItem_texture_info_extensions::parse( idToken &token ) { + parser->UnreadToken( &token ); + + gltfItemArray extensions; + GLTFARRAYITEM( extensions, KHR_texture_transform, gltfItem_TextureInfo_KHR_texture_transform ); + + KHR_texture_transform->Set( item, parser ); + extensions.Parse( parser ); + + if ( gltf_parseVerbose.GetBool( ) ) + common->Printf( "%s", token.c_str( ) ); +} + + +void GLTF_Parser::Shutdown( ) { + parser.FreeSource(); + currentFile.FreeData(); +} +GLTF_Parser::GLTF_Parser() + : parser( LEXFL_ALLOWPATHNAMES | LEXFL_ALLOWMULTICHARLITERALS | LEXFL_NOSTRINGESCAPECHARS | LEXFL_ALLOWPATHNAMES ) , buffersDone(false), bufferViewsDone( false ) { } + +void GLTF_Parser::Parse_ASSET( idToken &token ) +{ + idStr section; + parser.ParseBracedSection( section ); + common->Printf( "%s\n",section.c_str( ) ); +} +void GLTF_Parser::Parse_SCENE( idToken &token ) +{ + currentAsset->DefaultScene( ) = parser.ParseInt( ); + + if ( gltf_parseVerbose.GetBool( ) ) + common->Printf( " ^1 %s scene ^6 : ^8 %i", token.c_str( ),currentAsset->DefaultScene( ) ); +} +void GLTF_Parser::Parse_SCENES( idToken &token ) +{ + gltfItemArray scene; + GLTFARRAYITEM( scene, nodes, gltfItem_integer_array ); + GLTFARRAYITEM( scene, name, gltfItem ); + GLTFARRAYITEM( scene, extensions, gltfItem ); + GLTFARRAYITEM( scene, extras, gltfItem_Extra ); + + gltfPropertyArray array = gltfPropertyArray(&parser); + for (auto & prop : array ) + { + idLexer lexer( LEXFL_ALLOWPATHNAMES | LEXFL_ALLOWMULTICHARLITERALS | LEXFL_NOSTRINGESCAPECHARS | LEXFL_ALLOWPATHNAMES ); + lexer.LoadMemory( prop.item.c_str( ), prop.item.Size( ), "gltfScene", 0 ); + + gltfScene * gltfscene = currentAsset->Scene(); + nodes->Set ( &gltfscene->nodes, &lexer ); + GLTFARRAYITEMREF( gltfscene, name ); + GLTFARRAYITEMREF( gltfscene, extensions ); + extras->Set ( &gltfscene->extras, &lexer ); + scene.Parse( &lexer ); + + if ( gltf_parseVerbose.GetBool( ) ) + common->Printf("%s", prop.item.c_str()); + } + parser.ExpectTokenString( "]" ); +} +void GLTF_Parser::Parse_CAMERAS( idToken &token ) +{ + gltfItemArray camera; + GLTFARRAYITEM( camera, orthographic, gltfItem_camera_orthographic ); + GLTFARRAYITEM( camera, perspective, gltfItem_camera_perspective ); + GLTFARRAYITEM( camera, type, gltfItem ); + GLTFARRAYITEM( camera, name, gltfItem ); + GLTFARRAYITEM( camera, extensions, gltfItem ); + GLTFARRAYITEM( camera, extras, gltfItem_Extra ); + + gltfPropertyArray array = gltfPropertyArray( &parser ); + for ( auto &prop : array ) + { + idLexer lexer( LEXFL_ALLOWPATHNAMES | LEXFL_ALLOWMULTICHARLITERALS | LEXFL_NOSTRINGESCAPECHARS | LEXFL_ALLOWPATHNAMES ); + lexer.LoadMemory( prop.item.c_str( ), prop.item.Size( ), "gltfCamera", 0 ); + + gltfCamera *item = currentAsset->Camera( ); + orthographic->Set( &item->orthographic, &lexer ); + perspective->Set ( &item->perspective, &lexer ); + + GLTFARRAYITEMREF( item, type ); + GLTFARRAYITEMREF( item, name ); + GLTFARRAYITEMREF( item, extensions ); + extras->Set ( &item->extras, &lexer ); + + camera.Parse( &lexer ); + + if ( gltf_parseVerbose.GetBool( ) ) + common->Printf( "%s", prop.item.c_str( ) ); + } + parser.ExpectTokenString( "]" ); +} +void GLTF_Parser::Parse_NODES( idToken &token ) +{ + + gltfItemArray node; + GLTFARRAYITEM( node, camera, gltfItem_integer ); + GLTFARRAYITEM( node, children, gltfItem_integer_array ); + GLTFARRAYITEM( node, skin, gltfItem_integer ); + GLTFARRAYITEM( node, matrix, gltfItem_mat4 ); + GLTFARRAYITEM( node, mesh, gltfItem_integer ); + GLTFARRAYITEM( node, rotation, gltfItem_quat ); + GLTFARRAYITEM( node, scale, gltfItem_vec3 ); + GLTFARRAYITEM( node, translation, gltfItem_vec3 ); + GLTFARRAYITEM( node, weights, gltfItem_number_array ); + GLTFARRAYITEM( node, name, gltfItem ); + GLTFARRAYITEM( node, extensions, gltfItem_node_extensions ); + GLTFARRAYITEM( node, extras, gltfItem_Extra ); + + gltfPropertyArray array = gltfPropertyArray( &parser ); + for ( auto &prop : array ) { + idLexer lexer( LEXFL_ALLOWPATHNAMES | LEXFL_ALLOWMULTICHARLITERALS | LEXFL_NOSTRINGESCAPECHARS | LEXFL_ALLOWPATHNAMES ); + lexer.LoadMemory( prop.item.c_str( ), prop.item.Size( ), "gltfNode", 0 ); + + gltfNode *gltfnode = currentAsset->Node( ); + + GLTFARRAYITEMREF( gltfnode, camera ); + matrix->Set ( &gltfnode->matrix,&lexer); + children->Set ( &gltfnode->children,&lexer); + GLTFARRAYITEMREF( gltfnode, skin ); + matrix->Set ( &gltfnode->matrix,&lexer); + GLTFARRAYITEMREF( gltfnode, mesh ); + rotation->Set ( &gltfnode->rotation, &lexer ); + scale->Set ( &gltfnode->scale,&lexer); + translation->Set( &gltfnode->translation, &lexer ); + weights->Set ( &gltfnode->weights,&lexer); + GLTFARRAYITEMREF( gltfnode, name ); + extensions->Set ( &gltfnode->extensions, &lexer ); + extras->Set (&gltfnode->extras,&lexer); + node.Parse( &lexer ); + + if ( gltf_parseVerbose.GetBool( ) ) + common->Printf( "%s", prop.item.c_str( ) ); + } + parser.ExpectTokenString( "]" ); +} +void GLTF_Parser::Parse_MATERIALS( idToken &token ) +{ + gltfItemArray material; + GLTFARRAYITEM( material, pbrMetallicRoughness, gltfItem_pbrMetallicRoughness ); + GLTFARRAYITEM( material, normalTexture, gltfItem_normal_texture ); + GLTFARRAYITEM( material, occlusionTexture, gltfItem_occlusion_texture); + GLTFARRAYITEM( material, emissiveTexture, gltfItem_texture_info ); + GLTFARRAYITEM( material, emissiveFactor, gltfItem_vec3 ); + GLTFARRAYITEM( material, alphaMode, gltfItem ); + GLTFARRAYITEM( material, alphaCutoff, gltfItem_number ); + GLTFARRAYITEM( material, doubleSided, gltfItem_boolean ); + GLTFARRAYITEM( material, name, gltfItem ); + GLTFARRAYITEM( material, extensions, gltfItem_material_extensions ); + GLTFARRAYITEM( material, extras, gltfItem_Extra); + + gltfPropertyArray array = gltfPropertyArray( &parser ); + for ( auto &prop : array ) { + idLexer lexer( LEXFL_ALLOWPATHNAMES | LEXFL_ALLOWMULTICHARLITERALS | LEXFL_NOSTRINGESCAPECHARS | LEXFL_ALLOWPATHNAMES ); + lexer.LoadMemory( prop.item.c_str( ), prop.item.Size( ), "gltfMaterial", 0 ); + + gltfMaterial * gltfmaterial = currentAsset->Material( ); + + pbrMetallicRoughness->Set ( &gltfmaterial->pbrMetallicRoughness, &lexer ); + normalTexture->Set ( &gltfmaterial->normalTexture, &lexer ); + occlusionTexture->Set ( &gltfmaterial->occlusionTexture, &lexer ); + emissiveTexture->Set ( &gltfmaterial->emissiveTexture, &lexer ); + emissiveFactor->Set ( &gltfmaterial->emissiveFactor, &lexer ); + GLTFARRAYITEMREF ( gltfmaterial, alphaMode ); + GLTFARRAYITEMREF ( gltfmaterial, alphaCutoff ); + GLTFARRAYITEMREF ( gltfmaterial, doubleSided ); + GLTFARRAYITEMREF ( gltfmaterial, name ); + extensions->Set ( &gltfmaterial->extensions, &lexer ); + extras->Set ( &gltfmaterial->extras, &lexer ); + material.Parse( &lexer ); + + gltfmaterial->intType = gltfMaterial::resolveAlphaMode( gltfmaterial->alphaMode ); + + if ( gltf_parseVerbose.GetBool( ) ) + common->Printf( "%s", prop.item.c_str( ) ); + } + parser.ExpectTokenString( "]" ); +} +void GLTF_Parser::Parse_MESHES( idToken &token ) +{ + gltfItemArray mesh; + GLTFARRAYITEM( mesh, primitives, gltfItem_mesh_primitive ); // object + GLTFARRAYITEM( mesh, weights, gltfItem_number_array ); //number[1 - *] + GLTFARRAYITEM( mesh, name, gltfItem ); + GLTFARRAYITEM( mesh, extensions, gltfItem ); + GLTFARRAYITEM( mesh, extras, gltfItem_Extra ); + + gltfPropertyArray array = gltfPropertyArray( &parser ); + for ( auto &prop : array ) { + idLexer lexer( LEXFL_ALLOWPATHNAMES | LEXFL_ALLOWMULTICHARLITERALS | LEXFL_NOSTRINGESCAPECHARS | LEXFL_ALLOWPATHNAMES ); + lexer.LoadMemory( prop.item.c_str( ), prop.item.Size( ), "gltfMesh", 0 ); + + gltfMesh *gltfmesh = currentAsset->Mesh( ); + + primitives->Set ( &gltfmesh->primitives, &lexer); + weights->Set ( &gltfmesh->weights, &lexer ); + GLTFARRAYITEMREF( gltfmesh, name ); + GLTFARRAYITEMREF( gltfmesh, extensions ); + extras->Set (&gltfmesh->extras,&lexer); + mesh.Parse( &lexer ); + if ( gltf_parseVerbose.GetBool( ) ) + common->Printf( "%s", prop.item.c_str( ) ); + } + parser.ExpectTokenString( "]" ); +} +void GLTF_Parser::Parse_TEXTURES( idToken &token ) +{ + gltfItemArray texture; + GLTFARRAYITEM( texture, sampler, gltfItem_integer ); + GLTFARRAYITEM( texture, source, gltfItem_integer ); + GLTFARRAYITEM( texture, name, gltfItem ); + GLTFARRAYITEM( texture, extensions, gltfItem_texture_info_extensions ); + GLTFARRAYITEM( texture, extras, gltfItem_Extra ); + + gltfPropertyArray array = gltfPropertyArray( &parser ); + for ( auto &prop : array ) { + idLexer lexer( LEXFL_ALLOWPATHNAMES | LEXFL_ALLOWMULTICHARLITERALS | LEXFL_NOSTRINGESCAPECHARS | LEXFL_ALLOWPATHNAMES ); + lexer.LoadMemory( prop.item.c_str( ), prop.item.Size( ), "gltfTexture", 0 ); + + gltfTexture *gltftexture = currentAsset->Texture( ); + + GLTFARRAYITEMREF( gltftexture, sampler ); + GLTFARRAYITEMREF( gltftexture, source ); + GLTFARRAYITEMREF( gltftexture, name ); + extensions->Set ( &gltftexture->extensions, &lexer ); + extras->Set ( &gltftexture->extras, &lexer ); + texture.Parse( &lexer ); + + if ( gltf_parseVerbose.GetBool( ) ) + common->Printf( "%s", prop.item.c_str( ) ); + } + parser.ExpectTokenString( "]" ); +} +void GLTF_Parser::Parse_IMAGES( idToken &token ) +{ + //reference impl + gltfPropertyArray array = gltfPropertyArray( &parser ); + + gltfItemArray propItems; + auto uri = new gltfItem_uri ("uri"); propItems.AddItemDef((parsable*)uri); + auto mimeType = new gltfItem ("mimeType"); propItems.AddItemDef((parsable*)mimeType); + auto bufferView = new gltfItem_integer ("bufferView"); propItems.AddItemDef((parsable*)bufferView ); + auto name = new gltfItem ("name"); propItems.AddItemDef((parsable*)name); + auto extensions = new gltfItem ("extensions"); propItems.AddItemDef((parsable*)extensions); + auto extras = new gltfItem_Extra ("extras" ); propItems.AddItemDef( ( parsable * ) extras ); + + for ( auto &prop : array ) + { + idLexer lexer( LEXFL_ALLOWPATHNAMES | LEXFL_ALLOWMULTICHARLITERALS | LEXFL_NOSTRINGESCAPECHARS | LEXFL_ALLOWPATHNAMES ); + lexer.LoadMemory(prop.item.c_str(),prop.item.Size(),"gltfImage",0); + + gltfImage *image = currentAsset->Image(); + uri->Set (&image->uri, &image->bufferView,currentAsset); + mimeType->Set (&image->mimeType); + bufferView->Set (&image->bufferView); + name->Set (&image->name); + extensions->Set (&image->extensions); + extras->Set (&image->extras,&lexer); + propItems.Parse (&lexer); + + if ( gltf_parseVerbose.GetBool( ) ) + common->Printf( "%s", prop.item.c_str( ) ); + + //automate.. + //image->bgfxTexture.handle.idx = UINT16_MAX; + } + parser.ExpectTokenString( "]" ); +} +void GLTF_Parser::Parse_ACCESSORS( idToken &token ) +{ + gltfItemArray accessor; + GLTFARRAYITEM( accessor, bufferView, gltfItem_integer ); + GLTFARRAYITEM( accessor, byteOffset, gltfItem_integer ); + GLTFARRAYITEM( accessor, componentType, gltfItem_integer ); + GLTFARRAYITEM( accessor, normalized, gltfItem_boolean ); + GLTFARRAYITEM( accessor, count, gltfItem_integer ); + GLTFARRAYITEM( accessor, type, gltfItem ); + GLTFARRAYITEM( accessor, max, gltfItem_number_array ); + GLTFARRAYITEM( accessor, min, gltfItem_number_array ); + GLTFARRAYITEM( accessor, sparse, gltfItem_accessor_sparse); + GLTFARRAYITEM( accessor, name, gltfItem ); + GLTFARRAYITEM( accessor, extensions, gltfItem ); + GLTFARRAYITEM( accessor, extras, gltfItem_Extra ); + + gltfPropertyArray array = gltfPropertyArray( &parser ); + for ( auto & prop : array ) + { + idLexer lexer( LEXFL_ALLOWPATHNAMES | LEXFL_ALLOWMULTICHARLITERALS | LEXFL_NOSTRINGESCAPECHARS | LEXFL_ALLOWPATHNAMES ); + lexer.LoadMemory( prop.item.c_str( ), prop.item.Size( ), "gltfAccessor", 0 ); + + gltfAccessor * item = currentAsset->Accessor(); + GLTFARRAYITEMREF( item, bufferView); + GLTFARRAYITEMREF( item, byteOffset); + GLTFARRAYITEMREF( item, componentType); + GLTFARRAYITEMREF( item, normalized); + GLTFARRAYITEMREF( item, count); + GLTFARRAYITEMREF( item, type ); + max->Set ( &item->max, &lexer ); + min->Set ( &item->min, &lexer ); + sparse->Set ( &item->sparse, &lexer ); + GLTFARRAYITEMREF( item, name); + GLTFARRAYITEMREF( item, extensions); + extras->Set ( &item->extras, &lexer ); + accessor.Parse( &lexer ); + + GetComponentTypeEnum( item->componentType, &item->typeSize ); + + if ( gltf_parseVerbose.GetBool( ) ) + common->Printf( "%s", prop.item.c_str( ) ); + } + parser.ExpectTokenString( "]" ); +} +void GLTF_Parser::Parse_BUFFERVIEWS( idToken &token ) +{ + gltfItemArray bv; + GLTFARRAYITEM( bv, buffer, gltfItem_integer ); + GLTFARRAYITEM( bv, byteLength, gltfItem_integer ); + GLTFARRAYITEM( bv, byteStride, gltfItem_integer ); + GLTFARRAYITEM( bv, byteOffset, gltfItem_integer ); + GLTFARRAYITEM( bv, target, gltfItem_integer ); + GLTFARRAYITEM( bv, name, gltfItem ); + GLTFARRAYITEM( bv, extensions, gltfItem ); + GLTFARRAYITEM( bv, extras, gltfItem_Extra ); + + gltfPropertyArray array = gltfPropertyArray( &parser ); + for ( auto &prop : array ) + { + idLexer lexer( LEXFL_ALLOWPATHNAMES | LEXFL_ALLOWMULTICHARLITERALS | LEXFL_NOSTRINGESCAPECHARS | LEXFL_ALLOWPATHNAMES ); + lexer.LoadMemory( prop.item.c_str( ), prop.item.Size( ), "gltfBufferView", 0 ); + + gltfBufferView * gltfBV = currentAsset->BufferView(); + GLTFARRAYITEMREF( gltfBV, buffer ); + GLTFARRAYITEMREF( gltfBV, byteLength); + GLTFARRAYITEMREF( gltfBV, byteStride); + GLTFARRAYITEMREF( gltfBV, byteOffset); + GLTFARRAYITEMREF( gltfBV, target ); + GLTFARRAYITEMREF( gltfBV, name ); + GLTFARRAYITEMREF( gltfBV, extensions); + extras->Set ( &gltfBV->extras, &lexer ); + bv.Parse(&lexer); + gltfBV->parent = currentAsset; + + if (gltf_parseVerbose.GetBool()) + common->Printf( "%s", prop.item.c_str( ) ); + } + parser.ExpectTokenString( "]" ); +} +void GLTF_Parser::Parse_SAMPLERS( idToken &token ) +{ + gltfItemArray sampl; + GLTFARRAYITEM( sampl, magFilter, gltfItem_integer ); + GLTFARRAYITEM( sampl, minFilter, gltfItem_integer ); + GLTFARRAYITEM( sampl, wrapS, gltfItem_integer ); + GLTFARRAYITEM( sampl, wrapT, gltfItem_integer ); + GLTFARRAYITEM( sampl, name, gltfItem ); + GLTFARRAYITEM( sampl, extensions, gltfItem ); + GLTFARRAYITEM( sampl, extras, gltfItem_Extra ); + + gltfPropertyArray array = gltfPropertyArray( &parser ); + for ( auto &prop : array ) + { + idLexer lexer( LEXFL_ALLOWPATHNAMES | LEXFL_ALLOWMULTICHARLITERALS | LEXFL_NOSTRINGESCAPECHARS | LEXFL_ALLOWPATHNAMES ); + lexer.LoadMemory( prop.item.c_str( ), prop.item.Size( ), "gltfSampler", 0 ); + + gltfSampler * gltfSampl = currentAsset->Sampler(); + GLTFARRAYITEMREF( gltfSampl, magFilter ); + GLTFARRAYITEMREF( gltfSampl, minFilter ); + GLTFARRAYITEMREF( gltfSampl, wrapS ); + GLTFARRAYITEMREF( gltfSampl, wrapT ); + GLTFARRAYITEMREF( gltfSampl, name ); + GLTFARRAYITEMREF( gltfSampl, extensions); + extras->Set ( &gltfSampl->extras, &lexer ); + sampl.Parse(&lexer); + + if (gltf_parseVerbose.GetBool()) + common->Printf( "%s", prop.item.c_str( ) ); + } + parser.ExpectTokenString( "]" ); +} +void GLTF_Parser::Parse_BUFFERS( idToken &token ) +{ + gltfItemArray buf; + GLTFARRAYITEM( buf, uri, gltfItem_uri ); + GLTFARRAYITEM( buf, byteLength, gltfItem_integer ); + GLTFARRAYITEM( buf, name, gltfItem ); + GLTFARRAYITEM( buf, extensions, gltfItem ); + GLTFARRAYITEM( buf, extras, gltfItem_Extra ); + + gltfPropertyArray array = gltfPropertyArray( &parser ); + for ( auto &prop : array ) { + idLexer lexer( LEXFL_ALLOWPATHNAMES | LEXFL_ALLOWMULTICHARLITERALS | LEXFL_NOSTRINGESCAPECHARS | LEXFL_ALLOWPATHNAMES ); + lexer.LoadMemory( prop.item.c_str( ), prop.item.Size( ), "gltfBuffer", 0 ); + + gltfBuffer *gltfBuf = currentAsset->Buffer( ); + gltfBuf->parent = currentAsset; + + uri->Set( &gltfBuf->uri, nullptr , currentAsset ); + GLTFARRAYITEMREF( gltfBuf, byteLength ); + GLTFARRAYITEMREF( gltfBuf, name ); + GLTFARRAYITEMREF( gltfBuf, extensions ); + extras->Set ( &gltfBuf->extras, &lexer ); + buf.Parse( &lexer ); + if ( gltf_parseVerbose.GetBool( ) ) + common->Printf( "%s", prop.item.c_str( ) ); + } + parser.ExpectTokenString( "]" ); +} +void GLTF_Parser::Parse_ANIMATIONS( idToken &token ) +{ + gltfItemArray anim; + GLTFARRAYITEM( anim, channels, gltfItem_animation_channel ); //channel[1 - *] + GLTFARRAYITEM( anim, samplers, gltfItem_animation_sampler ); //sampler[1 - *] + GLTFARRAYITEM( anim, name, gltfItem ); + GLTFARRAYITEM( anim, extensions, gltfItem ); + GLTFARRAYITEM( anim, extras, gltfItem_Extra ); + + gltfPropertyArray array = gltfPropertyArray( &parser ); + for ( auto &prop : array ) { + idLexer lexer( LEXFL_ALLOWPATHNAMES | LEXFL_ALLOWMULTICHARLITERALS | LEXFL_NOSTRINGESCAPECHARS | LEXFL_ALLOWPATHNAMES ); + lexer.LoadMemory( prop.item.c_str( ), prop.item.Size( ), "gltfAnimation", 0 ); + + gltfAnimation *gltfanim= currentAsset->Animation( ); + + channels->Set ( &gltfanim->channels, &lexer); + samplers->Set ( &gltfanim->samplers, &lexer ); + GLTFARRAYITEMREF( gltfanim, name ); + GLTFARRAYITEMREF( gltfanim, extensions ); + extras->Set ( &gltfanim->extras, &lexer ); + anim.Parse( &lexer ); + + if ( gltf_parseVerbose.GetBool( ) ) + common->Printf( "%s", prop.item.c_str( ) ); + } + parser.ExpectTokenString( "]" ); + + +} +void GLTF_Parser::Parse_SKINS( idToken &token ) { + gltfItemArray skin; + GLTFARRAYITEM( skin, inverseBindMatrices, gltfItem_integer ); + GLTFARRAYITEM( skin, skeleton, gltfItem_integer ); + GLTFARRAYITEM( skin, joints, gltfItem_integer_array ); + GLTFARRAYITEM( skin, name, gltfItem ); + GLTFARRAYITEM( skin, extensions, gltfItem ); + GLTFARRAYITEM( skin, extras, gltfItem_Extra ); + + gltfPropertyArray array = gltfPropertyArray( &parser ); + for ( auto &prop : array ) { + idLexer lexer( LEXFL_ALLOWPATHNAMES | LEXFL_ALLOWMULTICHARLITERALS | LEXFL_NOSTRINGESCAPECHARS | LEXFL_ALLOWPATHNAMES ); + lexer.LoadMemory( prop.item.c_str( ), prop.item.Size( ), "gltfSkin", 0 ); + + gltfSkin *gltfSkin = currentAsset->Skin( ); + + GLTFARRAYITEMREF( gltfSkin, inverseBindMatrices ); + GLTFARRAYITEMREF( gltfSkin, skeleton ); + joints->Set ( &gltfSkin->joints, &lexer ); + GLTFARRAYITEMREF( gltfSkin, name ); + GLTFARRAYITEMREF( gltfSkin, extensions ); + extras->Set ( &gltfSkin->extras, &lexer ); + skin.Parse( &lexer ); + + if ( gltf_parseVerbose.GetBool( ) ) + common->Printf( "%s", prop.item.c_str( ) ); + } + parser.ExpectTokenString( "]" ); +} +void GLTF_Parser::Parse_EXTENSIONS( idToken &token ) +{ + idStr json; + parser.ParseBracedSection( json ); + + gltfItemArray extensions; + //GLTFARRAYITEM( extensions, KHR_materials_pbrSpecularGlossiness, gltfItem_KHR_materials_pbrSpecularGlossiness ); + GLTFARRAYITEM( extensions, KHR_lights_punctual, gltfItem_KHR_lights_punctual ); + + idLexer lexer( LEXFL_ALLOWPATHNAMES | LEXFL_ALLOWMULTICHARLITERALS | LEXFL_NOSTRINGESCAPECHARS | LEXFL_ALLOWPATHNAMES ); + lexer.LoadMemory( json.c_str( ), json.Size( ), "Extensions", 0 ); + gltfExtensions * gltfextension = currentAsset->Extensions(); + //KHR_materials_pbrSpecularGlossiness->Set( &gltfextensions, &lexer ); + KHR_lights_punctual->Set(gltfextension, &lexer ); + extensions.Parse( &lexer ); + + if ( gltf_parseVerbose.GetBool( ) ) + common->Printf( "%s", json.c_str( ) ); +} +void GLTF_Parser::Parse_EXTENSIONS_USED( idToken &token ) +{ + gltfPropertyArray array = gltfPropertyArray( &parser,false ); + for ( auto &prop : array ) { + gltfExtensionsUsed * ext = currentAsset->ExtensionsUsed( ); + ext->extension = prop.item; + if ( gltf_parseVerbose.GetBool( ) ) + common->Printf( "%s", prop.item.c_str( ) ); + } + parser.ExpectTokenString( "]" ); +} +void GLTF_Parser::Parse_EXTENSIONS_REQUIRED( idToken &token ) { + parser.ExpectTokenString( "[" ); + idStrList exts; + idToken item; + bool parsing = true; + while ( parsing && parser.ExpectAnyToken( &item ) ) { + if ( item.type != TT_STRING ) + common->FatalError( "malformed extensions_used array" ); + idStr &extension = exts.Alloc( ); + extension = item.c_str( ); + parsing = parser.PeekTokenString( "," ); + if ( parsing ) + parser.ExpectTokenString( "," ); + } + parser.ExpectTokenString( "]" ); + for ( auto &out : exts ) + common->Printf( "%s", out.c_str( ) ); +} + +gltfProperty GLTF_Parser::ParseProp( idToken & token ) +{ + parser.ExpectTokenString( ":" ); + gltfProperty prop = ResolveProp( token ); + + bool skipping = false; + if (!buffersDone || !bufferViewsDone) + { + if (prop == BUFFERS && !buffersDone) + { + Parse_BUFFERS( token ); + return prop; + } + if ( prop == BUFFERVIEWS && !bufferViewsDone ) { + Parse_BUFFERVIEWS( token ); + return prop; + } + skipping = true; + if ( gltf_parseVerbose.GetBool( ) ) + common->DPrintf( "Searching for buffer tag. Skipping %s.", token.c_str()); + }else + { + if (( prop == BUFFERS && buffersDone) || (prop == BUFFERVIEWS && bufferViewsDone )) + { + skipping = true; + if ( gltf_parseVerbose.GetBool( ) ) + common->DPrintf( "Skipping %s , already done.", token.c_str( ) ); + } + } + + if (skipping ) + { + //1. search for {} scope. + //2. search for [] scope. + //3. single token. + + idToken skipTok; + int sectionsSkipped = 0; + if (parser.PeekTokenString("{")) + parser.SkipBracedSection( true,BRSKIP_BRACES, §ionsSkipped ); + if ( !sectionsSkipped && parser.PeekTokenString( "[" ) ) + parser.SkipBracedSection( true, BRSKIP_BRACKET, §ionsSkipped ); + if ( !sectionsSkipped ) + parser.ExpectAnyToken( &skipTok ); + + return gltfProperty::INVALID; + } + + switch ( prop ) + { + case ASSET: + Parse_ASSET( token ); + break; + case CAMERAS: + Parse_CAMERAS( token ); + break; + case SCENE: + Parse_SCENE( token ); + break; + case SCENES: + Parse_SCENES( token ); + break; + case NODES: + Parse_NODES( token ); + break; + case MATERIALS: + Parse_MATERIALS( token ); + break; + case MESHES: + Parse_MESHES( token ); + break; + case TEXTURES: + Parse_TEXTURES( token ); + break; + case IMAGES: + Parse_IMAGES( token ); + break; + case ACCESSORS: + Parse_ACCESSORS( token ); + break; + case BUFFERVIEWS: + //Parse_BUFFERVIEWS( token ); + if ( !bufferViewsDone ) + common->FatalError( "Bufferviews should already be parsed!" ); + break; + case SAMPLERS: + Parse_SAMPLERS( token ); + break; + case BUFFERS: + if ( !buffersDone ) + common->FatalError( "Buffers should already be parsed!" ); + break; + case ANIMATIONS: + Parse_ANIMATIONS( token ); + break; + case SKINS: + Parse_SKINS( token ); + break; + case EXTENSIONS: + Parse_EXTENSIONS( token ); + break; + case EXTENSIONS_USED: + Parse_EXTENSIONS_USED( token ); + break; + case EXTENSIONS_REQUIRED: + Parse_EXTENSIONS_REQUIRED( token ); + break; + default: + common->FatalError("UnImplemented GLTF property : %s",token.c_str()); + } + return prop; +} +gltfProperty GLTF_Parser::ResolveProp( idToken & token ) +{ + if ( !idStr::Icmp( token.c_str( ), "asset" ) ) + return gltfProperty::ASSET; + else if ( !idStr::Icmp( token.c_str( ), "cameras" ) ) + return gltfProperty::CAMERAS; + else if ( !idStr::Icmp( token.c_str( ), "scene" ) ) + return gltfProperty::SCENE; + else if ( !idStr::Icmp( token.c_str( ), "scenes" ) ) + return gltfProperty::SCENES; + else if ( !idStr::Icmp( token.c_str( ), "nodes" ) ) + return gltfProperty::NODES; + else if ( !idStr::Icmp( token.c_str( ), "materials" ) ) + return gltfProperty::MATERIALS; + else if ( !idStr::Icmp( token.c_str( ), "meshes" ) ) + return gltfProperty::MESHES; + else if ( !idStr::Icmp( token.c_str( ), "textures" ) ) + return gltfProperty::TEXTURES; + else if ( !idStr::Icmp( token.c_str( ), "images" ) ) + return gltfProperty::IMAGES; + else if ( !idStr::Icmp( token.c_str( ), "accessors" ) ) + return gltfProperty::ACCESSORS; + else if ( !idStr::Icmp( token.c_str( ), "bufferViews" ) ) + return gltfProperty::BUFFERVIEWS; + else if ( !idStr::Icmp( token.c_str( ), "samplers" ) ) + return gltfProperty::SAMPLERS; + else if ( !idStr::Icmp( token.c_str( ), "buffers" ) ) + return gltfProperty::BUFFERS; + else if ( !idStr::Icmp( token.c_str( ), "animations" ) ) + return gltfProperty::ANIMATIONS; + else if ( !idStr::Icmp( token.c_str( ), "skins" ) ) + return gltfProperty::SKINS; + else if ( !idStr::Icmp( token.c_str( ), "extensions" ) ) + return gltfProperty::EXTENSIONS; + else if ( !idStr::Icmp( token.c_str( ), "extensionsused" ) ) + return gltfProperty::EXTENSIONS_USED; + else if ( !idStr::Icmp( token.c_str( ), "extensionsrequired") ) + return gltfProperty::EXTENSIONS_REQUIRED; + + return gltfProperty::INVALID; +} + +bool GLTF_Parser::loadGLB(idStr filename ) +{ + idFile * file = fileSystem->OpenFileRead( filename ); + + if ( file->Length() < 20 ) { + common->FatalError("Too short data size for glTF Binary." ); + return false; + } + idStr gltfMagic("glTF"); + unsigned char fileMagic[5]; + + file->Read((void*)&fileMagic,4 ); + fileMagic[4]=0; + if (gltfMagic.Icmp( (const char*)&fileMagic ) == 0) + { + common->Printf("reading %s...\n",filename.c_str() ); + } else { + common->Error( "invalid magic" ); + return false; + } + + unsigned int version = 0; // 4 bytes + unsigned int length = 0; // 4 bytes + + //HVG_TODO + //handle 0 bin chunk -> size is chunk[0].size + 20; + file->ReadUnsignedInt( version ); + file->ReadUnsignedInt( length ); + length -= 12; // header size + + unsigned int chunk_type=0; // 4 bytes + unsigned int chunk_length=0; // 4 bytes + byte * data = nullptr; + gltfData *dataCache = gltfData::Data( filename ); + currentAsset = dataCache; + + int chunkCount = 0; + while ( length ) { + unsigned int prev_length = chunk_length; + length -= file->ReadUnsignedInt( chunk_length ); + length -= file->ReadUnsignedInt( chunk_type ); + + data = dataCache->AddData( chunk_length ); + dataCache->FileName(filename); + + int read = file->Read((void*)data, chunk_length ); + if (read != chunk_length) + common->FatalError("Could not read full chunk (%i bytes) in file %s",chunk_length, filename ); + length -= read; + if (chunk_type == gltfChunk_Type_JSON) + { + currentFile = filename + "[JSON CHUNK]"; + parser.LoadMemory((const char*)data,chunk_length,"gltfJson",0); + }else if ( !chunkCount ) + common->FatalError("first chunk was not a json chunk"); + else { + common->Printf("BINCHUNK %i %i bytes\n",chunkCount, chunk_length ); + } + if (chunkCount++ && length ) + common->FatalError("corrupt glb file." ); + } + + Parse(); + return true; +} + +bool GLTF_Parser::Parse( ) { + bool parsing = true; + parser.ExpectTokenString( "{" ); + while ( parsing && parser.ExpectAnyToken( &token ) ) { + if ( token.type != TT_STRING ) + common->FatalError( "Expected an \"string\" " ); + + if ( gltf_parseVerbose.GetBool( ) ) + common->Printf( token.c_str( ) ); + gltfProperty prop = ParseProp( token ); + + if (( prop == BUFFERS && !buffersDone )) + { + parser.Reset(); + parser.ExpectTokenString( "{" ); + buffersDone = true; + continue; + } + if ((prop == BUFFERVIEWS && !bufferViewsDone)) + { + parser.Reset( ); + parser.ExpectTokenString( "{" ); + bufferViewsDone = true; + continue; + } + if ( gltf_parseVerbose.GetBool( ) ) + common->Printf( "\n" ); + parsing = parser.PeekTokenString( "," ); + if ( parsing ) + parser.ExpectTokenString( "," ); + else + { + //we are at the end, and no bufferview or buffer has been found. + if ( !buffersDone || !bufferViewsDone ) + { + if ( !buffersDone ) + { + buffersDone = true; + common->Printf( "no %s found", "buffers" ); + } + if ( !bufferViewsDone ) + { + common->Printf( "no %s found", "bufferviews" ); + bufferViewsDone = true; + } + parser.Reset( ); + parser.ExpectTokenString( "{" ); + parsing = true; + continue; + } + + + parser.ExpectTokenString( "}" ); + } + } + //parser should be at end. + parser.ReadToken( &token ); + if ( parser.EndOfFile( ) ) + common->Printf( "%s ^2loaded\n", currentFile.c_str( ) ); + else + common->FatalError( "%s not fully loaded.", currentFile ); + + buffersDone = false; + bufferViewsDone = false; + return true; +} + +bool GLTF_Parser::Load(idStr filename ) +{ + //seriously fix this; proper gltf data cache. + //.. and destroy it properly too!! + static idStr lastFile = ""; + + //next line still has to be fixed. + //gfx is not updated on command + common->SetRefreshOnPrint( true ); + + if (lastFile == filename ) { + common->Warning("Did not parse %s again",filename.c_str()); + return true; + } + lastFile = filename; + currentFile = filename; + if ( filename.CheckExtension( ".glb" ) ) { + if ( !loadGLB( filename ) ) + return false; + } + else if ( filename.CheckExtension( ".gltf" ) ) { + int length = fileSystem->ReadFile(filename,NULL); + if (!length) + common->FatalError("Failed to read file"); + + gltfData * data = gltfData::Data( filename ); + data->FileName(filename); + byte* dataBuff = data->AddData(length); + currentAsset = data; + + idFile* file = fileSystem->OpenFileRead( filename ); + if ( file->Read(dataBuff,length)!=length) + common->FatalError("Cannot read file, %s",filename.c_str() ); + + fileSystem->CloseFile(file); + + if ( !parser.LoadMemory((const char*)dataBuff,length,"GLTF_ASCII_JSON",0)) + return false; + + Parse(); + + }else + return false; + + parser.Reset(); + parser.FreeSource(); + common->SetRefreshOnPrint( false ); + + //fix up node hierarchy + auto &nodeList = currentAsset->NodeList( ); + for ( auto &scene : currentAsset->SceneList( ) ) + for ( auto &node : scene->nodes ) + SetNodeParent( nodeList[node] ); + + //prefix with id + if ( gltfParser_PrefixNodeWithID.GetBool() ) + for (int i = 0; i < nodeList.Num(); i++) + nodeList[i]->name = "[" + idStr( i ) + "]" + nodeList[i]->name; + + //CreateBgfxData(); + return true; +} + +void GLTF_Parser::SetNodeParent( gltfNode *node, gltfNode *parent) +{ + node->parent = parent; + for ( auto & child : node->children ) + SetNodeParent( currentAsset->NodeList()[child], node ); +} + +/* +void GLTF_Parser::CreateBgfxData( ) +{ + //buffers + for ( auto mesh : currentAsset->MeshList( ) ) + { + for ( auto prim : mesh->primitives ) + { + //gltfAccessor -> idList; + //vertex indices accessor + gltfAccessor * accessor = currentAsset->AccessorList( )[prim->indices]; + gltfBufferView *bv = currentAsset->BufferViewList( )[accessor->bufferView]; + gltfData *data = bv->parent; + + gltfBuffer *buff = data->BufferList( )[bv->buffer]; + uint idxDataSize = sizeof( uint ) * accessor->count; + uint * indices = ( uint *)Mem_ClearedAlloc( idxDataSize ); + + idFile_Memory idxBin = idFile_Memory( "gltfChunkIndices", + ( const char * ) ((data->GetData( bv->buffer ) + bv->byteOffset + accessor->byteOffset )), bv->byteLength ); + + for ( int i = 0; i < accessor->count; i++ ) { + idxBin.Read( ( void * ) ( &indices[i] ), accessor->typeSize ); + if ( bv->byteStride ) + idxBin.Seek( bv->byteStride - accessor->typeSize, FS_SEEK_CUR ); + } + prim->indexBufferHandle = bgfx::createIndexBuffer(bgfx::copy( indices,idxDataSize ),BGFX_BUFFER_INDEX32); + + Mem_Free( indices ); + + //vertex attribs + pbrVertex * vtxData = NULL; + uint vtxDataSize = 0; + bgfx::VertexLayout vtxLayout; + + for ( auto & attrib : prim->attributes ) + { + gltfAccessor * attrAcc = currentAsset->AccessorList( )[attrib->accessorIndex]; + gltfBufferView *attrBv = currentAsset->BufferViewList( )[attrAcc->bufferView]; + gltfData *attrData = attrBv->parent; + gltfBuffer *attrbuff = attrData->BufferList( )[attrBv->buffer]; + + idFile_Memory bin = idFile_Memory( "gltfChunkVertices", + ( const char * )(( attrData->GetData( attrBv->buffer ) + attrBv->byteOffset + attrAcc->byteOffset )) , attrBv->byteLength ); + + if ( vtxData == nullptr ) { + vtxDataSize = sizeof( pbrVertex ) * attrAcc->count; + vtxData = ( pbrVertex * ) Mem_ClearedAlloc( vtxDataSize ); + } + + switch (attrib->bgfxType) + { + case bgfx::Attrib::Enum::Position : { + for ( int i = 0; i < attrAcc->count; i++ ) { + bin.Read( ( void * ) ( &vtxData[i].pos.x ), attrAcc->typeSize ); + bin.Read( ( void * ) ( &vtxData[i].pos.y ), attrAcc->typeSize ); + bin.Read( ( void * ) ( &vtxData[i].pos.z ), attrAcc->typeSize ); + if ( attrBv->byteStride ) + bin.Seek( attrBv->byteStride - ( 3 * attrAcc->typeSize ), FS_SEEK_CUR ); + + idRandom rnd( i ); + int r = rnd.RandomInt( 255 ), g = rnd.RandomInt( 255 ), b = rnd.RandomInt( 255 ); + + //vtxData[i].abgr = 0xff000000 + ( b << 16 ) + ( g << 8 ) + r; + } + + break; + } + case bgfx::Attrib::Enum::Normal : { + for ( int i = 0; i < attrAcc->count; i++ ) { + bin.Read( ( void * ) ( &vtxData[i].normal.x ), attrAcc->typeSize ); + bin.Read( ( void * ) ( &vtxData[i].normal.y ), attrAcc->typeSize ); + bin.Read( ( void * ) ( &vtxData[i].normal.z ), attrAcc->typeSize ); + if ( attrBv->byteStride ) + bin.Seek( attrBv->byteStride - ( attrib->elementSize * attrAcc->typeSize ), FS_SEEK_CUR ); + } + + break; + } + case bgfx::Attrib::Enum::TexCoord0:{ + for ( int i = 0; i < attrAcc->count; i++ ) { + bin.Read( ( void * ) ( &vtxData[i].uv.x ), attrAcc->typeSize ); + bin.Read( ( void * ) ( &vtxData[i].uv.y ), attrAcc->typeSize ); + if ( attrBv->byteStride ) + bin.Seek( attrBv->byteStride - ( attrib->elementSize * attrAcc->typeSize ), FS_SEEK_CUR ); + } + + break; + } + case bgfx::Attrib::Enum::Tangent: + { + for ( int i = 0; i < attrAcc->count; i++ ) { + bin.Read( ( void * ) ( &vtxData[i].tangent.x ), attrAcc->typeSize ); + bin.Read( ( void * ) ( &vtxData[i].tangent.y ), attrAcc->typeSize ); + bin.Read( ( void * ) ( &vtxData[i].tangent.z ), attrAcc->typeSize ); + bin.Read( ( void * ) ( &vtxData[i].tangent.w ), attrAcc->typeSize ); + if ( attrBv->byteStride ) + bin.Seek( attrBv->byteStride - ( attrib->elementSize * attrAcc->typeSize ), FS_SEEK_CUR ); + } + break; + } + case bgfx::Attrib::Enum::Weight: + { + for ( int i = 0; i < attrAcc->count; i++ ) { + bin.Read( ( void * ) ( &vtxData[i].weight.x ), attrAcc->typeSize ); + bin.Read( ( void * ) ( &vtxData[i].weight.y ), attrAcc->typeSize ); + bin.Read( ( void * ) ( &vtxData[i].weight.z ), attrAcc->typeSize ); + bin.Read( ( void * ) ( &vtxData[i].weight.w ), attrAcc->typeSize ); + if ( attrBv->byteStride ) + bin.Seek( attrBv->byteStride - ( attrib->elementSize * attrAcc->typeSize ), FS_SEEK_CUR ); + } + break; + } + + case bgfx::Attrib::Enum::Indices: + { + for ( int i = 0; i < attrAcc->count; i++ ) { + bin.Read( ( void * ) ( &vtxData[i].boneIndex.x ), attrAcc->typeSize ); + bin.Read( ( void * ) ( &vtxData[i].boneIndex.y ), attrAcc->typeSize ); + bin.Read( ( void * ) ( &vtxData[i].boneIndex.z ), attrAcc->typeSize ); + bin.Read( ( void * ) ( &vtxData[i].boneIndex.w ), attrAcc->typeSize ); + if ( attrBv->byteStride ) + bin.Seek( attrBv->byteStride - ( attrib->elementSize * attrAcc->typeSize ), FS_SEEK_CUR ); + } + break; + } + } + } + + vtxLayout.begin( ) + .add( bgfx::Attrib::Position, 3, bgfx::AttribType::Float ) + .add( bgfx::Attrib::Normal, 3, bgfx::AttribType::Float ) + .add( bgfx::Attrib::Tangent, 4, bgfx::AttribType::Float ) + .add( bgfx::Attrib::TexCoord0, 2, bgfx::AttribType::Float ) + .add( bgfx::Attrib::Weight, 4, bgfx::AttribType::Float,true ) + .add( bgfx::Attrib::Indices, 4, bgfx::AttribType::Uint8,false,true ) + //.add( bgfx::Attrib::Color0, 4, bgfx::AttribType::Uint8, true ) + .end( ); + vtxLayout.end(); + if ( vtxData != NULL ) { + prim->vertexBufferHandle = bgfx::createVertexBuffer( + bgfx::copy( vtxData, vtxDataSize ), vtxLayout ); + + Mem_Free( vtxData ); + }else + common->FatalError("Failed to read vertex info" ); + } + } + + auto & samplerList = currentAsset->SamplerList( ); + //Samplers -> this should move to read/parse sampler call + for ( auto &sampler : samplerList ) + sampler->bgfxSamplerFlags = GetSamplerFlags(sampler); + + auto & imgList = currentAsset->ImageList( ); + //textures + for ( auto &texture : currentAsset->TextureList( ) ) + { + auto * image = imgList[texture->source]; + if ( image->bgfxTexture.handle.idx == UINT16_MAX ) { + gltfBufferView *bv = currentAsset->BufferViewList( )[image->bufferView]; + gltfData *data = bv->parent; + gltfBuffer *buff = data->BufferList( )[bv->buffer]; + + //image->bgfxTexture = bgfxImageLoad(data->GetData(bv->buffer) + bv->byteOffset,bv->byteLength ); + bgfxImageLoadAsync( data->GetData( bv->buffer ) + bv->byteOffset, bv->byteLength, &image->bgfxTexture, samplerList[texture->sampler]->bgfxSamplerFlags ); + } + + } + + idList afs; + //AF + for (auto * skin : currentAsset->SkinList() ){ + afs.Alloc() = new gltfArticulatedFigure(skin,currentAsset); + } +} +*/ + +idList &gltfData::GetAccessorView(gltfAccessor * accessor ) { + idList *&floatView = accessor->floatView;; + + if ( floatView == nullptr ) + { + gltfBufferView *attrBv = bufferViews[accessor->bufferView]; + gltfData *attrData = attrBv->parent; + gltfBuffer *attrbuff = attrData->BufferList( )[attrBv->buffer]; + assert(sizeof(float) == accessor->typeSize ); + + idFile_Memory bin = idFile_Memory( "GetAccessorView(float)", + ( const char * ) ( ( attrData->GetData( attrBv->buffer ) + attrBv->byteOffset + accessor->byteOffset ) ), attrBv->byteLength ); + + floatView = new idList( 16 ); + floatView->AssureSize( accessor->count ); + for ( int i = 0; i < accessor->count; i++ ) { + bin.Read( ( void * ) &( *floatView )[i] , accessor->typeSize ); + } + if ( attrBv->byteStride ) + bin.Seek( attrBv->byteStride - ( accessor->typeSize ), FS_SEEK_CUR ); + } + return *floatView; +} + +idList &gltfData::GetAccessorViewMat( gltfAccessor *accessor ) { + idList *&matView = accessor->matView; + if ( matView == nullptr ) { + gltfBufferView *attrBv = bufferViews[accessor->bufferView]; + gltfData *attrData = attrBv->parent; + gltfBuffer *attrbuff = attrData->BufferList( )[attrBv->buffer]; + assert( sizeof( float ) == accessor->typeSize ); + + idFile_Memory bin = idFile_Memory( "GetAccessorView(idMat4*)", + ( const char * ) ( ( attrData->GetData( attrBv->buffer ) + attrBv->byteOffset + accessor->byteOffset ) ), attrBv->byteLength ); + + size_t elementSize = accessor->typeSize * 16; + matView = new idList( 16 ); + matView->AssureSize( accessor->count ); + for ( int i = 0; i < accessor->count; i++ ) { + bin.Read( ( void * ) &( *matView )[i] , elementSize); + } + if ( attrBv->byteStride ) + bin.Seek( attrBv->byteStride - elementSize, FS_SEEK_CUR ); + } + return *matView; +} + + +template <> +idList &gltfData::GetAccessorView( gltfAccessor *accessor ) { + idList *&vecView = accessor->vecView; + + if ( vecView == nullptr ) { + gltfBufferView *attrBv = bufferViews[accessor->bufferView]; + gltfData *attrData = attrBv->parent; + gltfBuffer *attrbuff = attrData->BufferList( )[attrBv->buffer]; + assert(sizeof(float) == accessor->typeSize ); + + idFile_Memory bin = idFile_Memory( "GetAccessorView(idVec3*)", + ( const char * ) ( ( attrData->GetData( attrBv->buffer ) + attrBv->byteOffset + accessor->byteOffset ) ), attrBv->byteLength ); + + vecView = new idList( 16 ); + vecView->AssureSizeAlloc( accessor->count, idListNewElement ); + for ( int i = 0; i < accessor->count; i++ ) { + idVec3 & vec = *(*vecView)[i]; + bin.Read( ( void * ) &vec.x , accessor->typeSize ); + bin.Read( ( void * ) &vec.y , accessor->typeSize ); + bin.Read( ( void * ) &vec.z , accessor->typeSize ); + } + if ( attrBv->byteStride ) + bin.Seek( attrBv->byteStride - ( 3 * accessor->typeSize ), FS_SEEK_CUR ); + } + return *vecView; +} + +template <> +idList &gltfData::GetAccessorView( gltfAccessor *accessor ) { + idList *&quatView = accessor->quatView; + + if ( quatView == nullptr ) { + gltfBufferView *attrBv = bufferViews[accessor->bufferView]; + gltfData *attrData = attrBv->parent; + gltfBuffer *attrbuff = attrData->BufferList( )[attrBv->buffer]; + assert( sizeof( float ) == accessor->typeSize ); + + idFile_Memory bin = idFile_Memory( "GetAccessorView(idQuat*)", + ( const char * ) ( ( attrData->GetData( attrBv->buffer ) + attrBv->byteOffset + accessor->byteOffset ) ), attrBv->byteLength ); + + quatView = new idList( 16 ); + quatView->AssureSizeAlloc( accessor->count, idListNewElement ); + for ( int i = 0; i < accessor->count; i++ ) { + idQuat &vec = *( *quatView )[i]; + bin.Read( ( void * ) &vec.x, accessor->typeSize ); + bin.Read( ( void * ) &vec.y, accessor->typeSize ); + bin.Read( ( void * ) &vec.z, accessor->typeSize ); + bin.Read( ( void * ) &vec.w, accessor->typeSize ); + } + if ( attrBv->byteStride ) + bin.Seek( attrBv->byteStride - ( 4 * accessor->typeSize ), FS_SEEK_CUR ); + } + return *quatView; +} + + +gltfData::~gltfData() { + //hvg_todo + //delete data, not only pointer + common->Warning("GLTF DATA NOT FREED" ); + if (data) + delete[] data; + + //delete cameraManager; + +} + +GLTF_Parser localGltfParser; +GLTF_Parser * gltfParser = &localGltfParser; + +#undef GLTFARRAYITEM +#undef GLTFARRAYITEMREF + +CONSOLE_COMMAND_COMPILE( LoadGLTF, "Loads an .gltf or .glb file",idCmdSystem::ArgCompletion_MapName ) { + + if ( args.Argc( ) > 1 ) { + gltfParser->Load( args.Argv( 1 ) ); + } + +} +//+set r_fullscreen 0 +set com_allowConsole 1 +set developer 1 +set fs_debug 0 +set win_outputDebugString 1 +set fs_basepath "E:\SteamLibrary\steamapps\common\DOOM 3 BFG Edition\" \ No newline at end of file diff --git a/neo/idlib/gltfParser.h b/neo/idlib/gltfParser.h new file mode 100644 index 00000000..73ec046a --- /dev/null +++ b/neo/idlib/gltfParser.h @@ -0,0 +1,240 @@ +#pragma once +#include "containers/StrList.h" +#include +#include "gltfProperties.h" + +#pragma region GLTF Types parsing + +#pragma region Parser interfaces +struct parsable { +public: + virtual void parse(idToken & token )=0; + virtual void parse(idToken & token , idLexer * parser){}; + virtual idStr &Name( ) = 0; +}; + +template +class parseType { +public: + void Set(T * type ) { item = type; } + virtual ~parseType() { delete item; } + T* item; +}; + +class gltfItem : public parsable, public parseType +{ +public: + gltfItem( idStr Name) : name( Name ) { item = nullptr; } + virtual void parse( idToken &token ) { *item = token; }; + virtual idStr &Name( ) {return name;} + ~gltfItem(){} +private: + idStr name; +}; + +class gltfObject : public parsable, public parseType { +public: + gltfObject( idStr Name ) : name( Name ), object("null"){} + virtual void parse( idToken &token ) {} + virtual void parse(idToken & token , idLexer * parser){ + parser->UnreadToken( &token );parser->ParseBracedSection( object ); + } + virtual idStr &Name( ) { return name; } +private: + idStr name; + idStr object; +}; + +class gltfItemArray; +class gltfItem_Extra : public parsable, public parseType { +public: + gltfItem_Extra( idStr Name ) : name( Name ), data(nullptr),parser(nullptr) { item = nullptr; } + virtual void parse( idToken &token ) ; + virtual idStr &Name( ) { return name; } + void Set( gltfExtra *type, idLexer *lexer ) { parseType::Set( type ); parser = lexer; } + static void Register(parsable * extra); +private: + idStr name; + gltfData *data; + idLexer* parser; + static gltfItemArray*items; +}; + +class gltfItem_uri : public parsable, public parseType { +public: + gltfItem_uri( idStr Name ) : name( Name ) { item = nullptr; } + virtual void parse( idToken &token ) { *item = token; Convert(); }; + virtual idStr &Name( ) { return name; } + void Set( idStr *type,int * targetBufferview,gltfData* dataDestination ) { parseType::Set(type); bufferView = targetBufferview; data = dataDestination; } + // read data from uri file, and push it at end of current data buffer for this GLTF File + // bufferView will be set accordingly to the generated buffer. + bool Convert( ); +private: + idStr name; + int * bufferView; + gltfData * data; +}; +#pragma endregion + +#pragma region helper macro to define gltf data types with extra parsing context forced to be implemented externally +#define gltfItemClassParser(className,ptype) \ +class gltfItem_##className : public parsable, public parseType \ +{public: \ + gltfItem_##className( idStr Name ) : name( Name ){ item = nullptr; } \ + virtual void parse( idToken &token ); \ + virtual idStr &Name( ) { return name; } \ + void Set( ptype *type, idLexer *lexer ) { parseType::Set( type ); parser = lexer; } \ +private: \ + idStr name; \ + idLexer *parser;} +#pragma endregion + +gltfItemClassParser( animation_sampler, idList ); +gltfItemClassParser( animation_channel_target, gltfAnimation_Channel_Target ); +gltfItemClassParser( animation_channel, idList); +gltfItemClassParser( mesh_primitive, idList); +gltfItemClassParser( mesh_primitive_attribute, idList ); +gltfItemClassParser( integer_array, idList); +gltfItemClassParser( number_array, idList);//does float suffice? +gltfItemClassParser( mat4, idMat4 ); +gltfItemClassParser( vec4, idVec4 ); +gltfItemClassParser( vec3, idVec3 ); +gltfItemClassParser( vec2, idVec2 ); +gltfItemClassParser( quat, idQuat ); +gltfItemClassParser( accessor_sparse, gltfAccessor_Sparse ); +gltfItemClassParser( accessor_sparse_indices, gltfAccessor_Sparse_Indices ); +gltfItemClassParser( accessor_sparse_values, gltfAccessor_Sparse_Values ); +gltfItemClassParser( camera_perspective, gltfCamera_Perspective ); +gltfItemClassParser( camera_orthographic, gltfCamera_Orthographic ); +gltfItemClassParser( pbrMetallicRoughness, gltfMaterial_pbrMetallicRoughness ); +gltfItemClassParser( texture_info, gltfTexture_Info); +gltfItemClassParser( normal_texture, gltfNormalTexture_Info); +gltfItemClassParser( occlusion_texture, gltfOcclusionTexture_Info ); +gltfItemClassParser( node_extensions, gltfNode_Extensions ); +gltfItemClassParser( material_extensions, gltfMaterial_Extensions ); +gltfItemClassParser( texture_info_extensions, gltfTexture_Info_Extensions ); + +//extensions +gltfItemClassParser( KHR_lights_punctual, gltfExtensions ); +gltfItemClassParser( Node_KHR_lights_punctual, gltfNode_Extensions ); +gltfItemClassParser( Material_KHR_materials_pbrSpecularGlossiness, gltfMaterial_Extensions ); +gltfItemClassParser( TextureInfo_KHR_texture_transform, gltfTexture_Info_Extensions ); + +#undef gltfItemClassParser + +#pragma region helper macro to define more gltf data types that only rely on token +#define gltfItemClass(className,type,function) \ +class gltfItem_##className : public parsable, public parseType \ +{public: \ + gltfItem_##className( idStr Name ) : name( Name ){ item = nullptr; } \ + virtual void parse( idToken &token ) { function } \ + virtual idStr &Name( ) { return name; } \ +private: \ + idStr name;} +#pragma endregion + +gltfItemClass(integer, int, *item = token.GetIntValue( ); ); +gltfItemClass(number, float, *item = token.GetFloatValue( ); ); +gltfItemClass(boolean, bool, if (token.Icmp("true") == 0 ) *item=true; else{ if(token.Icmp("false") == 0)*item=false; else idLib::FatalError("parse error");}); +#undef gltfItemClass + +class gltfItemArray +{ +public: + ~gltfItemArray( ) { items.DeleteContents(true); } + gltfItemArray( ) { }; + int Num() { return items.Num(); } + void AddItemDef( parsable *item ) { items.Alloc( ) = item;} + int Fill(idLexer * lexer , idDict * strPairs ); + int Parse(idLexer * lexer , bool forwardLexer = false ); + template + T* Get(idStr name ){ for ( auto * item : items) if (item->Name() == name) return static_cast(item); return nullptr; } +private: + idList items; +}; +#pragma endregion + +#pragma region GLTF Object parsing +class gltfPropertyArray; +class gltfPropertyItem +{ +public: + gltfPropertyItem( ) : array(nullptr){ } + gltfPropertyArray * array; + idToken item; +}; + +class gltfPropertyArray +{ +public: + gltfPropertyArray( idLexer *Parser,bool AoS = true ); + ~gltfPropertyArray( ); + struct Iterator { + gltfPropertyArray * array; + gltfPropertyItem *p; + gltfPropertyItem &operator*( ) {return *p;} + bool operator != ( Iterator &rhs ) { + return p != rhs.p; + } + void operator ++( ); + }; + gltfPropertyArray::Iterator begin( ); + gltfPropertyArray::Iterator end( ); +private: + bool iterating; + bool dirty; + int index; + idLexer * parser; + idList properties; + gltfPropertyItem * endPtr; + bool isArrayOfStructs; +}; +#pragma endregion + +class GLTF_Parser +{ +public: + GLTF_Parser(); + void Shutdown(); + bool Parse(); + bool Load(idStr filename ); + bool loadGLB(idStr filename ); + + //current/last loaded gltf asset and index offsets + gltfData *currentAsset; +private: + void SetNodeParent( gltfNode *node, gltfNode *parent = nullptr ); + //void CreateBgfxData( ); + + void Parse_ASSET( idToken &token ); + void Parse_CAMERAS( idToken &token ); + void Parse_SCENE( idToken &token ); + void Parse_SCENES( idToken &token ); + void Parse_NODES( idToken &token ); + void Parse_MATERIALS( idToken &token ); + void Parse_MESHES( idToken &token ); + void Parse_TEXTURES( idToken &token ); + void Parse_IMAGES( idToken &token ); + void Parse_ACCESSORS( idToken &token ); + void Parse_BUFFERVIEWS( idToken &token ); + void Parse_SAMPLERS( idToken &token ); + void Parse_BUFFERS( idToken &token ); + void Parse_ANIMATIONS( idToken &token ); + void Parse_SKINS( idToken &token ); + void Parse_EXTENSIONS( idToken &token ); + void Parse_EXTENSIONS_USED( idToken &token ); + void Parse_EXTENSIONS_REQUIRED( idToken &token ); + + + gltfProperty ParseProp( idToken &token ); + gltfProperty ResolveProp( idToken &token ); + + idLexer parser; + idToken token; + idStr currentFile; + + bool buffersDone; + bool bufferViewsDone; +}; + +extern GLTF_Parser * gltfParser; \ No newline at end of file diff --git a/neo/idlib/gltfProperties.h b/neo/idlib/gltfProperties.h new file mode 100644 index 00000000..5e160f7f --- /dev/null +++ b/neo/idlib/gltfProperties.h @@ -0,0 +1,838 @@ +#pragma once +#include "containers/StrList.h" +#include +#include "math/Quat.h" +#include "Lib.h" +#include "containers/List.h" + +enum gltfProperty { + INVALID, + ASSET, + ACCESSOR, + CAMERAS, + SCENE, + SCENES, + NODES, + MATERIALS, + MESHES, + TEXTURES, + IMAGES, + ACCESSORS, + BUFFERVIEWS, + SAMPLERS, + BUFFERS, + ANIMATIONS, + SKINS, + EXTENSIONS, + EXTENSIONS_USED, + EXTENSIONS_REQUIRED +}; + + +class gltfData; + +struct gltf_accessor_component +{ + enum Type { + _byte, + _uByte, + _short, + _uShort, + _uInt, + _float, + _double, + Count + }; +}; + +template< class T > +struct gltf_accessor_component_type_map { + idStr stringID; + int id; + T type; + uint sizeInBytes;//single element +}; + +class gltfExtra +{ +public: + gltfExtra( ) { } + //entire extra json scope + idStr json; + //str:str pairs of each item + idDict strPairs; + //specialized parsers + idList extras; +}; + +class gltfExt_KHR_lights_punctual; +class gltfExtensions { +public: + gltfExtensions( ) { } + idList KHR_lights_punctual; +}; + +class gltfNode_KHR_lights_punctual { +public: + int light; +}; + +class gltfNode_Extensions { +public: + gltfNode_Extensions( ) : + KHR_lights_punctual( nullptr) { } + gltfNode_KHR_lights_punctual* KHR_lights_punctual; +}; + +class gltfExt_KHR_materials_pbrSpecularGlossiness; +class gltfMaterial_Extensions { +public: + gltfMaterial_Extensions( ) : + KHR_materials_pbrSpecularGlossiness( nullptr ) { } + gltfExt_KHR_materials_pbrSpecularGlossiness *KHR_materials_pbrSpecularGlossiness; +}; + + +class gltfNode { +public: + gltfNode( ) : camera( -1 ), skin( -1 ), matrix( mat4_zero ), + mesh( -1 ), rotation( 0.f, 0.f, 0.f, 1.f ), scale( 1.f, 1.f, 1.f ), + translation( vec3_zero ), parent( nullptr ), dirty( true ) { } + int camera; + idList children; + int skin; + idMat4 matrix; + int mesh; + idQuat rotation; + idVec3 scale; + idVec3 translation; + idList weights; + idStr name; + gltfNode_Extensions extensions; + gltfExtra extras; + + // + gltfNode * parent; + bool dirty; +}; + +struct gltfCameraNodePtrs { + gltfNode *translationNode = nullptr; + gltfNode *orientationNode = nullptr; +}; + +class gltfScene { +public: + gltfScene( ) { } + idList nodes; + idStr name; + idStr extensions; + gltfExtra extras; +}; + +class gltfMesh_Primitive_Attribute { +public: + enum Type { + Position, + Normal, + Tangent, + TexCoord0, + TexCoord1, + TexCoord2, + TexCoord3, + TexCoord4, + TexCoord5, + TexCoord6, + TexCoord7, + Color0, + Color1, + Color2, + Color3, + Weight, + Indices, + Count + }; + + gltfMesh_Primitive_Attribute( ) : accessorIndex( -1 ), elementSize( 0 ), type( gltfMesh_Primitive_Attribute::Type::Count ){ } + idStr attributeSemantic; + int accessorIndex; + uint elementSize; + + Type type; +}; + +struct gltf_mesh_attribute_map { + idStr stringID; + gltfMesh_Primitive_Attribute::Type attib; + uint elementSize; +}; + +class gltfMesh_Primitive { +public: + gltfMesh_Primitive( ) : indices( -1 ), material( -1 ), mode( -1 ) { } + idList attributes; + int indices; + int material; + int mode; + idStr target; + idStr extensions; + gltfExtra extras; +}; + +class gltfMesh { +public: + gltfMesh( ) { }; + + idList primitives; // gltfMesh_Primitive[1,*] + idList weights; // number[1,*] + idStr name; + idStr extensions; + gltfExtra extras; +}; + +class gltfCamera_Orthographic { +public: + gltfCamera_Orthographic( ) : xmag( 0.0f ), ymag( 0.0f ), zfar( 0.0f ), znear( 0.0f ) { }; + float xmag; + float ymag; + float zfar; + float znear; + idStr extensions; + gltfExtra extras; +}; + +class gltfCamera_Perspective { +public: + gltfCamera_Perspective( ) : aspectRatio( 0.0f ), yfov( 0.0f ), zfar( 0.0f ), znear( 0.0f ) { }; + float aspectRatio; + float yfov; + float zfar; + float znear; + idStr extensions; + gltfExtra extras; +}; + +class gltfCamera { +public: + gltfCamera( ) { }; + gltfCamera_Orthographic orthographic; + gltfCamera_Perspective perspective; + idStr type; + idStr name; + idStr extensions; + gltfExtra extras; +}; + +class gltfAnimation_Channel_Target { +public: + gltfAnimation_Channel_Target( ) : node( -1 ), TRS( gltfTRS::count ) { }; + int node; + idStr path; + idStr extensions; + gltfExtra extras; + + enum gltfTRS { + none, + rotation, + translation, + scale, + weights, + count + }; + + gltfTRS TRS; + + static gltfTRS resolveType( idStr type ) { + if ( type == "translation" ) + return gltfTRS::translation; + else if ( type == "rotation" ) + return gltfTRS::rotation; + else if ( type == "scale" ) + return gltfTRS::scale; + else if ( type == "weights" ) + return gltfTRS::weights; + return gltfTRS::count; + } +}; + +class gltfAnimation_Channel { +public: + gltfAnimation_Channel( ) : sampler( -1 ) { }; + int sampler; + gltfAnimation_Channel_Target target; + idStr extensions; + gltfExtra extras; +}; + +class gltfAnimation_Sampler { +public: + gltfAnimation_Sampler( ) : input( -1 ), interpolation("LINEAR"),output( -1 ), intType(gltfInterpType::count) { }; + int input; + idStr interpolation; + int output; + idStr extensions; + gltfExtra extras; + + enum gltfInterpType { + linear, + step, + cubicSpline, + count + }; + + gltfInterpType intType; + + static gltfInterpType resolveType( idStr type ) { + if ( type == "LINEAR" ) + return gltfInterpType::linear; + else if ( type == "STEP" ) + return gltfInterpType::step; + else if ( type == "CUBICSPLINE" ) + return gltfInterpType::cubicSpline; + return gltfInterpType::count; + } + +}; + +class gltfAnimation { +public: + gltfAnimation( ) : maxTime (0.0f),numFrames(0) { }; + idList channels; + idList samplers; + idStr name; + idStr extensions; + gltfExtra extras; + + float maxTime; + + //id specific + mutable int ref_count; + int numFrames; + void DecreaseRefs() const {ref_count--;}; + void IncreaseRefs() const {ref_count++;}; + bool GetBounds( idBounds &bnds, int time, int cyclecount ) const { return false;} + bool GetOriginRotation( idQuat &rotation, int time, int cyclecount ) const { return false;} + bool GetOrigin( idVec3 &offset, int time, int cyclecount ) const { return false;} + const idVec3 &TotalMovementDelta( void ) const {static idVec3 temp; return temp; } + int NumFrames() const {return numFrames;} +}; + +class gltfAccessor_Sparse_Values { +public: + gltfAccessor_Sparse_Values( ) : bufferView( -1 ), byteOffset( -1 ) { }; + int bufferView; + int byteOffset; + idStr extensions; + gltfExtra extras; +}; + +class gltfAccessor_Sparse_Indices { +public: + gltfAccessor_Sparse_Indices( ) : bufferView( -1 ), byteOffset( -1 ), componentType( -1 ) { }; + int bufferView; + int byteOffset; + int componentType; + idStr extensions; + gltfExtra extras; +}; + +class gltfAccessor_Sparse { +public: + gltfAccessor_Sparse( ) : count( -1 ) { }; + int count; + gltfAccessor_Sparse_Indices indices; + gltfAccessor_Sparse_Values values; + idStr extensions; + gltfExtra extras; +}; + +class gltfAccessor { +public: + gltfAccessor( ) : bufferView( -1 ), byteOffset( 0 ), componentType( -1 ), normalized( false ), count( -1 ) , + floatView(nullptr),vecView(nullptr),quatView(nullptr),matView(nullptr){ } + int bufferView; + int byteOffset; + int componentType; + bool normalized; + int count; + idStr type; + idList max; + idList min; + gltfAccessor_Sparse sparse; + idStr name; + idStr extensions; + gltfExtra extras; + + uint typeSize; + + idList * floatView; + idList * vecView; + idList * quatView; + idList * matView; +}; + +class gltfBufferView { +public: + gltfBufferView( ) : buffer( -1 ), byteLength( -1 ), byteStride( 0 ), byteOffset( 0 ), target( -1 ) { }; + int buffer; + int byteLength; + int byteStride; + int byteOffset; + int target; + idStr name; + idStr extensions; + gltfExtra extras; + // + gltfData *parent; +}; + +class gltfBuffer { +public: + gltfBuffer( ) : byteLength( -1 ), parent( nullptr ) { }; + idStr uri; + int byteLength; + idStr name; + idStr extensions; + gltfExtra extras; + // + gltfData * parent; +}; + +class gltfSampler { +public: + gltfSampler( ) : magFilter( 0 ), minFilter( 0 ), wrapS( 10497 ), wrapT( 10497 ) { }; + int magFilter; + int minFilter; + int wrapS; + int wrapT; + idStr name; + idStr extensions; + gltfExtra extras; + // + uint bgfxSamplerFlags; +}; + +class gltfImage { +public: + gltfImage( ) : bufferView( -1 ) { } + idStr uri; + idStr mimeType; + int bufferView; + idStr name; + idStr extensions; + gltfExtra extras; +}; + +class gltfSkin { +public: + gltfSkin( ) : inverseBindMatrices(-1),skeleton(-1),name("unnamedSkin"){ }; + int inverseBindMatrices; + int skeleton; + idList joints; // integer[1,*] + idStr name; + idStr extensions; + gltfExtra extras; +}; + +class gltfExt_KHR_texture_transform; +class gltfTexture_Info_Extensions { +public: + gltfTexture_Info_Extensions( ) : + KHR_texture_transform( nullptr ) { } + gltfExt_KHR_texture_transform *KHR_texture_transform; +}; + +class gltfOcclusionTexture_Info { +public: + gltfOcclusionTexture_Info( ) : index( -1 ), texCoord( 0 ), strength( 1.0f ) { } + int index; + int texCoord; + float strength; + gltfTexture_Info_Extensions extensions; + gltfExtra extras; +}; + +class gltfNormalTexture_Info { +public: + gltfNormalTexture_Info( ) : index( -1 ), texCoord( 0 ), scale( 1.0f ) { } + int index; + int texCoord; + float scale; + gltfTexture_Info_Extensions extensions; + gltfExtra extras; +}; + +class gltfTexture_Info { +public: + gltfTexture_Info( ) : index( -1 ), texCoord( 0 ) { } + int index; + int texCoord; + gltfTexture_Info_Extensions extensions; + gltfExtra extras; +}; + + +class gltfTexture { +public: + gltfTexture( ) : sampler( -1 ), source( -1 ) { } + int sampler; + int source; + idStr name; + gltfTexture_Info_Extensions extensions; + gltfExtra extras; +}; + +class gltfMaterial_pbrMetallicRoughness { +public: + gltfMaterial_pbrMetallicRoughness( ) : baseColorFactor( vec4_one ), metallicFactor( 1.0f ), roughnessFactor( 1.0f ) { } + idVec4 baseColorFactor; + gltfTexture_Info baseColorTexture; + float metallicFactor; + float roughnessFactor; + gltfTexture_Info metallicRoughnessTexture; + idStr extensions; + gltfExtra extras; +}; + +class gltfMaterial { +public: + enum gltfAlphaMode { + gltfOPAQUE, + gltfMASK, + gltfBLEND, + count + }; + + gltfMaterial( ) : emissiveFactor( vec3_zero ), alphaMode( "OPAQUE" ), alphaCutoff( 0.5f ), doubleSided( false ) { } + gltfMaterial_pbrMetallicRoughness pbrMetallicRoughness; + gltfNormalTexture_Info normalTexture; + gltfOcclusionTexture_Info occlusionTexture; + gltfTexture_Info emissiveTexture; + idVec3 emissiveFactor; + idStr alphaMode; + float alphaCutoff; + bool doubleSided; + idStr name; + gltfMaterial_Extensions extensions; + gltfExtra extras; + + gltfAlphaMode intType; + + static gltfAlphaMode resolveAlphaMode( idStr type ) { + if ( type == "OPAQUE" ) + return gltfAlphaMode::gltfOPAQUE; + else if ( type == "MASK" ) + return gltfAlphaMode::gltfMASK; + else if ( type == "BLEND" ) + return gltfAlphaMode::gltfBLEND; + return gltfAlphaMode::count; + } +}; + +class gltfAsset { +public: + gltfAsset( ) { } + idStr copyright; + idStr generator; + idStr version; + idStr minVersion; + idStr extensions; + gltfExtra extras; +}; + +//this is not used. +//if an extension is found, it _will_ be used. (if implemented) +class gltfExtensionsUsed { +public: + gltfExtensionsUsed( ) { } + idStr extension; +}; + +//ARCHIVED? +//https://github.com/KhronosGroup/glTF/tree/main/extensions/2.0/Archived/KHR_materials_pbrSpecularGlossiness +class gltfExt_KHR_materials_pbrSpecularGlossiness +{ +public: + gltfExt_KHR_materials_pbrSpecularGlossiness( ) { } + idVec4 diffuseFactor; + gltfTexture_Info diffuseTexture; + idVec3 specularFactor; + float glossinessFactor; + gltfTexture_Info specularGlossinessTexture; + idStr extensions; + gltfExtra extras; +}; + +//KHR_lights_punctual_spot +//https://github.com/KhronosGroup/glTF/blob/main/extensions/2.0/Khronos/KHR_lights_punctual/schema/light.spot.schema.json +class gltfExt_KHR_lights_punctual_spot { +public: + gltfExt_KHR_lights_punctual_spot( ) : innerConeAngle(0.0f), outerConeAngle( idMath::ONEFOURTH_PI ){ } + float innerConeAngle; + float outerConeAngle; + idStr extensions; + gltfExtra extras; +}; +typedef gltfExt_KHR_lights_punctual_spot spot; + +//KHR_lights_punctual +//https://github.com/KhronosGroup/glTF/blob/main/extensions/2.0/Khronos/KHR_lights_punctual/schema/light.schema.json +class gltfExt_KHR_lights_punctual { +public: + gltfExt_KHR_lights_punctual( ) : color(vec3_one),intensity(1.0f),range(-1.0f),intType(-1) { } + idVec3 color; + float intensity; + spot spot; + idStr type; //directional=0,point=1,spot=2 + float range; + idStr name; + idStr extensions; + gltfExtra extras; + + int intType; + + static int resolveType( idStr type ) { + if (type == "directional" ) + return 0; + else if (type == "point" ) + return 1; + else if (type == "spot" ) + return 2; + return -1; + } +}; + +//KHR_texture_transform +//https://github.com/KhronosGroup/glTF/blob/main/extensions/2.0/Khronos/KHR_texture_transform/schema/KHR_texture_transform.textureInfo.schema.json +class gltfExt_KHR_texture_transform { +public: + gltfExt_KHR_texture_transform( ) : offset( vec2_zero ), rotation( 0.0f ), scale( vec2_one ), texCoord( -1 ),index(0),resolved(false) { } + idVec2 offset; + float rotation; + idVec2 scale; + int texCoord; + idStr extensions; +gltfExtra extras; + + //for shader + uint index; + bool resolved; +}; + +///////////////////////////////////////////////////////////////////////////// +//// For these to function you need to add an private idList {target} +#define GLTFCACHEITEM(name,target) \ +gltf##name * name ( ) { target.AssureSizeAlloc( target.Num()+1,idListNewElement); return target[target.Num()-1];} \ +const inline idList & ##name##List() { return target; } + + +// URI's are resolved during parsing so that +// all data should be layed out like an GLB with multiple bin chunks +// EACH URI will have an unique chunk +// JSON chunk MUST be the first one to be allocated/added + +class gltfData { +public: + gltfData( ) : fileNameHash( 0 ), json( nullptr ), data( nullptr ), totalChunks( -1 ) { }; + ~gltfData( ); + byte *AddData( int size, int *bufferID = nullptr ); + byte *GetJsonData( int &size ) { size = jsonDataLength; return json; } + byte *GetData( int index ) { return data[index]; } + void FileName( const idStr &file ) { fileName = file; fileNameHash = fileDataHash.GenerateKey( file.c_str( ) ); } + int FileNameHash( ) { return fileNameHash; } + idStr &FileName( ) { return fileName; } + + static idHashIndex fileDataHash; + static idList dataList; + //add data from filename + static gltfData *Data( idStr &fileName ) { + dataList.AssureSizeAlloc( dataList.Num( ) + 1, idListNewElement ); + dataList[dataList.Num( ) - 1]->FileName( fileName ); + fileDataHash.Add( fileDataHash.GenerateKey( fileName ), dataList.Num( ) - 1 ); + return dataList[dataList.Num( ) - 1]; + } + //find data; + static gltfData *Data( const char *filename ) { return dataList[fileDataHash.First( fileDataHash.GenerateKey( filename ) )]; } + static const idList &DataList( ) { return dataList; } + static void ClearData( ) { idLib::Warning( "TODO! DATA NOT FREED" ); } + + //return the GLTF nodes that control the given camera + //return TRUE if the camera uses 2 nodes (like when blender exports gltfs with +Y..) + //This is determined by checking for an "_Orientation" suffix to the camera name of the node that has the target camera assigned. + // if so, translate node will be set to the parent node of the orientation node. + //Note: does not take overides into account! + gltfNode* GetCameraNodes( gltfCamera *camera ) + { + gltfCameraNodePtrs result; + + assert( camera ); + int camId = -1; + for ( auto *cam : cameras ) + { + camId++; + if ( cam == camera ) + break; + } + + for ( int i = 0; i < nodes.Num( ); i++ ) + { + if ( nodes[i]->camera != -1 && nodes[i]->camera == camId ) + return nodes[i]; + } + + return nullptr; + } + + idMat4 GetViewMatrix( int camId ) const + { + //if (cameraManager->HasOverideID(camId) ) + //{ + // auto overrideCam = cameraManager->GetOverride( camId ); + // camId = overrideCam.newCameraID; + //} + + idMat4 result = mat4_identity; + + idList hierachy(2); + gltfNode* parent = nullptr; + + for ( int i = 0; i < nodes.Num( ); i++ ) + { + if ( nodes[i]->camera != -1 && nodes[i]->camera == camId ) + { + parent = nodes[i]; + while ( parent ) { + hierachy.Append( parent ); + parent = parent->parent; + } + break; + } + } + + for ( int i = hierachy.Num( ) - 1; i >= 0; i-- ) + { + ResolveNodeMatrix(hierachy[i]); + result *= hierachy[i]->matrix; + } + + return result; + } + //Please note : assumes all nodes are _not_ dirty! + idMat4 GetLightMatrix( int lightId ) const + { + idMat4 result = mat4_identity; + + idList hierachy; + gltfNode *parent = nullptr; + hierachy.SetGranularity( 2 ); + + for ( int i = 0; i < nodes.Num( ); i++ ) { + if ( nodes[i]->extensions.KHR_lights_punctual && nodes[i]->extensions.KHR_lights_punctual->light == lightId ) { + parent = nodes[i]; + while ( parent ) { + hierachy.Append( parent ); + parent = parent->parent; + } + break; + } + } + + for ( int i = hierachy.Num( ) - 1; i >= 0; i-- ) + result *= hierachy[i]->matrix; + + return result; + } + + // v * T * R * S. ->row major + // v' = S * R * T * v -> column major; + //bgfx = column-major + //idmath = row major, except mat3 + //gltf matrices : column-major. + //if mat* is valid , it will be multplied by this node's matrix that is resolved in its full hiararchy and stops at root. + static void ResolveNodeMatrix( gltfNode *node, idMat4 *mat = nullptr ,gltfNode *root = nullptr ) + { + if ( node->dirty ) + { + idMat4 scaleMat = idMat4( + node->scale.x, 0, 0, 0, + 0, node->scale.y, 0, 0, + 0, 0, node->scale.z, 0, + 0, 0, 0, 1 + ); + + node->matrix = idMat4( mat3_identity, node->translation ) * node->rotation.ToMat4( ).Transpose( ) * scaleMat; + + node->dirty = false; + } + + //resolve full hierarchy + if ( mat != nullptr ) { + idList hierachy(2); + gltfNode *parent = node; + while ( parent ) { + ResolveNodeMatrix(parent); + hierachy.Append( parent ); + if ( parent == root ) + break; + parent = parent->parent; + } + for ( int i = hierachy.Num( ) - 1; i >= 0; i-- ) + *mat *= hierachy[i]->matrix; + } + } + + void Advance( gltfAnimation *anim = nullptr ); + + //this copies the data and view cached on the accessor + template + idList &GetAccessorView( gltfAccessor *accessor ); + idList &GetAccessorView( gltfAccessor *accessor ); + idList &GetAccessorViewMat( gltfAccessor *accessor ); + + int &DefaultScene( ) { return scene; } + GLTFCACHEITEM( Buffer, buffers ) + GLTFCACHEITEM( Sampler, samplers ) + GLTFCACHEITEM( BufferView, bufferViews ) + GLTFCACHEITEM( Image, images ) + GLTFCACHEITEM( Texture, textures ) + GLTFCACHEITEM( Accessor, accessors ) + GLTFCACHEITEM( ExtensionsUsed, extensionsUsed ) + GLTFCACHEITEM( Mesh, meshes ) + GLTFCACHEITEM( Scene, scenes ) + GLTFCACHEITEM( Node, nodes ) + GLTFCACHEITEM( Camera, cameras ) + GLTFCACHEITEM( Material, materials ) + GLTFCACHEITEM( Extensions, extensions ) + GLTFCACHEITEM( Animation, animations ) + GLTFCACHEITEM( Skin, skins ) + + //gltfCameraManager * cameraManager; +private: + idStr fileName; + int fileNameHash; + + byte *json; + byte **data; + int jsonDataLength; + int totalChunks; + + idList buffers; + idList images; + idList assetData; + idList samplers; + idList bufferViews; + idList textures; + idList accessors; + idList extensionsUsed; + idList meshes; + int scene; + idList scenes; + idList nodes; + idList cameras; + idList materials; + idList extensions; + idList animations; + idList skins; +}; + +#undef GLTFCACHEITEM \ No newline at end of file diff --git a/neo/idlib/math/Vector.cpp b/neo/idlib/math/Vector.cpp index 64e0bb4d..8547e010 100644 --- a/neo/idlib/math/Vector.cpp +++ b/neo/idlib/math/Vector.cpp @@ -30,6 +30,10 @@ If you have questions concerning this license or the applicable additional terms #include "precompiled.h" #pragma hdrstop +idVec2 vec2_one( 1.0f, 1.0f ); +idVec3 vec3_one( 1.0f, 1.0f, 1.0f ); +idVec4 vec4_one( 1.0f, 1.0f, 1.0f, 1.0f ); + idVec2 vec2_origin( 0.0f, 0.0f ); idVec3 vec3_origin( 0.0f, 0.0f, 0.0f ); idVec4 vec4_origin( 0.0f, 0.0f, 0.0f, 0.0f ); diff --git a/neo/idlib/math/Vector.h b/neo/idlib/math/Vector.h index 1c0fc138..c8853b6d 100644 --- a/neo/idlib/math/Vector.h +++ b/neo/idlib/math/Vector.h @@ -109,6 +109,7 @@ public: extern idVec2 vec2_origin; #define vec2_zero vec2_origin +extern idVec2 vec2_one; ID_INLINE idVec2::idVec2() { @@ -463,6 +464,7 @@ public: extern idVec3 vec3_origin; #define vec3_zero vec3_origin +extern idVec3 vec3_one; ID_INLINE idVec3::idVec3() { @@ -1066,6 +1068,7 @@ public: extern idVec4 vec4_origin; #define vec4_zero vec4_origin +extern idVec4 vec4_one; ID_INLINE void idVec4::Set( const float x, const float y, const float z, const float w ) { diff --git a/neo/idlib/precompiled.h b/neo/idlib/precompiled.h index ba7147fc..41f548af 100644 --- a/neo/idlib/precompiled.h +++ b/neo/idlib/precompiled.h @@ -46,6 +46,9 @@ If you have questions concerning this license or the applicable additional terms // id lib #include "../idlib/Lib.h" +#include "../idlib/gltfProperties.h" +#include "../idlib/gltfParser.h" + #include "sys/sys_filesystem.h" diff --git a/neo/idlib/sys/sys_alloc_tags.h b/neo/idlib/sys/sys_alloc_tags.h index fbec0c68..51426715 100644 --- a/neo/idlib/sys/sys_alloc_tags.h +++ b/neo/idlib/sys/sys_alloc_tags.h @@ -85,6 +85,7 @@ MEM_TAG( IDLIB_SURFACE ) MEM_TAG( IDLIB_WINDING ) MEM_TAG( IDLIB_LEXER ) MEM_TAG( IDLIB_PARSER ) +MEM_TAG( IDLIB_GLTF ) MEM_TAG( AF ) MEM_TAG( COLLISION ) MEM_TAG( COLLISION_QUERY ) diff --git a/neo/renderer/ModelManager.cpp b/neo/renderer/ModelManager.cpp index 2bd24af2..9f2ffdcf 100644 --- a/neo/renderer/ModelManager.cpp +++ b/neo/renderer/ModelManager.cpp @@ -29,7 +29,7 @@ If you have questions concerning this license or the applicable additional terms #include "precompiled.h" #pragma hdrstop - +#include "Model_gltf.h" #include "Model_local.h" #include "RenderCommon.h" // just for R_FreeWorldInteractions and R_CreateWorldInteractions @@ -356,9 +356,15 @@ idRenderModel* idRenderModelManagerLocal::GetModel( const char* _modelName, bool // determine which subclass of idRenderModel to initialize idRenderModel* model = NULL; - + // HvG: GLTF 2 support + if ( (extension.Icmp( GLTF_GLB_EXT ) == 0 ) ||( extension.Icmp( GLTF_EXT ) == 0 )) + { + model = new( TAG_MODEL ) idRenderModelGLTF; + // RB: Collada DAE and Wavefront OBJ - if( ( extension.Icmp( "dae" ) == 0 ) || ( extension.Icmp( "obj" ) == 0 ) || ( extension.Icmp( "ase" ) == 0 ) || ( extension.Icmp( "lwo" ) == 0 ) || ( extension.Icmp( "flt" ) == 0 ) || ( extension.Icmp( "ma" ) == 0 ) ) + }else if ( ( extension.Icmp( "dae" ) == 0 ) || ( extension.Icmp( "obj" ) == 0 ) // RB: Collada DAE and Wavefront OBJ + || ( extension.Icmp( "ase" ) == 0 ) || ( extension.Icmp( "lwo" ) == 0 ) + || ( extension.Icmp( "flt" ) == 0 ) || ( extension.Icmp( "ma" ) == 0 ) ) { model = new( TAG_MODEL ) idRenderModelStatic; } diff --git a/neo/renderer/Model_gltf.cpp b/neo/renderer/Model_gltf.cpp new file mode 100644 index 00000000..3ea0faed --- /dev/null +++ b/neo/renderer/Model_gltf.cpp @@ -0,0 +1,465 @@ + +#include "precompiled.h" +#pragma hdrstop + + +#include "Model_gltf.h" +#include "Model_local.h" + + + +bool idRenderModelStatic::ConvertGltfMeshToModelsurfaces( const gltfMesh *mesh ) { + + //for ( auto mesh : currentAsset->MeshList( ) ) { + // for ( auto prim : mesh->primitives ) { + + return false; +} + +void MapPolygonMesh::ConvertFromMeshGltf( const gltfMesh *_mesh, gltfData *data ) { + for ( auto* gltfMesh : data->MeshList( ) ) { + for ( auto prim : gltfMesh->primitives ) { + common->Printf("primitive for %s\n",gltfMesh->name.c_str() ); + + gltfAccessor *accessor = data->AccessorList( )[prim->indices]; + gltfBufferView *bv = data->BufferViewList( )[accessor->bufferView]; + gltfData *data = bv->parent; + + gltfBuffer *buff = data->BufferList( )[bv->buffer]; + uint idxDataSize = sizeof( uint ) * accessor->count; + uint *indices = ( uint * ) Mem_ClearedAlloc( idxDataSize , TAG_IDLIB_GLTF); + + idFile_Memory idxBin = idFile_Memory( "gltfChunkIndices", + ( const char * ) ( ( data->GetData( bv->buffer ) + bv->byteOffset + accessor->byteOffset ) ), bv->byteLength ); + + for ( int i = 0; i < accessor->count; i++ ) { + idxBin.Read( ( void * ) ( &indices[i] ), accessor->typeSize ); + if ( bv->byteStride ) + idxBin.Seek( bv->byteStride - accessor->typeSize, FS_SEEK_CUR ); + } + + for ( int i = 0; i < accessor->count; i+=3 ) { + MapPolygon &polygon = polygons.Alloc( ); + polygon.SetMaterial( "textures/base_wall/lfwall27d" ); + polygon.AddIndex( indices[i + 1] ); + polygon.AddIndex( indices[i + 2] ); + polygon.AddIndex( indices[i + 0] ); + } + + Mem_Free( indices ); + bool sizeSet = false; + + for ( auto &attrib : prim->attributes ) { + gltfAccessor *attrAcc = data->AccessorList( )[attrib->accessorIndex]; + gltfBufferView *attrBv = data->BufferViewList( )[attrAcc->bufferView]; + gltfData *attrData = attrBv->parent; + gltfBuffer *attrbuff = attrData->BufferList( )[attrBv->buffer]; + + idFile_Memory bin = idFile_Memory( "gltfChunkVertices", + ( const char * ) ( ( attrData->GetData( attrBv->buffer ) + attrBv->byteOffset + attrAcc->byteOffset ) ), attrBv->byteLength ); + + if ( !sizeSet ) + { + verts.AssureSize( attrAcc->count ); + sizeSet = true; + } + + switch ( attrib->type ) { + case gltfMesh_Primitive_Attribute::Type::Position: + { + for ( int i = attrAcc->count-1; i >= 0; i-- ) { + bin.Read( ( void * ) ( &verts[i].xyz.x ), attrAcc->typeSize ); + bin.Read( ( void * ) ( &verts[i].xyz.y ), attrAcc->typeSize ); + bin.Read( ( void * ) ( &verts[i].xyz.z ), attrAcc->typeSize ); + if ( attrBv->byteStride ) + bin.Seek( attrBv->byteStride - ( 3 * attrAcc->typeSize ), FS_SEEK_CUR ); + + idRandom rnd( i ); + int r = rnd.RandomInt( 255 ), g = rnd.RandomInt( 255 ), b = rnd.RandomInt( 255 ); + + //vtxData[i].abgr = 0xff000000 + ( b << 16 ) + ( g << 8 ) + r; + } + + break; + } + case gltfMesh_Primitive_Attribute::Type::Normal: + { + idVec3 vec; + for ( int i = 0; i < attrAcc->count; i++ ) { + idVec3 vec; + bin.Read( ( void * ) ( &vec.x ), attrAcc->typeSize ); + bin.Read( ( void * ) ( &vec.y ), attrAcc->typeSize ); + bin.Read( ( void * ) ( &vec.z ), attrAcc->typeSize ); + if ( attrBv->byteStride ) + bin.Seek( attrBv->byteStride - ( attrib->elementSize * attrAcc->typeSize ), FS_SEEK_CUR ); + verts[i].SetNormal(vec); + } + + break; + } + case gltfMesh_Primitive_Attribute::Type::TexCoord0: + { + idVec2 vec; + for ( int i = 0; i < attrAcc->count; i++ ) { + bin.Read( ( void * ) ( &vec.x ), attrAcc->typeSize ); + bin.Read( ( void * ) ( &vec.y ), attrAcc->typeSize ); + if ( attrBv->byteStride ) + bin.Seek( attrBv->byteStride - ( attrib->elementSize * attrAcc->typeSize ), FS_SEEK_CUR ); + verts[i].SetTexCoord(vec); + } + + break; + } + case gltfMesh_Primitive_Attribute::Type::Tangent: + { + idVec4 vec; + for ( int i = 0; i < attrAcc->count; i++ ) { + bin.Read( ( void * ) ( &vec.x ), attrAcc->typeSize ); + bin.Read( ( void * ) ( &vec.y ), attrAcc->typeSize ); + bin.Read( ( void * ) ( &vec.z ), attrAcc->typeSize ); + bin.Read( ( void * ) ( &vec.w ), attrAcc->typeSize ); + if ( attrBv->byteStride ) + bin.Seek( attrBv->byteStride - ( attrib->elementSize * attrAcc->typeSize ), FS_SEEK_CUR ); + verts[i].SetTangent(vec.ToVec3()); + verts[i].SetBiTangentSign(vec.w); + } + break; + } + //case gltfMesh_Primitive_Attribute::Type::Weight: + //{ + // for ( int i = 0; i < attrAcc->count; i++ ) { + // bin.Read( ( void * ) ( &vtxData[i].weight.x ), attrAcc->typeSize ); + // bin.Read( ( void * ) ( &vtxData[i].weight.y ), attrAcc->typeSize ); + // bin.Read( ( void * ) ( &vtxData[i].weight.z ), attrAcc->typeSize ); + // bin.Read( ( void * ) ( &vtxData[i].weight.w ), attrAcc->typeSize ); + // if ( attrBv->byteStride ) + // bin.Seek( attrBv->byteStride - ( attrib->elementSize * attrAcc->typeSize ), FS_SEEK_CUR ); + // } + // break; + //} + //case gltfMesh_Primitive_Attribute::Type::Indices: + //{ + // for ( int i = 0; i < attrAcc->count; i++ ) { + // bin.Read( ( void * ) ( &vtxData[i].boneIndex.x ), attrAcc->typeSize ); + // bin.Read( ( void * ) ( &vtxData[i].boneIndex.y ), attrAcc->typeSize ); + // bin.Read( ( void * ) ( &vtxData[i].boneIndex.z ), attrAcc->typeSize ); + // bin.Read( ( void * ) ( &vtxData[i].boneIndex.w ), attrAcc->typeSize ); + // if ( attrBv->byteStride ) + // bin.Seek( attrBv->byteStride - ( attrib->elementSize * attrAcc->typeSize ), FS_SEEK_CUR ); + // } + // break; + //} + } + + } + } + } + SetContents(); +} + +int idMapEntity::GetEntities( gltfData * data, EntityListRef entities, int sceneID ) { + int entityCount = 0; + for ( auto & nodeID : data->SceneList()[sceneID]->nodes ) + { + auto * node = data->NodeList()[nodeID]; + auto * newEntity = new idMapEntity( ); + + bool isWorldSpawn = false; + + //set name and retrieve epairs from node extras + if ( node->name.Length() ) + newEntity->epairs.Set( "name", node->name); + newEntity->epairs.Copy(node->extras.strPairs); + + isWorldSpawn = idStr::Icmp(newEntity->epairs.GetString( "classname" ), "worldspawn" ) == 0; + + if ( isWorldSpawn ) + newEntity->primitives.Resize( 1024, 256 ); + + //check for primitive type + idStr primType = newEntity->epairs.GetString( "PrimitiveType" ); + if ( primType.Length() ) + { + if ( primType.Icmp( "BrushDef3" ) == 0 ) // change to MapMesh/Primitive + { + MapPolygonMesh *meshPrim = new MapPolygonMesh( ); + meshPrim->epairs.Copy( newEntity->epairs ); + + meshPrim->ConvertFromMeshGltf(data->MeshList()[node->mesh],data); + newEntity->AddPrimitive(meshPrim); + } + } + data->ResolveNodeMatrix(node); + newEntity->epairs.Set( "origin", node->translation.ToString()); + common->Printf(" %s \n ", node->name.c_str( ) ); + entities.Append(newEntity); + + entityCount++; + } + + if ( entities.Num( ) > 0 && ( idStr::Icmp( entities[0]->epairs.GetString( "name" ), "worldspawn" ) != 0 ) ) { + // move world spawn to first place + for ( int i = 1; i < entities.Num( ); i++ ) { + if ( idStr::Icmp( entities[i]->epairs.GetString( "name" ), "worldspawn" ) == 0 ) { + idMapEntity *tmp = entities[0]; + entities[0] = entities[i]; + entities[i] = tmp; + break; + } + } + } + + return entityCount; +} + + +// [filename].[%i|%s].[gltf/glb] +bool gltfManager::ExtractMeshIdentifier( idStr &filename, int &meshId, idStr &meshName ) { + + idStr extension; + filename.ExtractFileExtension(extension); + + idStr idPart = filename.Left(filename.Length()-extension.Length()-1); + idStr id; + idPart.ExtractFileExtension(id); + + if ( !id.Length() ) + { + idLib::Warning( "no gltf mesh identifier"); + return false; + } + + filename = idPart.Left(idPart.Length()-id.Length()) + extension; + + idLexer lexer( LEXFL_ALLOWMULTICHARLITERALS | LEXFL_NOSTRINGESCAPECHARS ); + lexer.LoadMemory( id.c_str( ), id.Size( ), "GltfmeshID", 0 ); + + idToken token; + if (lexer.ExpectAnyToken(&token)) + { + if (lexer.EndOfFile() && (token.type == TT_NUMBER) && (token.subtype & TT_INTEGER) ) + meshId = token.GetIntValue(); + else if (token.type == TT_NUMBER || token.type == TT_STRING ) + meshName = id; + else { + lexer.Warning("malformed gltf mesh identifier" ); + return false; + } + return true; + }else + lexer.Warning("malformed gltf mesh identifier" ); + + return false; +} + +void idRenderModelGLTF::InitFromFile( const char *fileName ) { + common->Warning( "The method or operation is not implemented." ); + +} + +bool idRenderModelGLTF::LoadBinaryModel( idFile *file, const ID_TIME_T sourceTimeStamp ) { + common->Warning( "The method or operation is not implemented." ); + return false; +} + +void idRenderModelGLTF::WriteBinaryModel( idFile *file, ID_TIME_T *_timeStamp /*= NULL */ ) const { + common->Warning( "The method or operation is not implemented." ); +} + +bool idRenderModelGLTF::SupportsBinaryModel( ) { + common->Warning( "The method or operation is not implemented." ); + return false; +} + +void idRenderModelGLTF::ExportOBJ( idFile *objFile, idFile *mtlFile, ID_TIME_T *_timeStamp /*= NULL */ ) { + common->Warning( "The method or operation is not implemented." ); +} + +void idRenderModelGLTF::PartialInitFromFile( const char *fileName ) { + common->Warning( "The method or operation is not implemented." ); +} + +void idRenderModelGLTF::PurgeModel( ) { + common->Warning( "The method or operation is not implemented." ); +} + +void idRenderModelGLTF::Reset( ) { + common->Warning( "The method or operation is not implemented." ); +} + +void idRenderModelGLTF::LoadModel( ) { + common->Warning( "The method or operation is not implemented." ); +} + +bool idRenderModelGLTF::IsLoaded( ) { + common->Warning( "The method or operation is not implemented." ); + return false; +} + +void idRenderModelGLTF::SetLevelLoadReferenced( bool referenced ) { + common->Warning( "The method or operation is not implemented." ); +} + +bool idRenderModelGLTF::IsLevelLoadReferenced( ) { + common->Warning( "The method or operation is not implemented." ); + return false; +} + +void idRenderModelGLTF::TouchData( ) { + common->Warning( "The method or operation is not implemented." ); +} + +void idRenderModelGLTF::CreateBuffers( nvrhi::ICommandList *commandList ) { + common->Warning( "The method or operation is not implemented." ); +} + +void idRenderModelGLTF::InitEmpty( const char *name ) { + common->Warning( "The method or operation is not implemented." ); +} + +void idRenderModelGLTF::AddSurface( modelSurface_t surface ) { + common->Warning( "The method or operation is not implemented." ); +} + +void idRenderModelGLTF::FinishSurfaces( bool useMikktspace ) { + common->Warning( "The method or operation is not implemented." ); +} + +void idRenderModelGLTF::FreeVertexCache( ) { + common->Warning( "The method or operation is not implemented." ); +} + +const char *idRenderModelGLTF::Name( ) const { + common->Warning( "The method or operation is not implemented." ); + return ""; +} + +void idRenderModelGLTF::Print( ) const { + common->Warning( "The method or operation is not implemented." ); +} + +void idRenderModelGLTF::List( ) const { + common->Warning( "The method or operation is not implemented." ); +} + +int idRenderModelGLTF::Memory( ) const { + common->Warning( "The method or operation is not implemented." ); + return -1; +} + +ID_TIME_T idRenderModelGLTF::Timestamp( ) const { + common->Warning( "The method or operation is not implemented." ); + return FILE_NOT_FOUND_TIMESTAMP; +} + +int idRenderModelGLTF::NumSurfaces( ) const { + common->Warning( "The method or operation is not implemented." ); + return -1; +} + +int idRenderModelGLTF::NumBaseSurfaces( ) const { + common->Warning( "The method or operation is not implemented." ); + return -1; +} + +const modelSurface_t *idRenderModelGLTF::Surface( int surfaceNum ) const { + common->Warning( "The method or operation is not implemented." ); + return nullptr; +} + +srfTriangles_t *idRenderModelGLTF::AllocSurfaceTriangles( int numVerts, int numIndexes ) const { + common->Warning( "The method or operation is not implemented." ); + return nullptr; +} + +void idRenderModelGLTF::FreeSurfaceTriangles( srfTriangles_t *tris ) const { + common->Warning( "The method or operation is not implemented." ); +} + +bool idRenderModelGLTF::IsStaticWorldModel( ) const { + common->Warning( "The method or operation is not implemented." ); + return false; +} + +dynamicModel_t idRenderModelGLTF::IsDynamicModel( ) const { + common->Warning( "The method or operation is not implemented." ); + return dynamicModel_t(); +} + +bool idRenderModelGLTF::IsDefaultModel( ) const { + common->Warning( "The method or operation is not implemented." ); + return false; +} + +bool idRenderModelGLTF::IsReloadable( ) const { + common->Warning( "The method or operation is not implemented." ); + return false; +} + +idRenderModel *idRenderModelGLTF::InstantiateDynamicModel( const struct renderEntity_s *ent, const viewDef_t *view, idRenderModel *cachedModel ) { + common->Warning( "The method or operation is not implemented." ); + return nullptr; +} + +int idRenderModelGLTF::NumJoints( ) const { + common->Warning( "The method or operation is not implemented." ); + return 0; +} + +const idMD5Joint *idRenderModelGLTF::GetJoints( ) const { + common->Warning( "The method or operation is not implemented." ); + return nullptr; +} + +jointHandle_t idRenderModelGLTF::GetJointHandle( const char *name ) const { + common->Warning( "The method or operation is not implemented." ); + return jointHandle_t(); +} + +const char *idRenderModelGLTF::GetJointName( jointHandle_t handle ) const { + common->Warning( "The method or operation is not implemented." ); + return ""; +} + +const idJointQuat *idRenderModelGLTF::GetDefaultPose( ) const { + common->Warning( "The method or operation is not implemented." ); + return nullptr; +} + +int idRenderModelGLTF::NearestJoint( int surfaceNum, int a, int b, int c ) const { + common->Warning( "The method or operation is not implemented." ); + return -1; +} + +idBounds idRenderModelGLTF::Bounds( const struct renderEntity_s *ent ) const { + common->Warning( "The method or operation is not implemented." ); + return idBounds(); +} + +void idRenderModelGLTF::ReadFromDemoFile( class idDemoFile *f ) { + common->Warning( "The method or operation is not implemented." ); +} + +void idRenderModelGLTF::WriteToDemoFile( class idDemoFile *f ) { + common->Warning( "The method or operation is not implemented." ); +} + +float idRenderModelGLTF::DepthHack( ) const { + common->Warning( "The method or operation is not implemented." ); + return -1.0f; +} + +bool idRenderModelGLTF::ModelHasDrawingSurfaces( ) const { + common->Warning( "The method or operation is not implemented." ); + return false; +} + +bool idRenderModelGLTF::ModelHasInteractingSurfaces( ) const { + common->Warning( "The method or operation is not implemented." ); + return false; +} + +bool idRenderModelGLTF::ModelHasShadowCastingSurfaces( ) const { + common->Warning( "The method or operation is not implemented." ); + return false; +} diff --git a/neo/renderer/Model_gltf.h b/neo/renderer/Model_gltf.h new file mode 100644 index 00000000..25fdda89 --- /dev/null +++ b/neo/renderer/Model_gltf.h @@ -0,0 +1,68 @@ +#pragma once +#include "Model_local.h" + +class gltfManager { +public: + static bool ExtractMeshIdentifier(idStr& filename , int & meshId, idStr & meshName ); +}; + +class idGltfMesh +{ +public: + idGltfMesh(gltfMesh * _mesh, gltfData * _data) : mesh(_mesh),data(_data){}; + +private: + gltfMesh * mesh; + gltfData * data; +}; + +class idRenderModelGLTF : public idRenderModelStatic +{ +public: + void InitFromFile( const char *fileName ) override; + bool LoadBinaryModel( idFile *file, const ID_TIME_T sourceTimeStamp ) override; + void WriteBinaryModel( idFile *file, ID_TIME_T *_timeStamp = NULL ) const override; + bool SupportsBinaryModel( ) override; + void ExportOBJ( idFile *objFile, idFile *mtlFile, ID_TIME_T *_timeStamp = NULL ) override; + void PartialInitFromFile( const char *fileName ) override; + void PurgeModel( ) override; + void Reset( ) override; + void LoadModel( ) override; + bool IsLoaded( ) override; + void SetLevelLoadReferenced( bool referenced ) override; + bool IsLevelLoadReferenced( ) override; + void TouchData( ) override; + void CreateBuffers( nvrhi::ICommandList *commandList ) override; + void InitEmpty( const char *name ) override; + void AddSurface( modelSurface_t surface ) override; + void FinishSurfaces( bool useMikktspace ) override; + void FreeVertexCache( ) override; + const char *Name( ) const override; + void Print( ) const override; + void List( ) const override; + int Memory( ) const override; + ID_TIME_T Timestamp( ) const override; + int NumSurfaces( ) const override; + int NumBaseSurfaces( ) const override; + const modelSurface_t *Surface( int surfaceNum ) const override; + srfTriangles_t *AllocSurfaceTriangles( int numVerts, int numIndexes ) const override; + void FreeSurfaceTriangles( srfTriangles_t *tris ) const override; + bool IsStaticWorldModel( ) const override; + dynamicModel_t IsDynamicModel( ) const override; + bool IsDefaultModel( ) const override; + bool IsReloadable( ) const override; + idRenderModel *InstantiateDynamicModel( const struct renderEntity_s *ent, const viewDef_t *view, idRenderModel *cachedModel ) override; + int NumJoints( ) const override; + const idMD5Joint *GetJoints( ) const override; + jointHandle_t GetJointHandle( const char *name ) const override; + const char *GetJointName( jointHandle_t handle ) const override; + const idJointQuat *GetDefaultPose( ) const override; + int NearestJoint( int surfaceNum, int a, int b, int c ) const override; + idBounds Bounds( const struct renderEntity_s *ent ) const override; + void ReadFromDemoFile( class idDemoFile *f ) override; + void WriteToDemoFile( class idDemoFile *f ) override; + float DepthHack( ) const override; + bool ModelHasDrawingSurfaces( ) const override; + bool ModelHasInteractingSurfaces( ) const override; + bool ModelHasShadowCastingSurfaces( ) const override; +}; \ No newline at end of file