From 2c3827f403094f65ca5c63cd4ebe89b8df47d4d2 Mon Sep 17 00:00:00 2001 From: "C. J. Howard" Date: Wed, 29 Jun 2022 14:43:56 +0800 Subject: [PATCH] Improve OpenEXR image loading --- src/game/state/nuptial-flight.cpp | 14 +++++ src/resources/image-loader.cpp | 88 ++++++++++++++++++++++++++----- 2 files changed, 90 insertions(+), 12 deletions(-) diff --git a/src/game/state/nuptial-flight.cpp b/src/game/state/nuptial-flight.cpp index ad17e09..a18c061 100644 --- a/src/game/state/nuptial-flight.cpp +++ b/src/game/state/nuptial-flight.cpp @@ -38,6 +38,7 @@ #include "render/passes/clear-pass.hpp" #include "render/passes/ground-pass.hpp" #include "state-machine.hpp" +#include "scene/ambient-light.hpp" #include "config.hpp" namespace game { @@ -112,6 +113,19 @@ nuptial_flight::nuptial_flight(game::context& ctx): ctx.astronomy_system->set_observer_location(double3{observer.elevation, observer.latitude, observer.longitude}); } + /* + scene::ambient_light* light = new scene::ambient_light(); + light->set_color(float3{1, 1, 1} * 10000.0f); + ctx.surface_scene->add_object(light); + */ + + // Create color checker + { + entity::archetype* color_checker_archetype = ctx.resource_manager->load("color-checker.ent"); + auto color_checker_eid = color_checker_archetype->create(*ctx.entity_registry); + entity::command::warp_to(*ctx.entity_registry, color_checker_eid, {0, 0, 0}); + } + // Setup camera setup_camera(); diff --git a/src/resources/image-loader.cpp b/src/resources/image-loader.cpp index c9274aa..aeadc50 100644 --- a/src/resources/image-loader.cpp +++ b/src/resources/image-loader.cpp @@ -41,30 +41,94 @@ image* resource_loader::load(resource_manager* resource_manager, PHYSFS_F if (path.extension() == ".exr") { // Load OpenEXR with TinyEXR - - float* pixels = nullptr; - int width = 0; - int height = 0; + int status = TINYEXR_SUCCESS; const char* error = nullptr; - int status = LoadEXRFromMemory(&pixels, &width, &height, buffer, size, &error); + // Read EXR version + EXRVersion exr_version; + status = ParseEXRVersionFromMemory(&exr_version, buffer, size); if (status != TINYEXR_SUCCESS) { delete[] buffer; + throw std::runtime_error("TinyEXR parse version error (" + std::to_string(status) + "): invalid EXR file"); + } + + // Check if image is multipart + if (exr_version.multipart) + { + throw std::runtime_error("OpenEXR multipart images not supported"); + } + + // Read EXR header + EXRHeader exr_header; + InitEXRHeader(&exr_header); + status = ParseEXRHeaderFromMemory(&exr_header, &exr_version, buffer, size, &error); + if (status != TINYEXR_SUCCESS) + { std::string error_string(error); FreeEXRErrorMessage(error); - throw std::runtime_error("TinyEXR error (" + std::to_string(status) + "): " + error_string); + delete[] buffer; + throw std::runtime_error("TinyEXR parse header error (" + std::to_string(status) + "): " + error_string); + } + + // Check if image is tiled + if (exr_header.tiled) + { + FreeEXRHeader(&exr_header); + delete[] buffer; + throw std::runtime_error("OpenEXR tiled images not supported"); } + // Read half channels as float + for (int i = 0; i < exr_header.num_channels; ++i) + { + if (exr_header.pixel_types[i] == TINYEXR_PIXELTYPE_HALF) + { + exr_header.requested_pixel_types[i] = TINYEXR_PIXELTYPE_FLOAT; + } + } + + // Read EXR data + EXRImage exr_image; + InitEXRImage(&exr_image); + status = LoadEXRImageFromMemory(&exr_image, &exr_header, buffer, size, &error); + if (status != TINYEXR_SUCCESS) + { + std::string error_string(error); + FreeEXRErrorMessage(error); + FreeEXRHeader(&exr_header); + delete[] buffer; + throw std::runtime_error("TinyEXR load error (" + std::to_string(status) + "): " + error_string); + } + + // Free file buffer + delete[] buffer; + // Create image - std::size_t component_size = sizeof(float); image = new ::image(); - image->format(component_size, 4); - image->resize(static_cast(width), static_cast(height)); - std::memcpy(image->get_pixels(), pixels, image->get_size()); + image->format(sizeof(float), exr_image.num_channels); + image->resize(static_cast(exr_image.width), static_cast(exr_image.height)); + + // Fill image pixels + float* component = static_cast(image->get_pixels()); + for (int y = exr_image.height - 1; y >= 0; --y) + { + int row_offset = y * exr_image.width; + + for (int x = 0; x < exr_image.width; ++x) + { + int pixel_index = row_offset + x; + + for (int c = exr_image.num_channels - 1; c >= 0; --c) + { + *(component++) = reinterpret_cast(exr_image.images)[c][pixel_index]; + } + } + } - // Free loaded pixels - free(pixels); + // Free EXR data + FreeEXRImage(&exr_image); + FreeEXRHeader(&exr_header); } else {