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 return mat;
114 }
115
116 // Helper for TextureType to string conversion
117 static const char *texture_type_to_string(TextureType type) {
118 switch (type) {
119 case TextureType::DIFFUSE: return "diffuse";
120 case TextureType::NORMAL: return "normal";
121 case TextureType::SPECULAR: return "specular";
122 case TextureType::METALNESS: return "metalness";
123 case TextureType::ROUGHNESS: return "roughness";
124 case TextureType::AMBIENT_OCCLUSION: return "ambient_occlusion";
125 case TextureType::EMISSIVE: return "emissive";
126 default: return "unknown";
127 }
128 }
129
130 static TextureType string_to_texture_type(const std::string &str) {
131 if (str == "diffuse") return TextureType::DIFFUSE;
132 if (str == "normal") return TextureType::NORMAL;
133 if (str == "specular") return TextureType::SPECULAR;
134 if (str == "metalness") return TextureType::METALNESS;
135 if (str == "roughness") return TextureType::ROUGHNESS;
136 if (str == "ambient_occlusion") return TextureType::AMBIENT_OCCLUSION;
137 if (str == "emissive") return TextureType::EMISSIVE;
138 return TextureType::DIFFUSE; // fallback
139 }
140
141 bool MaterialSerializer::save_json(const std::filesystem::path &filepath, const MaterialData &mat) {
142 nlohmann::json j;
143
144 j["version"] = VERSION;
145 j["name"] = mat.name;
146
147 j["colors"] = {
148 {"diffuse", vec3_to_json(mat.diffuse_color)},
149 {"ambient", vec3_to_json(mat.ambient_color)},
150 {"specular", vec3_to_json(mat.specular_color)},
151 {"emissive", vec3_to_json(mat.emissive_color)}
152 };
153
154 j["properties"] = {
155 {"opacity", mat.opacity},
156 {"shininess", mat.shininess},
157 {"metallic", mat.metallic},
158 {"roughness", mat.roughness}
159 };
160
161 j["uv"] = {
162 {"scale", vec2_to_json(mat.uv_scale)},
163 {"offset", vec2_to_json(mat.uv_offset)}
164 };
165
166 j["flags"] = {
167 {"double_sided", mat.double_sided},
168 {"alpha_blend", mat.alpha_blend},
169 {"alpha_cutoff", mat.alpha_cutoff}
170 };
171
172 // Textures
173 nlohmann::json textures = nlohmann::json::object();
174 for (const auto &[type, asset_id]: mat.texture_assets) {
175 textures[texture_type_to_string(type)] = asset_id;
176 }
177 j["textures"] = textures;
178
179 std::ofstream file(filepath);
180 if (!file) return false;
181
182 file << j.dump(2);
183 return file.good();
184 }
185
186 std::optional<MaterialData> MaterialSerializer::load_json(const std::filesystem::path &filepath) {
187 std::ifstream file(filepath);
188 if (!file) return std::nullopt;
189
190 try {
191 nlohmann::json j;
192 file >> j;
193
194 MaterialData mat;
195
196 mat.name = j.value("name", "Material");
197
198 // Colors
199 if (j.contains("colors")) {
200 const auto &colors = j["colors"];
201 if (auto v = json_get_vec3(colors, "diffuse")) mat.diffuse_color = *v;
202 if (auto v = json_get_vec3(colors, "ambient")) mat.ambient_color = *v;
203 if (auto v = json_get_vec3(colors, "specular")) mat.specular_color = *v;
204 if (auto v = json_get_vec3(colors, "emissive")) mat.emissive_color = *v;
205 }
206
207 // Properties
208 if (j.contains("properties")) {
209 const auto &props = j["properties"];
210 mat.opacity = props.value("opacity", 1.0f);
211 mat.shininess = props.value("shininess", 32.0f);
212 mat.metallic = props.value("metallic", 0.0f);
213 mat.roughness = props.value("roughness", 0.5f);
214 }
215
216 // UV
217 if (j.contains("uv")) {
218 const auto &uv = j["uv"];
219 if (auto v = json_get_vec2(uv, "scale")) mat.uv_scale = *v;
220 if (auto v = json_get_vec2(uv, "offset")) mat.uv_offset = *v;
221 }
222
223 // Flags
224 if (j.contains("flags")) {
225 const auto &flags = j["flags"];
226 mat.double_sided = flags.value("double_sided", false);
227 mat.alpha_blend = flags.value("alpha_blend", false);
228 mat.alpha_cutoff = flags.value("alpha_cutoff", 0.5f);
229 }
230
231 // Textures
232 if (j.contains("textures") && j["textures"].is_object()) {
233 for (const auto &[key, value]: j["textures"].items()) {
234 TextureType type = string_to_texture_type(key);
235 AssetID asset_id = value.get<AssetID>();
236 mat.texture_assets[type] = asset_id;
237 }
238 }
239
240 return mat;
241 } catch (const std::exception &e) {
242 std::cerr << "MaterialSerializer: JSON parse error: " << e.what() << std::endl;
243 return std::nullopt;
244 }
245 }
246}
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)