Loading...
Searching...
No Matches
MaterialSerializer.cpp
Go to the documentation of this file.
1//
2// Created by denzel on 08/12/2025.
3//
4
6
7#include <fstream>
8#include <iostream>
9
10#include "hellfire/utilities/SerializerUtils.h"
11
12namespace hellfire {
13 bool MaterialSerializer::save(const std::filesystem::path &filepath, const MaterialData &mat) {
14 std::ofstream file(filepath, std::ios::binary);
15 if (!file) {
16 std::cerr << "MaterialSerializer: Cannot open file for writing: " << filepath << std::endl;
17 return false;
18 }
19
20 // Header
21 write_header(file, MAGIC, VERSION);
22
23 // Name
24 write_binary_string(file, mat.name);
25
26 // Colors (write as raw floats)
27 write_binary(file, mat.diffuse_color);
28 write_binary(file, mat.ambient_color);
29 write_binary(file, mat.specular_color);
30 write_binary(file, mat.emissive_color);
31
32 // Scalars
33 write_binary(file, mat.opacity);
34 write_binary(file, mat.shininess);
35 write_binary(file, mat.metallic);
36 write_binary(file, mat.roughness);
37
38 // UV transforms
39 write_binary(file, mat.uv_scale);
40 write_binary(file, mat.uv_offset);
41
42 // Flags
43 write_binary(file, mat.double_sided);
44 write_binary(file, mat.alpha_blend);
45 write_binary(file, mat.alpha_cutoff);
46
47 // Texture references
48 const uint32_t texture_count = static_cast<uint32_t>(mat.texture_assets.size());
49 write_binary(file, texture_count);
50
51 for (const auto &[type, asset_id]: mat.texture_assets) {
52 write_binary(file, static_cast<uint32_t>(type));
53 write_binary(file, asset_id);
54 }
55
56 return file.good();
57 }
58
59 std::optional<MaterialData> MaterialSerializer::load(const std::filesystem::path &filepath) {
60 std::ifstream file(filepath, std::ios::binary);
61 if (!file) {
62 std::cerr << "MaterialSerializer: Cannot open file: " << filepath << std::endl;
63 return std::nullopt;
64 }
65
66 // Validate header
67 uint32_t version;
68 if (!read_and_validate_header(file, MAGIC, VERSION, version)) {
69 std::cerr << "MaterialSerializer: Invalid file header: " << filepath << std::endl;
70 return std::nullopt;
71 }
72
73 MaterialData mat;
74
75 // Name
76 if (!read_binary_string(file, mat.name)) return std::nullopt;
77
78 // Colors
79 if (!read_binary(file, mat.diffuse_color)) return std::nullopt;
80 if (!read_binary(file, mat.ambient_color)) return std::nullopt;
81 if (!read_binary(file, mat.specular_color)) return std::nullopt;
82 if (!read_binary(file, mat.emissive_color)) return std::nullopt;
83
84 // Scalars
85 if (!read_binary(file, mat.opacity)) return std::nullopt;
86 if (!read_binary(file, mat.shininess)) return std::nullopt;
87 if (!read_binary(file, mat.metallic)) return std::nullopt;
88 if (!read_binary(file, mat.roughness)) return std::nullopt;
89
90 // UV transforms
91 if (!read_binary(file, mat.uv_scale)) return std::nullopt;
92 if (!read_binary(file, mat.uv_offset)) return std::nullopt;
93
94 // Flags
95 if (!read_binary(file, mat.double_sided)) return std::nullopt;
96 if (!read_binary(file, mat.alpha_blend)) return std::nullopt;
97 if (!read_binary(file, mat.alpha_cutoff)) return std::nullopt;
98
99 // Texture references
100 uint32_t texture_count;
101 if (!read_binary(file, texture_count)) return std::nullopt;
102
103 for (uint32_t i = 0; i < texture_count; i++) {
104 uint32_t type_raw;
105 AssetID asset_id;
106
107 if (!read_binary(file, type_raw)) return std::nullopt;
108 if (!read_binary(file, asset_id)) return std::nullopt;
109
110 mat.texture_assets[static_cast<TextureType>(type_raw)] = asset_id;
111 }
112
113 std::cout << "Loaded material from: " << filepath << std::endl;
114 for (const auto& [type, asset_id] : mat.texture_assets) {
115 std::cout << " " << static_cast<int>(type) << " -> " << asset_id << std::endl;
116 }
117
118 return mat;
119 }
120
121 // Helper for TextureType to string conversion
122 static const char *texture_type_to_string(TextureType type) {
123 switch (type) {
124 case TextureType::DIFFUSE: return "diffuse";
125 case TextureType::NORMAL: return "normal";
126 case TextureType::SPECULAR: return "specular";
127 case TextureType::METALNESS: return "metalness";
128 case TextureType::ROUGHNESS: return "roughness";
129 case TextureType::AMBIENT_OCCLUSION: return "ambient_occlusion";
130 case TextureType::EMISSIVE: return "emissive";
131 default: return "unknown";
132 }
133 }
134
135 static TextureType string_to_texture_type(const std::string &str) {
136 if (str == "diffuse") return TextureType::DIFFUSE;
137 if (str == "normal") return TextureType::NORMAL;
138 if (str == "specular") return TextureType::SPECULAR;
139 if (str == "metalness") return TextureType::METALNESS;
140 if (str == "roughness") return TextureType::ROUGHNESS;
141 if (str == "ambient_occlusion") return TextureType::AMBIENT_OCCLUSION;
142 if (str == "emissive") return TextureType::EMISSIVE;
143 return TextureType::DIFFUSE; // fallback
144 }
145
146 bool MaterialSerializer::save_json(const std::filesystem::path &filepath, const MaterialData &mat) {
147 nlohmann::json j;
148
149 j["version"] = VERSION;
150 j["name"] = mat.name;
151
152 j["colors"] = {
153 {"diffuse", vec3_to_json(mat.diffuse_color)},
154 {"ambient", vec3_to_json(mat.ambient_color)},
155 {"specular", vec3_to_json(mat.specular_color)},
156 {"emissive", vec3_to_json(mat.emissive_color)}
157 };
158
159 j["properties"] = {
160 {"opacity", mat.opacity},
161 {"shininess", mat.shininess},
162 {"metallic", mat.metallic},
163 {"roughness", mat.roughness}
164 };
165
166 j["uv"] = {
167 {"scale", vec2_to_json(mat.uv_scale)},
168 {"offset", vec2_to_json(mat.uv_offset)}
169 };
170
171 j["flags"] = {
172 {"double_sided", mat.double_sided},
173 {"alpha_blend", mat.alpha_blend},
174 {"alpha_cutoff", mat.alpha_cutoff}
175 };
176
177 // Textures
178 nlohmann::json textures = nlohmann::json::object();
179 for (const auto &[type, asset_id]: mat.texture_assets) {
180 textures[texture_type_to_string(type)] = asset_id;
181 }
182 j["textures"] = textures;
183
184 std::ofstream file(filepath);
185 if (!file) return false;
186
187 file << j.dump(2);
188 return file.good();
189 }
190
191 std::optional<MaterialData> MaterialSerializer::load_json(const std::filesystem::path &filepath) {
192 std::ifstream file(filepath);
193 if (!file) return std::nullopt;
194
195 try {
196 nlohmann::json j;
197 file >> j;
198
199 MaterialData mat;
200
201 mat.name = j.value("name", "Material");
202
203 // Colors
204 if (j.contains("colors")) {
205 const auto &colors = j["colors"];
206 if (auto v = json_get_vec3(colors, "diffuse")) mat.diffuse_color = *v;
207 if (auto v = json_get_vec3(colors, "ambient")) mat.ambient_color = *v;
208 if (auto v = json_get_vec3(colors, "specular")) mat.specular_color = *v;
209 if (auto v = json_get_vec3(colors, "emissive")) mat.emissive_color = *v;
210 }
211
212 // Properties
213 if (j.contains("properties")) {
214 const auto &props = j["properties"];
215 mat.opacity = props.value("opacity", 1.0f);
216 mat.shininess = props.value("shininess", 32.0f);
217 mat.metallic = props.value("metallic", 0.0f);
218 mat.roughness = props.value("roughness", 0.5f);
219 }
220
221 // UV
222 if (j.contains("uv")) {
223 const auto &uv = j["uv"];
224 if (auto v = json_get_vec2(uv, "scale")) mat.uv_scale = *v;
225 if (auto v = json_get_vec2(uv, "offset")) mat.uv_offset = *v;
226 }
227
228 // Flags
229 if (j.contains("flags")) {
230 const auto &flags = j["flags"];
231 mat.double_sided = flags.value("double_sided", false);
232 mat.alpha_blend = flags.value("alpha_blend", false);
233 mat.alpha_cutoff = flags.value("alpha_cutoff", 0.5f);
234 }
235
236 // Textures
237 if (j.contains("textures") && j["textures"].is_object()) {
238 for (const auto &[key, value]: j["textures"].items()) {
239 TextureType type = string_to_texture_type(key);
240 AssetID asset_id = value.get<AssetID>();
241 mat.texture_assets[type] = asset_id;
242 }
243 }
244
245 return mat;
246 } catch (const std::exception &e) {
247 std::cerr << "MaterialSerializer: JSON parse error: " << e.what() << std::endl;
248 return std::nullopt;
249 }
250 }
251}
static constexpr uint32_t VERSION
static constexpr uint32_t MAGIC
static bool save(const std::filesystem::path &filepath, const MaterialData &material)
static bool save_json(const std::filesystem::path &filepath, const MaterialData &material)
static const char * texture_type_to_string(TextureType type)
TextureType
Definition Texture.h:13
static TextureType string_to_texture_type(const std::string &str)
Serializable material data (separate from runtime Material class)