Brenta Engine 1.2
Loading...
Searching...
No Matches
model.cpp
1// SPDX-License-Identifier: MIT
2// Author: Giovanni Santini
3// Mail: giovanni.santini@proton.me
4// Github: @San7o
5
6#include <brenta/renderer/model.hpp>
7#include <brenta/logger.hpp>
8
9#include <tenno/std_interop.hpp>
10
11using namespace brenta;
12
13Model::Model(Config &conf)
14{
15 this->path = conf.model_path.string();
16 this->transform = conf.transform;
17 this->material = conf.material;
18
19 if (conf.model_path != "")
20 this->load(conf.texture_props);
21
22 for (size_t i = 0; i < conf.meshes.size(); ++i)
23 {
24 this->meshes.push_back(tenno::move(conf.meshes[i].build()));
25
26 for (auto& t : this->meshes.back().value()->textures)
27 this->textures_loaded.push_back(t);
28 }
29
30 EVENT(Logger::Event::Lifetime, "model: initialized {}",
31 this->path.string());
32 return;
33}
34
35Model::Model(Builder& builder)
36{
37 *this = builder.build();
38}
39
40Model::~Model()
41{
42 if (this->path.string() == "") return;
43
44 EVENT(Logger::Event::Lifetime, "model: destroyed {}",
45 this->path.string());
46 return;
47}
48
49void Model::draw() const
50{
51 if (this->material)
52 this->material->apply();
53
54 for (unsigned int i = 0; i < meshes.size(); i++)
55 {
56 meshes[i].draw();
57 }
58 return;
59}
60
61void Model::load(const Texture::Properties &props)
62{
63 tinyobj::ObjReaderConfig reader_config;
64 std::string path_str = path.string();
65 this->directory = path_str.substr(0, path_str.find_last_of('/'));
66 reader_config.mtl_search_path = this->directory; // Path to material files
67
68 tinyobj::ObjReader reader;
69
70 if (!reader.ParseFromFile(this->path.string(), reader_config))
71 {
72 if (!reader.Error().empty())
73 {
74 std::cerr << "TinyObjReader: " << reader.Error();
75 }
76 exit(1);
77 }
78
79 auto& attrib = reader.GetAttrib();
80 auto& shapes = reader.GetShapes();
81 auto& materials = reader.GetMaterials();
82
83 // Loop over shapes (equivalent to Assimp meshes/nodes)
84 for (size_t s = 0; s < shapes.size(); s++)
85 {
86 process_shape(attrib, shapes[s], tenno::from_std(materials), props);
87 }
88}
89
90void Model::process_shape(const tinyobj::attrib_t& attrib,
91 const tinyobj::shape_t& shape,
92 const tenno::vector<tinyobj::material_t>& materials,
93 const Texture::Properties& props)
94{
95 // Map material_id -> Mesh Data (vertices and indices)
96 std::map<int, tenno::vector<Mesh::Vertex>> per_mat_vertices;
97 std::map<int, tenno::vector<unsigned int>> per_mat_indices;
98
99 size_t index_offset = 0;
100
101 for (size_t f = 0; f < shape.mesh.num_face_vertices.size(); f++)
102 {
103 size_t fv = size_t(shape.mesh.num_face_vertices[f]);
104 int mat_id = shape.mesh.material_ids[f]; // Get material for THIS face
105
106 for (size_t v = 0; v < fv; v++)
107 {
108 tinyobj::index_t idx = shape.mesh.indices[index_offset + v];
109 Mesh::Vertex vertex;
110
111 // Positions
112 vertex.position = {
113 attrib.vertices[3 * idx.vertex_index + 0],
114 attrib.vertices[3 * idx.vertex_index + 1],
115 attrib.vertices[3 * idx.vertex_index + 2]
116 };
117
118 // Normals
119 if (idx.normal_index >= 0)
120 {
121 vertex.normal = {
122 attrib.normals[3 * idx.normal_index + 0],
123 attrib.normals[3 * idx.normal_index + 1],
124 attrib.normals[3 * idx.normal_index + 2]
125 };
126 }
127
128 // UVs with Flip
129 if (idx.texcoord_index >= 0)
130 {
131 vertex.tex_coords = {
132 attrib.texcoords[2 * idx.texcoord_index + 0],
133 1.0f - attrib.texcoords[2 * idx.texcoord_index + 1]
134 };
135 }
136
137 per_mat_vertices[mat_id].push_back(vertex);
138 per_mat_indices[mat_id].push_back(per_mat_indices[mat_id].size());
139 }
140 index_offset += fv;
141 }
142
143 // Now create a Mesh for each material group found in this shape
144 for (auto const& [mat_id, verts] : per_mat_vertices)
145 {
146 tenno::vector<tenno::shared_ptr<Texture>> textures;
147 if (mat_id >= 0)
148 {
149 textures = load_tiny_material(materials[mat_id], props);
150 }
151
152 // Push the mesh to your model's mesh list
153 meshes.push_back(Mesh({verts, per_mat_indices[mat_id], textures}));
154 }
155}
156
157tenno::vector<tenno::shared_ptr<Texture>>
158Model::load_tiny_material(const tinyobj::material_t& mat,
159 const Texture::Properties &props)
160{
161 tenno::vector<tenno::shared_ptr<Texture>> textures;
162
163 auto load_tex = [&](std::string tex_name, Texture::Type type)
164 {
165 if (tex_name.empty()) return;
166
167 std::string full_path = this->directory + "/" + tex_name;
168
169 // Check cache (textures_loaded)
170 for (auto& loaded : textures_loaded)
171 {
172 if (loaded->get_path() == full_path)
173 {
174 textures.push_back(loaded);
175 return;
176 }
177 }
178
179 // Use your Builder pattern
180 auto t =
181 tenno::make_shared<Texture>(Texture::Builder()
182 .type(type)
183 .target(Texture::Target::Texture2D)
184 .path(full_path)
185 .properties(props)
186 .build());
187
188 textures_loaded.push_back(t);
189 textures.push_back(t);
190 };
191
192 load_tex(mat.diffuse_texname, Texture::Type::Diffuse);
193 load_tex(mat.specular_texname, Texture::Type::Specular);
194
195 return textures;
196}
197
198//
199// Builder functions
200//
201
202Model::Builder &Model::Builder::transform(const Transform& transform)
203{
204 this->conf.transform = transform;
205 return *this;
206}
207
208Model::Builder &Model::Builder::material(tenno::shared_ptr<Material> material)
209{
210 this->conf.material = material;
211 return *this;
212}
213
214Model::Builder &Model::Builder::material(Material&& material)
215{
216 this->conf.material = tenno::make_shared<Material>(tenno::move(material));
217 return *this;
218}
219
220Model::Builder &Model::Builder::path(const std::filesystem::path &path)
221{
222 this->conf.model_path = path;
223 this->watch_paths.push_back(path);
224 return *this;
225}
226
227Model::Builder &Model::Builder::texture_props(const Texture::Properties &props)
228{
229 this->conf.texture_props = props;
230 return *this;
231}
232
233Model::Builder &Model::Builder::mesh(const Mesh::Builder &mesh)
234{
235 this->conf.meshes.push_back(mesh);
236 return *this;
237}
238
239Model::Builder &Model::Builder::mesh(Mesh::Builder &&mesh)
240{
241 this->conf.meshes.push_back(tenno::move(mesh));
242 return *this;
243}
244
245Model::Builder &Model::Builder::meshes(const tenno::vector<Mesh::Builder> &meshes)
246{
247 for (size_t i = 0; i < meshes.size(); ++i)
248 this->conf.meshes.push_back(tenno::move(meshes[i]));
249 return *this;
250}
251
252Model::Builder &Model::Builder::meshes(tenno::vector<Mesh::Builder> &&meshes)
253{
254 for (size_t i = 0; i < meshes.size(); ++i)
255 this->conf.meshes.push_back(meshes[i]);
256 return *this;
257}
258
259Model::Builder &Model::Builder::watch(const std::filesystem::path& path)
260{
261 this->watch_paths.push_back(path);
262 return *this;
263}
264
265Model Model::Builder::build()
266{
267 return Model(this->conf);
268}
269
270tenno::vector<std::filesystem::path> Model::Builder::get_watch_paths() const
271{
272 return this->watch_paths;
273}