TDME2  1.9.200
ObjectRenderGroup.cpp
Go to the documentation of this file.
2 
3 #include <memory>
4 #include <string>
5 #include <vector>
6 
7 #include <tdme/tdme.h>
12 #include <tdme/engine/model/Node.h>
16 #include <tdme/engine/Engine.h>
17 #include <tdme/engine/LODObject.h>
18 #include <tdme/engine/Object.h>
19 #include <tdme/engine/Partition.h>
20 #include <tdme/engine/Transform.h>
21 #include <tdme/math/Math.h>
22 #include <tdme/math/Matrix4x4.h>
23 #include <tdme/math/Vector2.h>
24 #include <tdme/math/Vector3.h>
26 
27 using std::make_unique;
28 using std::string;
29 using std::to_string;
30 using std::unique_ptr;
31 using std::vector;
32 
47 using tdme::math::Math;
52 
53 ObjectRenderGroup::ObjectRenderGroup(
54  const string& id,
55  int lodLevels,
56  float modelLOD2MinDistance,
57  float modelLOD3MinDistance,
58  int modelLOD2ReduceBy,
59  int modelLOD3ReduceBy,
60  bool optimizeModels
61 ):
62  id(id)
63 {
64  this->enabled = true;
65  this->pickable = false;
66  this->contributesShadows = false;
67  this->receivesShadows = false;
68  this->effectColorMul.set(1.0f, 1.0f, 1.0f, 1.0f);
69  this->effectColorAdd.set(0.0f, 0.0f, 0.0f, 0.0f);
71  this->combinedModels.resize(Math::clamp(lodLevels, 1, 3));
72  this->combinedEntity = nullptr;
73  this->lodReduceBy[0] = 1;
74  this->lodReduceBy[1] = modelLOD2ReduceBy;
75  this->lodReduceBy[2] = modelLOD3ReduceBy;
76  this->modelLOD2MinDistance = modelLOD2MinDistance;
77  this->modelLOD3MinDistance = modelLOD3MinDistance;
78  this->optimizeModels = optimizeModels;
79 }
80 
82 }
83 
84 void ObjectRenderGroup::combineNode(Node* sourceNode, const vector<Vector3>& origins, const vector<Matrix4x4>& objectParentTransformMatrices, Model* combinedModel) {
85  // create node in combined model
86  auto combinedModelNode = combinedModel->getNodeById(sourceNode->getId());
87  if (combinedModelNode == nullptr) {
88  auto newCombinedModelNode = make_unique<Node>(
89  combinedModel,
90  sourceNode->getParentNode() == nullptr?nullptr:combinedModel->getNodeById(sourceNode->getParentNode()->getId()),
91  sourceNode->getId(),
92  sourceNode->getName()
93  );
94  if (sourceNode->getParentNode() == nullptr) {
95  combinedModel->getSubNodes()[newCombinedModelNode->getId()] = newCombinedModelNode.get();
96  } else {
97  combinedModelNode->getParentNode()->getSubNodes()[newCombinedModelNode->getId()] = newCombinedModelNode.get();
98  }
99  combinedModel->getNodes()[newCombinedModelNode->getId()] = newCombinedModelNode.get();
100  //
101  combinedModelNode = newCombinedModelNode.release();
102  }
103 
104  {
105  auto sourceNodeVerticesSize = sourceNode->getVertices().size();
106  auto sourceNodeNormalsSize = sourceNode->getNormals().size();
107  auto sourceNodeTextureCoordinatesSize = sourceNode->getTextureCoordinates().size();
108  auto sourceNodeTangentsSize = sourceNode->getTangents().size();
109  auto sourceNodeBitangentsSize = sourceNode->getBitangents().size();
110 
111  // vertices and such from new model
112  auto combinedModelNodeVertices = combinedModelNode->getVertices();
113  auto combinedModelNodeNormals = combinedModelNode->getNormals();
114  auto combinedModelNodeTextureCoordinates = combinedModelNode->getTextureCoordinates();
115  auto combinedModelNodeTangents = combinedModelNode->getTangents();
116  auto combinedModelNodeBitangents = combinedModelNode->getBitangents();
117  auto combinedModelNodeFacesEntities = combinedModelNode->getFacesEntities();
118  auto combinedModelNodeOrigins = combinedModelNode->getOrigins();
119 
120  // current indices
121  auto combinedModelNodeVerticesIdxStart = combinedModelNodeVertices.size();
122  auto combinedModelNodeNormalsIdxStart = combinedModelNodeNormals.size();
123  auto combinedModelNodeTextureCoordinatesIdxStart = combinedModelNodeTextureCoordinates.size();
124  auto combinedModelNodeTangentsIdxStart = combinedModelNodeTangents.size();
125  auto combinedModelNodeBitangentsIdxStart = combinedModelNodeBitangents.size();
126 
127  // add vertices and such from source node to new node
128  {
129  auto i = 0;
130  for (const auto& objectParentTransformMatrix: objectParentTransformMatrices) {
132  transformMatrix.set(sourceNode->getTransformMatrix());
133  transformMatrix.multiply(objectParentTransformMatrix);
134 
135  //
136  for (const auto& vertex: sourceNode->getVertices()) {
137  combinedModelNodeOrigins.push_back(origins[i]);
138  combinedModelNodeVertices.push_back(transformMatrix.multiply(vertex));
139  }
140  for (const auto& normal: sourceNode->getNormals()) {
141  combinedModelNodeNormals.push_back(transformMatrix.multiplyNoTranslation(normal));
142  }
143  for (const auto& textureCoordinate: sourceNode->getTextureCoordinates()) {
144  combinedModelNodeTextureCoordinates.push_back(textureCoordinate);
145  }
146  for (const auto& tangent: sourceNode->getTangents()) {
147  combinedModelNodeTangents.push_back(transformMatrix.multiplyNoTranslation(tangent));
148  }
149  for (const auto& bitangent: sourceNode->getBitangents()) {
150  combinedModelNodeBitangents.push_back(transformMatrix.multiplyNoTranslation(bitangent));
151  }
152 
153  //
154  i++;
155  }
156  }
157 
158  // add source node faces to new new faces entity
159  for (const auto& facesEntity: sourceNode->getFacesEntities()) {
160  bool haveTextureCoordinates = facesEntity.isTextureCoordinatesAvailable();
161  bool haveTangentsBitangents = facesEntity.isTangentBitangentAvailable();
162 
163  // get faces entity
164  FacesEntity* combinedModelNodeFacesEntity = nullptr;
165  for (auto& combinedModelNodeFacesEntityExisting: combinedModelNodeFacesEntities) {
166  if (combinedModelNodeFacesEntityExisting.getId() == facesEntity.getId()) {
167  combinedModelNodeFacesEntity = &combinedModelNodeFacesEntityExisting;
168  break;
169  }
170  }
171  // create
172  if (combinedModelNodeFacesEntity == nullptr) {
173  auto newFacesEntity = FacesEntity(
174  combinedModelNode,
175  facesEntity.getId()
176  );
177  combinedModelNodeFacesEntities.push_back(newFacesEntity);
178  combinedModelNodeFacesEntity = &combinedModelNodeFacesEntities[combinedModelNodeFacesEntities.size() - 1];
179  auto combinedModelNodeFacesEntityMaterial = combinedModel->getMaterials()[facesEntity.getMaterial()->getId()];
180  if (combinedModelNodeFacesEntityMaterial == nullptr) {
181  combinedModelNodeFacesEntityMaterial = ModelTools::cloneMaterial(facesEntity.getMaterial());
182  combinedModel->getMaterials()[combinedModelNodeFacesEntityMaterial->getId()] = combinedModelNodeFacesEntityMaterial;
183  }
184  combinedModelNodeFacesEntity->setMaterial(combinedModelNodeFacesEntityMaterial);
185  }
186 
187  //
188  auto combinedModelNodeFaces = combinedModelNodeFacesEntity->getFaces();
189 
190  //
191  auto combinedModelNodeVerticesIdx = combinedModelNodeVerticesIdxStart;
192  auto combinedModelNodeNormalsIdx = combinedModelNodeNormalsIdxStart;
193  auto combinedModelNodeTextureCoordinatesIdx = combinedModelNodeTextureCoordinatesIdxStart;
194  auto combinedModelNodeTangentsIdx = combinedModelNodeTangentsIdxStart;
195  auto combinedModelNodeBitangentsIdx = combinedModelNodeBitangentsIdxStart;
196  for (const auto& objectParentTransformMatrix: objectParentTransformMatrices) {
197  // add faces
198  for (const auto& face: facesEntity.getFaces()) {
199  // get face vertices and such
200  const auto& faceVertexIndices = face.getVertexIndices();
201  const auto& faceNormalIndices = face.getNormalIndices();
202  const auto& faceTextureCoordinatesIndices = face.getTextureCoordinateIndices();
203  const auto& faceTangentIndices = face.getTangentIndices();
204  const auto& faceBitangentIndices = face.getBitangentIndices();
205 
206  //
207  auto combinedModelNodeFace =
208  Face(
209  combinedModelNode,
210  combinedModelNodeVerticesIdx + faceVertexIndices[0],
211  combinedModelNodeVerticesIdx + faceVertexIndices[1],
212  combinedModelNodeVerticesIdx + faceVertexIndices[2],
213  combinedModelNodeNormalsIdx + faceNormalIndices[0],
214  combinedModelNodeNormalsIdx + faceNormalIndices[1],
215  combinedModelNodeNormalsIdx + faceNormalIndices[2]
216  );
217  if (haveTextureCoordinates == true) {
218  combinedModelNodeFace.setTextureCoordinateIndices(
219  combinedModelNodeTextureCoordinatesIdx + faceTextureCoordinatesIndices[0],
220  combinedModelNodeTextureCoordinatesIdx + faceTextureCoordinatesIndices[1],
221  combinedModelNodeTextureCoordinatesIdx + faceTextureCoordinatesIndices[2]
222  );
223  }
224  if (haveTangentsBitangents == true) {
225  combinedModelNodeFace.setTangentIndices(
226  combinedModelNodeTangentsIdx + faceTangentIndices[0],
227  combinedModelNodeTangentsIdx + faceTangentIndices[1],
228  combinedModelNodeTangentsIdx + faceTangentIndices[2]
229  );
230  combinedModelNodeFace.setBitangentIndices(
231  combinedModelNodeBitangentsIdx + faceBitangentIndices[0],
232  combinedModelNodeBitangentsIdx + faceBitangentIndices[1],
233  combinedModelNodeBitangentsIdx + faceBitangentIndices[2]
234  );
235  }
236  combinedModelNodeFaces.push_back(combinedModelNodeFace);
237  }
238 
239  //
240  combinedModelNodeVerticesIdx+= sourceNodeVerticesSize;
241  combinedModelNodeNormalsIdx+= sourceNodeNormalsSize;
242  combinedModelNodeTextureCoordinatesIdx+= sourceNodeTextureCoordinatesSize;
243  combinedModelNodeTangentsIdx+= sourceNodeTangentsSize;
244  combinedModelNodeBitangentsIdx+= sourceNodeBitangentsSize;
245  }
246  combinedModelNodeFacesEntity->setFaces(combinedModelNodeFaces);
247  }
248 
249  // store back to model
250  combinedModelNode->setVertices(combinedModelNodeVertices);
251  combinedModelNode->setNormals(combinedModelNodeNormals);
252  combinedModelNode->setTextureCoordinates(combinedModelNodeTextureCoordinates);
253  combinedModelNode->setTangents(combinedModelNodeTangents);
254  combinedModelNode->setBitangents(combinedModelNodeBitangents);
255  combinedModelNode->setFacesEntities(combinedModelNodeFacesEntities);
256  combinedModelNode->setOrigins(combinedModelNodeOrigins);
257  }
258 
259  // do child nodes
260  for (const auto& [subNodeId, subNode]: sourceNode->getSubNodes()) {
261  combineNode(subNode, origins, objectParentTransformMatrices, combinedModel);
262  }
263 }
264 
265 void ObjectRenderGroup::combineObjects(Model* model, const vector<Transform>& objectsTransform, Model* combinedModel) {
266  vector<Matrix4x4> objectTransformMatrices;
267  vector<Vector3> origins;
268  for (const auto& objectTransform: objectsTransform) {
271  transformMatrix.multiply(objectTransform.getTransformMatrix());
272  objectTransformMatrices.push_back(transformMatrix);
273  origins.push_back(objectTransform.getTranslation());
274  }
275  for (const auto& [subNodeId, subNode]: model->getSubNodes()) {
276  combineNode(subNode, origins, objectTransformMatrices, combinedModel);
277  }
278 }
279 
281  // dispose old object and combined model
282  if (combinedEntity != nullptr) {
283  if (engine != nullptr) engine->removeEntityFromLists(combinedEntity.get());
284  combinedEntity->dispose();
285  combinedEntity = nullptr;
286  }
287 
288  // combine objects to a new model
289  for (auto i = 0; i < combinedModels.size(); i++) {
290  combinedModels[i] = make_unique<Model>(
291  id + ".o3rg.lod." + to_string(i),
292  id + ".o3rg.lod." + to_string(i),
293  UpVector::Y_UP,
294  RotationOrder::ZYX,
295  nullptr
296  );
297  }
298 
299  //
300  for (const auto& [model, objectsTransform]: transformByModel) {
301  auto lodLevel = 0;
302  for (const auto& combinedModel: combinedModels) {
303  auto reduceByFactor = lodReduceBy[lodLevel];
304  lodLevel++;
305  auto objectCount = 0;
306  vector<Transform> reducedObjectsTransform;
307  for (const auto& objectTransform: objectsTransform) {
308  if (objectCount % reduceByFactor != 0) {
309  objectCount++;
310  continue;
311  }
312  reducedObjectsTransform.push_back(objectTransform);
313  objectCount++;
314  }
315  combineObjects(model, reducedObjectsTransform, combinedModel.get());
316  }
317  }
318 
319  // create new combined object
320  for (const auto& combinedModel: combinedModels) {
321  if (combinedModel != nullptr) {
322  // post process combined model
323  ModelTools::shrinkToFit(combinedModel.get());
324  ModelTools::createDefaultAnimation(combinedModel.get(), 0);
325  ModelTools::setupJoints(combinedModel.get());
326  ModelTools::fixAnimationLength(combinedModel.get());
327  }
328  }
329 
330  // optimize models
331  if (optimizeModels == true) {
332  for (auto i = 0; i < combinedModels.size(); i++) {
333  combinedModels[i] = unique_ptr<Model>(ModelTools::optimizeModel(combinedModels[i].release()));
334  }
335  }
336 
337  // no lod
338  if (combinedModels.size() == 1) {
339  auto combinedObject = new Object(id + ".o3rg", combinedModels[0].get());
340  combinedObject->setParentEntity(this);
341  combinedObject->setParentTransform(parentTransform);
342  combinedObject->setShader(shaderId);
343  combinedObject->setContributesShadows(contributesShadows);
344  combinedObject->setReceivesShadows(receivesShadows);
345  combinedObject->setEngine(engine);
346  combinedObject->shaderParameters = shaderParameters;
347  combinedObject->update();
348  combinedEntity = unique_ptr<Entity>(combinedObject);
349  } else
350  // lod
351  if (combinedModels.size() > 1) {
352  // create object, initialize and
353  auto combinedLODObject = new LODObject(
354  id + ".o3rg",
355  combinedModels[0].get(),
358  combinedModels[1].get(),
361  combinedModels[2].get()
362  );
363  combinedLODObject->setParentEntity(this);
364  combinedLODObject->setParentTransform(parentTransform);
365  combinedLODObject->setShader(shaderId);
366  combinedLODObject->setContributesShadows(contributesShadows);
367  combinedLODObject->setReceivesShadows(receivesShadows);
368  combinedLODObject->setEngine(engine);
369  combinedLODObject->shaderParameters = shaderParameters;
370  if (combinedLODObject->objectLOD1 != nullptr) {
371  combinedLODObject->objectLOD1->shaderParameters = shaderParameters;
372  }
373  if (combinedLODObject->objectLOD2 != nullptr) {
374  combinedLODObject->objectLOD2->shaderParameters = shaderParameters;
375  }
376  if (combinedLODObject->objectLOD3 != nullptr) {
377  combinedLODObject->objectLOD3->shaderParameters = shaderParameters;
378  }
379  combinedLODObject->update();
380  combinedEntity = unique_ptr<Entity>(combinedLODObject);
381  }
382 
383  //
385  update();
386 }
387 
388 void ObjectRenderGroup::addObject(Model* model, const Transform& transform) {
389  transformByModel[model].push_back(transform);
390 }
391 
393 {
394  if (this->engine != nullptr) this->engine->deregisterEntity(this);
395  this->engine = engine;
396  if (engine != nullptr) engine->registerEntity(this);
397  if (combinedEntity != nullptr) combinedEntity->setEngine(engine);
398 }
399 
401 {
402 }
403 
405 {
406  Transform::setTransform(transform);
407  //
408  auto entityTransform = parentTransform * (*this);
409  entityTransformMatrix = entityTransform.getTransformMatrix();
410  // update world bounding box
412  //
413  if (combinedEntity != nullptr) combinedEntity->setParentTransform(parentTransform);
414  // update object
415  if (parentEntity == nullptr && frustumCulling == true && engine != nullptr && enabled == true) engine->partition->updateEntity(this);
416 }
417 
419 {
421  //
422  auto entityTransform = parentTransform * (*this);
423  entityTransformMatrix = entityTransform.getTransformMatrix();
424  // update world bounding box
426  //
427  if (combinedEntity != nullptr) combinedEntity->setParentTransform(parentTransform);
428  // update object
429  if (parentEntity == nullptr && frustumCulling == true && engine != nullptr && enabled == true) engine->partition->updateEntity(this);
430 }
431 
433 {
434  // return if enable state has not changed
435  if (this->enabled == enabled) return;
436 
437  // frustum if root entity
438  if (parentEntity == nullptr) {
439  // frustum culling enabled?
440  if (frustumCulling == true) {
441  // yeo, add or remove from partition
442  if (enabled == true) {
443  if (engine != nullptr) engine->partition->addEntity(this);
444  } else {
445  if (engine != nullptr) engine->partition->removeEntity(this);
446  }
447  }
448  }
449 
450  //
451  this->enabled = enabled;
452 }
453 
455  return frustumCulling;
456 }
457 
458 void ObjectRenderGroup::setFrustumCulling(bool frustumCulling) {
459  // check if enabled and engine attached
460  if (enabled == true && engine != nullptr) {
461  // had frustum culling
462  if (this->frustumCulling == true) {
463  // yep, remove if set to false now
464  if (frustumCulling == false) engine->partition->removeEntity(this);
465  } else {
466  // yep, add if set to true now
467  if (frustumCulling == true) engine->partition->addEntity(this);
468  }
469  }
470  this->frustumCulling = frustumCulling;
471  // delegate change to engine
472  if (engine != nullptr) engine->updateEntityRegistration(this);
473 }
474 
476 {
477  // delegate to combined object
478  if (combinedEntity != nullptr) {
479  combinedEntity->dispose();
480  combinedEntity = nullptr;
481  }
482  // disose combined model
483  combinedModels.clear();
484 }
485 
487 {
488  if (combinedEntity != nullptr) combinedEntity->initialize();
489 }
490 
void set(float r, float g, float b, float a)
Sets this color by its components.
Definition: Color4.h:66
Engine main class.
Definition: Engine.h:131
unique_ptr< Partition > partition
Definition: Engine.h:289
void updateEntityRegistration(Entity *entity)
Updates registration of engine by performing deregisterEntity() and registerEntity()
Definition: Engine.h:1489
void deregisterEntity(Entity *entity)
Removes a entity from internal lists, those entities can also be sub entities from entity hierarchy o...
Definition: Engine.cpp:382
void removeEntityFromLists(Entity *entity)
Remove entity.
Definition: Engine.cpp:674
void registerEntity(Entity *entity)
Adds a entity to internal lists, those entities can also be sub entities from entity hierarchy or par...
Definition: Engine.cpp:437
Entity * parentEntity
Definition: Entity.h:39
LOD object to be used with engine class.
Definition: LODObject.h:49
Object render group for static objects that might be animated by shaders.
void dispose() override
Dispose this entity.
static void combineNode(Node *sourceNode, const vector< Vector3 > &origins, const vector< Matrix4x4 > &objectParentTransformMatrices, Model *combinedModel)
Combine node into given combined model.
void initialize() override
Initiates this entity.
EntityShaderParameters shaderParameters
void update() override
Update transform.
void updateRenderGroup()
Update render group model and bounding box.
vector< unique_ptr< Model > > combinedModels
void setTransform(const Transform &transform) override
Set transform.
void updateBoundingBox()
Compute bounding box.
unordered_map< Model *, vector< Transform > > transformByModel
void addObject(Model *model, const Transform &transform)
Adds a instance to this render group.
static void combineObjects(Model *model, const vector< Transform > &objectsTransform, Model *combinedModel)
Combine model with transform into current model.
unique_ptr< Entity > combinedEntity
void setFrustumCulling(bool frustumCulling) override
Set frustum culling.
void setEngine(Engine *engine) override
Set up engine.
void setEnabled(bool enabled) override
Enable/disable rendering.
void setRenderer(Renderer *renderer) override
Set up renderer.
Object to be used with engine class.
Definition: Object.h:60
Transform which contain scale, rotations and translation.
Definition: Transform.h:29
virtual void setTransform(const Transform &transform)
Set transform.
Definition: Transform.h:177
Matrix4x4 transformMatrix
Definition: Transform.h:35
virtual void update()
Computes transform matrix.
Definition: Transform.cpp:33
Represents a model face, consisting of vertex, normal, tangent and bitangent vectors,...
Definition: Face.h:18
Node faces entity A node can have multiple entities containing faces and a applied material.
Definition: FacesEntity.h:23
const vector< Face > & getFaces() const
Definition: FacesEntity.h:81
void setMaterial(Material *material)
Set up the entity's material.
Definition: FacesEntity.h:67
void setFaces(const vector< Face > &faces)
Set up entity's faces.
Definition: FacesEntity.cpp:43
Represents a material.
Definition: Material.h:23
Representation of a 3D model.
Definition: Model.h:35
unordered_map< string, Node * > & getSubNodes()
Returns object's sub nodes.
Definition: Model.h:220
unordered_map< string, Material * > & getMaterials()
Returns all object materials.
Definition: Model.h:185
const Matrix4x4 & getImportTransformMatrix()
Definition: Model.h:340
unordered_map< string, Node * > & getNodes()
Returns all object's nodes.
Definition: Model.h:199
Node * getNodeById(const string &id)
Returns a node by given name or null.
Definition: Model.h:208
Model node.
Definition: Node.h:32
const vector< Vector3 > & getBitangents() const
Definition: Node.h:216
unordered_map< string, Node * > & getSubNodes()
Definition: Node.h:293
const vector< Vector3 > & getTangents() const
Definition: Node.h:203
const string & getName()
Definition: Node.h:96
const vector< Vector3 > & getVertices() const
Definition: Node.h:155
const vector< Vector3 > & getNormals() const
Definition: Node.h:177
const vector< Vector2 > & getTextureCoordinates() const
Definition: Node.h:190
const string & getId()
Returns id.
Definition: Node.h:89
Node * getParentNode()
Definition: Node.h:81
const vector< FacesEntity > & getFacesEntities() const
Definition: Node.h:260
const Matrix4x4 & getTransformMatrix() const
Definition: Node.h:125
Represents rotation orders of a model.
Definition: RotationOrder.h:23
Model up vector.
Definition: UpVector.h:20
Axis aligned bounding box used for frustum, this is not directly connectable with physics engine.
Definition: BoundingBox.h:26
void fromBoundingVolumeWithTransformMatrix(BoundingBox *original, const Matrix4x4 &transformMatrix)
Create bounding volume from given original(of same type) with applied transform matrix.
Definition: BoundingBox.cpp:79
Standard math functions.
Definition: Math.h:19
Matrix4x4 class representing matrix4x4 mathematical structure and operations for 3d space.
Definition: Matrix4x4.h:23
Matrix4x4 & identity()
Creates identity matrix.
Definition: Matrix4x4.h:158
Vector3 multiply(const Vector3 &vector3) const
Multiplies this matrix with vector3.
Definition: Matrix4x4.h:225
Matrix4x4 & set(float r0c0, float r0c1, float r0c2, float r0c3, float r1c0, float r1c1, float r1c2, float r1c3, float r2c0, float r2c1, float r2c2, float r2c3, float r3c0, float r3c1, float r3c2, float r3c3)
Sets this matrix by its components.
Definition: Matrix4x4.h:108
Vector3 multiplyNoTranslation(const Vector3 &vector3) const
Multiplies this matrix with vector3 while ignoring translation.
Definition: Matrix4x4.h:238
Vector2 class representing vector2 mathematical structure and operations with x, y components.
Definition: Vector2.h:20
Vector3 class representing vector3 mathematical structure and operations with x, y,...
Definition: Vector3.h:20
Model tools functions class.
Definition: ModelTools.h:42
Partition interface.
Definition: Partition.h:18