Search code examples
c++openglassimp

Translate Assimp 3D Models also rotates


This is the view before translate, which is correct. This is the view after translate, which is incorrect.

I have a 3D Model in an OpenGL (C++) loaded using Assimp. I need to move this model around the screen (translate) like it is always facing the camera in the X and Y axis (no Z axis). It would be like moving this model like it is 2D only (but of course if I rotate it, it would show the Z axis as well).

my render function is :

    camX = CamY = 0;  
    camZ = 5;  
    lookatX = lookatY = lookatZ = 0;  

    void C3DModel::render(void)  
    { 

        static float step = 0.0f;  
        setCamera(camX, camY, camZ, lookatX, lookatY, lookatZ);  
        translate(-3, 1, 0); // here is the issue . 
        scale(scaleFactor, scaleFactor, scaleFactor);  
        rotate(step, 0.0f, 1.0f, 0.0f); 
    }

    void C3DModel::translate(float x, float y, float z) 
    {

       float aux[16];

       setTranslationMatrix(aux, x, y, z);
       multMatrix(modelMatrix, aux);
       setModelMatrix();
   }

   void C3DModel::setTranslationMatrix(float *mat, float x, float y, float z)
   {

       setIdentityMatrix(mat, 4);
       mat[12] = x;
       mat[13] = y;
       mat[14] = z;
   }

   void C3DModel::setScaleMatrix(float *mat, float sx, float sy, float sz)
   {

       setIdentityMatrix(mat, 4);
       mat[0] = sx;
       mat[5] = sy;
       mat[10] = sz;
   }

   void C3DModel::setRotationMatrix(float *mat, float angle, float x, float y, float z)
   {
       float radAngle = DegToRad(angle);
       float co = cos(radAngle);
       float si = sin(radAngle);
       float x2 = x * x;
       float y2 = y * y;
       float z2 = z * z;

       mat[0] = x2 + (y2 + z2) * co;
       mat[4] = x * y * (1 - co) - z * si;
       mat[8] = x * z * (1 - co) + y * si;
       mat[12] = 0.0f;

       mat[1] = x * y * (1 - co) + z * si;
       mat[5] = y2 + (x2 + z2) * co;
       mat[9] = y * z * (1 - co) - x * si;
       mat[13] = 0.0f;

       mat[2] = x * z * (1 - co) - y * si;
       mat[6] = y * z * (1 - co) + x * si;
       mat[10] = z2 + (x2 + y2) * co;
       mat[14] = 0.0f;

       mat[3] = 0.0f;
       mat[7] = 0.0f;
       mat[11] = 0.0f;
       mat[15] = 1.0f;
   }


   void C3DModel::rotate(float angle, float x, float y, float z) 
   {

      float aux[16];

      setRotationMatrix(aux, angle, x, y, z);
      multMatrix(modelMatrix, aux);
      setModelMatrix();
  }

  void C3DModel::scale(float x, float y, float z)
  {
      float aux[16];

      setScaleMatrix(aux, x, y, z);
      multMatrix(modelMatrix, aux);
      setModelMatrix();
  }

  void C3DModel::setIdentityMatrix(float *mat, int size)
  {
     // fill matrix with 0s
     for (int i = 0; i < size * size; ++i)
         mat[i] = 0.0f;
     // fill diagonal with 1s
     for (int i = 0; i < size; ++i)
        mat[i + i * size] = 1.0f;
  }

  void C3DModel::multMatrix(float *a, float *b) 
  {

     float res[16];

     for (int i = 0; i < 4; ++i) 
     {
         for (int j = 0; j < 4; ++j) 
         {
             res[j * 4 + i] = 0.0f;
             for (int k = 0; k < 4; ++k) 
             {
                 res[j * 4 + i] += a[k * 4 + i] * b[j * 4 + k];
             }
         }
     }
     memcpy(a, res, 16 * sizeof(float));
  }

  void C3DModel::setModelMatrix() 
  {
      glBindBuffer(GL_UNIFORM_BUFFER, matricesUniBuffer);
      glBufferSubData(GL_UNIFORM_BUFFER, ModelMatrixOffset, MatrixSize, modelMatrix);
      glBindBuffer(GL_UNIFORM_BUFFER, 0);
 }

 void C3DModel::crossProduct(float *a, float *b, float *res) 
 {
     res[0] = a[1] * b[2] - b[1] * a[2];
     res[1] = a[2] * b[0] - b[2] * a[0];
     res[2] = a[0] * b[1] - b[0] * a[1];
 }

 // Normalize a vec3
 void C3DModel::normalize(float *a)
 {

     float mag = sqrt(a[0] * a[0] + a[1] * a[1] + a[2] * a[2]);
     a[0] /= mag;
     a[1] /= mag;
     a[2] /= mag;
 }

 void C3DModel::setCamera(float posX, float posY, float posZ, float lookAtX, float lookAtY, float lookAtZ) 
 {

     float dir[3], right[3], up[3];
     up[0] = 0.0f;  up[1] = 1.0f;   up[2] = 0.0f;
     dir[0] = (lookAtX - posX);
     dir[1] = (lookAtY - posY);
     dir[2] = (lookAtZ - posZ);
     normalize(dir);
     crossProduct(dir, up, right);
     normalize(right);
     crossProduct(right, dir, up);
     normalize(up);
     float viewMatrix[16], aux[16];
     viewMatrix[0] = right[0];
     viewMatrix[4] = right[1];
     viewMatrix[8] = right[2];
     viewMatrix[12] = 0.0f;
     viewMatrix[1] = up[0];
     viewMatrix[5] = up[1];
     viewMatrix[9] = up[2];
     viewMatrix[13] = 0.0f;
     viewMatrix[2] = -dir[0];
     viewMatrix[6] = -dir[1];
     viewMatrix[10] = -dir[2];
     viewMatrix[14] = 0.0f;
     viewMatrix[3] = 0.0f;
     viewMatrix[7] = 0.0f;
     viewMatrix[11] = 0.0f;
     viewMatrix[15] = 1.0f;
     setTranslationMatrix(aux, -posX, -posY, -posZ);
     multMatrix(viewMatrix, aux);
     glBindBuffer(GL_UNIFORM_BUFFER, matricesUniBuffer);
     glBufferSubData(GL_UNIFORM_BUFFER, ViewMatrixOffset, MatrixSize, viewMatrix);
     glBindBuffer(GL_UNIFORM_BUFFER, 0);
 }

Solution

  • What i will try is to separate the rotation of your object and the translation requested for your screen position, in 2 different matrices.

    At each frame, I would compute the rotation matrice with the code inside your C3DModel::setRotationMatrix and the translation with C3DModel::setTranslationMatrix, combine them in a fresh new model matrice and apply it to your object. Keep in mind that the order matters, if you rotate first the object will turn around the origin in your obj file, if you rotate after the translation it will rotate around the worl origin (like a planet around the sun, the sun would be the origin).

    In the end, it would looks like:

    void C3DModel::render(void){
    
        float* rotation = createRotation(angle, x, y, z);
        float* translation = createTranslation(x, y, z);
        float* updatedModel = mul(rotation, translation) //order matters
        setModel(updatedModel);
    }