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