Skip to content

Commit

Permalink
[Data/GltfLoad] Images can be read from buffers
Browse files Browse the repository at this point in the history
- Added a textured box test GLB file and a related unit test
  • Loading branch information
Razakhel committed Dec 26, 2023
1 parent 2a55602 commit 8a4812a
Show file tree
Hide file tree
Showing 3 changed files with 101 additions and 18 deletions.
50 changes: 35 additions & 15 deletions src/RaZ/Data/GltfLoad.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ std::pair<Mesh, MeshRenderer> loadMeshes(const std::vector<fastgltf::Mesh>& mesh
const std::vector<fastgltf::Buffer>& buffers,
const std::vector<fastgltf::BufferView>& bufferViews,
const std::vector<fastgltf::Accessor>& accessors) {
Logger::debug("[GltfLoad] Loading " + std::to_string(meshes.size()) + " meshes...");
Logger::debug("[GltfLoad] Loading " + std::to_string(meshes.size()) + " mesh(es)...");

Mesh loadedMesh;
MeshRenderer loadedMeshRenderer;
Expand All @@ -165,28 +165,48 @@ std::pair<Mesh, MeshRenderer> loadMeshes(const std::vector<fastgltf::Mesh>& mesh
}
}

Logger::debug("[GltfLoad] Loaded meshes");
Logger::debug("[GltfLoad] Loaded mesh(es)");

return { std::move(loadedMesh), std::move(loadedMeshRenderer) };
}

std::vector<Image> loadImages(const std::vector<fastgltf::Image>& images, const FilePath& rootFilePath) {
Logger::debug("[GltfLoad] Loading " + std::to_string(images.size()) + " images...");
std::vector<Image> loadImages(const std::vector<fastgltf::Image>& images,
const std::vector<fastgltf::Buffer>& buffers,
const std::vector<fastgltf::BufferView>& bufferViews,
const FilePath& rootFilePath) {
Logger::debug("[GltfLoad] Loading " + std::to_string(images.size()) + " image(s)...");

std::vector<Image> loadedImages;
loadedImages.reserve(images.size());

for (const fastgltf::Image& img : images) {
if (!std::holds_alternative<fastgltf::sources::URI>(img.data)) {
Logger::error("[GltfLoad] Images can only be loaded from a file path for now.");
continue;
}
static constexpr auto loadFailure = [] (const auto&) {
Logger::error("[GltfLoad] Cannot find a suitable way of loading an image.");
};

const auto& imgPath = std::get<fastgltf::sources::URI>(img.data).uri.path();
loadedImages.emplace_back(ImageFormat::load(rootFilePath + imgPath));
for (const fastgltf::Image& img : images) {
std::visit(fastgltf::visitor {
[&loadedImages, &rootFilePath] (const fastgltf::sources::URI& imgPath) {
loadedImages.emplace_back(ImageFormat::load(rootFilePath + imgPath.uri.path()));
},
[&loadedImages] (const fastgltf::sources::Vector& imgData) {
loadedImages.emplace_back(ImageFormat::loadFromData(imgData.bytes));
},
[&bufferViews, &buffers, &loadedImages] (const fastgltf::sources::BufferView& bufferViewSource) {
const fastgltf::BufferView& imgView = bufferViews[bufferViewSource.bufferViewIndex];
const fastgltf::Buffer& imgBuffer = buffers[imgView.bufferIndex];

std::visit(fastgltf::visitor {
[&loadedImages, &imgView] (const fastgltf::sources::Vector& imgData) {
loadedImages.emplace_back(ImageFormat::loadFromData(imgData.bytes.data() + imgView.byteOffset, imgView.byteLength));
},
loadFailure
}, imgBuffer.data);
},
loadFailure
}, img.data);
}

Logger::debug("[GltfLoad] Loaded images");
Logger::debug("[GltfLoad] Loaded image(s)");

return loadedImages;
}
Expand Down Expand Up @@ -230,7 +250,7 @@ std::pair<Image, Image> extractMetalnessRoughnessImages(const Image& metalRoughI
}

void loadMaterials(const std::vector<fastgltf::Material>& materials, const std::vector<Image>& images, MeshRenderer& meshRenderer) {
Logger::debug("[GltfLoad] Loading " + std::to_string(materials.size()) + " materials...");
Logger::debug("[GltfLoad] Loading " + std::to_string(materials.size()) + " material(s)...");

meshRenderer.getMaterials().clear();

Expand Down Expand Up @@ -270,7 +290,7 @@ void loadMaterials(const std::vector<fastgltf::Material>& materials, const std::
loadedMat.loadType(MaterialType::COOK_TORRANCE);
}

Logger::debug("[GltfLoad] Loaded materials");
Logger::debug("[GltfLoad] Loaded material(s)");
}

} // namespace
Expand Down Expand Up @@ -309,7 +329,7 @@ std::pair<Mesh, MeshRenderer> load(const FilePath& filePath) {

auto [mesh, meshRenderer] = loadMeshes(asset->meshes, asset->buffers, asset->bufferViews, asset->accessors);

const std::vector<Image> images = loadImages(asset->images, parentPath);
const std::vector<Image> images = loadImages(asset->images, asset->buffers, asset->bufferViews, parentPath);
loadMaterials(asset->materials, images, meshRenderer);

Logger::debug("[GltfLoad] Loaded glTF file (" + std::to_string(mesh.getSubmeshes().size()) + " submesh(es), "
Expand Down
Binary file added tests/assets/meshes/ßøӾTêxtùrëd.glb
Binary file not shown.
69 changes: 66 additions & 3 deletions tests/src/RaZ/Data/GltfFormat.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ TEST_CASE("GltfFormat load glTF") {
const auto [mesh, meshRenderer] = Raz::GltfFormat::load(RAZ_TESTS_ROOT "assets/meshes/çûbè.gltf");

REQUIRE(mesh.getSubmeshes().size() == 2);
CHECK(mesh.getSubmeshes()[0].getVertexCount() == 36);
REQUIRE(mesh.getSubmeshes()[0].getVertexCount() == 36);
CHECK(mesh.getSubmeshes()[1].getVertexCount() == 36);
CHECK(mesh.recoverVertexCount() == 72);
CHECK(mesh.getSubmeshes()[0].getTriangleIndexCount() == 36);
Expand Down Expand Up @@ -38,6 +38,8 @@ TEST_CASE("GltfFormat load glTF") {
CHECK(matProgram.getAttribute<float>(Raz::MaterialAttribute::Roughness) == 0.25f);

{
REQUIRE(matProgram.hasTexture(Raz::MaterialTexture::BaseColor));

const auto& baseColorMap = static_cast<const Raz::Texture2D&>(matProgram.getTexture(Raz::MaterialTexture::BaseColor));

REQUIRE(baseColorMap.getWidth() == 2);
Expand All @@ -63,6 +65,8 @@ TEST_CASE("GltfFormat load glTF") {
}

{
REQUIRE(matProgram.hasTexture(Raz::MaterialTexture::Emissive));

const auto& emissiveMap = static_cast<const Raz::Texture2D&>(matProgram.getTexture(Raz::MaterialTexture::Emissive));

REQUIRE(emissiveMap.getWidth() == 2);
Expand All @@ -88,6 +92,8 @@ TEST_CASE("GltfFormat load glTF") {
}

{
REQUIRE(matProgram.hasTexture(Raz::MaterialTexture::Normal));

const auto& normalMap = static_cast<const Raz::Texture2D&>(matProgram.getTexture(Raz::MaterialTexture::Normal));

REQUIRE(normalMap.getWidth() == 2);
Expand All @@ -113,6 +119,8 @@ TEST_CASE("GltfFormat load glTF") {
}

{
REQUIRE(matProgram.hasTexture(Raz::MaterialTexture::Metallic));

const auto& metallicMap = static_cast<const Raz::Texture2D&>(matProgram.getTexture(Raz::MaterialTexture::Metallic));

REQUIRE(metallicMap.getWidth() == 2);
Expand Down Expand Up @@ -145,6 +153,8 @@ TEST_CASE("GltfFormat load glTF") {
}

{
REQUIRE(matProgram.hasTexture(Raz::MaterialTexture::Roughness));

const auto& roughnessMap = static_cast<const Raz::Texture2D&>(matProgram.getTexture(Raz::MaterialTexture::Roughness));

REQUIRE(roughnessMap.getWidth() == 2);
Expand Down Expand Up @@ -177,6 +187,8 @@ TEST_CASE("GltfFormat load glTF") {
}

{
REQUIRE(matProgram.hasTexture(Raz::MaterialTexture::Ambient));

const auto& ambientOcclusionMap = static_cast<const Raz::Texture2D&>(matProgram.getTexture(Raz::MaterialTexture::Ambient));

REQUIRE(ambientOcclusionMap.getWidth() == 2);
Expand Down Expand Up @@ -209,11 +221,11 @@ TEST_CASE("GltfFormat load glTF") {
}
}

TEST_CASE("GltfFormat load GLB") {
TEST_CASE("GltfFormat load GLB simple") {
const auto [mesh, meshRenderer] = Raz::GltfFormat::load(RAZ_TESTS_ROOT "assets/meshes/ßøӾ.glb");

REQUIRE(mesh.getSubmeshes().size() == 1);
CHECK(mesh.getSubmeshes()[0].getVertexCount() == 24);
REQUIRE(mesh.getSubmeshes()[0].getVertexCount() == 24);
CHECK(mesh.recoverVertexCount() == 24);
CHECK(mesh.getSubmeshes()[0].getTriangleIndexCount() == 36);
CHECK(mesh.recoverTriangleCount() == 12); // 36 / 3
Expand All @@ -236,3 +248,54 @@ TEST_CASE("GltfFormat load GLB") {
CHECK(matProgram.getAttribute<float>(Raz::MaterialAttribute::Metallic) == 0.f);
CHECK(matProgram.getAttribute<float>(Raz::MaterialAttribute::Roughness) == 1.f);
}

TEST_CASE("GltfFormat load GLB textured") {
const auto [mesh, meshRenderer] = Raz::GltfFormat::load(RAZ_TESTS_ROOT "assets/meshes/ßøӾTêxtùrëd.glb");

REQUIRE(mesh.getSubmeshes().size() == 1);
REQUIRE(mesh.getSubmeshes()[0].getVertexCount() == 24);
CHECK(mesh.recoverVertexCount() == 24);
CHECK(mesh.getSubmeshes()[0].getTriangleIndexCount() == 36);
CHECK(mesh.recoverTriangleCount() == 12); // 36 / 3

CHECK(mesh.getSubmeshes()[0].getVertices()[0] == Raz::Vertex{ Raz::Vec3f(-0.5f, -0.5f, 0.5f), Raz::Vec2f(0.f, 0.f), Raz::Axis::Z, Raz::Vec3f(0.f) });
CHECK(mesh.getSubmeshes()[0].getVertices()[1] == Raz::Vertex{ Raz::Vec3f(0.5f, -0.5f, 0.5f), Raz::Vec2f(0.f, 0.f), Raz::Axis::Z, Raz::Vec3f(0.f) });
CHECK(mesh.getSubmeshes()[0].getVertices()[12] == Raz::Vertex{ Raz::Vec3f(0.5f, -0.5f, 0.5f), Raz::Vec2f(0.f, 0.f), -Raz::Axis::Y, Raz::Vec3f(0.f) });
CHECK(mesh.getSubmeshes()[0].getVertices()[23] == Raz::Vertex{ Raz::Vec3f(0.5f, 0.5f, -0.5f), Raz::Vec2f(1.f, 1.f), -Raz::Axis::Z, Raz::Vec3f(0.f) });

REQUIRE(meshRenderer.getSubmeshRenderers().size() == 1);
CHECK(meshRenderer.getSubmeshRenderers()[0].getRenderMode() == Raz::RenderMode::TRIANGLE);
CHECK(meshRenderer.getSubmeshRenderers()[0].getMaterialIndex() == 0);

REQUIRE(meshRenderer.getMaterials().size() == 1);

const Raz::RenderShaderProgram& matProgram = meshRenderer.getMaterials()[0].getProgram();

CHECK(matProgram.getAttribute<Raz::Vec3f>(Raz::MaterialAttribute::BaseColor) == Raz::ColorPreset::White);
CHECK(matProgram.getAttribute<Raz::Vec3f>(Raz::MaterialAttribute::Emissive) == Raz::ColorPreset::Black);
CHECK(matProgram.getAttribute<float>(Raz::MaterialAttribute::Metallic) == 0.f);
CHECK(matProgram.getAttribute<float>(Raz::MaterialAttribute::Roughness) == 1.f);

{
REQUIRE(matProgram.hasTexture(Raz::MaterialTexture::BaseColor));

const auto& baseColorMap = static_cast<const Raz::Texture2D&>(matProgram.getTexture(Raz::MaterialTexture::BaseColor));

REQUIRE(baseColorMap.getWidth() == 256);
REQUIRE(baseColorMap.getHeight() == 256);
REQUIRE(baseColorMap.getColorspace() == Raz::TextureColorspace::RGB);
REQUIRE(baseColorMap.getDataType() == Raz::TextureDataType::BYTE);

#if !defined(USE_OPENGL_ES)
const Raz::Image baseColorImg = baseColorMap.recoverImage();
REQUIRE_FALSE(baseColorImg.isEmpty());

CHECK(baseColorImg.recoverPixel<uint8_t, 3>(0, 0) == Raz::Vec3b(220));
CHECK(baseColorImg.recoverPixel<uint8_t, 3>(64, 64) == Raz::Vec3b(108, 173, 223));
CHECK(baseColorImg.recoverPixel<uint8_t, 3>(127, 127) == Raz::Vec3b(255));
CHECK(baseColorImg.recoverPixel<uint8_t, 3>(167, 71) == Raz::Vec3b(255));
CHECK(baseColorImg.recoverPixel<uint8_t, 3>(192, 192) == Raz::Vec3b(92, 135, 39));
CHECK(baseColorImg.recoverPixel<uint8_t, 3>(255, 255) == Raz::Vec3b(220));
#endif
}
}

0 comments on commit 8a4812a

Please sign in to comment.