Line data Source code
1 : /* Copyright (C) 2022 Wildfire Games.
2 : * This file is part of 0 A.D.
3 : *
4 : * 0 A.D. is free software: you can redistribute it and/or modify
5 : * it under the terms of the GNU General Public License as published by
6 : * the Free Software Foundation, either version 2 of the License, or
7 : * (at your option) any later version.
8 : *
9 : * 0 A.D. 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 0 A.D. If not, see <http://www.gnu.org/licenses/>.
16 : */
17 :
18 : #include "precompiled.h"
19 :
20 : #include "ModelDef.h"
21 :
22 : #include "graphics/SkeletonAnimDef.h"
23 : #include "lib/sysdep/arch/x86_x64/simd.h"
24 : #include "maths/Vector4D.h"
25 : #include "ps/FileIo.h"
26 :
27 : #if COMPILER_HAS_SSE
28 : # include <xmmintrin.h>
29 : #endif
30 :
31 0 : void CModelDef::GetMaxBounds(CSkeletonAnimDef* anim, bool loop, CBoundingBoxAligned& result)
32 : {
33 0 : const u32 animIndex = anim ? anim->m_UID : 0;
34 :
35 0 : std::unordered_map<u32, CBoundingBoxAligned>::const_iterator it = m_MaxBoundsPerAnimDef.find(animIndex);
36 0 : if (it != m_MaxBoundsPerAnimDef.end())
37 : {
38 0 : result = it->second;
39 0 : return;
40 : }
41 :
42 0 : size_t numverts = GetNumVertices();
43 0 : SModelVertex* verts = GetVertices();
44 :
45 0 : if (!anim)
46 : {
47 0 : for (size_t i = 0; i < numverts; ++i)
48 0 : result += verts[i].m_Coords;
49 0 : m_MaxBoundsPerAnimDef[animIndex] = result;
50 0 : return;
51 : }
52 0 : ENSURE(animIndex != 0);
53 0 : CMatrix3D* inverseBindBoneMatrix = GetInverseBindBoneMatrices();
54 0 : std::vector<CMatrix3D> boneMatrix(anim->GetNumKeys());
55 :
56 0 : const size_t numFrames = anim->GetNumFrames();
57 0 : const float frameTime = anim->GetFrameTime();
58 0 : const size_t numBones = GetNumBones();
59 :
60 : // NB: by using frames, the bounds are technically pessimistic,
61 : // since interpolation between frames can put vertices farther.
62 0 : for (size_t j = 0; j < numFrames; ++j)
63 : {
64 0 : anim->BuildBoneMatrices(j * frameTime, boneMatrix.data(), loop);
65 0 : for (size_t i = 0; i < numBones; ++i)
66 0 : boneMatrix[i] = boneMatrix[i] * inverseBindBoneMatrix[i];
67 0 : for (size_t i = 0; i < numverts; ++i)
68 0 : result += SkinPoint(verts[i], boneMatrix.data());
69 : }
70 0 : m_MaxBoundsPerAnimDef[animIndex] = result;
71 : }
72 :
73 0 : CVector3D CModelDef::SkinPoint(const SModelVertex& vtx,
74 : const CMatrix3D newPoseMatrices[])
75 : {
76 0 : CVector3D result (0, 0, 0);
77 :
78 0 : for (int i = 0; i < SVertexBlend::SIZE && vtx.m_Blend.m_Bone[i] != 0xff; ++i)
79 : {
80 0 : result += newPoseMatrices[vtx.m_Blend.m_Bone[i]].Transform(vtx.m_Coords) * vtx.m_Blend.m_Weight[i];
81 : }
82 :
83 0 : return result;
84 : }
85 :
86 0 : CVector3D CModelDef::SkinNormal(const SModelVertex& vtx,
87 : const CMatrix3D newPoseMatrices[])
88 : {
89 : // To be correct, the normal vectors apparently need to be multiplied by the
90 : // inverse of the transpose. Unfortunately inverses are slow.
91 : // If a matrix is orthogonal, M * M^T = I and so the inverse of the transpose
92 : // is the original matrix. But that's not entirely relevant here, because
93 : // the bone matrices include translation components and so they're not
94 : // orthogonal.
95 : // But that's okay because we have
96 : // M = T * R
97 : // and want to find
98 : // n' = (M^T^-1) * n
99 : // = (T * R)^T^-1 * n
100 : // = (R^T * T^T)^-1 * n
101 : // = (T^T^-1 * R^T^-1) * n
102 : // R is indeed orthogonal so R^T^-1 = R. T isn't orthogonal at all.
103 : // But n is only a 3-vector, and from the forms of T and R (which have
104 : // lots of zeroes) I can convince myself that replacing T with T^T^-1 has no
105 : // effect on anything but the fourth component of M^T^-1 - and the fourth
106 : // component is discarded since it has no effect on n', and so we can happily
107 : // use n' = M*n.
108 : //
109 : // (This isn't very good as a proof, but it's better than assuming M is
110 : // orthogonal when it's clearly not.)
111 :
112 0 : CVector3D result (0, 0, 0);
113 :
114 0 : for (int i = 0; i < SVertexBlend::SIZE && vtx.m_Blend.m_Bone[i] != 0xff; ++i)
115 : {
116 0 : result += newPoseMatrices[vtx.m_Blend.m_Bone[i]].Rotate(vtx.m_Norm) * vtx.m_Blend.m_Weight[i];
117 : }
118 :
119 : // If there was more than one influence, the result is probably not going
120 : // to be of unit length (since it's a weighted sum of several independent
121 : // unit vectors), so we need to normalise it.
122 : // (It's fairly common to only have one influence, so it seems sensible to
123 : // optimise that case a bit.)
124 0 : if (vtx.m_Blend.m_Bone[1] != 0xff) // if more than one influence
125 0 : result.Normalize();
126 :
127 0 : return result;
128 : }
129 :
130 : void(*CModelDef::SkinPointsAndNormals)(
131 : size_t numVertices,
132 : const VertexArrayIterator<CVector3D>& Position,
133 : const VertexArrayIterator<CVector3D>& Normal,
134 : const SModelVertex* vertices,
135 : const size_t* blendIndices,
136 : const CMatrix3D newPoseMatrices[]) {};
137 :
138 :
139 0 : static void SkinPointsAndNormalsFallback(
140 : size_t numVertices,
141 : const VertexArrayIterator<CVector3D>& Position,
142 : const VertexArrayIterator<CVector3D>& Normal,
143 : const SModelVertex* vertices,
144 : const size_t* blendIndices,
145 : const CMatrix3D newPoseMatrices[])
146 : {
147 : // To avoid some performance overhead, get the raw vertex array pointers
148 0 : char* PositionData = Position.GetData();
149 0 : size_t PositionStride = Position.GetStride();
150 0 : char* NormalData = Normal.GetData();
151 0 : size_t NormalStride = Normal.GetStride();
152 :
153 0 : for (size_t j = 0; j < numVertices; ++j)
154 : {
155 0 : const SModelVertex& vtx = vertices[j];
156 :
157 0 : CVector3D pos = newPoseMatrices[blendIndices[j]].Transform(vtx.m_Coords);
158 0 : CVector3D norm = newPoseMatrices[blendIndices[j]].Rotate(vtx.m_Norm);
159 :
160 : // If there was more than one influence, the result is probably not going
161 : // to be of unit length (since it's a weighted sum of several independent
162 : // unit vectors), so we need to normalise it.
163 : // (It's fairly common to only have one influence, so it seems sensible to
164 : // optimise that case a bit.)
165 0 : if (vtx.m_Blend.m_Bone[1] != 0xff) // if more than one influence
166 0 : norm.Normalize();
167 :
168 0 : memcpy(PositionData + PositionStride*j, &pos.X, 3*sizeof(float));
169 0 : memcpy(NormalData + NormalStride*j, &norm.X, 3*sizeof(float));
170 : }
171 0 : }
172 :
173 : #if COMPILER_HAS_SSE
174 0 : static void SkinPointsAndNormalsSSE(
175 : size_t numVertices,
176 : const VertexArrayIterator<CVector3D>& Position,
177 : const VertexArrayIterator<CVector3D>& Normal,
178 : const SModelVertex* vertices,
179 : const size_t* blendIndices,
180 : const CMatrix3D newPoseMatrices[])
181 : {
182 : // To avoid some performance overhead, get the raw vertex array pointers
183 0 : char* PositionData = Position.GetData();
184 0 : size_t PositionStride = Position.GetStride();
185 0 : char* NormalData = Normal.GetData();
186 0 : size_t NormalStride = Normal.GetStride();
187 :
188 : // Must be aligned correctly for SSE
189 0 : ASSERT((intptr_t)newPoseMatrices % 16 == 0);
190 0 : ASSERT((intptr_t)PositionData % 16 == 0);
191 0 : ASSERT((intptr_t)PositionStride % 16 == 0);
192 0 : ASSERT((intptr_t)NormalData % 16 == 0);
193 0 : ASSERT((intptr_t)NormalStride % 16 == 0);
194 :
195 : __m128 col0, col1, col2, col3, vec0, vec1, vec2;
196 :
197 0 : for (size_t j = 0; j < numVertices; ++j)
198 : {
199 0 : const SModelVertex& vtx = vertices[j];
200 0 : const CMatrix3D& mtx = newPoseMatrices[blendIndices[j]];
201 :
202 : // Loads matrix to xmm registers.
203 0 : const float* data = mtx.AsFloatArray().data();
204 0 : col0 = _mm_load_ps(data);
205 0 : col1 = _mm_load_ps(data + 4);
206 0 : col2 = _mm_load_ps(data + 8);
207 0 : col3 = _mm_load_ps(data + 12);
208 :
209 : // Loads and computes vertex coordinates.
210 0 : vec0 = _mm_load1_ps(&vtx.m_Coords.X); // v0 = [x, x, x, x]
211 0 : vec0 = _mm_mul_ps(col0, vec0); // v0 = [_11*x, _21*x, _31*x, _41*x]
212 0 : vec1 = _mm_load1_ps(&vtx.m_Coords.Y); // v1 = [y, y, y, y]
213 0 : vec1 = _mm_mul_ps(col1, vec1); // v1 = [_12*y, _22*y, _32*y, _42*y]
214 0 : vec0 = _mm_add_ps(vec0, vec1); // v0 = [_11*x + _12*y, ...]
215 0 : vec1 = _mm_load1_ps(&vtx.m_Coords.Z); // v1 = [z, z, z, z]
216 0 : vec1 = _mm_mul_ps(col2, vec1); // v1 = [_13*z, _23*z, _33*z, _43*z]
217 0 : vec1 = _mm_add_ps(vec1, col3); // v1 = [_13*z + _14, ...]
218 0 : vec0 = _mm_add_ps(vec0, vec1); // v0 = [_11*x + _12*y + _13*z + _14, ...]
219 0 : _mm_store_ps((float*)(PositionData + PositionStride*j), vec0);
220 :
221 : // Loads and computes normal vectors.
222 0 : vec0 = _mm_load1_ps(&vtx.m_Norm.X); // v0 = [x, x, x, x]
223 0 : vec0 = _mm_mul_ps(col0, vec0); // v0 = [_11*x, _21*x, _31*x, _41*x]
224 0 : vec1 = _mm_load1_ps(&vtx.m_Norm.Y); // v1 = [y, y, y, y]
225 0 : vec1 = _mm_mul_ps(col1, vec1); // v1 = [_12*y, _22*y, _32*y, _42*y]
226 0 : vec0 = _mm_add_ps(vec0, vec1); // v0 = [_11*x + _12*y, ...]
227 0 : vec1 = _mm_load1_ps(&vtx.m_Norm.Z); // v1 = [z, z, z, z]
228 0 : vec1 = _mm_mul_ps(col2, vec1); // v1 = [_13*z, _23*z, _33*z, _43*z]
229 0 : vec0 = _mm_add_ps(vec0, vec1); // v0 = [_11*x + _12*y + _13*z, ...]
230 :
231 : // If there was more than one influence, the result is probably not going
232 : // to be of unit length (since it's a weighted sum of several independent
233 : // unit vectors), so we need to normalise it.
234 : // (It's fairly common to only have one influence, so it seems sensible to
235 : // optimise that case a bit.)
236 0 : if (vtx.m_Blend.m_Bone[1] != 0xff) // if more than one influence
237 : {
238 : // Normalization.
239 : // vec1 = [x*x, y*y, z*z, ?*?]
240 0 : vec1 = _mm_mul_ps(vec0, vec0);
241 : // vec2 = [y*y, z*z, x*x, ?*?]
242 0 : vec2 = _mm_shuffle_ps(vec1, vec1, _MM_SHUFFLE(3, 0, 2, 1));
243 0 : vec1 = _mm_add_ps(vec1, vec2);
244 : // vec2 = [z*z, x*x, y*y, ?*?]
245 0 : vec2 = _mm_shuffle_ps(vec2, vec2, _MM_SHUFFLE(3, 0, 2, 1));
246 0 : vec1 = _mm_add_ps(vec1, vec2);
247 : // rsqrt(a) = 1 / sqrt(a)
248 0 : vec1 = _mm_rsqrt_ps(vec1);
249 0 : vec0 = _mm_mul_ps(vec0, vec1);
250 : }
251 0 : _mm_store_ps((float*)(NormalData + NormalStride*j), vec0);
252 : }
253 0 : }
254 : #endif
255 :
256 0 : void CModelDef::BlendBoneMatrices(
257 : CMatrix3D boneMatrices[])
258 : {
259 0 : for (size_t i = 0; i < m_NumBlends; ++i)
260 : {
261 0 : const SVertexBlend& blend = m_pBlends[i];
262 0 : CMatrix3D& boneMatrix = boneMatrices[m_NumBones + 1 + i];
263 :
264 : // Note: there is a special case of joint influence, in which the vertex
265 : // is influenced by the bind-shape matrix instead of a particular bone,
266 : // which we indicate by setting the bone ID to the total number of bones.
267 : // It should be blended with the world space transform and we have already
268 : // set up this matrix in boneMatrices.
269 : // (see http://trac.wildfiregames.com/ticket/1012)
270 :
271 0 : boneMatrix.Blend(boneMatrices[blend.m_Bone[0]], blend.m_Weight[0]);
272 0 : for (size_t j = 1; j < SVertexBlend::SIZE && blend.m_Bone[j] != 0xFF; ++j)
273 0 : boneMatrix.AddBlend(boneMatrices[blend.m_Bone[j]], blend.m_Weight[j]);
274 : }
275 0 : }
276 :
277 : // CModelDef Constructor
278 6 : CModelDef::CModelDef() :
279 : m_NumVertices(0), m_NumUVsPerVertex(0), m_pVertices(0), m_NumFaces(0), m_pFaces(0),
280 : m_NumBones(0), m_Bones(0), m_InverseBindBoneMatrices(NULL),
281 : m_NumBlends(0), m_pBlends(0), m_pBlendIndices(0),
282 6 : m_Name(L"[not loaded]")
283 : {
284 6 : }
285 :
286 : // CModelDef Destructor
287 12 : CModelDef::~CModelDef()
288 : {
289 6 : for(RenderDataMap::iterator it = m_RenderData.begin(); it != m_RenderData.end(); ++it)
290 0 : delete it->second;
291 6 : delete[] m_pVertices;
292 6 : delete[] m_pFaces;
293 6 : delete[] m_Bones;
294 6 : delete[] m_InverseBindBoneMatrices;
295 6 : delete[] m_pBlends;
296 6 : delete[] m_pBlendIndices;
297 6 : }
298 :
299 : // FindPropPoint: find and return pointer to prop point matching given name;
300 : // return null if no match (case insensitive search)
301 0 : const SPropPoint* CModelDef::FindPropPoint(const char* name) const
302 : {
303 0 : for (size_t i = 0; i < m_PropPoints.size(); ++i)
304 0 : if (m_PropPoints[i].m_Name == name)
305 0 : return &m_PropPoints[i];
306 :
307 0 : return 0;
308 : }
309 :
310 : // Load: read and return a new CModelDef initialised with data from given file
311 6 : CModelDef* CModelDef::Load(const VfsPath& filename, const VfsPath& name)
312 : {
313 12 : CFileUnpacker unpacker;
314 :
315 : // read everything in from file
316 6 : unpacker.Read(filename,"PSMD");
317 :
318 : // check version
319 6 : if (unpacker.GetVersion()<FILE_READ_VERSION) {
320 0 : throw PSERROR_File_InvalidVersion();
321 : }
322 :
323 12 : std::unique_ptr<CModelDef> mdef = std::make_unique<CModelDef>();
324 6 : mdef->m_Name = name;
325 :
326 : // now unpack everything
327 6 : mdef->m_NumVertices = unpacker.UnpackSize();
328 :
329 : // versions prior to 4 only support 1 UV set, 4 and later store it here
330 6 : if (unpacker.GetVersion() <= 3)
331 : {
332 3 : mdef->m_NumUVsPerVertex = 1;
333 : }
334 : else
335 : {
336 3 : mdef->m_NumUVsPerVertex = unpacker.UnpackSize();
337 : }
338 :
339 6 : mdef->m_pVertices = new SModelVertex[mdef->m_NumVertices];
340 6 : mdef->m_UVCoordinates.reserve(mdef->m_NumVertices * mdef->m_NumUVsPerVertex);
341 :
342 752 : for (size_t i = 0; i < mdef->m_NumVertices; ++i)
343 : {
344 746 : unpacker.UnpackRaw(&mdef->m_pVertices[i].m_Coords, 12);
345 746 : unpacker.UnpackRaw(&mdef->m_pVertices[i].m_Norm, 12);
346 :
347 1492 : for (size_t s = 0; s < mdef->m_NumUVsPerVertex; ++s)
348 : {
349 : float uv[2];
350 746 : unpacker.UnpackRaw(&uv[0], 8);
351 746 : mdef->m_UVCoordinates.emplace_back(uv[0], uv[1]);
352 : }
353 :
354 746 : unpacker.UnpackRaw(&mdef->m_pVertices[i].m_Blend, sizeof(SVertexBlend));
355 : }
356 :
357 6 : mdef->m_NumFaces = unpacker.UnpackSize();
358 6 : mdef->m_pFaces=new SModelFace[mdef->m_NumFaces];
359 6 : unpacker.UnpackRaw(mdef->m_pFaces,sizeof(SModelFace)*mdef->m_NumFaces);
360 :
361 6 : mdef->m_NumBones = unpacker.UnpackSize();
362 6 : if (mdef->m_NumBones)
363 : {
364 0 : mdef->m_Bones=new CBoneState[mdef->m_NumBones];
365 0 : unpacker.UnpackRaw(mdef->m_Bones,mdef->m_NumBones*sizeof(CBoneState));
366 :
367 0 : mdef->m_pBlendIndices = new size_t[mdef->m_NumVertices];
368 0 : std::vector<SVertexBlend> blends;
369 0 : for (size_t i = 0; i < mdef->m_NumVertices; i++)
370 : {
371 0 : const SVertexBlend &blend = mdef->m_pVertices[i].m_Blend;
372 0 : if (blend.m_Bone[1] == 0xFF)
373 : {
374 0 : mdef->m_pBlendIndices[i] = blend.m_Bone[0];
375 : }
376 : else
377 : {
378 : // If there's already a vertex using the same blend as this, then
379 : // reuse its entry from blends; otherwise add the new one to blends
380 : size_t j;
381 0 : for (j = 0; j < blends.size(); j++)
382 : {
383 0 : if (blend == blends[j]) break;
384 : }
385 0 : if (j >= blends.size())
386 0 : blends.push_back(blend);
387 : // This index is offset by one to allow the special case of a
388 : // weighted influence relative to the bind-shape rather than
389 : // a particular bone. See comment in BlendBoneMatrices.
390 0 : mdef->m_pBlendIndices[i] = mdef->m_NumBones + 1 + j;
391 : }
392 : }
393 :
394 0 : mdef->m_NumBlends = blends.size();
395 0 : mdef->m_pBlends = new SVertexBlend[mdef->m_NumBlends];
396 0 : std::copy(blends.begin(), blends.end(), mdef->m_pBlends);
397 : }
398 :
399 6 : if (unpacker.GetVersion() >= 2)
400 : {
401 : // versions >=2 also have prop point data
402 6 : size_t numPropPoints = unpacker.UnpackSize();
403 6 : mdef->m_PropPoints.resize(numPropPoints);
404 6 : if (numPropPoints)
405 : {
406 6 : for (size_t i = 0; i < numPropPoints; i++)
407 : {
408 3 : unpacker.UnpackString(mdef->m_PropPoints[i].m_Name);
409 3 : unpacker.UnpackRaw(&mdef->m_PropPoints[i].m_Position.X, sizeof(mdef->m_PropPoints[i].m_Position));
410 3 : unpacker.UnpackRaw(&mdef->m_PropPoints[i].m_Rotation.m_V.X, sizeof(mdef->m_PropPoints[i].m_Rotation));
411 3 : unpacker.UnpackRaw(&mdef->m_PropPoints[i].m_BoneIndex, sizeof(mdef->m_PropPoints[i].m_BoneIndex));
412 :
413 : // build prop point transform
414 3 : mdef->m_PropPoints[i].m_Transform.SetIdentity();
415 3 : mdef->m_PropPoints[i].m_Transform.Rotate(mdef->m_PropPoints[i].m_Rotation);
416 3 : mdef->m_PropPoints[i].m_Transform.Translate(mdef->m_PropPoints[i].m_Position);
417 : }
418 : }
419 : }
420 :
421 6 : if (unpacker.GetVersion() <= 2)
422 : {
423 : // Versions <=2 don't include the default 'root' prop point, so add it here
424 :
425 0 : SPropPoint prop;
426 0 : prop.m_Name = "root";
427 0 : prop.m_Transform.SetIdentity();
428 0 : prop.m_BoneIndex = 0xFF;
429 :
430 0 : mdef->m_PropPoints.push_back(prop);
431 : }
432 :
433 6 : if (unpacker.GetVersion() <= 2)
434 : {
435 : // Versions <=2 store the vertexes relative to the bind pose. That
436 : // isn't useful when you want to do correct skinning, so later versions
437 : // store them in world space. So, fix the old models by skinning each
438 : // vertex:
439 :
440 0 : if (mdef->m_NumBones) // only do skinned models
441 : {
442 0 : std::vector<CMatrix3D> bindPose (mdef->m_NumBones);
443 :
444 0 : for (size_t i = 0; i < mdef->m_NumBones; ++i)
445 : {
446 0 : bindPose[i].SetIdentity();
447 0 : bindPose[i].Rotate(mdef->m_Bones[i].m_Rotation);
448 0 : bindPose[i].Translate(mdef->m_Bones[i].m_Translation);
449 : }
450 :
451 0 : for (size_t i = 0; i < mdef->m_NumVertices; ++i)
452 : {
453 0 : mdef->m_pVertices[i].m_Coords = SkinPoint(mdef->m_pVertices[i], &bindPose[0]);
454 0 : mdef->m_pVertices[i].m_Norm = SkinNormal(mdef->m_pVertices[i], &bindPose[0]);
455 : }
456 : }
457 : }
458 :
459 : // Compute the inverse bind-pose matrices, needed by the skinning code
460 6 : mdef->m_InverseBindBoneMatrices = new CMatrix3D[mdef->m_NumBones];
461 6 : CBoneState* defpose = mdef->GetBones();
462 6 : for (size_t i = 0; i < mdef->m_NumBones; ++i)
463 : {
464 0 : mdef->m_InverseBindBoneMatrices[i].SetIdentity();
465 0 : mdef->m_InverseBindBoneMatrices[i].Translate(-defpose[i].m_Translation);
466 0 : mdef->m_InverseBindBoneMatrices[i].Rotate(defpose[i].m_Rotation.GetInverse());
467 : }
468 :
469 12 : return mdef.release();
470 : }
471 :
472 : // Save: write the given CModelDef to the given file
473 0 : void CModelDef::Save(const VfsPath& filename, const CModelDef* mdef)
474 : {
475 0 : CFilePacker packer(FILE_VERSION, "PSMD");
476 :
477 : // pack everything up
478 0 : const size_t numVertices = mdef->GetNumVertices();
479 0 : packer.PackSize(numVertices);
480 0 : packer.PackRaw(mdef->GetVertices(), sizeof(SModelVertex) * numVertices);
481 :
482 0 : const size_t numFaces = mdef->GetNumFaces();
483 0 : packer.PackSize(numFaces);
484 0 : packer.PackRaw(mdef->GetFaces(), sizeof(SModelFace) * numFaces);
485 :
486 0 : const size_t numBones = mdef->m_NumBones;
487 0 : packer.PackSize(numBones);
488 0 : if (numBones)
489 0 : packer.PackRaw(mdef->m_Bones, sizeof(CBoneState) * numBones);
490 :
491 0 : const size_t numPropPoints = mdef->m_PropPoints.size();
492 0 : packer.PackSize(numPropPoints);
493 0 : for (size_t i = 0; i < numPropPoints; i++)
494 : {
495 0 : packer.PackString(mdef->m_PropPoints[i].m_Name);
496 0 : packer.PackRaw(&mdef->m_PropPoints[i].m_Position.X, sizeof(mdef->m_PropPoints[i].m_Position));
497 0 : packer.PackRaw(&mdef->m_PropPoints[i].m_Rotation.m_V.X, sizeof(mdef->m_PropPoints[i].m_Rotation));
498 0 : packer.PackRaw(&mdef->m_PropPoints[i].m_BoneIndex, sizeof(mdef->m_PropPoints[i].m_BoneIndex));
499 : }
500 :
501 : // flush everything out to file
502 0 : packer.Write(filename);
503 0 : }
504 :
505 : // SetRenderData: Set the render data object for the given key,
506 0 : void CModelDef::SetRenderData(const void* key, CModelDefRPrivate* data)
507 : {
508 0 : delete m_RenderData[key];
509 0 : m_RenderData[key] = data;
510 0 : }
511 :
512 : // GetRenderData: Get the render data object for the given key,
513 : // or 0 if no such object exists.
514 : // Reference count of the render data object is automatically increased.
515 0 : CModelDefRPrivate* CModelDef::GetRenderData(const void* key) const
516 : {
517 0 : RenderDataMap::const_iterator it = m_RenderData.find(key);
518 :
519 0 : if (it != m_RenderData.end())
520 0 : return it->second;
521 :
522 0 : return 0;
523 : }
524 :
525 6 : void ModelDefActivateFastImpl()
526 : {
527 : #if COMPILER_HAS_SSE
528 6 : if (HostHasSSE())
529 : {
530 6 : CModelDef::SkinPointsAndNormals = SkinPointsAndNormalsSSE;
531 6 : return;
532 : }
533 : #endif
534 0 : CModelDef::SkinPointsAndNormals = SkinPointsAndNormalsFallback;
535 3 : }
|