Browse Source

Add support for loading OpenEXR images

master
C. J. Howard 2 years ago
parent
commit
66f114dbf7
18 changed files with 121 additions and 81 deletions
  1. +2
    -0
      CMakeLists.txt
  2. +3
    -3
      src/game/state/boot.cpp
  3. +1
    -1
      src/resources/behavior-tree-loader.cpp
  4. +1
    -1
      src/resources/entity-archetype-loader.cpp
  5. +1
    -1
      src/resources/file-buffer-loader.cpp
  6. +70
    -34
      src/resources/image-loader.cpp
  7. +1
    -1
      src/resources/json-loader.cpp
  8. +1
    -1
      src/resources/material-loader.cpp
  9. +1
    -1
      src/resources/mesh-loader.cpp
  10. +1
    -1
      src/resources/model-loader.cpp
  11. +3
    -2
      src/resources/resource-loader.hpp
  12. +7
    -7
      src/resources/resource-manager.cpp
  13. +22
    -21
      src/resources/resource-manager.hpp
  14. +2
    -2
      src/resources/shader-program-loader.cpp
  15. +2
    -2
      src/resources/string-table-loader.cpp
  16. +1
    -1
      src/resources/text-file-loader.cpp
  17. +1
    -1
      src/resources/texture-loader.cpp
  18. +1
    -1
      src/resources/typeface-loader.cpp

+ 2
- 0
CMakeLists.txt View File

@ -9,6 +9,7 @@ project(antkeeper VERSION ${VERSION_STRING} LANGUAGES CXX)
# Find dependency packages # Find dependency packages
find_package(dr_wav REQUIRED CONFIG) find_package(dr_wav REQUIRED CONFIG)
find_package(stb REQUIRED CONFIG) find_package(stb REQUIRED CONFIG)
find_package(tinyexr REQUIRED CONFIG)
find_package(glad REQUIRED CONFIG) find_package(glad REQUIRED CONFIG)
find_package(EnTT REQUIRED CONFIG) find_package(EnTT REQUIRED CONFIG)
find_package(OpenGL REQUIRED) find_package(OpenGL REQUIRED)
@ -21,6 +22,7 @@ find_package(freetype REQUIRED CONFIG)
set(STATIC_LIBS set(STATIC_LIBS
dr_wav dr_wav
stb stb
tinyexr
glad glad
EnTT EnTT
SDL2::SDL2-static SDL2::SDL2-static

+ 3
- 3
src/game/state/boot.cpp View File

@ -300,13 +300,13 @@ void boot::setup_resources()
// Mount mods // Mount mods
for (const std::filesystem::path& mod_path: mod_paths) for (const std::filesystem::path& mod_path: mod_paths)
ctx.resource_manager->mount((ctx.mods_path / mod_path).string());
ctx.resource_manager->mount(ctx.mods_path / mod_path);
// Mount config path // Mount config path
ctx.resource_manager->mount(ctx.config_path.string());
ctx.resource_manager->mount(ctx.config_path);
// Mount data package // Mount data package
ctx.resource_manager->mount(ctx.data_package_path.string());
ctx.resource_manager->mount(ctx.data_package_path);
// Include resource search paths in order of priority // Include resource search paths in order of priority
ctx.resource_manager->include("/shaders/"); ctx.resource_manager->include("/shaders/");

+ 1
- 1
src/resources/behavior-tree-loader.cpp View File

@ -142,7 +142,7 @@ static void load_node_children(entity::ebt::composite_node* node, const nlohmann
} }
template <> template <>
entity::ebt::node* resource_loader<entity::ebt::node>::load(resource_manager* resource_manager, PHYSFS_File* file)
entity::ebt::node* resource_loader<entity::ebt::node>::load(resource_manager* resource_manager, PHYSFS_File* file, const std::filesystem::path& path)
{ {
// Read file into buffer // Read file into buffer
std::size_t size = static_cast<int>(PHYSFS_fileLength(file)); std::size_t size = static_cast<int>(PHYSFS_fileLength(file));

+ 1
- 1
src/resources/entity-archetype-loader.cpp View File

@ -237,7 +237,7 @@ static bool load_component(entity::archetype& archetype, resource_manager& resou
} }
template <> template <>
entity::archetype* resource_loader<entity::archetype>::load(resource_manager* resource_manager, PHYSFS_File* file)
entity::archetype* resource_loader<entity::archetype>::load(resource_manager* resource_manager, PHYSFS_File* file, const std::filesystem::path& path)
{ {
// Allocate archetype // Allocate archetype
entity::archetype* archetype = new entity::archetype(resource_manager->get_archetype_registry()); entity::archetype* archetype = new entity::archetype(resource_manager->get_archetype_registry());

+ 1
- 1
src/resources/file-buffer-loader.cpp View File

@ -22,7 +22,7 @@
#include <physfs.h> #include <physfs.h>
template <> template <>
file_buffer* resource_loader<file_buffer>::load(resource_manager* resource_manager, PHYSFS_File* file)
file_buffer* resource_loader<file_buffer>::load(resource_manager* resource_manager, PHYSFS_File* file, const std::filesystem::path& path)
{ {
file_buffer* buffer = new file_buffer(); file_buffer* buffer = new file_buffer();

+ 70
- 34
src/resources/image-loader.cpp View File

@ -23,56 +23,92 @@
#include <cstring> #include <cstring>
#include <stdexcept> #include <stdexcept>
#include <physfs.h> #include <physfs.h>
#include <tinyexr.h>
template <> template <>
image* resource_loader<image>::load(resource_manager* resource_manager, PHYSFS_File* file)
image* resource_loader<image>::load(resource_manager* resource_manager, PHYSFS_File* file, const std::filesystem::path& path)
{ {
unsigned char* buffer; unsigned char* buffer;
int size; int size;
int width;
int height;
int channels;
bool hdr;
void* pixels;
::image* image = nullptr;
// Read input stream into buffer // Read input stream into buffer
size = static_cast<int>(PHYSFS_fileLength(file)); size = static_cast<int>(PHYSFS_fileLength(file));
buffer = new unsigned char[size]; buffer = new unsigned char[size];
PHYSFS_readBytes(file, buffer, size); PHYSFS_readBytes(file, buffer, size);
// Determine if image is in an HDR format
hdr = (stbi_is_hdr_from_memory(buffer, size) != 0);
// Set vertical flip on load in order to upload pixels correctly to OpenGL
stbi_set_flip_vertically_on_load(true);
// Load image data
if (hdr)
// Select loader according to file extension
if (path.extension() == ".exr")
{ {
pixels = stbi_loadf_from_memory(buffer, size, &width, &height, &channels, 0);
// Load OpenEXR with TinyEXR
float* pixels = nullptr;
int width = 0;
int height = 0;
const char* error = nullptr;
int status = LoadEXRFromMemory(&pixels, &width, &height, buffer, size, &error);
if (status != TINYEXR_SUCCESS)
{
delete[] buffer;
std::string error_string(error);
FreeEXRErrorMessage(error);
throw std::runtime_error("TinyEXR error (" + std::to_string(status) + "): " + error_string);
}
// Create image
std::size_t component_size = sizeof(float);
image = new ::image();
image->format(component_size, 4);
image->resize(static_cast<unsigned int>(width), static_cast<unsigned int>(height));
std::memcpy(image->get_pixels(), pixels, image->get_size());
// Free loaded pixels
free(pixels);
} }
else else
{ {
pixels = stbi_load_from_memory(buffer, size, &width, &height, &channels, 0);
}
// Free file buffer
delete[] buffer;
// Check if image was loaded
if (!pixels)
{
throw std::runtime_error("STBI failed to load image from memory.");
// Load all other formats with stb_image
// Determine if image is in an HDR format
bool hdr = (stbi_is_hdr_from_memory(buffer, size) != 0);
// Set vertical flip on load in order to upload pixels correctly to OpenGL
stbi_set_flip_vertically_on_load(true);
// Load image data
void* pixels = nullptr;
int width = 0;
int height = 0;
int channels = 0;
if (hdr)
{
pixels = stbi_loadf_from_memory(buffer, size, &width, &height, &channels, 0);
}
else
{
pixels = stbi_load_from_memory(buffer, size, &width, &height, &channels, 0);
}
// Free file buffer
delete[] buffer;
// Check if image was loaded
if (!pixels)
{
throw std::runtime_error("STBI failed to load image from memory.");
}
// Create image
std::size_t component_size = (hdr) ? sizeof(float) : sizeof(unsigned char);
image = new ::image();
image->format(component_size, channels);
image->resize(static_cast<unsigned int>(width), static_cast<unsigned int>(height));
std::memcpy(image->get_pixels(), pixels, image->get_size());
// Free loaded image data
stbi_image_free(pixels);
} }
std::size_t component_size = (hdr) ? sizeof(float) : sizeof(unsigned char);
::image* image = new ::image();
image->format(component_size, channels);
image->resize(static_cast<unsigned int>(width), static_cast<unsigned int>(height));
std::memcpy(image->get_pixels(), pixels, image->get_size());
// Free loaded image data
stbi_image_free(pixels);
return image; return image;
} }

+ 1
- 1
src/resources/json-loader.cpp View File

@ -23,7 +23,7 @@
#include <physfs.h> #include <physfs.h>
template <> template <>
json* resource_loader<json>::load(resource_manager* resource_manager, PHYSFS_File* file)
json* resource_loader<json>::load(resource_manager* resource_manager, PHYSFS_File* file, const std::filesystem::path& path)
{ {
// Read file into buffer // Read file into buffer
std::size_t size = static_cast<std::size_t>(PHYSFS_fileLength(file)); std::size_t size = static_cast<std::size_t>(PHYSFS_fileLength(file));

+ 1
- 1
src/resources/material-loader.cpp View File

@ -223,7 +223,7 @@ static bool load_matrix_property(render::material* material, const std::string&
} }
template <> template <>
render::material* resource_loader<render::material>::load(resource_manager* resource_manager, PHYSFS_File* file)
render::material* resource_loader<render::material>::load(resource_manager* resource_manager, PHYSFS_File* file, const std::filesystem::path& path)
{ {
// Read file into buffer // Read file into buffer
std::size_t size = static_cast<int>(PHYSFS_fileLength(file)); std::size_t size = static_cast<int>(PHYSFS_fileLength(file));

+ 1
- 1
src/resources/mesh-loader.cpp View File

@ -26,7 +26,7 @@
#include <physfs.h> #include <physfs.h>
template <> template <>
geom::mesh* resource_loader<geom::mesh>::load(resource_manager* resource_manager, PHYSFS_File* file)
geom::mesh* resource_loader<geom::mesh>::load(resource_manager* resource_manager, PHYSFS_File* file, const std::filesystem::path& path)
{ {
std::string line; std::string line;
std::vector<float3> vertices; std::vector<float3> vertices;

+ 1
- 1
src/resources/model-loader.cpp View File

@ -357,7 +357,7 @@ model* resource_loader::load(resource_manager* resource_manager, PHYSFS_F
*/ */
template <> template <>
render::model* resource_loader<render::model>::load(resource_manager* resource_manager, PHYSFS_File* file)
render::model* resource_loader<render::model>::load(resource_manager* resource_manager, PHYSFS_File* file, const std::filesystem::path& path)
{ {
// Read file into buffer // Read file into buffer
std::size_t size = static_cast<int>(PHYSFS_fileLength(file)); std::size_t size = static_cast<int>(PHYSFS_fileLength(file));

+ 3
- 2
src/resources/resource-loader.hpp View File

@ -20,6 +20,7 @@
#ifndef RESOURCE_LOADER_HPP #ifndef RESOURCE_LOADER_HPP
#define RESOURCE_LOADER_HPP #define RESOURCE_LOADER_HPP
#include <filesystem>
#include <string> #include <string>
class resource_manager; class resource_manager;
@ -41,7 +42,7 @@ public:
* @param file PhysicsFS file handle. * @param file PhysicsFS file handle.
* @return Pointer to the loaded resource. * @return Pointer to the loaded resource.
*/ */
static T* load(resource_manager* resourceManager, PHYSFS_File* file);
static T* load(resource_manager* resourceManager, PHYSFS_File* file, const std::filesystem::path& path);
/** /**
* Saves resource data. * Saves resource data.
@ -50,7 +51,7 @@ public:
* @param file PhysicsFS file handle. * @param file PhysicsFS file handle.
* @param resource Pointer to the resource data. * @param resource Pointer to the resource data.
*/ */
static void save(resource_manager* resourceManager, PHYSFS_File* file, const T* resource);
static void save(resource_manager* resourceManager, PHYSFS_File* file, const std::filesystem::path& path, const T* resource);
}; };
/// getline function for PhysicsFS file handles /// getline function for PhysicsFS file handles

+ 7
- 7
src/resources/resource-manager.cpp View File

@ -56,10 +56,10 @@ resource_manager::~resource_manager()
} }
} }
bool resource_manager::mount(const std::string& path)
bool resource_manager::mount(const std::filesystem::path& path)
{ {
logger->push_task("Mounting path \"" + path + "\"");
if (!PHYSFS_mount(path.c_str(), nullptr, 1))
logger->push_task("Mounting path \"" + path.string() + "\"");
if (!PHYSFS_mount(path.string().c_str(), nullptr, 1))
{ {
logger->error(std::string("PhysicsFS error: ") + PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode())); logger->error(std::string("PhysicsFS error: ") + PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode()));
logger->pop_task(EXIT_FAILURE); logger->pop_task(EXIT_FAILURE);
@ -72,10 +72,10 @@ bool resource_manager::mount(const std::string& path)
} }
} }
void resource_manager::unload(const std::string& name)
void resource_manager::unload(const std::filesystem::path& path)
{ {
// Check if resource is in the cache // Check if resource is in the cache
auto it = resource_cache.find(name);
auto it = resource_cache.find(path);
if (it != resource_cache.end()) if (it != resource_cache.end())
{ {
// Decrement the resource handle reference count // Decrement the resource handle reference count
@ -86,7 +86,7 @@ void resource_manager::unload(const std::string& name)
{ {
if (logger) if (logger)
{ {
logger->push_task("Unloading resource \"" + name + "\"");
logger->push_task("Unloading resource \"" + path.string() + "\"");
} }
delete it->second; delete it->second;
@ -102,7 +102,7 @@ void resource_manager::unload(const std::string& name)
} }
} }
void resource_manager::include(const std::string& search_path)
void resource_manager::include(const std::filesystem::path& search_path)
{ {
search_paths.push_back(search_path); search_paths.push_back(search_path);
} }

+ 22
- 21
src/resources/resource-manager.hpp View File

@ -30,6 +30,7 @@
#include <string> #include <string>
#include <entt/entt.hpp> #include <entt/entt.hpp>
#include <physfs.h> #include <physfs.h>
#include <filesystem>
/** /**
* Loads resources. * Loads resources.
@ -47,14 +48,14 @@ public:
*/ */
~resource_manager(); ~resource_manager();
bool mount(const std::string& path);
bool mount(const std::filesystem::path& path);
/** /**
* Adds a path to be searched when a resource is requested. * Adds a path to be searched when a resource is requested.
* *
* @param path Search path. * @param path Search path.
*/ */
void include(const std::string& path);
void include(const std::filesystem::path& path);
/** /**
* Loads the requested resource. If the resource has already been loaded it will be retrieved from the resource cache and its reference count incremented. * Loads the requested resource. If the resource has already been loaded it will be retrieved from the resource cache and its reference count incremented.
@ -64,14 +65,14 @@ public:
* @return Pointer to the requested resource, or nullptr if the resource could not be found nor loaded. * @return Pointer to the requested resource, or nullptr if the resource could not be found nor loaded.
*/ */
template <typename T> template <typename T>
T* load(const std::string& name);
T* load(const std::filesystem::path& path);
/** /**
* Decrements a resource's reference count and unloads the resource if it's unreferenced. * Decrements a resource's reference count and unloads the resource if it's unreferenced.
* *
* @param path Path to the resource, relative to the search paths. * @param path Path to the resource, relative to the search paths.
*/ */
void unload(const std::string& name);
void unload(const std::filesystem::path& path);
/** /**
* Saves the specified resource. * Saves the specified resource.
@ -81,28 +82,28 @@ public:
* @param path Path to the resource. * @param path Path to the resource.
*/ */
template <typename T> template <typename T>
void save(const T* resource, const std::string& path);
void save(const T* resource, const std::filesystem::path& path);
entt::registry& get_archetype_registry(); entt::registry& get_archetype_registry();
private: private:
std::map<std::string, resource_handle_base*> resource_cache;
std::list<std::string> search_paths;
std::map<std::filesystem::path, resource_handle_base*> resource_cache;
std::list<std::filesystem::path> search_paths;
entt::registry archetype_registry; entt::registry archetype_registry;
debug::logger* logger; debug::logger* logger;
}; };
template <typename T> template <typename T>
T* resource_manager::load(const std::string& name)
T* resource_manager::load(const std::filesystem::path& path)
{ {
// Check if resource is in the cache // Check if resource is in the cache
auto it = resource_cache.find(name);
auto it = resource_cache.find(path);
if (it != resource_cache.end()) if (it != resource_cache.end())
{ {
/* /*
if (logger) if (logger)
{ {
logger->log("Fetched resource \"" + name + "\"");
logger->log("Fetched resource \"" + path.string() + "\"");
} }
*/ */
@ -118,18 +119,18 @@ T* resource_manager::load(const std::string& name)
if (logger) if (logger)
{ {
logger->push_task("Loading resource \"" + name + "\"");
logger->push_task("Loading resource \"" + path.string() + "\"");
} }
// Resource not cached, look for file in search paths // Resource not cached, look for file in search paths
T* data = nullptr; T* data = nullptr;
bool found = false; bool found = false;
for (const std::string& search_path: search_paths)
for (const std::filesystem::path& search_path: search_paths)
{ {
std::string path = search_path + name;
std::filesystem::path full_path = search_path / path;
// Check if file exists // Check if file exists
if (!PHYSFS_exists(path.c_str()))
if (!PHYSFS_exists(full_path.string().c_str()))
{ {
continue; continue;
} }
@ -138,7 +139,7 @@ T* resource_manager::load(const std::string& name)
found = true; found = true;
// Open file for reading // Open file for reading
PHYSFS_File* file = PHYSFS_openRead(path.c_str());
PHYSFS_File* file = PHYSFS_openRead(full_path.string().c_str());
if (!file) if (!file)
{ {
logger->error(std::string("PhysicsFS error: ") + PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode())); logger->error(std::string("PhysicsFS error: ") + PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode()));
@ -148,7 +149,7 @@ T* resource_manager::load(const std::string& name)
// Load opened file // Load opened file
try try
{ {
data = resource_loader<T>::load(this, file);
data = resource_loader<T>::load(this, file, full_path);
} }
catch (const std::exception& e) catch (const std::exception& e)
{ {
@ -181,7 +182,7 @@ T* resource_manager::load(const std::string& name)
resource->reference_count = 1; resource->reference_count = 1;
// Add resource to the cache // Add resource to the cache
resource_cache[name] = resource;
resource_cache[path] = resource;
if (logger) if (logger)
{ {
@ -192,12 +193,12 @@ T* resource_manager::load(const std::string& name)
} }
template <typename T> template <typename T>
void resource_manager::save(const T* resource, const std::string& path)
void resource_manager::save(const T* resource, const std::filesystem::path& path)
{ {
logger->push_task("Saving resource to \"" + path + "\"");
logger->push_task("Saving resource to \"" + path.string() + "\"");
// Open file for writing // Open file for writing
PHYSFS_File* file = PHYSFS_openWrite(path.c_str());
PHYSFS_File* file = PHYSFS_openWrite(path.string().c_str());
if (!file) if (!file)
{ {
logger->error(std::string("PhysicsFS error: ") + PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode())); logger->error(std::string("PhysicsFS error: ") + PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode()));
@ -209,7 +210,7 @@ void resource_manager::save(const T* resource, const std::string& path)
int status = EXIT_SUCCESS; int status = EXIT_SUCCESS;
try try
{ {
resource_loader<T>::save(this, file, resource);
resource_loader<T>::save(this, file, path, resource);
} }
catch (const std::exception& e) catch (const std::exception& e)
{ {

+ 2
- 2
src/resources/shader-program-loader.cpp View File

@ -76,10 +76,10 @@ static void handle_includes(text_file* source, resource_manager* resource_manage
} }
template <> template <>
gl::shader_program* resource_loader<gl::shader_program>::load(resource_manager* resource_manager, PHYSFS_File* file)
gl::shader_program* resource_loader<gl::shader_program>::load(resource_manager* resource_manager, PHYSFS_File* file, const std::filesystem::path& path)
{ {
// Load shader template source // Load shader template source
text_file source_lines = *resource_loader<text_file>::load(resource_manager, file);
text_file source_lines = *resource_loader<text_file>::load(resource_manager, file, path);
// Handle `#pragma include` directives // Handle `#pragma include` directives
handle_includes(&source_lines, resource_manager); handle_includes(&source_lines, resource_manager);

+ 2
- 2
src/resources/string-table-loader.cpp View File

@ -93,7 +93,7 @@ static string_table_row parse_row(const std::string& line)
} }
template <> template <>
string_table* resource_loader<string_table>::load(resource_manager* resource_manager, PHYSFS_File* file)
string_table* resource_loader<string_table>::load(resource_manager* resource_manager, PHYSFS_File* file, const std::filesystem::path& path)
{ {
string_table* table = new string_table(); string_table* table = new string_table();
std::string line; std::string line;
@ -108,7 +108,7 @@ string_table* resource_loader::load(resource_manager* resource_man
} }
template <> template <>
void resource_loader<string_table>::save(resource_manager* resource_manager, PHYSFS_File* file, const string_table* table)
void resource_loader<string_table>::save(resource_manager* resource_manager, PHYSFS_File* file, const std::filesystem::path& path, const string_table* table)
{ {
const char* delimeter = ","; const char* delimeter = ",";
const char* newline = "\n"; const char* newline = "\n";

+ 1
- 1
src/resources/text-file-loader.cpp View File

@ -22,7 +22,7 @@
#include <physfs.h> #include <physfs.h>
template <> template <>
text_file* resource_loader<text_file>::load(resource_manager* resource_manager, PHYSFS_File* file)
text_file* resource_loader<text_file>::load(resource_manager* resource_manager, PHYSFS_File* file, const std::filesystem::path& path)
{ {
text_file* text = new text_file(); text_file* text = new text_file();
std::string line; std::string line;

+ 1
- 1
src/resources/texture-loader.cpp View File

@ -31,7 +31,7 @@
#include <physfs.h> #include <physfs.h>
template <> template <>
gl::texture_2d* resource_loader<gl::texture_2d>::load(resource_manager* resource_manager, PHYSFS_File* file)
gl::texture_2d* resource_loader<gl::texture_2d>::load(resource_manager* resource_manager, PHYSFS_File* file, const std::filesystem::path& path)
{ {
// Read file into buffer // Read file into buffer
std::size_t size = static_cast<int>(PHYSFS_fileLength(file)); std::size_t size = static_cast<int>(PHYSFS_fileLength(file));

+ 1
- 1
src/resources/typeface-loader.cpp View File

@ -25,7 +25,7 @@
#include FT_FREETYPE_H #include FT_FREETYPE_H
template <> template <>
type::typeface* resource_loader<type::typeface>::load(resource_manager* resource_manager, PHYSFS_File* file)
type::typeface* resource_loader<type::typeface>::load(resource_manager* resource_manager, PHYSFS_File* file, const std::filesystem::path& path)
{ {
FT_Error error = 0; FT_Error error = 0;

Loading…
Cancel
Save