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