LCOV - code coverage report
Current view: top level - engine/model - gltfloader.cpp (source / functions) Coverage Total Hit
Test: Libprimis Test Coverage Lines: 93.1 % 494 460
Test Date: 2026-10-21 06:30:36 Functions: 100.0 % 21 21

            Line data    Source code
       1              : /**
       2              :  * @file gltfloader.cpp
       3              :  * @brief GLTF 2.0 loading functionality
       4              :  *
       5              :  * This file handles the loading of GLTF 2.0 files, converting them into data
       6              :  * structures readable by the program.
       7              :  *
       8              :  * The various get() functions *generate* the output vectors of arrays on demand;
       9              :  * they are not stored inside the object.
      10              :  *
      11              :  * This file, and gltfloader.h along with it, are explicitly designed not to rely
      12              :  * on the dependencies of the rest of the engine. It should be possible to compile
      13              :  * this file without the build system of the engine at large.
      14              :  */
      15              : #include <cstdlib>
      16              : #include <fstream>
      17              : #include <iostream>
      18              : #include <vector>
      19              : #include <string>
      20              : #include <array>
      21              : #include <optional>
      22              : #include <cstdint>
      23              : 
      24              : typedef unsigned int uint;
      25              : typedef unsigned short ushort;
      26              : 
      27              : #include "gltfloader.h"
      28              : 
      29              : //for GL types (GL_FLOAT, GL_UNSIGNED_INT, etc.)
      30              : #include <GL/gl.h>
      31              : 
      32              : //these values should be loaded by gl.h, fallback if not defined
      33              : #ifndef GL_UNSIGNED_INT
      34              :     #define GL_UNSIGNED_INT 5125
      35              : #endif
      36              : #ifndef GL_UNSIGNED_SHORT
      37              :     #define GL_UNSIGNED_SHORT 5123
      38              : #endif
      39              : #ifndef GL_UNSIGNED_BYTE
      40              :     #define GL_UNSIGNED_SHORT 5121
      41              : #endif
      42              : #ifndef GL_FLOAT
      43              :     #define GL_FLOAT 5126
      44              : #endif
      45              : 
      46              : //populates the object vectors with the data in the gltf file
      47           21 : GLTFModelInfo::GLTFModelInfo(std::string_view path, bool messages) : messages(messages)
      48              : {
      49           21 :     std::ifstream infile;
      50           21 :     infile.exceptions(std::ifstream::failbit);
      51           21 :     infile.open(path.data());
      52           20 :     std::vector<std::string> output;
      53              : 
      54           20 :     findnodes(path);
      55           18 :     findmeshes(path);
      56           18 :     findaccessors(path);
      57           18 :     findbufferviews(path);
      58           18 :     findbuffers(path);
      59           18 :     findanimations(path);
      60           41 : }
      61              : 
      62              : //NodeType_Mesh will return all nodes which contain meshes
      63           19 : std::vector<std::string> GLTFModelInfo::getnodenames(int type) const
      64              : {
      65           19 :     std::vector<std::string> nodenames;
      66           71 :     for(const Node &n : nodes)
      67              :     {
      68           52 :         switch(type)
      69              :         {
      70            4 :             case NodeType_All:
      71              :             {
      72            4 :                 nodenames.push_back(n.name);
      73            4 :                 break;
      74              :             }
      75           48 :             case NodeType_Mesh:
      76              :             {
      77           48 :                 if(n.mesh)
      78              :                 {
      79           21 :                     nodenames.push_back(n.name);
      80              :                 }
      81           48 :                 break;
      82              :             }
      83              :         }
      84              :     }
      85           19 :     return nodenames;
      86            0 : }
      87              : 
      88              : //getter functions generate vectors of arrays of the appropriate type
      89              : //given the node name
      90            8 : std::vector<std::array<float, 3>> GLTFModelInfo::getpositions(std::string_view name) const
      91              : {
      92            8 :     std::vector<std::array<float, 3>> positions;
      93           13 :     for(const Node &n : nodes)
      94              :     {
      95           13 :         if(n.name == name && n.mesh)
      96              :         {
      97            8 :             const Mesh &m = meshes[n.mesh.value()];
      98            8 :             if(!m.positions) //bail out if optional is nullopt
      99              :             {
     100            8 :                 return positions;
     101              :             }
     102            8 :             const Accessor &a = accessors[m.positions.value()];
     103            8 :             const BufferView &bv = bufferviews[a.bufferview];
     104            8 :             if(a.componenttype == GL_FLOAT)
     105              :             {
     106            8 :                 std::vector<float> floatbuf = gettypeblock<float>(bv.buffer, bv.bytelength, bv.byteoffset);
     107            8 :                 if(n.translation)
     108              :                 {
     109           25 :                     for(size_t i = 0; i < floatbuf.size(); i+=3)
     110              :                     {
     111           24 :                         floatbuf[i] += n.translation.value()[0];
     112           24 :                         floatbuf[i+1] += n.translation.value()[1];
     113           24 :                         floatbuf[i+2] += n.translation.value()[2];
     114              :                     }
     115              :                 }
     116            8 :                 positions = fillvector<float, float, 3>(floatbuf);
     117            8 :             }
     118              :             else
     119              :             {
     120            0 :                 std::printf("invalid component type %u\n (want: %u)", a.componenttype, GL_FLOAT);
     121            0 :                 throw std::logic_error("invalid vertex position component type");
     122              :             }
     123            8 :             return positions;
     124              :         }
     125              :     }
     126            0 :     return positions; //empty fallback
     127            0 : }
     128              : 
     129            6 : std::vector<std::array<float, 3>> GLTFModelInfo::getnormals(std::string_view name) const
     130              : {
     131            6 :     std::vector<std::array<float, 3>> normals;
     132            6 :     for(const Mesh &m : meshes)
     133              :     {
     134            6 :         if(m.name == name && m.normals)
     135              :         {
     136            6 :             const Accessor &a = accessors[m.normals.value()];
     137            6 :             const BufferView &bv = bufferviews[a.bufferview];
     138            6 :             if(a.componenttype == GL_FLOAT)
     139              :             {
     140            6 :                 std::vector<float> floatbuf = gettypeblock<float>(bv.buffer, bv.bytelength, bv.byteoffset);
     141            6 :                 normals = fillvector<float, float, 3>(floatbuf);
     142            6 :             }
     143              :             else
     144              :             {
     145            0 :                 std::printf("invalid component type %u\n (want: %u)", a.componenttype, GL_FLOAT);
     146            0 :                 throw std::logic_error("invalid vertex normal component type");
     147              :             }
     148            6 :             return normals;
     149              :         }
     150              :     }
     151            0 :     return normals; //empty fallback
     152            0 : }
     153              : 
     154            6 : std::vector<std::array<float, 2>> GLTFModelInfo::gettexcoords(std::string_view name) const
     155              : {
     156            6 :     std::vector<std::array<float, 2>> texcoords;
     157            6 :     for(const Mesh &m : meshes)
     158              :     {
     159            6 :         if(m.name == name && m.texcoords)
     160              :         {
     161            6 :             const Accessor &a = accessors[m.texcoords.value()];
     162            6 :             const BufferView &bv = bufferviews[a.bufferview];
     163            6 :             if(a.componenttype == GL_FLOAT)
     164              :             {
     165            6 :                 std::vector<float> floatbuf = gettypeblock<float>(bv.buffer, bv.bytelength, bv.byteoffset);
     166            6 :                 texcoords = fillvector<float, float, 2>(floatbuf);
     167            6 :             }
     168              :             else
     169              :             {
     170            0 :                 std::printf("invalid component type %u\n (want: %u)", a.componenttype, GL_FLOAT);
     171            0 :                 throw std::logic_error("invalid vertex texture coordinate component type");
     172              :             }
     173            6 :             return texcoords;
     174              :         }
     175              :     }
     176            0 :     return texcoords; //empty fallback
     177            0 : }
     178              : 
     179            7 : std::vector<std::array<uint, 4>> GLTFModelInfo::getjoints(std::string_view name) const
     180              : {
     181            7 :     std::vector<std::array<uint, 4>> joints;
     182           12 :     for(const Mesh &m : meshes)
     183              :     {
     184            7 :         if(m.name == name && m.joints)
     185              :         {
     186            2 :             const Accessor &a = accessors[m.joints.value()];
     187            2 :             const BufferView &bv = bufferviews[a.bufferview];
     188            2 :             if(a.componenttype == GL_UNSIGNED_BYTE)
     189              :             {
     190            2 :                 std::vector<uint8_t> scalarbuf = gettypeblock<uint8_t>(bv.buffer, bv.bytelength, bv.byteoffset);
     191            2 :                 joints = fillvector<uint, uint8_t, 4>(scalarbuf);
     192            2 :             }
     193            0 :             else if(a.componenttype == GL_UNSIGNED_SHORT)
     194              :             {
     195            0 :                 std::vector<ushort> scalarbuf = gettypeblock<ushort>(bv.buffer, bv.bytelength, bv.byteoffset);
     196            0 :                 joints = fillvector<uint, ushort, 4>(scalarbuf);
     197            0 :             }
     198              :             else
     199              :             {
     200            0 :                 std::printf("invalid component type %u\n (want: %u %u)", a.componenttype, GL_UNSIGNED_BYTE, GL_UNSIGNED_SHORT);
     201            0 :                 throw std::logic_error("invalid vertex joint component type");
     202              :             }
     203            2 :             return joints;
     204              :         }
     205              :     }
     206            5 :     return joints; //empty fallback
     207            0 : }
     208              : 
     209            7 : std::vector<std::array<float, 4>> GLTFModelInfo::getweights(std::string_view name) const
     210              : {
     211            7 :     std::vector<std::array<float, 4>> weights;
     212           12 :     for(const Mesh &m : meshes)
     213              :     {
     214            7 :         if(m.name == name && m.weights)
     215              :         {
     216            2 :             const Accessor &a = accessors[m.weights.value()];
     217            2 :             const BufferView &bv = bufferviews[a.bufferview];
     218            2 :             if(a.componenttype == GL_FLOAT)
     219              :             {
     220            2 :                 std::vector<float> floatbuf = gettypeblock<float>(bv.buffer, bv.bytelength, bv.byteoffset);
     221            2 :                 weights = fillvector<float, float, 4>(floatbuf);
     222            2 :             }
     223              :             else
     224              :             {
     225            0 :                 std::printf("invalid component type %u\n (want: %u)", a.componenttype, GL_FLOAT);
     226            0 :                 throw std::logic_error("invalid vertex weight component type");
     227              :             }
     228            2 :             return weights;
     229              :         }
     230              :     }
     231            5 :     return weights; //empty fallback
     232            0 : }
     233              : 
     234            7 : std::vector<std::array<uint, 3>> GLTFModelInfo::getindices(std::string_view name) const
     235              : {
     236            7 :     std::vector<std::array<uint, 3>> indices;
     237            7 :     for(const Mesh &m : meshes)
     238              :     {
     239            7 :         if(m.name == name && m.indices)
     240              :         {
     241            7 :             const Accessor &a = accessors[m.indices.value()];
     242            7 :             const BufferView &bv = bufferviews[a.bufferview];
     243            7 :             if(a.componenttype == GL_UNSIGNED_SHORT)
     244              :             {
     245            7 :                 std::vector<ushort> scalarbuf = gettypeblock<ushort>(bv.buffer, bv.bytelength, bv.byteoffset);
     246            7 :                 indices = fillvector<uint, ushort, 3>(scalarbuf);
     247            7 :             }
     248            0 :             else if(a.componenttype == GL_UNSIGNED_INT)
     249              :             {
     250            0 :                 std::vector<uint> uintbuf = gettypeblock<uint>(bv.buffer, bv.bytelength, bv.byteoffset);
     251            0 :                 indices = fillvector<uint, uint, 3>(uintbuf);
     252            0 :             }
     253              :             else
     254              :             {
     255            0 :                 std::printf("invalid component type %u\n (want: %u %u)", a.componenttype, GL_UNSIGNED_INT, GL_UNSIGNED_SHORT);
     256            0 :                 throw std::logic_error("invalid vertex index component type");
     257              :             }
     258              :             //check that all indices are <= size of that mesh's texture coordinate list
     259          193 :             for(std::array<uint,3> i : indices)
     260              :             {
     261          746 :                 for(size_t j = 0; j < 3; ++j)
     262              :                 {
     263          560 :                     if(i[j] >= accessors[m.positions.value()].count)
     264              :                     {
     265            1 :                         throw std::logic_error("invalid texture index");
     266              :                     }
     267              :                 }
     268              :             }
     269            6 :             return indices;
     270              :         }
     271              :     }
     272            0 :     return indices; //empty fallback
     273            1 : }
     274              : 
     275            3 : bool GLTFModelInfo::operator==(const GLTFModelInfo &m) const
     276              : {
     277            3 :     std::vector<std::string> names = getnodenames(NodeType_Mesh);
     278            3 :     std::vector<std::string> mnames = m.getnodenames(NodeType_Mesh);
     279            3 :     if(names != mnames)
     280              :     {
     281            1 :         return false;
     282              :     }
     283            4 :     for(const std::string &s : names)
     284              :     {
     285            6 :         if( !( getpositions(s) == m.getpositions(s)
     286            4 :             && getnormals(s) == m.getnormals(s)
     287            4 :             && gettexcoords(s) == m.gettexcoords(s)
     288            4 :             && getjoints(s) == m.getjoints(s)
     289            4 :             && getweights(s) == m.getweights(s)
     290            4 :             && getindices(s) == m.getindices(s)))
     291              :         {
     292            0 :             return false;
     293              :         }
     294              :     }
     295            2 :     return true;
     296            3 : }
     297              : 
     298              : ////////////////////////////////////////
     299              : //private methods
     300              : ////////////////////////////////////////
     301              : 
     302          110 : std::vector<std::string> GLTFModelInfo::loadjsonfile(std::string_view name)
     303              : {
     304          110 :     std::string wholefile;
     305          110 :     std::string line;
     306          110 :     std::ifstream infile;
     307          110 :     infile.open(name.data());
     308          110 :     std::vector<std::string> output;
     309              : 
     310          110 :     if(!infile.good())
     311              :     {
     312            0 :         perror("Error opening GLTF file");
     313            0 :         return output;//empty vector
     314              :     }
     315        27040 :     while(getline(infile, line))
     316              :     {
     317        26930 :         bool stringliteral = false;
     318              :         //strip all whitespace not inside string literals
     319       580248 :         for(size_t i = 0; i < line.size(); ++i)
     320              :         {
     321       553318 :             if((i == 0 && line[i] == '"') || (line[i] == '"' && line[i-1] != '\\'))
     322              :             {
     323        35460 :                 stringliteral = !stringliteral;
     324              :             }
     325       553318 :             if(!stringliteral)
     326              :             {
     327       402838 :                 if(line[i] == ' ' || line[i] == '\t')
     328              :                 {
     329       310110 :                     line.erase(i, 1);
     330       310110 :                     i--;
     331              :                 }
     332              :             }
     333              :         }
     334        26930 :         stringliteral = false;
     335        26930 :         wholefile.append(line);
     336              :     }
     337              :     //line break after every { } ] ,
     338              : 
     339              :     //helper lambda
     340        27510 :     auto pushstring = [] (size_t depth, std::vector<std::string> &out, const std::string &wholefile, size_t lastbreak, size_t cur)
     341              :     {
     342        27510 :         std::string line(depth, ' ');
     343        27510 :         line.append(wholefile.substr(lastbreak, cur - lastbreak));
     344        27510 :         out.push_back(line);
     345        27510 :     };
     346          110 :     size_t lastbreak = 0,
     347          110 :            bracketdepth = 0;
     348       243242 :     for(size_t i = 0; i < wholefile.size(); ++i)
     349              :     {
     350       243133 :         if(wholefile[i] == ']' || wholefile[i] == '}')
     351              :         {
     352         6558 :             if(bracketdepth == 0)
     353              :             {
     354            1 :                 throw std::logic_error("GLTF loader error: too many trailing } or ]");
     355              :             }
     356         6557 :             pushstring(bracketdepth, output, wholefile, lastbreak, i);
     357         6557 :             bracketdepth--;
     358         6557 :             lastbreak = i;
     359              :         }
     360       236575 :         else if(wholefile[i] =='{')
     361              :         {
     362         4555 :             pushstring(bracketdepth, output, wholefile, lastbreak, i+1);
     363         4555 :             bracketdepth++;
     364         4555 :             lastbreak = i+1;
     365              :         }
     366       232020 :         else if(wholefile[i] =='[')
     367              :         {
     368         2034 :             pushstring(bracketdepth, output, wholefile, lastbreak, i+1);
     369         2034 :             bracketdepth++;
     370         2034 :             lastbreak = i+1;
     371              :         }
     372       229986 :         else if(wholefile[i] ==',')
     373              :         {
     374        14364 :             pushstring(bracketdepth, output, wholefile, lastbreak, i+1);
     375        14364 :             lastbreak = i+1;
     376              :         }
     377              :     }
     378          109 :     if(bracketdepth != 0)
     379              :     {
     380            1 :         throw std::logic_error("GLTF loader error: too few trailing } or ]");
     381              :     }
     382              : 
     383          108 :     infile.close();
     384          108 :     return output;
     385          116 : }
     386              : 
     387              : //helper for find<object> functions
     388          110 : std::vector<std::string> GLTFModelInfo::getblockbyname(std::string_view path, std::string_view blockname, size_t maxdepth)
     389              : {
     390          110 :     std::vector<std::string> file = loadjsonfile(path);
     391          108 :     size_t blockstart = 0;
     392        13593 :     for(size_t i = 0; i < file.size(); ++i)
     393              :     {
     394        13584 :         size_t itr = file[i].find(blockname);
     395        13584 :         if(maxdepth)
     396              :         {
     397          273 :             if(itr <= maxdepth)
     398              :             {
     399           18 :                 blockstart = i;
     400           18 :                 break;
     401              :             }
     402              :         }
     403        13311 :         else if(itr != std::string::npos)
     404              :         {
     405           81 :             blockstart = i;
     406           81 :             break;
     407              :         }
     408              :     }
     409          108 :     std::vector<std::string> block = getblock(file, blockstart);
     410          216 :     return block;
     411          108 : }
     412              : 
     413              : //helper to sanitize things dropped by sscanf
     414              : //removes ", from strings, part of json syntax
     415          204 : void GLTFModelInfo::cleanstring(std::string &s)
     416              : {
     417         2229 :     for(size_t i = 0; i < s.size(); ++i)
     418              :     {
     419         2025 :         if(s[i] == '\"' || s[i] == ',')
     420              :         {
     421          501 :             s.erase(i, 1);
     422          501 :             i--;
     423              :         }
     424              :     }
     425          204 : }
     426              : 
     427              : //returns number of nodes
     428           20 : size_t GLTFModelInfo::findnodes(std::string_view path)
     429              : {
     430           20 :     nodes.clear();
     431           20 :     std::vector<std::string> nodeblock = getblockbyname(path, "\"nodes\"", 1); //get only "node" at indent depth 1
     432           18 :     size_t numnodes = 0;
     433              :     //get indices by parsing sub-blocks
     434          114 :     for(size_t i = 0; i < nodeblock.size(); ++i)
     435              :     {
     436           96 :         std::vector<std::string> block = getblock(nodeblock, i);
     437           96 :         if(!block.size())
     438              :         {
     439           48 :             continue;
     440              :         }
     441           96 :         Node n{"", std::nullopt, std::nullopt, std::nullopt, {}};
     442          330 :         for(size_t j = 0; j < block.size(); ++j)
     443              :         {
     444          282 :             if(block[j].find(" \"name\":") != std::string::npos)
     445              :             {
     446              :                 std::array<char, 256> s;
     447           48 :                 s.fill(0);
     448           48 :                 std::sscanf(block[j].c_str(), " \"name\":%s", s.data());
     449           48 :                 n.name = s.data();
     450           48 :                 cleanstring(n.name);
     451              :             }
     452          234 :             else if(block[j].find("\"mesh\"") != std::string::npos)
     453              :             {
     454           21 :                 uint mesh = 0;
     455           21 :                 std::sscanf( block[j].c_str(), " \"mesh\":%u", &mesh);
     456           21 :                 n.mesh = mesh;
     457              :             }
     458          213 :             else if(block[j].find("\"translation\"") != std::string::npos)
     459              :             {
     460           21 :                 std::array<float, 3> translation = {0,0,0};
     461           21 :                 std::vector<std::string> translationblock = getblock(block, j);
     462           84 :                 for(size_t k = 0; k < translationblock.size(); ++k)
     463              :                 {
     464           63 :                     std::sscanf( translationblock[k].c_str(), " %f", &translation[k]);
     465              :                 }
     466           21 :                 n.translation = translation;
     467           21 :             }
     468          192 :             else if(block[j].find("\"children\"") != std::string::npos)
     469              :             {
     470           18 :                 std::vector<size_t> children;
     471           18 :                 std::vector<std::string> translationblock = getblock(block, j);
     472           45 :                 for(size_t k = 0; k < translationblock.size(); ++k)
     473              :                 {
     474           27 :                     size_t child = 0;
     475           27 :                     std::sscanf( translationblock[k].c_str(), " %lu", &child);
     476           27 :                     children.push_back(child);
     477              :                 }
     478           18 :                 n.children = children;
     479           18 :             }
     480              :         }
     481           48 :         if(messages)
     482              :         {
     483           33 :             std::string nomesh = n.mesh ? "" : "(no mesh)";
     484           75 :             std::printf("new node created: %s %zu %s\n",
     485              :                 n.name.c_str(),
     486           42 :                 n.mesh ? n.mesh.value() : 0,
     487              :                 nomesh.c_str()
     488              :             );
     489           33 :             if(n.translation)
     490              :             {
     491           16 :                 std::printf("node translation: %f %f %f\n", n.translation.value()[0], n.translation.value()[1], n.translation.value()[2]);
     492              :             }
     493              : 
     494           33 :         }
     495           48 :         nodes.push_back(n); //no benefit to std::move fundamental types
     496           48 :         i += block.size();
     497           48 :         numnodes++;
     498           96 :     }
     499           18 :     return numnodes;
     500           18 : }
     501              : 
     502              : //returns number of meshes
     503           18 : size_t GLTFModelInfo::findmeshes(std::string_view path)
     504              : {
     505           18 :     meshes.clear();
     506           18 :     std::vector<std::string> accessorblock = getblockbyname(path, "\"meshes\"");
     507           18 :     size_t nummeshes = 0;
     508              :     //get indices by parsing sub-blocks
     509           60 :     for(size_t i = 0; i < accessorblock.size(); ++i)
     510              :     {
     511           42 :         std::vector<std::string> block = getblock(accessorblock, i);
     512           42 :         if(!block.size())
     513              :         {
     514           21 :             continue;
     515              :         }
     516           42 :         Mesh m{"", std::nullopt, std::nullopt, std::nullopt, std::nullopt, std::nullopt, std::nullopt};
     517          282 :         for(std::string_view j : block)
     518              :         {
     519          261 :             if(j.find(" \"name\":") != std::string_view::npos)
     520              :             {
     521              :                 std::array<char, 256> s;
     522           21 :                 s.fill(0);
     523           21 :                 std::sscanf(j.data(), " \"name\":%s", s.data());
     524           21 :                 m.name = s.data();
     525           21 :                 cleanstring(m.name);
     526              :             }
     527          240 :             else if(j.find(" \"POSITION\":") != std::string_view::npos)
     528              :             {
     529           21 :                 uint positions = 0;
     530           21 :                 std::sscanf(j.data(), " \"POSITION\":%u", &positions);
     531           21 :                 m.positions = positions; //assign to optional with operator=
     532              :             }
     533          219 :             else if(j.find("\"NORMAL\"") != std::string_view::npos)
     534              :             {
     535           21 :                 uint normals = 0;
     536           21 :                 std::sscanf(j.data(), " \"NORMAL\":%u", &normals);
     537           21 :                 m.normals = normals;
     538              :             }
     539          198 :             else if(j.find("\"TEXCOORD_0\"") != std::string_view::npos)
     540              :             {
     541           21 :                 uint texcoords = 0;
     542           21 :                 std::sscanf( j.data(), " \"TEXCOORD_0\":%u", &texcoords);
     543           21 :                 m.texcoords = texcoords;
     544              :             }
     545          177 :             else if(j.find("\"JOINTS_0\"") != std::string_view::npos)
     546              :             {
     547            9 :                 uint joints = 0;
     548            9 :                 std::sscanf( j.data(), " \"JOINTS_0\":%u", &joints);
     549            9 :                 m.joints = joints;
     550              :             }
     551          168 :             else if(j.find("\"WEIGHTS_0\"") != std::string_view::npos)
     552              :             {
     553            9 :                 uint weights = 0;
     554            9 :                 std::sscanf( j.data(), " \"WEIGHTS_0\":%u", &weights);
     555            9 :                 m.weights = weights;
     556              :             }
     557          159 :             else if(j.find("\"indices\"") != std::string_view::npos)
     558              :             {
     559           21 :                 uint indices = 0;
     560           21 :                 std::sscanf( j.data(), " \"indices\":%u", &indices);
     561           21 :                 m.indices = indices;
     562              :             }
     563              :         }
     564           21 :         if(messages)
     565              :         {
     566              :             //note: nullopt is represented by -1 aka 18446744073709551615
     567           63 :             std::printf("new mesh created: %s %u %u %u %u %u %u\n",
     568              :                 m.name.c_str(),
     569           18 :                 m.positions ? m.positions.value() : -1,
     570           18 :                 m.normals ? m.normals.value() : -1,
     571           18 :                 m.texcoords ? m.texcoords.value() : -1,
     572           17 :                 m.joints ? m.joints.value() : -1,
     573           17 :                 m.weights ? m.weights.value() : -1,
     574           18 :                 m.indices ? m.indices.value() : -1
     575              :             );
     576              :         }
     577           21 :         meshes.push_back(std::move(m));
     578           21 :         i += block.size();
     579           21 :         nummeshes++;
     580           42 :     }
     581           18 :     return nummeshes;
     582           18 : }
     583              : 
     584              : //clears accessors vector, assigns to it ones found in the given math, returns number of accessors
     585           18 : size_t GLTFModelInfo::findaccessors(std::string_view path)
     586              : {
     587           18 :     accessors.clear();
     588           18 :     std::vector<std::string> accessorblock = getblockbyname(path, "\"accessors\"");
     589           18 :     size_t numaccessors = 0;
     590              :     //get indices by parsing sub-blocks
     591          384 :     for(size_t i = 0; i < accessorblock.size(); ++i)
     592              :     {
     593          366 :         std::vector<std::string> block = getblock(accessorblock, i);
     594          366 :         if(!block.size())
     595              :         {
     596          183 :             continue;
     597              :         }
     598          366 :         Accessor a{0,0,0,0,""};
     599          183 :         a.index = accessors.size();
     600         1233 :         for(std::string_view j : block)
     601              :         {
     602         1050 :             if(j.find(" \"bufferView\":") != std::string_view::npos)
     603              :             {
     604          183 :                 std::sscanf(j.data(), " \"bufferView\":%u", &a.bufferview);
     605              :             }
     606          867 :             else if(j.find("\"componentType\"") != std::string_view::npos)
     607              :             {
     608          183 :                 std::sscanf(j.data(), " \"componentType\":%u", &a.componenttype);
     609              :             }
     610          684 :             else if(j.find("\"count\"") != std::string_view::npos)
     611              :             {
     612          183 :                 std::sscanf( j.data(), " \"count\":%u", &a.count);
     613              :             }
     614          501 :             else if(j.find("\"type\"") != std::string_view::npos)
     615              :             {
     616              :                 std::array<char, 32> s;
     617          183 :                 s.fill(0);
     618          183 :                 std::sscanf(j.data(), " \"type\":%s", s.data());
     619          183 :                 a.type = s.data();
     620              :             }
     621              :         }
     622          183 :         if(messages)
     623              :         {
     624          124 :             std::printf("new accessor created: %lu %u %u %u %s\n", a.index, a.bufferview, a.componenttype, a.count, a.type.c_str());
     625              :         }
     626          183 :         accessors.push_back(std::move(a));
     627          183 :         i += block.size();
     628          183 :         numaccessors++;
     629          366 :     }
     630           18 :     return numaccessors;
     631           18 : }
     632              : 
     633              : //clears buffer views vector, assigns to it buffers found in file, returns number of buffer views
     634           18 : size_t GLTFModelInfo::findbufferviews(std::string_view path)
     635              : {
     636           18 :     bufferviews.clear();
     637           18 :     std::vector<std::string> bufferviewblock = getblockbyname(path, "\"bufferViews\"");
     638           18 :     size_t numbufferviews = 0;
     639          384 :     for(size_t i = 0; i < bufferviewblock.size(); ++i)
     640              :     {
     641          366 :         std::vector<std::string> block = getblock(bufferviewblock, i);
     642          366 :         if(!block.size())
     643              :         {
     644          183 :             continue;
     645              :         }
     646          183 :         BufferView b{0,0,0,0};
     647          183 :         b.index = bufferviews.size();
     648          780 :         for(std::string_view j : block)
     649              :         {
     650          597 :             if(j.find(" \"buffer\":") != std::string_view::npos)
     651              :             {
     652          183 :                 std::sscanf(j.data(), " \"buffer\":%u", &b.buffer);
     653              :             }
     654          414 :             else if(j.find("\"byteLength\"") != std::string_view::npos)
     655              :             {
     656          183 :                 std::sscanf(j.data(), " \"byteLength\":%u", &b.bytelength);
     657              :             }
     658          231 :             else if(j.find("\"byteOffset\"") != std::string_view::npos)
     659              :             {
     660          183 :                 std::sscanf( j.data(), " \"byteOffset\":%u", &b.byteoffset);
     661              :             }
     662              :         }
     663          183 :         if(messages)
     664              :         {
     665          124 :             std::printf("new bufferview created: %lu %u %u %u\n", b.index, b.buffer, b.bytelength, b.byteoffset);
     666              :         }
     667          183 :         bufferviews.push_back(b); //no benefit from std::move of fundamental types
     668          183 :         i += block.size();
     669          183 :         numbufferviews++;
     670          366 :     }
     671           18 :     return numbufferviews;
     672           18 : }
     673              : 
     674           18 : size_t GLTFModelInfo::findbuffers(std::string_view path)
     675              : {
     676           18 :     buffers.clear();
     677           18 :     std::vector<std::string> bufferblock = getblockbyname(path, "\"buffers\"");
     678           18 :     size_t numbuffers = 0;
     679           54 :     for(size_t i = 0; i < bufferblock.size(); ++i)
     680              :     {
     681           36 :         std::vector<std::string> block = getblock(bufferblock, i);
     682           36 :         if(!block.size())
     683              :         {
     684           18 :             continue;
     685              :         }
     686           18 :         std::string dir = path.data();
     687           18 :         dir = dir.substr(0, dir.find_last_of("/\\") + 1);
     688           18 :         Buffer b{0, 0, dir, {}};
     689           18 :         b.index = buffers.size();
     690           54 :         for(std::string_view j : block)
     691              :         {
     692           36 :             if(j.find(" \"byteLength\":") != std::string_view::npos)
     693              :             {
     694           18 :                 std::sscanf(j.data(), " \"byteLength\":%u", &b.bytelength);
     695              :             }
     696           18 :             else if(j.find("\"uri\"") != std::string_view::npos)
     697              :             {
     698              :                 std::array<char, 256> s;
     699           18 :                 s.fill(0);
     700           18 :                 std::sscanf(j.data(), " \"uri\":\"%s", s.data());
     701           18 :                 b.uri.append(s.data());
     702           18 :                 cleanstring(b.uri);
     703              :             }
     704              :         }
     705           18 :         std::ifstream binary(b.uri, std::ios::binary);
     706           18 :         std::vector<char> buffer(std::istreambuf_iterator<char>(binary), {});
     707           18 :         b.buf = buffer;
     708           18 :         if(messages)
     709              :         {
     710            9 :             std::printf("new buffer created: %lu %u %s %lu\n", b.index, b.bytelength, b.uri.c_str(), buffer.size());
     711              :         }
     712           18 :         buffers.push_back(std::move(b));
     713           18 :         i += block.size();
     714           18 :         numbuffers++;
     715           18 :         binary.close();
     716           36 :     }
     717           18 :     return numbuffers;
     718           18 : }
     719              : 
     720              : //clears buffer views vector, assigns to it buffers found in file, returns number of buffer views
     721           18 : size_t GLTFModelInfo::findanimations(std::string_view path)
     722              : {
     723           18 :     animations.clear();
     724           18 :     std::vector<std::string> animationsblock = getblockbyname(path, "\"animations\""); //all of the animations section
     725           18 :     size_t numanimations = 0;
     726           36 :     for(size_t i = 0; i < animationsblock.size(); ++i)
     727              :     {
     728           18 :         std::vector<std::string> block = getblock(animationsblock, i); //a single animation data block
     729           18 :         if(!block.size())
     730              :         {
     731            0 :             continue;
     732              :         }
     733           18 :         Animation a;
     734          729 :         for(size_t j = 0; j < block.size(); ++j)
     735              :         {
     736          711 :             if(block[j].find(" \"name\":") != std::string::npos)
     737              :             {
     738              :                 std::array<char, 256> s;
     739            9 :                 s.fill(0);
     740            9 :                 std::sscanf(block[j].c_str(), " \"name\":\"%s", s.data());
     741            9 :                 a.name = s.data();
     742            9 :                 cleanstring(a.name);
     743              :             }
     744          711 :             if(block[j].find(" \"channels\":") != std::string::npos)
     745              :             {
     746            9 :                 std::vector<std::string> channelblock = getblock(animationsblock, j+1); // all of the channel information of a single anim
     747          117 :                 for(size_t k = 0; k < channelblock.size(); ++k)
     748              :                 {
     749          108 :                     std::vector<std::string> channeldata = getblock(channelblock, k); // a single channel data block
     750          108 :                     if(!channeldata.size())
     751              :                     {
     752           54 :                         continue;
     753              :                     }
     754          108 :                     Animation::Channel c{a.channels.size(),0,0,""};
     755          324 :                     for(std::string_view l : channeldata)
     756              :                     {
     757          270 :                         if(l.find(" \"sampler\":") != std::string_view::npos)
     758              :                         {
     759           54 :                             std::sscanf(l.data(), " \"sampler\":%lu", &c.sampler);
     760              :                         }
     761          216 :                         else if(l.find("\"node\"") != std::string_view::npos)
     762              :                         {
     763           54 :                             std::sscanf(l.data(), " \"node\":%lu", &c.targetnode);
     764              :                         }
     765          162 :                         else if(l.find("\"path\"") != std::string_view::npos)
     766              :                         {
     767              :                             std::array<char, 256> s;
     768           54 :                             s.fill(0);
     769           54 :                             std::sscanf(l.data(), " \"path\":%s", s.data());
     770           54 :                             c.targetpath = s.data();
     771           54 :                             cleanstring(c.targetpath);
     772              :                         }
     773              :                     }
     774           54 :                     if(messages)
     775              :                     {
     776           48 :                         std::printf("new channel (animation %lu) added: %lu %lu %s\n", animations.size(), c.sampler, c.targetnode, c.targetpath.c_str());
     777              :                     }
     778           54 :                     a.channels.push_back(std::move(c));
     779           54 :                     k += channeldata.size();
     780          108 :                 }
     781            9 :             }
     782          711 :             if(block[j].find(" \"samplers\":") != std::string::npos)
     783              :             {
     784            9 :                 std::vector<std::string> channelblock = getblock(animationsblock, j+1); // all of the channel information of a single anim
     785          117 :                 for(size_t k = 0; k < channelblock.size(); ++k)
     786              :                 {
     787          108 :                     std::vector<std::string> channeldata = getblock(channelblock, k); // a single channel data block
     788          108 :                     if(!channeldata.size())
     789              :                     {
     790           54 :                         continue;
     791              :                     }
     792          108 :                     Animation::Sampler s{a.samplers.size(),0,"", 0};
     793          216 :                     for(std::string_view l : channeldata)
     794              :                     {
     795          162 :                         if(l.find(" \"input\":") != std::string_view::npos)
     796              :                         {
     797           54 :                             std::sscanf(l.data(), " \"input\":%lu", &s.input);
     798              :                         }
     799          108 :                         else if(l.find("\"output\"") != std::string_view::npos)
     800              :                         {
     801           54 :                             std::sscanf(l.data(), " \"output\":%lu", &s.output);
     802              :                         }
     803           54 :                         else if(l.find("\"interpolation\"") != std::string_view::npos)
     804              :                         {
     805              :                             std::array<char, 256> str;
     806           54 :                             str.fill(0);
     807           54 :                             std::sscanf(l.data(), " \"interpolation\":%s", str.data());
     808           54 :                             s.interpolation = str.data();
     809           54 :                             cleanstring(s.interpolation);
     810              :                         }
     811              :                     }
     812           54 :                     if(messages)
     813              :                     {
     814           48 :                         std::printf("new sampler (animation %lu) added: %lu %lu %s %lu\n", animations.size(), s.index, s.input, s.interpolation.c_str(), s.output);
     815              :                     }
     816           54 :                     a.samplers.push_back(std::move(s));
     817           54 :                     k += channeldata.size();
     818          108 :                 }
     819            9 :             }
     820              :         }
     821           18 :         if(messages)
     822              :         {
     823            9 :             std::printf("new animation (index %lu) created: %s\n", animations.size(), a.name.c_str());
     824              :         }
     825           18 :         animations.push_back(std::move(a));
     826           18 :         i += animationsblock.size();
     827           18 :         numanimations++;
     828           18 :     }
     829           18 :     return numanimations;
     830           18 : }
     831              : 
     832              : //get the indented block starting at the specified line
     833         1305 : std::vector<std::string> GLTFModelInfo::getblock(const std::vector<std::string> &file, size_t line)
     834              : {
     835        11691 :     auto getindentationdepth = [] (const std::string &r) -> size_t
     836              :     {
     837        11691 :         size_t indentationdepth = 0;
     838        49866 :         for(const char &c : r)
     839              :         {
     840        49866 :             if(c != ' ')
     841              :             {
     842        11691 :                 break;
     843              :             }
     844        38175 :             indentationdepth++;
     845              :         }
     846        11691 :         return indentationdepth;
     847              :     };
     848              : 
     849         1305 :     std::string line0 = std::string(file[line]);
     850         1305 :     size_t line0depth = getindentationdepth(line0);
     851              : 
     852         1305 :     std::vector<std::string> block;
     853        10503 :     for(size_t i = line+1; i < file.size(); ++i)
     854              :     {
     855        10386 :         if(getindentationdepth(file[i]) <= line0depth)
     856              :         {
     857         1188 :             break;
     858              :         }
     859         9198 :         block.push_back(file[i]);
     860              :     }
     861         2610 :     return block;
     862         1305 : }
        

Generated by: LCOV version 2.0-1