LCOV - code coverage report
Current view: top level - engine/model - gltfloader.cpp (source / functions) Coverage Total Hit
Test: Libprimis Test Coverage Lines: 93.1 % 495 461
Test Date: 2025-02-21 06:59:27 Functions: 100.0 % 21 21

            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 trailing } 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 trailing } 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_view 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           20 :     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           96 :         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); //no benefit to std::move fundamental types
     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           18 :     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           42 :         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(std::move(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           18 :     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          366 :         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(std::move(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           18 :     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,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); //no benefit from std::move of fundamental types
     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           18 :     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(std::move(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           18 :     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          108 :                     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(std::move(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          108 :                     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(std::move(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(std::move(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 2.0-1