TDME2  1.9.200
GLTFReader.cpp
Go to the documentation of this file.
2 
3 #include <map>
4 #include <memory>
5 #include <set>
6 #include <string>
7 #include <vector>
8 
9 #define TINYGLTF_IMPLEMENTATION
10 #define STB_IMAGE_IMPLEMENTATION
11 #define STB_IMAGE_WRITE_IMPLEMENTATION
12 
13 #include <ext/libpng/png.h>
14 #include <ext/tinygltf/tiny_gltf.h>
15 
16 #include <tdme/tdme.h>
17 #include <tdme/engine/Texture.h>
19 #include <tdme/engine/Color4.h>
20 #include <tdme/engine/model/Face.h>
26 #include <tdme/engine/model/Node.h>
33 #include <tdme/math/Matrix4x4.h>
34 #include <tdme/math/Quaternion.h>
35 #include <tdme/math/Vector3.h>
40 #include <tdme/utilities/Console.h>
45 
46 using std::make_unique;
47 using std::map;
48 using std::set;
49 using std::string;
50 using std::to_string;
51 using std::unique_ptr;
52 using std::vector;
53 
55 
84 
85 Model* GLTFReader::read(const string& pathName, const string& fileName, bool useBC7TextureCompression)
86 {
87  // hello
88  Console::println("GLTFReader::read(): Loading model: " + pathName + "/" + fileName);
89 
90  // parse model
91  string err;
92  string warn;
93  tinygltf::Model gltfModel;
94  tinygltf::TinyGLTF gltfLoader;
95  auto success = false;
96  if (StringTools::endsWith(fileName, ".glb") == true) {
97  vector<uint8_t> glftBinaryData;
98  FileSystem::getInstance()->getContent(pathName, fileName, glftBinaryData);
99  success = gltfLoader.LoadBinaryFromMemory(&gltfModel, &err, &warn, glftBinaryData.data(), glftBinaryData.size());
100  } else
101  if (StringTools::endsWith(fileName, ".gltf") == true) {
102  string glftASCIIData = FileSystem::getInstance()->getContentAsString(pathName, fileName);
103  success = gltfLoader.LoadASCIIFromString(&gltfModel, &err, &warn, glftASCIIData.c_str(), glftASCIIData.size(), pathName.c_str());
104  } else {
105  Console::println("GLTFReader::read(): File not supported");
106  return nullptr;
107  }
108 
109  if (warn.empty() == false) Console::println("GLTFReader::read(): warnings: " + warn);
110  if (err.empty() == false) Console::println("GLTFReader::read(): errors: " + err);
111  if (success == false){
112  Console::println("GLTFReader::read(): Failed to load model: " + pathName + "/" + fileName);
113  return nullptr;
114  }
115 
116  // create model
117  auto model = make_unique<Model>(
118  fileName,
119  fileName,
120  UpVector::Y_UP,
121  RotationOrder::ZYX,
122  nullptr,
124  );
125  model->setShaderModel(ShaderModel::SPECULARPBR);
126  model->setEmbedSpecularTextures(true);
127  model->setEmbedPBRTextures(true);
128 
129  // parse nodes aka scene
130  int anonymousNodeIdx = 1;
131  for (const auto& gltfScene: gltfModel.scenes) {
132  for (const auto gltfNodeIdx: gltfScene.nodes) {
133  const auto& glTfNode = gltfModel.nodes[gltfNodeIdx];
134  auto node = parseNode(pathName, fileName, gltfModel, gltfNodeIdx, model.get(), nullptr, anonymousNodeIdx, useBC7TextureCompression);
135  model->getNodes()[node->getId()] = node;
136  if (model->getSubNodes().find(node->getId()) != model->getSubNodes().end()) {
137  Console::println("GLTFReader::read(): node already exists: " + node->getId());
138  }
139  model->getSubNodes()[node->getId()] = node;
140  if (glTfNode.children.empty() == false) parseNodeChildren(pathName, fileName, gltfModel, glTfNode.children, node, anonymousNodeIdx, useBC7TextureCompression);
141  }
142  }
143 
144  // animations
145  auto maxFrames = 0;
146  {
147  set<string> animationNodes;
148  map<string, vector<Matrix4x4>> animationScaleMatrices;
149  map<string, vector<Matrix4x4>> animationRotationMatrices;
150  map<string, vector<Matrix4x4>> animationTranslationMatrices;
151  for (const auto& gltfAnimation: gltfModel.animations) {
152  auto frames = 0;
153  for (const auto& gltfChannel: gltfAnimation.channels) {
154  auto gltfNodeName = gltfModel.nodes[gltfChannel.target_node].name;
155  auto node = model->getNodeById(gltfNodeName);
156  if (node == nullptr) {
157  Console::println("GLTFReader::read(): '" + gltfNodeName + "': animation: " + gltfAnimation.name + ": Could not find animation node");
158  continue;
159  }
160  animationNodes.insert(node->getId());
161  const auto& gltfSample = gltfAnimation.samplers[gltfChannel.sampler];
162  // animation input: key frame time stamps
163  const auto& animationInputAccessor = gltfModel.accessors[gltfSample.input];
164  const auto& animationInputBufferView = gltfModel.bufferViews[animationInputAccessor.bufferView];
165  const auto& animationInputBuffer = gltfModel.buffers[animationInputBufferView.buffer];
166  const auto animationInputBufferData = (const float*)(animationInputBuffer.data.data() + animationInputAccessor.byteOffset + animationInputBufferView.byteOffset);
167  if (animationInputAccessor.componentType != TINYGLTF_COMPONENT_TYPE_FLOAT) {
168  Console::println("GLTFReader::read(): " + node->getId() + ": animation: " + gltfAnimation.name + ": Invalid input attributes component: " + getComponentTypeString(animationInputAccessor.componentType) + ", with size: " + to_string(getComponentTypeByteSize(animationInputAccessor.componentType)));
169  continue;
170  }
171  auto channelFrames = Math::max(1, static_cast<int32_t>(Math::ceil(animationInputBufferData[animationInputAccessor.count - 1] * 30.0f)));
172  // animation output: translation, rotation, scale
173  const auto& animationOutputAccessor = gltfModel.accessors[gltfSample.output];
174  const auto& animationOutputBufferView = gltfModel.bufferViews[animationOutputAccessor.bufferView];
175  const auto& animationOutputBuffer = gltfModel.buffers[animationOutputBufferView.buffer];
176  const auto animationOutputBufferData = (const float*)(animationOutputBuffer.data.data() + animationOutputAccessor.byteOffset + animationOutputBufferView.byteOffset);
177  if (animationOutputAccessor.componentType != TINYGLTF_COMPONENT_TYPE_FLOAT) {
178  Console::println("GLTFReader::read(): " + node->getId() + ": animation: " + gltfAnimation.name + ": Invalid output attributes component: " + getComponentTypeString(animationOutputAccessor.componentType) + ", with size: " + to_string(getComponentTypeByteSize(animationOutputAccessor.componentType)));
179  continue;
180  }
181  auto& scaleMatrices = animationScaleMatrices[node->getId()];
182  if (maxFrames + channelFrames > scaleMatrices.size()) {
183  while (scaleMatrices.size() < maxFrames + channelFrames) scaleMatrices.emplace_back(getNodeScaleMatrix(gltfModel, node->getId()));
184  }
185  auto& rotationMatrices = animationRotationMatrices[node->getId()];
186  if (maxFrames + channelFrames > rotationMatrices.size()) {
187  while (rotationMatrices.size() < maxFrames + channelFrames) rotationMatrices.emplace_back(getNodeRotationMatrix(gltfModel, node->getId()));
188  }
189  auto& translationMatrices = animationTranslationMatrices[node->getId()];
190  if (maxFrames + channelFrames > translationMatrices.size()) {
191  while (translationMatrices.size() < maxFrames + channelFrames) translationMatrices.emplace_back(getNodeTranslationMatrix(gltfModel, node->getId()));
192  }
193  if (gltfChannel.target_path == "translation") {
194  if (animationOutputAccessor.type != TINYGLTF_TYPE_VEC3) {
195  Console::println("GLTFReader::read(): " + node->getId() + ": animation: " + gltfAnimation.name + ": Invalid translation channel output type: " + getTypeString(animationOutputAccessor.type) + ", expected: Vector3");
196  continue;
197  }
198  vector<Matrix4x4> keyFrameMatrices(animationOutputAccessor.count);
199  for (auto i = 0; i < animationOutputAccessor.count; i++) {
200  keyFrameMatrices[i].identity();
201  keyFrameMatrices[i].setTranslation(Vector3(animationOutputBufferData[i * 3 + 0], animationOutputBufferData[i * 3 + 1], animationOutputBufferData[i * 3 + 2]));
202  }
203  interpolateKeyFrames(animationInputAccessor.count, animationInputBufferData, keyFrameMatrices, channelFrames, translationMatrices, maxFrames);
204  } else
205  if (gltfChannel.target_path == "rotation") {
206  if (animationOutputAccessor.type != TINYGLTF_TYPE_VEC4) {
207  Console::println("GLTFReader::read(): " + node->getId() + ": animation: " + gltfAnimation.name + ": Invalid rotation channel output type: " + getTypeString(animationOutputAccessor.type) + ", expected: Vector4");
208  continue;
209  }
210  vector<Matrix4x4> keyFrameMatrices(animationOutputAccessor.count);
211  Quaternion rotationQuaternion;
212  for (auto i = 0; i < animationOutputAccessor.count; i++) {
213  rotationQuaternion.set(animationOutputBufferData[i * 4 + 0], animationOutputBufferData[i * 4 + 1], animationOutputBufferData[i * 4 + 2], animationOutputBufferData[i * 4 + 3]);
214  keyFrameMatrices[i] = rotationQuaternion.computeMatrix();
215  }
216  interpolateKeyFrames(animationInputAccessor.count, animationInputBufferData, keyFrameMatrices, channelFrames, rotationMatrices, maxFrames);
217  } else
218  if (gltfChannel.target_path == "scale") {
219  if (animationOutputAccessor.type != TINYGLTF_TYPE_VEC3) {
220  Console::println("GLTFReader::read(): " + node->getId() + ": animation: " + gltfAnimation.name + ": Invalid scale channel output type: " + getTypeString(animationOutputAccessor.type) + ", expected: Vector3");
221  continue;
222  }
223  vector<Matrix4x4> keyFrameMatrices(animationOutputAccessor.count);
224  for (auto i = 0; i < animationOutputAccessor.count; i++) {
225  keyFrameMatrices[i].identity();
226  keyFrameMatrices[i].scale(Vector3(animationOutputBufferData[i * 3 + 0], animationOutputBufferData[i * 3 + 1], animationOutputBufferData[i * 3 + 2]));
227  }
228  interpolateKeyFrames(animationInputAccessor.count, animationInputBufferData, keyFrameMatrices, channelFrames, scaleMatrices, maxFrames);
229  } else {
230  Console::println("GLTFReader::GLTFReader(): " + gltfAnimation.name + ": Invalid target path:" + gltfChannel.target_path);
231  }
232  if (channelFrames > frames) frames = channelFrames;
233  }
234  model->addAnimationSetup(gltfAnimation.name, maxFrames, maxFrames + frames - 1, false);
235  maxFrames+= frames;
236  }
237 
238  // extend all animation matrices to max frames
239  for (auto& [nodeId, animationMatrices]: animationScaleMatrices) {
240  while (animationMatrices.size() < maxFrames) animationMatrices.emplace_back(getNodeScaleMatrix(gltfModel, nodeId));
241  }
242  for (auto& [nodeId, animationMatrices]: animationRotationMatrices) {
243  while (animationMatrices.size() < maxFrames) animationMatrices.emplace_back(getNodeRotationMatrix(gltfModel, nodeId));
244  }
245  for (auto& [nodeId, animationMatrices]: animationTranslationMatrices) {
246  while (animationMatrices.size() < maxFrames) animationMatrices.emplace_back(getNodeTranslationMatrix(gltfModel, nodeId));
247  }
248 
249  // set up nodes animations if we have frames
250  for (const auto& animationNode: animationNodes) {
251  auto node = model->getNodeById(animationNode);
252  if (node == nullptr) {
253  Console::println("GLTFReader::GLTFReader(): animation: node not found:" + animationNode);
254  continue;
255  }
256  //
257  const auto& nodeAnimationScaleMatrices = animationScaleMatrices[node->getId()];
258  const auto& nodeAnimationRotationMatrices = animationRotationMatrices[node->getId()];
259  const auto& nodeAnimationTranslationMatrices = animationTranslationMatrices[node->getId()];
260 
261  //
262  vector<Matrix4x4> animationFinalMatrices(maxFrames);
263  for (auto i = 0; i < maxFrames; i++) {
264  animationFinalMatrices[i].set(nodeAnimationScaleMatrices[i]);
265  animationFinalMatrices[i].multiply(nodeAnimationRotationMatrices[i]);
266  animationFinalMatrices[i].multiply(nodeAnimationTranslationMatrices[i]);
267  }
268  auto animation = make_unique<Animation>();
269  animation->setTransformMatrices(animationFinalMatrices);
270  node->setAnimation(animation.release());
271  }
272  }
273 
274  // set up joints
275  ModelTools::setupJoints(model.get());
276 
277  // check if to compute normals
278  {
279  auto computeNormals = false;
280  for (const auto& [nodeId, node]: model->getNodes()) {
281  if (node->getVertices().size() != node->getNormals().size()) {
282  computeNormals = true;
283  }
284  }
285  if (computeNormals == true) {
286  Console::println("GLTFReader::read(): Computing normals, as they were missing or mismatching vertex count");
287  ModelTools::computeNormals(model.get());
288  }
289  }
290 
291  // compute tangents and bitangents
292  for (const auto& [nodeId, node]: model->getSubNodes()) {
294  }
295 
296  // lets prepare for indexed rendering, or disable it later, as it makes not much sense with tangents and bitangents
297  ModelTools::prepareForIndexedRendering(model.get());
298 
299  // create default animations
300  ModelTools::createDefaultAnimation(model.get(), maxFrames);
301 
302  // fix animation length
303  ModelTools::fixAnimationLength(model.get());
304 
305  //
306  return model.release();
307 }
308 
309 void GLTFReader::interpolateKeyFrames(int frameTimeCount, const float* frameTimes, const vector<Matrix4x4>& keyFrameMatrices, int interpolatedMatrixCount, vector<Matrix4x4>& interpolatedMatrices, int frameStartIdx) {
310  auto keyFrameIdx = 0;
311  auto frameIdx = 0;
312  auto timeStampLast = frameTimes[keyFrameIdx];
313  auto tansformationsMatrixLast = &keyFrameMatrices[keyFrameIdx];
314  interpolatedMatrices[frameStartIdx + frameIdx++] = keyFrameMatrices[keyFrameIdx++];
315  for (auto i = 1; i < frameTimeCount; i++) {
316  auto keyFrameTime = frameTimes[i];
317  auto transformMatrixCurrent = &keyFrameMatrices[(keyFrameIdx) % keyFrameMatrices.size()];
318  float timeStamp;
319  for (timeStamp = timeStampLast; timeStamp < keyFrameTime; timeStamp += 1.0f / 30.0f) {
320  if (frameIdx >= interpolatedMatrixCount) {
321  // TODO: check me again!
322  // Console::println(string("Warning: skipping frame: ") + to_string(frameIdx));
323  frameIdx++;
324  continue;
325  }
326  // Console::println("yyy: " + to_string(frameStartIdx + frameIdx) + ": key frame idx: " + to_string(keyFrameIdx) + ", interpolation t: " + to_string((timeStamp - timeStampLast) / (keyFrameTime - timeStampLast)));
327  interpolatedMatrices[frameStartIdx + frameIdx] = Matrix4x4::interpolateLinear(*tansformationsMatrixLast, *transformMatrixCurrent, (timeStamp - timeStampLast) / (keyFrameTime - timeStampLast));
328  frameIdx++;
329  }
330  timeStampLast = timeStamp;
331  tansformationsMatrixLast = transformMatrixCurrent;
332  keyFrameIdx++;
333  }
334 }
335 
336 Node* GLTFReader::parseNode(const string& pathName, const string& fileName, tinygltf::Model& gltfModel, int gltfNodeIdx, Model* model, Node* parentNode, int& anonymousNodeIdx, bool useBC7TextureCompression) {
337  auto& gltfNode = gltfModel.nodes[gltfNodeIdx];
338  // this fixes nodes that have no name
339  auto nodeId = gltfNode.name.empty() == true?"<" + to_string(anonymousNodeIdx++) + ">":gltfNode.name;
340  gltfNode.name = nodeId;
341  //
342  auto node = make_unique<Node>(model, parentNode, nodeId, nodeId);
343  if (gltfNode.matrix.size() == 16) {
344  node->setTransformMatrix(
345  Matrix4x4(
346  static_cast<float>(gltfNode.matrix[0]),
347  static_cast<float>(gltfNode.matrix[1]),
348  static_cast<float>(gltfNode.matrix[2]),
349  static_cast<float>(gltfNode.matrix[3]),
350  static_cast<float>(gltfNode.matrix[4]),
351  static_cast<float>(gltfNode.matrix[5]),
352  static_cast<float>(gltfNode.matrix[6]),
353  static_cast<float>(gltfNode.matrix[7]),
354  static_cast<float>(gltfNode.matrix[8]),
355  static_cast<float>(gltfNode.matrix[9]),
356  static_cast<float>(gltfNode.matrix[10]),
357  static_cast<float>(gltfNode.matrix[11]),
358  static_cast<float>(gltfNode.matrix[12]),
359  static_cast<float>(gltfNode.matrix[13]),
360  static_cast<float>(gltfNode.matrix[14]),
361  static_cast<float>(gltfNode.matrix[15])
362  )
363  );
364  } else {
365  Matrix4x4 nodeScaleMatrix;
366  Matrix4x4 nodeRotationMatrix;
367  Matrix4x4 nodeTranslationMatrix;
368  nodeScaleMatrix.identity();
369  nodeRotationMatrix.identity();
370  nodeTranslationMatrix.identity();
371  if (gltfNode.scale.size() == 3) {
372  nodeScaleMatrix.scale(Vector3(gltfNode.scale[0], gltfNode.scale[1], gltfNode.scale[2]));
373  }
374  if (gltfNode.rotation.size() == 4) {
375  Quaternion rotationQuaternion(gltfNode.rotation[0], gltfNode.rotation[1], gltfNode.rotation[2], gltfNode.rotation[3]);
376  nodeRotationMatrix = rotationQuaternion.computeMatrix();
377  }
378  if (gltfNode.translation.size() == 3) {
379  nodeTranslationMatrix.setTranslation(Vector3(gltfNode.translation[0], gltfNode.translation[1], gltfNode.translation[2]));
380  }
381  Matrix4x4 nodeTransformMatrix;
382  nodeTransformMatrix.set(nodeScaleMatrix);
383  nodeTransformMatrix.multiply(nodeRotationMatrix);
384  nodeTransformMatrix.multiply(nodeTranslationMatrix);
385  node->setTransformMatrix(nodeTransformMatrix);
386  }
387  if (gltfNode.mesh == -1) return node.release();
388  vector<int> joints;
389  vector<float> weights;
390  vector<Vector3> vertices;
391  vector<Vector3> normals;
392  vector<Vector2> textureCoordinates;
393  vector<FacesEntity> facesEntities;
394  const auto& mesh = gltfModel.meshes[gltfNode.mesh];
395  int facesEntityIdx = 0;
396  for (const auto& gltfPrimitive: mesh.primitives) {
397  Material* material = nullptr;
398  if (gltfPrimitive.material != -1) {
399  const auto& gltfMaterial = gltfModel.materials[gltfPrimitive.material];
400  const auto& gltfMaterialName = gltfMaterial.name;
401  auto materialIt = model->getMaterials().find(gltfMaterialName);
402  if (materialIt != model->getMaterials().end()) {
403  material = materialIt->second;
404  } else {
405  auto newMaterial = make_unique<Material>(gltfMaterial.name);
406  newMaterial->setDoubleSided(false/*TODO: enable me: gltfMaterial.doubleSided*/);
407  auto pbrMaterialProperties = make_unique<PBRMaterialProperties>();
408  auto specularMaterialProperties = make_unique<SpecularMaterialProperties>();
409  // some adjustment, lets see if we can extract this later
410  specularMaterialProperties->setAmbientColor(Color4(0.8f, 0.8f, 0.8f, 1.0f));
411  specularMaterialProperties->setDiffuseColor(Color4(0.2f, 0.2f, 0.2f, 1.0f));
412  if (gltfMaterial.values.find("baseColorFactor") != gltfMaterial.values.end()) {
413  const auto& gltfMaterialBaseColorFactor = gltfMaterial.values.find("baseColorFactor")->second;
414  Console::println(
415  "GLTFReader::parseNode(): " +
416  node->getId() + ": " +
417  "have base color factor with " +
418  to_string(gltfMaterialBaseColorFactor.number_array[0]) + ", " +
419  to_string(gltfMaterialBaseColorFactor.number_array[1]) + ", " +
420  to_string(gltfMaterialBaseColorFactor.number_array[2]) + ", " +
421  to_string(gltfMaterialBaseColorFactor.number_array[3])
422  );
423  pbrMaterialProperties->setBaseColorFactor(
424  Color4(
425  gltfMaterialBaseColorFactor.number_array[0],
426  gltfMaterialBaseColorFactor.number_array[1],
427  gltfMaterialBaseColorFactor.number_array[2],
428  gltfMaterialBaseColorFactor.number_array[3]
429  )
430  );
431  specularMaterialProperties->setAmbientColor(
432  Color4(
433  specularMaterialProperties->getAmbientColor().getRed() * gltfMaterialBaseColorFactor.number_array[0],
434  specularMaterialProperties->getAmbientColor().getGreen() * gltfMaterialBaseColorFactor.number_array[1],
435  specularMaterialProperties->getAmbientColor().getBlue() * gltfMaterialBaseColorFactor.number_array[2],
436  gltfMaterialBaseColorFactor.number_array[3]
437  )
438  );
439  specularMaterialProperties->setDiffuseColor(
440  Color4(
441  specularMaterialProperties->getDiffuseColor().getRed() * gltfMaterialBaseColorFactor.number_array[0],
442  specularMaterialProperties->getDiffuseColor().getGreen() * gltfMaterialBaseColorFactor.number_array[1],
443  specularMaterialProperties->getDiffuseColor().getBlue() * gltfMaterialBaseColorFactor.number_array[2],
444  gltfMaterialBaseColorFactor.number_array[3]
445  )
446  );
447  }
448  if (gltfMaterial.values.find("metallicFactor") != gltfMaterial.values.end()) {
449  const auto& gltfMaterialMatallicFactor = gltfMaterial.values.find("metallicFactor")->second;
450  Console::println(
451  "GLTFReader::parseNode(): " +
452  node->getId() + ": " +
453  "have metallic factor with " +
454  to_string(gltfMaterialMatallicFactor.number_value)
455  );
456  pbrMaterialProperties->setMetallicFactor(gltfMaterialMatallicFactor.number_value);
457  }
458  if (gltfMaterial.values.find("roughnessFactor") != gltfMaterial.values.end()) {
459  const auto& gltfMaterialRoughnessFactor = gltfMaterial.values.find("roughnessFactor")->second;
460  Console::println(
461  "GLTFReader::parseNode(): " +
462  node->getId() + ": " +
463  "have roughness factor with " +
464  to_string(gltfMaterialRoughnessFactor.number_value)
465  );
466  pbrMaterialProperties->setRoughnessFactor(gltfMaterialRoughnessFactor.number_value);
467  }
468  // we ignore for now Factor, ColorFactor, TextureScale, TextureStrength, TextureTexCoord as I do not see them feasible in Blender exported GLTF files
469  if (gltfMaterial.values.find("baseColorTexture") != gltfMaterial.values.end() &&
470  gltfMaterial.values.find("baseColorTexture")->second.TextureIndex() != -1) {
471  const auto& gltfMaterialBaseColorTexture = gltfMaterial.values.find("baseColorTexture")->second;
472  const auto& gltfTexture = gltfModel.textures[gltfMaterialBaseColorTexture.TextureIndex()];
473  const auto& image = gltfModel.images[gltfTexture.source];
474  try {
475  if (image.component != 3 && image.component != 4) throw ExceptionBase("We only support RGB or RGBA textures for now");
476  if (image.bits != 8) throw ExceptionBase("We only support 8 bit channels for now");
477  auto textureFileName = determineTextureFileName(pathName, fileName, image.name);
478  Console::println("GLTFReader::parseNode(): " + node->getId() + ": have base color texture with " + to_string(image.width) + " x " + to_string(image.height) + " x " + to_string(image.component) + " x " + to_string(image.bits) + ": " + fileName);
479  auto textureData = ByteBuffer(image.width * image.height * image.component * image.bits / 8);
480  for (int y = image.height - 1; y >= 0; y--) {
481  textureData.put(&image.image[y * image.width * image.component * image.bits / 8], image.width * image.component * image.bits / 8);
482  }
483  auto texture =
484  unique_ptr<
485  Texture,
486  decltype([](Texture* texture){ texture->releaseReference(); })
487  >(new Texture(
488  textureFileName,
489  Texture::getRGBDepthByPixelBitsPerPixel(image.bits * image.component),
490  Texture::getPNGFormatByPixelBitsPerPixel(image.bits * image.component),
491  image.width,
492  image.height,
493  image.width,
494  image.height,
495  Texture::getRGBFormatByPixelBitsPerPixel(image.bits * image.component),
496  textureData
497  )
498  );
499  texture->acquireReference();
500  texture->setUseCompression(useBC7TextureCompression);
501  //
502  pbrMaterialProperties->setBaseColorTexture(texture.get());
503  texture->acquireReference();
504  if (pbrMaterialProperties->hasBaseColorTextureTransparency() == true) pbrMaterialProperties->setBaseColorTextureMaskedTransparency(true);
505  specularMaterialProperties->setDiffuseTexture(texture.get());
506  texture->acquireReference();
507  if (specularMaterialProperties->hasDiffuseTextureTransparency() == true) specularMaterialProperties->setDiffuseTextureMaskedTransparency(true);
508  //
509  if (gltfTexture.sampler != -1) {
510  const auto& sampler = gltfModel.samplers[gltfTexture.sampler];
511  switch (sampler.minFilter) {
512  case TINYGLTF_TEXTURE_FILTER_NEAREST: texture->setMinFilter(Texture::TEXTUREFILTER_NEAREST); texture->setUseMipMap(false); break;
513  case TINYGLTF_TEXTURE_FILTER_LINEAR: texture->setMinFilter(Texture::TEXTUREFILTER_LINEAR); texture->setUseMipMap(false); break;
514  case TINYGLTF_TEXTURE_FILTER_NEAREST_MIPMAP_NEAREST: texture->setMinFilter(Texture::TEXTUREFILTER_NEAREST_MIPMAP_NEAREST); texture->setUseMipMap(true); break;
515  case TINYGLTF_TEXTURE_FILTER_LINEAR_MIPMAP_NEAREST: texture->setMinFilter(Texture::TEXTUREFILTER_LINEAR_MIPMAP_NEAREST); texture->setUseMipMap(true); break;
516  case TINYGLTF_TEXTURE_FILTER_NEAREST_MIPMAP_LINEAR: texture->setMinFilter(Texture::TEXTUREFILTER_NEAREST_MIPMAP_LINEAR); texture->setUseMipMap(true); break;
517  case TINYGLTF_TEXTURE_FILTER_LINEAR_MIPMAP_LINEAR: texture->setMinFilter(Texture::TEXTUREFILTER_LINEAR_MIPMAP_LINEAR); texture->setUseMipMap(true); break;
518  }
519  switch (sampler.magFilter) {
520  case TINYGLTF_TEXTURE_FILTER_NEAREST: texture->setMagFilter(Texture::TEXTUREFILTER_NEAREST); break;
521  case TINYGLTF_TEXTURE_FILTER_LINEAR: texture->setMagFilter(Texture::TEXTUREFILTER_LINEAR); break;
522  case TINYGLTF_TEXTURE_FILTER_NEAREST_MIPMAP_NEAREST: texture->setMagFilter(Texture::TEXTUREFILTER_NEAREST_MIPMAP_NEAREST); texture->setUseMipMap(true); break;
523  case TINYGLTF_TEXTURE_FILTER_LINEAR_MIPMAP_NEAREST: texture->setMagFilter(Texture::TEXTUREFILTER_LINEAR_MIPMAP_NEAREST); texture->setUseMipMap(true); break;
524  case TINYGLTF_TEXTURE_FILTER_NEAREST_MIPMAP_LINEAR: texture->setMagFilter(Texture::TEXTUREFILTER_NEAREST_MIPMAP_LINEAR); texture->setUseMipMap(true); break;
525  case TINYGLTF_TEXTURE_FILTER_LINEAR_MIPMAP_LINEAR: texture->setMagFilter(Texture::TEXTUREFILTER_LINEAR_MIPMAP_LINEAR); texture->setUseMipMap(true); break;
526  }
527  }
528  } catch (Exception& exception) {
529  Console::println("GLTFReader::parseNode(): " + node->getId() + ": An error occurred: " + exception.what());
530  }
531  }
532  if (gltfMaterial.values.find("metallicRoughnessTexture") != gltfMaterial.values.end() &&
533  gltfMaterial.values.find("metallicRoughnessTexture")->second.TextureIndex() != -1) {
534  const auto& gltfMetallicRoughnessTexture = gltfMaterial.values.find("metallicRoughnessTexture")->second;
535  const auto& gltfTexture = gltfModel.textures[gltfMetallicRoughnessTexture.TextureIndex()];
536  const auto& image = gltfModel.images[gltfTexture.source];
537  try {
538  if (image.component != 3 && image.component != 4) throw ExceptionBase("We only support RGB or RGBA textures for now");
539  if (image.bits != 8) throw ExceptionBase("We only support 8 bit channels for now");
540  auto textureFileName = determineTextureFileName(pathName, fileName, image.name);
541  Console::println("GLTFReader::parseNode(): " + node->getId() + ": have metallic roughness texture with " + to_string(image.width) + " x " + to_string(image.height) + " x " + to_string(image.component) + " x " + to_string(image.bits) + ": " + fileName);
542  auto textureData = ByteBuffer(image.width * image.height * image.component * image.bits / 8);
543  for (int y = image.height - 1; y >= 0; y--) {
544  textureData.put(&image.image[y * image.width * image.component * image.bits / 8], image.width * image.component * image.bits / 8);
545  }
546  auto texture =
547  unique_ptr<
548  Texture,
549  decltype([](Texture* texture){ texture->releaseReference(); })
550  >(new Texture(
551  textureFileName,
552  Texture::getRGBDepthByPixelBitsPerPixel(image.bits * image.component),
553  Texture::getPNGFormatByPixelBitsPerPixel(image.bits * image.component),
554  image.width,
555  image.height,
556  image.width,
557  image.height,
558  Texture::getRGBFormatByPixelBitsPerPixel(image.bits * image.component),
559  textureData
560  )
561  );
562  texture->acquireReference();
563  texture->setUseCompression(useBC7TextureCompression);
564  //
565  pbrMaterialProperties->setMetallicRoughnessTexture(texture.get());
566  texture->acquireReference();
567  //
568  if (gltfTexture.sampler != -1) {
569  const auto& sampler = gltfModel.samplers[gltfTexture.sampler];
570  switch (sampler.minFilter) {
571  case TINYGLTF_TEXTURE_FILTER_NEAREST: texture->setMinFilter(Texture::TEXTUREFILTER_NEAREST); texture->setUseMipMap(false); break;
572  case TINYGLTF_TEXTURE_FILTER_LINEAR: texture->setMinFilter(Texture::TEXTUREFILTER_LINEAR); texture->setUseMipMap(false); break;
573  case TINYGLTF_TEXTURE_FILTER_NEAREST_MIPMAP_NEAREST: texture->setMinFilter(Texture::TEXTUREFILTER_NEAREST_MIPMAP_NEAREST); texture->setUseMipMap(true); break;
574  case TINYGLTF_TEXTURE_FILTER_LINEAR_MIPMAP_NEAREST: texture->setMinFilter(Texture::TEXTUREFILTER_LINEAR_MIPMAP_NEAREST); texture->setUseMipMap(true); break;
575  case TINYGLTF_TEXTURE_FILTER_NEAREST_MIPMAP_LINEAR: texture->setMinFilter(Texture::TEXTUREFILTER_NEAREST_MIPMAP_LINEAR); texture->setUseMipMap(true); break;
576  case TINYGLTF_TEXTURE_FILTER_LINEAR_MIPMAP_LINEAR: texture->setMinFilter(Texture::TEXTUREFILTER_LINEAR_MIPMAP_LINEAR); texture->setUseMipMap(true); break;
577  }
578  switch (sampler.magFilter) {
579  case TINYGLTF_TEXTURE_FILTER_NEAREST: texture->setMagFilter(Texture::TEXTUREFILTER_NEAREST); break;
580  case TINYGLTF_TEXTURE_FILTER_LINEAR: texture->setMagFilter(Texture::TEXTUREFILTER_LINEAR); break;
581  case TINYGLTF_TEXTURE_FILTER_NEAREST_MIPMAP_NEAREST: texture->setMagFilter(Texture::TEXTUREFILTER_NEAREST_MIPMAP_NEAREST); texture->setUseMipMap(true); break;
582  case TINYGLTF_TEXTURE_FILTER_LINEAR_MIPMAP_NEAREST: texture->setMagFilter(Texture::TEXTUREFILTER_LINEAR_MIPMAP_NEAREST); texture->setUseMipMap(true); break;
583  case TINYGLTF_TEXTURE_FILTER_NEAREST_MIPMAP_LINEAR: texture->setMagFilter(Texture::TEXTUREFILTER_NEAREST_MIPMAP_LINEAR); texture->setUseMipMap(true); break;
584  case TINYGLTF_TEXTURE_FILTER_LINEAR_MIPMAP_LINEAR: texture->setMagFilter(Texture::TEXTUREFILTER_LINEAR_MIPMAP_LINEAR); texture->setUseMipMap(true); break;
585  }
586  }
587  } catch (Exception& exception) {
588  Console::println("GLTFReader::parseNode(): " + node->getId() + ": An error occurred: " + exception.what());
589  }
590  }
591  if (gltfMaterial.additionalValues.find("normalTexture") != gltfMaterial.additionalValues.end() &&
592  gltfMaterial.additionalValues.find("normalTexture")->second.TextureIndex() != -1) {
593  const auto& gltfNormalTexture = gltfMaterial.additionalValues.find("normalTexture")->second;
594  const auto& gltfTexture = gltfModel.textures[gltfNormalTexture.TextureIndex()];
595  const auto& image = gltfModel.images[gltfTexture.source];
596  try {
597  if (image.component != 3 && image.component != 4) throw ExceptionBase("We only support RGB or RGBA textures for now");
598  if (image.bits != 8) throw ExceptionBase("We only support 8 bit channels for now");
599  auto textureFileName = determineTextureFileName(pathName, fileName, image.name);
600  Console::println("GLTFReader::parseNode(): " + node->getId() + ": have normal texture with " + to_string(image.width) + " x " + to_string(image.height) + " x " + to_string(image.component) + " x " + to_string(image.bits) + ": " + fileName);
601  auto textureData = ByteBuffer(image.width * image.height * image.component * image.bits / 8);
602  for (int y = image.height - 1; y >= 0; y--) {
603  textureData.put(&image.image[y * image.width * image.component * image.bits / 8], image.width * image.component * image.bits / 8);
604  }
605  auto texture =
606  unique_ptr<
607  Texture,
608  decltype([](Texture* texture){ texture->releaseReference(); })
609  >(new Texture(
610  textureFileName,
611  Texture::getRGBDepthByPixelBitsPerPixel(image.bits * image.component),
612  Texture::getPNGFormatByPixelBitsPerPixel(image.bits * image.component),
613  image.width,
614  image.height,
615  image.width,
616  image.height,
617  Texture::getRGBFormatByPixelBitsPerPixel(image.bits * image.component),
618  textureData
619  )
620  );
621  texture->acquireReference();
622  texture->setUseCompression(useBC7TextureCompression);
623  //
624  pbrMaterialProperties->setNormalTexture(texture.get());
625  texture->acquireReference();
626  //
627  if (gltfTexture.sampler != -1) {
628  const auto& sampler = gltfModel.samplers[gltfTexture.sampler];
629  switch (sampler.minFilter) {
630  case TINYGLTF_TEXTURE_FILTER_NEAREST: texture->setMinFilter(Texture::TEXTUREFILTER_NEAREST); texture->setUseMipMap(false); break;
631  case TINYGLTF_TEXTURE_FILTER_LINEAR: texture->setMinFilter(Texture::TEXTUREFILTER_LINEAR); texture->setUseMipMap(false); break;
632  case TINYGLTF_TEXTURE_FILTER_NEAREST_MIPMAP_NEAREST: texture->setMinFilter(Texture::TEXTUREFILTER_NEAREST_MIPMAP_NEAREST); texture->setUseMipMap(true); break;
633  case TINYGLTF_TEXTURE_FILTER_LINEAR_MIPMAP_NEAREST: texture->setMinFilter(Texture::TEXTUREFILTER_LINEAR_MIPMAP_NEAREST); texture->setUseMipMap(true); break;
634  case TINYGLTF_TEXTURE_FILTER_NEAREST_MIPMAP_LINEAR: texture->setMinFilter(Texture::TEXTUREFILTER_NEAREST_MIPMAP_LINEAR); texture->setUseMipMap(true); break;
635  case TINYGLTF_TEXTURE_FILTER_LINEAR_MIPMAP_LINEAR: texture->setMinFilter(Texture::TEXTUREFILTER_LINEAR_MIPMAP_LINEAR); texture->setUseMipMap(true); break;
636  }
637  switch (sampler.magFilter) {
638  case TINYGLTF_TEXTURE_FILTER_NEAREST: texture->setMagFilter(Texture::TEXTUREFILTER_NEAREST); break;
639  case TINYGLTF_TEXTURE_FILTER_LINEAR: texture->setMagFilter(Texture::TEXTUREFILTER_LINEAR); break;
640  case TINYGLTF_TEXTURE_FILTER_NEAREST_MIPMAP_NEAREST: texture->setMagFilter(Texture::TEXTUREFILTER_NEAREST_MIPMAP_NEAREST); texture->setUseMipMap(true); break;
641  case TINYGLTF_TEXTURE_FILTER_LINEAR_MIPMAP_NEAREST: texture->setMagFilter(Texture::TEXTUREFILTER_LINEAR_MIPMAP_NEAREST); texture->setUseMipMap(true); break;
642  case TINYGLTF_TEXTURE_FILTER_NEAREST_MIPMAP_LINEAR: texture->setMagFilter(Texture::TEXTUREFILTER_NEAREST_MIPMAP_LINEAR); texture->setUseMipMap(true); break;
643  case TINYGLTF_TEXTURE_FILTER_LINEAR_MIPMAP_LINEAR: texture->setMagFilter(Texture::TEXTUREFILTER_LINEAR_MIPMAP_LINEAR); texture->setUseMipMap(true); break;
644  }
645  }
646  } catch (Exception& exception) {
647  Console::println("GLTFReader::parseNode(): " + node->getId() + ": An error occurred: " + exception.what());
648  }
649  }
650  if (gltfMaterial.emissiveTexture.index != -1) {
651  const auto& gltfTexture = gltfModel.textures[gltfMaterial.emissiveTexture.index];
652  const auto& image = gltfModel.images[gltfTexture.source];
653  try {
654  if (image.component != 3 && image.component != 4) throw ExceptionBase("We only support RGB or RGBA textures for now");
655  if (image.bits != 8) throw ExceptionBase("We only support 8 bit channels for now");
656  auto textureFileName = determineTextureFileName(pathName, fileName, image.name);
657  Console::println("GLTFReader::parseNode(): " + node->getId() + ": have emissive texture with " + to_string(image.width) + " x " + to_string(image.height) + " x " + to_string(image.component) + " x " + to_string(image.bits) + ": " + fileName);
658  auto textureData = ByteBuffer(image.width * image.height * image.component * image.bits / 8);
659  for (int y = image.height - 1; y >= 0; y--) {
660  textureData.put(&image.image[y * image.width * image.component * image.bits / 8], image.width * image.component * image.bits / 8);
661  }
662  auto texture =
663  unique_ptr<
664  Texture,
665  decltype([](Texture* texture){ texture->releaseReference(); })
666  >(new Texture(
667  textureFileName,
668  Texture::getRGBDepthByPixelBitsPerPixel(image.bits * image.component),
669  Texture::getPNGFormatByPixelBitsPerPixel(image.bits * image.component),
670  image.width,
671  image.height,
672  image.width,
673  image.height,
674  Texture::getRGBFormatByPixelBitsPerPixel(image.bits * image.component),
675  textureData
676  )
677  );
678  texture->acquireReference();
679  texture->setUseCompression(useBC7TextureCompression);
680  //
681  pbrMaterialProperties->setEmissiveFactor(Color4(gltfMaterial.emissiveFactor[0], gltfMaterial.emissiveFactor[1], gltfMaterial.emissiveFactor[2], 1.0f));
682  pbrMaterialProperties->setEmissiveTexture(texture.get());
683  texture->acquireReference();
684  //
685  if (gltfTexture.sampler != -1) {
686  const auto& sampler = gltfModel.samplers[gltfTexture.sampler];
687  switch (sampler.minFilter) {
688  case TINYGLTF_TEXTURE_FILTER_NEAREST: texture->setMinFilter(Texture::TEXTUREFILTER_NEAREST); texture->setUseMipMap(false); break;
689  case TINYGLTF_TEXTURE_FILTER_LINEAR: texture->setMinFilter(Texture::TEXTUREFILTER_LINEAR); texture->setUseMipMap(false); break;
690  case TINYGLTF_TEXTURE_FILTER_NEAREST_MIPMAP_NEAREST: texture->setMinFilter(Texture::TEXTUREFILTER_NEAREST_MIPMAP_NEAREST); texture->setUseMipMap(true); break;
691  case TINYGLTF_TEXTURE_FILTER_LINEAR_MIPMAP_NEAREST: texture->setMinFilter(Texture::TEXTUREFILTER_LINEAR_MIPMAP_NEAREST); texture->setUseMipMap(true); break;
692  case TINYGLTF_TEXTURE_FILTER_NEAREST_MIPMAP_LINEAR: texture->setMinFilter(Texture::TEXTUREFILTER_NEAREST_MIPMAP_LINEAR); texture->setUseMipMap(true); break;
693  case TINYGLTF_TEXTURE_FILTER_LINEAR_MIPMAP_LINEAR: texture->setMinFilter(Texture::TEXTUREFILTER_LINEAR_MIPMAP_LINEAR); texture->setUseMipMap(true); break;
694  }
695  switch (sampler.magFilter) {
696  case TINYGLTF_TEXTURE_FILTER_NEAREST: texture->setMagFilter(Texture::TEXTUREFILTER_NEAREST); break;
697  case TINYGLTF_TEXTURE_FILTER_LINEAR: texture->setMagFilter(Texture::TEXTUREFILTER_LINEAR); break;
698  case TINYGLTF_TEXTURE_FILTER_NEAREST_MIPMAP_NEAREST: texture->setMagFilter(Texture::TEXTUREFILTER_NEAREST_MIPMAP_NEAREST); texture->setUseMipMap(true); break;
699  case TINYGLTF_TEXTURE_FILTER_LINEAR_MIPMAP_NEAREST: texture->setMagFilter(Texture::TEXTUREFILTER_LINEAR_MIPMAP_NEAREST); texture->setUseMipMap(true); break;
700  case TINYGLTF_TEXTURE_FILTER_NEAREST_MIPMAP_LINEAR: texture->setMagFilter(Texture::TEXTUREFILTER_NEAREST_MIPMAP_LINEAR); texture->setUseMipMap(true); break;
701  case TINYGLTF_TEXTURE_FILTER_LINEAR_MIPMAP_LINEAR: texture->setMagFilter(Texture::TEXTUREFILTER_LINEAR_MIPMAP_LINEAR); texture->setUseMipMap(true); break;
702  }
703  }
704  } catch (Exception& exception) {
705  Console::println("GLTFReader::parseNode(): " + node->getId() + ": An error occurred: " + exception.what());
706  }
707  }
708  newMaterial->setSpecularMaterialProperties(specularMaterialProperties.release());
709  newMaterial->setPBRMaterialProperties(pbrMaterialProperties.release());
710  model->getMaterials()[newMaterial->getId()] = newMaterial.get();
711  //
712  material = newMaterial.release();
713  }
714  }
715  if (gltfPrimitive.mode != TINYGLTF_MODE_TRIANGLES) {
716  Console::println("GLTFReader::parseNode(): " + node->getId() + ": Invalid primitive mode: " + to_string(gltfPrimitive.mode));
717  continue;
718  }
719 
720  //
721  auto start = 0;
722  bool haveVertices = false;
723  bool haveNormals = false;
724  bool haveTextureCoordinates = false;
725  for (const auto& [gltfBufferType, gltfAttributeValue]: gltfPrimitive.attributes) {
726  const auto& attributeAccessor = gltfModel.accessors[gltfAttributeValue];
727  const auto& attributeBufferView = gltfModel.bufferViews[attributeAccessor.bufferView];
728  const auto& attributeBuffer = gltfModel.buffers[attributeBufferView.buffer];
729  if (gltfBufferType == "POSITION") {
730  if (attributeAccessor.componentType != TINYGLTF_COMPONENT_TYPE_FLOAT) {
731  Console::println("GLTFReader::parseNode(): " + node->getId() + ": POSITION: Invalid attributes component: " + to_string(attributeAccessor.componentType) + ", with size: " + to_string(getComponentTypeByteSize(attributeAccessor.componentType)));
732  continue;
733  }
734  auto stride = attributeBufferView.byteStride == 0?3 * sizeof(float) / sizeof(float):attributeBufferView.byteStride / sizeof(float);
735  haveVertices = true;
736  start = vertices.size();
737  if (start + attributeAccessor.count > vertices.size()) vertices.resize(start + attributeAccessor.count);
738  auto bufferData = (const float*)(attributeBuffer.data.data() + attributeAccessor.byteOffset + attributeBufferView.byteOffset);
739  for (auto i = 0; i < attributeAccessor.count; i++) {
740  vertices[start + i] = Vector3(bufferData[i * stride + 0], bufferData[i * stride + 1], bufferData[i * stride + 2]);
741  }
742  } else
743  if (gltfBufferType == "NORMAL") {
744  if (attributeAccessor.componentType != TINYGLTF_COMPONENT_TYPE_FLOAT) {
745  Console::println("GLTFReader::parseNode(): " + node->getId() + ": NORMAL: Invalid attributes component: " + to_string(attributeAccessor.componentType) + ", with size: " + to_string(getComponentTypeByteSize(attributeAccessor.componentType)));
746  continue;
747  }
748  auto stride = attributeBufferView.byteStride == 0?3 * sizeof(float) / sizeof(float):attributeBufferView.byteStride / sizeof(float);
749  haveNormals = true;
750  auto start = normals.size();
751  if (start + attributeAccessor.count > normals.size()) normals.resize(start + attributeAccessor.count);
752  auto bufferData = (const float*)(attributeBuffer.data.data() + attributeAccessor.byteOffset + attributeBufferView.byteOffset);
753  for (auto i = 0; i < attributeAccessor.count; i++) {
754  normals[start + i] = Vector3(bufferData[i * stride + 0], bufferData[i * stride + 1], bufferData[i * stride + 2]);
755  }
756  } else
757  if (gltfBufferType == "TEXCOORD_0") {
758  if (attributeAccessor.componentType != TINYGLTF_COMPONENT_TYPE_FLOAT) {
759  Console::println("GLTFReader::parseNode(): " + node->getId() + ": TEXTCOORD_0: Invalid attributes component: " + to_string(attributeAccessor.componentType) + ", with size: " + to_string(getComponentTypeByteSize(attributeAccessor.componentType)));
760  continue;
761  }
762  auto stride = attributeBufferView.byteStride == 0?2 * sizeof(float) / sizeof(float):attributeBufferView.byteStride / sizeof(float);
763  haveTextureCoordinates = true;
764  auto start = textureCoordinates.size();
765  if (start + attributeAccessor.count > textureCoordinates.size()) textureCoordinates.resize(start + attributeAccessor.count);
766  auto bufferData = (const float*)(attributeBuffer.data.data() + attributeAccessor.byteOffset + attributeBufferView.byteOffset);
767  for (auto i = 0; i < attributeAccessor.count; i++) {
768  textureCoordinates[start + i] = Vector2(bufferData[i * stride + 0], 1.0f - bufferData[i * stride + 1]);
769  }
770  } else
771  if (gltfBufferType == "COLOR_0") {
772  // ignored for now
773  } else
774  if (gltfBufferType == "WEIGHTS_0") {
775  if (attributeAccessor.componentType != TINYGLTF_COMPONENT_TYPE_FLOAT) {
776  Console::println("GLTFReader::parseNode(): " + node->getId() + ": WEIGHTS_0: Invalid attributes component: " + to_string(attributeAccessor.componentType) + ", with size: " + to_string(getComponentTypeByteSize(attributeAccessor.componentType)));
777  continue;
778  }
779  auto stride = attributeBufferView.byteStride == 0?4 * sizeof(float) / sizeof(float):attributeBufferView.byteStride / sizeof(float);
780  auto start = weights.size();
781  if (start + attributeAccessor.count * 4 > weights.size()) weights.resize(start + attributeAccessor.count * 4);
782  auto bufferData = (const float*)(attributeBuffer.data.data() + attributeAccessor.byteOffset + attributeBufferView.byteOffset);
783  for (auto i = 0; i < attributeAccessor.count; i++) {
784  weights[start + i * 4 + 0] = bufferData[i * stride + 0];
785  weights[start + i * 4 + 1] = bufferData[i * stride + 1];
786  weights[start + i * 4 + 2] = bufferData[i * stride + 2];
787  weights[start + i * 4 + 3] = bufferData[i * stride + 3];
788  }
789  } else
790  if (gltfBufferType == "JOINTS_0") {
791  if (attributeAccessor.componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE) {
792  auto stride = attributeBufferView.byteStride == 0?4 * sizeof(uint8_t) / sizeof(uint8_t):attributeBufferView.byteStride / sizeof(uint8_t);
793  auto start = joints.size();
794  if (start + attributeAccessor.count * 4 > joints.size()) joints.resize(start + attributeAccessor.count * 4);
795  auto bufferData = (const uint8_t*)(attributeBuffer.data.data() + attributeAccessor.byteOffset + attributeBufferView.byteOffset);
796  for (auto i = 0; i < attributeAccessor.count; i++) {
797  joints[start + i * 4 + 0] = bufferData[i * stride + 0];
798  joints[start + i * 4 + 1] = bufferData[i * stride + 1];
799  joints[start + i * 4 + 2] = bufferData[i * stride + 2];
800  joints[start + i * 4 + 3] = bufferData[i * stride + 3];
801  }
802  } else
803  if (attributeAccessor.componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT) {
804  auto stride = attributeBufferView.byteStride == 0?4 * sizeof(uint16_t) / sizeof(uint16_t):attributeBufferView.byteStride / sizeof(uint16_t);
805  auto start = joints.size();
806  if (start + attributeAccessor.count * 4 > joints.size()) joints.resize(start + attributeAccessor.count * 4);
807  auto bufferData = (const uint16_t*)(attributeBuffer.data.data() + attributeAccessor.byteOffset + attributeBufferView.byteOffset);
808  for (auto i = 0; i < attributeAccessor.count; i++) {
809  joints[start + i * 4 + 0] = bufferData[i * stride + 0];
810  joints[start + i * 4 + 1] = bufferData[i * stride + 1];
811  joints[start + i * 4 + 2] = bufferData[i * stride + 2];
812  joints[start + i * 4 + 3] = bufferData[i * stride + 3];
813  }
814  } else {
815  Console::println("GLTFReader::parseNode(): " + node->getId() + ": JOINTS_0: Invalid attributes component: " + to_string(attributeAccessor.componentType) + ", with size: " + to_string(getComponentTypeByteSize(attributeAccessor.componentType)));
816  continue;
817  }
818  } else {
819  Console::println("GLTFReader::parseNode(): " + node->getId() + ": Invalid buffer type: " + gltfBufferType);
820  }
821  }
822 
823  // check for vertices
824  if (haveVertices == false) {
825  throw ModelFileIOException("Missing vertices");
826  }
827 
828  // indices
829  vector<int> indices;
830  {
831  if (gltfPrimitive.indices == -1) {
832  indices.resize(vertices.size());
833  for (auto i = 0; i < vertices.size(); i++) indices[i] = i;
834  } else {
835  const auto& indicesAccessor = gltfModel.accessors[gltfPrimitive.indices];
836  const auto& indicesBufferView = gltfModel.bufferViews[indicesAccessor.bufferView];
837  const auto& indicesBuffer = gltfModel.buffers[indicesBufferView.buffer];
838  switch (indicesAccessor.componentType) {
839  case TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE:
840  {
841  auto stride = indicesBufferView.byteStride == 0?1 * sizeof(uint8_t) / sizeof(uint8_t):indicesBufferView.byteStride / sizeof(uint8_t);
842  const uint8_t* indicesBufferData = (const uint8_t*)(indicesBuffer.data.data() + indicesAccessor.byteOffset + indicesBufferView.byteOffset);
843  for (auto i = 0; i < indicesAccessor.count; i++) {
844  indices.push_back(indicesBufferData[i * stride]);
845  }
846  break;
847  }
848  case TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT:
849  {
850  auto stride = indicesBufferView.byteStride == 0?1 * sizeof(uint16_t) / sizeof(uint16_t):indicesBufferView.byteStride / sizeof(uint16_t);
851  const uint16_t* indicesBufferData = (const uint16_t*)(indicesBuffer.data.data() + indicesAccessor.byteOffset + indicesBufferView.byteOffset);
852  for (auto i = 0; i < indicesAccessor.count; i++) {
853  indices.push_back(indicesBufferData[i * stride]);
854  }
855  break;
856  }
857  case TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT:
858  {
859  auto stride = indicesBufferView.byteStride == 0?1 * sizeof(uint32_t) / sizeof(uint32_t):indicesBufferView.byteStride / sizeof(uint32_t);
860  const uint32_t* indicesBufferData = (const uint32_t*)(indicesBuffer.data.data() + indicesAccessor.byteOffset + indicesBufferView.byteOffset);
861  for (auto i = 0; i < indicesAccessor.count; i++) {
862  indices.push_back(indicesBufferData[i * stride]);
863  }
864  break;
865  }
866  default:
867  Console::println("GLTFReader::parseNode(): " + node->getId() + ": Invalid indices component: " + to_string(indicesAccessor.componentType) + ", with size: " + to_string(getComponentTypeByteSize(indicesAccessor.componentType)));
868  }
869  }
870  }
871 
872  //
873  FacesEntity facesEntity(node.get(), node->getId() + "-" + to_string(facesEntityIdx));
874  facesEntity.setMaterial(material);
875  vector<Face> faces;
876  if (haveTextureCoordinates == true) {
877  for (auto i = 0; i < indices.size() / 3; i++) {
878  faces.emplace_back(
879  node.get(),
880  start + indices[i * 3 + 0], start + indices[i * 3 + 1], start + indices[i * 3 + 2],
881  start + indices[i * 3 + 0], start + indices[i * 3 + 1], start + indices[i * 3 + 2],
882  start + indices[i * 3 + 0], start + indices[i * 3 + 1], start + indices[i * 3 + 2]
883  );
884  }
885  } else {
886  for (auto i = 0; i < indices.size() / 3; i++) {
887  faces.emplace_back(
888  node.get(),
889  start + indices[i * 3 + 0], start + indices[i * 3 + 1], start + indices[i * 3 + 2],
890  start + indices[i * 3 + 0], start + indices[i * 3 + 1], start + indices[i * 3 + 2]
891  );
892  }
893  }
894  facesEntity.setFaces(faces);
895  facesEntities.push_back(facesEntity);
896  facesEntityIdx++;
897  }
898 
899  // skinning
900  if (gltfNode.skin != -1) {
901  const auto& gltfSkin = gltfModel.skins[gltfNode.skin];
902  const auto& inverseBindMatricesAccessor = gltfModel.accessors[gltfSkin.inverseBindMatrices];
903  const auto& inverseBindMatricesBufferView = gltfModel.bufferViews[inverseBindMatricesAccessor.bufferView];
904  const auto& inverseBindMatricesBuffer = gltfModel.buffers[inverseBindMatricesBufferView.buffer];
905  const float* inverseBindMatricesBufferData = nullptr;
906  auto stride = inverseBindMatricesBufferView.byteStride == 0?16 * sizeof(float) / sizeof(float):inverseBindMatricesBufferView.byteStride / sizeof(float);
907  if (inverseBindMatricesAccessor.componentType != TINYGLTF_COMPONENT_TYPE_FLOAT) {
908  Console::println("GLTFReader::parseNode(): " + node->getId() + ": Inverse bind matrices: Invalid attributes component: " + to_string(inverseBindMatricesAccessor.componentType) + ", with size: " + to_string(getComponentTypeByteSize(inverseBindMatricesAccessor.componentType)));
909  } else {
910  inverseBindMatricesBufferData = (const float*)(inverseBindMatricesBuffer.data.data() + inverseBindMatricesAccessor.byteOffset + inverseBindMatricesBufferView.byteOffset);
911  }
912  if (inverseBindMatricesBufferData != nullptr) {
913  auto skinning = make_unique<Skinning>();
914  {
915  auto i = 0;
916  vector<Joint> skinningJoints(gltfSkin.joints.size());
917  for (const auto gltfJointNodeIdx: gltfSkin.joints) {
918  Joint joint(gltfModel.nodes[gltfJointNodeIdx].name);
919  joint.setBindMatrix(
920  Matrix4x4(
921  inverseBindMatricesBufferData[i * stride + 0],
922  inverseBindMatricesBufferData[i * stride + 1],
923  inverseBindMatricesBufferData[i * stride + 2],
924  inverseBindMatricesBufferData[i * stride + 3],
925  inverseBindMatricesBufferData[i * stride + 4],
926  inverseBindMatricesBufferData[i * stride + 5],
927  inverseBindMatricesBufferData[i * stride + 6],
928  inverseBindMatricesBufferData[i * stride + 7],
929  inverseBindMatricesBufferData[i * stride + 8],
930  inverseBindMatricesBufferData[i * stride + 9],
931  inverseBindMatricesBufferData[i * stride + 10],
932  inverseBindMatricesBufferData[i * stride + 11],
933  inverseBindMatricesBufferData[i * stride + 12],
934  inverseBindMatricesBufferData[i * stride + 13],
935  inverseBindMatricesBufferData[i * stride + 14],
936  inverseBindMatricesBufferData[i * stride + 15]
937  )
938  );
939  skinningJoints[i++] = joint;
940  }
941  skinning->setJoints(skinningJoints);
942  }
943  {
944  vector<float> skinningWeights;
945  vector<vector<JointWeight>> skinningJointWeights(vertices.size());
946  for (auto i = 0; i < vertices.size(); i++) {
947  for (auto j = 0; j < 4; j++) {
948  // TODO: reuse weights
949  if (weights[i * 4 + j] > Math::EPSILON) {
950  skinningJointWeights[i].emplace_back(joints[i * 4 + j], static_cast<int32_t>(skinningWeights.size()));
951  skinningWeights.push_back(weights[i * 4 + j]);
952  }
953  }
954  }
955  skinning->setWeights(skinningWeights);
956  skinning->setVerticesJointsWeights(skinningJointWeights);
957  }
958  node->setSkinning(skinning.release());
959  }
960  }
961 
962  // set up node
963  node->setVertices(vertices);
964  node->setNormals(normals);
965  node->setTextureCoordinates(textureCoordinates);
966  node->setFacesEntities(facesEntities);
967 
968  //
969  return node.release();
970 }
971 
972 void GLTFReader::parseNodeChildren(const string& pathName, const string& fileName, tinygltf::Model& gltfModel, const vector<int>& gltfNodeChildrenIdx, Node* parentNode, int& anonymousNodeIdx, bool useBC7TextureCompression) {
973  for (const auto gltfNodeIdx: gltfNodeChildrenIdx) {
974  const auto& gltfNode = gltfModel.nodes[gltfNodeIdx];
975  auto node = parseNode(pathName, fileName, gltfModel, gltfNodeIdx, parentNode->getModel(), parentNode, anonymousNodeIdx, useBC7TextureCompression);
976  parentNode->getModel()->getNodes()[node->getId()] = node;
977  if (parentNode->getSubNodes().find(node->getId()) != parentNode->getSubNodes().end()) {
978  Console::println("GLTFReader::parseNodeChildren(): node already exists: " + node->getId());
979  }
980  parentNode->getSubNodes()[node->getId()] = node;
981  if (gltfNode.children.empty() == false) parseNodeChildren(pathName, fileName, gltfModel, gltfNode.children, node, anonymousNodeIdx, useBC7TextureCompression);
982  }
983 }
984 
986  ModelTools::computeTangentsAndBitangents(node);
987  for (const auto& [nodeId, node]: node->getSubNodes()) {
989  }
990 }
991 
992 const Matrix4x4 GLTFReader::getNodeScaleMatrix(const tinygltf::Model& gltfModel, const string& nodeId) {
993  Matrix4x4 scaleMatrix;
994  scaleMatrix.identity();
995  auto foundNode = false;
996  for (const auto& gltfNode: gltfModel.nodes) {
997  if (gltfNode.name == nodeId) {
998  foundNode = true;
999  //
1000  if (gltfNode.scale.size() == 3) {
1001  scaleMatrix.scale(Vector3(gltfNode.scale[0], gltfNode.scale[1], gltfNode.scale[2]));
1002  }
1003  //
1004  break;
1005  }
1006  }
1007  if (foundNode == false) {
1008  Console::println("GLTFReader::GLTFReader(): getting node scale: GLTF node not found:" + nodeId);
1009  }
1010  //
1011  return scaleMatrix;
1012 }
1013 
1014 const Matrix4x4 GLTFReader::getNodeRotationMatrix(const tinygltf::Model& gltfModel, const string& nodeId) {
1015  Matrix4x4 rotationMatrix;
1016  rotationMatrix.identity();
1017  auto foundNode = false;
1018  for (const auto& gltfNode: gltfModel.nodes) {
1019  if (gltfNode.name == nodeId) {
1020  foundNode = true;
1021  //
1022  if (gltfNode.rotation.size() == 4) {
1023  Quaternion rotationQuaternion(gltfNode.rotation[0], gltfNode.rotation[1], gltfNode.rotation[2], gltfNode.rotation[3]);
1024  rotationMatrix = rotationQuaternion.computeMatrix();
1025  }
1026  //
1027  break;
1028  }
1029  }
1030  if (foundNode == false) {
1031  Console::println("GLTFReader::GLTFReader(): getting node rotation: GLTF node not found:" + nodeId);
1032  }
1033  //
1034  return rotationMatrix;
1035 }
1036 
1037 const Matrix4x4 GLTFReader::getNodeTranslationMatrix(const tinygltf::Model& gltfModel, const string& nodeId) {
1038  Matrix4x4 translationMatrix;
1039  translationMatrix.identity();
1040  auto foundNode = false;
1041  for (const auto& gltfNode: gltfModel.nodes) {
1042  if (gltfNode.name == nodeId) {
1043  foundNode = true;
1044  //
1045  if (gltfNode.translation.size() == 3) {
1046  translationMatrix.setTranslation(Vector3(gltfNode.translation[0], gltfNode.translation[1], gltfNode.translation[2]));
1047  }
1048  //
1049  break;
1050  }
1051  }
1052  if (foundNode == false) {
1053  Console::println("GLTFReader::GLTFReader(): getting node translation: GLTF node not found:" + nodeId);
1054  }
1055  //
1056  return translationMatrix;
1057 }
Color 4 definition class.
Definition: Color4.h:18
Texture entity.
Definition: Texture.h:24
static TextureFormat getRGBFormatByPixelBitsPerPixel(int bpp)
Return RGB/A texture format by bits per pixel.
Definition: Texture.h:72
static TextureFormat getPNGFormatByPixelBitsPerPixel(int bpp)
Return PNG RGB/A texture format by bits per pixel.
Definition: Texture.h:85
void setUseCompression(bool useCompression)
Set if to use compression.
Definition: Texture.h:287
void setUseMipMap(bool useMipMap)
Set if to use mip map.
Definition: Texture.h:302
static TextureDepth getRGBDepthByPixelBitsPerPixel(int bpp)
Return RGB/A texture depth by bits per pixel.
Definition: Texture.h:59
@ TEXTUREFILTER_LINEAR_MIPMAP_LINEAR
Definition: Texture.h:49
@ TEXTUREFILTER_LINEAR_MIPMAP_NEAREST
Definition: Texture.h:47
@ TEXTUREFILTER_NEAREST_MIPMAP_NEAREST
Definition: Texture.h:46
@ TEXTUREFILTER_NEAREST_MIPMAP_LINEAR
Definition: Texture.h:48
void setMinFilter(TextureFilter filter)
Set texture min filter.
Definition: Texture.h:347
void setMagFilter(TextureFilter filter)
Set texture mag filter.
Definition: Texture.h:362
static void computeTangentsAndBitangents(Node *node)
Compute tangents and bitangents.
Definition: GLTFReader.cpp:985
static string getComponentTypeString(int type)
Definition: GLTFReader.h:74
static string getTypeString(int type)
Definition: GLTFReader.h:100
static const Matrix4x4 getNodeScaleMatrix(const tinygltf::Model &gltfModel, const string &nodeId)
Get node scale matrix.
Definition: GLTFReader.cpp:992
static size_t getComponentTypeByteSize(int type)
Definition: GLTFReader.h:51
static Node * parseNode(const string &pathName, const string &fileName, tinygltf::Model &gltfModel, int gltfNodeIdx, Model *model, Node *parentNode, int &anonymousNodeIdx, bool useBC7TextureCompression)
Parse GLTF node.
Definition: GLTFReader.cpp:336
static void parseNodeChildren(const string &pathName, const string &fileName, tinygltf::Model &gltfModel, const vector< int > &gltfNodeChildrenIdx, Node *parentNode, int &anonymousNodeIdx, bool useBC7TextureCompression)
Parse GLTF node children into TDME node.
Definition: GLTFReader.cpp:972
static const string determineTextureFileName(const string &pathName, const string &fileName, const string &imageName)
Determine texture file name.
Definition: GLTFReader.h:169
static const Matrix4x4 getNodeTranslationMatrix(const tinygltf::Model &gltfModel, const string &nodeId)
Get node translation matrix.
static const Matrix4x4 getNodeRotationMatrix(const tinygltf::Model &gltfModel, const string &nodeId)
Get node rotation matrix.
static void interpolateKeyFrames(int frameTimeCount, const float *frameTimes, const vector< Matrix4x4 > &keyFrameMatrices, int interpolatedMatrixCount, vector< Matrix4x4 > &interpolatedMatrices, int frameStartIdx)
Interpolate key frames to our internal 30fps format.
Definition: GLTFReader.cpp:309
Represents a model face, consisting of vertex, normal, tangent and bitangent vectors,...
Definition: Face.h:18
Node faces entity A node can have multiple entities containing faces and a applied material.
Definition: FacesEntity.h:23
void setMaterial(Material *material)
Set up the entity's material.
Definition: FacesEntity.h:67
void setFaces(const vector< Face > &faces)
Set up entity's faces.
Definition: FacesEntity.cpp:43
Joint / Bone.
Definition: Joint.h:19
void setBindMatrix(const Matrix4x4 &bindMatrix)
Bind matrix.
Definition: Joint.h:56
Represents a material.
Definition: Material.h:23
Representation of a 3D model.
Definition: Model.h:35
unordered_map< string, Material * > & getMaterials()
Returns all object materials.
Definition: Model.h:185
unordered_map< string, Node * > & getNodes()
Returns all object's nodes.
Definition: Model.h:199
Model node.
Definition: Node.h:32
unordered_map< string, Node * > & getSubNodes()
Definition: Node.h:293
Model * getModel()
Definition: Node.h:74
Represents specular material properties.
Represents rotation orders of a model.
Definition: RotationOrder.h:23
Skinning definition for nodes.
Definition: Skinning.h:25
Represents specular material properties.
Model up vector.
Definition: UpVector.h:20
Matrix4x4 class representing matrix4x4 mathematical structure and operations for 3d space.
Definition: Matrix4x4.h:23
Matrix4x4 & identity()
Creates identity matrix.
Definition: Matrix4x4.h:158
Matrix4x4 & scale(float scalar)
Scales by scalar.
Definition: Matrix4x4.h:183
Vector3 multiply(const Vector3 &vector3) const
Multiplies this matrix with vector3.
Definition: Matrix4x4.h:225
Matrix4x4 & set(float r0c0, float r0c1, float r0c2, float r0c3, float r1c0, float r1c1, float r1c2, float r1c3, float r2c0, float r2c1, float r2c2, float r2c3, float r3c0, float r3c1, float r3c2, float r3c3)
Sets this matrix by its components.
Definition: Matrix4x4.h:108
Matrix4x4 & setTranslation(const Vector3 &translation)
Set translation.
Definition: Matrix4x4.h:442
Quaternion class representing quaternion mathematical structure and operations with x,...
Definition: Quaternion.h:24
Matrix4x4 computeMatrix() const
Computes a rotation matrix4x4 from this quaternion.
Definition: Quaternion.h:379
Quaternion & set(float x, float y, float z, float w)
Sets this quaternion by its components.
Definition: Quaternion.h:98
Vector2 class representing vector2 mathematical structure and operations with x, y components.
Definition: Vector2.h:20
Vector3 class representing vector3 mathematical structure and operations with x, y,...
Definition: Vector3.h:20
File system singleton class.
Definition: FileSystem.h:17
Byte buffer class.
Definition: ByteBuffer.h:27
Console class.
Definition: Console.h:29
Exception base class.
Definition: ExceptionBase.h:19
Model tools functions class.
Definition: ModelTools.h:42
virtual void releaseReference()
Releases a reference, thus decrementing the counter and delete it if reference counter is zero.
Definition: Reference.h:38
virtual void acquireReference()
Acquires a reference, incrementing the counter.
Definition: Reference.h:31
String tools class.
Definition: StringTools.h:22
std::exception Exception
Exception base class.
Definition: Exception.h:18