Loading...
Searching...
No Matches
ModelSerializer.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#include "glm/glm.hpp"
12
13namespace hellfire {
14 bool ModelSerializer::save(const std::filesystem::path &filepath, const ImportResult &result) {
15 if (!result.success) {
16 std::cerr << "ModelSerializer: Cannot save failed import result" << std::endl;
17 return false;
18 }
19
20 std::ofstream file(filepath, std::ios::binary);
21 if (!file) {
22 std::cerr << "ModelSerializer: Cannot open file for writing: " << filepath << std::endl;
23 return false;
24 }
25
26 // Header
27 write_header(file, MAGIC, VERSION);
28
29 // Root node index
30 write_binary(file, static_cast<uint32_t>(result.root_node_index));
31
32 // Nodes
33 const uint32_t node_count = static_cast<uint32_t>(result.nodes.size());
34 write_binary(file, node_count);
35
36 for (const auto& node : result.nodes) {
37 write_binary_string(file, node.name);
38 write_binary(file, node.position);
39 write_binary(file, node.rotation);
40 write_binary(file, node.scale);
41
42 // Mesh indices
43 const uint32_t mesh_idx_count = static_cast<uint32_t>(node.mesh_indices.size());
44 write_binary(file, mesh_idx_count);
45 for (size_t idx : node.mesh_indices) {
46 write_binary(file, static_cast<uint32_t>(idx));
47 }
48
49 // Child indices
50 const uint32_t child_idx_count = static_cast<uint32_t>(node.child_indices.size());
51 write_binary(file, child_idx_count);
52 for (size_t idx : node.child_indices) {
53 write_binary(file, static_cast<uint32_t>(idx));
54 }
55 }
56
57 // Meshes
58 const uint32_t mesh_count = static_cast<uint32_t>(result.meshes.size());
59 write_binary(file, mesh_count);
60
61 for (const auto& mesh : result.meshes) {
62 write_binary_string(file, mesh.name);
63 write_binary(file, mesh.mesh_asset);
64 write_binary(file, mesh.material_asset);
65 }
66
67 return file.good();
68 }
69
70 std::optional<ImportResult> ModelSerializer::load(const std::filesystem::path &filepath) {
71 std::ifstream file(filepath, std::ios::binary);
72 if (!file) {
73 std::cerr << "ModelSerializer: Cannot open file: " << filepath << std::endl;
74 return std::nullopt;
75 }
76
77 // Validate header
78 uint32_t version;
79 if (!read_and_validate_header(file, MAGIC, VERSION, version)) {
80 std::cerr << "ModelSerializer: Invalid file header: " << filepath << std::endl;
81 return std::nullopt;
82 }
83
84 ImportResult result;
85 result.success = true;
86
87 // Root node index
88 uint32_t root_idx;
89 if (!read_binary(file, root_idx)) return std::nullopt;
90 result.root_node_index = root_idx;
91
92 // Nodes
93 uint32_t node_count;
94 if (!read_binary(file, node_count)) return std::nullopt;
95 result.nodes.resize(node_count);
96
97 for (auto& node : result.nodes) {
98 if (!read_binary_string(file, node.name)) return std::nullopt;
99 if (!read_binary(file, node.position)) return std::nullopt;
100 if (!read_binary(file, node.rotation)) return std::nullopt;
101 if (!read_binary(file, node.scale)) return std::nullopt;
102
103 // Mesh indices
104 uint32_t mesh_idx_count;
105 if (!read_binary(file, mesh_idx_count)) return std::nullopt;
106 node.mesh_indices.resize(mesh_idx_count);
107 for (uint32_t i = 0; i < mesh_idx_count; i++) {
108 uint32_t idx;
109 if (!read_binary(file, idx)) return std::nullopt;
110 node.mesh_indices[i] = idx;
111 }
112
113 // Child indices
114 uint32_t child_idx_count;
115 if (!read_binary(file, child_idx_count)) return std::nullopt;
116 node.child_indices.resize(child_idx_count);
117 for (uint32_t i = 0; i < child_idx_count; i++) {
118 uint32_t idx;
119 if (!read_binary(file, idx)) return std::nullopt;
120 node.child_indices[i] = idx;
121 }
122 }
123
124 // Meshes
125 uint32_t mesh_count;
126 if (!read_binary(file, mesh_count)) return std::nullopt;
127 result.meshes.resize(mesh_count);
128
129 for (auto& mesh : result.meshes) {
130 if (!read_binary_string(file, mesh.name)) return std::nullopt;
131 if (!read_binary(file, mesh.mesh_asset)) return std::nullopt;
132 if (!read_binary(file, mesh.material_asset)) return std::nullopt;
133 }
134
135 return result;
136 }
137
138 bool ModelSerializer::save_json(const std::filesystem::path &filepath, const ImportResult &result) {
139 if (!result.success) return false;
140
141 nlohmann::json j;
142 j["version"] = VERSION;
143 j["root_node_index"] = result.root_node_index;
144
145 // Nodes
146 auto& nodes_json = j["nodes"];
147 for (const auto& node : result.nodes) {
148 nlohmann::json node_json;
149 node_json["name"] = node.name;
150 node_json["position"] = vec3_to_json(node.position);
151 node_json["rotation"] = vec3_to_json(node.rotation);
152 node_json["scale"] = vec3_to_json(node.scale);
153 node_json["mesh_indices"] = node.mesh_indices;
154 node_json["child_indices"] = node.child_indices;
155 nodes_json.push_back(node_json);
156 }
157
158 // Meshes
159 auto& meshes_json = j["meshes"];
160 for (const auto& mesh : result.meshes) {
161 meshes_json.push_back({
162 {"name", mesh.name},
163 {"mesh_asset", mesh.mesh_asset},
164 {"material_asset", mesh.material_asset}
165 });
166 }
167
168 std::ofstream file(filepath);
169 if (!file) return false;
170
171 file << j.dump(2);
172 return file.good();
173 }
174
175 std::optional<ImportResult> ModelSerializer::load_json(const std::filesystem::path &filepath) {
176 std::ifstream file(filepath);
177 if (!file) return std::nullopt;
178
179 try {
180 nlohmann::json j;
181 file >> j;
182
183 ImportResult result;
184 result.success = true;
185 result.root_node_index = j.value("root_node_index", 0);
186
187 // Nodes
188 for (const auto& node_json : j["nodes"]) {
189 ImportedNode node;
190 node.name = node_json.value("name", "Node");
191
192 if (auto v = json_get_vec3(node_json, "position")) node.position = *v;
193 if (auto v = json_get_vec3(node_json, "rotation")) node.rotation = *v;
194 if (auto v = json_get_vec3(node_json, "scale")) node.scale = *v;
195 else node.scale = glm::vec3(1.0f);
196
197 node.mesh_indices = node_json["mesh_indices"].get<std::vector<size_t>>();
198 node.child_indices = node_json["child_indices"].get<std::vector<size_t>>();
199
200 result.nodes.push_back(node);
201 }
202
203 // Meshes
204 for (const auto& mesh_json : j["meshes"]) {
205 ImportedMesh mesh;
206 mesh.name = mesh_json.value("name", "Mesh");
207 mesh.mesh_asset = mesh_json.value("mesh_asset", INVALID_ASSET_ID);
208 mesh.material_asset = mesh_json.value("material_asset", INVALID_ASSET_ID);
209 result.meshes.push_back(mesh);
210 }
211
212 return result;
213 } catch (const std::exception& e) {
214 std::cerr << "ModelSerializer: JSON parse error: " << e.what() << std::endl;
215 return std::nullopt;
216 }
217 }
218} // hellfire
Serializes the ImportResult to a .hfmodel file.
static std::optional< ImportResult > load_json(const std::filesystem::path &filepath)
static constexpr uint32_t VERSION
static bool save(const std::filesystem::path &filepath, const ImportResult &result)
static constexpr uint32_t MAGIC
static bool save_json(const std::filesystem::path &filepath, const ImportResult &result)
static std::optional< ImportResult > load(const std::filesystem::path &filepath)
Complete result of importing a model file.