Merge pull request #3 from jordan4ibanez/feat/gltf-loader

Fix indices, inverted models, & multiple models failing to load
This commit is contained in:
JosiahWI 2023-01-31 07:49:03 -06:00 committed by GitHub
commit eb77d07f39
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 275 additions and 86 deletions

2
.gitignore vendored
View File

@ -18,4 +18,4 @@ scripts/glext.h
*.vcxproj*
*.dir/
*.sln
*visualstudio/
*visualstudio/

6
.vscode/settings.json vendored Normal file
View File

@ -0,0 +1,6 @@
{
"files.associations": {
"*.tcc": "cpp",
"iomanip": "cpp"
}
}

View File

@ -18,6 +18,8 @@
#include <cstring>
#include <memory>
#include <string>
#include <tuple>
#include <vector>
template <class T>
struct Span
@ -53,10 +55,11 @@ private:
};
// A helper function to disable tinygltf embedded image loading
bool turn_off_textures_hack(tinygltf::Image *a, const int b, std::string *c,
std::string *d, int e, int f, const unsigned char * g,int h, void *user_pointer)
bool dummyImageLoader(tinygltf::Image *a, const int b, std::string *c,
std::string *d, int e, int f, const unsigned char * g,
int h, void *user_pointer)
{
return true;
return false;
};
namespace irr
@ -69,9 +72,8 @@ static bool tryParseGLTF(io::IReadFile* file, tinygltf::Model& model)
{
tinygltf::TinyGLTF loader {};
// Stop embedded textures from making model fail to load
void *the_void = 0;
loader.SetImageLoader(turn_off_textures_hack, the_void);
// Stop embedded textures from making model fail to load
loader.SetImageLoader(dummyImageLoader, nullptr);
std::string err {};
std::string warn {};
@ -79,7 +81,12 @@ static bool tryParseGLTF(io::IReadFile* file, tinygltf::Model& model)
auto buf = std::make_unique<char[]>(file->getSize());
file->read(buf.get(), file->getSize());
return loader.LoadASCIIFromString(&model, &err, &warn, buf.get(), file->getSize(), "", 1);
if (err != "" || warn != "") {
return false;
}
return loader.LoadASCIIFromString(&model, &err, &warn, buf.get(),
file->getSize(), "", 1);
}
template <class T>
@ -96,9 +103,8 @@ static T readPrimitive(const BufferOffset& readFrom)
static core::vector2df readVec2DF(const BufferOffset& readFrom)
{
return core::vector2df(
readPrimitive<float>(readFrom),
readPrimitive<float>(BufferOffset(readFrom, sizeof(float))));
return core::vector2df(readPrimitive<float>(readFrom),
readPrimitive<float>(BufferOffset( readFrom, sizeof(float))));
}
@ -110,19 +116,10 @@ static core::vector3df readVec3DF(const BufferOffset& readFrom,
return core::vector3df(
scale * readPrimitive<float>(readFrom),
scale * readPrimitive<float>(BufferOffset(readFrom, sizeof(float))),
-scale * readPrimitive<float>(BufferOffset(readFrom, 2 * sizeof(float))));
-scale * readPrimitive<float>(BufferOffset(readFrom, 2 *
sizeof(float))));
}
static u16* readIndices(const BufferOffset& readFrom, const std::size_t count)
{
auto* indices = new u16[count]{};
for (std::size_t i = 0; i < count; ++i) {
indices[i] = readPrimitive<u16>(BufferOffset(readFrom, i * sizeof(u16)));
}
return indices;
}
float getScale(const tinygltf::Model& model)
{
if (model.nodes[0].scale.size() > 0) {
@ -132,81 +129,93 @@ float getScale(const tinygltf::Model& model)
}
static void copyPositions(const tinygltf::Model& model,
const Span<video::S3DVertex> vertices,
const std::size_t accessorId)
const Span<video::S3DVertex> vertices, const std::size_t accessorId)
{
const auto& view = model.bufferViews[
model.accessors[accessorId].bufferView];
const auto& buffer = model.buffers[view.buffer];
for (std::size_t i = 0; i < model.accessors[accessorId].count; ++i) {
const auto count = model.accessors[accessorId].count;
float scale = getScale(model);
for (std::size_t i = 0; i < count; i++) {
const auto v = readVec3DF(BufferOffset(
buffer.data, view.byteOffset + 3 * sizeof(float) * i),
getScale(model));
buffer.data, view.byteOffset + (3 * sizeof(float) * i)),scale);
vertices.buffer[i].Pos = v;
}
}
static void copyNormals(const tinygltf::Model& model,
const Span<video::S3DVertex> vertices,
const std::size_t accessorId)
const Span<video::S3DVertex> vertices,const std::size_t accessorId)
{
const auto& view = model.bufferViews[
model.accessors[accessorId].bufferView];
const auto& buffer = model.buffers[view.buffer];
for (std::size_t i = 0; i < model.accessors[accessorId].count; ++i) {
const auto n = readVec3DF(BufferOffset(
buffer.data, view.byteOffset + 3 * sizeof(float) * i));
const auto count = model.accessors[accessorId].count;
for (std::size_t i = 0; i < count; i++) {
const auto n = readVec3DF(BufferOffset( buffer.data,
view.byteOffset + 3 * sizeof(float) * i ));
vertices.buffer[i].Normal = n;
}
}
static void copyTCoords(const tinygltf::Model& model,
const Span<video::S3DVertex> vertices,
const std::size_t accessorId)
const Span<video::S3DVertex> vertices, const std::size_t accessorId)
{
const auto& view = model.bufferViews[
model.accessors[accessorId].bufferView];
const auto& buffer = model.buffers[view.buffer];
for (std::size_t i = 0; i < model.accessors[accessorId].count; ++i) {
const auto count = model.accessors[accessorId].count;
for (std::size_t i = 0; i < count; ++i) {
const auto t = readVec2DF(BufferOffset(
buffer.data, view.byteOffset + 2 * sizeof(float) * i));
vertices.buffer[i].TCoords = t;
}
}
static video::S3DVertex* getVertices(const tinygltf::Model& model,
const std::size_t accessorId)
{
auto* vertexBuffer = new video::S3DVertex[
model.accessors[accessorId].count]{};
Span<video::S3DVertex> vertices{
vertexBuffer, model.accessors[accessorId].count};
copyPositions(model, vertices, accessorId);
const auto normalsField
= model.meshes[0].primitives[0].attributes.find("NORMAL");
if (normalsField != model.meshes[0].primitives[0].attributes.end()) {
copyNormals(model, vertices, normalsField->second);
}
const auto tCoordsField
= model.meshes[0].primitives[0].attributes.find("TEXCOORD_0");
if (tCoordsField != model.meshes[0].primitives[0].attributes.end()) {
copyTCoords(model, vertices, tCoordsField->second);
}
return vertexBuffer;
}
static u16* getIndices(const tinygltf::Model& model,
const std::size_t accessorId)
static void getIndices(const tinygltf::Model& model,
const std::size_t accessorId, std::vector<u16>& indicesBuffer)
{
const auto& view = model.bufferViews[
model.accessors[accessorId].bufferView];
const auto& indicesBuffer = model.buffers[view.buffer];
return readIndices(
BufferOffset(indicesBuffer.data, view.byteOffset),
model.accessors[accessorId].count);
const auto& modelIndices = model.buffers[view.buffer];
auto buffOffset = BufferOffset(modelIndices.data, view.byteOffset);
auto count = model.accessors[accessorId].count;
for (std::size_t i = 0; i < count; i++) {
indicesBuffer[i] = readPrimitive<u16>(BufferOffset(
buffOffset, i * sizeof(u16)));
}
}
//Returns a tuple of the current counts (current_vertex_index,
// current_normals_index, current_tcoords_index)
static void getVertices
( const tinygltf::Model& model, const std::size_t accessorId,
Span<video::S3DVertex>& verticesBuffer,
std::size_t mesh_index, std::size_t primitive_index )
{
copyPositions(model, verticesBuffer, accessorId);
const auto normalsField = model.meshes[mesh_index]
.primitives[primitive_index].attributes.find("NORMAL");
if (normalsField != model.meshes[mesh_index]
.primitives[primitive_index].attributes.end()) {
copyNormals(model, verticesBuffer, normalsField->second);
}
const auto tCoordsField = model.meshes[mesh_index]
.primitives[primitive_index].attributes.find("TEXCOORD_0");
if (tCoordsField != model.meshes[mesh_index]
.primitives[primitive_index].attributes.end()) {
copyTCoords(model, verticesBuffer, tCoordsField->second);
}
}
CGLTFMeshFileLoader::CGLTFMeshFileLoader()
@ -227,24 +236,55 @@ IAnimatedMesh* CGLTFMeshFileLoader::createMesh(io::IReadFile* file)
return nullptr;
}
const auto indicesAccessorId =
model.meshes[0].primitives[0].indices;
const auto positionAccessorId =
model.meshes[0].primitives[0].attributes["POSITION"];
auto* indices = getIndices(model, indicesAccessorId);
auto* vertices = getVertices(model, positionAccessorId);
SMeshBuffer* meshbuf { new SMeshBuffer {} };
meshbuf->append(vertices, model.accessors[positionAccessorId].count,
indices, model.accessors[indicesAccessorId].count);
// Create the base mesh
SMesh* mesh { new SMesh {} };
mesh->addMeshBuffer(meshbuf);
// Iterate models
for (std::size_t mesh_index = 0;
mesh_index < model.meshes.size(); mesh_index++) {
// Iterate primitives
for (std::size_t primitive_index = 0; primitive_index < model
.meshes[mesh_index].primitives.size(); primitive_index++) {
const auto positionAccessorId = model.meshes[mesh_index]
.primitives[primitive_index].attributes["POSITION"];
const auto indicesAccessorId = model.meshes[mesh_index]
.primitives[primitive_index].indices;
// Creates counts for preallocation
std::size_t vertices_count = model.accessors[positionAccessorId].count;
// We must count to create containers for the data
// Create new buffer for vertices, positions, and normals
auto* vertexBuffer = new video::S3DVertex[vertices_count]();
// This is used to copy data into the vertexBuffer
Span<video::S3DVertex> verticesBuffer{vertexBuffer,vertices_count};
// Create dynamic indices buffer so it's easier to work with.
// Preallocate needed resources to boost game startup speed
std::vector<u16> indicesBuffer(model.accessors[
indicesAccessorId].count);
getIndices(model, indicesAccessorId, indicesBuffer);
getVertices(model, positionAccessorId, verticesBuffer,
mesh_index, primitive_index);
// Inverse the order of indices due to the axis of the model being
// inverted when going from left handed to right handed coordinates
std::reverse(indicesBuffer.begin(),indicesBuffer.end());
// Create the mesh buffer
SMeshBuffer* meshbuf { new SMeshBuffer {} };
meshbuf->append(vertexBuffer, vertices_count, indicesBuffer.data(),
indicesBuffer.size());
mesh->addMeshBuffer(meshbuf);
}
}
// Create the mesh animations
SAnimatedMesh* animatedMesh { new SAnimatedMesh {} };
animatedMesh->addMesh(mesh);
animatedMesh->addMesh(mesh);
return animatedMesh;
}

File diff suppressed because one or more lines are too long

View File

@ -1,6 +1,9 @@
#define CATCH_CONFIG_MAIN
#include <catch.hpp>
#include <irrlicht.h>
#include <iostream>
using namespace std;
class ScopedMesh
{
@ -60,9 +63,9 @@ TEST_CASE("minimal triangle") {
REQUIRE(sm.getMesh()->getMeshBuffer(0)->getIndexCount() == 3);
const auto* indices = reinterpret_cast<irr::u16*>(
sm.getMesh()->getMeshBuffer(0)->getIndices());
CHECK(indices[0] == 0);
CHECK(indices[0] == 2);
CHECK(indices[1] == 1);
CHECK(indices[2] == 2);
CHECK(indices[2] == 0);
}
}
@ -88,10 +91,10 @@ TEST_CASE("blender cube") {
REQUIRE(sm.getMesh()->getMeshBuffer(0)->getIndexCount() == 36);
const auto* indices = reinterpret_cast<irr::u16*>(
sm.getMesh()->getMeshBuffer(0)->getIndices());
CHECK(indices[0] == 0);
CHECK(indices[1] == 3);
CHECK(indices[2] == 9);
CHECK(indices[35] == 16);
CHECK(indices[0] == 16);
CHECK(indices[1] == 5);
CHECK(indices[2] == 22);
CHECK(indices[35] == 0);
}
SECTION("vertex normals are correct") {
@ -129,3 +132,142 @@ TEST_CASE("invalid JSON returns nullptr") {
CHECK(sm.getMesh() == nullptr);
}
TEST_CASE("snow man") {
ScopedMesh sm("source/Irrlicht/tests/assets/snow_man.gltf");
REQUIRE(sm.getMesh() != nullptr);
REQUIRE(sm.getMesh()->getMeshBufferCount() == 3);
SECTION("vertex coordinates are correct for all buffers") {
REQUIRE(sm.getMesh()->getMeshBuffer(0)->getVertexCount() == 24);
const auto* vertices = reinterpret_cast<irr::video::S3DVertex*>(
sm.getMesh()->getMeshBuffer(0)->getVertices());
CHECK(vertices[0].Pos == irr::core::vector3df{3.0f, 24.0f, -3.0f});
CHECK(vertices[3].Pos == irr::core::vector3df{3.0f, 18.0f, 3.0f});
CHECK(vertices[6].Pos == irr::core::vector3df{-3.0f, 18.0f, -3.0f});
CHECK(vertices[9].Pos == irr::core::vector3df{3.0f, 24.0f, 3.0f});
CHECK(vertices[12].Pos == irr::core::vector3df{3.0f, 18.0f, -3.0f});
CHECK(vertices[15].Pos == irr::core::vector3df{-3.0f, 18.0f, 3.0f});
CHECK(vertices[18].Pos == irr::core::vector3df{3.0f, 18.0f, -3.0f});
CHECK(vertices[21].Pos == irr::core::vector3df{3.0f, 18.0f, 3.0f});
vertices = reinterpret_cast<irr::video::S3DVertex*>(
sm.getMesh()->getMeshBuffer(1)->getVertices());
CHECK(vertices[2].Pos == irr::core::vector3df{5.0f, 10.0f, 5.0f});
CHECK(vertices[3].Pos == irr::core::vector3df{5.0f, 0.0f, 5.0f});
CHECK(vertices[7].Pos == irr::core::vector3df{-5.0f, 0.0f, 5.0f});
CHECK(vertices[8].Pos == irr::core::vector3df{5.0f, 10.0f, -5.0f});
CHECK(vertices[14].Pos == irr::core::vector3df{5.0f, 0.0f, 5.0f});
CHECK(vertices[16].Pos == irr::core::vector3df{5.0f, 10.0f, -5.0f});
CHECK(vertices[22].Pos == irr::core::vector3df{-5.0f, 10.0f, 5.0f});
CHECK(vertices[23].Pos == irr::core::vector3df{-5.0f, 0.0f, 5.0f});
vertices = reinterpret_cast<irr::video::S3DVertex*>(
sm.getMesh()->getMeshBuffer(2)->getVertices());
CHECK(vertices[1].Pos == irr::core::vector3df{4.0f, 10.0f, -4.0f});
CHECK(vertices[2].Pos == irr::core::vector3df{4.0f, 18.0f, 4.0f});
CHECK(vertices[3].Pos == irr::core::vector3df{4.0f, 10.0f, 4.0f});
CHECK(vertices[10].Pos == irr::core::vector3df{-4.0f, 18.0f, -4.0f});
CHECK(vertices[11].Pos == irr::core::vector3df{-4.0f, 18.0f, 4.0f});
CHECK(vertices[12].Pos == irr::core::vector3df{4.0f, 10.0f, -4.0f});
CHECK(vertices[17].Pos == irr::core::vector3df{-4.0f, 18.0f, -4.0f});
CHECK(vertices[18].Pos == irr::core::vector3df{4.0f, 10.0f, -4.0f});
}
SECTION("vertex indices are correct for all buffers") {
REQUIRE(sm.getMesh()->getMeshBuffer(0)->getIndexCount() == 36);
const auto* indices = reinterpret_cast<irr::u16*>(
sm.getMesh()->getMeshBuffer(0)->getIndices());
CHECK(indices[0] == 23);
CHECK(indices[1] == 21);
CHECK(indices[2] == 22);
CHECK(indices[35] == 2);
REQUIRE(sm.getMesh()->getMeshBuffer(1)->getIndexCount() == 36);
indices = reinterpret_cast<irr::u16*>(
sm.getMesh()->getMeshBuffer(1)->getIndices());
CHECK(indices[10] == 16);
CHECK(indices[11] == 18);
CHECK(indices[15] == 13);
CHECK(indices[27] == 5);
REQUIRE(sm.getMesh()->getMeshBuffer(1)->getIndexCount() == 36);
indices = reinterpret_cast<irr::u16*>(
sm.getMesh()->getMeshBuffer(2)->getIndices());
CHECK(indices[26] == 6);
CHECK(indices[27] == 5);
CHECK(indices[29] == 6);
CHECK(indices[32] == 2);
}
SECTION("vertex normals are correct for all buffers") {
REQUIRE(sm.getMesh()->getMeshBuffer(0)->getVertexCount() == 24);
const auto* vertices = reinterpret_cast<irr::video::S3DVertex*>(
sm.getMesh()->getMeshBuffer(0)->getVertices());
CHECK(vertices[0].Normal == irr::core::vector3df{1.0f, 0.0f, -0.0f});
CHECK(vertices[1].Normal == irr::core::vector3df{1.0f, 0.0f, -0.0f});
CHECK(vertices[2].Normal == irr::core::vector3df{1.0f, 0.0f, -0.0f});
CHECK(vertices[3].Normal == irr::core::vector3df{1.0f, 0.0f, -0.0f});
CHECK(vertices[6].Normal == irr::core::vector3df{-1.0f, 0.0f, -0.0f});
CHECK(vertices[23].Normal == irr::core::vector3df{0.0f, 0.0f, 1.0f});
vertices = reinterpret_cast<irr::video::S3DVertex*>(
sm.getMesh()->getMeshBuffer(1)->getVertices());
CHECK(vertices[0].Normal == irr::core::vector3df{1.0f, 0.0f, -0.0f});
CHECK(vertices[1].Normal == irr::core::vector3df{1.0f, 0.0f, -0.0f});
CHECK(vertices[3].Normal == irr::core::vector3df{1.0f, 0.0f, -0.0f});
CHECK(vertices[6].Normal == irr::core::vector3df{-1.0f, 0.0f, -0.0f});
CHECK(vertices[7].Normal == irr::core::vector3df{-1.0f, 0.0f, -0.0f});
CHECK(vertices[22].Normal == irr::core::vector3df{0.0f, 0.0f, 1.0f});
vertices = reinterpret_cast<irr::video::S3DVertex*>(
sm.getMesh()->getMeshBuffer(2)->getVertices());
CHECK(vertices[3].Normal == irr::core::vector3df{1.0f, 0.0f, -0.0f});
CHECK(vertices[4].Normal == irr::core::vector3df{-1.0f, 0.0f, -0.0f});
CHECK(vertices[5].Normal == irr::core::vector3df{-1.0f, 0.0f, -0.0f});
CHECK(vertices[10].Normal == irr::core::vector3df{0.0f, 1.0f, -0.0f});
CHECK(vertices[11].Normal == irr::core::vector3df{0.0f, 1.0f, -0.0f});
CHECK(vertices[19].Normal == irr::core::vector3df{0.0f, 0.0f, -1.0f});
}
SECTION("texture coords are correct for all buffers") {
REQUIRE(sm.getMesh()->getMeshBuffer(0)->getVertexCount() == 24);
const auto* vertices = reinterpret_cast<irr::video::S3DVertex*>(
sm.getMesh()->getMeshBuffer(0)->getVertices());
CHECK(vertices[0].TCoords == irr::core::vector2df{0.583333, 0.791667});
CHECK(vertices[1].TCoords == irr::core::vector2df{0.583333, 0.666667});
CHECK(vertices[2].TCoords == irr::core::vector2df{0.708333, 0.791667});
CHECK(vertices[5].TCoords == irr::core::vector2df{0.375, 0.416667});
CHECK(vertices[6].TCoords == irr::core::vector2df{0.5, 0.291667});
CHECK(vertices[19].TCoords == irr::core::vector2df{0.708333, 0.75});
vertices = reinterpret_cast<irr::video::S3DVertex*>(
sm.getMesh()->getMeshBuffer(1)->getVertices());
CHECK(vertices[1].TCoords == irr::core::vector2df{0, 0.791667});
CHECK(vertices[4].TCoords == irr::core::vector2df{0.208333, 0.791667});
CHECK(vertices[5].TCoords == irr::core::vector2df{0, 0.791667});
CHECK(vertices[6].TCoords == irr::core::vector2df{0.208333, 0.583333});
CHECK(vertices[12].TCoords == irr::core::vector2df{0.416667, 0.791667});
CHECK(vertices[15].TCoords == irr::core::vector2df{0.208333, 0.583333});
vertices = reinterpret_cast<irr::video::S3DVertex*>(
sm.getMesh()->getMeshBuffer(2)->getVertices());
CHECK(vertices[10].TCoords == irr::core::vector2df{0.375, 0.416667});
CHECK(vertices[11].TCoords == irr::core::vector2df{0.375, 0.583333});
CHECK(vertices[12].TCoords == irr::core::vector2df{0.708333, 0.625});
CHECK(vertices[17].TCoords == irr::core::vector2df{0.541667, 0.458333});
CHECK(vertices[20].TCoords == irr::core::vector2df{0.208333, 0.416667});
CHECK(vertices[22].TCoords == irr::core::vector2df{0.375, 0.416667});
}
}