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