프로그래밍 2016. 2. 18. 02:42
728x90

obb 충돌 구현 2

 

 

obb 충돌 체크를 위해서는 바운드박스와 더불어,

 

세 축의 x,y,z

세 축의 길이값

축의 중심 포지션( 월드포지션 )

을 가지고 있는

obbbox 구조체가 필요하다.

 

class COBBBox
{
  public:
     COBBBox()
     {
          center[0] = center[1] = center[2] = 0.f;
          extent[0] = extent[1] = extent[2] = 0.f;

  

    // identity world coordinate axis
    axis[0][0] = 1.0F; axis[0][1] = 0.0F; axis[0][2] = 0.0F;
    axis[1][0] = 0.0F; axis[1][1] = 1.0F; axis[1][2] = 0.0F;
    axis[2][0] = 0.0F; axis[2][1] = 0.0F; axis[2][2] = 1.0F;

     }

     ~COBBBox()
     {

     }

 

     void CalcOBBBox( const Vector3 _vLocalMin, const Vector3 _vLocalMax,

const Vector3 _vScale, zMatrix* _pmatRT );

 

public:
     float center[3]; //월드중심 포지션
     float extent[3]; //세축의 길이
     float axis[3][3]; //세축의 x,y,z

 

};

 

void
COBBBox::CalcOBBBox( const Vector3 _vLocalMin, const Vector3 _vLocalMax, const Vector3 _vScale, zMatrix* _pmatRT )
{
 

     Vector3 vC = (_vLocalMax + _vLocalMin) * 0.5f;

     

//local extent
extent[0] = (_vLocalMax.x - vC.x) * _vScale.x;
extent[1] = (_vLocalMax.y - vC.y) * _vScale.y;
extent[2] = (_vLocalMax.z - vC.z) * _vScale.z;

 

      //world pos
      center[0] = _pmatRT->_41;
      center[1] = _pmatRT->_42;
      center[2] = _pmatRT->_43;

 

      //world axis
      //분리축
      //회전만이 적용된 매트릭스값을 축으로 사용한다
      //매트릭스에 포지션값이 들어있어도 41,42,43 값은 사용되지 않는다.
      //스케일값이 매트릭스에 적용되어 있으면, 분리축에 대한 충돌검사가 제대로 되지 않는다
      //따라서 RT 매트릭스를 사용한다.

      for( int i=0; i<3; i++ )
      {
           for( int j=0; j<3; j++ )
           {
                axis[i][j] = (*_pmatRT)( i, j );
           }
      }
}

 

obb box 생성은 대략 아래 코드와 같다.

 

_mOBB.CalcOBBBox( 바운드박스의 min(로컬값),

 바운드박스의 max(로컬값),

 오브젝트의 스케일 벡터,

 오브젝트의 TM 매트릭스

 ( 스케일 적용이 빠진, 회전과 이동값만이 포함된  월드 매트릭스) 

);

 

바운드박스를 가진 오브젝트의 월드변환 매트릭스와 포지션, 스케일값으로 obb box 를 생성할수 있다.

이때 주의할 점은 obb box 의 분리축은 회전에 대한 매트릭스값만으로 구성된다는 것이다.

월드변환 매트릭스를 구성할때, 스케일값이 들어가면, 매트릭스에서의 회전값에 영향을 주기 때문에, 스케일값은 별로도 처리해 주어야 한다.

스케일값은 축의 길이에 곱해서 사용하면 된다.

 

 

참고로,

 

zMatrix matAABB;

zMatrixIdentity( &matAABB );

matAABB._41 = pos.x;

matAABB._42 = pos.y;

matAABB._43 = pos.z;

 

오브젝트의 TM 매트릭스를, 회전값을 빼고 위와 같이 사용하면, AABB 충돌 체크와 동일해 진다.

필요에 따라 캐릭터는 위와 같이 사용할 일이 생길수도 있다. 

(실제로는 캐릭터가 회전을 하지만, 바운드는 aabb 형식으로 사용하고 싶을때 )

 

 

obb 충돌이 필요한 오브젝트들의 obb box 를 생성해 두고,

(정적인 오브젝트들은 값이 변하지 않기 때문에 한번만 구해두면 된다. )

OBB VS OBB 충돌 체크를 수행해주면 된다.

 

*obb 충돌 체크 코드

 

float FDotProduct( const float v0[3], const float v1[3] )
{
     return v0[0]*v1[0] + v0[1]*v1[1] + v0[2]*v1[2];
}

 

//center - 월드위치의 중심값
//extent - 로컬에서 계산된 x,y,z 축의 길이값
//axis - 회전이 적용된 월드 변환된 축
int OBB2OBBIntersection( const COBBBox& box0, const COBBBox& box1 )
{
     // compute difference of box centers,D=C1-C0
     float D[3] =
     {
          box1.center[0] - box0.center[0],
          box1.center[1] - box0.center[1],
          box1.center[2] - box0.center[2]
     };
 
     float C[3][3];    //matrix C=A^T B,c_{ij}=Dot(A_i,B_j)
     float absC[3][3]; //|c_{ij}|
     float AD[3];      //Dot(A_i,D)
     float R0,R1,R;    //interval radii and distance between centers
     float R01;        //=R0+R1
 
     //A0
     C[0][0] = FDotProduct( box0.axis[0], box1.axis[0] );
     C[0][1] = FDotProduct( box0.axis[0], box1.axis[1] );
     C[0][2] = FDotProduct( box0.axis[0], box1.axis[2] );
     AD[0] = FDotProduct( box0.axis[0], D );
     absC[0][0] = (float)fabsf(C[0][0]);
     absC[0][1] = (float)fabsf(C[0][1]);
     absC[0][2] = (float)fabsf(C[0][2]);
     R = (float)fabsf(AD[0]);
     R1 = box1.extent[0] * absC[0][0] + box1.extent[1] * absC[0][1] + box1.extent[2] * absC[0][2];
     R01 = box0.extent[0] + R1;
     if( R > R01 )
         return 0;
 
     //A1
     C[1][0] = FDotProduct( box0.axis[1], box1.axis[0] );
     C[1][1] = FDotProduct( box0.axis[1], box1.axis[1] );
     C[1][2] = FDotProduct( box0.axis[1], box1.axis[2] );
     AD[1] = FDotProduct( box0.axis[1], D );
     absC[1][0] = (float)fabsf(C[1][0]);
     absC[1][1] = (float)fabsf(C[1][1]);
     absC[1][2] = (float)fabsf(C[1][2]);
     R = (float)fabsf(AD[1]);
     R1 = box1.extent[0] * absC[1][0] + box1.extent[1] * absC[1][1] + box1.extent[2] *     absC[1][2];
     R01 = box0.extent[1] + R1;
     if( R > R01 ) 
          return 0;
 
     //A2
     C[2][0] = FDotProduct( box0.axis[2], box1.axis[0] );
     C[2][1] = FDotProduct( box0.axis[2], box1.axis[1] );
     C[2][2] = FDotProduct( box0.axis[2], box1.axis[2] );
     AD[2] = FDotProduct( box0.axis[2], D );
     absC[2][0] = (float)fabsf(C[2][0]);
     absC[2][1] = (float)fabsf(C[2][1]);
     absC[2][2] = (float)fabsf(C[2][2]);
     R = (float)fabsf(AD[2]);
     R1 = box1.extent[0] * absC[2][0] + box1.extent[1] * absC[2][1] + box1.extent[2] * absC[2][2];
     R01 = box0.extent[2] + R1;
     if( R > R01 )
          return 0;

 

     //B0
     R = (float)fabsf( FDotProduct(box1.axis[0], D) );
     R0 = box0.extent[0] * absC[0][0] + box0.extent[1] * absC[1][0] + box0.extent[2] * absC[2][0];
     R01 = R0 + box1.extent[0];
     if( R > R01 )
          return 0;
 
     //B1
     R = (float)fabsf( FDotProduct(box1.axis[1], D) );
     R0 = box0.extent[0] * absC[0][1] + box0.extent[1] * absC[1][1] + box0.extent[2] * absC[2][1];
     R01 = R0 + box1.extent[1];
     if( R > R01 ) 
          return 0;
 
     //B2
     R = (float)fabsf( FDotProduct(box1.axis[2],D) );
     R0 = box0.extent[0] * absC[0][2] + box0.extent[1] * absC[1][2] + box0.extent[2] * absC[2][2];
     R01 = R0 + box1.extent[2];
     if( R > R01 )
          return 0;
 
     //A0xB0
     R = (float)fabsf(AD[2]*C[1][0] - AD[1]*C[2][0]);
     R0 = box0.extent[1] * absC[2][0] + box0.extent[2] * absC[1][0];
     R1 = box1.extent[1] * absC[0][2] + box1.extent[2] * absC[0][1];
     R01 = R0 + R1;
     if( R > R01 )
          return 0;
 
     //A0xB1
     R = (float)fabsf(AD[2]*C[1][1] - AD[1]*C[2][1]);
     R0 = box0.extent[1] * absC[2][1] + box0.extent[2] * absC[1][1];
     R1 = box1.extent[0] * absC[0][2] + box1.extent[2] * absC[0][0];
     R01 = R0 + R1;
     if( R > R01 )
          return 0;
 
     //A0xB2
     R = (float)fabsf(AD[2]*C[1][2] - AD[1]*C[2][2]);
     R0 = box0.extent[1] * absC[2][2] + box0.extent[2] * absC[1][2];
     R1 = box1.extent[0] * absC[0][1] + box1.extent[1] * absC[0][0];
     R01 = R0+R1;
     if( R > R01 )
          return 0;
 
     //A1xB0
     R = (float)fabsf(AD[0]*C[2][0] - AD[2]*C[0][0]);
     R0 = box0.extent[0] * absC[2][0] + box0.extent[2] * absC[0][0];
     R1 = box1.extent[1] * absC[1][2] + box1.extent[2] * absC[1][1];
     R01 = R0 + R1;
     if( R > R01 )
          return 0;
 
     //A1xB1
     R = (float)fabsf(AD[0]*C[2][1] - AD[2]*C[0][1]);
     R0 = box0.extent[0] * absC[2][1] + box0.extent[2] * absC[0][1];
     R1 = box1.extent[0] * absC[1][2] + box1.extent[2] * absC[1][0];
     R01 = R0 + R1;
     if( R > R01 )
          return 0;
 
     //A1xB2
     R = (float)fabsf(AD[0]*C[2][2] - AD[2]*C[0][2]);
     R0 = box0.extent[0] * absC[2][2] + box0.extent[2] * absC[0][2];
     R1 = box1.extent[0] * absC[1][1] + box1.extent[1] * absC[1][0];
     R01 = R0 + R1;
     if( R > R01 )
          return 0;
 
     //A2xB0
     R = (float)fabsf(AD[1]*C[0][0] - AD[0]*C[1][0]);
     R0 = box0.extent[0] * absC[1][0] + box0.extent[1] * absC[0][0];
     R1 = box1.extent[1] * absC[2][2] + box1.extent[2] * absC[2][1];
     R01 = R0 + R1;
     if( R > R01 )
          return 0;
 
     //A2xB1
     R = (float)fabsf(AD[1]*C[0][1] - AD[0]*C[1][1]);
     R0 = box0.extent[0]*absC[1][1]+box0.extent[1]*absC[0][1];
     R1 = box1.extent[0]*absC[2][2]+box1.extent[2]*absC[2][0];
     R01 = R0 + R1;
     if( R > R01 )
          return 0;
 
     //A2xB2
     R = (float)fabsf(AD[1]*C[0][2] - AD[0]*C[1][2]);
     R0 = box0.extent[0] * absC[1][2] + box0.extent[1] * absC[0][2];
     R1 = box1.extent[0] * absC[2][1] + box1.extent[1] * absC[2][0];
     R01 = R0 + R1;
     if( R > R01 )
          return 0;
 
     return 1;
}

 

충돌 코드는 인터넷에서 발췌하였다.

충돌 코드에 대한 설명은 밑 주소를 참고하자.

http://www.gingaminga.com/Data/Note/oriented_bounding_boxes/

 

 

위 코드들을 이용하면,

충돌박스면을 따라 정확하게 충돌이 검출된다.

 

충돌이 검출되면,

충돌된 바운드박스면을 알아내어, 면의 노말을 얻어와서( 평면의 방정식을 이용)

진행방향과 면의 노말을 이용하여 슬라이딩 벡터를 생성하여,

면을 따라 이동하게 만들수 있다.

이동 처리 부분은 기회가 있으면, 설명을 올리겠다.

 

밑의 스샷들은 테스트용 스샷들인데,

뭐, 특정한 표시같은 것은 나오지 않는다. 그냥 눈요기로 추가 ^^;;

 

 

충돌 테스트용으로 배치한 오브젝트들

회전과 스케일을 다양하게 적용하였다.

 

 

울타리도 테스트 해보고,

 

 

 

이것은 키를 넘는 울타리

 

 

 

 

우옷! 막혀서 나갈수가 없다!

당연하지~

 

 

 

 

 

역시 막혔어~~

 

 

 

 

 

벽을 따라 스무드하게 이동된다.

 

 

 

 

728x90

'프로그래밍' 카테고리의 다른 글

오브젝트 바닥 충돌 처리  (3) 2016.04.18
흑백 쉐이더, 그레이 쉐이더  (0) 2016.03.30
obb 충돌 구현 1  (2) 2016.02.18
vsm with cascade shadow map 2  (0) 2016.01.26
vsm with cascade shadow map 1  (0) 2016.01.25
posted by BK dddDang
: