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-05-09 04:28:55 Functions: 100.0 % 21 21

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

Generated by: LCOV version 2.0-1