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