LCOV - code coverage report
Current view: top level - engine/model - gltfloader.cpp (source / functions) Hit Total Coverage
Test: Libprimis Test Coverage Lines: 461 495 93.1 %
Date: 2024-11-22 05:07:59 Functions: 21 21 100.0 %

          Line data    Source code
       1             : 
       2             : /**
       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(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           2 :     }
     295           2 :     return true;
     296           3 : }
     297             : 
     298             : ////////////////////////////////////////
     299             : //private methods
     300             : ////////////////////////////////////////
     301             : /* loadjsonfile: loads a (gltf) json file to a std::vector
     302             :  *
     303             :  * Loads a JSON file and creates a new line for each bracket level and entry.
     304             :  *
     305             :  * Parameters:
     306             :  *  - std::string name: path to the file to load
     307             :  * Returns:
     308             :  *  - std::vector<std::string> of file
     309             :  */
     310         110 : std::vector<std::string> GLTFModelInfo::loadjsonfile(std::string_view name)
     311             : {
     312         110 :     std::string wholefile;
     313         110 :     std::string line;
     314         110 :     std::ifstream infile;
     315         110 :     infile.open(name.data());
     316         110 :     std::vector<std::string> output;
     317             : 
     318         110 :     if(!infile.good())
     319             :     {
     320           0 :         perror("Error opening GLTF file");
     321           0 :         return output;//empty vector
     322             :     }
     323       27040 :     while(getline(infile, line))
     324             :     {
     325       26930 :         bool stringliteral = false;
     326             :         //strip all whitespace not inside string literals
     327      580248 :         for(size_t i = 0; i < line.size(); ++i)
     328             :         {
     329      553318 :             if((i == 0 && line[i] == '"') || (line[i] == '"' && line[i-1] != '\\'))
     330             :             {
     331       35460 :                 stringliteral = !stringliteral;
     332             :             }
     333      553318 :             if(!stringliteral)
     334             :             {
     335      402838 :                 if(line[i] == ' ' || line[i] == '\t')
     336             :                 {
     337      310110 :                     line.erase(i, 1);
     338      310110 :                     i--;
     339             :                 }
     340             :             }
     341             :         }
     342       26930 :         stringliteral = false;
     343       26930 :         wholefile.append(line);
     344             :     }
     345             :     //line break after every { } ] ,
     346             : 
     347             :     //helper lambda
     348       27510 :     auto pushstring = [] (size_t depth, std::vector<std::string> &out, const std::string &wholefile, size_t lastbreak, size_t cur)
     349             :     {
     350       27510 :         std::string line(depth, ' ');
     351       27510 :         line.append(wholefile.substr(lastbreak, cur - lastbreak));
     352       27510 :         out.push_back(line);
     353       27510 :     };
     354         110 :     size_t lastbreak = 0,
     355         110 :            bracketdepth = 0;
     356      243242 :     for(size_t i = 0; i < wholefile.size(); ++i)
     357             :     {
     358      243133 :         if(wholefile[i] == ']' || wholefile[i] == '}')
     359             :         {
     360        6558 :             if(bracketdepth == 0)
     361             :             {
     362           1 :                 throw std::logic_error("GLTF loader error: too many closing } or ]");
     363             :             }
     364        6557 :             pushstring(bracketdepth, output, wholefile, lastbreak, i);
     365        6557 :             bracketdepth--;
     366        6557 :             lastbreak = i;
     367             :         }
     368      236575 :         else if(wholefile[i] =='{')
     369             :         {
     370        4555 :             pushstring(bracketdepth, output, wholefile, lastbreak, i+1);
     371        4555 :             bracketdepth++;
     372        4555 :             lastbreak = i+1;
     373             :         }
     374      232020 :         else if(wholefile[i] =='[')
     375             :         {
     376        2034 :             pushstring(bracketdepth, output, wholefile, lastbreak, i+1);
     377        2034 :             bracketdepth++;
     378        2034 :             lastbreak = i+1;
     379             :         }
     380      229986 :         else if(wholefile[i] ==',')
     381             :         {
     382       14364 :             pushstring(bracketdepth, output, wholefile, lastbreak, i+1);
     383       14364 :             lastbreak = i+1;
     384             :         }
     385             :     }
     386         109 :     if(bracketdepth != 0)
     387             :     {
     388           1 :         throw std::logic_error("GLTF loader error: too few } or ]");
     389             :     }
     390             : 
     391         108 :     infile.close();
     392         108 :     return output;
     393         116 : }
     394             : 
     395             : //helper for find<object> functions
     396         110 : std::vector<std::string> GLTFModelInfo::getblockbyname(std::string_view path, std::string blockname, size_t maxdepth)
     397             : {
     398         110 :     std::vector<std::string> file = loadjsonfile(path);
     399         108 :     size_t blockstart = 0;
     400       13593 :     for(size_t i = 0; i < file.size(); ++i)
     401             :     {
     402       13584 :         size_t itr = file[i].find(blockname);
     403       13584 :         if(maxdepth)
     404             :         {
     405         273 :             if(itr <= maxdepth)
     406             :             {
     407          18 :                 blockstart = i;
     408          18 :                 break;
     409             :             }
     410             :         }
     411       13311 :         else if(itr != std::string::npos)
     412             :         {
     413          81 :             blockstart = i;
     414          81 :             break;
     415             :         }
     416             :     }
     417         108 :     std::vector<std::string> block = getblock(file, blockstart);
     418         216 :     return block;
     419         108 : }
     420             : 
     421             : //helper to sanitize things dropped by sscanf
     422             : //removes ", from strings, part of json syntax
     423         204 : void GLTFModelInfo::cleanstring(std::string &s)
     424             : {
     425        2229 :     for(size_t i = 0; i < s.size(); ++i)
     426             :     {
     427        2025 :         if(s[i] == '\"' || s[i] == ',')
     428             :         {
     429         501 :             s.erase(i, 1);
     430         501 :             i--;
     431             :         }
     432             :     }
     433         204 : }
     434             : 
     435             : //returns number of nodes
     436          20 : size_t GLTFModelInfo::findnodes(std::string_view path)
     437             : {
     438          20 :     nodes.clear();
     439          42 :     std::vector<std::string> nodeblock = getblockbyname(path, "\"nodes\"", 1); //get only "node" at indent depth 1
     440          18 :     size_t numnodes = 0;
     441             :     //get indices by parsing sub-blocks
     442         114 :     for(size_t i = 0; i < nodeblock.size(); ++i)
     443             :     {
     444          96 :         std::vector<std::string> block = getblock(nodeblock, i);
     445          96 :         if(!block.size())
     446             :         {
     447          48 :             continue;
     448             :         }
     449          48 :         Node n{"", std::nullopt, std::nullopt, std::nullopt, {}};
     450         330 :         for(size_t j = 0; j < block.size(); ++j)
     451             :         {
     452         282 :             if(block[j].find(" \"name\":") != std::string::npos)
     453             :             {
     454             :                 std::array<char, 256> s;
     455          48 :                 s.fill(0);
     456          48 :                 std::sscanf(block[j].c_str(), " \"name\":\%s", s.data());
     457          48 :                 n.name = s.data();
     458          48 :                 cleanstring(n.name);
     459             :             }
     460         234 :             else if(block[j].find("\"mesh\"") != std::string::npos)
     461             :             {
     462          21 :                 uint mesh = 0;
     463          21 :                 std::sscanf( block[j].c_str(), " \"mesh\":%u", &mesh);
     464          21 :                 n.mesh = mesh;
     465             :             }
     466         213 :             else if(block[j].find("\"translation\"") != std::string::npos)
     467             :             {
     468          21 :                 std::array<float, 3> translation = {0,0,0};
     469          21 :                 std::vector<std::string> translationblock = getblock(block, j);
     470          84 :                 for(size_t k = 0; k < translationblock.size(); ++k)
     471             :                 {
     472          63 :                     std::sscanf( translationblock[k].c_str(), " %f", &translation[k]);
     473             :                 }
     474          21 :                 n.translation = translation;
     475          21 :             }
     476         192 :             else if(block[j].find("\"children\"") != std::string::npos)
     477             :             {
     478          18 :                 std::vector<size_t> children;
     479          18 :                 std::vector<std::string> translationblock = getblock(block, j);
     480          45 :                 for(size_t k = 0; k < translationblock.size(); ++k)
     481             :                 {
     482          27 :                     size_t child = 0;
     483          27 :                     std::sscanf( translationblock[k].c_str(), " %lu", &child);
     484          27 :                     children.push_back(child);
     485             :                 }
     486          18 :                 n.children = children;
     487          18 :             }
     488             :         }
     489          48 :         if(messages)
     490             :         {
     491          33 :             std::string nomesh = n.mesh ? "" : "(no mesh)";
     492          75 :             std::printf("new node created: %s %lu %s\n",
     493             :                 n.name.c_str(),
     494          42 :                 n.mesh ? n.mesh.value() : 0,
     495             :                 nomesh.c_str()
     496             :             );
     497          33 :             if(n.translation)
     498             :             {
     499          16 :                 std::printf("node translation: %f %f %f\n", n.translation.value()[0], n.translation.value()[1], n.translation.value()[2]);
     500             :             }
     501             : 
     502          33 :         }
     503          48 :         nodes.push_back(n);
     504          48 :         i += block.size();
     505          48 :         numnodes++;
     506          96 :     }
     507          18 :     return numnodes;
     508          18 : }
     509             : 
     510             : //returns number of meshes
     511          18 : size_t GLTFModelInfo::findmeshes(std::string_view path)
     512             : {
     513          18 :     meshes.clear();
     514          36 :     std::vector<std::string> accessorblock = getblockbyname(path, "\"meshes\"");
     515          18 :     size_t nummeshes = 0;
     516             :     //get indices by parsing sub-blocks
     517          60 :     for(size_t i = 0; i < accessorblock.size(); ++i)
     518             :     {
     519          42 :         std::vector<std::string> block = getblock(accessorblock, i);
     520          42 :         if(!block.size())
     521             :         {
     522          21 :             continue;
     523             :         }
     524          21 :         Mesh m{"",std::nullopt, std::nullopt, std::nullopt, std::nullopt, std::nullopt, std::nullopt};
     525         282 :         for(std::string_view j : block)
     526             :         {
     527         261 :             if(j.find(" \"name\":") != std::string_view::npos)
     528             :             {
     529             :                 std::array<char, 256> s;
     530          21 :                 s.fill(0);
     531          21 :                 std::sscanf(j.data(), " \"name\":\%s", s.data());
     532          21 :                 m.name = s.data();
     533          21 :                 cleanstring(m.name);
     534             :             }
     535         240 :             else if(j.find(" \"POSITION\":") != std::string_view::npos)
     536             :             {
     537          21 :                 uint positions = 0;
     538          21 :                 std::sscanf(j.data(), " \"POSITION\":%u", &positions);
     539          21 :                 m.positions = positions; //assign to optional with operator=
     540             :             }
     541         219 :             else if(j.find("\"NORMAL\"") != std::string_view::npos)
     542             :             {
     543          21 :                 uint normals = 0;
     544          21 :                 std::sscanf(j.data(), " \"NORMAL\":%u", &normals);
     545          21 :                 m.normals = normals;
     546             :             }
     547         198 :             else if(j.find("\"TEXCOORD_0\"") != std::string_view::npos)
     548             :             {
     549          21 :                 uint texcoords = 0;
     550          21 :                 std::sscanf( j.data(), " \"TEXCOORD_0\":%u", &texcoords);
     551          21 :                 m.texcoords = texcoords;
     552             :             }
     553         177 :             else if(j.find("\"JOINTS_0\"") != std::string_view::npos)
     554             :             {
     555           9 :                 uint joints = 0;
     556           9 :                 std::sscanf( j.data(), " \"JOINTS_0\":%u", &joints);
     557           9 :                 m.joints = joints;
     558             :             }
     559         168 :             else if(j.find("\"WEIGHTS_0\"") != std::string_view::npos)
     560             :             {
     561           9 :                 uint weights = 0;
     562           9 :                 std::sscanf( j.data(), " \"WEIGHTS_0\":%u", &weights);
     563           9 :                 m.weights = weights;
     564             :             }
     565         159 :             else if(j.find("\"indices\"") != std::string_view::npos)
     566             :             {
     567          21 :                 uint indices = 0;
     568          21 :                 std::sscanf( j.data(), " \"indices\":%u", &indices);
     569          21 :                 m.indices = indices;
     570             :             }
     571             :         }
     572          21 :         if(messages)
     573             :         {
     574             :             //note: nullopt is represented by -1 aka 18446744073709551615
     575          63 :             std::printf("new mesh created: %s %u %u %u %u %u %u\n",
     576             :                 m.name.c_str(),
     577          18 :                 m.positions ? m.positions.value() : -1,
     578          18 :                 m.normals ? m.normals.value() : -1,
     579          18 :                 m.texcoords ? m.texcoords.value() : -1,
     580          17 :                 m.joints ? m.joints.value() : -1,
     581          17 :                 m.weights ? m.weights.value() : -1,
     582          18 :                 m.indices ? m.indices.value() : -1
     583             :             );
     584             :         }
     585          21 :         meshes.push_back(m);
     586          21 :         i += block.size();
     587          21 :         nummeshes++;
     588          42 :     }
     589          18 :     return nummeshes;
     590          18 : }
     591             : 
     592             : //clears accessors vector, assigns to it ones found in the given math, returns number of accessors
     593          18 : size_t GLTFModelInfo::findaccessors(std::string_view path)
     594             : {
     595          18 :     accessors.clear();
     596          36 :     std::vector<std::string> accessorblock = getblockbyname(path, "\"accessors\"");
     597          18 :     size_t numaccessors = 0;
     598             :     //get indices by parsing sub-blocks
     599         384 :     for(size_t i = 0; i < accessorblock.size(); ++i)
     600             :     {
     601         366 :         std::vector<std::string> block = getblock(accessorblock, i);
     602         366 :         if(!block.size())
     603             :         {
     604         183 :             continue;
     605             :         }
     606         183 :         Accessor a{0,0,0,0,""};
     607         183 :         a.index = accessors.size();
     608        1233 :         for(std::string_view j : block)
     609             :         {
     610        1050 :             if(j.find(" \"bufferView\":") != std::string_view::npos)
     611             :             {
     612         183 :                 std::sscanf(j.data(), " \"bufferView\":%u", &a.bufferview);
     613             :             }
     614         867 :             else if(j.find("\"componentType\"") != std::string_view::npos)
     615             :             {
     616         183 :                 std::sscanf(j.data(), " \"componentType\":%u", &a.componenttype);
     617             :             }
     618         684 :             else if(j.find("\"count\"") != std::string_view::npos)
     619             :             {
     620         183 :                 std::sscanf( j.data(), " \"count\":%u", &a.count);
     621             :             }
     622         501 :             else if(j.find("\"type\"") != std::string_view::npos)
     623             :             {
     624             :                 std::array<char, 32> s;
     625         183 :                 s.fill(0);
     626         183 :                 std::sscanf(j.data(), " \"type\":%s", s.data());
     627         183 :                 a.type = s.data();
     628             :             }
     629             :         }
     630         183 :         if(messages)
     631             :         {
     632         124 :             std::printf("new accessor created: %lu %u %u %u %s\n", a.index, a.bufferview, a.componenttype, a.count, a.type.c_str());
     633             :         }
     634         183 :         accessors.push_back(a);
     635         183 :         i += block.size();
     636         183 :         numaccessors++;
     637         366 :     }
     638          18 :     return numaccessors;
     639          18 : }
     640             : 
     641             : //clears buffer views vector, assigns to it buffers found in file, returns number of buffer views
     642          18 : size_t GLTFModelInfo::findbufferviews(std::string_view path)
     643             : {
     644          18 :     bufferviews.clear();
     645          36 :     std::vector<std::string> bufferviewblock = getblockbyname(path, "\"bufferViews\"");
     646          18 :     size_t numbufferviews = 0;
     647         384 :     for(size_t i = 0; i < bufferviewblock.size(); ++i)
     648             :     {
     649         366 :         std::vector<std::string> block = getblock(bufferviewblock, i);
     650         366 :         if(!block.size())
     651             :         {
     652         183 :             continue;
     653             :         }
     654         183 :         BufferView b{0,0,0};
     655         183 :         b.index = bufferviews.size();
     656         780 :         for(std::string_view j : block)
     657             :         {
     658         597 :             if(j.find(" \"buffer\":") != std::string_view::npos)
     659             :             {
     660         183 :                 std::sscanf(j.data(), " \"buffer\":%u", &b.buffer);
     661             :             }
     662         414 :             else if(j.find("\"byteLength\"") != std::string_view::npos)
     663             :             {
     664         183 :                 std::sscanf(j.data(), " \"byteLength\":%u", &b.bytelength);
     665             :             }
     666         231 :             else if(j.find("\"byteOffset\"") != std::string_view::npos)
     667             :             {
     668         183 :                 std::sscanf( j.data(), " \"byteOffset\":%u", &b.byteoffset);
     669             :             }
     670             :         }
     671         183 :         if(messages)
     672             :         {
     673         124 :             std::printf("new bufferview created: %lu %u %u %u\n", b.index, b.buffer, b.bytelength, b.byteoffset);
     674             :         }
     675         183 :         bufferviews.push_back(b);
     676         183 :         i += block.size();
     677         183 :         numbufferviews++;
     678         366 :     }
     679          18 :     return numbufferviews;
     680          18 : }
     681             : 
     682          18 : size_t GLTFModelInfo::findbuffers(std::string_view path)
     683             : {
     684          18 :     buffers.clear();
     685          36 :     std::vector<std::string> bufferblock = getblockbyname(path, "\"buffers\"");
     686          18 :     size_t numbuffers = 0;
     687          54 :     for(size_t i = 0; i < bufferblock.size(); ++i)
     688             :     {
     689          36 :         std::vector<std::string> block = getblock(bufferblock, i);
     690          36 :         if(!block.size())
     691             :         {
     692          18 :             continue;
     693             :         }
     694          18 :         std::string dir = path.data();
     695          18 :         dir = dir.substr(0, dir.find_last_of("/\\") + 1);
     696          18 :         Buffer b{0,0,dir};
     697          18 :         b.index = buffers.size();
     698          54 :         for(std::string_view j : block)
     699             :         {
     700          36 :             if(j.find(" \"byteLength\":") != std::string_view::npos)
     701             :             {
     702          18 :                 std::sscanf(j.data(), " \"byteLength\":%u", &b.bytelength);
     703             :             }
     704          18 :             else if(j.find("\"uri\"") != std::string_view::npos)
     705             :             {
     706             :                 std::array<char, 256> s;
     707          18 :                 s.fill(0);
     708          18 :                 std::sscanf(j.data(), " \"uri\":\"%s", s.data());
     709          18 :                 b.uri.append(s.data());
     710          18 :                 cleanstring(b.uri);
     711             :             }
     712             :         }
     713          18 :         std::ifstream binary(b.uri, std::ios::binary);
     714          18 :         std::vector<char> buffer(std::istreambuf_iterator<char>(binary), {});
     715          18 :         b.buf = buffer;
     716          18 :         if(messages)
     717             :         {
     718           9 :             std::printf("new buffer created: %lu %u %s %lu\n", b.index, b.bytelength, b.uri.c_str(), buffer.size());
     719             :         }
     720          18 :         buffers.push_back(b);
     721          18 :         i += block.size();
     722          18 :         numbuffers++;
     723          18 :         binary.close();
     724          36 :     }
     725          18 :     return numbuffers;
     726          18 : }
     727             : 
     728             : //clears buffer views vector, assigns to it buffers found in file, returns number of buffer views
     729          18 : size_t GLTFModelInfo::findanimations(std::string_view path)
     730             : {
     731          18 :     animations.clear();
     732          36 :     std::vector<std::string> animationsblock = getblockbyname(path, "\"animations\""); //all of the animations section
     733          18 :     size_t numanimations = 0;
     734          36 :     for(size_t i = 0; i < animationsblock.size(); ++i)
     735             :     {
     736          18 :         std::vector<std::string> block = getblock(animationsblock, i); //a single animation data block
     737          18 :         if(!block.size())
     738             :         {
     739           0 :             continue;
     740             :         }
     741          18 :         Animation a;
     742         729 :         for(size_t j = 0; j < block.size(); ++j)
     743             :         {
     744         711 :             if(block[j].find(" \"name\":") != std::string::npos)
     745             :             {
     746             :                 std::array<char, 256> s;
     747           9 :                 s.fill(0);
     748           9 :                 std::sscanf(block[j].c_str(), " \"name\":\"%s", s.data());
     749           9 :                 a.name = s.data();
     750           9 :                 cleanstring(a.name);
     751             :             }
     752         711 :             if(block[j].find(" \"channels\":") != std::string::npos)
     753             :             {
     754           9 :                 std::vector<std::string> channelblock = getblock(animationsblock, j+1); // all of the channel information of a single anim
     755         117 :                 for(size_t k = 0; k < channelblock.size(); ++k)
     756             :                 {
     757         108 :                     std::vector<std::string> channeldata = getblock(channelblock, k); // a single channel data block
     758         108 :                     if(!channeldata.size())
     759             :                     {
     760          54 :                         continue;
     761             :                     }
     762          54 :                     Animation::Channel c{a.channels.size(),0,0,""};
     763         324 :                     for(std::string_view l : channeldata)
     764             :                     {
     765         270 :                         if(l.find(" \"sampler\":") != std::string_view::npos)
     766             :                         {
     767          54 :                             std::sscanf(l.data(), " \"sampler\":%lu", &c.sampler);
     768             :                         }
     769         216 :                         else if(l.find("\"node\"") != std::string_view::npos)
     770             :                         {
     771          54 :                             std::sscanf(l.data(), " \"node\":%lu", &c.targetnode);
     772             :                         }
     773         162 :                         else if(l.find("\"path\"") != std::string_view::npos)
     774             :                         {
     775             :                             std::array<char, 256> s;
     776          54 :                             s.fill(0);
     777          54 :                             std::sscanf(l.data(), " \"path\":%s", s.data());
     778          54 :                             c.targetpath = s.data();
     779          54 :                             cleanstring(c.targetpath);
     780             :                         }
     781             :                     }
     782          54 :                     if(messages)
     783             :                     {
     784          48 :                         std::printf("new channel (animation %lu) added: %lu %lu %s\n", animations.size(), c.sampler, c.targetnode, c.targetpath.c_str());
     785             :                     }
     786          54 :                     a.channels.push_back(c);
     787          54 :                     k += channeldata.size();
     788         108 :                 }
     789           9 :             }
     790         711 :             if(block[j].find(" \"samplers\":") != std::string::npos)
     791             :             {
     792           9 :                 std::vector<std::string> channelblock = getblock(animationsblock, j+1); // all of the channel information of a single anim
     793         117 :                 for(size_t k = 0; k < channelblock.size(); ++k)
     794             :                 {
     795         108 :                     std::vector<std::string> channeldata = getblock(channelblock, k); // a single channel data block
     796         108 :                     if(!channeldata.size())
     797             :                     {
     798          54 :                         continue;
     799             :                     }
     800          54 :                     Animation::Sampler s{a.samplers.size(),0,"", 0};
     801         216 :                     for(std::string_view l : channeldata)
     802             :                     {
     803         162 :                         if(l.find(" \"input\":") != std::string_view::npos)
     804             :                         {
     805          54 :                             std::sscanf(l.data(), " \"input\":%lu", &s.input);
     806             :                         }
     807         108 :                         else if(l.find("\"output\"") != std::string_view::npos)
     808             :                         {
     809          54 :                             std::sscanf(l.data(), " \"output\":%lu", &s.output);
     810             :                         }
     811          54 :                         else if(l.find("\"interpolation\"") != std::string_view::npos)
     812             :                         {
     813             :                             std::array<char, 256> str;
     814          54 :                             str.fill(0);
     815          54 :                             std::sscanf(l.data(), " \"interpolation\":%s", str.data());
     816          54 :                             s.interpolation = str.data();
     817          54 :                             cleanstring(s.interpolation);
     818             :                         }
     819             :                     }
     820          54 :                     if(messages)
     821             :                     {
     822          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);
     823             :                     }
     824          54 :                     a.samplers.push_back(s);
     825          54 :                     k += channeldata.size();
     826         108 :                 }
     827           9 :             }
     828             :         }
     829          18 :         if(messages)
     830             :         {
     831           9 :             std::printf("new animation (index %lu) created: %s\n", animations.size(), a.name.c_str());
     832             :         }
     833          18 :         animations.push_back(a);
     834          18 :         i += animationsblock.size();
     835          18 :         numanimations++;
     836          18 :     }
     837          18 :     return numanimations;
     838          18 : }
     839             : 
     840             : //get the indented block starting at the specified line
     841        1305 : std::vector<std::string> GLTFModelInfo::getblock(const std::vector<std::string> &file, uint line)
     842             : {
     843       11691 :     auto getindentationdepth = [] (const std::string &r)
     844             :     {
     845       11691 :         uint indentationdepth = 0;
     846       49866 :         for(const char &c : r)
     847             :         {
     848       49866 :             if(c != ' ')
     849             :             {
     850       11691 :                 break;
     851             :             }
     852       38175 :             indentationdepth++;
     853             :         }
     854       11691 :         return indentationdepth;
     855             :     };
     856             : 
     857        1305 :     std::string line0 = std::string(file[line]);
     858        1305 :     uint line0depth = getindentationdepth(line0);
     859             : 
     860        1305 :     std::vector<std::string> block;
     861       10503 :     for(uint i = line+1; i < file.size(); ++i)
     862             :     {
     863       10386 :         if(getindentationdepth(file[i]) <= line0depth)
     864             :         {
     865        1188 :             break;
     866             :         }
     867        9198 :         block.push_back(file[i]);
     868             :     }
     869        2610 :     return block;
     870        1305 : }

Generated by: LCOV version 1.14