OpenClonk
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros
StdMesh.cpp
Go to the documentation of this file.
1 /*
2  * OpenClonk, http://www.openclonk.org
3  *
4  * Copyright (c) 2001-2009, RedWolf Design GmbH, http://www.clonk.de/
5  * Copyright (c) 2009-2016, The OpenClonk Team and contributors
6  *
7  * Distributed under the terms of the ISC license; see accompanying file
8  * "COPYING" for details.
9  *
10  * "Clonk" is a registered trademark of Matthes Bender, used with permission.
11  * See accompanying file "TRADEMARK" for details.
12  *
13  * To redistribute this file separately, substitute the full license texts
14  * for the above references.
15  */
16 
17 #include "C4Include.h"
19 #include "lib/StdMesh.h"
20 
21 #include "graphics/C4DrawGL.h"
22 
23 namespace
24 {
25  struct StdMeshFaceOrderHelper
26  {
27  float z;
28  unsigned int i;
29  };
30 }
31 
32 static int StdMeshFaceCmp(const StdMeshFaceOrderHelper& h1, const StdMeshFaceOrderHelper& h2)
33 {
34  if(h1.z < h2.z) return -1;
35  else if(h1.z > h2.z) return +1;
36  return 0;
37 }
38 
39 #define SORT_NAME StdMesh
40 #define SORT_TYPE StdMeshFaceOrderHelper
41 #define SORT_CMP StdMeshFaceCmp
42 #include "timsort/sort.h"
43 
44 std::vector<StdMeshInstance::SerializableValueProvider::IDBase*>* StdMeshInstance::SerializableValueProvider::IDs = nullptr;
45 
46 namespace
47 {
48  // Helper to sort submeshes so that opaque ones appear before non-opaque ones
49  struct StdMeshSubMeshVisibilityCmpPred
50  {
51  bool operator()(const StdSubMesh& first, const StdSubMesh& second)
52  {
53  return first.GetMaterial().IsOpaque() > second.GetMaterial().IsOpaque();
54  }
55  };
56 
57  // Helper to sort submesh instances so that opaque ones appear before non-opaque ones,
58  // this is required if materials are changed with SetMeshMaterial.
59  struct StdMeshSubMeshInstanceVisibilityCmpPred
60  {
61  bool operator()(const StdSubMeshInstance* first, const StdSubMeshInstance* second)
62  {
63  return first->GetMaterial().IsOpaque() > second->GetMaterial().IsOpaque();
64  }
65  };
66 
67  float StdMeshFaceOrderGetVertexZ(const StdMeshVertex& vtx, const StdMeshMatrix& trans)
68  {
69  // TODO: Need to apply attach matrix in case of attached meshes
70 
71  // We need to evaluate the Z coordinate of the transformed vertex
72  // (for all three vertices of the two faces), something like
73  // float z11 = (trans*m_vertices[face1.Vertices[0]]).z;
74  // However we don't do the full matrix multiplication as we are
75  // only interested in the Z coordinate of the result, also we are
76  // not interested in the resulting normals.
77  return trans(2,0)*vtx.x + trans(2,1)*vtx.y + trans(2,2)*vtx.z + trans(2,3);
78  }
79 
80  float StdMeshFaceOrderGetFaceZ(const StdMeshVertex* vertices, const StdMeshFace& face, const StdMeshMatrix& trans)
81  {
82  const float z1 = StdMeshFaceOrderGetVertexZ(vertices[face.Vertices[0]], trans);
83  const float z2 = StdMeshFaceOrderGetVertexZ(vertices[face.Vertices[1]], trans);
84  const float z3 = StdMeshFaceOrderGetVertexZ(vertices[face.Vertices[2]], trans);
85  return std::max(std::max(z1, z2), z3);
86  }
87 
88  void SortFacesArray(const StdMeshVertex* vertices, std::vector<StdMeshFace>& faces, StdSubMeshInstance::FaceOrdering face_ordering, const StdMeshMatrix& trans)
89  {
90  if(faces.empty()) return;
91 
92  std::vector<StdMeshFaceOrderHelper> helpers(faces.size());
93  for(unsigned int i = 0; i < faces.size(); ++i)
94  {
95  helpers[i].i = i;
96  helpers[i].z = StdMeshFaceOrderGetFaceZ(vertices, faces[i], trans);
97  }
98 
99  // The reason to use timsort here instead of std::sort is for performance
100  // reasons. This is performance critical code, with this function being
101  // called at least once per frame for each semi-transparent object. I have
102  // measured a factor 7 difference between the two sorting algorithms on my
103  // system.
104 
105  // We also pre-compute the Z values that we use for sorting, and sort the
106  // array of Z values, then use the resorted indices to sort the original
107  // faces array. The reason for this is twofold:
108  // 1. We don't need to compute the Z value every time the comparison function
109  // is called. Even though the computation is not very expensive, we have
110  // to do many comparisons, and small things add up. I have measured a
111  // 5-10% performance benefit.
112  // 2. More importantly, due to floating point rounding errors we cannot guarantee
113  // that Z values computed in the sorting function always yield the exact same
114  // number, and the same sorting result for the same faces. This can lead to
115  // a crash, because the f(a1, a2) = -f(a2, a1) property for the sorting function
116  // would no longer be met, resulting in undefined behaviour in the sort call.
117  // See http://bugs.openclonk.org/view.php?id=984.
118  StdMesh_tim_sort(&helpers[0], helpers.size());
119 
120  std::vector<StdMeshFace> new_faces(faces.size());
121  switch(face_ordering)
122  {
124  assert(false);
125  break;
127  for(unsigned int i = 0; i < faces.size(); ++i)
128  new_faces[i] = faces[helpers[i].i];
129  break;
131  for(unsigned int i = 0; i < faces.size(); ++i)
132  new_faces[i] = faces[helpers[faces.size() - i - 1].i];
133  break;
134  default:
135  assert(false);
136  break;
137  }
138 
139  faces.swap(new_faces);
140  }
141 
142  // Serialize a ValueProvider with StdCompiler
143  struct ValueProviderAdapt
144  {
145  ValueProviderAdapt(StdMeshInstance::ValueProvider** Provider):
146  ValueProvider(Provider) {}
147 
148  StdMeshInstance::ValueProvider** ValueProvider;
149 
150  void CompileFunc(StdCompiler* pComp)
151  {
154 
155  if(pComp->isDeserializer())
156  {
157  StdCopyStrBuf id_str;
158  pComp->Value(mkParAdapt(id_str, StdCompiler::RCT_Idtf));
159 
161  if(!id) pComp->excCorrupt(R"(No value provider for ID "%s")", id_str.getData());
162  }
163  else
164  {
165  svp = dynamic_cast<StdMeshInstance::SerializableValueProvider*>(*ValueProvider);
166  if(!svp) pComp->excCorrupt("Value provider cannot be compiled");
168  if(!id) pComp->excCorrupt("No ID for value provider registered");
169 
170  StdCopyStrBuf id_str(id->name);
171  pComp->Value(mkParAdapt(id_str, StdCompiler::RCT_Idtf));
172  }
173 
175  pComp->Value(mkContextPtrAdapt(svp, *id, false));
177 
178  if(pComp->isDeserializer())
179  *ValueProvider = svp;
180  }
181  };
182 
183  ValueProviderAdapt mkValueProviderAdapt(StdMeshInstance::ValueProvider** ValueProvider) { return ValueProviderAdapt(ValueProvider); }
184 
185  void CompileFloat(StdCompiler* pComp, float& f)
186  {
187  // TODO: Teach StdCompiler how to handle float
188  if(pComp->isDeserializer())
189  {
190  C4Real r;
191  pComp->Value(r);
192  f = fixtof(r);
193  }
194  else
195  {
196  C4Real r = ftofix(f);
197  pComp->Value(r);
198  }
199  }
200 
201  // Serialize a transformation matrix by name with StdCompiler
202  struct MatrixAdapt
203  {
204  StdMeshMatrix& Matrix;
205  MatrixAdapt(StdMeshMatrix& matrix): Matrix(matrix) {}
206 
207  void CompileFunc(StdCompiler* pComp)
208  {
210  for(unsigned int i = 0; i < 3; ++i)
211  {
212  for(unsigned int j = 0; j < 4; ++j)
213  {
214  if(i != 0 || j != 0) pComp->Separator();
215  CompileFloat(pComp, Matrix(i, j));
216  }
217  }
218 
220  }
221  };
222 
223  struct TransformAdapt
224  {
225  StdMeshTransformation& Trans;
226  TransformAdapt(StdMeshTransformation& trans): Trans(trans) {}
227 
228  void CompileFunc(StdCompiler* pComp)
229  {
231  CompileFloat(pComp, Trans.translate.x);
232  CompileFloat(pComp, Trans.translate.y);
233  CompileFloat(pComp, Trans.translate.z);
234  CompileFloat(pComp, Trans.rotate.w);
235  CompileFloat(pComp, Trans.rotate.x);
236  CompileFloat(pComp, Trans.rotate.y);
237  CompileFloat(pComp, Trans.rotate.z);
238  CompileFloat(pComp, Trans.scale.x);
239  CompileFloat(pComp, Trans.scale.y);
240  CompileFloat(pComp, Trans.scale.z);
242  }
243  };
244 
245  MatrixAdapt mkMatrixAdapt(StdMeshMatrix& Matrix) { return MatrixAdapt(Matrix); }
246  TransformAdapt mkTransformAdapt(StdMeshTransformation& Trans) { return TransformAdapt(Trans); }
247 
248  // Reset all animation list entries corresponding to node or its children
249  void ClearAnimationListRecursively(std::vector<StdMeshInstance::AnimationNode*>& list, StdMeshInstance::AnimationNode* node)
250  {
251  list[node->GetNumber()] = nullptr;
252 
254  {
255  ClearAnimationListRecursively(list, node->GetLeftChild());
256  ClearAnimationListRecursively(list, node->GetRightChild());
257  }
258  }
259 
260  // Mirror is wrt Z axis
261  void MirrorKeyFrame(StdMeshKeyFrame& frame, const StdMeshTransformation& old_bone_transformation, const StdMeshTransformation& new_inverse_bone_transformation)
262  {
263  // frame was a keyframe of a track for old_bone and was now transplanted to new_bone.
264  frame.Transformation.rotate.x = -frame.Transformation.rotate.x;
265  frame.Transformation.rotate.y = -frame.Transformation.rotate.y;
266 
267  StdMeshVector d = old_bone_transformation.scale * (old_bone_transformation.rotate * frame.Transformation.translate);
268  d.z = -d.z;
269  frame.Transformation.translate = new_inverse_bone_transformation.rotate * (new_inverse_bone_transformation.scale * d);
270 
271  // TODO: scale
272  }
273 
274  bool MirrorName(StdStrBuf& buf)
275  {
276  unsigned int len = buf.getLength();
277 
278  if(buf.Compare_(".R", len-2) == 0)
279  buf.getMData()[len-1] = 'L';
280  else if(buf.Compare_(".L", len-2) == 0)
281  buf.getMData()[len-1] = 'R';
282  else
283  return false;
284 
285  return true;
286  }
287 }
288 
289 StdMeshTransformation StdMeshTrack::GetTransformAt(float time, float length) const
290 {
291  std::map<float, StdMeshKeyFrame>::const_iterator iter = Frames.lower_bound(time);
292 
293  // We are at or before the first keyframe. This short typically not
294  // happen, since all animations have a keyframe 0. Simply return the
295  // first keyframe.
296  if (iter == Frames.begin())
297  return iter->second.Transformation;
298 
299  std::map<float, StdMeshKeyFrame>::const_iterator prev_iter = iter;
300  --prev_iter;
301 
302  float iter_pos;
303  if (iter == Frames.end())
304  {
305  // We are beyond the last keyframe.
306  // Interpolate between the last and the first keyframe.
307  // See also bug #1406.
308  iter = Frames.begin();
309  iter_pos = length;
310  }
311  else
312  {
313  iter_pos = iter->first;
314  }
315 
316  // No two keyframes with the same position:
317  assert(iter_pos > prev_iter->first);
318 
319  // Requested position is between the two selected keyframes:
320  assert(time >= prev_iter->first);
321  assert(iter_pos >= time);
322 
323  float dt = iter_pos - prev_iter->first;
324  float weight1 = (time - prev_iter->first) / dt;
325  float weight2 = (iter_pos - time) / dt;
326  (void)weight2; // used in assertion only
327 
328  assert(weight1 >= 0 && weight2 >= 0 && weight1 <= 1 && weight2 <= 1);
329  assert(fabs(weight1 + weight2 - 1) < 1e-6);
330 
331  return StdMeshTransformation::Nlerp(prev_iter->second.Transformation, iter->second.Transformation, weight1);
332 }
333 
335  Name(other.Name), Length(other.Length), Tracks(other.Tracks.size())
336 {
337  // Note that all Tracks are already default-initialized to zero
338  for (unsigned int i = 0; i < Tracks.size(); ++i)
339  if (other.Tracks[i])
340  Tracks[i] = new StdMeshTrack(*other.Tracks[i]);
341 
342  OriginSkeleton = other.OriginSkeleton;
343 }
344 
346 {
347  for (auto & Track : Tracks)
348  delete Track;
349 }
350 
352 {
353  if (this == &other) return *this;
354 
355  Name = other.Name;
356  Length = other.Length;
357 
358  for (auto & Track : Tracks)
359  delete Track;
360 
361  Tracks.resize(other.Tracks.size());
362 
363  for (unsigned int i = 0; i < Tracks.size(); ++i)
364  if (other.Tracks[i])
365  Tracks[i] = new StdMeshTrack(*other.Tracks[i]);
366 
367  return *this;
368 }
369 
370 StdMeshSkeleton::StdMeshSkeleton() = default;
371 
373 {
374  for (auto & Bone : Bones)
375  delete Bone;
376 }
377 
378 void StdMeshSkeleton::AddMasterBone(StdMeshBone *bone)
379 {
380  bone->Index = Bones.size(); // Remember index in master bone table
381  Bones.push_back(bone);
382  for (auto & i : bone->Children)
383  AddMasterBone(i);
384 }
385 
387 {
388  // Lookup parent bone
389  for (auto Bone : Bones)
390  if (Bone->Name == name)
391  return Bone;
392 
393  return nullptr;
394 }
395 
397 {
398  StdCopyStrBuf name2(name);
399  std::map<StdCopyStrBuf, StdMeshAnimation>::const_iterator iter = Animations.find(name2);
400  if (iter == Animations.end()) return nullptr;
401  return &iter->second;
402 }
403 
404 std::vector<const StdMeshAnimation*> StdMeshSkeleton::GetAnimations() const
405 {
406  std::vector<const StdMeshAnimation*> result;
407  result.reserve(Animations.size());
408  for (const auto & Animation : Animations)
409  result.push_back(&Animation.second);
410  return result;
411 }
412 
414 {
415  StdCopyStrBuf name(animation.Name);
416 
417  // do nothing if the name cannot be switched from *.L to *.R or vice versa
418  // or if the animation already exists
419  if (!MirrorName(name) || Animations.find(name) != Animations.end())
420  {
421  return;
422  }
423 
424  StdMeshAnimation& new_anim = Animations.insert(std::make_pair(name, animation)).first->second;
425  new_anim.Name = name;
426 
427  // Go through all bones
428  for (unsigned int i = 0; i < GetNumBones(); ++i)
429  {
430  const StdMeshBone& bone = GetBone(i);
431  StdCopyStrBuf other_bone_name(bone.Name);
432  if (MirrorName(other_bone_name))
433  {
434  const StdMeshBone* other_bone = GetBoneByName(other_bone_name);
435  if (!other_bone)
436  throw std::runtime_error(std::string("No counterpart for bone ") + bone.Name.getData() + " found");
437 
438  // Make sure to not swap tracks twice
439  if ((animation.Tracks[i] != nullptr || animation.Tracks[other_bone->Index] != nullptr) &&
440  other_bone->Index > bone.Index)
441  {
442  std::swap(new_anim.Tracks[i], new_anim.Tracks[other_bone->Index]);
443 
444  StdMeshTransformation own_trans = bone.GetParent()->InverseTransformation * bone.Transformation;
445  StdMeshTransformation other_own_trans = other_bone->GetParent()->InverseTransformation * other_bone->Transformation;
446 
447  // Mirror all the keyframes of both tracks
448  if (new_anim.Tracks[i] != nullptr)
449  for (auto & Frame : new_anim.Tracks[i]->Frames)
450  MirrorKeyFrame(Frame.second, own_trans, StdMeshTransformation::Inverse(other_own_trans));
451 
452  if (new_anim.Tracks[other_bone->Index] != nullptr)
453  for (auto & Frame : new_anim.Tracks[other_bone->Index]->Frames)
454  MirrorKeyFrame(Frame.second, other_own_trans, StdMeshTransformation::Inverse(own_trans));
455  }
456  }
457  else if (bone.Name.Compare_(".N", bone.Name.getLength() - 2) != 0)
458  {
459  if (new_anim.Tracks[i] != nullptr)
460  {
461  StdMeshTransformation own_trans = bone.Transformation;
462  if (bone.GetParent()) own_trans = bone.GetParent()->InverseTransformation * bone.Transformation;
463 
464  for (auto & Frame : new_anim.Tracks[i]->Frames)
465  MirrorKeyFrame(Frame.second, own_trans, StdMeshTransformation::Inverse(own_trans));
466  }
467  }
468  }
469 }
470 
472 {
473  assert(Animations.find(animation.Name) == Animations.end());
474 
475  Animations.insert(std::make_pair(animation.Name, animation));
476 }
477 
479 {
480  assert(Animations.find(animation.Name) == Animations.end());
481 
482  // get matching bones from the source
483  std::vector<int> bone_index_source = source.GetMatchingBones(*this);
484 
485  // create a new animation and copy the basic data from the other animation
486  StdMeshAnimation anim;
487  anim.Name = animation.Name;
488  anim.Length = animation.Length;
489  anim.Tracks.resize(GetNumBones());
490  anim.OriginSkeleton = &source;
491 
492  // sort the tracks according to the matched bones
493  for (unsigned int i = 0; i < anim.Tracks.size(); ++i)
494  {
495  if (bone_index_source[i] > -1 && animation.Tracks[bone_index_source[i]] != nullptr)
496  {
497  anim.Tracks[i] = new StdMeshTrack(*animation.Tracks[bone_index_source[i]]);
498  }
499  }
500 
501  // and add it to the map
502  Animations.insert(std::make_pair(animation.Name, anim));
503 }
504 
506 {
507  // Mirror .R and .L animations without counterpart
508  for (auto & Animation : Animations)
509  {
510  // For debugging purposes:
511  // if(iter->second.Name == "Jump")
512  // MirrorAnimation(StdCopyStrBuf("Jump.Mirror"), iter->second);
513 
514  // mirrors only if necessary
515  MirrorAnimation(Animation.second);
516  }
517 }
518 
519 std::vector<int> StdMeshSkeleton::GetMatchingBones(const StdMeshSkeleton& child_skeleton) const
520 {
521  std::vector<int> MatchedBoneInParentSkeleton;
522 
523  // find matching bones names in both skeletons
524  for (unsigned int i = 0; i < child_skeleton.GetNumBones(); ++i)
525  {
526  int parent_bone_index = -1; // instantiate with invalid index == no match
527 
528  for (unsigned int j = 0; j < GetNumBones(); ++j)
529  {
530  // start searching at the same index
531  int sample_index = (i + j) % GetNumBones();
532 
533  if (GetBone(sample_index).Name == child_skeleton.GetBone(i).Name)
534  {
535  parent_bone_index = sample_index;
536  break;
537  }
538  }
539 
540  // add valid or invalid mapped index to list of mapped bones
541  MatchedBoneInParentSkeleton.push_back(parent_bone_index);
542  }
543 
544  return MatchedBoneInParentSkeleton;
545 }
546 
547 StdMesh::StdMesh() :
548  Skeleton(new StdMeshSkeleton)
549 {
550  BoundingBox.x1 = BoundingBox.y1 = BoundingBox.z1 = 0.0f;
551  BoundingBox.x2 = BoundingBox.y2 = BoundingBox.z2 = 0.0f;
552  BoundingRadius = 0.0f;
553 }
554 
556 {
557 #ifndef USE_CONSOLE
558  if (ibo)
559  glDeleteBuffers(1, &ibo);
560  if (vbo)
561  glDeleteBuffers(1, &vbo);
562  if (vaoid)
563  pGL->FreeVAOID(vaoid);
564 #endif
565 }
566 
568 {
569 #ifndef USE_CONSOLE
570  // Order submeshes so that opaque submeshes come before non-opaque ones
571  std::sort(SubMeshes.begin(), SubMeshes.end(), StdMeshSubMeshVisibilityCmpPred());
572  UpdateVBO();
573  UpdateIBO();
574 
575  // Allocate a VAO ID as well
576  assert(vaoid == 0);
577  vaoid = pGL->GenVAOID();
578 #endif
579 }
580 
581 #ifndef USE_CONSOLE
582 void StdMesh::UpdateVBO()
583 {
584  // We're only uploading vertices once, so there shouldn't be a VBO so far
585  assert(vbo == 0);
586  if (vbo != 0)
587  glDeleteBuffers(1, &vbo);
588  glGenBuffers(1, &vbo);
589 
590  // Calculate total number of vertices
591  size_t total_vertices = SharedVertices.size();
592  for (auto &submesh : SubMeshes)
593  {
594  total_vertices += submesh.GetNumVertices();
595  }
596 
597  glBindBuffer(GL_ARRAY_BUFFER, vbo);
598  pGL->ObjectLabel(GL_BUFFER, vbo, -1, (Label + "/VBO").c_str());
599 
600  // Unmapping the buffer may fail for certain reasons, in which case we need to try again.
601  do
602  {
603  // Allocate VBO backing memory. If this mesh's skeleton has no animations
604  // defined, we assume that the VBO will not change frequently.
605  glBufferData(GL_ARRAY_BUFFER, total_vertices * sizeof(StdMeshVertex), nullptr, GL_STATIC_DRAW);
606  void *map = glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY);
607  uint8_t *buffer = static_cast<uint8_t*>(map);
608  uint8_t *cursor = buffer;
609 
610  // Add shared vertices to buffer
611  if (!SharedVertices.empty())
612  {
613  size_t shared_vertices_size = SharedVertices.size() * sizeof(SharedVertices[0]);
614  std::memcpy(cursor, &SharedVertices[0], shared_vertices_size);
615  cursor += shared_vertices_size;
616  }
617 
618  // Add all submeshes to buffer
619  for (auto &submesh : SubMeshes)
620  {
621  // Store the offset, so the render code can use it later
622  submesh.vertex_buffer_offset = cursor - buffer;
623 
624  if (submesh.Vertices.empty()) continue;
625  size_t vertices_size = sizeof(submesh.Vertices[0]) * submesh.Vertices.size();
626  std::memcpy(cursor, &submesh.Vertices[0], vertices_size);
627  cursor += vertices_size;
628  }
629  } while (glUnmapBuffer(GL_ARRAY_BUFFER) == GL_FALSE);
630  // Unbind the buffer so following rendering calls do not use it
631  glBindBuffer(GL_ARRAY_BUFFER, 0);
632 }
633 
634 void StdMesh::UpdateIBO()
635 {
636  assert(ibo == 0);
637  if (ibo != 0)
638  glDeleteBuffers(1, &ibo);
639  glGenBuffers(1, &ibo);
640  glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo);
641  pGL->ObjectLabel(GL_BUFFER, ibo, -1, (Label + "/IBO").c_str());
642 
643  size_t total_faces = 0;
644  for (auto &submesh : SubMeshes)
645  total_faces += submesh.GetNumFaces();
646  glBufferData(GL_ELEMENT_ARRAY_BUFFER, total_faces * 3 * sizeof(GLuint), nullptr, GL_STATIC_DRAW);
647  size_t offset = 0;
648  for (auto &submesh : SubMeshes)
649  {
650  submesh.index_buffer_offset = offset * 3 * sizeof(GLuint);
651  glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, submesh.index_buffer_offset, submesh.GetNumFaces() * 3 * sizeof(GLuint), &submesh.Faces[0]);
652  offset += submesh.GetNumFaces();
653  }
654  glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
655 }
656 #endif
657 
658 StdSubMeshInstance::StdSubMeshInstance(StdMeshInstance& instance, const StdSubMesh& submesh, float completion):
659  base(&submesh), Material(nullptr), CurrentFaceOrdering(FO_Fixed)
660 {
661 #ifndef USE_CONSOLE
662  LoadFacesForCompletion(instance, submesh, completion);
663 #endif
664 
665  SetMaterial(submesh.GetMaterial());
666 }
667 
668 void StdSubMeshInstance::LoadFacesForCompletion(StdMeshInstance& instance, const StdSubMesh& submesh, float completion)
669 {
670 #ifndef USE_CONSOLE
671  // First: Copy all faces
672  Faces.resize(submesh.GetNumFaces());
673  for (unsigned int i = 0; i < submesh.GetNumFaces(); ++i)
674  Faces[i] = submesh.GetFace(i);
675 
676  if(completion < 1.0f)
677  {
678  // Second: Order by Y position. StdMeshInstanceFaceOrderingCmpPred orders by Z position,
679  // however we can simply give an appropriate transformation matrix to the face ordering.
680  // At this point, all vertices are in the OGRE coordinate frame, and Z in OGRE equals
681  // Y in Clonk, so we are fine without additional transformation.
682  const StdMeshVertex* vertices;
683  if(submesh.GetNumVertices() > 0)
684  vertices = &submesh.GetVertices()[0];
685  else
686  vertices = &instance.GetSharedVertices()[0];
687  SortFacesArray(vertices, Faces, FO_FarthestToNearest, StdMeshMatrix::Identity());
688 
689  // Third: Only use the first few ones
690  assert(submesh.GetNumFaces() >= 1);
691  Faces.resize(Clamp<unsigned int>(static_cast<unsigned int>(completion * submesh.GetNumFaces() + 0.5), 1, submesh.GetNumFaces()));
692  }
693 #endif
694 }
695 
697 {
698  Material = &material;
699 
700 #ifndef USE_CONSOLE
701  // Setup initial texture animation data
702  assert(Material->BestTechniqueIndex >= 0);
704  PassData.resize(technique.Passes.size());
705  for (unsigned int i = 0; i < PassData.size(); ++i)
706  {
707  const StdMeshMaterialPass& pass = technique.Passes[i];
708  // Clear from previous material
709  PassData[i].TexUnits.clear();
710 
711  for (unsigned int j = 0; j < pass.TextureUnits.size(); ++j)
712  {
713  TexUnit unit;
714  unit.Phase = 0;
715  unit.PhaseDelay = 0.0f;
716  unit.Position = 0.0;
717  PassData[i].TexUnits.push_back(unit);
718  }
719  }
720 
721  // TODO: Reset face ordering
722 #endif
723 }
724 
726 {
727 #ifndef USE_CONSOLE
728  if (CurrentFaceOrdering != ordering)
729  {
730  CurrentFaceOrdering = ordering;
731  if (ordering == FO_Fixed)
732  {
733  LoadFacesForCompletion(instance, submesh, instance.GetCompletion());
734  }
735  }
736 #endif
737 }
738 
740 {
741 #ifndef USE_CONSOLE
742  bool opaque = Material->IsOpaque();
743 
744  if(!opaque)
745  SetFaceOrdering(instance, submesh, FO_FarthestToNearest);
746  else if( ((clrmod >> 24) & 0xff) != 0xff)
747  SetFaceOrdering(instance, submesh, FO_NearestToFarthest);
748  else
749  SetFaceOrdering(instance, submesh, FO_Fixed);
750 #endif
751 }
752 
754 {
755  // TODO: We should also serialize the texture animation positions
756  if(pComp->isDeserializer())
757  {
758  StdCopyStrBuf material_name;
759  pComp->Value(mkNamingAdapt(material_name, "Material"));
760  if(material_name != Material->Name)
761  {
762  const StdMeshMaterial* material = ::MeshMaterialManager.GetMaterial(material_name.getData());
763  if(!material)
764  {
765  StdStrBuf buf;
766  buf.Format(R"(There is no such material with name "%s")", material_name.getData());
767  pComp->excCorrupt(buf.getData());
768  }
769 
770  SetMaterial(*material);
771  }
772  }
773  else
774  {
775  StdCopyStrBuf material_name = Material->Name;
776  pComp->Value(mkNamingAdapt(material_name, "Material"));
777  }
778 }
779 
781 {
782  pComp->Value(Value);
783 }
784 
786 {
787  Leaf.Animation = nullptr;
788  Leaf.Position = nullptr;
789 }
790 
792 {
793  Leaf.Animation = animation;
794  Leaf.Position = position;
795 }
796 
797 StdMeshInstanceAnimationNode::StdMeshInstanceAnimationNode(const StdMeshBone* bone, const StdMeshTransformation& trans):
798  Type(CustomNode), Parent(nullptr)
799 {
800  Custom.BoneIndex = bone->Index;
801  Custom.Transformation = new StdMeshTransformation(trans);
802 }
803 
805  Type(LinearInterpolationNode), Parent(nullptr)
806 {
807  LinearInterpolation.ChildLeft = child_left;
808  LinearInterpolation.ChildRight = child_right;
809  LinearInterpolation.Weight = weight;
810 }
811 
813 {
814  switch (Type)
815  {
816  case LeafNode:
817  delete Leaf.Position;
818  break;
819  case CustomNode:
820  delete Custom.Transformation;
821  break;
823  delete LinearInterpolation.ChildLeft;
824  delete LinearInterpolation.ChildRight;
825  delete LinearInterpolation.Weight;
826  break;
827  }
828 }
829 
830 bool StdMeshInstanceAnimationNode::GetBoneTransform(unsigned int bone, StdMeshTransformation& transformation)
831 {
832  StdMeshTransformation combine_with;
833  StdMeshTrack* track;
834 
835  switch (Type)
836  {
837  case LeafNode:
838  track = Leaf.Animation->Tracks[bone];
839  if (!track) return false;
840  transformation = track->GetTransformAt(fixtof(Leaf.Position->Value), Leaf.Animation->Length);
841  return true;
842  case CustomNode:
843  if(bone == Custom.BoneIndex)
844  transformation = *Custom.Transformation;
845  else
846  return false;
847  return true;
849  if (!LinearInterpolation.ChildLeft->GetBoneTransform(bone, transformation))
850  return LinearInterpolation.ChildRight->GetBoneTransform(bone, transformation);
851  if (!LinearInterpolation.ChildRight->GetBoneTransform(bone, combine_with))
852  return true; // First Child affects bone
853 
854  transformation = StdMeshTransformation::Nlerp(transformation, combine_with, fixtof(LinearInterpolation.Weight->Value));
855  return true;
856  default:
857  assert(false);
858  return false;
859  }
860 }
861 
863 {
864  static const StdEnumEntry<NodeType> NodeTypes[] =
865  {
866  { "Leaf", LeafNode },
867  { "Custom", CustomNode },
868  { "LinearInterpolation", LinearInterpolationNode },
869 
870  { nullptr, static_cast<NodeType>(0) }
871  };
872 
873  pComp->Value(mkNamingAdapt(Slot, "Slot"));
874  pComp->Value(mkNamingAdapt(Number, "Number"));
875  pComp->Value(mkNamingAdapt(mkEnumAdaptT<uint8_t>(Type, NodeTypes), "Type"));
876 
877  switch(Type)
878  {
879  case LeafNode:
880  if(pComp->isDeserializer())
881  {
882  StdCopyStrBuf anim_name;
883  pComp->Value(mkNamingAdapt(toC4CStrBuf(anim_name), "Animation"));
884  Leaf.Animation = Mesh->GetSkeleton().GetAnimationByName(anim_name);
885  if(!Leaf.Animation) pComp->excCorrupt(R"(No such animation: "%s")", anim_name.getData());
886  }
887  else
888  {
889  pComp->Value(mkNamingAdapt(mkParAdapt(mkDecompileAdapt(Leaf.Animation->Name), StdCompiler::RCT_All), "Animation"));
890  }
891 
892  pComp->Value(mkNamingAdapt(mkValueProviderAdapt(&Leaf.Position), "Position"));
893  break;
894  case CustomNode:
895  if(pComp->isDeserializer())
896  {
897  StdCopyStrBuf bone_name;
898  pComp->Value(mkNamingAdapt(toC4CStrBuf(bone_name), "Bone"));
899  const StdMeshBone* bone = Mesh->GetSkeleton().GetBoneByName(bone_name);
900  if(!bone) pComp->excCorrupt(R"(No such bone: "%s")", bone_name.getData());
901  Custom.BoneIndex = bone->Index;
902  Custom.Transformation = new StdMeshTransformation;
903  }
904  else
905  {
906  pComp->Value(mkNamingAdapt(mkParAdapt(mkDecompileAdapt(Mesh->GetSkeleton().GetBone(Custom.BoneIndex).Name), StdCompiler::RCT_All), "Bone"));
907  }
908 
909  pComp->Value(mkNamingAdapt(mkTransformAdapt(*Custom.Transformation), "Transformation"));
910  break;
912  pComp->Value(mkParAdapt(mkNamingPtrAdapt(LinearInterpolation.ChildLeft, "ChildLeft"), Mesh));
913  pComp->Value(mkParAdapt(mkNamingPtrAdapt(LinearInterpolation.ChildRight, "ChildRight"), Mesh));
914  pComp->Value(mkNamingAdapt(mkValueProviderAdapt(&LinearInterpolation.Weight), "Weight"));
915  if(pComp->isDeserializer())
916  {
917  if(LinearInterpolation.ChildLeft->Slot != Slot)
918  pComp->excCorrupt("Slot of left child does not match parent slot");
919  if(LinearInterpolation.ChildRight->Slot != Slot)
920  pComp->excCorrupt("Slof of right child does not match parent slot");
921  LinearInterpolation.ChildLeft->Parent = this;
922  LinearInterpolation.ChildRight->Parent = this;
923  }
924  break;
925  default:
926  pComp->excCorrupt("Invalid animation node type");
927  break;
928  }
929 }
930 
932 {
933  StdMeshInstance::SerializableValueProvider* value_provider = nullptr;
934  switch(Type)
935  {
936  case LeafNode:
937  value_provider = dynamic_cast<StdMeshInstance::SerializableValueProvider*>(Leaf.Position);
938  break;
939  case CustomNode:
940  value_provider = nullptr;
941  break;
943  value_provider = dynamic_cast<StdMeshInstance::SerializableValueProvider*>(LinearInterpolation.Weight);
944  // non-recursive, StdMeshInstance::DenumeratePointers walks over all nodes
945  break;
946  }
947 
948  if(value_provider) value_provider->DenumeratePointers();
949 }
950 
952 {
953  StdMeshInstance::SerializableValueProvider* value_provider = nullptr;
954  switch(Type)
955  {
956  case LeafNode:
957  value_provider = dynamic_cast<StdMeshInstance::SerializableValueProvider*>(Leaf.Position);
958  break;
959  case CustomNode:
960  value_provider = nullptr;
961  break;
963  value_provider = dynamic_cast<StdMeshInstance::SerializableValueProvider*>(LinearInterpolation.Weight);
964  // non-recursive, StdMeshInstance::ClearPointers walks over all nodes
965  break;
966  }
967 
968  if(value_provider) value_provider->ClearPointers(pObj);
969 }
970 
972 
973 StdMeshInstance::AttachedMesh::AttachedMesh(unsigned int number, StdMeshInstance* parent, StdMeshInstance* child, bool own_child, Denumerator* denumerator,
974  unsigned int parent_bone, unsigned int child_bone, const StdMeshMatrix& transform, uint32_t flags):
975  Number(number), Parent(parent), Child(child), OwnChild(own_child), ChildDenumerator(denumerator),
976  ParentBone(parent_bone), ChildBone(child_bone), AttachTrans(transform), Flags(flags),
977  FinalTransformDirty(true)
978 {
979  MapBonesOfChildToParent(parent->GetMesh().GetSkeleton(), child->GetMesh().GetSkeleton());
980 }
981 
983 {
984  if (OwnChild)
985  delete Child;
986  delete ChildDenumerator;
987 }
988 
990 {
991  const StdMeshBone* bone_obj = Parent->GetMesh().GetSkeleton().GetBoneByName(bone);
992  if (!bone_obj) return false;
993  ParentBone = bone_obj->Index;
994 
995  FinalTransformDirty = true;
996  return true;
997 }
998 
1000 {
1001  const StdMeshBone* bone_obj = Child->GetMesh().GetSkeleton().GetBoneByName(bone);
1002  if (!bone_obj) return false;
1003  ChildBone = bone_obj->Index;
1004 
1005  FinalTransformDirty = true;
1006  return true;
1007 }
1008 
1009 void StdMeshInstance::AttachedMesh::SetAttachTransformation(const StdMeshMatrix& transformation)
1010 {
1011  AttachTrans = transformation;
1012  FinalTransformDirty = true;
1013 }
1014 
1015 void StdMeshInstance::AttachedMesh::CompileFunc(StdCompiler* pComp, DenumeratorFactoryFunc Factory)
1016 {
1017  if(pComp->isDeserializer())
1018  {
1019  FinalTransformDirty = true;
1020  ChildDenumerator = Factory();
1021  }
1022 
1023  const StdBitfieldEntry<uint8_t> AM_Entries[] =
1024  {
1025  { "MatchSkeleton", AM_MatchSkeleton },
1026  { "DrawBefore", AM_DrawBefore },
1027  { nullptr, 0 }
1028  };
1029 
1030  pComp->Value(mkNamingAdapt(Number, "Number"));
1031  pComp->Value(mkNamingAdapt(ParentBone, "ParentBone")); // TODO: Save as string
1032  pComp->Value(mkNamingAdapt(ChildBone, "ChildBone")); // TODO: Save as string (note we can only resolve this in DenumeratePointers then!)
1033  pComp->Value(mkNamingAdapt(mkMatrixAdapt(AttachTrans), "AttachTransformation"));
1034 
1035  uint8_t dwSyncFlags = static_cast<uint8_t>(Flags);
1036  pComp->Value(mkNamingAdapt(mkBitfieldAdapt(dwSyncFlags, AM_Entries), "Flags", 0u));
1037  if(pComp->isDeserializer()) Flags = dwSyncFlags;
1038 
1039  pComp->Value(mkParAdapt(*ChildDenumerator, this));
1040 }
1041 
1043 {
1044  ChildDenumerator->DenumeratePointers(this);
1045 
1046  assert(Child != nullptr);
1047  Child->AttachParent = this;
1048 
1049  MapBonesOfChildToParent(Parent->GetMesh().GetSkeleton(), Child->GetMesh().GetSkeleton());
1050 
1051  if(OwnChild)
1052  Child->DenumeratePointers();
1053 }
1054 
1056 {
1057  return ChildDenumerator->ClearPointers(pObj);
1058 }
1059 
1060 void StdMeshInstance::AttachedMesh::MapBonesOfChildToParent(const StdMeshSkeleton& parent_skeleton, const StdMeshSkeleton& child_skeleton)
1061 {
1062  // not necessary if we do not match anyway.
1063  if (!(Flags & AM_MatchSkeleton)) return;
1064 
1065  // clear array to avoid filling it twice
1066  MatchedBoneInParentSkeleton.clear();
1067  MatchedBoneInParentSkeleton = parent_skeleton.GetMatchingBones(child_skeleton);
1068 }
1069 
1070 StdMeshInstance::StdMeshInstance(const StdMesh& mesh, float completion):
1071  Mesh(&mesh), Completion(completion),
1072  BoneTransforms(Mesh->GetSkeleton().GetNumBones(), StdMeshMatrix::Identity()),
1073  SubMeshInstances(Mesh->GetNumSubMeshes()), AttachParent(nullptr),
1074  BoneTransformsDirty(false)
1075 #ifndef USE_CONSOLE
1076  , ibo(0), vaoid(0)
1077 #endif
1078 {
1079  // Create submesh instances
1080  for (unsigned int i = 0; i < Mesh->GetNumSubMeshes(); ++i)
1081  {
1082  const StdSubMesh& submesh = Mesh->GetSubMesh(i);
1083  SubMeshInstances[i] = new StdSubMeshInstance(*this, submesh, completion);
1084  }
1085 
1086  // copy, order is fine at the moment since only default materials are used.
1088 }
1089 
1091 {
1092 #ifndef USE_CONSOLE
1093  if (ibo) glDeleteBuffers(1, &ibo);
1094  if (vaoid) pGL->FreeVAOID(vaoid);
1095 #endif
1096 
1097  // If we are attached then detach from parent
1098  if (AttachParent)
1100 
1101  // Remove all attach children
1102  while (!AttachChildren.empty())
1103  DetachMesh(AttachChildren.back()->Number);
1104 
1105  while (!AnimationStack.empty())
1106  StopAnimation(AnimationStack.front());
1107  assert(AnimationNodes.empty());
1108 
1109  // Delete submeshes
1110  for (auto & SubMeshInstance : SubMeshInstances)
1111  {
1112  delete SubMeshInstance;
1113  }
1114 }
1115 
1117 {
1118 #ifndef USE_CONSOLE
1119  for (unsigned int i = 0; i < Mesh->GetNumSubMeshes(); ++i)
1120  SubMeshInstances[i]->SetFaceOrdering(*this, Mesh->GetSubMesh(i), ordering);
1121 
1122  // Faces have been reordered: upload new order to GPU
1123  UpdateIBO();
1124 
1125  // Update attachments (only own meshes for now... others might be displayed both attached and non-attached...)
1126  // still not optimal.
1127  for (AttachedMeshIter iter = AttachChildren.begin(); iter != AttachChildren.end(); ++iter)
1128  if ((*iter)->OwnChild)
1129  (*iter)->Child->SetFaceOrdering(ordering);
1130 #endif
1131 }
1132 
1134 {
1135 #ifndef USE_CONSOLE
1136  for (unsigned int i = 0; i < Mesh->GetNumSubMeshes(); ++i)
1137  SubMeshInstances[i]->SetFaceOrderingForClrModulation(*this, Mesh->GetSubMesh(i), clrmod);
1138 
1139  // Faces have been reordered: upload new order to GPU
1140  UpdateIBO();
1141 
1142  // Update attachments (only own meshes for now... others might be displayed both attached and non-attached...)
1143  // still not optimal.
1144  for (AttachedMeshIter iter = AttachChildren.begin(); iter != AttachChildren.end(); ++iter)
1145  if ((*iter)->OwnChild)
1146  (*iter)->Child->SetFaceOrderingForClrModulation(clrmod);
1147 #endif
1148 }
1149 
1150 void StdMeshInstance::SetCompletion(float completion)
1151 {
1152  Completion = completion;
1153 
1154 #ifndef USE_CONSOLE
1155  // TODO: Load all submesh faces and then determine the ones to use from the
1156  // full pool.
1157  for(unsigned int i = 0; i < Mesh->GetNumSubMeshes(); ++i)
1158  SubMeshInstances[i]->LoadFacesForCompletion(*this, Mesh->GetSubMesh(i), completion);
1159 
1160  // Faces have been reordered: upload new order to GPU
1161  UpdateIBO();
1162 #endif
1163 }
1164 
1165 StdMeshInstance::AnimationNode* StdMeshInstance::PlayAnimation(const StdStrBuf& animation_name, int slot, AnimationNode* sibling, ValueProvider* position, ValueProvider* weight, bool stop_previous_animation)
1166 {
1167  const StdMeshAnimation* animation = Mesh->GetSkeleton().GetAnimationByName(animation_name);
1168  if (!animation) { delete position; delete weight; return nullptr; }
1169 
1170  return PlayAnimation(*animation, slot, sibling, position, weight, stop_previous_animation);
1171 }
1172 
1173 StdMeshInstance::AnimationNode* StdMeshInstance::PlayAnimation(const StdMeshAnimation& animation, int slot, AnimationNode* sibling, ValueProvider* position, ValueProvider* weight, bool stop_previous_animation)
1174 {
1175  position->Value = Clamp(position->Value, Fix0, ftofix(animation.Length));
1176  AnimationNode* child = new AnimationNode(&animation, position);
1177  InsertAnimationNode(child, slot, sibling, weight, stop_previous_animation);
1178  return child;
1179 }
1180 
1181 StdMeshInstance::AnimationNode* StdMeshInstance::PlayAnimation(const StdMeshBone* bone, const StdMeshTransformation& trans, int slot, AnimationNode* sibling, ValueProvider* weight, bool stop_previous_animation)
1182 {
1183  AnimationNode* child = new AnimationNode(bone, trans);
1184  InsertAnimationNode(child, slot, sibling, weight, stop_previous_animation);
1185  return child;
1186 }
1187 
1189 {
1190  ClearAnimationListRecursively(AnimationNodes, node);
1191 
1192  AnimationNode* parent = node->Parent;
1193  if (parent == nullptr)
1194  {
1195  AnimationNodeList::iterator iter = GetStackIterForSlot(node->Slot, false);
1196  assert(iter != AnimationStack.end() && *iter == node);
1197  AnimationStack.erase(iter);
1198  delete node;
1199  }
1200  else
1201  {
1202  assert(parent->Type == AnimationNode::LinearInterpolationNode);
1203 
1204  // Remove parent interpolation node and re-link
1205  AnimationNode* other_child;
1206  if (parent->LinearInterpolation.ChildLeft == node)
1207  {
1208  other_child = parent->LinearInterpolation.ChildRight;
1209  parent->LinearInterpolation.ChildRight = nullptr;
1210  }
1211  else
1212  {
1213  other_child = parent->LinearInterpolation.ChildLeft;
1214  parent->LinearInterpolation.ChildLeft = nullptr;
1215  }
1216 
1217  if (parent->Parent)
1218  {
1220  if (parent->Parent->LinearInterpolation.ChildLeft == parent)
1221  parent->Parent->LinearInterpolation.ChildLeft = other_child;
1222  else
1223  parent->Parent->LinearInterpolation.ChildRight = other_child;
1224  other_child->Parent = parent->Parent;
1225  }
1226  else
1227  {
1228  AnimationNodeList::iterator iter = GetStackIterForSlot(node->Slot, false);
1229  assert(iter != AnimationStack.end() && *iter == parent);
1230  *iter = other_child;
1231 
1232  other_child->Parent = nullptr;
1233  }
1234 
1235  AnimationNodes[parent->Number] = nullptr;
1236  // Recursively deletes parent and its descendants
1237  delete parent;
1238  }
1239 
1240  while (!AnimationNodes.empty() && AnimationNodes.back() == nullptr)
1241  AnimationNodes.erase(AnimationNodes.end()-1);
1242  SetBoneTransformsDirty(true);
1243 }
1244 
1246 {
1247  if (number >= AnimationNodes.size()) return nullptr;
1248  return AnimationNodes[number];
1249 }
1250 
1252 {
1253  AnimationNodeList::iterator iter = GetStackIterForSlot(slot, false);
1254  if (iter == AnimationStack.end()) return nullptr;
1255  return *iter;
1256 }
1257 
1259 {
1260  assert(node->GetType() == AnimationNode::LeafNode);
1261  delete node->Leaf.Position;
1262  node->Leaf.Position = position;
1263 
1264  position->Value = Clamp(position->Value, Fix0, ftofix(node->Leaf.Animation->Length));
1265 
1266  SetBoneTransformsDirty(true);
1267 }
1268 
1269 void StdMeshInstance::SetAnimationBoneTransform(AnimationNode* node, const StdMeshTransformation& trans)
1270 {
1271  assert(node->GetType() == AnimationNode::CustomNode);
1272  *node->Custom.Transformation = trans;
1273  SetBoneTransformsDirty(true);
1274 }
1275 
1277 {
1279  delete node->LinearInterpolation.Weight; node->LinearInterpolation.Weight = weight;
1280 
1281  weight->Value = Clamp(weight->Value, Fix0, itofix(1));
1282 
1283  SetBoneTransformsDirty(true);
1284 }
1285 
1287 {
1288  // Iterate from the back since slots might be removed
1289  for (unsigned int i = AnimationStack.size(); i > 0; --i)
1292 
1293 #ifndef USE_CONSOLE
1294  // Update animated textures
1295  for (auto & SubMeshInstance : SubMeshInstances)
1296  {
1297  StdSubMeshInstance& submesh = *SubMeshInstance;
1298  const StdMeshMaterial& material = submesh.GetMaterial();
1299  const StdMeshMaterialTechnique& technique = material.Techniques[material.BestTechniqueIndex];
1300  for (unsigned int j = 0; j < submesh.PassData.size(); ++j)
1301  {
1302  StdSubMeshInstance::Pass& pass = submesh.PassData[j];
1303  for (unsigned int k = 0; k < pass.TexUnits.size(); ++k)
1304  {
1305  const StdMeshMaterialTextureUnit& texunit = technique.Passes[j].TextureUnits[k];
1306  StdSubMeshInstance::TexUnit& texunit_instance = submesh.PassData[j].TexUnits[k];
1307  if (texunit.HasFrameAnimation())
1308  {
1309  const unsigned int NumPhases = texunit.GetNumTextures();
1310  const float PhaseDuration = texunit.Duration / NumPhases;
1311 
1312  const float Position = texunit_instance.PhaseDelay + dt;
1313  const unsigned int AddPhases = static_cast<unsigned int>(Position / PhaseDuration);
1314 
1315  texunit_instance.Phase = (texunit_instance.Phase + AddPhases) % NumPhases;
1316  texunit_instance.PhaseDelay = Position - AddPhases * PhaseDuration;
1317  }
1318 
1319  if (texunit.HasTexCoordAnimation())
1320  texunit_instance.Position += dt;
1321  }
1322  }
1323  }
1324 #endif
1325 
1326  // Update animation for attached meshes
1327  for (auto & iter : AttachChildren)
1328  iter->Child->ExecuteAnimation(dt);
1329 }
1330 
1331 StdMeshInstance::AttachedMesh* StdMeshInstance::AttachMesh(const StdMesh& mesh, AttachedMesh::Denumerator* denumerator, const StdStrBuf& parent_bone, const StdStrBuf& child_bone, const StdMeshMatrix& transformation, uint32_t flags, unsigned int attach_number)
1332 {
1333  std::unique_ptr<AttachedMesh::Denumerator> auto_denumerator(denumerator);
1334 
1335  StdMeshInstance* instance = new StdMeshInstance(mesh, 1.0f);
1336  AttachedMesh* attach = AttachMesh(*instance, auto_denumerator.release(), parent_bone, child_bone, transformation, flags, true, attach_number);
1337  if (!attach) { delete instance; return nullptr; }
1338  return attach;
1339 }
1340 
1341 StdMeshInstance::AttachedMesh* StdMeshInstance::AttachMesh(StdMeshInstance& instance, AttachedMesh::Denumerator* denumerator, const StdStrBuf& parent_bone, const StdStrBuf& child_bone, const StdMeshMatrix& transformation, uint32_t flags, bool own_child, unsigned int attach_number)
1342 {
1343  std::unique_ptr<AttachedMesh::Denumerator> auto_denumerator(denumerator);
1344 
1345  // Owned attach children must be set via the topmost instance, to ensure
1346  // attach number uniqueness.
1347  if (AttachParent && AttachParent->OwnChild) return nullptr;
1348 
1349  // Find free index.
1350  unsigned int number = 0;
1351  ScanAttachTree(AttachChildren.begin(), AttachChildren.end(), [&number](AttachedMeshList::const_iterator iter) { number = std::max(number, (*iter)->Number); return true; });
1352  number += 1; // One above highest
1353 
1354  StdMeshInstance* direct_parent = this;
1355  if (attach_number != 0)
1356  {
1357  AttachedMesh* attach = GetAttachedMeshByNumber(attach_number);
1358  if (attach == nullptr) return nullptr;
1359  direct_parent = attach->Child;
1360  }
1361 
1362  return direct_parent->AttachMeshImpl(instance, auto_denumerator.release(), parent_bone, child_bone, transformation, flags, own_child, number);
1363 }
1364 
1365 StdMeshInstance::AttachedMesh* StdMeshInstance::AttachMeshImpl(StdMeshInstance& instance, AttachedMesh::Denumerator* denumerator, const StdStrBuf& parent_bone, const StdStrBuf& child_bone, const StdMeshMatrix& transformation, uint32_t flags, bool own_child, unsigned int new_attach_number)
1366 {
1367  std::unique_ptr<AttachedMesh::Denumerator> auto_denumerator(denumerator);
1368 
1369  // We don't allow an instance to be attached to multiple parent instances for now
1370  if (instance.AttachParent) return nullptr;
1371 
1372  // Make sure there are no cyclic attachments
1373  for (StdMeshInstance* Parent = this; Parent->AttachParent != nullptr; Parent = Parent->AttachParent->Parent)
1374  if (Parent == &instance)
1375  return nullptr;
1376 
1377  const StdMeshBone* parent_bone_obj = Mesh->GetSkeleton().GetBoneByName(parent_bone);
1378  const StdMeshBone* child_bone_obj = instance.Mesh->GetSkeleton().GetBoneByName(child_bone);
1379  if (!parent_bone_obj || !child_bone_obj) return nullptr;
1380 
1381  AttachedMesh* attach = new AttachedMesh(new_attach_number, this, &instance, own_child, auto_denumerator.release(), parent_bone_obj->Index, child_bone_obj->Index, transformation, flags);
1382  instance.AttachParent = attach;
1383 
1384  // If DrawInFront is set then sort before others so that drawing order is easy
1385  if(flags & AM_DrawBefore)
1386  AttachChildren.insert(AttachChildren.begin(), attach);
1387  else
1388  AttachChildren.insert(AttachChildren.end(), attach);
1389 
1390  return attach;
1391 }
1392 
1393 bool StdMeshInstance::DetachMesh(unsigned int number)
1394 {
1395  return !ScanAttachTree(AttachChildren.begin(), AttachChildren.end(), [this, number](AttachedMeshList::iterator iter)
1396  {
1397  if ((*iter)->Number == number)
1398  {
1399  AttachedMesh* attached = *iter;
1400 
1401  // Reset attach parent of child so it does not try
1402  // to detach itself on destruction.
1403  attached->Child->AttachParent = nullptr;
1404  attached->Parent->AttachChildren.erase(iter);
1405 
1406  delete attached;
1407 
1408  // Finish scan
1409  return false;
1410  }
1411 
1412  // Continue scan
1413  return true;
1414  });
1415 }
1416 
1418 {
1419  StdMeshInstance::AttachedMesh* result = nullptr;
1420 
1421  ScanAttachTree(AttachChildren.begin(), AttachChildren.end(), [number, &result](AttachedMeshList::const_iterator iter)
1422  {
1423  if ((*iter)->Number == number)
1424  {
1425  result = *iter;
1426  return false;
1427  }
1428 
1429  return true;
1430  });
1431 
1432  return result;
1433 }
1434 
1435 void StdMeshInstance::SetMaterial(size_t i, const StdMeshMaterial& material)
1436 {
1437  assert(i < SubMeshInstances.size());
1438  SubMeshInstances[i]->SetMaterial(material);
1439 #ifndef USE_CONSOLE
1440  std::stable_sort(SubMeshInstancesOrdered.begin(), SubMeshInstancesOrdered.end(), StdMeshSubMeshInstanceVisibilityCmpPred());
1441 #endif
1442 }
1443 
1444 const StdMeshMatrix& StdMeshInstance::GetBoneTransform(size_t i) const
1445 {
1446  if ((AttachParent != nullptr) && (AttachParent->GetFlags() & AM_MatchSkeleton))
1447  {
1448  assert(i < AttachParent->MatchedBoneInParentSkeleton.size());
1449 
1450  int parent_bone_index = AttachParent->MatchedBoneInParentSkeleton[i];
1451 
1452  if (parent_bone_index > -1)
1453  {
1454  return AttachParent->Parent->BoneTransforms[i];
1455  }
1456  }
1457 
1458  return BoneTransforms[i];
1459 }
1460 
1462 {
1463  if ((AttachParent != nullptr) && (AttachParent->GetFlags() & AM_MatchSkeleton))
1464  return AttachParent->MatchedBoneInParentSkeleton.size();
1465  else
1466  return BoneTransforms.size();
1467 }
1468 
1470 {
1471  bool was_dirty = BoneTransformsDirty;
1472 
1473  // Nothing changed since last time
1474  if (BoneTransformsDirty)
1475  {
1476  // Compute transformation matrix for each bone.
1477  for (unsigned int i = 0; i < BoneTransforms.size(); ++i)
1478  {
1479  StdMeshTransformation Transformation;
1480 
1481  const StdMeshBone& bone = Mesh->GetSkeleton().GetBone(i);
1482  const StdMeshBone* parent = bone.GetParent();
1483  assert(!parent || parent->Index < i);
1484 
1485  bool have_transform = false;
1486  for (auto & j : AnimationStack)
1487  {
1488  if (have_transform)
1489  {
1490  StdMeshTransformation other;
1491  if (j->GetBoneTransform(i, other))
1492  Transformation = StdMeshTransformation::Nlerp(Transformation, other, 1.0f); // TODO: Allow custom weighing for slot combination
1493  }
1494  else
1495  {
1496  have_transform = j->GetBoneTransform(i, Transformation);
1497  }
1498  }
1499 
1500  if (!have_transform)
1501  {
1502  if (parent)
1503  BoneTransforms[i] = BoneTransforms[parent->Index];
1504  else
1505  BoneTransforms[i] = StdMeshMatrix::Identity();
1506  }
1507  else
1508  {
1509  BoneTransforms[i] = StdMeshMatrix::Transform(bone.Transformation * Transformation * bone.InverseTransformation);
1510  if (parent) BoneTransforms[i] = BoneTransforms[parent->Index] * BoneTransforms[i];
1511  }
1512  }
1513  }
1514 
1515  // Update attachment's attach transformations. Note this is done recursively.
1516  for (auto attach : AttachChildren)
1517  {
1518  const bool ChildBoneTransformsDirty = attach->Child->BoneTransformsDirty;
1519  attach->Child->UpdateBoneTransforms();
1520 
1521  if (BoneTransformsDirty || ChildBoneTransformsDirty || attach->FinalTransformDirty)
1522  {
1523  was_dirty = true;
1524 
1525  // Compute matrix to change the coordinate system to the one of the attached bone:
1526  // The idea is that a vertex at the child bone's position transforms to the parent bone's position.
1527  // Therefore (read from right to left) we first apply the inverse of the child bone transformation,
1528  // then an optional scaling matrix, and finally the parent bone transformation
1529 
1530  // TODO: we can cache the three matrices in the middle since they don't change over time,
1531  // reducing this to two matrix multiplications instead of four each frame.
1532  // Might even be worth to compute the complete transformation directly when rendering then
1533  // (saves per-instance memory, but requires recomputation if the animation does not change).
1534  // TODO: We might also be able to cache child inverse, and only recomupte it if
1535  // child bone transforms are dirty (saves matrix inversion for unanimated attach children).
1536  attach->FinalTrans = GetBoneTransform(attach->ParentBone)
1537  * StdMeshMatrix::Transform(Mesh->GetSkeleton().GetBone(attach->ParentBone).Transformation)
1538  * attach->AttachTrans
1539  * StdMeshMatrix::Transform(attach->Child->Mesh->GetSkeleton().GetBone(attach->ChildBone).InverseTransformation)
1540  * StdMeshMatrix::Inverse(attach->Child->GetBoneTransform(attach->ChildBone));
1541 
1542  attach->FinalTransformDirty = false;
1543  }
1544  }
1545 
1546  SetBoneTransformsDirty(false);
1547  return was_dirty;
1548 }
1549 
1550 void StdMeshInstance::ReorderFaces(StdMeshMatrix* global_trans)
1551 {
1552 #ifndef USE_CONSOLE
1553  for (auto & SubMeshInstance : SubMeshInstances)
1554  {
1555  StdSubMeshInstance& inst = *SubMeshInstance;
1556  assert((inst.Faces.size() > 0) && "StdMeshInstance sub-mesh instance has zero faces");
1557 
1558  if(inst.Faces.size() > 0 && inst.CurrentFaceOrdering != StdSubMeshInstance::FO_Fixed)
1559  {
1560  const StdMeshVertex* vertices;
1561  if(inst.GetSubMesh().GetNumVertices() > 0)
1562  vertices = &inst.GetSubMesh().GetVertices()[0];
1563  else
1564  vertices = &GetSharedVertices()[0];
1565  SortFacesArray(vertices, inst.Faces, inst.CurrentFaceOrdering, global_trans ? *global_trans : StdMeshMatrix::Identity());
1566  }
1567  }
1568 
1569  // TODO: Also reorder submeshes, attached meshes and include AttachTransformation for attached meshes...
1570 
1571  // Faces have been reordered: upload new order to GPU
1572  UpdateIBO();
1573 #endif
1574 }
1575 
1576 void StdMeshInstance::CompileFunc(StdCompiler* pComp, AttachedMesh::DenumeratorFactoryFunc Factory)
1577 {
1578  if(pComp->isDeserializer())
1579  {
1580  // Only initially created instances can be compiled
1581  assert(!AttachParent);
1582  assert(AttachChildren.empty());
1583  assert(AnimationStack.empty());
1584  SetBoneTransformsDirty(true);
1585 
1586  bool valid;
1587  pComp->Value(mkNamingAdapt(valid, "Valid"));
1588  if(!valid) pComp->excCorrupt("Mesh instance is invalid");
1589 
1590  int32_t iSubMeshCnt;
1591  pComp->Value(mkNamingCountAdapt(iSubMeshCnt, "SubMesh"));
1592  if(static_cast<uint32_t>(iSubMeshCnt) != SubMeshInstances.size())
1593  pComp->excCorrupt("Invalid number of submeshes");
1594  for(int32_t i = 0; i < iSubMeshCnt; ++i)
1595  pComp->Value(mkNamingAdapt(*SubMeshInstances[i], "SubMesh"));
1596 #ifndef USE_CONSOLE
1597  // The sorting predicate depends on having a gfx implementation.
1598  std::stable_sort(SubMeshInstancesOrdered.begin(), SubMeshInstancesOrdered.end(), StdMeshSubMeshInstanceVisibilityCmpPred());
1599 #endif
1600 
1601  int32_t iAnimCnt = AnimationStack.size();
1602  pComp->Value(mkNamingCountAdapt(iAnimCnt, "AnimationNode"));
1603 
1604  for(int32_t i = 0; i < iAnimCnt; ++i)
1605  {
1606  AnimationNode* node = nullptr;
1607  pComp->Value(mkParAdapt(mkNamingPtrAdapt(node, "AnimationNode"), Mesh));
1608  AnimationNodeList::iterator iter = GetStackIterForSlot(node->Slot, true);
1609  if(*iter != nullptr) { delete node; pComp->excCorrupt("Duplicate animation slot index"); }
1610  *iter = node;
1611 
1612  // Add nodes into lookup table
1613  std::vector<AnimationNode*> nodes(1, node);
1614  while(!nodes.empty())
1615  {
1616  node = nodes.back();
1617  nodes.erase(nodes.end()-1);
1618 
1619  if (AnimationNodes.size() <= node->Number)
1620  AnimationNodes.resize(node->Number+1);
1621  if(AnimationNodes[node->Number] != nullptr) pComp->excCorrupt("Duplicate animation node number");
1622  AnimationNodes[node->Number] = node;
1623 
1625  {
1626  nodes.push_back(node->LinearInterpolation.ChildLeft);
1627  nodes.push_back(node->LinearInterpolation.ChildRight);
1628  }
1629  }
1630  }
1631 
1632  int32_t iAttachedCnt;
1633  pComp->Value(mkNamingCountAdapt(iAttachedCnt, "Attached"));
1634 
1635  for(int32_t i = 0; i < iAttachedCnt; ++i)
1636  {
1637  AttachChildren.push_back(new AttachedMesh);
1638  AttachedMesh* attach = AttachChildren.back();
1639 
1640  attach->Parent = this;
1641  pComp->Value(mkNamingAdapt(mkParAdapt(*attach, Factory), "Attached"));
1642  }
1643  }
1644  else
1645  {
1646  // Write something to make sure that the parent
1647  // named section ([Mesh] or [ChildInstance]) is written.
1648  // StdCompilerIni does not make a difference between
1649  // non-existing and empty named sections.
1650  bool valid = true;
1651  pComp->Value(mkNamingAdapt(valid, "Valid"));
1652 
1653  int32_t iSubMeshCnt = SubMeshInstances.size();
1654  pComp->Value(mkNamingCountAdapt(iSubMeshCnt, "SubMesh"));
1655  for(int32_t i = 0; i < iSubMeshCnt; ++i)
1656  pComp->Value(mkNamingAdapt(*SubMeshInstances[i], "SubMesh"));
1657 
1658  int32_t iAnimCnt = AnimationStack.size();
1659  pComp->Value(mkNamingCountAdapt(iAnimCnt, "AnimationNode"));
1660 
1661  for(auto & iter : AnimationStack)
1662  pComp->Value(mkParAdapt(mkNamingPtrAdapt(iter, "AnimationNode"), Mesh));
1663 
1664  int32_t iAttachedCnt = AttachChildren.size();
1665  pComp->Value(mkNamingCountAdapt(iAttachedCnt, "Attached"));
1666 
1667  for(auto & i : AttachChildren)
1668  pComp->Value(mkNamingAdapt(mkParAdapt(*i, Factory), "Attached"));
1669  }
1670 }
1671 
1673 {
1674  for(auto & AnimationNode : AnimationNodes)
1675  if(AnimationNode)
1677 
1678  for(auto & i : AttachChildren)
1679  {
1680  i->DenumeratePointers();
1681  }
1682 }
1683 
1685 {
1686  for(auto & AnimationNode : AnimationNodes)
1687  if(AnimationNode)
1689 
1690  std::vector<unsigned int> Removal;
1691  for(auto & i : AttachChildren)
1692  if(!i->ClearPointers(pObj))
1693  Removal.push_back(i->Number);
1694 
1695  for(unsigned int i : Removal)
1696  DetachMesh(i);
1697 }
1698 
1699 template<typename IteratorType, typename FuncObj>
1700 bool StdMeshInstance::ScanAttachTree(IteratorType begin, IteratorType end, const FuncObj& obj)
1701 {
1702  for (IteratorType iter = begin; iter != end; ++iter)
1703  {
1704  if (!obj(iter)) return false;
1705 
1706  // Scan attached tree of own children. For non-owned children,
1707  // we can't guarantee unique attach numbers.
1708  if( (*iter)->OwnChild)
1709  if (!ScanAttachTree((*iter)->Child->AttachChildren.begin(), (*iter)->Child->AttachChildren.end(), obj))
1710  return false;
1711  }
1712 
1713  return true;
1714 }
1715 
1716 StdMeshInstance::AnimationNodeList::iterator StdMeshInstance::GetStackIterForSlot(int slot, bool create)
1717 {
1718  // TODO: bsearch
1719  for (AnimationNodeList::iterator iter = AnimationStack.begin(); iter != AnimationStack.end(); ++iter)
1720  {
1721  if ((*iter)->Slot == slot)
1722  {
1723  return iter;
1724  }
1725  else if ((*iter)->Slot > slot)
1726  {
1727  if (!create)
1728  return AnimationStack.end();
1729  else
1730  return AnimationStack.insert(iter, nullptr);
1731  }
1732  }
1733 
1734  if (!create)
1735  return AnimationStack.end();
1736  else
1737  return AnimationStack.insert(AnimationStack.end(), nullptr);
1738 }
1739 
1740 void StdMeshInstance::InsertAnimationNode(AnimationNode* node, int slot, AnimationNode* sibling, ValueProvider* weight, bool stop_previous_animation)
1741 {
1742  assert(!sibling || !stop_previous_animation);
1743  // Default
1744  if (!sibling) sibling = GetRootAnimationForSlot(slot);
1745  assert(!sibling || sibling->Slot == slot);
1746 
1747  // Stop any animation already running in this slot?
1748  if (sibling && stop_previous_animation)
1749  {
1750  StopAnimation(sibling);
1751  sibling = nullptr;
1752  }
1753 
1754  // Find two subsequent numbers in case we need to create two nodes, so
1755  // script can deduce the second node.
1756  unsigned int Number1, Number2;
1757  for (Number1 = 0; Number1 < AnimationNodes.size(); ++Number1)
1758  if (AnimationNodes[Number1] == nullptr && (!sibling || Number1+1 == AnimationNodes.size() || AnimationNodes[Number1+1] == nullptr))
1759  break;
1760  /* for(Number2 = Number1+1; Number2 < AnimationNodes.size(); ++Number2)
1761  if(AnimationNodes[Number2] == nullptr)
1762  break;*/
1763  Number2 = Number1 + 1;
1764 
1765  weight->Value = Clamp(weight->Value, Fix0, itofix(1));
1766 
1767  if (Number1 == AnimationNodes.size()) AnimationNodes.push_back( (StdMeshInstance::AnimationNode*) nullptr);
1768  if (sibling && Number2 == AnimationNodes.size()) AnimationNodes.push_back( (StdMeshInstance::AnimationNode*) nullptr);
1769 
1770  AnimationNodes[Number1] = node;
1771  node->Number = Number1;
1772  node->Slot = slot;
1773 
1774  if (sibling)
1775  {
1776  AnimationNode* parent = new AnimationNode(node, sibling, weight);
1777  AnimationNodes[Number2] = parent;
1778  parent->Number = Number2;
1779  parent->Slot = slot;
1780 
1781  node->Parent = parent;
1782  parent->Parent = sibling->Parent;
1783  parent->LinearInterpolation.ChildLeft = sibling;
1784  parent->LinearInterpolation.ChildRight = node;
1785  if (sibling->Parent)
1786  {
1787  if (sibling->Parent->LinearInterpolation.ChildLeft == sibling)
1788  sibling->Parent->LinearInterpolation.ChildLeft = parent;
1789  else
1790  sibling->Parent->LinearInterpolation.ChildRight = parent;
1791  }
1792  else
1793  {
1794  // set new parent
1795  AnimationNodeList::iterator iter = GetStackIterForSlot(slot, false);
1796  // slot must not be empty, since sibling uses same slot
1797  assert(iter != AnimationStack.end() && *iter != nullptr);
1798  *iter = parent;
1799  }
1800 
1801  sibling->Parent = parent;
1802  }
1803  else
1804  {
1805  delete weight;
1806  AnimationNodeList::iterator iter = GetStackIterForSlot(slot, true);
1807  assert(!*iter); // we have a sibling if slot is not empty
1808  *iter = node;
1809  }
1810 
1811  SetBoneTransformsDirty(true);
1812 }
1813 
1815 {
1816  ValueProvider* provider = nullptr;
1817  C4Real min;
1818  C4Real max;
1819 
1820  switch (node->GetType())
1821  {
1823  provider = node->GetPositionProvider();
1824  min = Fix0;
1825  max = ftofix(node->GetAnimation()->Length);
1826  break;
1828  // No execution necessary
1829  return true;
1831  provider = node->GetWeightProvider();
1832  min = Fix0;
1833  max = itofix(1);
1834  break;
1835  default:
1836  assert(false);
1837  break;
1838  }
1839 
1840  assert(provider);
1841  const C4Real old_value = provider->Value;
1842 
1843  if (!provider->Execute())
1844  {
1845  if (node->GetType() == AnimationNode::LeafNode) return false;
1846 
1847  // Remove the child with less weight (normally weight reaches 0.0 or 1.0)
1848  if (node->GetWeight() > itofix(1, 2))
1849  {
1850  // Remove both children (by parent) if other wants to be deleted as well
1851  if (!ExecuteAnimationNode(node->GetRightChild())) return false;
1852  // Remove left child as it has less weight
1853  StopAnimation(node->LinearInterpolation.ChildLeft);
1854  }
1855  else
1856  {
1857  // Remove both children (by parent) if other wants to be deleted as well
1858  if (!ExecuteAnimationNode(node->GetLeftChild())) return false;
1859  // Remove right child as it has less weight
1860  StopAnimation(node->LinearInterpolation.ChildRight);
1861  }
1862  }
1863  else
1864  {
1865  if (provider->Value != old_value)
1866  {
1867  provider->Value = Clamp(provider->Value, min, max);
1868  SetBoneTransformsDirty(true);
1869  }
1870 
1872  {
1873  const bool left_result = ExecuteAnimationNode(node->GetLeftChild());
1874  const bool right_result = ExecuteAnimationNode(node->GetRightChild());
1875 
1876  // Remove this node completely
1877  if (!left_result && !right_result)
1878  return false;
1879 
1880  // Note that either of this also removes node
1881  if (!left_result)
1882  StopAnimation(node->GetLeftChild());
1883  if (!right_result)
1884  StopAnimation(node->GetRightChild());
1885  }
1886  }
1887 
1888  return true;
1889 }
1890 
1892 {
1893  BoneTransformsDirty = value;
1894 
1895  // only if the value is true, so that updates happen
1896  if (value)
1897  {
1898  // Update attachment's attach transformations. Note this is done recursively.
1899  for (auto attach : AttachChildren)
1900  {
1901  if (attach->GetFlags() & AM_MatchSkeleton)
1902  {
1903  attach->Child->SetBoneTransformsDirty(value);
1904  }
1905  }
1906  }
1907 }
1908 
1909 #ifndef USE_CONSOLE
1911 {
1912  // First, find out whether we have fixed face ordering or not
1913  bool all_submeshes_fixed = true;
1914  for (StdSubMeshInstance* inst : SubMeshInstances)
1915  {
1916  all_submeshes_fixed = (inst->GetFaceOrdering() == StdSubMeshInstance::FO_Fixed);
1917  if (!all_submeshes_fixed) break;
1918 
1919  // If true, submesh is 100% complete
1920  all_submeshes_fixed = inst->GetNumFaces() == inst->GetSubMesh().GetNumFaces();
1921  if (!all_submeshes_fixed) break;
1922  }
1923 
1924  // If the face ordering is fixed, then we don't need a custom
1925  // IBO. This is typically the case for all meshes without transparency
1926  // and 100% completion.
1927  if (all_submeshes_fixed)
1928  {
1929  if (ibo) glDeleteBuffers(1, &ibo);
1930  if (vaoid) pGL->FreeVAOID(vaoid);
1931  ibo = 0; vaoid = 0;
1932  }
1933  else
1934  {
1935  // We have a custom face ordering, or we render only a subset
1936  // of our faces. Create a custom IBO and upload the index
1937  // data.
1938  if (ibo == 0)
1939  {
1940  // This is required, because the IBO binding is part
1941  // of the VAO state. If we create a new IBO we cannot
1942  // keep using any old VAO. But we always create and
1943  // destroy them together, so we can assert here.
1944  assert(vaoid == 0);
1945 
1946  size_t total_faces = 0;
1947  for (unsigned int i = 0; i < Mesh->GetNumSubMeshes(); ++i)
1948  total_faces += Mesh->GetSubMesh(i).GetNumFaces();
1949 
1950  glGenBuffers(1, &ibo);
1951  glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo);
1952 
1953  // TODO: Optimize mode. In many cases this is still fairly static.
1954  glBufferData(GL_ELEMENT_ARRAY_BUFFER, total_faces * 3 * sizeof(GLuint), nullptr, GL_STREAM_DRAW);
1955  }
1956  else
1957  {
1958  glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo);
1959  }
1960 
1961  for (StdSubMeshInstance* inst : SubMeshInstances)
1962  {
1963  assert(inst->GetNumFaces() <= inst->GetSubMesh().GetNumFaces());
1964  glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, inst->GetSubMesh().GetOffsetInIBO(), inst->GetNumFaces() * 3 * sizeof(GLuint), &inst->Faces[0]);
1965  }
1966 
1967  glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
1968 
1969  if (vaoid == 0)
1970  vaoid = pGL->GenVAOID();
1971  }
1972 }
1973 #endif
FaceOrdering CurrentFaceOrdering
Definition: StdMesh.h:301
const char * getData() const
Definition: StdBuf.h:442
virtual bool Separator(Sep eSep=SEP_SEP)
Definition: StdCompiler.h:119
std::vector< StdSubMeshInstance * > SubMeshInstancesOrdered
Definition: StdMesh.h:656
const StdMeshMaterial & GetMaterial() const
Definition: StdMesh.h:170
AnimationNodeList AnimationStack
Definition: StdMesh.h:652
void LoadFacesForCompletion(class StdMeshInstance &instance, const StdSubMesh &submesh, float completion)
Definition: StdMesh.cpp:668
const StdSubMesh & GetSubMesh() const
Definition: StdMesh.h:265
Definition: StdAdaptors.h:850
#define z
StdMeshInstance(const StdMesh &mesh, float completion=1.0f)
Definition: StdMesh.cpp:1070
void CompileFunc(StdCompiler *pComp)
Definition: StdMesh.cpp:753
Definition: StdAdaptors.h:762
StdNamingCountAdapt< int_t > mkNamingCountAdapt(int_t &iCount, const char *szName)
Definition: StdAdaptors.h:976
void CompileFunc(StdCompiler *pComp, AttachedMesh::DenumeratorFactoryFunc Factory)
Definition: StdMesh.cpp:1576
bool BoneTransformsDirty
Definition: StdMesh.h:663
const StdMeshMaterial * Material
Definition: StdMesh.h:282
const StdMeshMaterial & GetMaterial() const
Definition: StdMesh.h:270
NodeType GetType() const
Definition: StdMesh.h:347
void excCorrupt(const char *szMessage,...)
Definition: StdCompiler.h:249
const std::vector< StdMeshVertex > & GetSharedVertices() const
Definition: StdMesh.h:549
virtual void CompileFunc(StdCompiler *pComp)
Definition: StdMesh.cpp:780
void SetFaceOrderingForClrModulation(uint32_t clrmod)
Definition: StdMesh.cpp:1133
AttachedMesh * GetAttachedMeshByNumber(unsigned int number) const
Definition: StdMesh.cpp:1417
unsigned int vaoid
Definition: StdMesh.h:671
GLuint ibo
Definition: StdMesh.h:670
size_t GetNumFaces() const
Definition: StdMesh.h:168
StdCopyStrBuf Name
Definition: StdMesh.h:97
StdMeshTransformation Transformation
Definition: StdMesh.h:37
std::vector< TexUnit > TexUnits
Definition: StdMesh.h:297
void FreeVAOID(unsigned int vaoid)
Definition: C4DrawGL.cpp:967
void PostInit()
Definition: StdMesh.cpp:505
AnimationNode * GetRootAnimationForSlot(int slot)
Definition: StdMesh.cpp:1251
const StdMeshSkeleton & GetSkeleton() const
Definition: StdMesh.h:203
AnimationNode * GetAnimationNodeByNumber(unsigned int number)
Definition: StdMesh.cpp:1245
void Format(const char *szFmt,...) GNUC_FORMAT_ATTRIBUTE_O
Definition: StdBuf.cpp:174
bool IsOpaque() const
#define z2
ValueProvider * GetWeightProvider()
Definition: StdMesh.h:356
bool SetChildBone(const StdStrBuf &bone)
Definition: StdMesh.cpp:999
StdMeshInstance * Child
Definition: StdMesh.h:511
void SetFaceOrdering(class StdMeshInstance &instance, const StdSubMesh &submesh, FaceOrdering ordering)
Definition: StdMesh.cpp:725
StdMeshAnimation()=default
size_t GetNumSubMeshes() const
Definition: StdMesh.h:588
size_t GetNumSubMeshes() const
Definition: StdMesh.h:199
unsigned int Vertices[3]
Definition: StdMesh.h:61
Definition: C4Real.h:58
T Clamp(T bval, T lbound, T rbound)
Definition: Standard.h:44
void MirrorAnimation(const StdMeshAnimation &animation)
Definition: StdMesh.cpp:413
char * getMData()
Definition: StdBuf.h:443
StdNamingAdapt< T > mkNamingAdapt(T &&rValue, const char *szName)
Definition: StdAdaptors.h:92
const StdMeshMatrix & GetBoneTransform(size_t i) const
Definition: StdMesh.cpp:1444
void InsertAnimationNode(AnimationNode *node, int slot, AnimationNode *sibling, ValueProvider *weight, bool stop_previous_animation)
Definition: StdMesh.cpp:1740
AttachedMesh * AttachMeshImpl(StdMeshInstance &instance, AttachedMesh::Denumerator *denumerator, const StdStrBuf &parent_bone, const StdStrBuf &child_bone, const StdMeshMatrix &transformation, uint32_t flags, bool own_child, unsigned int new_attach_number)
Definition: StdMesh.cpp:1365
void ClearPointers(class C4Object *pObj)
Definition: StdMesh.cpp:1684
void InsertAnimation(const StdMeshAnimation &animation)
Definition: StdMesh.cpp:471
unsigned int GetNumber() const
Definition: StdMesh.h:346
void SetFaceOrderingForClrModulation(class StdMeshInstance &instance, const StdSubMesh &submesh, uint32_t clrmod)
Definition: StdMesh.cpp:739
const StdMeshFace & GetFace(size_t i) const
Definition: StdMesh.h:167
AnimationNode * PlayAnimation(const StdStrBuf &animation_name, int slot, AnimationNode *sibling, ValueProvider *position, ValueProvider *weight, bool stop_previous_animation)
Definition: StdMesh.cpp:1165
void DenumeratePointers()
Definition: StdMesh.cpp:1672
C4Real GetWeight() const
Definition: StdMesh.h:357
const StdMeshBone & GetBone(size_t i) const
Definition: StdMesh.h:116
void PostInit()
Definition: StdMesh.cpp:567
void ClearPointers(class C4Object *pObj)
Definition: StdMesh.cpp:951
size_t GetNumTextures() const
StdBitfieldAdapt< T > mkBitfieldAdapt(T &rVal, const StdBitfieldEntry< T > *pNames)
Definition: StdAdaptors.h:953
void ExecuteAnimation(float dt)
Definition: StdMesh.cpp:1286
AnimationNode * Parent
Definition: StdMesh.h:367
void StopAnimation(AnimationNode *node)
Definition: StdMesh.cpp:1188
StdDecompileAdapt< T > mkDecompileAdapt(const T &rValue)
Definition: StdAdaptors.h:153
void ReorderFaces(StdMeshMatrix *global_trans)
Definition: StdMesh.cpp:1550
ValueProvider * GetPositionProvider()
Definition: StdMesh.h:351
AnimationNode * GetRightChild()
Definition: StdMesh.h:355
bool ClearPointers(class C4Object *pObj)
Definition: StdMesh.cpp:1055
void CompileFunc(StdCompiler *pComp, const StdMesh *Mesh)
Definition: StdMesh.cpp:862
size_t GetNumVertices() const
Definition: StdMesh.h:165
virtual void ClearPointers(class C4Object *pObj)
Definition: StdMesh.h:480
std::vector< Pass > PassData
Definition: StdMesh.h:300
const StdMeshAnimation * GetAnimationByName(const StdStrBuf &name) const
Definition: StdMesh.cpp:396
const StdSubMesh & GetSubMesh(size_t i) const
Definition: StdMesh.h:198
C4Fixed itofix(int32_t x)
Definition: C4Real.h:261
void CompileFunc(StdCompiler *pComp, DenumeratorFactoryFunc Factory)
Definition: StdMesh.cpp:1015
bool HasTexCoordAnimation() const
uint32_t GetFlags() const
Definition: StdMesh.h:519
const StdMesh * Mesh
Definition: StdMesh.h:647
void Value(const T &rStruct)
Definition: StdCompiler.h:161
C4Fixed ftofix(float x)
Definition: C4Real.h:258
void SetAttachTransformation(const StdMeshMatrix &transformation)
Definition: StdMesh.cpp:1009
bool ExecuteAnimationNode(AnimationNode *node)
Definition: StdMesh.cpp:1814
AttachedMeshList::const_iterator AttachedMeshIter
Definition: StdMesh.h:544
virtual bool isDeserializer()
Definition: StdCompiler.h:53
StdMeshInstanceAnimationNode AnimationNode
Definition: StdMesh.h:399
bool DetachMesh(unsigned int number)
Definition: StdMesh.cpp:1393
void SetBoneTransformsDirty(bool value)
Definition: StdMesh.cpp:1891
const StdMeshBone * GetBoneByName(const StdStrBuf &name) const
Definition: StdMesh.cpp:386
const std::vector< Vertex > & GetVertices() const
Definition: StdMesh.h:163
float GetCompletion() const
Definition: StdMesh.h:554
void SetCompletion(float completion)
Definition: StdMesh.cpp:1150
std::vector< StdMeshMaterialPass > Passes
void SetFaceOrdering(FaceOrdering ordering)
Definition: StdMesh.cpp:1116
float Completion
Definition: StdMesh.h:649
bool UpdateBoneTransforms()
Definition: StdMesh.cpp:1469
void SetMaterial(const StdMeshMaterial &material)
Definition: StdMesh.cpp:696
std::vector< StdMeshMaterialTextureUnit > TextureUnits
std::vector< const StdMeshAnimation * > GetAnimations() const
Definition: StdMesh.cpp:404
StdCopyStrBuf Name
Definition: StdMesh.h:34
unsigned int GenVAOID()
Definition: C4DrawGL.cpp:925
AttachedMesh * AttachParent
Definition: StdMesh.h:661
AttachedMesh * AttachMesh(const StdMesh &mesh, AttachedMesh::Denumerator *denumerator, const StdStrBuf &parent_bone, const StdStrBuf &child_bone, const StdMeshMatrix &transformation=StdMeshMatrix::Identity(), uint32_t flags=AM_None, unsigned int attach_number=0)
Definition: StdMesh.cpp:1331
StdMeshTransformation GetTransformAt(float time, float length) const
Definition: StdMesh.cpp:289
void SetMaterial(size_t i, const StdMeshMaterial &material)
Definition: StdMesh.cpp:1435
void SetAnimationPosition(AnimationNode *node, ValueProvider *position)
Definition: StdMesh.cpp:1258
std::vector< StdMeshFace > Faces
Definition: StdMesh.h:280
void SetAnimationWeight(AnimationNode *node, ValueProvider *weight)
Definition: StdMesh.cpp:1276
float fixtof(const C4Fixed &x)
Definition: C4Real.h:257
StdMeshTransformation InverseTransformation
Definition: StdMesh.h:39
unsigned int Index
Definition: StdMesh.h:32
StdCopyStrBuf Name
std::vector< StdMeshMatrix > BoneTransforms
Definition: StdMesh.h:653
void ObjectLabel(uint32_t identifier, uint32_t name, int32_t length, const char *label)
Definition: C4DrawGL.cpp:274
StdParameterAdapt< T, P > mkParAdapt(T &&rObj, P &&rPar)
Definition: StdAdaptors.h:458
const StdMeshAnimation * GetAnimation() const
Definition: StdMesh.h:350
AnimationNodeList AnimationNodes
Definition: StdMesh.h:651
AnimationNode * GetLeftChild()
Definition: StdMesh.h:354
AnimationNodeList::iterator GetStackIterForSlot(int slot, bool create)
Definition: StdMesh.cpp:1716
size_t getLength() const
Definition: StdBuf.h:445
std::vector< StdMeshMaterialTechnique > Techniques
const StdMeshMaterial * GetMaterial(const char *material_name) const
StdMeshAnimation & operator=(const StdMeshAnimation &other)
Definition: StdMesh.cpp:351
std::vector< AttachedMesh * > AttachChildren
Definition: StdMesh.h:660
StdSubMeshInstance(class StdMeshInstance &instance, const StdSubMesh &submesh, float completion)
Definition: StdMesh.cpp:658
CStdGL * pGL
Definition: C4DrawGL.cpp:905
StdPtrAdapt< T > mkNamingPtrAdapt(T *&rpObj, const char *szNaming)
Definition: StdAdaptors.h:604
const StdMesh & GetMesh() const
Definition: StdMesh.h:622
static bool ScanAttachTree(IteratorType begin, IteratorType end, const FuncObj &obj)
Definition: StdMesh.cpp:1700
bool SetParentBone(const StdStrBuf &bone)
Definition: StdMesh.cpp:989
#define toC4CStrBuf(rBuf)
Definition: StdAdaptors.h:25
bool GetBoneTransform(unsigned int bone, StdMeshTransformation &transformation)
Definition: StdMesh.cpp:830
~StdMesh()
Definition: StdMesh.cpp:555
void UpdateIBO()
Definition: StdMesh.cpp:1910
float Length
Definition: StdMesh.h:98
std::vector< int > GetMatchingBones(const StdMeshSkeleton &skeleton) const
Definition: StdMesh.cpp:519
static const IDBase * Lookup(const char *name)
Definition: StdMesh.h:460
StdMeshMatManager MeshMaterialManager
size_t GetNumBones() const
Definition: StdMesh.h:117
void SetAnimationBoneTransform(AnimationNode *node, const StdMeshTransformation &trans)
Definition: StdMesh.cpp:1269
size_t GetBoneCount() const
Definition: StdMesh.cpp:1461
void CompileFunc(C4Real &rValue, StdCompiler *pComp)
Definition: C4Real.cpp:9033
StdMeshTransformation Transformation
Definition: StdMesh.h:68
StdMeshInstance * Parent
Definition: StdMesh.h:510
StdContextPtrAdapt< T, ContextT > mkContextPtrAdapt(T *&rpObj, const ContextT &ctx, bool fAllowNull=true)
Definition: StdAdaptors.h:609
const C4Real Fix0
Definition: C4Real.h:312
const StdMeshBone * GetParent() const
Definition: StdMesh.h:41
std::vector< StdSubMeshInstance * > SubMeshInstances
Definition: StdMesh.h:655
int Compare_(const char *pCData, size_t iAt=0) const
Definition: StdBuf.h:484