1 //  SuperTuxKart - a fun racing game with go-kart
2 //  Copyright (C) 2018 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/sp_mesh.hpp"
19 #include "graphics/sp/sp_animation.hpp"
20 #include "graphics/sp/sp_mesh_buffer.hpp"
21 #include "graphics/material.hpp"
22 #include "utils/mini_glm.hpp"
23 
24 #include <algorithm>
25 
26 namespace SP
27 {
28 // ----------------------------------------------------------------------------
SPMesh()29 SPMesh::SPMesh()
30 {
31     m_fps = 0.025f;
32     m_bind_frame = 0;
33     m_total_joints = 0;
34     m_joint_using = 0;
35     m_frame_count = 0;
36 }   // SPMesh
37 
38 // ----------------------------------------------------------------------------
~SPMesh()39 SPMesh::~SPMesh()
40 {
41     for (unsigned i=0; i < m_buffer.size(); i++)
42     {
43         if (m_buffer[i])
44         {
45             m_buffer[i]->drop();
46         }
47     }
48 }   // ~SPMesh
49 
50 // ----------------------------------------------------------------------------
getMeshBuffer(u32 nr) const51 IMeshBuffer* SPMesh::getMeshBuffer(u32 nr) const
52 {
53     if (nr < m_buffer.size())
54     {
55         return m_buffer[nr];
56     }
57     return NULL;
58 }   // getMeshBuffer
59 
60 // ----------------------------------------------------------------------------
getSPMeshBuffer(u32 nr) const61 SPMeshBuffer* SPMesh::getSPMeshBuffer(u32 nr) const
62 {
63     if (nr < m_buffer.size())
64     {
65         return m_buffer[nr];
66     }
67     return NULL;
68 }   // getMeshBuffer
69 
70 // ----------------------------------------------------------------------------
getMeshBuffer(const video::SMaterial & material) const71 IMeshBuffer* SPMesh::getMeshBuffer(const video::SMaterial &material) const
72 {
73     for (unsigned i = 0; i < m_buffer.size(); i++)
74     {
75         if (m_buffer[i]->getMaterial() == material)
76         {
77             return m_buffer[i];
78         }
79     }
80     return NULL;
81 }   // getMeshBuffer
82 
83 // ----------------------------------------------------------------------------
getJointName(u32 number) const84 const c8* SPMesh::getJointName(u32 number) const
85 {
86     if (number >= m_joint_using)
87     {
88         return "";
89     }
90     int i = 0;
91     int j = number;
92     while (j >= int(m_all_armatures[i].m_joint_used))
93     {
94         j -= int(m_all_armatures[i].m_joint_used);
95         i++;
96     }
97     return m_all_armatures.at(i).m_joint_names[j].c_str();
98 
99 }   // getJointName
100 
101 // ----------------------------------------------------------------------------
getJointIDWithArm(const c8 * name,unsigned * arm_id) const102 s32 SPMesh::getJointIDWithArm(const c8* name, unsigned* arm_id) const
103 {
104     for (unsigned i = 0; i < m_all_armatures.size(); i++)
105     {
106         const Armature& arm = m_all_armatures[i];
107         auto found = std::find(arm.m_joint_names.begin(),
108             arm.m_joint_names.end(), name);
109         if (found != arm.m_joint_names.end())
110         {
111             if (arm_id != NULL)
112             {
113                 *arm_id = i;
114             }
115             return (int)(found - arm.m_joint_names.begin());
116         }
117     }
118     return -1;
119 }   // getJointIDWithArm
120 
121 // ----------------------------------------------------------------------------
getSkinningMatrices(f32 frame,std::array<float,16> * dest)122 void SPMesh::getSkinningMatrices(f32 frame, std::array<float, 16>* dest)
123 {
124     unsigned accumulated_joints = 0;
125     for (unsigned i = 0; i < m_all_armatures.size(); i++)
126     {
127         m_all_armatures[i].getPose(frame, &dest[accumulated_joints]);
128         accumulated_joints += m_all_armatures[i].m_joint_used;
129     }
130 
131 }   // getSkinningMatrices
132 
133 // ----------------------------------------------------------------------------
updateBoundingBox()134 void SPMesh::updateBoundingBox()
135 {
136     m_bounding_box.reset(0.0f, 0.0f, 0.0f);
137     for (unsigned i = 0; i < m_buffer.size(); i++)
138     {
139         m_buffer[i]->recalculateBoundingBox();
140         m_bounding_box.addInternalBox(m_buffer[i]->getBoundingBox());
141     }
142 }   // updateBoundingBox
143 
144 // ----------------------------------------------------------------------------
finalize()145 void SPMesh::finalize()
146 {
147     updateBoundingBox();
148     for (Armature& arm : getArmatures())
149     {
150         arm.getInterpolatedMatrices((float)m_bind_frame);
151         for (auto& p : arm.m_world_matrices)
152         {
153             p.second = false;
154         }
155         for (unsigned i = 0; i < arm.m_joint_names.size(); i++)
156         {
157             core::matrix4 m;
158             arm.getWorldMatrix(arm.m_interpolated_matrices, i).getInverse(m);
159             arm.m_joint_matrices[i] = m;
160         }
161     }
162     m_bounding_box.reset(0.0f, 0.0f, 0.0f);
163     // Sort with same shader name
164     std::sort(m_buffer.begin(), m_buffer.end(),
165         [](const SPMeshBuffer* a, const SPMeshBuffer* b)->bool
166         {
167             return a->getSTKMaterial()->getShaderName() <
168                 b->getSTKMaterial()->getShaderName();
169         });
170 
171     for (unsigned i = 0; i < m_buffer.size(); i++)
172     {
173         m_buffer[i]->recalculateBoundingBox();
174         m_bounding_box.addInternalBox(m_buffer[i]->getBoundingBox());
175         m_buffer[i]->initDrawMaterial();
176         if (!isStatic())
177         {
178             m_buffer[i]->enableSkinningData();
179         }
180     }
181 
182     auto itr = m_buffer.begin();
183     while (itr != m_buffer.end())
184     {
185         auto itr_next = itr + 1;
186         if (itr_next != m_buffer.end() &&
187             (*itr)->getSTKMaterial()->getShaderName() ==
188             (*itr_next)->getSTKMaterial()->getShaderName())
189         {
190             if ((*itr)->combineMeshBuffer(*itr_next))
191             {
192                 (*itr)->recalculateBoundingBox();
193                 delete *itr_next;
194                 m_buffer.erase(itr_next);
195                 continue;
196             }
197         }
198         itr++;
199     }
200 
201     for (unsigned i = 0; i < m_buffer.size(); i++)
202     {
203         m_buffer[i]->shrinkToFit();
204     }
205 
206 }   // finalize
207 
208 }
209