diff --git a/.gitignore b/.gitignore index 09bcdd59..f0df6f25 100644 --- a/.gitignore +++ b/.gitignore @@ -18,4 +18,4 @@ scripts/glext.h *.vcxproj* *.dir/ *.sln -*visualstudio/ +*visualstudio/ \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 00000000..c942d044 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,6 @@ +{ + "files.associations": { + "*.tcc": "cpp", + "iomanip": "cpp" + } +} \ No newline at end of file diff --git a/source/Irrlicht/CGLTFMeshFileLoader.cpp b/source/Irrlicht/CGLTFMeshFileLoader.cpp index 08a51b62..0fc67b0b 100644 --- a/source/Irrlicht/CGLTFMeshFileLoader.cpp +++ b/source/Irrlicht/CGLTFMeshFileLoader.cpp @@ -18,6 +18,8 @@ #include #include #include +#include +#include template 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(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 @@ -96,9 +103,8 @@ static T readPrimitive(const BufferOffset& readFrom) static core::vector2df readVec2DF(const BufferOffset& readFrom) { - return core::vector2df( - readPrimitive(readFrom), - readPrimitive(BufferOffset(readFrom, sizeof(float)))); + return core::vector2df(readPrimitive(readFrom), + readPrimitive(BufferOffset( readFrom, sizeof(float)))); } @@ -110,19 +116,10 @@ static core::vector3df readVec3DF(const BufferOffset& readFrom, return core::vector3df( scale * readPrimitive(readFrom), scale * readPrimitive(BufferOffset(readFrom, sizeof(float))), - -scale * readPrimitive(BufferOffset(readFrom, 2 * sizeof(float)))); + -scale * readPrimitive(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(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 vertices, - const std::size_t accessorId) + const Span 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 vertices, - const std::size_t accessorId) +const Span 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 vertices, - const std::size_t accessorId) + const Span 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 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& 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(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& 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 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 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; } diff --git a/source/Irrlicht/tests/assets/snow_man.gltf b/source/Irrlicht/tests/assets/snow_man.gltf new file mode 100644 index 00000000..d384867c --- /dev/null +++ b/source/Irrlicht/tests/assets/snow_man.gltf @@ -0,0 +1 @@ +{"asset":{"version":"2.0","generator":"Blockbench 4.6.0 glTF exporter"},"scenes":[{"nodes":[3],"name":"blockbench_export"}],"scene":0,"nodes":[{"name":"cube","mesh":0},{"name":"cube","mesh":1},{"name":"cube","mesh":2},{"children":[0,1,2]}],"bufferViews":[{"buffer":0,"byteOffset":0,"byteLength":288,"target":34962,"byteStride":12},{"buffer":0,"byteOffset":288,"byteLength":288,"target":34962,"byteStride":12},{"buffer":0,"byteOffset":576,"byteLength":192,"target":34962,"byteStride":8},{"buffer":0,"byteOffset":768,"byteLength":72,"target":34963},{"buffer":0,"byteOffset":840,"byteLength":288,"target":34962,"byteStride":12},{"buffer":0,"byteOffset":1128,"byteLength":288,"target":34962,"byteStride":12},{"buffer":0,"byteOffset":1416,"byteLength":192,"target":34962,"byteStride":8},{"buffer":0,"byteOffset":1608,"byteLength":72,"target":34963},{"buffer":0,"byteOffset":1680,"byteLength":288,"target":34962,"byteStride":12},{"buffer":0,"byteOffset":1968,"byteLength":288,"target":34962,"byteStride":12},{"buffer":0,"byteOffset":2256,"byteLength":192,"target":34962,"byteStride":8},{"buffer":0,"byteOffset":2448,"byteLength":72,"target":34963}],"buffers":[{"byteLength":2520,"uri":"data:application/octet-stream;base64,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"}],"accessors":[{"bufferView":0,"componentType":5126,"count":24,"max":[3,24,3],"min":[-3,18,-3],"type":"VEC3"},{"bufferView":1,"componentType":5126,"count":24,"max":[1,1,1],"min":[-1,-1,-1],"type":"VEC3"},{"bufferView":2,"componentType":5126,"count":24,"max":[0.8333333134651184,1],"min":[0,0.25],"type":"VEC2"},{"bufferView":3,"componentType":5123,"count":36,"max":[23],"min":[0],"type":"SCALAR"},{"bufferView":4,"componentType":5126,"count":24,"max":[5,10,5],"min":[-5,0,-5],"type":"VEC3"},{"bufferView":5,"componentType":5126,"count":24,"max":[1,1,1],"min":[-1,-1,-1],"type":"VEC3"},{"bufferView":6,"componentType":5126,"count":24,"max":[0.625,1],"min":[0,0.375],"type":"VEC2"},{"bufferView":7,"componentType":5123,"count":36,"max":[23],"min":[0],"type":"SCALAR"},{"bufferView":8,"componentType":5126,"count":24,"max":[4,18,4],"min":[-4,10,-4],"type":"VEC3"},{"bufferView":9,"componentType":5126,"count":24,"max":[1,1,1],"min":[-1,-1,-1],"type":"VEC3"},{"bufferView":10,"componentType":5126,"count":24,"max":[0.7083333134651184,0.7916666865348816],"min":[0.2083333283662796,0.25],"type":"VEC2"},{"bufferView":11,"componentType":5123,"count":36,"max":[23],"min":[0],"type":"SCALAR"}],"materials":[{"pbrMetallicRoughness":{"metallicFactor":0,"roughnessFactor":1},"alphaMode":"MASK","alphaCutoff":0.05,"doubleSided":true}],"meshes":[{"primitives":[{"mode":4,"attributes":{"POSITION":0,"NORMAL":1,"TEXCOORD_0":2},"indices":3,"material":0}]},{"primitives":[{"mode":4,"attributes":{"POSITION":4,"NORMAL":5,"TEXCOORD_0":6},"indices":7,"material":0}]},{"primitives":[{"mode":4,"attributes":{"POSITION":8,"NORMAL":9,"TEXCOORD_0":10},"indices":11,"material":0}]}]} \ No newline at end of file diff --git a/source/Irrlicht/tests/testCGLTFMeshFileLoader.cpp b/source/Irrlicht/tests/testCGLTFMeshFileLoader.cpp index 21d62634..d960e860 100644 --- a/source/Irrlicht/tests/testCGLTFMeshFileLoader.cpp +++ b/source/Irrlicht/tests/testCGLTFMeshFileLoader.cpp @@ -1,6 +1,9 @@ #define CATCH_CONFIG_MAIN #include #include +#include + +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( 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( 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( + 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( + 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( + 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( + 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( + 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( + 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( + 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( + 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( + 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( + 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( + 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( + 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}); + } +} \ No newline at end of file