1 // SuperTuxKart - a fun racing game with go-kart
2 // Copyright (C) 2017 SuperTuxKart-Team
3 //
4 // This program is free software; you can redistribute it and/or
5 // modify it under the terms of the GNU General Public License
6 // as published by the Free Software Foundation; either version 3
7 // of the License, or (at your option) any later version.
8 //
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU General Public License for more details.
13 //
14 // You should have received a copy of the GNU General Public License
15 // along with this program; if not, write to the Free Software
16 // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17
18 #include "graphics/sp_mesh_loader.hpp"
19
20 #include "graphics/sp/sp_mesh.hpp"
21 #include "graphics/sp/sp_mesh_buffer.hpp"
22 #include "graphics/central_settings.hpp"
23 #include "graphics/material_manager.hpp"
24 #include "graphics/stk_tex_manager.hpp"
25 #include "utils/constants.hpp"
26 #include "utils/mini_glm.hpp"
27
28 #include "../../lib/irrlicht/source/Irrlicht/CSkinnedMesh.h"
29 const uint8_t VERSION_NOW = 1;
30
31 #include <algorithm>
32 #include <cmath>
33 #include <IVideoDriver.h>
34 #include <IFileSystem.h>
35
36 // ----------------------------------------------------------------------------
isALoadableFileExtension(const io::path & filename) const37 bool SPMeshLoader::isALoadableFileExtension(const io::path& filename) const
38 {
39 return core::hasFileExtension(filename, "spm");
40 } // isALoadableFileExtension
41
42 // ----------------------------------------------------------------------------
createMesh(io::IReadFile * f)43 scene::IAnimatedMesh* SPMeshLoader::createMesh(io::IReadFile* f)
44 {
45 #ifndef SERVER_ONLY
46 const bool real_spm = CVS->isGLSL();
47 #else
48 const bool real_spm = false;
49 #endif
50 if (!IS_LITTLE_ENDIAN)
51 {
52 Log::error("SPMeshLoader", "Not little endian machine.");
53 return NULL;
54 }
55 if (f == NULL)
56 {
57 return NULL;
58 }
59 m_bind_frame = 0;
60 m_joint_count = 0;
61 m_frame_count = 0;
62 m_mesh = NULL;
63 m_mesh = real_spm ? new SP::SPMesh() : m_scene_manager->createSkinnedMesh();
64 io::IFileSystem* fs = m_scene_manager->getFileSystem();
65 std::string base_path = fs->getFileDir(f->getFileName()).c_str();
66 std::string header;
67 header.resize(2);
68 f->read(&header.front(), 2);
69 if (header != "SP")
70 {
71 Log::error("SPMeshLoader", "Not a spm file.");
72 m_mesh->drop();
73 return NULL;
74 }
75 uint8_t byte = 0;
76 f->read(&byte, 1);
77 uint8_t version = byte >> 3;
78 if (version != VERSION_NOW)
79 {
80 Log::error("SPMeshLoader", "Version mismatch, file %d SP %d", version,
81 VERSION_NOW);
82 m_mesh->drop();
83 return NULL;
84 }
85 byte &= ~0x08;
86 header = byte == 0 ? "SPMS" : byte == 1 ? "SPMA" : "SPMN";
87 if (header == "SPMS")
88 {
89 Log::error("SPMeshLoader", "Space partitioned mesh not supported.");
90 m_mesh->drop();
91 return NULL;
92 }
93 f->read(&byte, 1);
94 bool read_normal = byte & 0x01;
95 bool read_vcolor = byte >> 1 & 0x01;
96 bool read_tangent = byte >> 2 & 0x01;
97 const bool is_skinned = header == "SPMA";
98 const SPVertexType vt = is_skinned ? SPVT_SKINNED : SPVT_NORMAL;
99 float bbox[6];
100 f->read(bbox, 24);
101 uint16_t size_num = 0;
102 f->read(&size_num, 2);
103 unsigned id = 0;
104 std::unordered_map<unsigned, std::tuple<video::SMaterial, bool,
105 bool> > mat_map;
106 std::unordered_map<unsigned, std::tuple<Material*, bool,
107 bool> > sp_mat_map;
108 while (size_num != 0)
109 {
110 uint8_t tex_size;
111 std::string tex_name_1, tex_name_2;
112 f->read(&tex_size, 1);
113 if (tex_size > 0)
114 {
115 tex_name_1.resize(tex_size);
116 f->read(&tex_name_1.front(), tex_size);
117 }
118 f->read(&tex_size, 1);
119 if (tex_size > 0)
120 {
121 tex_name_2.resize(tex_size);
122 f->read(&tex_name_2.front(), tex_size);
123 }
124 if (real_spm)
125 {
126 if (!tex_name_1.empty())
127 {
128 std::string full_path = base_path + "/" + tex_name_1;
129 if (fs->existFile(full_path.c_str()))
130 {
131 tex_name_1 = full_path;
132 }
133 }
134 sp_mat_map[id] =
135 std::make_tuple(
136 material_manager->getMaterialSPM(tex_name_1, tex_name_2),
137 !tex_name_1.empty(), !tex_name_2.empty());
138 }
139 else
140 {
141 video::ITexture* textures[2] = { NULL, NULL };
142 if (!tex_name_1.empty())
143 {
144 std::string full_path = base_path + "/" + tex_name_1;
145 if (fs->existFile(full_path.c_str()))
146 {
147 tex_name_1 = full_path;
148 }
149 video::ITexture* tex = STKTexManager::getInstance()
150 ->getTexture(tex_name_1);
151 if (tex != NULL)
152 {
153 textures[0] = tex;
154 }
155 }
156 if (!tex_name_2.empty())
157 {
158 std::string full_path = base_path + "/" + tex_name_2;
159 if (fs->existFile(full_path.c_str()))
160 {
161 tex_name_2 = full_path;
162 }
163 textures[1] = STKTexManager::getInstance()->getTexture
164 (tex_name_2);
165 }
166
167 video::SMaterial m;
168 m.MaterialType = video::EMT_SOLID;
169 if (textures[0] != NULL)
170 {
171 m.setTexture(0, textures[0]);
172 }
173 if (textures[1] != NULL)
174 {
175 m.setTexture(1, textures[1]);
176 }
177 mat_map[id] =
178 std::make_tuple(m, !tex_name_1.empty(), !tex_name_2.empty());
179 }
180 size_num--;
181 id++;
182 }
183 f->read(&size_num, 2);
184 while (size_num != 0)
185 {
186 uint16_t mat_size;
187 f->read(&mat_size, 2);
188 while (mat_size != 0)
189 {
190 uint32_t vertices_count, indices_count;
191 uint16_t mat_id;
192 f->read(&vertices_count, 4);
193 if (vertices_count > 65535)
194 {
195 Log::error("SPMeshLoader", "32bit index not supported.");
196 m_mesh->drop();
197 return NULL;
198 }
199 f->read(&indices_count, 4);
200 f->read(&mat_id, 2);
201 if (real_spm)
202 {
203 assert(mat_id < sp_mat_map.size());
204 decompressSPM(f, vertices_count, indices_count, read_normal,
205 read_vcolor, read_tangent, std::get<1>(sp_mat_map[mat_id]),
206 std::get<2>(sp_mat_map[mat_id]), vt,
207 std::get<0>(sp_mat_map[mat_id]));
208 }
209 else
210 {
211 assert(mat_id < mat_map.size());
212 decompress(f, vertices_count, indices_count, read_normal,
213 read_vcolor, read_tangent, std::get<1>(mat_map[mat_id]),
214 std::get<2>(mat_map[mat_id]), vt,
215 std::get<0>(mat_map[mat_id]));
216 }
217 mat_size--;
218 }
219 if (header == "SPMS")
220 {
221 // Reserved, never used
222 assert(false);
223 f->read(bbox, 24);
224 }
225 size_num--;
226 }
227 if (header == "SPMA")
228 {
229 createAnimationData(f);
230 convertIrrlicht();
231 }
232 else if (header == "SPMS")
233 {
234 // Reserved, never used
235 assert(false);
236 uint16_t pre_computed_size = 0;
237 f->read(&pre_computed_size, 2);
238 }
239 const bool has_armature = !m_all_armatures.empty();
240 if (real_spm)
241 {
242 SP::SPMesh* spm = static_cast<SP::SPMesh*>(m_mesh);
243 spm->m_bind_frame = m_bind_frame;
244 spm->m_joint_using = m_joint_count;
245 // Because the last frame in spm is usable
246 if (has_armature)
247 {
248 spm->m_frame_count = m_frame_count + 1;
249 }
250 for (unsigned i = 0; i < m_all_armatures.size(); i++)
251 {
252 // This is diffferent from m_joint_using
253 spm->m_total_joints +=
254 (unsigned)m_all_armatures[i].m_joint_names.size();
255 }
256 spm->m_all_armatures = std::move(m_all_armatures);
257 }
258 m_mesh->finalize();
259 if (!real_spm && has_armature)
260 {
261 // Because the last frame in spm is usable
262 static_cast<scene::CSkinnedMesh*>(m_mesh)->AnimationFrames =
263 (float)m_frame_count + 1.0f;
264 }
265 m_all_armatures.clear();
266 m_to_bind_pose_matrices.clear();
267 m_joints.clear();
268 return m_mesh;
269 } // createMesh
270
271 // ----------------------------------------------------------------------------
decompressSPM(irr::io::IReadFile * spm,unsigned vertices_count,unsigned indices_count,bool read_normal,bool read_vcolor,bool read_tangent,bool uv_one,bool uv_two,SPVertexType vt,Material * m)272 void SPMeshLoader::decompressSPM(irr::io::IReadFile* spm,
273 unsigned vertices_count,
274 unsigned indices_count, bool read_normal,
275 bool read_vcolor, bool read_tangent,
276 bool uv_one, bool uv_two, SPVertexType vt,
277 Material* m)
278 {
279 assert(vertices_count != 0);
280 assert(indices_count != 0);
281
282 using namespace SP;
283 SPMeshBuffer* mb = new SPMeshBuffer();
284 static_cast<SPMesh*>(m_mesh)->m_buffer.push_back(mb);
285 const unsigned idx_size = vertices_count > 255 ? 2 : 1;
286 for (unsigned i = 0; i < vertices_count; i++)
287 {
288 video::S3DVertexSkinnedMesh vertex = {};
289 // 3 * float position
290 spm->read(&vertex.m_position, 12);
291 if (read_normal)
292 {
293 spm->read(&vertex.m_normal, 4);
294 }
295 else
296 {
297 // 0, 1, 0
298 vertex.m_normal = 0x1FF << 10;
299 }
300 if (read_vcolor)
301 {
302 // Color identifier
303 uint8_t ci;
304 spm->read(&ci, 1);
305 if (ci == 128)
306 {
307 // All white
308 vertex.m_color = video::SColor(255, 255, 255, 255);
309 }
310 else
311 {
312 uint8_t r, g, b;
313 spm->read(&r, 1);
314 spm->read(&g, 1);
315 spm->read(&b, 1);
316 vertex.m_color = video::SColor(255, r, g, b);
317 }
318 }
319 else
320 {
321 vertex.m_color = video::SColor(255, 255, 255, 255);
322 }
323 if (uv_one)
324 {
325 spm->read(&vertex.m_all_uvs[0], 4);
326 if (uv_two)
327 {
328 spm->read(&vertex.m_all_uvs[2], 4);
329 }
330 if (read_tangent)
331 {
332 spm->read(&vertex.m_tangent, 4);
333 }
334 else
335 {
336 vertex.m_tangent = MiniGLM::quickTangent(vertex.m_normal);
337 }
338 }
339 if (vt == SPVT_SKINNED)
340 {
341 spm->read(&vertex.m_joint_idx[0], 16);
342 if (vertex.m_joint_idx[0] == -1 ||
343 vertex.m_weight[0] == 0 ||
344 // -0.0 in half float (16bit)
345 vertex.m_weight[0] == -32768)
346 {
347 // For the skinned mesh shader
348 vertex.m_joint_idx[0] = -32767;
349 // 1.0 in half float (16bit)
350 vertex.m_weight[0] = 15360;
351 }
352 }
353 mb->addSPMVertex(vertex);
354 }
355
356 std::vector<uint16_t> indices;
357 indices.resize(indices_count);
358 if (idx_size == 2)
359 {
360 spm->read(indices.data(), indices_count * 2);
361 }
362 else
363 {
364 std::vector<uint8_t> tmp_idx;
365 tmp_idx.resize(indices_count);
366 spm->read(tmp_idx.data(), indices_count);
367 for (unsigned i = 0; i < indices_count; i++)
368 {
369 indices[i] = tmp_idx[i];
370 }
371 }
372 mb->setIndices(indices);
373 mb->setSTKMaterial(m);
374
375 } // decompressSPM
376
377 // ----------------------------------------------------------------------------
decompress(irr::io::IReadFile * spm,unsigned vertices_count,unsigned indices_count,bool read_normal,bool read_vcolor,bool read_tangent,bool uv_one,bool uv_two,SPVertexType vt,const video::SMaterial & m)378 void SPMeshLoader::decompress(irr::io::IReadFile* spm, unsigned vertices_count,
379 unsigned indices_count, bool read_normal,
380 bool read_vcolor, bool read_tangent, bool uv_one,
381 bool uv_two, SPVertexType vt,
382 const video::SMaterial& m)
383 {
384 assert(vertices_count != 0);
385 assert(indices_count != 0);
386 scene::SSkinMeshBuffer* mb = m_mesh->addMeshBuffer();
387 if (uv_two)
388 {
389 mb->convertTo2TCoords();
390 }
391 using namespace MiniGLM;
392 const unsigned idx_size = vertices_count > 255 ? 2 : 1;
393 char tmp[8] = {};
394 std::vector<std::pair<std::array<short, 4>, std::array<float, 4> > >
395 cur_joints;
396 for (unsigned i = 0; i < vertices_count; i++)
397 {
398 video::S3DVertex2TCoords vertex;
399 // 3 * float position
400 spm->read(&vertex.Pos, 12);
401 if (read_normal)
402 {
403 // 3 10 + 2 bits normal
404 uint32_t packed;
405 spm->read(&packed, 4);
406 vertex.Normal = decompressVector3(packed);
407 }
408 if (read_vcolor)
409 {
410 // Color identifier
411 uint8_t ci;
412 spm->read(&ci, 1);
413 if (ci == 128)
414 {
415 // All white
416 vertex.Color = video::SColor(255, 255, 255, 255);
417 }
418 else
419 {
420 uint8_t r, g, b;
421 spm->read(&r, 1);
422 spm->read(&g, 1);
423 spm->read(&b, 1);
424 vertex.Color = video::SColor(255, r, g, b);
425 }
426 }
427 else
428 {
429 vertex.Color = video::SColor(255, 255, 255, 255);
430 }
431 if (uv_one)
432 {
433 short hf[2];
434 spm->read(hf, 4);
435 vertex.TCoords.X = toFloat32(hf[0]);
436 vertex.TCoords.Y = toFloat32(hf[1]);
437 assert(!std::isnan(vertex.TCoords.X));
438 assert(!std::isnan(vertex.TCoords.Y));
439 if (uv_two)
440 {
441 spm->read(hf, 4);
442 vertex.TCoords2.X = toFloat32(hf[0]);
443 vertex.TCoords2.Y = toFloat32(hf[1]);
444 assert(!std::isnan(vertex.TCoords2.X));
445 assert(!std::isnan(vertex.TCoords2.Y));
446 }
447 if (read_tangent)
448 {
449 uint32_t packed;
450 spm->read(&packed, 4);
451 }
452 }
453 if (vt == SPVT_SKINNED)
454 {
455 std::array<short, 4> joint_idx;
456 spm->read(joint_idx.data(), 8);
457 spm->read(tmp, 8);
458 std::array<float, 4> joint_weight = {};
459 for (int j = 0; j < 8; j += 2)
460 {
461 short hf;
462 memcpy(&hf, tmp + j, 2);
463 const unsigned idx = j >> 1;
464 joint_weight[idx] = toFloat32(hf);
465 assert(!std::isnan(joint_weight[idx]));
466 }
467 cur_joints.emplace_back(joint_idx, joint_weight);
468 }
469 if (uv_two)
470 {
471 mb->Vertices_2TCoords.push_back(vertex);
472 }
473 else
474 {
475 mb->Vertices_Standard.push_back(vertex);
476 }
477 }
478 if (vt == SPVT_SKINNED)
479 {
480 m_joints.emplace_back(std::move(cur_joints));
481 }
482 if (m.TextureLayer[0].Texture != NULL)
483 {
484 mb->Material = m;
485 }
486 mb->Indices.set_used(indices_count);
487 if (idx_size == 2)
488 {
489 spm->read(mb->Indices.pointer(), indices_count * 2);
490 }
491 else
492 {
493 std::vector<uint8_t> tmp_idx;
494 tmp_idx.resize(indices_count);
495 spm->read(tmp_idx.data(), indices_count);
496 for (unsigned i = 0; i < indices_count; i++)
497 {
498 mb->Indices[i] = tmp_idx[i];
499 }
500 }
501
502 if (!read_normal)
503 {
504 for (unsigned i = 0; i < mb->Indices.size(); i += 3)
505 {
506 core::plane3df p(mb->getVertex(mb->Indices[i])->Pos,
507 mb->getVertex(mb->Indices[i + 1])->Pos,
508 mb->getVertex(mb->Indices[i + 2])->Pos);
509 mb->getVertex(mb->Indices[i])->Normal += p.Normal;
510 mb->getVertex(mb->Indices[i + 1])->Normal += p.Normal;
511 mb->getVertex(mb->Indices[i + 2])->Normal += p.Normal;
512 }
513 for (unsigned i = 0; i < mb->getVertexCount(); i++)
514 {
515 mb->getVertex(i)->Normal.normalize();
516 }
517 }
518 } // decompress
519
520 // ----------------------------------------------------------------------------
createAnimationData(irr::io::IReadFile * spm)521 void SPMeshLoader::createAnimationData(irr::io::IReadFile* spm)
522 {
523 uint8_t armature_size = 0;
524 spm->read(&armature_size, 1);
525 assert(armature_size > 0);
526 m_bind_frame = 0;
527 spm->read(&m_bind_frame, 2);
528 m_all_armatures.resize(armature_size);
529 for (unsigned i = 0; i < armature_size; i++)
530 {
531 m_all_armatures[i].read(spm);
532 }
533 for (unsigned i = 0; i < armature_size; i++)
534 {
535 m_frame_count = std::max(m_frame_count,
536 (unsigned)m_all_armatures[i].m_frame_pose_matrices.back().first);
537 m_joint_count += m_all_armatures[i].m_joint_used;
538 }
539
540 m_to_bind_pose_matrices.resize(m_joint_count);
541 unsigned accumulated_joints = 0;
542 for (unsigned i = 0; i < armature_size; i++)
543 {
544 m_all_armatures[i].getPose((float)m_bind_frame,
545 &m_to_bind_pose_matrices[accumulated_joints]);
546 accumulated_joints += m_all_armatures[i].m_joint_used;
547 }
548
549 // Only for legacy device
550 if (m_joints.empty())
551 {
552 return;
553 }
554 assert(m_joints.size() == m_mesh->getMeshBufferCount());
555 for (unsigned i = 0; i < m_to_bind_pose_matrices.size(); i++)
556 {
557 m_to_bind_pose_matrices[i].makeInverse();
558 }
559 for (unsigned i = 0; i < m_mesh->getMeshBufferCount(); i++)
560 {
561 for (unsigned j = 0; j < m_joints[i].size(); j++)
562 {
563 if (!(m_joints[i][j].first[0] == -1 ||
564 m_joints[i][j].second[0] == 0.0f))
565 {
566 core::vector3df bind_pos, bind_nor;
567 for (unsigned k = 0; k < 4; k++)
568 {
569 if (m_joints[i][j].second[k] == 0.0f)
570 {
571 break;
572 }
573 core::vector3df cur_pos, cur_nor;
574 m_to_bind_pose_matrices[m_joints[i][j].first[k]]
575 .transformVect(cur_pos,
576 m_mesh->getMeshBuffers()[i]->getVertex(j)->Pos);
577 bind_pos += cur_pos * m_joints[i][j].second[k];
578 m_to_bind_pose_matrices[m_joints[i][j].first[k]]
579 .rotateVect(cur_nor,
580 m_mesh->getMeshBuffers()[i]->getVertex(j)->Normal);
581 bind_nor += cur_nor * m_joints[i][j].second[k];
582 }
583 m_mesh->getMeshBuffers()[i]->getVertex(j)->Pos = bind_pos;
584 m_mesh->getMeshBuffers()[i]->getVertex(j)->Normal = bind_nor;
585 }
586 }
587 }
588 } // createAnimationData
589
590 // ----------------------------------------------------------------------------
convertIrrlicht()591 void SPMeshLoader::convertIrrlicht()
592 {
593 // Only for legacy device
594 if (m_joints.empty())
595 {
596 return;
597 }
598 unsigned total_joints = 0;
599 for (unsigned i = 0; i < m_all_armatures.size(); i++)
600 {
601 total_joints += (unsigned)m_all_armatures[i].m_joint_names.size();
602 }
603 for (unsigned i = 0; i < total_joints; i++)
604 {
605 m_mesh->addJoint(NULL);
606 }
607 core::array<scene::ISkinnedMesh::SJoint*>& joints = m_mesh->getAllJoints();
608 std::vector<int> used_joints_map;
609 used_joints_map.resize(m_joint_count);
610 total_joints = 0;
611 unsigned used_joints = 0;
612 for (unsigned i = 0; i < m_all_armatures.size(); i++)
613 {
614 for (unsigned j = 0; j < m_all_armatures[i].m_joint_names.size(); j++)
615 {
616 if (m_all_armatures[i].m_joint_used > j)
617 {
618 used_joints_map[used_joints + j] = total_joints + j;
619 }
620 joints[total_joints + j]->Name =
621 m_all_armatures[i].m_joint_names[j].c_str();
622 const int p_id = m_all_armatures[i].m_parent_infos[j];
623 core::matrix4 tmp;
624 if (p_id != -1)
625 {
626 joints[total_joints + p_id]->Children.push_back
627 (joints[total_joints + j]);
628 m_all_armatures[i].m_joint_matrices[j].getInverse(tmp);
629 tmp = m_all_armatures[i].m_joint_matrices[p_id] * tmp;
630 }
631 else
632 {
633 m_all_armatures[i].m_joint_matrices[j].getInverse(tmp);
634 }
635 joints[total_joints + j]->LocalMatrix = tmp;
636 for (unsigned k = 0; k <
637 m_all_armatures[i].m_frame_pose_matrices.size(); k++)
638 {
639 float frame = (float)
640 m_all_armatures[i].m_frame_pose_matrices[k].first;
641 core::vector3df pos = m_all_armatures[i]
642 .m_frame_pose_matrices[k].second[j].m_loc;
643 core::quaternion q = m_all_armatures[i]
644 .m_frame_pose_matrices[k].second[j].m_rot;
645 core::vector3df scl = m_all_armatures[i]
646 .m_frame_pose_matrices[k].second[j].m_scale;
647 joints[total_joints + j]->PositionKeys.push_back({frame, pos});
648 joints[total_joints + j]->RotationKeys.push_back({frame,
649 // Reverse for broken irrlicht quaternion
650 core::quaternion(q.X, q.Y, q.Z, -q.W)});
651 joints[total_joints + j]->ScaleKeys.push_back({frame, scl});
652 }
653 }
654 total_joints += (unsigned)m_all_armatures[i].m_joint_names.size();
655 used_joints += m_all_armatures[i].m_joint_used;
656 }
657
658 for (unsigned i = 0; i < m_joints.size(); i++)
659 {
660 for (unsigned j = 0; j < m_joints[i].size(); j++)
661 {
662 for (unsigned k = 0; k < 4; k++)
663 {
664 if (m_joints[i][j].first[k] == -1 ||
665 m_joints[i][j].second[k] == 0.0f)
666 {
667 break;
668 }
669 scene::ISkinnedMesh::SWeight* w = m_mesh->addWeight
670 (joints[used_joints_map[m_joints[i][j].first[k]]]);
671 w->buffer_id = (uint16_t)i;
672 w->vertex_id = j;
673 w->strength = m_joints[i][j].second[k];
674 }
675 }
676 }
677
678 } // convertIrrlicht
679