RigsofRods
Soft-body Physics Simulation
FlexBody.cpp
Go to the documentation of this file.
1 /*
2  This source file is part of Rigs of Rods
3  Copyright 2005-2012 Pierre-Michel Ricordel
4  Copyright 2007-2012 Thomas Fischer
5  Copyright 2013-2020 Petr Ohlidal
6 
7  For more information, see http://www.rigsofrods.org/
8 
9  Rigs of Rods is free software: you can redistribute it and/or modify
10  it under the terms of the GNU General Public License version 3, as
11  published by the Free Software Foundation.
12 
13  Rigs of Rods is distributed in the hope that it will be useful,
14  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16  GNU General Public License for more details.
17 
18  You should have received a copy of the GNU General Public License
19  along with Rigs of Rods. If not, see <http://www.gnu.org/licenses/>.
20 */
21 
22 #include "FlexBody.h"
23 
24 #include "Application.h"
25 #include "ApproxMath.h"
26 #include "Console.h"
27 #include "SimData.h"
28 #include "FlexFactory.h"
29 #include "GfxActor.h"
30 #include "GfxScene.h"
31 #include "RigDef_File.h"
32 
33 #include <Ogre.h>
34 
35 using namespace Ogre;
36 using namespace RoR;
37 
38 FlexBody::FlexBody(
39  RoR::FlexBodyCacheData* preloaded_from_cache,
40  RoR::GfxActor* gfx_actor,
41  Ogre::Entity* ent,
42  NodeNum_t ref,
43  NodeNum_t nx,
44  NodeNum_t ny,
45  Ogre::Vector3 offset,
46  Ogre::Quaternion const & rot,
47  std::vector<unsigned int> & node_indices
48 ):
49  m_center_offset(offset)
50  , m_node_center(ref)
51  , m_node_x(nx)
52  , m_node_y(ny)
53  , m_scene_entity(ent)
54  , m_gfx_actor(gfx_actor)
55 {
58 
59  Ogre::Vector3* vertices = nullptr;
60  std::string mesh_name = ent->getMesh()->getName();
61 
62  Vector3 normal = Vector3::UNIT_Y;
63  Vector3 position = Vector3::ZERO;
64  Quaternion orientation = Quaternion::ZERO;
65 
67 
69  {
70  Vector3 diffX = nodes[nx].AbsPosition-nodes[ref].AbsPosition;
71  Vector3 diffY = nodes[ny].AbsPosition-nodes[ref].AbsPosition;
72 
73  normal = (diffY.crossProduct(diffX)).normalisedCopy();
74 
75  // position
76  position = nodes[ref].AbsPosition + offset.x * diffX + offset.y * diffY;
77  position = position + offset.z * normal;
78 
79  // orientation
80  Vector3 refX = diffX.normalisedCopy();
81  Vector3 refY = refX.crossProduct(normal);
82  orientation = Quaternion(refX, normal, refY) * rot;
83  }
84  else
85  {
86  // special case!
87  normal = Vector3::UNIT_Y;
88  position = nodes[0].AbsPosition + offset;
89  orientation = rot;
90  }
91 
92  Ogre::MeshPtr mesh=ent->getMesh();
93  m_orig_mesh_info = RoR::PrintMeshInfo("Original", mesh); // For diagnostics only
94  int num_submeshes = static_cast<int>(mesh->getNumSubMeshes());
95  if (preloaded_from_cache == nullptr)
96  {
97  //determine if we have texture coordinates everywhere
98  if (mesh->sharedVertexData && mesh->sharedVertexData->vertexDeclaration->findElementBySemantic(VES_TEXTURE_COORDINATES)==0)
99  {
100  m_has_texture=false;
101  }
102  for (int i=0; i<num_submeshes; i++)
103  {
104  if (!mesh->getSubMesh(i)->useSharedVertices && mesh->getSubMesh(i)->vertexData->vertexDeclaration->findElementBySemantic(VES_TEXTURE_COORDINATES)==0)
105  {
106  m_has_texture=false;
107  }
108  }
109  if (!m_has_texture)
110  {
111  LOG("FLEXBODY Warning: at least one part of this mesh does not have texture coordinates, switching off texturing!");
112  m_has_texture_blend=false;
113  }
114 
115  //detect the anomalous case where a mesh is exported without normal vectors
116  bool havenormal=true;
117  if (mesh->sharedVertexData && mesh->sharedVertexData->vertexDeclaration->findElementBySemantic(VES_NORMAL)==0)
118  {
119  havenormal=false;
120  }
121  for (int i=0; i<num_submeshes; i++)
122  {
123  if (!mesh->getSubMesh(i)->useSharedVertices && mesh->getSubMesh(i)->vertexData->vertexDeclaration->findElementBySemantic(VES_NORMAL)==0)
124  {
125  havenormal=false;
126  }
127  }
128  if (!havenormal)
129  {
130  LOG("FLEXBODY Error: at least one part of this mesh does not have normal vectors, export your mesh with normal vectors! Disabling flexbody");
131  // NOTE: Intentionally not disabling, for compatibility with v0.4.0.7
132  }
133  }
134  else
135  {
138  }
139 
140  //create optimal VertexDeclaration
141  VertexDeclaration* optimalVD=HardwareBufferManager::getSingleton().createVertexDeclaration();
142  optimalVD->addElement(0, 0, VET_FLOAT3, VES_POSITION);
143  optimalVD->addElement(1, 0, VET_FLOAT3, VES_NORMAL);
144  if (m_has_texture_blend) optimalVD->addElement(2, 0, VET_COLOUR_ARGB, VES_DIFFUSE);
145  if (m_has_texture) optimalVD->addElement(3, 0, VET_FLOAT2, VES_TEXTURE_COORDINATES);
146  optimalVD->sort();
147  optimalVD->closeGapsInSource();
148  BufferUsageList optimalBufferUsages;
149  for (size_t u = 0; u <= optimalVD->getMaxSource(); ++u)
150  {
151  optimalBufferUsages.push_back(HardwareBuffer::HBU_DYNAMIC_WRITE_ONLY_DISCARDABLE);
152  }
153 
154  //adding color buffers, well get the reference later
156  {
157  if (mesh->sharedVertexData)
158  {
159  if (mesh->sharedVertexData->vertexDeclaration->findElementBySemantic(VES_DIFFUSE)==0)
160  {
161  //add buffer
162  int index=mesh->sharedVertexData->vertexDeclaration->getMaxSource()+1;
163  mesh->sharedVertexData->vertexDeclaration->addElement(index, 0, VET_COLOUR_ARGB, VES_DIFFUSE);
164  mesh->sharedVertexData->vertexDeclaration->sort();
165  index=mesh->sharedVertexData->vertexDeclaration->findElementBySemantic(VES_DIFFUSE)->getSource();
166  HardwareVertexBufferSharedPtr vbuf=HardwareBufferManager::getSingleton().createVertexBuffer(VertexElement::getTypeSize(VET_COLOUR_ARGB), mesh->sharedVertexData->vertexCount, HardwareBuffer::HBU_DYNAMIC_WRITE_ONLY_DISCARDABLE);
167  mesh->sharedVertexData->vertexBufferBinding->setBinding(index, vbuf);
168  }
169  }
170  for (int i=0; i<num_submeshes; i++)
171  {
172  if (!mesh->getSubMesh(i)->useSharedVertices)
173  {
174  Ogre::VertexData* vertex_data = mesh->getSubMesh(i)->vertexData;
175  Ogre::VertexDeclaration* vertex_decl = vertex_data->vertexDeclaration;
176  if (vertex_decl->findElementBySemantic(VES_DIFFUSE)==0)
177  {
178  //add buffer
179  int index = vertex_decl->getMaxSource()+1;
180  vertex_decl->addElement(index, 0, VET_COLOUR_ARGB, VES_DIFFUSE);
181  vertex_decl->sort();
182  vertex_decl->findElementBySemantic(VES_DIFFUSE)->getSource();
183  HardwareVertexBufferSharedPtr vbuf = HardwareBufferManager::getSingleton().createVertexBuffer(
184  VertexElement::getTypeSize(VET_COLOUR_ARGB), vertex_data->vertexCount, HardwareBuffer::HBU_DYNAMIC_WRITE_ONLY_DISCARDABLE);
185  vertex_data->vertexBufferBinding->setBinding(index, vbuf);
186  }
187  }
188  }
189  }
190 
191  //reorg
192  //LOG("FLEXBODY reorganizing buffers");
193  if (mesh->sharedVertexData)
194  {
195  mesh->sharedVertexData->reorganiseBuffers(optimalVD, optimalBufferUsages);
196  mesh->sharedVertexData->removeUnusedBuffers();
197  mesh->sharedVertexData->closeGapsInBindings();
198  }
199  Mesh::SubMeshIterator smIt = mesh->getSubMeshIterator();
200  while (smIt.hasMoreElements())
201  {
202  SubMesh* sm = smIt.getNext();
203  if (!sm->useSharedVertices)
204  {
205  sm->vertexData->reorganiseBuffers(optimalVD->clone(), optimalBufferUsages);
206  sm->vertexData->removeUnusedBuffers();
207  sm->vertexData->closeGapsInBindings();
208  }
209  }
210 
211  //print mesh information
212  //LOG("FLEXBODY Printing modififed mesh informations:");
213  //printMeshInfo(ent->getMesh().getPointer());
214 
215  //get the buffers
216  //getMeshInformation(ent->getMesh().getPointer(),m_vertex_count,vertices,index_count,indices, position, orientation, Vector3(1,1,1));
217 
218  //getting vertex counts
219  if (preloaded_from_cache == nullptr)
220  {
221  m_vertex_count=0;
224  if (mesh->sharedVertexData)
225  {
226  m_vertex_count+=mesh->sharedVertexData->vertexCount;
228  }
229  for (int i=0; i<num_submeshes; i++)
230  {
231  if (!mesh->getSubMesh(i)->useSharedVertices)
232  {
233  m_vertex_count+=mesh->getSubMesh(i)->vertexData->vertexCount;
235  }
236  }
237  } else
238  {
239  m_vertex_count = preloaded_from_cache->header.vertex_count;
241  m_num_submesh_vbufs = preloaded_from_cache->header.num_submesh_vbufs;
242  }
243 
244  // Profiler data
245  double stat_manual_buffers_created_time = -1;
246  double stat_transformed_time = -1;
247  double stat_located_time = -1;
248  if (preloaded_from_cache != nullptr)
249  {
250  m_dst_pos = preloaded_from_cache->dst_pos;
251  m_src_normals = preloaded_from_cache->src_normals;
252  m_locators = preloaded_from_cache->locators;
253  m_dst_normals = (Vector3*)malloc(sizeof(Vector3)*m_vertex_count); // Use malloc() for compatibility
254 
256  {
257  m_src_colors = preloaded_from_cache->src_colors;
258  }
259 
260  if (mesh->sharedVertexData)
261  {
262  m_shared_buf_num_verts=(int)mesh->sharedVertexData->vertexCount;
263 
264  //vertices
265  int source=mesh->sharedVertexData->vertexDeclaration->findElementBySemantic(VES_POSITION)->getSource();
266  m_shared_vbuf_pos=mesh->sharedVertexData->vertexBufferBinding->getBuffer(source);
267  //normals
268  source=mesh->sharedVertexData->vertexDeclaration->findElementBySemantic(VES_NORMAL)->getSource();
269  m_shared_vbuf_norm=mesh->sharedVertexData->vertexBufferBinding->getBuffer(source);
270  //colors
272  {
273  source=mesh->sharedVertexData->vertexDeclaration->findElementBySemantic(VES_DIFFUSE)->getSource();
274  m_shared_vbuf_color=mesh->sharedVertexData->vertexBufferBinding->getBuffer(source);
275  }
276  }
277  unsigned int curr_submesh_idx = 0;
278  for (int i=0; i<num_submeshes; i++)
279  {
280  const Ogre::SubMesh* submesh = mesh->getSubMesh(i);
281  if (submesh->useSharedVertices)
282  {
283  continue;
284  }
285  const Ogre::VertexData* vertex_data = submesh->vertexData;
286  m_submesh_vbufs_vertex_counts[curr_submesh_idx] = (int)vertex_data->vertexCount;
287 
288  int source_pos = vertex_data->vertexDeclaration->findElementBySemantic(VES_POSITION)->getSource();
289  int source_norm = vertex_data->vertexDeclaration->findElementBySemantic(VES_NORMAL)->getSource();
290  m_submesh_vbufs_pos [curr_submesh_idx] = vertex_data->vertexBufferBinding->getBuffer(source_pos);
291  m_submesh_vbufs_norm[curr_submesh_idx] = vertex_data->vertexBufferBinding->getBuffer(source_norm);
292 
294  {
295  int source_color = vertex_data->vertexDeclaration->findElementBySemantic(VES_DIFFUSE)->getSource();
296  m_submesh_vbufs_color[curr_submesh_idx] = vertex_data->vertexBufferBinding->getBuffer(source_color);
297  }
298  curr_submesh_idx++;
299  }
300  }
301  else
302  {
303  vertices=(Vector3*)malloc(sizeof(Vector3)*m_vertex_count);
304  m_dst_pos=(Vector3*)malloc(sizeof(Vector3)*m_vertex_count);
305  m_src_normals=(Vector3*)malloc(sizeof(Vector3)*m_vertex_count);
306  m_dst_normals=(Vector3*)malloc(sizeof(Vector3)*m_vertex_count);
308  {
309  m_src_colors=(ARGB*)malloc(sizeof(ARGB)*m_vertex_count);
310  for (int i=0; i<(int)m_vertex_count; i++) m_src_colors[i]=0x00000000;
311  }
312  Vector3* vpt=vertices;
313  Vector3* npt=m_src_normals;
314  if (mesh->sharedVertexData)
315  {
316  m_shared_buf_num_verts=(int)mesh->sharedVertexData->vertexCount;
317  //vertices
318  int source=mesh->sharedVertexData->vertexDeclaration->findElementBySemantic(VES_POSITION)->getSource();
319  m_shared_vbuf_pos=mesh->sharedVertexData->vertexBufferBinding->getBuffer(source);
320  m_shared_vbuf_pos->readData(0, mesh->sharedVertexData->vertexCount*sizeof(Vector3), (void*)vpt);
321  vpt+=mesh->sharedVertexData->vertexCount;
322  //normals
323  source=mesh->sharedVertexData->vertexDeclaration->findElementBySemantic(VES_NORMAL)->getSource();
324  m_shared_vbuf_norm=mesh->sharedVertexData->vertexBufferBinding->getBuffer(source);
325  m_shared_vbuf_norm->readData(0, mesh->sharedVertexData->vertexCount*sizeof(Vector3), (void*)npt);
326  npt+=mesh->sharedVertexData->vertexCount;
327  //colors
329  {
330  source=mesh->sharedVertexData->vertexDeclaration->findElementBySemantic(VES_DIFFUSE)->getSource();
331  m_shared_vbuf_color=mesh->sharedVertexData->vertexBufferBinding->getBuffer(source);
332  m_shared_vbuf_color->writeData(0, mesh->sharedVertexData->vertexCount*sizeof(ARGB), (void*)m_src_colors);
333  }
334  }
335  int cursubmesh=0;
336  for (int i=0; i<num_submeshes; i++)
337  {
338  const Ogre::SubMesh* submesh = mesh->getSubMesh(i);
339  if (submesh->useSharedVertices)
340  {
341  continue;
342  }
343  const Ogre::VertexData* vertex_data = submesh->vertexData;
344  int vertex_count = (int)vertex_data->vertexCount;
345  m_submesh_vbufs_vertex_counts[cursubmesh] = vertex_count;
346  //vertices
347  int source = vertex_data->vertexDeclaration->findElementBySemantic(VES_POSITION)->getSource();
348  m_submesh_vbufs_pos[cursubmesh]=vertex_data->vertexBufferBinding->getBuffer(source);
349  m_submesh_vbufs_pos[cursubmesh]->readData(0, vertex_count*sizeof(Vector3), (void*)vpt);
350  vpt += vertex_count;
351  //normals
352  source = vertex_data->vertexDeclaration->findElementBySemantic(VES_NORMAL)->getSource();
353  m_submesh_vbufs_norm[cursubmesh]=vertex_data->vertexBufferBinding->getBuffer(source);
354  m_submesh_vbufs_norm[cursubmesh]->readData(0, vertex_count*sizeof(Vector3), (void*)npt);
355  npt += vertex_count;
356  //colors
358  {
359  source = vertex_data->vertexDeclaration->findElementBySemantic(VES_DIFFUSE)->getSource();
360  m_submesh_vbufs_color[cursubmesh] = vertex_data->vertexBufferBinding->getBuffer(source);
361  m_submesh_vbufs_color[cursubmesh]->writeData(0, vertex_count*sizeof(ARGB), (void*)m_src_colors);
362  }
363  cursubmesh++;
364  }
365 
366  //transform
367  for (int i=0; i<(int)m_vertex_count; i++)
368  {
369  vertices[i]=(orientation*vertices[i])+position;
370  }
371 
373  for (int i=0; i<(int)m_vertex_count; i++)
374  {
375  //search nearest node as the local origin
376  float closest_node_distance = std::numeric_limits<float>::max();
377  int closest_node_index = -1;
378  for (auto node_index : node_indices)
379  {
380  float node_distance = vertices[i].squaredDistance(nodes[node_index].AbsPosition);
381  if (node_distance < closest_node_distance)
382  {
383  closest_node_distance = node_distance;
384  closest_node_index = node_index;
385  }
386  }
387  if (closest_node_index == -1)
388  {
389  LOG("FLEXBODY ERROR on mesh "+mesh_name+": REF node not found");
390  closest_node_index = 0;
391  }
392  m_locators[i].ref=closest_node_index;
393 
394  //search the second nearest node as the X vector
395  closest_node_distance = std::numeric_limits<float>::max();
396  closest_node_index = -1;
397  for (auto node_index : node_indices)
398  {
399  if (node_index == m_locators[i].ref)
400  {
401  continue;
402  }
403  float node_distance = vertices[i].squaredDistance(nodes[node_index].AbsPosition);
404  if (node_distance < closest_node_distance)
405  {
406  closest_node_distance = node_distance;
407  closest_node_index = node_index;
408  }
409  }
410  if (closest_node_index == -1)
411  {
412  LOG("FLEXBODY ERROR on mesh "+mesh_name+": VX node not found");
413  closest_node_index = 0;
414  }
415  m_locators[i].nx=closest_node_index;
416 
417  //search another close, orthogonal node as the Y vector
418  closest_node_distance = std::numeric_limits<float>::max();
419  closest_node_index = -1;
420  Vector3 vx = (nodes[m_locators[i].nx].AbsPosition - nodes[m_locators[i].ref].AbsPosition).normalisedCopy();
421  for (auto node_index : node_indices)
422  {
423  if (node_index == m_locators[i].ref || node_index == m_locators[i].nx)
424  {
425  continue;
426  }
427  float node_distance = vertices[i].squaredDistance(nodes[node_index].AbsPosition);
428  if (node_distance < closest_node_distance)
429  {
430  Vector3 vt = (nodes[node_index].AbsPosition - nodes[m_locators[i].ref].AbsPosition).normalisedCopy();
431  float cost = vx.dotProduct(vt);
432  if (std::abs(cost) > std::sqrt(2.0f) / 2.0f)
433  {
434  continue; //rejection, fails the orthogonality criterion (+-45 degree)
435  }
436  closest_node_distance = node_distance;
437  closest_node_index = node_index;
438  }
439  }
440  if (closest_node_index == -1)
441  {
442  LOG("FLEXBODY ERROR on mesh "+mesh_name+": VY node not found");
443  closest_node_index = 0;
444  }
445  m_locators[i].ny=closest_node_index;
446 
447  Matrix3 mat;
448  Vector3 diffX = nodes[m_locators[i].nx].AbsPosition-nodes[m_locators[i].ref].AbsPosition;
449  Vector3 diffY = nodes[m_locators[i].ny].AbsPosition-nodes[m_locators[i].ref].AbsPosition;
450 
451  mat.SetColumn(0, diffX);
452  mat.SetColumn(1, diffY);
453  mat.SetColumn(2, (diffX.crossProduct(diffY)).normalisedCopy()); // Old version: mat.SetColumn(2, nodes[loc.nz].AbsPosition-nodes[loc.ref].AbsPosition);
454 
455  mat = mat.Inverse();
456 
457  //compute coordinates in the newly formed Euclidean basis
458  m_locators[i].coords = mat * (vertices[i] - nodes[m_locators[i].ref].AbsPosition);
459 
460  // that's it!
461  }
462 
463  } // if (preloaded_from_cache == nullptr)
464 
465  //adjusting bounds
466  AxisAlignedBox aab=mesh->getBounds();
467  Vector3 v=aab.getMinimum();
468  float mi=v.x;
469  if (v.y<mi) mi=v.y;
470  if (v.z<mi) mi=v.z;
471  mi=fabs(mi);
472  v=aab.getMaximum();
473  float ma=v.x;
474  if (ma<v.y) ma=v.y;
475  if (ma<v.z) ma=v.z;
476  ma=fabs(ma);
477  if (mi>ma) ma=mi;
478  aab.setMinimum(Vector3(-ma,-ma,-ma));
479  aab.setMaximum(Vector3(ma,ma,ma));
480  mesh->_setBounds(aab, true);
481 
482  //okay, show the mesh now
483  m_scene_node=App::GetGfxScene()->GetSceneManager()->getRootSceneNode()->createChildSceneNode();
484  m_scene_node->attachObject(ent);
485  m_scene_node->setPosition(position);
486 
487  if (preloaded_from_cache == nullptr)
488  {
489  for (int i=0; i<(int)m_vertex_count; i++)
490  {
491  Matrix3 mat;
492  Vector3 diffX = nodes[m_locators[i].nx].AbsPosition-nodes[m_locators[i].ref].AbsPosition;
493  Vector3 diffY = nodes[m_locators[i].ny].AbsPosition-nodes[m_locators[i].ref].AbsPosition;
494 
495  mat.SetColumn(0, diffX);
496  mat.SetColumn(1, diffY);
497  mat.SetColumn(2, diffX.crossProduct(diffY).normalisedCopy()); // Old version: mat.SetColumn(2, nodes[loc.nz].AbsPosition-nodes[loc.ref].AbsPosition);
498 
499  mat = mat.Inverse();
500 
501  // compute coordinates in the Euclidean basis
502  m_src_normals[i] = mat*(orientation * m_src_normals[i]);
503  }
504  }
505 
506  if (vertices != nullptr) { free(vertices); }
507 
508  // Keep the forset nodes for diagnostics
509  for (unsigned int nodenum : node_indices)
510  {
511  m_forset_nodes.push_back((NodeNum_t)nodenum);
512  }
513 
514  if (App::GetConsole()->cVarGet("flexbody_defrag_enabled", CVAR_TYPE_BOOL)->getBool()
515  // For simplicity, only take 1-submesh meshes (almost always the case anyway)
516  && m_scene_entity->getMesh()->getNumSubMeshes() == 1)
517  {
518  this->defragmentFlexbodyMesh();
519  }
520 }
521 
522 FlexBody::FlexBody(PlaceholderType p_type, FlexbodyID_t id, const std::string& orig_meshname)
523 {
525  m_id = id;
526  m_orig_mesh_name = orig_meshname;
527  m_placeholder_type = p_type;
528 }
529 
531 {
532  // Stuff using <new>
533  if (m_locators != nullptr) { delete[] m_locators; }
534  // Stuff using malloc()
535  if (m_src_normals != nullptr) { free(m_src_normals); }
536  if (m_dst_normals != nullptr) { free(m_dst_normals); }
537  if (m_dst_pos != nullptr) { free(m_dst_pos ); }
538  if (m_src_colors != nullptr) { free(m_src_colors ); }
539 
540  this->destroyOgreObjects();
541 }
542 
544 {
545  switch (type)
546  {
547  case PlaceholderType::NOT_A_PLACEHOLDER: return "NOT_A_PLACEHOLDER";
548  case PlaceholderType::TUNING_REMOVED_PLACEHOLDER: return "TUNING_REMOVED_PLACEHOLDER";
549  case PlaceholderType::FAULTY_FORSET_PLACEHOLDER: return "FAULTY_FORSET_PLACEHOLDER";
550  case PlaceholderType::FAULTY_MESH_PLACEHOLDER: return "FAULTY_MESH_PLACEHOLDER";
551  default: return "";
552  }
553 }
554 
556 {
557  // Separated out from destructor so that exceptions can be handled separately (C++ destructor cannot propagate exceptions)
558  // -----------------------------------------------------------------------------------------------------------------------
559 
560  // OGRE resource - scene node
561  if (m_scene_node != nullptr)
562  {
563  m_scene_node->getParentSceneNode()->removeChild(m_scene_node);
564  App::GetGfxScene()->GetSceneManager()->destroySceneNode(m_scene_node);
565  }
566  m_scene_node = nullptr;
567 
568  // OGRE resource - scene entity
569  if (m_scene_entity != nullptr)
570  {
571  Ogre::MeshPtr mesh = m_scene_entity->getMesh();
572  App::GetGfxScene()->GetSceneManager()->destroyEntity(m_scene_entity);
573 
574  // OGRE resource - mesh (unique copy - should be destroyed)
575  Ogre::MeshManager::getSingleton().remove(mesh->getHandle());
576  }
577  m_scene_entity = nullptr;
578 }
579 
581 {
582  // Scene node is NULL if disabled via addonpart/tuneup.
583  return m_scene_node
584  && m_scene_node->isInSceneGraph()
585  && m_scene_node->getAttachedObject(0)->isVisible();
586 
587 }
588 
589 void FlexBody::setVisible(bool visible)
590 {
591  // Scene node is NULL if disabled via addonpart/tuneup.
592  if (m_scene_node)
593  m_scene_node->setVisible(visible);
594 }
595 
597 {
598  // Scene entity is NULL if disabled via addonpart/tuneup.
599  if (m_scene_entity)
600  m_scene_entity->setCastShadows(val);
601 }
602 
604 {
606 
608 
609  // compute the local center
610  if (m_node_center >= 0)
611  {
612  Vector3 diffX = nodes[m_node_x].AbsPosition - nodes[m_node_center].AbsPosition;
613  Vector3 diffY = nodes[m_node_y].AbsPosition - nodes[m_node_center].AbsPosition;
614  Ogre::Vector3 flexit_normal = fast_normalise(diffY.crossProduct(diffX));
615 
616  m_flexit_center = nodes[m_node_center].AbsPosition + m_center_offset.x * diffX + m_center_offset.y * diffY;
617  m_flexit_center += m_center_offset.z * flexit_normal;
618  }
619  else
620  {
621  m_flexit_center = nodes[0].AbsPosition;
622  }
623 
624  for (int i=0; i<(int)m_vertex_count; i++)
625  {
626  Vector3 diffX = nodes[m_locators[i].nx].AbsPosition - nodes[m_locators[i].ref].AbsPosition;
627  Vector3 diffY = nodes[m_locators[i].ny].AbsPosition - nodes[m_locators[i].ref].AbsPosition;
628  Vector3 nCross = fast_normalise(diffX.crossProduct(diffY)); //nCross.normalise();
629 
630  m_dst_pos[i].x = diffX.x * m_locators[i].coords.x + diffY.x * m_locators[i].coords.y + nCross.x * m_locators[i].coords.z;
631  m_dst_pos[i].y = diffX.y * m_locators[i].coords.x + diffY.y * m_locators[i].coords.y + nCross.y * m_locators[i].coords.z;
632  m_dst_pos[i].z = diffX.z * m_locators[i].coords.x + diffY.z * m_locators[i].coords.y + nCross.z * m_locators[i].coords.z;
633 
634  m_dst_pos[i] += nodes[m_locators[i].ref].AbsPosition - m_flexit_center;
635 
636  m_dst_normals[i].x = diffX.x * m_src_normals[i].x + diffY.x * m_src_normals[i].y + nCross.x * m_src_normals[i].z;
637  m_dst_normals[i].y = diffX.y * m_src_normals[i].x + diffY.y * m_src_normals[i].y + nCross.y * m_src_normals[i].z;
638  m_dst_normals[i].z = diffX.z * m_src_normals[i].x + diffY.z * m_src_normals[i].y + nCross.z * m_src_normals[i].z;
639 
641  }
642 }
643 
645 {
646  if (!m_scene_node) // Disabled via addonpart/tuneup
647  return;
648 
649  Vector3 *ppt = m_dst_pos;
650  Vector3 *npt = m_dst_normals;
652  {
653  m_shared_vbuf_pos->writeData(0, m_shared_buf_num_verts*sizeof(Vector3), ppt, true);
654  ppt += m_shared_buf_num_verts;
655  m_shared_vbuf_norm->writeData(0, m_shared_buf_num_verts*sizeof(Vector3), npt, true);
656  npt += m_shared_buf_num_verts;
657  }
658  for (int i=0; i<m_num_submesh_vbufs; i++)
659  {
660  m_submesh_vbufs_pos[i]->writeData(0, m_submesh_vbufs_vertex_counts[i]*sizeof(Vector3), ppt, true);
662  m_submesh_vbufs_norm[i]->writeData(0, m_submesh_vbufs_vertex_counts[i]*sizeof(Vector3), npt, true);
664  }
665 
666  if (m_blend_changed)
667  {
668  writeBlend();
669  m_blend_changed = false;
670  }
671 
672  m_scene_node->setPosition(m_flexit_center);
673 }
674 
676 {
678  {
679  for (int i=0; i<(int)m_vertex_count; i++) m_src_colors[i]=0x00000000;
680  writeBlend();
681  }
682 }
683 
685 {
686  if (!m_has_texture_blend) return;
687  ARGB *cpt = m_src_colors;
689  {
690  m_shared_vbuf_color->writeData(0, m_shared_buf_num_verts*sizeof(ARGB), (void*)cpt, true);
692  }
693  for (int i=0; i<m_num_submesh_vbufs; i++)
694  {
695  m_submesh_vbufs_color[i]->writeData(0, m_submesh_vbufs_vertex_counts[i]*sizeof(ARGB), (void*)cpt, true);
697  }
698 }
699 
700 void FlexBody::updateBlend() //so easy!
701 {
703  for (int i=0; i<(int)m_vertex_count; i++)
704  {
705  RoR::NodeSB *nd = &nodes[m_locators[i].ref];
706  ARGB col = m_src_colors[i];
707  if (nd->nd_has_contact && !(col&0xFF000000))
708  {
709  m_src_colors[i]=col|0xFF000000;
710  m_blend_changed = true;
711  }
712  if (nd->nd_is_wet ^ ((col&0x000000FF)>0))
713  {
714  m_src_colors[i]=(col&0xFFFFFF00)+0x000000FF*nd->nd_is_wet;
715  m_blend_changed = true;
716  }
717  }
718 }
719 
721 {
722  if (a > b)
723  {
726  }
727  else if (a < b)
728  {
731  }
732  else
733  {
734  return 0;
735  }
736 }
737 
739 {
740  return 0
745  + evalNodeDistance(a.getMean(), b.getMean());
746 }
747 
748 template<typename uint_T> void reorderIndexBuffer(Ogre::IndexData* idx_data, std::vector<int> const& new_index_lookup)
749 {
750  uint_T* workibuf = new uint_T[idx_data->indexCount];
751  idx_data->indexBuffer->readData(0, idx_data->indexBuffer->getSizeInBytes(), workibuf);
752  for (size_t i = 0; i < idx_data->indexCount; i++)
753  {
754  workibuf[i] = new_index_lookup[workibuf[i]];
755  }
756  idx_data->indexBuffer->writeData(0, idx_data->indexBuffer->getSizeInBytes(), workibuf);
757  delete[] workibuf;
758 }
759 
760 void reorderVertexBuffer(Ogre::HardwareVertexBufferSharedPtr vert_buf, const Ogre::VertexElement* vert_elem, std::vector<int> const& new_index_lookup)
761 {
762  char* workbuf_src = new char[vert_buf->getSizeInBytes()];
763  char* workbuf_dst = new char[vert_buf->getSizeInBytes()];
764  vert_buf->readData(0, vert_buf->getSizeInBytes(), workbuf_src);
765  for (size_t i = 0; i < vert_buf->getNumVertices(); i++)
766  {
767  void* src = workbuf_src + (i * vert_elem->getSize());
768  void* dst = workbuf_dst + (new_index_lookup[i] * vert_elem->getSize());
769  std::memcpy(dst, src, vert_elem->getSize());
770  }
771  vert_buf->writeData(0, vert_buf->getSizeInBytes(), workbuf_dst);
772  delete[] workbuf_src;
773  delete[] workbuf_dst;
774 }
775 
777 {
778  // Analysis
779  NodeNum_t forset_max = std::numeric_limits<NodeNum_t>::min();
780  NodeNum_t forset_min = std::numeric_limits<NodeNum_t>::max();
781  for (NodeNum_t n : this->getForsetNodes())
782  {
783  if (n > forset_max) { forset_max = n; }
784  if (n < forset_min) { forset_min = n; }
785  }
786 
787  std::vector<int> new_index_lookup(m_vertex_count);
788  for (int i = 0; i < (int)m_vertex_count; i++)
789  {
790  new_index_lookup[i] = i;
791  }
792 
793  Locator_t prev_loc;
794  // edge values to start with
795  prev_loc.ref = forset_min;
796  prev_loc.nx = forset_min;
797  prev_loc.ny = forset_min;
798 
799  // SELECTION SORT (https://www.geeksforgeeks.org/selection-sort/)
800  for (int i = 0; i < m_vertex_count; i++)
801  {
802  // Find the next locator closest in memory
803  int closest_loc = i;
804  int closest_loc_penalty = INT_MAX;
805  for (int j = i; j < m_vertex_count; j++)
806  {
807  int penalty = evalMemoryDistance(prev_loc, m_locators[j]);
808  if (penalty < closest_loc_penalty)
809  {
810  closest_loc_penalty = penalty;
811  closest_loc = j;
812  }
813  }
814 
815  // Swap locators+normals in memory, update lookup
816  Locator_t loc_tmp = m_locators[closest_loc];
817  Ogre::Vector3 norm_tmp = m_src_normals[closest_loc];
818  int idx_tmp = new_index_lookup[closest_loc];
819 
820  m_locators[closest_loc] = m_locators[i];
821  m_src_normals[closest_loc] = m_src_normals[i];
822  new_index_lookup[closest_loc] = new_index_lookup[i];
823 
824  m_locators[i] = loc_tmp;
825  m_src_normals[i] = norm_tmp;
826  new_index_lookup[i] = idx_tmp;
827 
828  // Go next
829  prev_loc = m_locators[i];
830  }
831 
832  if (App::flexbody_defrag_invert_lookup->getBool())
833  {
834  std::vector<int> inverted_lookup(m_vertex_count);
835  for (int i = 0; i < (int)m_vertex_count; i++)
836  {
837  inverted_lookup[new_index_lookup[i]] = i;
838  }
839  for (int i = 0; i < (int)m_vertex_count; i++)
840  {
841  new_index_lookup[i] = inverted_lookup[i];
842  }
843  }
844 
845  // REORDERING VERTICES
846  // * positions/normals are calculated, no action needed.
847  // * texcoords (aka UV-coords) must be fixed.
849  {
850  Ogre::VertexData* vert_data = nullptr;
851  if (m_scene_entity->getMesh()->sharedVertexData)
852  {
853  vert_data = m_scene_entity->getMesh()->sharedVertexData;
854  }
855  else
856  {
857  // for simplicity we only support single submesh
858  vert_data = m_scene_entity->getMesh()->getSubMesh(0)->vertexData;
859  }
860  const Ogre::VertexElement* uv_elem = vert_data->vertexDeclaration->findElementBySemantic(Ogre::VES_TEXTURE_COORDINATES);
861  Ogre::HardwareVertexBufferSharedPtr uv_buf = vert_data->vertexBufferBinding->getBuffer(uv_elem->getSource());
862  reorderVertexBuffer(uv_buf, uv_elem, new_index_lookup);
863  }
864 
865  // REORDERING INDICES
867  {
868  Ogre::IndexData* idx_data = m_scene_entity->getMesh()->getSubMesh(0)->indexData;
869  // Index can be 16-bit or 32-bit!
870  if (idx_data->indexBuffer->getType() == Ogre::HardwareIndexBuffer::IT_16BIT)
871  {
872  reorderIndexBuffer<uint16_t>(idx_data, new_index_lookup);
873  }
874  else
875  {
876  reorderIndexBuffer<uint32_t>(idx_data, new_index_lookup);
877  }
878  }
879 }
ROR_ASSERT
#define ROR_ASSERT(_EXPR)
Definition: Application.h:40
RoR::FlexBodyCacheData
Definition: FlexFactory.h:66
RoR::Locator_t::nx
NodeNum_t nx
Definition: Locator_t.h:13
RoR::FlexBody::reset
void reset()
Definition: FlexBody.cpp:675
RoR::FlexBody::destroyOgreObjects
void destroyOgreObjects()
Definition: FlexBody.cpp:555
RoR::FlexBodyCacheData::header
FlexBodyRecordHeader header
Definition: FlexFactory.h:77
RoR::Locator_t::ny
NodeNum_t ny
Definition: Locator_t.h:14
RoR::FlexBody::m_scene_node
Ogre::SceneNode * m_scene_node
Definition: FlexBody.h:129
evalMemoryDistance
int evalMemoryDistance(Locator_t &a, Locator_t &b)
Definition: FlexBody.cpp:738
RoR::FlexBody::m_placeholder_type
PlaceholderType m_placeholder_type
Definition: FlexBody.h:117
RoR::FlexBody::PlaceholderType::NOT_A_PLACEHOLDER
@ NOT_A_PLACEHOLDER
RoR::NodeSB
Definition: SimBuffers.h:67
RoR::Locator_t::ref
NodeNum_t ref
Definition: Locator_t.h:12
RoR::Locator_t
Definition: Locator_t.h:10
RoR::FlexBody::m_forset_nodes
std::vector< NodeNum_t > m_forset_nodes
Definition: FlexBody.h:150
RoR::FlexBody::m_submesh_vbufs_vertex_counts
int m_submesh_vbufs_vertex_counts[16]
Definition: FlexBody.h:139
RoR::FlexBodyRecordHeader::HAS_TEXTURE
static const BitMask_t HAS_TEXTURE
Definition: FlexFactory.h:62
RoR::FlexBody::m_uses_shared_vertex_data
bool m_uses_shared_vertex_data
Definition: FlexBody.h:144
RoR::GfxActor::GetSimNodeBuffer
NodeSB * GetSimNodeBuffer()
Definition: GfxActor.h:120
RoR::FlexBody::updateFlexbodyVertexBuffers
void updateFlexbodyVertexBuffers()
Definition: FlexBody.cpp:644
RoR::FlexBodyRecordHeader::USES_SHARED_VERTEX_DATA
static const BitMask_t USES_SHARED_VERTEX_DATA
Definition: FlexFactory.h:61
RoR::FlexBody::m_orig_mesh_name
std::string m_orig_mesh_name
Definition: FlexBody.h:152
RoR::NODENUM_INVALID
static const NodeNum_t NODENUM_INVALID
Definition: ForwardDeclarations.h:53
RoR::FlexBody::defragmentFlexbodyMesh
void defragmentFlexbodyMesh()
Definition: FlexBody.cpp:776
RoR::FlexBody::m_shared_vbuf_pos
Ogre::HardwareVertexBufferSharedPtr m_shared_vbuf_pos
Definition: FlexBody.h:134
RoR::PrintMeshInfo
std::string PrintMeshInfo(std::string const &title, Ogre::MeshPtr mesh)
RoR::FlexBody::getForsetNodes
std::vector< NodeNum_t > & getForsetNodes()
Definition: FlexBody.h:97
RoR::FlexBodyRecordHeader::HAS_TEXTURE_BLEND
static const BitMask_t HAS_TEXTURE_BLEND
Definition: FlexFactory.h:63
RoR::NodeSB::nd_is_wet
bool nd_is_wet
Definition: SimBuffers.h:71
Console.h
RoR::FlexBody::setVisible
void setVisible(bool visible)
Definition: FlexBody.cpp:589
RoR::CAMERA_MODE_ALWAYS_HIDDEN
static CameraMode_t CAMERA_MODE_ALWAYS_HIDDEN
Definition: GfxData.h:124
RoR::FlexBody::FlexBody
FlexBody(RoR::FlexBodyCacheData *preloaded_from_cache, RoR::GfxActor *gfx_actor, Ogre::Entity *entity, NodeNum_t ref, NodeNum_t nx, NodeNum_t ny, Ogre::Vector3 offset, Ogre::Quaternion const &rot, std::vector< unsigned int > &node_indices)
Definition: FlexBody.cpp:38
RoR::FlexBody::m_blend_changed
bool m_blend_changed
Definition: FlexBody.h:147
RoR::FlexBody::m_dst_pos
Ogre::Vector3 * m_dst_pos
Definition: FlexBody.h:119
RoR::FlexBody::m_dst_normals
Ogre::Vector3 * m_dst_normals
Definition: FlexBody.h:121
RoR::FlexBody::updateBlend
void updateBlend()
Definition: FlexBody.cpp:700
RoR::GfxScene::GetSceneManager
Ogre::SceneManager * GetSceneManager()
Definition: GfxScene.h:64
RoR::FlexBody::PlaceholderType::FAULTY_MESH_PLACEHOLDER
@ FAULTY_MESH_PLACEHOLDER
RoR::NodeSB::nd_has_contact
bool nd_has_contact
Definition: SimBuffers.h:70
RoR::FlexBody::m_has_texture
bool m_has_texture
Definition: FlexBody.h:145
RoR::FlexBody::m_camera_mode
int m_camera_mode
Visibility control {-2 = always, -1 = 3rdPerson only, 0+ = cinecam index}.
Definition: FlexBody.h:131
RoR::NodeNum_t
uint16_t NodeNum_t
Node position within Actor::ar_nodes; use RoR::NODENUM_INVALID as empty value.
Definition: ForwardDeclarations.h:52
SimData.h
Core data structures for simulation; Everything affected by by either physics, network or user intera...
RoR::FlexBody::m_submesh_vbufs_color
Ogre::HardwareVertexBufferSharedPtr m_submesh_vbufs_color[16]
colors
Definition: FlexBody.h:142
RoR::FlexBody::PlaceholderType::FAULTY_FORSET_PLACEHOLDER
@ FAULTY_FORSET_PLACEHOLDER
RoR::FlexBody::~FlexBody
~FlexBody()
Definition: FlexBody.cpp:530
BITMASK_IS_1
#define BITMASK_IS_1(VAR, FLAGS)
Definition: BitFlags.h:14
RoR::FlexBody::m_src_colors
Ogre::ARGB * m_src_colors
Definition: FlexBody.h:122
RoR::FlexBody::m_shared_buf_num_verts
int m_shared_buf_num_verts
Definition: FlexBody.h:133
RoR::App::flexbody_defrag_reorder_indices
CVar * flexbody_defrag_reorder_indices
Definition: Application.cpp:257
GfxScene.h
RoR::FlexBody::m_num_submesh_vbufs
int m_num_submesh_vbufs
Definition: FlexBody.h:138
RoR::FlexBody::isVisible
bool isVisible() const
Definition: FlexBody.cpp:580
RoR::App::flexbody_defrag_reorder_texcoords
CVar * flexbody_defrag_reorder_texcoords
Definition: Application.cpp:258
fast_normalise
Ogre::Vector3 fast_normalise(Ogre::Vector3 v)
Definition: ApproxMath.h:151
Application.h
Central state/object manager and communications hub.
RoR::App::GetConsole
Console * GetConsole()
Definition: Application.cpp:270
RoR::FlexBody::PlaceholderType
PlaceholderType
Definition: FlexBody.h:62
FlexBody.h
RoR::Locator_t::getMean
NodeNum_t getMean()
Definition: Locator_t.h:19
ApproxMath.h
RoR::FlexBodyCacheData::src_normals
Ogre::Vector3 * src_normals
Definition: FlexFactory.h:80
RoR::App::flexbody_defrag_invert_lookup
CVar * flexbody_defrag_invert_lookup
Definition: Application.cpp:259
RoR::FlexBody::m_gfx_actor
RoR::GfxActor * m_gfx_actor
Definition: FlexBody.h:113
RoR::FlexBody::setFlexbodyCastShadow
void setFlexbodyCastShadow(bool val)
Definition: FlexBody.cpp:596
RoR::FlexBody::PlaceholderTypeToString
static const char * PlaceholderTypeToString(PlaceholderType type)
Definition: FlexBody.cpp:543
RoR::Locator_t::coords
Ogre::Vector3 coords
Definition: Locator_t.h:15
RoR::FlexBody::PlaceholderType::TUNING_REMOVED_PLACEHOLDER
@ TUNING_REMOVED_PLACEHOLDER
RoR::FlexBodyCacheData::src_colors
Ogre::ARGB * src_colors
Definition: FlexFactory.h:81
evalNodeDistance
int evalNodeDistance(NodeNum_t a, NodeNum_t b)
Definition: FlexBody.cpp:720
RoR::FlexBody::m_flexit_center
Ogre::Vector3 m_flexit_center
Updated per frame.
Definition: FlexBody.h:115
RoR::CVAR_TYPE_BOOL
@ CVAR_TYPE_BOOL
Definition: CVar.h:38
RoR::FlexBody::m_scene_entity
Ogre::Entity * m_scene_entity
Definition: FlexBody.h:130
nodes
or anywhere else will not be considered a but parsed as regular data ! Each line is treated as values separated by separators Possible i e animators Multiline description Single does not affect it Directive usualy set global attributes or change behavior of the parsing Directive may appear in any block section Modularity The elements can be grouped into modules Each module must belong to one or more configurations Directives sectionconfig specify truck configurations the user can choose from Exactly one must be selected If the first defined is used lettercase matches original docs(parsing is insensitive). NAME TYPE NOTES advdrag BLOCK add_animation DIRECTIVE Special syntax airbrakes BLOCK animators BLOCK Special syntax IF(values[0]=="") bad trailing chars are silently ignored no space at the end Items delimited On each side of there is max item Empty invalid string parses as node num items Acceptable item the node is the others When a node range has more than nodes
Definition: ReadMe.txt:302
RoR::FlexBodyRecordHeader::flags
BitMask_t flags
Definition: FlexFactory.h:58
reorderIndexBuffer
void reorderIndexBuffer(Ogre::IndexData *idx_data, std::vector< int > const &new_index_lookup)
Definition: FlexBody.cpp:748
RoR::FlexBody::m_locators
Locator_t * m_locators
1 loc per vertex
Definition: FlexBody.h:123
RoR::FlexBody::m_center_offset
Ogre::Vector3 m_center_offset
Definition: FlexBody.h:128
RoR::GfxActor
Definition: GfxActor.h:52
RoR::FlexBody::m_node_y
NodeNum_t m_node_y
Definition: FlexBody.h:127
Ogre
Definition: ExtinguishableFireAffector.cpp:35
GfxActor.h
Manager for all visuals belonging to a single actor.
RoR::FlexBody::m_src_normals
Ogre::Vector3 * m_src_normals
Definition: FlexBody.h:120
RoR::FlexBody::m_node_center
NodeNum_t m_node_center
Definition: FlexBody.h:125
RoR::FlexBody::m_has_texture_blend
bool m_has_texture_blend
Definition: FlexBody.h:146
RoR::CVar::getInt
int getInt() const
Definition: CVar.h:97
RoR::Locator_t::getSmallestNode
NodeNum_t getSmallestNode()
Definition: Locator_t.h:17
RoR::App::flexbody_defrag_prog_down_penalty
CVar * flexbody_defrag_prog_down_penalty
Definition: Application.cpp:256
RoR::FlexbodyID_t
int FlexbodyID_t
Index to GfxActor::m_flexbodies, use RoR::FLEXBODYID_INVALID as empty value.
Definition: ForwardDeclarations.h:62
RoR::FlexBodyRecordHeader::num_submesh_vbufs
int num_submesh_vbufs
Definition: FlexFactory.h:57
RoR::FlexBodyRecordHeader::vertex_count
int vertex_count
Definition: FlexFactory.h:50
FlexFactory.h
RoR::FlexBodyCacheData::locators
Locator_t * locators
1 loc per vertex
Definition: FlexFactory.h:82
RoR::FlexBody::m_id
FlexbodyID_t m_id
Definition: FlexBody.h:116
RoR::FlexBody::computeFlexbody
void computeFlexbody()
Updates mesh deformation; works on CPU using local copy of vertex data.
Definition: FlexBody.cpp:603
RoR::FlexBody::m_shared_vbuf_color
Ogre::HardwareVertexBufferSharedPtr m_shared_vbuf_color
Definition: FlexBody.h:136
RoR::FlexBody::m_submesh_vbufs_pos
Ogre::HardwareVertexBufferSharedPtr m_submesh_vbufs_pos[16]
positions
Definition: FlexBody.h:140
RoR::FlexBody::writeBlend
void writeBlend()
Definition: FlexBody.cpp:684
RoR::FlexBody::m_shared_vbuf_norm
Ogre::HardwareVertexBufferSharedPtr m_shared_vbuf_norm
Definition: FlexBody.h:135
RoR::FlexBodyCacheData::dst_pos
Ogre::Vector3 * dst_pos
Definition: FlexFactory.h:79
RigDef_File.h
Data structures representing 'truck' file format, see https://docs.rigsofrods.org/vehicle-creation/fi...
RoR
Definition: AppContext.h:36
RoR::App::flexbody_defrag_const_penalty
CVar * flexbody_defrag_const_penalty
Definition: Application.cpp:254
RoR::App::GetGfxScene
GfxScene * GetGfxScene()
Definition: Application.cpp:276
RoR::FlexBody::m_submesh_vbufs_norm
Ogre::HardwareVertexBufferSharedPtr m_submesh_vbufs_norm[16]
normals
Definition: FlexBody.h:141
RoR::FlexBody::m_orig_mesh_info
std::string m_orig_mesh_info
Definition: FlexBody.h:151
RoR::FlexBody::m_node_x
NodeNum_t m_node_x
Definition: FlexBody.h:126
RoR::FlexBody::m_vertex_count
size_t m_vertex_count
Definition: FlexBody.h:114
reorderVertexBuffer
void reorderVertexBuffer(Ogre::HardwareVertexBufferSharedPtr vert_buf, const Ogre::VertexElement *vert_elem, std::vector< int > const &new_index_lookup)
Definition: FlexBody.cpp:760