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 : }
|