irrlicht/source/Irrlicht/CGLTFMeshFileLoader.cpp
jordan4ibanez 08d226cdb5 Fix indices, inverted models, & multiple models failing to load
Fix embedded textures causing a model not to load

Add todo

Add another todo

Push current (broken) prototyping

Fix missing bracket

Make a single array object work

Convert hard array into dynamic vector

Simplify semantics

Remove "new"

Add blocker for vscode environment changes

Disable non-dynamic prototyping

Add comment

Add more informative debug & disable it

Add additional items to gitignore

Add debug info for scalar value

Output even more debug info

Make textures render correctly

Insert the indices properly

Update .gitignore

Disable y flip

Make a reusable vertex buffer :)

Disallow embedded textures

More disable

Set up implementation for contiguous model

Add a note

More automation & framework

Final framework before stepping into function overhaul

Hold track of current_index

Integrate iterators

More integration

Rename, it's going to need 2 counters

Correctly offset the count to the right

Sync normals with positions

Time to use a tuple

Set up other counters

Do return value

Do input passing

Make (somewhat) working contiguous model

Add getter for translation data

Add debug info for future utilization

Update .gitignore

More debug

Update .gitignore

Update .gitignore

Remove all debug info & clean up

Delete this thing

Automate everything & put it into spec

Spaces into tabs

Remove array include as build test

Fix the gitignore
2024-04-18 07:21:59 -05:00

269 lines
7.9 KiB
C++

#include "CGLTFMeshFileLoader.h"
#include "CMeshBuffer.h"
#include "coreutil.h"
#include "IAnimatedMesh.h"
#include "IReadFile.h"
#include "irrTypes.h"
#include "path.h"
#include "S3DVertex.h"
#include "SAnimatedMesh.h"
#include "SColor.h"
#include "SMesh.h"
#include "vector3d.h"
#include <vector>
#include <tuple>
#define TINYGLTF_IMPLEMENTATION
#include <tiny_gltf.h>
#include <cstddef>
#include <cstring>
#include <memory>
#include <string>
template <class T>
struct Span
{
T* buffer = nullptr;
std::size_t size = 0;
};
class BufferOffset
{
public:
BufferOffset(const std::vector<unsigned char>& buf,
const std::size_t offset)
: m_buf(buf)
, m_offset(offset)
{
}
BufferOffset(const BufferOffset& other, const std::size_t fromOffset)
: m_buf(other.m_buf)
, m_offset(other.m_offset + fromOffset)
{
}
unsigned char at(const std::size_t fromOffset) const
{
return m_buf.at(m_offset + fromOffset);
}
private:
const std::vector<unsigned char>& m_buf;
std::size_t m_offset;
};
// 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)
{
return false;
};
namespace irr
{
namespace scene
{
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);
std::string err {};
std::string warn {};
auto buf = std::make_unique<char[]>(file->getSize());
file->read(buf.get(), file->getSize());
if (err != "" || warn != "") {
return false;
}
return loader.LoadASCIIFromString(&model, &err, &warn, buf.get(), file->getSize(), "", 1);
}
template <class T>
static T readPrimitive(const BufferOffset& readFrom)
{
unsigned char d[sizeof(T)]{};
for (std::size_t i = 0; i < sizeof(T); ++i) {
d[i] = readFrom.at(i);
}
T dest;
std::memcpy(&dest, d, sizeof(dest));
return dest;
}
static core::vector2df readVec2DF(const BufferOffset& readFrom)
{
return core::vector2df( readPrimitive<float>(readFrom), readPrimitive<float>( BufferOffset( readFrom, sizeof(float) ) ) );
}
static core::vector3df readVec3DF(const BufferOffset& readFrom,
const float scale = 1.0f)
{
// glTF's coordinate system is right-handed, Irrlicht's is left-handed
// glTF's +Z axis corresponds to Irrlicht's -Z axis
return core::vector3df(
scale * readPrimitive<float>(readFrom),
scale * readPrimitive<float>(BufferOffset(readFrom, sizeof(float))),
-scale * readPrimitive<float>(BufferOffset(readFrom, 2 * sizeof(float))));
}
float getScale(const tinygltf::Model& model)
{
if (model.nodes[0].scale.size() > 0) {
return static_cast<float>(model.nodes[0].scale[0]);
}
return 1.0f;
}
static void copyPositions(const tinygltf::Model& model, 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];
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)), scale);
vertices.buffer[i].Pos = v;
}
}
static void copyNormals(const tinygltf::Model& model, 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];
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 auto& view = model.bufferViews[ model.accessors[accessorId].bufferView ];
const auto& buffer = model.buffers[view.buffer];
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 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& modelIndices = model.buffers[view.buffer];
auto bufferobject = BufferOffset(modelIndices.data, view.byteOffset);
auto count = model.accessors[accessorId].count;
for (std::size_t i = 0; i < count; i++) {
auto current = readPrimitive<u16>(BufferOffset(bufferobject, i * sizeof(u16)));
// Inverse the order of indices
indicesBuffer->insert(indicesBuffer->begin(), current);
}
}
// 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()
{
}
bool CGLTFMeshFileLoader::isALoadableFileExtension(
const io::path& filename) const
{
return core::hasFileExtension(filename, "gltf");
}
IAnimatedMesh* CGLTFMeshFileLoader::createMesh(io::IReadFile* file)
{
tinygltf::Model model{};
if (file->getSize() == 0 || !tryParseGLTF(file, model)) {
return nullptr;
}
// Create the base mesh
SMesh* mesh { new SMesh {} };
// 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. Will loop through this soon
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
std::vector<u16> indicesBuffer;
getIndices(model, indicesAccessorId, &indicesBuffer);
getVertices(model, positionAccessorId, &verticesBuffer, mesh_index, primitive_index);
// 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);
return animatedMesh;
}
} // namespace scene
} // namespace irr