Vulkan Schnee 0.0.1
High-performance rendering engine
Loading...
Searching...
No Matches
ExrLoader.cpp
Go to the documentation of this file.
2#include "plog/Log.h"
3
5#include <complex>
6#include <tinyexr.h>
7
8namespace EngineCore {
15 ExrHeader::ExrHeader(EXRHeader header) :
16 valid(true),
17 name(header.name),
20 {
21 // Deep copy channel information
22 channels.reserve(header.num_channels);
23 for (int i = 0; i < header.num_channels; ++i) {
24 ExrChannelInfo channelInfo;
25 channelInfo.name = std::string(header.channels[i].name);
26 channelInfo.pixel_type = header.channels[i].pixel_type;
27 channelInfo.x_sampling = header.channels[i].x_sampling;
28 channelInfo.y_sampling = header.channels[i].y_sampling;
29 channelInfo.p_linear = header.channels[i].p_linear;
30 channels.push_back(channelInfo);
31 }
32 }
33
40 tinygltf::Image ExrLoader::load(const std::filesystem::path &filename) {
41 TRACY_ZONE_SCOPED_NAMED("ExrLoader::load");
42 EXRImage exrImage;
43 InitEXRImage(&exrImage);
44
45 const char *err = nullptr; // Pointer to an error string
46
47 const std::filesystem::path abs_path = std::filesystem::absolute(filename);
48 const std::string file_str = abs_path.string(); // Store the string to ensure lifetime
49 const char *file_c_str = file_str.c_str(); // Use c_str() from the owned string
50
51 PLOGI << "ExrLoader: input=" << filename << " absolute=" << abs_path
52 << " exists=" << std::filesystem::exists(abs_path);
53
54 // Load the actual EXR header for image loading
55 EXRHeader header;
56 InitEXRHeader(&header);
57 const EXRVersion version = {};
58
59 int parseResult = ParseEXRHeaderFromFile(&header, &version, file_c_str, &err);
60 if (parseResult != TINYEXR_SUCCESS) {
61 if (err) {
62 PLOGE << "Error parsing EXR header: " << err;
63 FreeEXRErrorMessage(err);
64 } else {
65 PLOGE << "Unknown error while parsing EXR file";
66 }
67 FreeEXRImage(&exrImage);
68 FreeEXRHeader(&header);
69 return {};
70 }
71
72 // load the actual image data
73 int result = LoadEXRImageFromFile(&exrImage, &header, file_c_str, &err);
74 if (result != TINYEXR_SUCCESS) {
75 if (err) {
76 PLOGE << "Error parsing EXR file: " << err;
77 FreeEXRErrorMessage(err);
78 } else {
79 PLOGE << "Unknown error while parsing EXR file";
80 }
81 FreeEXRImage(&exrImage);
82 FreeEXRHeader(&header);
83 return {};
84 }
85
86 PLOGI << "Loaded EXR: " << exrImage.width << "x" << exrImage.height
87 << " with " << exrImage.num_channels << " channels.";
88
89 tinygltf::Image image;
90 image.width = exrImage.width;
91 image.height = exrImage.height;
92 image.mimeType = "image/x-exr"; // Custom MIME type for EXR
93 image.uri = filename.string(); // Store the string directly
94 image.name = file_str;
95
96 // tinyexr's LoadEXRImageFromFile typically converts channels to 32-bit float
97 // (TINYEXR_PIXELTYPE_FLOAT) and stores them as `float*` in `exrImage_struct.images`.
98 image.bits = 32;
99 image.pixel_type = TINYGLTF_COMPONENT_TYPE_FLOAT;
100 image.component = exrImage.num_channels; // Use actual number of channels
101
102 // Check if we have at least 3 channels (RGB) for common glTF usage
103 if (image.component < 3) {
104 PLOGW << "EXR has less than 3 channels (" << image.component << "). glTF might expect RGB.";
105 // You might want to pad with zeros or return an error here depending on requirements.
106 // For now, we'll copy existing channels and pad implicitly with zero bytes if component > num_channels.
107 }
108
109 // Calculate total bytes for the interleaved image data
110 // Each component is 32 bits (4 bytes)
111 const size_t bytes_per_component = image.bits / 8; // Should be 4
112 const size_t totalPixels = static_cast<size_t>(image.width) * image.height;
113 const size_t totalBytes = totalPixels * image.component * bytes_per_component;
114 image.image.resize(totalBytes);
115
116 // Prepare pointers to source channels (they are planar in exrImage_struct.images)
117 // Assuming up to 4 channels (RGBA) for convenience
118 float *src_channels[4] = {nullptr, nullptr, nullptr, nullptr};
119 for (int c = 0; c < std::min(exrImage.num_channels, 4); ++c) {
120 src_channels[c] = reinterpret_cast<float *>(exrImage.images[c]);
121 }
122
123 // Interleave the planar float data into the tinygltf::Image's byte vector
124 unsigned char *dest_ptr = image.image.data();
125 for (int y = 0; y < image.height; ++y) {
126 for (int x = 0; x < image.width; ++x) {
127 int pixel_idx = y * image.width + x;
128 for (int c = 0; c < image.component; ++c) {
129 float value = 0.0f; // Default value for padding missing channels
130 if (c == 3) {
131 // Default alpha to 1.0 if not present
132 value = 1.0f;
133 }
134
135 // Determine the actual source channel index for the current destination channel (c)
136 int actual_src_channel_idx = c;
137 if (image.component >= 3) {
138 // Only swap if we have at least RGB channels
139 if (c == 0) {
140 // If current destination is Red (channel 0)
141 actual_src_channel_idx = 2; // Get data from source's Blue channel (channel 2)
142 } else if (c == 2) {
143 // If current destination is Blue (channel 2)
144 actual_src_channel_idx = 0; // Get data from source's Red channel (channel 0)
145 }
146 // Green (channel 1) remains Green (channel 1 from source)
147 // Alpha (channel 3) remains Alpha (channel 3 from source, if present)
148 }
149
150 // If source channel exists, use its value
151 if (actual_src_channel_idx < std::min(exrImage.num_channels, 4) && src_channels[
152 actual_src_channel_idx] != nullptr) {
153 value = src_channels[actual_src_channel_idx][pixel_idx];
154 }
155
156 // Copy the raw bytes of the float value
157 memcpy(dest_ptr, &value, bytes_per_component);
158 dest_ptr += bytes_per_component;
159 }
160 }
161 }
162
163 // 3. Clean up tinyexr memory
164 FreeEXRImage(&exrImage);
165 FreeEXRHeader(&header);
166
167 return image;
168 }
169
176 ExrHeader ExrLoader::loadHeader(std::filesystem::path path) {
177 TRACY_ZONE_SCOPED_NAMED("Parse EXR Header information");
178 EXRHeader header;
179 InitEXRHeader(&header);
180 const EXRVersion version = {};
181
182 const char *err = nullptr; // Pointer to an error string
183
184 const std::string file_str = path.string(); // Store the string to ensure lifetime
185 const char *file_c_str = file_str.c_str(); // Use c_str() from the owned string
186
187 // 1. parse exr header
188 int result = ParseEXRHeaderFromFile(&header, &version, file_c_str, &err);
189 if (result != TINYEXR_SUCCESS) {
190 if (err) {
191 PLOGE << "Error parsing EXR header: " << err;
192 FreeEXRErrorMessage(err);
193 } else {
194 PLOGE << "Unknown error while parsing EXR file";
195 }
196 FreeEXRHeader(&header);
197 return ExrHeader();
198 }
199 ExrHeader headerWrapper = ExrHeader(header);
200 FreeEXRHeader(&header);
201 return headerWrapper;
202 }
203} // namespace EngineCore
#define TRACY_ZONE_SCOPED_NAMED(name)
tinygltf::Image load(const std::filesystem::path &filename)
Load a complete EXR image file.
Definition ExrLoader.cpp:40
ExrHeader loadHeader(std::filesystem::path path)
Load only the header information from an EXR file.
Log category system implementation.
Represents channel information from an EXR file header.
Definition ExrLoader.h:15
int pixel_type
< Channel name (e.g., "R", "G", "B", "A") std::string name;
Definition ExrLoader.h:19
unsigned char p_linear
< Sampling rate in Y direction int y_sampling;
Definition ExrLoader.h:28
int x_sampling
< Pixel type (TINYEXR_PIXELTYPE_HALF, FLOAT, etc.) int pixel_type;
Definition ExrLoader.h:22
int y_sampling
< Sampling rate in X direction int x_sampling;
Definition ExrLoader.h:25
Wrapper for EXR file header information with owned data.
Definition ExrLoader.h:42
bool valid
< Channel information for each channel std::vector<ExrChannelInfo> channels;
Definition ExrLoader.h:65
std::vector< ExrChannelInfo > channels
< Compression type used int compression_type = 0;
Definition ExrLoader.h:61
int num_channels
< Image name/identifier std::string name;
Definition ExrLoader.h:55
int compression_type
< Number of color channels int num_channels = 0;
Definition ExrLoader.h:58