irrlicht/source/Irrlicht/CGLTFMeshFileLoader.cpp
jordan4ibanez 971a9e214e Update code with requested changes
Fix mistake on github

Comply with changes 1

Comply with requested changes 2

Comply with requested changed 3

Comply with requested changed 4

Requested changed 5

Requested changed 6

Requested changed 7

Requested changed 8

Requested changed 9

Requested changed 10

Requested changes 11

Requested changes 12

This one wasn't even requested

I just turned on my vertical ruler and I'm going to go nuts

Line 81 doesn't exist anymore

Requested changes 13

Requested changes 14

Begin snow man test

Write a lot of tests for snow man

Add to git ignore

Now unignore it

Alphabetical order

Pass by reference

Pass by reference

vertexBuffer is now handled on stack

Preallocate indicesBuffer then reverse it when complete

Undo vertexBuffer stack change causing mac builds to fail

Use direct initialization on the vertexBuffer
2024-04-18 07:21:59 -05:00

296 lines
8.2 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"
#define TINYGLTF_IMPLEMENTATION
#include <tiny_gltf.h>
#include <cstddef>
#include <cstring>
#include <memory>
#include <string>
#include <tuple>
#include <vector>
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 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 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
loader.SetImageLoader(dummyImageLoader, nullptr);
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 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()
{
}
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
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);
return animatedMesh;
}
} // namespace scene
} // namespace irr