Manipulating a TriMesh

Hi Everyone,

I’m loading an Obj file (using ObjLoader) to a TriMesh, in order to get its AABB (by calling calcBoundingBox()) which I need for some physics calculation.

However, the model needs to be manipulated before (scale, rotation and translation).
Is there any way to apply those transformation on the mesh that are not in the context of drawing?
(e.g something similar to chaining geom::Scale()/geom::Rotate()/geom::Translate()as with geom::Source) such that when calling tocalcBoundingBox()` the model would already be in its final configuration.

Thanks!

Hi,

You can submit a mat4 for calcBoundingBox() and then your model will be transformed by that matrix when calculating the bounding box. Would this solve your problem?

-Gabor

Hi @gabor_papp thanks for the quick reply.
Could you elaborate please? Is there a way to build that matrix once and use it for both the drawing and the calculation of the AABB?

Not tested code:

mat4 translate = translate( mat4(), vec3( 10.0f, 0.0f, 0.0f ) );
mat4 scale = scale( vec3( 2.0f, 2.0f, 2.0f ) );
mat4 rotate = rotate( 0.5f, vec3( 1.0f, 0.0f, 0.0f );
mTransform = translate * rotate * scale;

Use gl::multModelMatrix( mTransform ); before drawing your mesh.

You can calculate the bounding box with this transformation matrix like this:

AxisAlignedBox bbox = mTriMesh->calcBoundingBox( mTransform );

This can be slow if you transform many vertices, but if the model is static, you can calculate the bounding box once and transform it with the matrix every frame as the transformation changes.

// do this once when loading the mesh
mTriMeshBbox = mTriMesh->calcBoundingBox();
...
// update this every frame
AxisAlignedBox bbox = mTriMeshBbox.transformed( mTransform );

thanks again @gabor_papp it looks exactly like what I needed.

However, I’m getting the following compiler error:

error: type 'mat4' (aka 'tmat4x4<float, highp>') does not provide a call operator 
            mat4 scale = scale(0.027f, 0.027f, 0.027f);

(Using clang on MacOS, and cinder 0.9.1)

Weirdly, mat4 rotate = rotate( 0.5f, vec3( 1.0f, 0.0f, 0.0f ); does compile and work.

sorry, try this:

mat4 scale = scale( vec3( 0.027f, 0.027f, 0.027f ) );

@gabor_papp still getting the same error.

@gabor_papp If I understand correctly those functions are all directly implemented by GLM, and are documented here: https://glm.g-truc.net/0.9.4/api/a00206.html

According to this, both calls should work.

can you try:

mat4 scale = glm::scale( vec3( 0.027f, 0.027f, 0.027f ) );

@gabor_papp Hah! it did the trick. I was sure I’ve tried it before…

The whole thing works now - thanks.

Still, I don’t understand how rotate() was found without the namespace prefix and scale() wasn’t (I don’t have other function with the same name in any scope)

Some glm functions and types are aliased in the cinder namespace. For example, ci::vec4 is an alias for glm::vec4. Same might be true for rotate.

-Paul

P.S. I’d like to add two tips to @gabor_papp’s :

  • before calling gl::multModelMatrix, make sure to store/push the current matrix so you can restore it when you’re done drawing the mesh. You can do this by calling gl::pushModelMatrix and gl::popModelMatrix, but a more convenient way is to call gl::ScopedModelMatrix scpModel; This will create an object which pushes the matrices, then pops them again when it goes out of scope, RAII style. There are many more scoped variations like that, all worth using. The following code is equivalent:
// A
{
    gl::pushModelMatrix();
    gl::multModelMatrix( mTransform );
    gl::draw( mTriMesh );
    gl::popModelMatrix();
}

// B
{
    gl::ScopedModelMatrix scpModel;
    gl::multModelMatrix( mTransform );
    gl::draw( mTriMesh );
}
  • When multiplying matrices, the order is important. Transformations are performed from last to first in the line mTransform = translate * rotate * scale;. Usually, a mesh is first rotated and/or scaled, followed by a translation. That way, it rotates or scales around its own origin.
2 Likes