Thanks to visit codestin.com
Credit goes to glvis.github.io

GLVis  v4.2
Accurate and flexible finite element visualization
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Pages
gltf.cpp
Go to the documentation of this file.
1 // Copyright (c) 2010-2022, Lawrence Livermore National Security, LLC. Produced
2 // at the Lawrence Livermore National Laboratory. All Rights reserved. See files
3 // LICENSE and NOTICE for details. LLNL-CODE-443271.
4 //
5 // This file is part of the GLVis visualization tool and library. For more
6 // information and source code availability see https://glvis.org.
7 //
8 // GLVis is free software; you can redistribute it and/or modify it under the
9 // terms of the BSD-3 license. We welcome feedback and contributions, see file
10 // CONTRIBUTING.md for details.
11 
12 #include "gltf.hpp"
13 #include "aux_vis.hpp" // SaveAsPNG
14 
15 using namespace std;
16 
17 
18 const char *glTF_Builder::tensorTypes[] =
19 {
20  "SCALAR", "VEC2", "VEC3", "VEC4", "MAT2", "MAT3", "MAT4"
21 };
22 
24 glTF_Builder::addBuffer(const string &bufferName)
25 {
26  buffers.resize(buffers.size() + 1);
27  auto &buf = buffers.back();
28  buf.uri.value = file_prefix + "." + bufferName + ".bin";
29  buf.uri.valid = true;
30  buf.byteLength.value = 0;
31  buf.byteLength.valid = true;
32  buf.file.reset(new ofstream(buf.uri.value, ios::out | ios::binary));
33 
34  return {(unsigned)buffers.size() - 1};
35 }
36 
39  const void *data,
40  size_t byteLength,
41  size_t byteStride,
42  size_t byteAlign,
43  target_type target)
44 {
45  if (buffer.id >= buffers.size()) { return {INVALID_ID}; }
46 
47  buffer_views.resize(buffer_views.size() + 1);
48  auto &buf_view = buffer_views.back();
49  auto &buf = buffers[buffer.id];
50 
51  buf_view.buffer.value = buffer.id;
52  buf_view.buffer.valid = true;
53 
54  const unsigned buf_offset = buf.byteLength.value;
55  const unsigned new_offset = byteAlign*((buf_offset+byteAlign-1)/byteAlign);
56  buf_view.byteOffset.value = new_offset;
57  buf_view.byteOffset.valid = true;
58 
59  buf_view.byteLength.value = byteLength;
60  buf_view.byteLength.valid = true;
61 
62  if (target == target_type::ARRAY_BUFFER)
63  {
64  buf_view.byteStride.value = byteStride;
65  buf_view.byteStride.valid = true;
66  }
67 
68  buf_view.target.value = (unsigned)target;
69  buf_view.target.valid = true;
70 
71  // append padding to file
72  for (unsigned i = buf_offset; i != new_offset; ++i) { buf.file->put('\0'); }
73  // write data to file
74  buf.file->write(reinterpret_cast<const char *>(data), byteLength);
75 
76  buf.byteLength.value = new_offset + byteLength;
77 
78  return {(unsigned)buffer_views.size() - 1};
79 }
80 
82  const void *data,
83  size_t byteLength)
84 {
85  if (bufferView.id >= buffer_views.size()) { return; }
86 
87  auto &buf_view = buffer_views[bufferView.id];
88  auto &buf = buffers[buf_view.buffer.value];
89 
90  buf_view.byteLength.value += byteLength;
91 
92  buf.file->write(reinterpret_cast<const char *>(data), byteLength);
93  buf.byteLength.value += byteLength;
94 }
95 
98  size_t byteOffset,
99  component_type componentType,
100  size_t count,
101  tensor_type tensorType)
102 {
103  if (bufferView.id >= buffer_views.size() || count == 0)
104  {
105  return {INVALID_ID};
106  }
107 
108  accessors.resize(accessors.size() + 1);
109  auto &acc = accessors.back();
110 
111  acc.bufferView.value = bufferView.id;
112  acc.bufferView.valid = true;
113 
114  acc.byteOffset.value = byteOffset;
115  acc.byteOffset.valid = true;
116 
117  acc.componentType.value = (unsigned)componentType;
118  acc.componentType.valid = true;
119 
120  acc.count.value = count;
121  acc.count.valid = true;
122 
123  acc.type.value = tensorTypes[(unsigned)tensorType];
124  acc.type.valid = true;
125 
126  // Note: acc.min and acc.max remain invalid and will not be written too file
127 
128  if (componentType != component_type::FLOAT &&
129  buffer_views[bufferView.id].target.value !=
130  (unsigned)target_type::ELEMENT_ARRAY_BUFFER)
131  {
132  acc.normalized.value = true;
133  acc.normalized.valid = true;
134  }
135 
136  return {(unsigned)accessors.size() - 1};
137 }
138 
141  size_t byteOffset,
142  size_t count,
143  vec2f min,
144  vec2f max)
145 {
146  auto id = addAccessor(bufferView,
147  byteOffset,
148  component_type::FLOAT,
149  count,
150  tensor_type::VEC2);
151 
152  if (id.id != INVALID_ID)
153  {
154  auto &acc = accessors[id.id];
155  acc.min.value.assign(min.begin(), min.end());
156  acc.min.valid = true;
157  acc.max.value.assign(max.begin(), max.end());
158  acc.max.valid = true;
159  }
160 
161  return id;
162 }
163 
166  size_t byteOffset,
167  size_t count,
168  vec3f min,
169  vec3f max)
170 {
171  auto id = addAccessor(bufferView,
172  byteOffset,
173  component_type::FLOAT,
174  count,
175  tensor_type::VEC3);
176 
177  if (id.id != INVALID_ID)
178  {
179  auto &acc = accessors[id.id];
180  acc.min.value.assign(min.begin(), min.end());
181  acc.min.valid = true;
182  acc.max.value.assign(max.begin(), max.end());
183  acc.max.valid = true;
184  }
185 
186  return id;
187 }
188 
190 glTF_Builder::addImage(const string &imageName,
191  int width,
192  int height,
193  const color4f *pixels)
194 {
195 #ifndef GLVIS_USE_LIBPNG
196 
197  return {INVALID_ID};
198 
199 #else
200 
201  images.resize(images.size() + 1);
202  auto &img = images.back();
203 
204  img.uri.value = file_prefix + "." + imageName + ".png";
205  img.uri.valid = true;
206 
207  img.name.value = imageName;
208  img.name.valid = true;
209 
210  // write the image
211  auto get_row = [&](int row, void *pxls)
212  {
213  auto pxls_out = reinterpret_cast<array<uint8_t,4>*>(pxls);
214  auto pxls_in = pixels + row*width;
215  for (int i = 0; i < width; ++i)
216  {
217  for (int j = 0; j < 4; ++j)
218  {
219  pxls_out[i][j] = std::min(int(pxls_in[i][j]*256), 255);
220  }
221  }
222  };
223  SaveAsPNG(img.uri.value.c_str(), width, height,
224  /* is_hidpi: */ false, /* with_alpha: */ true, get_row);
225 
226  return {(unsigned)images.size() - 1};
227 
228 #endif // GLVIS_USE_LIBPNG
229 }
230 
233  min_filter minFilter,
234  wrap_type wrapS,
235  wrap_type wrapT)
236 {
237  samplers.resize(samplers.size() + 1);
238  auto &sampler = samplers.back();
239 
240  sampler.magFilter.value = (unsigned)magFilter;
241  sampler.magFilter.valid = true;
242 
243  sampler.minFilter.value = (unsigned)minFilter;
244  sampler.minFilter.valid= true;
245 
246  sampler.wrapS.value = (unsigned)wrapS;
247  sampler.wrapS.valid = true;
248 
249  sampler.wrapT.value = (unsigned)wrapT;
250  sampler.wrapT.valid = true;
251 
252  return {(unsigned)samplers.size() - 1};
253 }
254 
257 {
258  if (sampler.id >= samplers.size() || source.id >= images.size())
259  {
260  return {INVALID_ID};
261  }
262 
263  textures.resize(textures.size() + 1);
264  auto &tex = textures.back();
265 
266  tex.sampler.value = sampler.id;
267  tex.sampler.valid = true;
268 
269  tex.source.value = source.id;
270  tex.source.valid = true;
271 
272  return {(unsigned)textures.size() - 1};
273 }
274 
276 glTF_Builder::addMaterial(const string &materialName,
277  const pbr_matallic_roughness &pbrMetallicRoughness,
278  bool doubleSided)
279 {
280  if (pbrMetallicRoughness.haveTexture &&
281  pbrMetallicRoughness.baseColorTexture.id >= textures.size())
282  {
283  return {INVALID_ID};
284  }
285 
286  materials.resize(materials.size() + 1);
287  auto &mat = materials.back();
288 
289  mat.name.value = materialName;
290  mat.name.valid = true;
291 
292  auto &pbr = mat.pbrMetallicRoughness.value;
293 
294  pbr.baseColorFactor.value = pbrMetallicRoughness.baseColorFactor;
295  pbr.baseColorFactor.valid = true;
296 
297  auto &tex_info = pbr.baseColorTexture.value;
298 
299  tex_info.index.value = pbrMetallicRoughness.baseColorTexture.id;
300  tex_info.index.valid = true;
301 
302  tex_info.texCoord.value = 0;
303  tex_info.texCoord.valid = true;
304 
305  pbr.baseColorTexture.valid = pbrMetallicRoughness.haveTexture;
306 
307  pbr.metallicFactor.value = pbrMetallicRoughness.metallicFactor;
308  pbr.metallicFactor.valid = true;
309 
310  pbr.roughnessFactor.value = pbrMetallicRoughness.roughnessFactor;
311  pbr.roughnessFactor.valid = true;
312 
313  mat.pbrMetallicRoughness.valid = true;
314 
315  mat.doubleSided.value = doubleSided;
316  mat.doubleSided.valid = true;
317 
318  return {(unsigned)materials.size() - 1};
319 }
320 
322 glTF_Builder::addMesh(const string &meshName)
323 {
324  meshes.resize(meshes.size() + 1);
325  auto &mesh = meshes.back();
326 
327  mesh.name.value = meshName;
328  mesh.name.valid = true;
329 
330  return {(unsigned)meshes.size() - 1};
331 }
332 
334  accessor_id vertexPositions,
335  accessor_id vertexNormals,
336  accessor_id vertexTexCoords0,
337  accessor_id vertexIndices,
338  material_id material)
339 {
340  if (mesh.id >= meshes.size() || vertexPositions.id >= accessors.size())
341  { return; }
342 
343  auto &primitives = meshes[mesh.id].primitives;
344  primitives.resize(primitives.size() + 1);
345  auto &pri = primitives.back();
346 
347  pri.attributes.value.POSITION.value = vertexPositions.id;
348  pri.attributes.value.POSITION.valid = true;
349 
350  if (vertexNormals.id < accessors.size())
351  {
352  pri.attributes.value.NORMAL.value = vertexNormals.id;
353  pri.attributes.value.NORMAL.valid = true;
354  }
355 
356  if (vertexTexCoords0.id < accessors.size())
357  {
358  pri.attributes.value.TEXCOORD_0.value = vertexTexCoords0.id;
359  pri.attributes.value.TEXCOORD_0.valid = true;
360  }
361 
362  pri.attributes.valid = true;
363 
364  if (vertexIndices.id < accessors.size())
365  {
366  pri.indices.value = vertexIndices.id;
367  pri.indices.valid = true;
368  }
369 
370  if (material.id < materials.size())
371  {
372  pri.material.value = material.id;
373  pri.material.valid = true;
374  }
375 
376  // pri.mode remains undefined since default is 4 = TRIANGLES
377 }
378 
380  accessor_id vertexPositions,
381  accessor_id vertexTexcoords0,
382  accessor_id vertexColors0,
383  material_id material)
384 {
385  if (mesh.id >= meshes.size()) { return; }
386 
387  auto &primitives = meshes[mesh.id].primitives;
388  primitives.resize(primitives.size() + 1);
389  auto &pri = primitives.back();
390 
391  pri.attributes.value.POSITION.value = vertexPositions.id;
392  pri.attributes.value.POSITION.valid = true;
393 
394  if (vertexTexcoords0.id < accessors.size())
395  {
396  pri.attributes.value.TEXCOORD_0.value = vertexTexcoords0.id;
397  pri.attributes.value.TEXCOORD_0.valid = true;
398  }
399  else if (vertexColors0.id < accessors.size())
400  {
401  pri.attributes.value.COLOR_0.value = vertexColors0.id;
402  pri.attributes.value.COLOR_0.valid = true;
403  }
404 
405  // NORMAL remains undefined
406 
407  pri.attributes.valid = true;
408 
409  // pri.indices remain undefined
410 
411  if (material.id < materials.size())
412  {
413  pri.material.value = material.id;
414  pri.material.valid = true;
415  }
416 
417  pri.mode.value = 1; // = LINES
418  pri.mode.valid = true;
419 }
420 
422 glTF_Builder::addNode(const string &nodeName)
423 {
424  nodes.resize(nodes.size() + 1);
425  auto &node = nodes.back();
426 
427  node.name.value = nodeName;
428  node.name.valid = true;
429 
430  return {(unsigned)nodes.size() - 1};
431 }
432 
434 {
435  if (node.id >= nodes.size()) { return; }
436 
437  nodes[node.id].mesh.value = mesh.id;
438  nodes[node.id].mesh.valid = true;
439 }
440 
442 {
443  if (node.id >= nodes.size()) { return; }
444 
445  nodes[node.id].scale.value = scale;
446  nodes[node.id].scale.valid = true;
447 }
448 
450 {
451  if (node.id >= nodes.size()) { return; }
452 
453  nodes[node.id].translation.value = translation;
454  nodes[node.id].translation.valid = true;
455 }
456 
458  pbr_matallic_roughness &pbr_mr_copy)
459 {
460  if (material.id >= materials.size()) { return; }
461 
462  auto &mat = materials[material.id];
463  auto &pbr = mat.pbrMetallicRoughness.value;
464 
465  pbr_mr_copy.haveTexture = pbr.baseColorTexture.valid;
466  pbr_mr_copy.baseColorFactor = pbr.baseColorFactor.value;
467  pbr_mr_copy.baseColorTexture = {pbr.baseColorTexture.value.index.value};
468  pbr_mr_copy.metallicFactor = pbr.metallicFactor.value;
469  pbr_mr_copy.roughnessFactor = pbr.roughnessFactor.value;
470 }
471 
473 {
474  if (nodes.size() == 0)
475  {
476  return 1;
477  }
478 
479  ofstream gltf(file_prefix + ".gltf");
480  gltf.precision(8);
481  gltf.setf(ios::boolalpha);
482 
483  // ~~~ Tutorial ~~~
484  // https://github.com/KhronosGroup/glTF-Tutorials/blob/master/gltfTutorial/README.md
485 
486  // https://www.khronos.org/registry/glTF/specs/2.0/glTF-2.0.html#reference-scene
487  gltf <<
488  "{\n"
489  " \"scene\": 0,\n"
490  " \"scenes\" : [ {\n"
491  " \"nodes\" : [";
492  for (size_t i = 0; i != nodes.size(); ++i) { gltf << sep(i) << ' ' << i; }
493  gltf <<
494  " ]\n"
495  " } ],\n\n";
496 
497  // https://www.khronos.org/registry/glTF/specs/2.0/glTF-2.0.html#reference-node
498  gltf << " \"nodes\" : [";
499  for (size_t i = 0; i != nodes.size(); ++i)
500  {
501  gltf << sep(i) << " {";
502  int pos = 0;
503  print_node(gltf, pos, "\n ", nodes[i].name);
504  print_node(gltf, pos, "\n ", nodes[i].mesh);
505  print_node(gltf, pos, "\n ", nodes[i].scale);
506  print_node(gltf, pos, "\n ", nodes[i].translation);
507  gltf << "\n }";
508  }
509  gltf << " ],\n\n";
510 
511  // https://www.khronos.org/registry/glTF/specs/2.0/glTF-2.0.html#reference-mesh
512  gltf << " \"meshes\" : [";
513  for (size_t i = 0; i != meshes.size(); ++i)
514  {
515  gltf << sep(i) << " {";
516  int pos = 0;
517  print_node(gltf, pos, "\n ", meshes[i].name);
518  gltf << sep(pos++) << "\n \"primitives\" : [";
519  auto &primitives = meshes[i].primitives;
520  for (size_t j = 0; j != primitives.size(); ++j)
521  {
522  gltf << sep(j) << " {";
523  int pos2 = 0;
524  print_node(gltf, pos2, "\n ", primitives[j].attributes);
525  print_node(gltf, pos2, "\n ", primitives[j].indices);
526  print_node(gltf, pos2, "\n ", primitives[j].material);
527  print_node(gltf, pos2, "\n ", primitives[j].mode);
528  gltf << "\n }";
529  }
530  gltf << " ]\n }";
531  }
532  gltf << " ],\n\n";
533 
534  // https://www.khronos.org/registry/glTF/specs/2.0/glTF-2.0.html#reference-material
535  gltf << " \"materials\" : [";
536  for (size_t i = 0; i != materials.size(); ++i)
537  {
538  gltf << sep(i) << " {";
539  int pos = 0;
540  print_node(gltf, pos, "\n ", materials[i].name);
541  print_node(gltf, pos, "\n ", materials[i].pbrMetallicRoughness);
542  print_node(gltf, pos, "\n ", materials[i].doubleSided);
543  gltf << "\n }";
544  }
545  gltf << " ],\n\n";
546 
547  gltf << " \"textures\" : [";
548  for (size_t i = 0; i != textures.size(); ++i)
549  {
550  gltf << sep(i) << " {";
551  int pos = 0;
552  print_node(gltf, pos, "\n ", textures[i].sampler);
553  print_node(gltf, pos, "\n ", textures[i].source);
554  gltf << "\n }";
555  }
556  gltf << " ],\n\n";
557 
558  gltf << " \"images\" : [";
559  for (size_t i = 0; i != images.size(); ++i)
560  {
561  gltf << sep(i) << " {";
562  int pos = 0;
563  print_node(gltf, pos, "\n ", images[i].name);
564  print_node(gltf, pos, "\n ", images[i].uri);
565  gltf << "\n }";
566  }
567  gltf << " ],\n\n";
568 
569  // https://www.khronos.org/registry/glTF/specs/2.0/glTF-2.0.html#reference-sampler
570  // see also: PaletteState::{ToTextureDiscrete(),ToTextureSmooth()}
571  gltf << " \"samplers\" : [";
572  for (size_t i = 0; i != samplers.size(); ++i)
573  {
574  gltf << sep(i) << " {";
575  int pos = 0;
576  print_node(gltf, pos, "\n ", samplers[i].magFilter);
577  print_node(gltf, pos, "\n ", samplers[i].minFilter);
578  print_node(gltf, pos, "\n ", samplers[i].wrapS);
579  print_node(gltf, pos, "\n ", samplers[i].wrapT);
580  gltf << "\n }";
581  }
582  gltf << " ],\n\n";
583 
584  // https://www.khronos.org/registry/glTF/specs/2.0/glTF-2.0.html#reference-buffer
585  gltf << " \"buffers\" : [";
586  for (size_t i = 0; i != buffers.size(); ++i)
587  {
588  gltf << sep(i) << " {";
589  int pos = 0;
590  print_node(gltf, pos, "\n ", buffers[i].uri);
591  print_node(gltf, pos, "\n ", buffers[i].byteLength);
592  gltf << "\n }";
593  }
594  gltf << " ],\n\n";
595 
596  // https://www.khronos.org/registry/glTF/specs/2.0/glTF-2.0.html#reference-bufferview
597  gltf << " \"bufferViews\" : [";
598  for (size_t i = 0; i != buffer_views.size(); ++i)
599  {
600  gltf << sep(i) << " {";
601  int pos = 0;
602  print_node(gltf, pos, "\n ", buffer_views[i].buffer);
603  print_node(gltf, pos, "\n ", buffer_views[i].byteOffset);
604  print_node(gltf, pos, "\n ", buffer_views[i].byteLength);
605  print_node(gltf, pos, "\n ", buffer_views[i].byteStride);
606  print_node(gltf, pos, "\n ", buffer_views[i].target);
607  gltf << "\n }";
608  }
609  gltf << " ],\n\n";
610 
611  // https://www.khronos.org/registry/glTF/specs/2.0/glTF-2.0.html#reference-accessor
612  gltf << " \"accessors\" : [";
613  for (size_t i = 0; i != accessors.size(); ++i)
614  {
615  gltf << sep(i) << " {";
616  int pos = 0;
617  print_node(gltf, pos, "\n ", accessors[i].bufferView);
618  print_node(gltf, pos, "\n ", accessors[i].byteOffset);
619  print_node(gltf, pos, "\n ", accessors[i].componentType);
620  print_node(gltf, pos, "\n ", accessors[i].count);
621  print_node(gltf, pos, "\n ", accessors[i].type);
622  print_node(gltf, pos, "\n ", accessors[i].min);
623  print_node(gltf, pos, "\n ", accessors[i].max);
624  print_node(gltf, pos, "\n ", accessors[i].normalized);
625  gltf << "\n }";
626  }
627  gltf << " ],\n\n";
628 
629  // https://www.khronos.org/registry/glTF/specs/2.0/glTF-2.0.html#reference-asset
630  gltf <<
631  " \"asset\" : {\n"
632  " \"version\" : \"2.0\",\n"
633  " \"generator\" : \"GLVis\"\n"
634  " }\n"
635  "}\n";
636 
637  return 0;
638 }
material_id addMaterial(const std::string &materialName, const pbr_matallic_roughness &pbrMetallicRoughness, bool doubleSided=false)
Definition: gltf.cpp:276
void addNodeScale(node_id node, vec3f scale)
Definition: gltf.cpp:441
accessor_id addAccessorVec3f(buffer_view_id bufferView, size_t byteOffset, size_t count, vec3f min, vec3f max)
Definition: gltf.cpp:165
buffer_view_id addBufferView(buffer_id buffer, const void *data, size_t byteLength, size_t byteStride, size_t byteAlign, target_type target)
Definition: gltf.cpp:38
sampler_id addSampler(mag_filter magFilter=mag_filter::NEAREST, min_filter minFilter=min_filter::NEAREST, wrap_type wrapS=wrap_type::CLAMP_TO_EDGE, wrap_type wrapT=wrap_type::CLAMP_TO_EDGE)
Definition: gltf.cpp:232
void appendToBufferView(buffer_view_id bufferView, const void *data, size_t byteLength)
Definition: gltf.cpp:81
void addMeshLines(mesh_id mesh, accessor_id vertexPositions, accessor_id vertexTexcoords0, accessor_id vertexColors0, material_id material)
Definition: gltf.cpp:379
void addNodeTranslation(node_id node, vec3f translation)
Definition: gltf.cpp:449
void addMeshTriangles(mesh_id mesh, accessor_id vertexPositions, accessor_id vertexNormals, accessor_id vertexTexCoords0, accessor_id vertexIndices, material_id material)
Definition: gltf.cpp:333
buffer_id addBuffer(const std::string &bufferName)
Definition: gltf.cpp:24
std::array< float, 3 > vec3f
Definition: gltf.hpp:28
texture_id addTexture(sampler_id sampler, image_id source)
Definition: gltf.cpp:256
mesh_id addMesh(const std::string &meshName)
Definition: gltf.cpp:322
int SaveAsPNG(const char *fname, int w, int h, bool is_hidpi, bool with_alpha, std::function< void(int, void *)> get_row)
Definition: aux_vis.cpp:895
node_id addNode(const std::string &nodeName)
Definition: gltf.cpp:422
image_id addImage(const std::string &imageName, int width, int height, const color4f *pixels)
Definition: gltf.cpp:190
accessor_id addAccessor(buffer_view_id bufferView, size_t byteOffset, component_type componentType, size_t count, tensor_type tensorType)
Definition: gltf.cpp:97
int writeFile()
Definition: gltf.cpp:472
std::array< float, 4 > color4f
Definition: gltf.hpp:29
void getMaterialPBRMR(material_id material, pbr_matallic_roughness &pbr_mr_copy)
Definition: gltf.cpp:457
Material materials[5]
Definition: material.cpp:14
void addNodeMesh(node_id node, mesh_id mesh)
Definition: gltf.cpp:433
std::array< float, 2 > vec2f
Definition: gltf.hpp:27
static const char * tensorTypes[]
Definition: gltf.hpp:252
accessor_id addAccessorVec2f(buffer_view_id bufferView, size_t byteOffset, size_t count, vec2f min, vec2f max)
Definition: gltf.cpp:140