Example #1
0
 //----------------------------------------------------------------------------------------------------------------------
 float vecAngle(const ngl::Vec3 &_vec1, const ngl::Vec3 &_vec2)
 {
   // calculate the angle between two vectors using the dot product
   float angle;
   angle=_vec1.dot(_vec2)/_vec1.length()*_vec2.length();
   angle=ngl::degrees(acos(angle));
   return angle;
 }
Example #2
0
ngl::Mat4 Bvh::getRotationFromZ(ngl::Vec3 _vec) const
{
    ngl::Mat4 rotM;
    float x, y, z;
    // rotate negative z axis to _vec direction
    _vec.normalize();
    ngl::Vec3 nz(0,0,-1);
    float angle = acos(_vec.dot(nz));
    ngl::Vec3 norm = _vec.cross(nz);
    if(norm.length()<= 0.0001)
    {
        x= z = 0.0;
        y = 1.0;
    }
    else
    {
        norm.normalize();
        x = norm.m_x;
        y = norm.m_y;
        z = norm.m_z;
    }

    // Axis and Angle matrix rotation see
    // http://en.wikipedia.org/wiki/Rotation_matrix for more details
    float c = cos(angle);
    float s = sin(angle);
    float C=1-c;
    float xs  = x*s;  float  ys = y*s;  float  zs = z*s;
    float xC  = x*C;  float  yC = y*C;  float  zC = z*C;
    float xyC = x*yC; float yzC = y*zC; float zxC = z*xC;

    rotM.m_m[0][0]=x*xC+c;  rotM.m_m[0][1]= xyC-zs;  rotM.m_m[0][2]= zxC+ys;
    rotM.m_m[1][0]=xyC+zs;   rotM.m_m[1][1]=y*yC+c;  rotM.m_m[1][2]= yzC-xs;
    rotM.m_m[2][0]=zxC-ys;   rotM.m_m[2][1]=yzC+xs;  rotM.m_m[2][2]=z*zC+c;
    return rotM;
}
void NGLScene::paintGL()
{

    if(testangle==89)
        vary=-1;

    if(testangle==-89)
        vary=1;

//This bit has been  MOVED TO TIMER EVENT for more 'slow-motion' control
//    testangle+=vary;
    std::cout<<testangle<<std::endl;

    ngl::Vec3 v1(-5+15*sin((testangle)*(M_PI/180))-2,-5+15*sin((testangle)*(M_PI/180)),  4*sin((testangle)*(M_PI/180))-2);
    ngl::Vec3 v2(-4,0.01,-5+15*sin((testangle)*(M_PI/180)));//transform the triangle vao to 2,2,0

      ngl::Vec3 v2NonNormalized=v2;
      ngl::Vec3 v1NonNormalized=v1;


  // clear the screen and depth buffer
  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
  // Rotation based on the mouse position for our global transform

  // Rotation based on the mouse position for our global
  // transform
  ngl::Mat4 rotX;
  ngl::Mat4 rotY;
  // create the rotation matrices
  rotX.rotateX(m_spinXFace);
  rotY.rotateY(m_spinYFace);
  // multiply the rotations
  m_mouseGlobalTX=rotY*rotX;
  // add the translations
  m_mouseGlobalTX.m_m[3][0] = m_modelPos.m_x;
  m_mouseGlobalTX.m_m[3][1] = m_modelPos.m_y;
  m_mouseGlobalTX.m_m[3][2] = m_modelPos.m_z;


  ngl::ShaderLib *shader=ngl::ShaderLib::instance();
  (*shader)["Phong"]->use();
  ngl::Material m(ngl::STDMAT::PEWTER);
  // load our material values to the shader into the structure material (see Vertex shader)
  m.loadToShader("material");

  ngl::Mat4 MV;
  ngl::Mat4 MVP;
  ngl::Mat3 normalMatrix;
  ngl::Mat4 M;


  //*********
  m_transform.reset();
  //draw box
  {
      m_transform.setPosition(v1NonNormalized);
      M=m_transform.getMatrix()*m_mouseGlobalTX;
      MV=  M*m_cam->getViewMatrix();
      MVP= M*m_cam->getVPMatrix();
      normalMatrix=MV;
      normalMatrix.inverse();
      shader->setShaderParamFromMat4("MV",MV);
      shader->setShaderParamFromMat4("MVP",MVP);
      shader->setShaderParamFromMat3("normalMatrix",normalMatrix);
      shader->setShaderParamFromMat4("M",M);


      //ngl::VAOPrimitives::instance()->draw("cube");
      m_vao2->bind();
      m_vao2->draw();
      m_vao2->unbind();

  }


    v1.normalize();
    v2.normalize();
    float angle = /*atan2(v1.m_y,v1.m_x) - atan2(v2.m_y,v2.m_x);//*/acos(v1.dot(v2));

    ngl::Vec3 rotationAxis = v1.cross(v2);
    rotationAxis.normalize();

//    ngl::Quaternion q ;
//    q.fromAxisAngle(rotationAxis,angle);

    ngl::Mat4 s,rotateMat,translateMat;
    s=1;

    //Use either RotationBetweenVectors or matrixFromAxisAngle
    rotateMat=RotationBetweenVectors(v2,v1).toMat4();
    //rotateMat=matrixFromAxisAngle(rotationAxis,angle);//q.toMat4();


    //calculate euler angles from axis-angle
//    double heading,attitude,bank;
//    toEuler(rotationAxis.m_x, rotationAxis.m_y, rotationAxis.m_z, angle, heading, attitude, bank);

//    m_transform.reset();
//    std::cout<<bank*(180/M_PI)<<","<<heading*(180/M_PI)<<","<<attitude*(180/M_PI)<<","<<std::endl;
//    m_transform.setRotation(bank*(180/M_PI),heading*(180/M_PI),attitude*(180/M_PI));
//    r= m_transform.getMatrix();

    m_transform.reset();
    m_transform.setPosition(v2NonNormalized);
    translateMat= m_transform.getMatrix();

    ngl::Mat4 modelmatrix=s*rotateMat*translateMat;

  m_transform.reset();
  //draw triangle
  {
      //    load our material values to the shader into the structure material (see Vertex shader)
      m.set(ngl::STDMAT::BRONZE);
      m.loadToShader("material");

      M=/*m_transform.getMatrix()*/  modelmatrix*m_mouseGlobalTX;
      MV=  M*m_cam->getViewMatrix();
      MVP= M*m_cam->getVPMatrix();
      normalMatrix=MV;
      normalMatrix.inverse();
      shader->setShaderParamFromMat4("MV",MV);
      shader->setShaderParamFromMat4("MVP",MVP);
      shader->setShaderParamFromMat3("normalMatrix",normalMatrix);
      shader->setShaderParamFromMat4("M",M);


//      ngl::VAOPrimitives::instance()->draw("cube");
      m_vao->bind();
      m_vao->draw();
      m_vao->unbind();

   }



//    //draw the tip-cube of the triangle
//  {
//    m.set(ngl::GOLD);
//    // load our material values to the shader into the structure material (see Vertex shader)
//    m.loadToShader("material");

//    translateMat=1;

//  //  rotateMat=1;
//  //  scaleMat=1;

//  //not working
//  //  ngl::Vec3 v=v1.cross(v2);
//  //  float c=v1.dot(v2);
//  //  float h=1-c/v.dot(v);
//  //  rotateMat=ngl::Mat4(c*h*v.m_x*v.m_x,                 h*v.m_x*v.m_y-v.m_z,           h*v.m_x*v.m_z+v.m_y, 1,
//  //                      h*v.m_x*v.m_y+v.m_z,             c+h*v.m_y*v.m_y,               h*v.m_y*v.m_z-v.m_x, 1,
//  //                      h*v.m_x*v.m_z-v.m_y,       h*v.m_y*v.m_z+v.m_x,                c+h*v.m_z*v.m_z,      1,
//  //                      0 ,                            0 ,                                     0,            1);
//  //  rotateMat.transpose();
//  //not working



//  //     ngl::Mat4 trs=m_transform.getMatrix();
//  //     rotateMat=matrixFromAxisAngle(rotationAxis,angle);
//  //     translateMat.translate(-v2NonNormalized.m_x,-(v2NonNormalized.m_y),-v2NonNormalized.m_z);

//    //Based on [R] = [T].inverse * [R0] * [T] //http://www.euclideanspace.com/maths/geometry/affine/aroundPoint/
//    /*
//      translate the arbitrary point to the origin (subtract P which is translate by -Px,-Py,-Pz)
//      rotate about the origin (can use 3×3 matrix R0)
//      then translate back. (add P which is translate by +Px,+Py,+Pz)
//    */
//       translateMat.inverse();//step 1.. translate pointToRotate to origin

//       //(rotation matrix) - same as the triangle's "rotateMat" //step 2 rotate..

//       translateMat2.translate(v2NonNormalized.m_x,(v2NonNormalized.m_y),v2NonNormalized.m_z);//step3 ..translate pointToRotate back to its original position in 3d space

//       std::cout<<translateMat2.m_30<<","<<translateMat2.m_31<<","<<translateMat2.m_32<<std::endl;



//  //     std::cout<<"mat Matrix():\n"<<"  "<<rotateMat.m_00<<"  "<< rotateMat.m_01<<"  "<<rotateMat.m_02 <<"  "<<rotateMat.m_03<<"  "<<
//  //                                    rotateMat.m_10<<"  "<< rotateMat.m_11<<"  "<<rotateMat.m_12 <<"  "<<rotateMat.m_13<<"  "<< rotateMat.m_20<<"  "<< rotateMat.m_21<<"  "<<rotateMat.m_22 <<"  "<<rotateMat.m_23<<"  "<< rotateMat.m_30<<"  "<<
//  //                                    rotateMat.m_31<<"  "<<rotateMat.m_32 <<"  "<<rotateMat.m_33<<"  "<<std::endl;

//  //     std::cout<<angle<<std::endl;

//       //place one one sphere-primitive in the tip of the triangle, but we translate first and then rotate (this effectively shows the "trajectory of the triangle rotation")

//       /*
//        * In order to calculate the rotation about any arbitrary point we need to calculate its new rotation and translation.
//        * In other words rotation about a point is an 'proper' isometry transformation' which means that it has a linear
//        *  and a rotational component.
//        * [resulting transform] = [+Px,+Py,+Pz] * [rotation] * [-Px,-Py,-Pz]
//        */


//        M=  translateMat*rotateMat*translateMat2 /**scaleMat*/; //in ngl multiplication happens from left to right

//        M= M*m_mouseGlobalTX;
//        MV=  M*m_cam->getViewMatrix();
//        MVP= M*m_cam->getVPMatrix();
//        normalMatrix=MV;
//        normalMatrix.inverse();
//        shader->setShaderParamFromMat4("MV",MV);
//        shader->setShaderParamFromMat4("MVP",MVP);
//        shader->setShaderParamFromMat3("normalMatrix",normalMatrix);
//        shader->setShaderParamFromMat4("M",M);

////        ngl::VAOPrimitives::instance()->createSphere("mysphere",0.1,10);

//        ngl::VAOPrimitives::instance()->draw("cube");

//  }















//  //*********NOW********* STEP 2
//  //Calculate rotation vector from 2nd to 1st triangle
//  //then

//  m.set(ngl::BRONZE);
//  // load our material values to the shader into the structure material (see Vertex shader)
//  m.loadToShader("material");

//  //... rotate and draw the 2nd triangle as well
//  m_transform.reset();
//  {

////------------------------------------------------------------------------------------------------------------------------------------
////------------------------------------------------------------------------------------------------------------------------------------
///**************
////not working
////     eulerAngles.m_x = atan2( rotationAxis.m_y, rotationAxis.m_z );
////     if (rotationAxis.m_z >= 0) {
////        eulerAngles.m_y = -atan2( rotationAxis.m_x * cos(eulerAngles.m_x), rotationAxis.m_z );
////     }else{
////        eulerAngles.m_y = atan2( rotationAxis.m_x * cos(eulerAngles.m_x), -rotationAxis.m_z );
////     }

////     eulerAngles.m_z = atan2( cos(eulerAngles.m_x), sin(eulerAngles.m_x) * sin(eulerAngles.m_y) );


//     //
////     eulerAngles.m_x= 0;
////     eulerAngles.m_y = atan2((v1-v2).m_x, (v1-v2).m_z);
////     float padj = sqrt(pow((v1-v2).m_x, 2) + pow((v1-v2).m_z, 2));
////     eulerAngles.m_y = atan2(padj, (v1-v2).m_y) ;

//  **************/
//     //convert axis anle to euler angles
////     toEuler(rotationAxis.m_x, rotationAxis.m_y, rotationAxis.m_z, angle);
////     m_transform.setRotation(eulerAngles.m_x*(180/M_PI),eulerAngles.m_y*(180/M_PI),eulerAngles.m_z*(180/M_PI));
//       ngl::Mat4 trs=m_transform.getMatrix();
////------------------------------------------------------------------------------------------------------------------------------------
////------------------------------------------------------------------------------------------------------------------------------------



//    //The following work with ngl::Transformation too, as well as with individual matrices
//    //       m_transform.reset();
//    //       if(testangle<360)
//    //           testangle++;
//    //       else
//    //       {
//    //           testangle=0;
//    //       }

//    //       m_transform.setRotation(testangle,0,0);

//    //Rotate based where v1 is (make v2NonNormalized(triangle) to point towards v1-cube)
//     rotateMat=matrixFromAxisAngle(rotationAxis,angle);//m_transform.getMatrix();
//     translateMat.translate(v2NonNormalized.m_x,v2NonNormalized.m_y,v2NonNormalized.m_z);


////     std::cout<<"mat Matrix():\n"<<"  "<<rotateMat.m_00<<"  "<< rotateMat.m_01<<"  "<<rotateMat.m_02 <<"  "<<rotateMat.m_03<<"  "<<
////                                    rotateMat.m_10<<"  "<< rotateMat.m_11<<"  "<<rotateMat.m_12 <<"  "<<rotateMat.m_13<<"  "<< rotateMat.m_20<<"  "<< rotateMat.m_21<<"  "<<rotateMat.m_22 <<"  "<<rotateMat.m_23<<"  "<< rotateMat.m_30<<"  "<<
////                                    rotateMat.m_31<<"  "<<rotateMat.m_32 <<"  "<<rotateMat.m_33<<"  "<<std::endl;
////     std::cout<<angle<<std::endl;




////not quite working
//        float norm_u_norm_v = sqrt(v2NonNormalized.lengthSquared() * v1NonNormalized.lengthSquared());
//        ngl::Vec3 w = v2.cross(v1);
//        ngl::Quaternion q = ngl::Quaternion(norm_u_norm_v + v2NonNormalized.dot(v1NonNormalized), w.m_x, w.m_y, w.m_z);
//        q.normalise();
//        rotateMat=q.toMat4();

////not quite working either
//     rotateMat=ngl::lookAt(v2NonNormalized,v1NonNormalized,ngl::Vec3(0,1,0));



//      M=rotateMat*translateMat;//left to right multiplication in ngl (first rotate then translate)

//      M=/*m_transform.getMatrix()*/ M/* trs*/*m_mouseGlobalTX;
//      MV=  M*m_cam->getViewMatrix();
//      MVP= M*m_cam->getVPMatrix();
//      normalMatrix=MV;
//      normalMatrix.inverse();
//      shader->setShaderParamFromMat4("MV",MV);
//      shader->setShaderParamFromMat4("MVP",MVP);
//      shader->setShaderParamFromMat3("normalMatrix",normalMatrix);
//      shader->setShaderParamFromMat4("M",M);


//      m_vao->bind();
//      m_vao->draw();//draw triangle now
//      m_vao->unbind();
//  }


//  //*********NOW********* STEP 3

//  //draw the tip-cube of the triangle
//{
//  m.set(ngl::GOLD);
//  // load our material values to the shader into the structure material (see Vertex shader)
//  m.loadToShader("material");

//  translateMat=1;

////  rotateMat=1;
////  scaleMat=1;

////not working
////  ngl::Vec3 v=v1.cross(v2);
////  float c=v1.dot(v2);
////  float h=1-c/v.dot(v);
////  rotateMat=ngl::Mat4(c*h*v.m_x*v.m_x,                 h*v.m_x*v.m_y-v.m_z,           h*v.m_x*v.m_z+v.m_y, 1,
////                      h*v.m_x*v.m_y+v.m_z,             c+h*v.m_y*v.m_y,               h*v.m_y*v.m_z-v.m_x, 1,
////                      h*v.m_x*v.m_z-v.m_y,       h*v.m_y*v.m_z+v.m_x,                c+h*v.m_z*v.m_z,      1,
////                      0 ,                            0 ,                                     0,            1);
////  rotateMat.transpose();
////not working



////     ngl::Mat4 trs=m_transform.getMatrix();
////     rotateMat=matrixFromAxisAngle(rotationAxis,angle);
////     translateMat.translate(-v2NonNormalized.m_x,-(v2NonNormalized.m_y),-v2NonNormalized.m_z);

//  //Based on [R] = [T].inverse * [R0] * [T] //http://www.euclideanspace.com/maths/geometry/affine/aroundPoint/
//  /*
//    translate the arbitrary point to the origin (subtract P which is translate by -Px,-Py,-Pz)
//    rotate about the origin (can use 3×3 matrix R0)
//    then translate back. (add P which is translate by +Px,+Py,+Pz)
//  */
//     translateMat.inverse();//step 1.. translate pointToRotate to origin

//     //(rotation matrix) - same as the triangle's "rotateMat" //step 2 rotate..

//     translateMat2.translate(v2NonNormalized.m_x,(v2NonNormalized.m_y),v2NonNormalized.m_z);//step3 ..translate pointToRotate back to its original position in 3d space

//     std::cout<<translateMat2.m_30<<","<<translateMat2.m_31<<","<<translateMat2.m_32<<std::endl;



////     std::cout<<"mat Matrix():\n"<<"  "<<rotateMat.m_00<<"  "<< rotateMat.m_01<<"  "<<rotateMat.m_02 <<"  "<<rotateMat.m_03<<"  "<<
////                                    rotateMat.m_10<<"  "<< rotateMat.m_11<<"  "<<rotateMat.m_12 <<"  "<<rotateMat.m_13<<"  "<< rotateMat.m_20<<"  "<< rotateMat.m_21<<"  "<<rotateMat.m_22 <<"  "<<rotateMat.m_23<<"  "<< rotateMat.m_30<<"  "<<
////                                    rotateMat.m_31<<"  "<<rotateMat.m_32 <<"  "<<rotateMat.m_33<<"  "<<std::endl;

////     std::cout<<angle<<std::endl;

//     //place one one sphere-primitive in the tip of the triangle, but we translate first and then rotate (this effectively shows the "trajectory of the triangle rotation")

//     /*
//      * In order to calculate the rotation about any arbitrary point we need to calculate its new rotation and translation.
//      * In other words rotation about a point is an 'proper' isometry transformation' which means that it has a linear
//      *  and a rotational component.
//      * [resulting transform] = [+Px,+Py,+Pz] * [rotation] * [-Px,-Py,-Pz]
//      */


//      M=  translateMat*rotateMat*translateMat2 /**scaleMat*/; //in ngl multiplication happens from left to right

//      M= M*m_mouseGlobalTX;
//      MV=  M*m_cam->getViewMatrix();
//      MVP= M*m_cam->getVPMatrix();
//      normalMatrix=MV;
//      normalMatrix.inverse();
//      shader->setShaderParamFromMat4("MV",MV);
//      shader->setShaderParamFromMat4("MVP",MVP);
//      shader->setShaderParamFromMat3("normalMatrix",normalMatrix);
//      shader->setShaderParamFromMat4("M",M);

//      ngl::VAOPrimitives::instance()->createSphere("mysphere",0.1,10);

//      ngl::VAOPrimitives::instance()->draw("cube");

//}



}
//return shortest arc quaternion that rotates start to dest
 ngl::Quaternion NGLScene::RotationBetweenVectors(ngl::Vec3 start, ngl::Vec3  dest){

     ngl::Quaternion q;

     (start).normalize();

     (dest).normalize();



     float cosTheta = start.dot(dest);

     ngl::Vec3  rotationAxis;


     /**
      * https://bitbucket.org/sinbad/ogre/src/9db75e3ba05c/OgreMain/include/OgreVector3.h?fileviewer=file-view-default#cl-651
      *
      * Gets the shortest arc quaternion to rotate this vector to the destination
                vector.
            @remarks
                If you call this with a dest vector that is close to the inverse
                of this vector, we will rotate 180 degrees around the 'fallbackAxis'
                (if specified, or a generated axis if not) since in this case
                ANY axis of rotation is valid.
 */
     if (cosTheta >= 1.0f)//same vectors
     {
         return ngl::Quaternion();//identity quaternion
     }

     if (cosTheta < (1e-6f - 1.0f))
     {
         // Generate an axis
         rotationAxis = ngl::Vec3 (0.0f, 0.0f, 1.0f).cross( start);

         if (rotationAxis.length()==0) // pick another if colinear
             rotationAxis = ngl::Vec3 (0.0f, 1.0f, 0.0f).cross( start);

         rotationAxis.normalize();
         q.fromAxisAngle(rotationAxis,180.0f);
     }




 //    if (cosTheta < -1 + 0.001f)
 //    {

 //        // special case when vectors in opposite directions:

 //        // there is no "ideal" rotation axis

 //        // So guess one; any will do as long as it's perpendicular to start

 //        rotationAxis = ngl::Vec3 (0.0f, 0.0f, 1.0f).cross( start);

 //        float t=rotationAxis.lengthSquared();
 //        if (t< 0.01 ) // bad luck, they were parallel, try again!

 //            rotationAxis = ngl::Vec3 (1.0f, 0.0f, 0.0f).cross( start);


 //        (rotationAxis).normalize();

 //        q.fromAxisAngle(rotationAxis,180.0f);

 //        return q;
 //    }



     rotationAxis = start.cross(dest);



     float s = sqrt( (1+cosTheta)*2 );

     float invs = 1 / s;



     return ngl::Quaternion(

         s * 0.5f,

         rotationAxis.m_x * invs,

         rotationAxis.m_y * invs,

         rotationAxis.m_z * invs

     );


  }
Example #5
0
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 * TRACE ALGORITHM * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
ngl::Colour Renderer::trace(ngl::Vec3 _from, ngl::Vec3 _direction, int depth)
{
  // create vector that will store intersection values for parameter t in the primary ray
  // a primary ray has a form like R = O + t * D where O is the origin vector, and D direction.
  std::vector<double> intersections;
  geo::Ray cam_ray(_from,_direction);

  // iterate over objects in the scene and find intersections
  for(unsigned int i = 0; i < m_scene->m_objects.size(); i++)
  {
    // each Shape subclass (Sphere, Plane...) has its own method for calculating intersections
    intersections.push_back( m_scene->m_objects.at(i)->getIntersection(cam_ray));
  }

  // find closest object
  int closest_index = getIndexClosest(intersections);

  // if no intersections are found RETURN black =
  if(closest_index == -1) {return ngl::Colour(0,0,0,1);}

  // calculate pHit (position of the new intersection) and nHit (normal at hit point)
  ngl::Vec3 pHit = _from + intersections.at(closest_index) * _direction;
  ngl::Vec3 nHit = m_scene->m_objects.at(closest_index)->getNormalAt(pHit);

  // calculate if we are inside or outside
  bool inside = false;
  if(_direction.dot(nHit) > 0)
  {
    nHit = -nHit;
    inside = true;
  }

 float bias = 0.01;
 // calculate if point is obscured or shadowed
 bool isObscured = raycast(pHit + nHit * bias, closest_index);

                      // // // // // // // // // // // // //
                      //  put all contributions together  //
                      // // // // // // // // // // // // //

  // is the object reflective or refractive???
  if ((m_scene->m_objects.at(closest_index)->getMaterial()->isReflective() ||
      m_scene->m_objects.at(closest_index)->getMaterial()->isRefractive()) &&
      depth < m_max_depth)
  {
    ngl::Colour crfr(0,0,0,1);
    ngl::Colour crfl(0,0,0,1);
    // check whether it is REFLECTIVE
    if (m_scene->m_objects.at(closest_index)->getMaterial()->isReflective())
    {
      // calculate reflection dir
      float bias = 0.01;
      ngl::Vec3 refl_dir = _direction - nHit * 2 * _direction.dot(nHit);
      refl_dir.normalize();

      // fire ray along reflection direction from hit point
      crfl = trace(pHit + bias*nHit, refl_dir, depth+1);
    }

    // check whether it is REFRACTIVE
    if (m_scene->m_objects.at(closest_index)->getMaterial()->isRefractive())
    {
      // calculate refrection dir (transmission ray)
      float ior = m_scene->m_objects.at(closest_index)->getMaterial()->getIOR();
      float eta = inside;
      float bias = 0.01;
      float cosi = -nHit.dot(_direction);

      if (eta == true) // we are inside
      {
        eta = ior;
      }
      else // we are outside
      {
        eta = 1 / ior;
      }

      float k = 1 - eta * eta * (1 - cosi * cosi);
      ngl::Vec3 refr_dir = _direction * eta + nHit * (eta * cosi - sqrt(k));
      refr_dir.normalize();
      crfr = trace(pHit - nHit * bias, refr_dir, depth+1);
    }

    ngl::Colour surfaceColor = m_scene->m_objects.at(closest_index)->getColour(pHit);
    float cosineFactor = std::max(-nHit.dot(cam_ray.getDirection()),(float)0);
    float attenuation;

    ngl::Colour Ka(1,0,0.4,1);
    ngl::Colour Kd;
    ngl::Colour Ks;

    float ambient_intensity = 0.05;

    ngl::Colour ambient_contrib = Ka * surfaceColor * ambient_intensity;
    ngl::Colour diffuse_contrib(0,0,0,1);
    ngl::Colour specular_contrib(0,0,0,1);

    for(unsigned int m = 0; m < m_scene->m_lights.size(); m++)
    {
      ngl::Vec3 v_distance = m_scene->m_lights.at(m)->m_pos - pHit;
      float distance = v_distance.length();
      float radius = 8;
      attenuation = 1 - pow(distance/radius,2);

      Kd = m_scene->m_lights.at(m)->m_diff_col;
      Ks = m_scene->m_lights.at(m)->m_spec_col;

      ngl::Vec3 L = m_scene->m_lights.at(m)->m_pos - pHit;
      L.normalize();
      ngl::Vec3 N = nHit;
      ngl::Vec3 R = 2 * (L.dot(N) * N) - L;
      R.normalize();

      diffuse_contrib  += (surfaceColor * (Kd * pow(std::max(L.dot(N),(float)0),2) * m_scene->m_lights.at(m)->m_diff_int))*attenuation;
      specular_contrib += ((Ks * pow(std::max(R.dot(-_direction),(float)0),900)*400 * m_scene->m_lights.at(m)->m_spec_int))*attenuation;
    }

    specular_contrib.clamp(0,0.8);

    ngl::Colour s01 = crfl * m_scene->m_objects.at(closest_index)->getMaterial()->getReflIntensity();
    ngl::Colour s02 = crfr * m_scene->m_objects.at(closest_index)->getMaterial()->getTransparency();
    ngl::Colour s03 = s01 + s02;
    ngl::Colour diffuseColor = m_scene->m_objects.at(closest_index)->getColour(pHit) * cosineFactor * m_scene->m_objects.at(closest_index)->getMaterial()->getDiffuseIntensity();

    // Do PHONG MODEL calculations stuff. By now I keep it VERY VERY simple
    ngl::Colour outRadiance = diffuseColor + s03 + specular_contrib + ambient_contrib;
    outRadiance.clamp(0,1);

    return isObscured ? outRadiance * 0.7f : outRadiance;
  }

  // if it is not REFLECTIVE nor REFRACTIVE
  else
  {
    ngl::Colour surfaceColor = m_scene->m_objects.at(closest_index)->getColour(pHit);
    float attenuation;

    ngl::Colour Ka(1,0,0.4,1);
    ngl::Colour Kd;
    ngl::Colour Ks;

    float ambient_intensity = 0.05;

    ngl::Colour ambient_contrib = Ka * surfaceColor * ambient_intensity;
    ngl::Colour diffuse_contrib(0,0,0,1);
    ngl::Colour specular_contrib(0,0,0,1);

    for(unsigned int m = 0; m < m_scene->m_lights.size(); m++)
    {
      ngl::Vec3 v_distance = m_scene->m_lights.at(m)->m_pos - pHit;
      float distance = v_distance.length();
      float radius = 8;
      attenuation = 1 - pow(distance/radius,2);

      Kd = m_scene->m_lights.at(m)->m_diff_col;
      Ks = m_scene->m_lights.at(m)->m_spec_col;

      ngl::Vec3 L = m_scene->m_lights.at(m)->m_pos - pHit;
      L.normalize();
      ngl::Vec3 N = nHit;
      ngl::Vec3 R = 2 * (L.dot(N) * N) - L;
      R.normalize();

      float spec_hardness = m_scene->m_objects.at(closest_index)->getMaterial()->m_spec_hardness;

      diffuse_contrib  += (surfaceColor * (Kd * pow(std::max(L.dot(N),(float)0),2) * m_scene->m_lights.at(m)->m_diff_int))*attenuation;
      specular_contrib += ((Ks * pow(std::max(R.dot(-_direction),(float)0),spec_hardness) * m_scene->m_lights.at(m)->m_spec_int))*attenuation;
    }

    diffuse_contrib.clamp(0,1);
    specular_contrib.clamp(0,1);

    ngl::Colour outRadiance = diffuse_contrib + specular_contrib + ambient_contrib;

    outRadiance.clamp(0,1);

    return isObscured ? outRadiance * 0.7f : outRadiance;
  }
}