Botcraft 1.21.4
Loading...
Searching...
No Matches
Model.cpp
Go to the documentation of this file.
4
6
7#include <sstream>
8#include <fstream>
9
10using namespace ProtocolCraft;
11
12namespace Botcraft
13{
14 std::unordered_map<std::string, Model> Model::cached_models;
15
17 {
18#if USE_GUI
19 ambient_occlusion = false;
20#endif
21 }
22
23 const Model& Model::GetModel(const std::string& filepath, const bool custom)
24 {
25 auto cached = cached_models.find(filepath);
26 if (cached != cached_models.end())
27 {
28 return cached->second;
29 }
30 cached_models[filepath] = Model(filepath, custom);
31 return cached_models[filepath];
32 }
33
34 Model Model::GetModel(const double height, const std::string& texture)
35 {
36 return Model(height, texture);
37 }
38
39 Model::Model(const std::string& filepath, const bool custom)
40 {
41 std::string full_filepath;
42
43 if (custom)
44 {
45 full_filepath = ASSETS_PATH + std::string("/custom/models/") + filepath + ".json";
46 }
47 else
48 {
49 full_filepath = ASSETS_PATH + std::string("/minecraft/models/") + filepath + ".json";
50 }
51
52 bool error = filepath == "";
53 Json::Value obj;
54
55 if (!error)
56 {
57 try
58 {
59 std::ifstream file(full_filepath);
60 file >> obj;
61 file.close();
62 }
63 catch (const std::runtime_error& e)
64 {
65 LOG_ERROR("Error reading model file " << full_filepath << '\n' << e.what());
66 error = true;
67 }
68 }
69
70 //Create default model (Full solid cube)
71 if (error)
72 {
73 colliders.insert(AABB(Vector3<double>(0.5, 0.5, 0.5), Vector3<double>(0.5, 0.5, 0.5)));
74#if USE_GUI
75 ambient_occlusion = false;
76 faces = std::vector<FaceDescriptor>();
77 for (int i = 0; i < 6; ++i)
78 {
79 FaceDescriptor current_face;
80 current_face.cullface_direction = (Orientation)i;
81 current_face.orientation = (Orientation)i;
82 current_face.transformations.scales.push_back(Renderer::ScalePtr(new Renderer::Scale(0.5f, 0.5f, 0.5f)));
83 current_face.face = Renderer::Face(current_face.transformations, current_face.orientation);
84 faces.push_back(current_face);
85 }
86#endif
87 return;
88 }
89
90 //Default values
91#if USE_GUI
92 ambient_occlusion = true;
93#endif
94
95 if (obj.contains("parent"))
96 {
97#if PROTOCOL_VERSION > 578 /* > 1.15.2 */
98 // Remove the minecraft: in front of the parent model name
99 std::string model_name = obj["parent"].get_string();
100 if (Utilities::StartsWith(model_name, "minecraft:"))
101 {
102 model_name = model_name.substr(10);
103 }
104 const Model& parent_model = GetModel(model_name, custom);
105#else
106 const Model& parent_model = GetModel(obj["parent"].get_string(), custom);
107#endif
108 colliders = parent_model.colliders;
109#if USE_GUI
112 faces = parent_model.faces;
114#endif
115 }
116
117#if USE_GUI
118 if (obj.contains("ambientocclusion"))
119 {
120 ambient_occlusion = obj["ambientocclusion"].get<bool>();
121 }
122#endif
123
124 //TODO do something with display information?
125
126#if USE_GUI
127 if (obj.contains("textures_base_size"))
128 {
129 for (const auto& [key, val] : obj["textures_base_size"].get_object())
130 {
131 textures_base_size[key] = std::pair<int, int>(val[0].get_number<int>(), val[1].get_number<int>());
132 }
133 }
134
135 if (obj.contains("textures"))
136 {
137 for (const auto& [key, val] : obj["textures"].get_object())
138 {
139#if PROTOCOL_VERSION > 578 /* > 1.15.2 */
140 // Remove leading minecraft: from the path of the textures
141 std::string texture_name = val.get_string();
142 if (Utilities::StartsWith(texture_name, "minecraft:"))
143 {
144 texture_name = texture_name.substr(10);
145 }
146#else
147 const std::string& texture_name = val.get_string();
148#endif
149 textures_variables["#" + key] = texture_name;
150 if (val.get_string().rfind("#", 0) == 0)
151 {
152 textures_variables[val.get_string()] = val.get_string();
153 }
154 }
155 }
156#endif
157
158 if (obj.contains("elements"))
159 {
160#if USE_GUI
161 //Override any elements from the parent
162 faces.clear();
163#endif
164
165 for (const auto& element : obj["elements"].get_array())
166 {
167#if USE_GUI
168 std::vector<FaceDescriptor> current_element;
169 Renderer::FaceTransformation element_global_transformations;
170#endif
171 int start_x, start_y, start_z;
172 int end_x, end_y, end_z;
173
174 if (element.contains("from"))
175 {
176 start_x = element["from"][0].get_number<int>();
177 start_y = element["from"][1].get_number<int>();
178 start_z = element["from"][2].get_number<int>();
179 }
180
181 if (element.contains("to"))
182 {
183 end_x = element["to"][0].get_number<int>();
184 end_y = element["to"][1].get_number<int>();
185 end_z = element["to"][2].get_number<int>();
186 }
187
188 colliders.insert(AABB(Vector3<double>(start_x + end_x, start_y + end_y, start_z + end_z) / 2.0 / 16.0, Vector3<double>(std::abs(end_x - start_x), std::abs(end_y - start_y), std::abs(end_z - start_z)) / 2.0 / 16.0));
189
190#if USE_GUI
191 if (element.contains("rotation"))
192 {
193 float origin_x, origin_y, origin_z;
194 origin_x = element["rotation"]["origin"][0].get_number<float>();
195 origin_y = element["rotation"]["origin"][1].get_number<float>();
196 origin_z = element["rotation"]["origin"][2].get_number<float>();
197
198 //Add the rotation to global transformations
199
200 const float angle = element["rotation"]["angle"].get_number<float>();
201 element_global_transformations.rotations.push_back(Renderer::TransformationPtr(new Renderer::Translation(-((start_x + end_x) / 2.0f - origin_x) / 16.0f, -((start_y + end_y) / 2.0f - origin_y) / 16.0f, -((start_z + end_z) / 2.0f - origin_z) / 16.0f)));
202 if (element["rotation"]["axis"].get_string() == "x")
203 {
204 element_global_transformations.rotations.push_back(Renderer::TransformationPtr(new Renderer::Rotation(1.0f, 0.0f, 0.0f, angle)));
205 }
206 else if (element["rotation"]["axis"].get_string() == "y")
207 {
208 element_global_transformations.rotations.push_back(Renderer::TransformationPtr(new Renderer::Rotation(0.0f, 1.0f, 0.0f, angle)));
209 }
210 else if (element["rotation"]["axis"].get_string() == "z")
211 {
212 element_global_transformations.rotations.push_back(Renderer::TransformationPtr(new Renderer::Rotation(0.0f, 0.0f, 1.0f, angle)));
213 }
214 element_global_transformations.rotations.push_back(Renderer::TransformationPtr(new Renderer::Translation(((start_x + end_x) / 2.0f - origin_x) / 16.0f, ((start_y + end_y) / 2.0f - origin_y) / 16.0f, ((start_z + end_z) / 2.0f - origin_z) / 16.0f)));
215
216 bool rescale = false;
217 if (element["rotation"].contains("rescale"))
218 {
219 rescale = element["rotation"]["rescale"].get<bool>();
220 }
221
222 if (rescale)
223 {
224 float scale_factor = std::abs(1.0f / (std::cos(angle * 3.14159f / 180.0f)));
225 if (element["rotation"]["axis"].get_string() == "x")
226 {
227 element_global_transformations.scales.push_back(Renderer::ScalePtr(new Renderer::Scale(1.0f, scale_factor, scale_factor)));
228 }
229 else if (element["rotation"]["axis"].get_string() == "y")
230 {
231 element_global_transformations.scales.push_back(Renderer::ScalePtr(new Renderer::Scale(scale_factor, 1.0f, scale_factor)));
232 }
233 else if (element["rotation"]["axis"].get_string() == "z")
234 {
235 element_global_transformations.scales.push_back(Renderer::ScalePtr(new Renderer::Scale(scale_factor, scale_factor, 1.0f)));
236 }
237 }
238 }
239
240 if (element.contains("shade"))
241 {
242 //TODO do something with shade value
243 }
244
245 if (element.contains("faces"))
246 {
247 for (const auto& [key, face_params] : element["faces"].get_object())
248 {
249 FaceDescriptor current_face;
250 if (key == "down")
251 {
252 current_face.orientation = Orientation::Bottom;
253 }
254 else if (key == "up")
255 {
256 current_face.orientation = Orientation::Top;
257 }
258 else if (key == "north")
259 {
260 current_face.orientation = Orientation::North;
261 }
262 else if (key == "south")
263 {
264 current_face.orientation = Orientation::South;
265 }
266 else if (key == "east")
267 {
268 current_face.orientation = Orientation::East;
269 }
270 else if (key == "west")
271 {
272 current_face.orientation = Orientation::West;
273 }
274
275 if (face_params.contains("uv"))
276 {
277 current_face.transformations.offset_x1 = face_params["uv"][0].get_number<float>();
278 current_face.transformations.offset_y1 = face_params["uv"][1].get_number<float>();
279 current_face.transformations.offset_x2 = face_params["uv"][2].get_number<float>();
280 current_face.transformations.offset_y2 = face_params["uv"][3].get_number<float>();
281 }
282 //If UV are not specified, we have to get them from [x,y,z] coordinates
283 else
284 {
285 switch (current_face.orientation)
286 {
288 current_face.transformations.offset_x1 = static_cast<float>(start_x);
289 current_face.transformations.offset_y1 = static_cast<float>(start_z);
290 current_face.transformations.offset_x2 = static_cast<float>(end_x);
291 current_face.transformations.offset_y2 = static_cast<float>(end_z);
292 break;
293 case Orientation::Top:
294 current_face.transformations.offset_x1 = static_cast<float>(start_x);
295 current_face.transformations.offset_y1 = static_cast<float>(start_z);
296 current_face.transformations.offset_x2 = static_cast<float>(end_x);
297 current_face.transformations.offset_y2 = static_cast<float>(end_z);
298 break;
300 current_face.transformations.offset_x1 = static_cast<float>(start_z);
301 current_face.transformations.offset_y1 = static_cast<float>(16 - end_y);
302 current_face.transformations.offset_x2 = static_cast<float>(end_z);
303 current_face.transformations.offset_y2 = static_cast<float>(16 - start_y);
304 break;
306 current_face.transformations.offset_x1 = static_cast<float>(start_z);
307 current_face.transformations.offset_y1 = static_cast<float>(16 - end_y);
308 current_face.transformations.offset_x2 = static_cast<float>(end_z);
309 current_face.transformations.offset_y2 = static_cast<float>(16 - start_y);
310 break;
312 current_face.transformations.offset_x1 = static_cast<float>(start_x);
313 current_face.transformations.offset_y1 = static_cast<float>(16 - end_y);
314 current_face.transformations.offset_x2 = static_cast<float>(end_x);
315 current_face.transformations.offset_y2 = static_cast<float>(16 - start_y);
316 break;
318 current_face.transformations.offset_x1 = static_cast<float>(start_x);
319 current_face.transformations.offset_y1 = static_cast<float>(16 - end_y);
320 current_face.transformations.offset_x2 = static_cast<float>(end_x);
321 current_face.transformations.offset_y2 = static_cast<float>(16 - start_y);
322 break;
323 default:
324 break;
325 }
326 }
327
328 if (face_params.contains("texture"))
329 {
330 const std::string& texture_name = face_params["texture"].get_string();
331 current_face.texture_names[0] = texture_name;
332 if (textures_variables.find(texture_name) == textures_variables.end())
333 {
334 textures_variables[texture_name] = texture_name;
335 }
336 }
337
338 if (face_params.contains("cullface"))
339 {
340 const std::string& value = face_params["cullface"].get_string();
341 if (value == "down")
342 {
344 }
345 else if (value == "up")
346 {
348 }
349 else if (value == "north")
350 {
352 }
353 else if (value == "south")
354 {
356 }
357 else if (value == "west")
358 {
360 }
361 else if (value == "east")
362 {
364 }
365 else
366 {
368 }
369 }
370 else
371 {
373 }
374
375 if (face_params.contains("rotation"))
376 {
377 current_face.transformations.rotation = face_params["rotation"].get_number<int>() / 90;
378 }
379
380 if (face_params.contains("tintindex"))
381 {
382 current_face.use_tintindexes[0] = true;
383 }
384 else
385 {
386 current_face.use_tintindexes[0] = false;
387 }
388 current_face.face = Renderer::Face(current_face.transformations, current_face.orientation);
389 current_element.push_back(current_face);
390 }
391 }
392
393 //Add the scaling and translation of this element (based on from to)
394 float center_x = (end_x + start_x) / 2.0f;
395 float center_y = (end_y + start_y) / 2.0f;
396 float center_z = (end_z + start_z) / 2.0f;
397
398 if (center_x != 8.0f || center_y != 8.0f || center_z != 8.0f)
399 {
400 element_global_transformations.translations.push_back(Renderer::TransformationPtr(new Renderer::Translation((center_x - 8) / 16.0f, (center_y - 8) / 16.0f, (center_z - 8) / 16.0f)));
401 }
402
403 //We divide by 32 because the base face size is 2 and a block size is 1
404 element_global_transformations.scales.push_back(Renderer::ScalePtr(new Renderer::Scale((end_x - start_x) / 32.0f, (end_y - start_y) / 32.0f, (end_z - start_z) / 32.0f)));
405
406 //Add the global transformations of this element to all the faces
407 for (int m = 0; m < current_element.size(); ++m)
408 {
409 for (int n = 0; n < element_global_transformations.scales.size(); ++n)
410 {
411 current_element[m].transformations.scales.push_back(element_global_transformations.scales[n]);
412 }
413 for (int n = 0; n < element_global_transformations.rotations.size(); ++n)
414 {
415 current_element[m].transformations.rotations.push_back(element_global_transformations.rotations[n]);
416 }
417 for (int n = 0; n < element_global_transformations.translations.size(); ++n)
418 {
419 current_element[m].transformations.translations.push_back(element_global_transformations.translations[n]);
420 }
421 current_element[m].face = Renderer::Face(current_element[m].transformations, current_element[m].orientation);
422 }
423
424 //Add the faces of the current element to the global faces
425 faces.insert(faces.end(), current_element.begin(), current_element.end());
426#endif
427 }
428 }
429
430#if USE_GUI
431 bool has_changed = true;
432 while (has_changed)
433 {
434 has_changed = false;
435 // Replace all the textures variables with their identifiers
436 for (int i = static_cast<int>(faces.size()) - 1; i > -1; --i)
437 {
438 // Treat #overlay as special cases
439 if (faces[i].texture_names[0].rfind("#overlay", 0) == 0)
440 {
441 int matching_index = -1;
442 for (int j = 0; j < faces.size(); ++j)
443 {
444 if (i != j && faces[i].face == faces[j].face)
445 {
446 matching_index = j;
447 break;
448 }
449 }
450
451 // We found the same face
452 if (matching_index != -1)
453 {
454 faces[matching_index].texture_names.push_back(faces[i].texture_names[0]);
455 faces[matching_index].use_tintindexes.push_back(faces[i].use_tintindexes[0]);
456 }
457 faces.erase(faces.begin() + i);
458 has_changed = true;
459 continue;
460 }
461 for (int s = 0; s < faces[i].texture_names.size(); ++s)
462 {
463 std::string variable_name = faces[i].texture_names[s];
464 while (variable_name.rfind("#", 0) == 0 && variable_name != textures_variables[variable_name])
465 {
466 variable_name = textures_variables[variable_name];
467 has_changed = true;
468 }
469 faces[i].texture_names[s] = variable_name;
470 }
471 }
472 }
473
474 // Once all the texture names have been replaced
475 // by their identifiers, multiply uv by texture
476 // sizes for special cases with non 64x64 default
477 // sizes
478 for (size_t i = 0; i < faces.size(); ++i)
479 {
480 if (faces[i].texture_names.size() == 0)
481 {
482 continue;
483 }
484
485 auto it = textures_base_size.find(faces[i].texture_names[0]);
486 if (it != textures_base_size.end())
487 {
488 faces[i].transformations.offset_x1 *= 16.0f / it->second.first;
489 faces[i].transformations.offset_y1 *= 16.0f / it->second.second;
490 faces[i].transformations.offset_x2 *= 16.0f / it->second.first;
491 faces[i].transformations.offset_y2 *= 16.0f / it->second.second;
492
493 faces[i].face = Renderer::Face(faces[i].transformations, faces[i].orientation);
494 }
495 }
496#endif
497 }
498
499 Model::Model(const double height, const std::string& texture)
500 {
501 colliders = std::set<AABB>({ AABB(Vector3<double>(0.5, 0.5 * height, 0.5), Vector3<double>(0.5, 0.5 * height, 0.5)) });
502
503#if USE_GUI
504 ambient_occlusion = false;
505 faces = std::vector<FaceDescriptor>(6);
506 for (int i = 0; i < 6; ++i)
507 {
508 faces[i].orientation = (Orientation)i;
509 faces[i].texture_names = { texture };
510 faces[i].cullface_direction = (Orientation)i;
511 faces[i].use_tintindexes = { false };
512
513 faces[i].transformations.scales.push_back(std::make_shared<Renderer::Scale>(0.5f, 0.5f * static_cast<float>(height), 0.5f));
514 faces[i].transformations.translations.push_back(std::make_shared<Renderer::Translation>(0.0f, 0.5f * static_cast<float>(height) - 0.5f, 0.0f));
515
516 faces[i].transformations.rotation = 0;
517
518 switch (faces[i].orientation)
519 {
521 faces[i].transformations.offset_x1 = 0.0f;
522 faces[i].transformations.offset_y1 = 0.0f;
523 faces[i].transformations.offset_x2 = 16.0f;
524 faces[i].transformations.offset_y2 = 16.0f;
525 break;
526 case Orientation::Top:
527 faces[i].transformations.offset_x1 = 0.0f;
528 faces[i].transformations.offset_y1 = 0.0f;
529 faces[i].transformations.offset_x2 = 16.0f;
530 faces[i].transformations.offset_y2 = 16.0f;
531 break;
533 faces[i].transformations.offset_x1 = 0.0f;
534 faces[i].transformations.offset_y1 = 16.0f * (1.0f - static_cast<float>(height));
535 faces[i].transformations.offset_x2 = 16.0f;
536 faces[i].transformations.offset_y2 = 16.0f;
537 break;
539 faces[i].transformations.offset_x1 = 0.0f;
540 faces[i].transformations.offset_y1 = 16.0f * (1.0f - static_cast<float>(height));
541 faces[i].transformations.offset_x2 = 16.0f;
542 faces[i].transformations.offset_y2 = 16.0f;
543 break;
545 faces[i].transformations.offset_x1 = 0.0f;
546 faces[i].transformations.offset_y1 = 16.0f * (1.0f - static_cast<float>(height));
547 faces[i].transformations.offset_x2 = 16.0f;
548 faces[i].transformations.offset_y2 = 16.0f;
549 break;
551 faces[i].transformations.offset_x1 = 0.0f;
552 faces[i].transformations.offset_y1 = 16.0f * (1.0f - static_cast<float>(height));
553 faces[i].transformations.offset_x2 = 16.0f;
554 faces[i].transformations.offset_y2 = 16.0f;
555 break;
556 default:
557 break;
558 }
559 faces[i].face = Renderer::Face(faces[i].transformations, faces[i].orientation);
560 }
561#endif
562 }
563
565 {
566 this->colliders.insert(m.colliders.begin(), m.colliders.end());
567#if USE_GUI
569 this->faces.insert(this->faces.end(), m.faces.begin(), m.faces.end());
570#endif
571 return *this;
572 }
573
574 bool Model::IsSame(const Model& other) const
575 {
576 // If we use GUI, models differ in how they are rendered.
577 // TODO: find a way to check if rendered models are the same?
578#if USE_GUI
579 return false;
580#else
581 return colliders == other.colliders;
582#endif
583 }
584
585 const std::set<AABB>& Model::GetColliders() const
586 {
587 return colliders;
588 }
589
590 void Model::SetColliders(const std::set<AABB>& colliders_)
591 {
592 colliders = colliders_;
593 }
594
596 {
597 cached_models.clear();
598 }
599
600#if USE_GUI
601 const std::vector<FaceDescriptor>& Model::GetFaces() const
602 {
603 return faces;
604 }
605
606 std::vector<FaceDescriptor>& Model::GetFaces()
607 {
608 return faces;
609 }
610#endif
611} //Botcraft
#define LOG_ERROR(osstream)
Definition Logger.hpp:45
const std::set< AABB > & GetColliders() const
Definition Model.cpp:585
Model & operator+=(const Model &m)
Definition Model.cpp:564
static void ClearCache()
Definition Model.cpp:595
void SetColliders(const std::set< AABB > &colliders_)
Definition Model.cpp:590
static std::unordered_map< std::string, Model > cached_models
Definition Model.hpp:74
std::map< std::string, std::string > textures_variables
Definition Model.hpp:78
const std::vector< FaceDescriptor > & GetFaces() const
Definition Model.cpp:601
std::map< std::string, std::pair< int, int > > textures_base_size
Definition Model.hpp:79
std::vector< FaceDescriptor > faces
Definition Model.hpp:82
static const Model & GetModel(const std::string &filepath, const bool custom)
Definition Model.cpp:23
std::set< AABB > colliders
Definition Model.hpp:84
bool ambient_occlusion
Definition Model.hpp:77
bool IsSame(const Model &other) const
Compare two models.
Definition Model.cpp:574
Main class, basically a JsonVariant with extra utility functions it doesn't inherit JsonVariant direc...
Definition Json.hpp:45
bool contains(const std::string &s) const
Definition Json.cpp:232
std::string & get_string()
Definition Json.cpp:119
std::shared_ptr< Scale > ScalePtr
std::shared_ptr< Transformation > TransformationPtr
bool StartsWith(const std::string &mainStr, const std::string &toMatch)
Renderer::FaceTransformation transformations
Definition Model.hpp:28
std::vector< bool > use_tintindexes
Definition Model.hpp:33
Orientation cullface_direction
Definition Model.hpp:32
std::vector< std::string > texture_names
Definition Model.hpp:31
Renderer::Face face
Definition Model.hpp:30
Orientation orientation
Definition Model.hpp:29
std::vector< TransformationPtr > rotations
std::vector< TransformationPtr > translations