Xemuth
Xemuth

Reputation: 349

Problem multiplying vec3 by model matrice (scaling problem)

in my 3D application I have an Object3D class which construct is bounding box at creation, this bounding box is a simple cuboid with a min and a max point : simple cuboid bounding box

However, when I translate my Object3D, I want to update my bounding box using the Model matrix of my object :

     BoundingBox& TransformBy(const glm::mat4& modelMatrice){
        //extract position of my model matrix
        glm::vec3 pos( modelMatrice[3] );
        
        //extract scale of my model matrix
        glm::vec3 scale( glm::length(glm::vec3(modelMatrice[0])), glm::length(glm::vec3(modelMatrice[1])) , glm::length(glm::vec3(modelMatrice[2])));
        
        //Creating vec4 min/max from vec3
        glm::vec4 min_(min.x, min.y, min.z, 1.0f);
        glm::vec4 max_(max.x, max.y, max.z, 1.0f);
        
        //Inverting the matrice to multiply my vec4 with it in order to rotate my min max
        glm::mat4 inverse = glm::inverse(modelMatrice);
        min_ = min_ * inverse;
        max_ = max_ * inverse;
        
        //Scale my min_ max_
        min_ *= glm::vec4(scale,1.0f);
        max_ *= glm::vec4(scale,1.0f);
        
        //adding model matrice translation to min_ and max_
        min_ += glm::vec4(pos,0.0f);
        max_ += glm::vec4(pos,0.0f);
    
        //Redefining max and min
        max = glm::vec3( (min_.x > max_.x)? min_.x:max_.x, (min_.y > max_.y)? min_.y: max_.y, (min_.z > max_.z)? min_.z : max_.z);
        min = glm::vec3( (min_.x < max_.x)? min_.x:max_.x, (min_.y < max_.y)? min_.y: max_.y, (min_.z < max_.z)? min_.z : max_.z);
        return *this;
    }

Here is how I call my TransformBy function :

box.TransformBy(transform.GetModelMatrix());

For some reason, it make my min and max point rotating correctly it also translate it but my scaling is not correctly applied, making my bounding box having a bigger scale than my 3D object.

Why my scaling is not working properly ? Maybe there is a less complicated way of doing what I want ?

Upvotes: 1

Views: 275

Answers (1)

FilmCoder
FilmCoder

Reputation: 106

If I understand correctly how the result of GetModelMatrix() is supposed to look, i.e. that it is just a normal 4x4 transformation matrix, then I think I can guess what the problem is. I find it easiest to visualize/explain with an example, so hopefully that's alright.

Suppose your result of GetModelMatrix() is the following, a simple transformation matrix with no rotational component, just a translation by [5 6 7].

| 1 0 0 5 |
| 0 1 0 6 |
| 0 0 1 7 |
| 0 0 0 1 |

The inverse of that, which gets passed in as modelMatrice, is just:

| 1 0 0 -5 |
| 0 1 0 -6 |
| 0 0 1 -7 |
| 0 0 0  1 |

After calling glm::vec4 min_(min.x,min.y,min.z,1.0f);, let us represent min_ by the vector [x y z 1]. Then min_ * modelMatrice looks like:

            | 1 0 0 -5 |   
            | 0 1 0 -6 |
[x y z 1] * | 0 0 1 -7 | = [x y z (-5x-6y-7z+1)]
            | 0 0 0  1 |   

That is, the x, y, and z components are not altered because it is the |0 0 0 1| row that gets applied to them in the multiplication, not the column containing the translation components.

May I ask, do you have a particular reason for (a) inverting GetModelMatrix() and (b) multiplying the matrix by the vector as opposed to vice versa? I'd guess that perhaps your code should instead look as follows:

BoundingBox& TransformBy(const glm::mat4 modelMatrice){
        ...
        min_ = modelMatrice * min_;
        max_ = modelMatrice * max_;
        ...
        return *this;
    } 

Called with box.TransformBy(transform.GetModelMatrix());

Update:

In regards to your new code, I question the need for inversion of the transformation matrix for rotation, the need to split up the scaling/translation/rotation into three separate steps, etc. It feels like, unless there is an error somewhere else, the process should be as simple as taking your object's transformation vector, multiplying min_ and max_ by it, and then doing the test for whether any min/max components swapped that you currently have at the end of the function:

     BoundingBox& TransformBy(const glm::mat4& modelMatrice){
        min_ = modelMatrice * min_;
        max_ = modelMatrice * max_;
        max = glm::vec3( (min_.x > max_.x)? min_.x:max_.x, (min_.y > max_.y)? min_.y: max_.y, (min_.z > max_.z)? min_.z : max_.z);
        min = glm::vec3( (min_.x < max_.x)? min_.x:max_.x, (min_.y < max_.y)? min_.y: max_.y, (min_.z < max_.z)? min_.z : max_.z);
        return *this;
    }

If the above five-line function does not work, please let me know how it fails; if it does not work, I feel the problem may lie elsewhere in your code.

However, to answer your updated question, as to why the scaling does not work: When you invert the transformation matrix, you are also inverting the scaling. Thus, you are not just rotating when you multiply by inverse: you are also applying the inverted scale. While you could fix this by multiplying by the scale again, the far simpler solution seems to be trying to get the above five-line solution working.

Upvotes: 2

Related Questions