9 #define TINYGLTF_IMPLEMENTATION
10 #define STB_IMAGE_IMPLEMENTATION
11 #define STB_IMAGE_WRITE_IMPLEMENTATION
13 #include <ext/libpng/png.h>
14 #include <ext/tinygltf/tiny_gltf.h>
46 using std::make_unique;
51 using std::unique_ptr;
85 Model* GLTFReader::read(
const string& pathName,
const string& fileName,
bool useBC7TextureCompression)
88 Console::println(
"GLTFReader::read(): Loading model: " + pathName +
"/" + fileName);
93 tinygltf::Model gltfModel;
94 tinygltf::TinyGLTF gltfLoader;
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());
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());
105 Console::println(
"GLTFReader::read(): File not supported");
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);
117 auto model = make_unique<Model>(
125 model->setShaderModel(ShaderModel::SPECULARPBR);
126 model->setEmbedSpecularTextures(
true);
127 model->setEmbedPBRTextures(
true);
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());
139 model->getSubNodes()[node->getId()] = node;
140 if (glTfNode.children.empty() ==
false)
parseNodeChildren(pathName, fileName, gltfModel, glTfNode.children, node, anonymousNodeIdx, useBC7TextureCompression);
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) {
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");
160 animationNodes.insert(node->getId());
161 const auto& gltfSample = gltfAnimation.samplers[gltfChannel.sampler];
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)));
171 auto channelFrames = Math::max(1,
static_cast<int32_t
>(Math::ceil(animationInputBufferData[animationInputAccessor.count - 1] * 30.0f)));
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)));
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()));
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()));
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()));
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");
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]));
203 interpolateKeyFrames(animationInputAccessor.count, animationInputBufferData, keyFrameMatrices, channelFrames, translationMatrices, maxFrames);
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");
210 vector<Matrix4x4> keyFrameMatrices(animationOutputAccessor.count);
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]);
216 interpolateKeyFrames(animationInputAccessor.count, animationInputBufferData, keyFrameMatrices, channelFrames, rotationMatrices, maxFrames);
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");
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]));
228 interpolateKeyFrames(animationInputAccessor.count, animationInputBufferData, keyFrameMatrices, channelFrames, scaleMatrices, maxFrames);
230 Console::println(
"GLTFReader::GLTFReader(): " + gltfAnimation.name +
": Invalid target path:" + gltfChannel.target_path);
232 if (channelFrames > frames) frames = channelFrames;
234 model->addAnimationSetup(gltfAnimation.name, maxFrames, maxFrames + frames - 1,
false);
239 for (
auto& [nodeId, animationMatrices]: animationScaleMatrices) {
240 while (animationMatrices.size() < maxFrames) animationMatrices.emplace_back(
getNodeScaleMatrix(gltfModel, nodeId));
242 for (
auto& [nodeId, animationMatrices]: animationRotationMatrices) {
243 while (animationMatrices.size() < maxFrames) animationMatrices.emplace_back(
getNodeRotationMatrix(gltfModel, nodeId));
245 for (
auto& [nodeId, animationMatrices]: animationTranslationMatrices) {
246 while (animationMatrices.size() < maxFrames) animationMatrices.emplace_back(
getNodeTranslationMatrix(gltfModel, nodeId));
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);
257 const auto& nodeAnimationScaleMatrices = animationScaleMatrices[node->getId()];
258 const auto& nodeAnimationRotationMatrices = animationRotationMatrices[node->getId()];
259 const auto& nodeAnimationTranslationMatrices = animationTranslationMatrices[node->getId()];
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]);
268 auto animation = make_unique<Animation>();
269 animation->setTransformMatrices(animationFinalMatrices);
270 node->setAnimation(animation.release());
275 ModelTools::setupJoints(model.get());
279 auto computeNormals =
false;
280 for (
const auto& [nodeId, node]: model->getNodes()) {
281 if (node->getVertices().size() != node->getNormals().size()) {
282 computeNormals =
true;
285 if (computeNormals ==
true) {
286 Console::println(
"GLTFReader::read(): Computing normals, as they were missing or mismatching vertex count");
287 ModelTools::computeNormals(model.get());
292 for (
const auto& [nodeId, node]: model->getSubNodes()) {
297 ModelTools::prepareForIndexedRendering(model.get());
300 ModelTools::createDefaultAnimation(model.get(), maxFrames);
303 ModelTools::fixAnimationLength(model.get());
306 return model.release();
309 void GLTFReader::interpolateKeyFrames(
int frameTimeCount,
const float* frameTimes,
const vector<Matrix4x4>& keyFrameMatrices,
int interpolatedMatrixCount, vector<Matrix4x4>& interpolatedMatrices,
int frameStartIdx) {
310 auto keyFrameIdx = 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()];
319 for (timeStamp = timeStampLast; timeStamp < keyFrameTime; timeStamp += 1.0f / 30.0f) {
320 if (frameIdx >= interpolatedMatrixCount) {
327 interpolatedMatrices[frameStartIdx + frameIdx] = Matrix4x4::interpolateLinear(*tansformationsMatrixLast, *transformMatrixCurrent, (timeStamp - timeStampLast) / (keyFrameTime - timeStampLast));
330 timeStampLast = timeStamp;
331 tansformationsMatrixLast = transformMatrixCurrent;
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];
339 auto nodeId = gltfNode.
name.empty() ==
true?
"<" + to_string(anonymousNodeIdx++) +
">":gltfNode.name;
340 gltfNode.name = nodeId;
342 auto node = make_unique<Node>(model, parentNode, nodeId, nodeId);
343 if (gltfNode.matrix.size() == 16) {
344 node->setTransformMatrix(
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])
371 if (gltfNode.scale.size() == 3) {
372 nodeScaleMatrix.
scale(
Vector3(gltfNode.scale[0], gltfNode.scale[1], gltfNode.scale[2]));
374 if (gltfNode.rotation.size() == 4) {
375 Quaternion rotationQuaternion(gltfNode.rotation[0], gltfNode.rotation[1], gltfNode.rotation[2], gltfNode.rotation[3]);
378 if (gltfNode.translation.size() == 3) {
379 nodeTranslationMatrix.
setTranslation(
Vector3(gltfNode.translation[0], gltfNode.translation[1], gltfNode.translation[2]));
382 nodeTransformMatrix.
set(nodeScaleMatrix);
383 nodeTransformMatrix.
multiply(nodeRotationMatrix);
384 nodeTransformMatrix.
multiply(nodeTranslationMatrix);
385 node->setTransformMatrix(nodeTransformMatrix);
387 if (gltfNode.mesh == -1)
return node.release();
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) {
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);
403 material = materialIt->second;
405 auto newMaterial = make_unique<Material>(gltfMaterial.name);
406 newMaterial->setDoubleSided(
false);
407 auto pbrMaterialProperties = make_unique<PBRMaterialProperties>();
408 auto specularMaterialProperties = make_unique<SpecularMaterialProperties>();
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;
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])
423 pbrMaterialProperties->setBaseColorFactor(
425 gltfMaterialBaseColorFactor.number_array[0],
426 gltfMaterialBaseColorFactor.number_array[1],
427 gltfMaterialBaseColorFactor.number_array[2],
428 gltfMaterialBaseColorFactor.number_array[3]
431 specularMaterialProperties->setAmbientColor(
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]
439 specularMaterialProperties->setDiffuseColor(
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]
448 if (gltfMaterial.values.find(
"metallicFactor") != gltfMaterial.values.end()) {
449 const auto& gltfMaterialMatallicFactor = gltfMaterial.values.find(
"metallicFactor")->second;
451 "GLTFReader::parseNode(): " +
452 node->getId() +
": " +
453 "have metallic factor with " +
454 to_string(gltfMaterialMatallicFactor.number_value)
456 pbrMaterialProperties->setMetallicFactor(gltfMaterialMatallicFactor.number_value);
458 if (gltfMaterial.values.find(
"roughnessFactor") != gltfMaterial.values.end()) {
459 const auto& gltfMaterialRoughnessFactor = gltfMaterial.values.find(
"roughnessFactor")->second;
461 "GLTFReader::parseNode(): " +
462 node->getId() +
": " +
463 "have roughness factor with " +
464 to_string(gltfMaterialRoughnessFactor.number_value)
466 pbrMaterialProperties->setRoughnessFactor(gltfMaterialRoughnessFactor.number_value);
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];
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");
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);
502 pbrMaterialProperties->setBaseColorTexture(texture.get());
504 if (pbrMaterialProperties->hasBaseColorTextureTransparency() ==
true) pbrMaterialProperties->setBaseColorTextureMaskedTransparency(
true);
505 specularMaterialProperties->setDiffuseTexture(texture.get());
507 if (specularMaterialProperties->hasDiffuseTextureTransparency() ==
true) specularMaterialProperties->setDiffuseTextureMaskedTransparency(
true);
509 if (gltfTexture.sampler != -1) {
510 const auto& sampler = gltfModel.samplers[gltfTexture.sampler];
511 switch (sampler.minFilter) {
519 switch (sampler.magFilter) {
529 Console::println(
"GLTFReader::parseNode(): " + node->getId() +
": An error occurred: " + exception.what());
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];
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");
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);
565 pbrMaterialProperties->setMetallicRoughnessTexture(texture.get());
568 if (gltfTexture.sampler != -1) {
569 const auto& sampler = gltfModel.samplers[gltfTexture.sampler];
570 switch (sampler.minFilter) {
578 switch (sampler.magFilter) {
588 Console::println(
"GLTFReader::parseNode(): " + node->getId() +
": An error occurred: " + exception.what());
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];
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");
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);
624 pbrMaterialProperties->setNormalTexture(texture.get());
627 if (gltfTexture.sampler != -1) {
628 const auto& sampler = gltfModel.samplers[gltfTexture.sampler];
629 switch (sampler.minFilter) {
637 switch (sampler.magFilter) {
647 Console::println(
"GLTFReader::parseNode(): " + node->getId() +
": An error occurred: " + exception.what());
650 if (gltfMaterial.emissiveTexture.index != -1) {
651 const auto& gltfTexture = gltfModel.textures[gltfMaterial.emissiveTexture.index];
652 const auto& image = gltfModel.images[gltfTexture.source];
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");
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);
681 pbrMaterialProperties->setEmissiveFactor(
Color4(gltfMaterial.emissiveFactor[0], gltfMaterial.emissiveFactor[1], gltfMaterial.emissiveFactor[2], 1.0f));
682 pbrMaterialProperties->setEmissiveTexture(texture.get());
685 if (gltfTexture.sampler != -1) {
686 const auto& sampler = gltfModel.samplers[gltfTexture.sampler];
687 switch (sampler.minFilter) {
695 switch (sampler.magFilter) {
705 Console::println(
"GLTFReader::parseNode(): " + node->getId() +
": An error occurred: " + exception.what());
708 newMaterial->setSpecularMaterialProperties(specularMaterialProperties.release());
709 newMaterial->setPBRMaterialProperties(pbrMaterialProperties.release());
710 model->
getMaterials()[newMaterial->getId()] = newMaterial.get();
712 material = newMaterial.release();
715 if (gltfPrimitive.mode != TINYGLTF_MODE_TRIANGLES) {
716 Console::println(
"GLTFReader::parseNode(): " + node->getId() +
": Invalid primitive mode: " + to_string(gltfPrimitive.mode));
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)));
734 auto stride = attributeBufferView.byteStride == 0?3 *
sizeof(float) /
sizeof(
float):attributeBufferView.byteStride /
sizeof(float);
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]);
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)));
748 auto stride = attributeBufferView.byteStride == 0?3 *
sizeof(float) /
sizeof(
float):attributeBufferView.byteStride /
sizeof(float);
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]);
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)));
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]);
771 if (gltfBufferType ==
"COLOR_0") {
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)));
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];
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];
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];
815 Console::println(
"GLTFReader::parseNode(): " + node->getId() +
": JOINTS_0: Invalid attributes component: " + to_string(attributeAccessor.componentType) +
", with size: " + to_string(
getComponentTypeByteSize(attributeAccessor.componentType)));
819 Console::println(
"GLTFReader::parseNode(): " + node->getId() +
": Invalid buffer type: " + gltfBufferType);
824 if (haveVertices ==
false) {
831 if (gltfPrimitive.indices == -1) {
832 indices.resize(vertices.size());
833 for (
auto i = 0; i < vertices.size(); i++) indices[i] = i;
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:
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]);
848 case TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT:
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]);
857 case TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT:
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]);
867 Console::println(
"GLTFReader::parseNode(): " + node->getId() +
": Invalid indices component: " + to_string(indicesAccessor.componentType) +
", with size: " + to_string(
getComponentTypeByteSize(indicesAccessor.componentType)));
873 FacesEntity facesEntity(node.get(), node->getId() +
"-" + to_string(facesEntityIdx));
876 if (haveTextureCoordinates ==
true) {
877 for (
auto i = 0; i < indices.size() / 3; i++) {
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]
886 for (
auto i = 0; i < indices.size() / 3; i++) {
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]
895 facesEntities.push_back(facesEntity);
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)));
910 inverseBindMatricesBufferData = (
const float*)(inverseBindMatricesBuffer.data.data() + inverseBindMatricesAccessor.byteOffset + inverseBindMatricesBufferView.byteOffset);
912 if (inverseBindMatricesBufferData !=
nullptr) {
913 auto skinning = make_unique<Skinning>();
916 vector<Joint> skinningJoints(gltfSkin.joints.size());
917 for (
const auto gltfJointNodeIdx: gltfSkin.joints) {
918 Joint joint(gltfModel.nodes[gltfJointNodeIdx].name);
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]
939 skinningJoints[i++] = joint;
941 skinning->setJoints(skinningJoints);
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++) {
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]);
955 skinning->setWeights(skinningWeights);
956 skinning->setVerticesJointsWeights(skinningJointWeights);
958 node->setSkinning(skinning.release());
963 node->setVertices(vertices);
964 node->setNormals(normals);
965 node->setTextureCoordinates(textureCoordinates);
966 node->setFacesEntities(facesEntities);
969 return node.release();
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);
978 Console::println(
"GLTFReader::parseNodeChildren(): node already exists: " + node->getId());
981 if (gltfNode.children.empty() ==
false)
parseNodeChildren(pathName, fileName, gltfModel, gltfNode.children, node, anonymousNodeIdx, useBC7TextureCompression);
986 ModelTools::computeTangentsAndBitangents(node);
987 for (
const auto& [nodeId, node]: node->
getSubNodes()) {
995 auto foundNode =
false;
996 for (
const auto& gltfNode: gltfModel.nodes) {
997 if (gltfNode.name == nodeId) {
1000 if (gltfNode.scale.size() == 3) {
1001 scaleMatrix.
scale(
Vector3(gltfNode.scale[0], gltfNode.scale[1], gltfNode.scale[2]));
1007 if (foundNode ==
false) {
1008 Console::println(
"GLTFReader::GLTFReader(): getting node scale: GLTF node not found:" + nodeId);
1017 auto foundNode =
false;
1018 for (
const auto& gltfNode: gltfModel.nodes) {
1019 if (gltfNode.name == nodeId) {
1022 if (gltfNode.rotation.size() == 4) {
1023 Quaternion rotationQuaternion(gltfNode.rotation[0], gltfNode.rotation[1], gltfNode.rotation[2], gltfNode.rotation[3]);
1030 if (foundNode ==
false) {
1031 Console::println(
"GLTFReader::GLTFReader(): getting node rotation: GLTF node not found:" + nodeId);
1034 return rotationMatrix;
1040 auto foundNode =
false;
1041 for (
const auto& gltfNode: gltfModel.nodes) {
1042 if (gltfNode.name == nodeId) {
1045 if (gltfNode.translation.size() == 3) {
1046 translationMatrix.
setTranslation(
Vector3(gltfNode.translation[0], gltfNode.translation[1], gltfNode.translation[2]));
1052 if (foundNode ==
false) {
1053 Console::println(
"GLTFReader::GLTFReader(): getting node translation: GLTF node not found:" + nodeId);
1056 return translationMatrix;
Color 4 definition class.
static TextureFormat getRGBFormatByPixelBitsPerPixel(int bpp)
Return RGB/A texture format by bits per pixel.
static TextureFormat getPNGFormatByPixelBitsPerPixel(int bpp)
Return PNG RGB/A texture format by bits per pixel.
void setUseCompression(bool useCompression)
Set if to use compression.
void setUseMipMap(bool useMipMap)
Set if to use mip map.
static TextureDepth getRGBDepthByPixelBitsPerPixel(int bpp)
Return RGB/A texture depth by bits per pixel.
@ TEXTUREFILTER_LINEAR_MIPMAP_LINEAR
@ TEXTUREFILTER_LINEAR_MIPMAP_NEAREST
@ TEXTUREFILTER_NEAREST_MIPMAP_NEAREST
@ TEXTUREFILTER_NEAREST_MIPMAP_LINEAR
void setMinFilter(TextureFilter filter)
Set texture min filter.
void setMagFilter(TextureFilter filter)
Set texture mag filter.
static void computeTangentsAndBitangents(Node *node)
Compute tangents and bitangents.
static string getComponentTypeString(int type)
static string getTypeString(int type)
static const Matrix4x4 getNodeScaleMatrix(const tinygltf::Model &gltfModel, const string &nodeId)
Get node scale matrix.
static size_t getComponentTypeByteSize(int type)
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.
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.
static const string determineTextureFileName(const string &pathName, const string &fileName, const string &imageName)
Determine texture file name.
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.
Represents a model face, consisting of vertex, normal, tangent and bitangent vectors,...
Node faces entity A node can have multiple entities containing faces and a applied material.
void setMaterial(Material *material)
Set up the entity's material.
void setFaces(const vector< Face > &faces)
Set up entity's faces.
void setBindMatrix(const Matrix4x4 &bindMatrix)
Bind matrix.
Representation of a 3D model.
unordered_map< string, Material * > & getMaterials()
Returns all object materials.
unordered_map< string, Node * > & getNodes()
Returns all object's nodes.
unordered_map< string, Node * > & getSubNodes()
Represents specular material properties.
Represents rotation orders of a model.
Skinning definition for nodes.
Represents specular material properties.
Matrix4x4 class representing matrix4x4 mathematical structure and operations for 3d space.
Matrix4x4 & identity()
Creates identity matrix.
Matrix4x4 & scale(float scalar)
Scales by scalar.
Vector3 multiply(const Vector3 &vector3) const
Multiplies this matrix with vector3.
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.
Matrix4x4 & setTranslation(const Vector3 &translation)
Set translation.
Quaternion class representing quaternion mathematical structure and operations with x,...
Matrix4x4 computeMatrix() const
Computes a rotation matrix4x4 from this quaternion.
Quaternion & set(float x, float y, float z, float w)
Sets this quaternion by its components.
Vector2 class representing vector2 mathematical structure and operations with x, y components.
Vector3 class representing vector3 mathematical structure and operations with x, y,...
File system singleton class.
virtual void releaseReference()
Releases a reference, thus decrementing the counter and delete it if reference counter is zero.
virtual void acquireReference()
Acquires a reference, incrementing the counter.
std::exception Exception
Exception base class.