OOBB Creation/Intersection

Started by
22 comments, last by oliii 15 years, 11 months ago
Maybe I read your reply wrong, however, the way you are describing it makes it seem like you are just building an AABB around an OBB and testing the collision like that. Doesn't that defeat the purpose of an OBB?

Also, given the case that I read your post wrong, how could I find the dir/half-size vectors if I only have the min/max and the array of corners stored in my class?
Advertisement
if you want to do OOBB intersection, that's a whole new cattle of fish.


And yes, what the algos do is find the AABB (which can be useful, for fast culling and broad-phase CD).

to calculate the OOBB,

obb.centre = (max + min) * 0.5f;obb.halfsize = (max - min) * 0.5f;


as for the directions, in your case, it's straight forward. As I said, the dir vectors form a orthonormal matrix (orientation matrix), and the dir vectors are all columns of your orientation matrix (or rows, but I think Direct3D uses column major matrices).

obb.dir[0] = Vector(orientation.m11, orientation.m21, orientation.m31);obb.dir[1] = Vector(orientation.m12, orientation.m22, orientation.m32);obb.dir[2] = Vector(orientation.m13, orientation.m23, orientation.m33);


if you have the corners, it's easy. The directions are basically the direction of edges that are made up from corners. The half-size is the 1/2 length of the edges, and the centre pos is the middle point between two diagonally opposed corners.

I use a vector to store the halfsize here, instead of an array of three floats, but in effect, it's the same thing. In C++, you will often see vector class overloading the operator[] to take advantage of that, and the vector class looking like...

class Vector{   union   {       float coords[3];       struct       {           float x, y, z;       };   };   float operator[](int i) const { return coords; }   float& operator[](int i) { return coords; }      //.....};


now, for collison detection between OBB, you should use the SAT test. It's very easy, very fast, and very robust.

Everything is better with Metal.

What is the SAT test? I have heard of that before, however, I can not find anything on it.
Quote:Original post by Hurp
What is the SAT test? I have heard of that before, however, I can not find anything on it.
The term to search for is 'separating axis test'.
Hello, I have continued to work on this and while I think I am very close, I still get in accurate collision. My first problem I would think is with my directions. My directions (In DirectX) should be the following right?

m_vDirections[0] = D3DXVECTOR3(m_mOrientation._11, m_mOrientation._12, m_mOrientation._13);
m_vDirections[1] = D3DXVECTOR3(m_mOrientation._22, m_mOrientation._22, m_mOrientation._23);
m_vDirections[2] = D3DXVECTOR3(m_mOrientation._33, m_mOrientation._32, m_mOrientation._33);

I have tested both row major and column major with the below code (very similar to what oliii has shown in the past. Does anyone see a problem with it? The min of my box is the position.

bool COBB::CheckCollisionWithOBB(COBB *incBox){	if(!AxisOverlap(this->m_vDirections[0], this, incBox)) return true;	if(!AxisOverlap(this->m_vDirections[1], this, incBox)) return true;	if(!AxisOverlap(this->m_vDirections[2], this, incBox)) return true;	if(!AxisOverlap(incBox->m_vDirections[0], this, incBox)) return true;	if(!AxisOverlap(incBox->m_vDirections[0], this, incBox)) return true;	if(!AxisOverlap(incBox->m_vDirections[0], this, incBox)) return true;	D3DXVECTOR3 BoxOnePosition = D3DXVECTOR3(GetMin().m_fX, GetMin().m_fY, GetMin().m_fZ);	D3DXVECTOR3 BoxTwoPosition = D3DXVECTOR3(incBox->GetMin().m_fX, incBox->GetMin().m_fY, incBox->GetMin().m_fZ);	D3DXVECTOR3 Test1, Test2, Test3, Test4, Test5, Test6, Test7, Test8, Test9;	D3DXVec3Cross(&Test1, &this->m_vDirections[0], &incBox->m_vDirections[0]);	D3DXVec3Cross(&Test2, &this->m_vDirections[0], &incBox->m_vDirections[1]);	D3DXVec3Cross(&Test3, &this->m_vDirections[0], &incBox->m_vDirections[2]);	D3DXVec3Cross(&Test4, &this->m_vDirections[1], &incBox->m_vDirections[0]);	D3DXVec3Cross(&Test5, &this->m_vDirections[1], &incBox->m_vDirections[1]);	D3DXVec3Cross(&Test6, &this->m_vDirections[1], &incBox->m_vDirections[2]);	D3DXVec3Cross(&Test7, &this->m_vDirections[2], &incBox->m_vDirections[0]);	D3DXVec3Cross(&Test8, &this->m_vDirections[2], &incBox->m_vDirections[1]);	D3DXVec3Cross(&Test9, &this->m_vDirections[2], &incBox->m_vDirections[2]);		if(!AxisOverlap(Test1, this, incBox)) return true;	if(!AxisOverlap(Test2, this, incBox)) return true;	if(!AxisOverlap(Test3, this, incBox)) return true;	if(!AxisOverlap(Test4, this, incBox)) return true;	if(!AxisOverlap(Test5, this, incBox)) return true;	if(!AxisOverlap(Test6, this, incBox)) return true;	if(!AxisOverlap(Test7, this, incBox)) return true;	if(!AxisOverlap(Test8, this, incBox)) return true;	if(!AxisOverlap(Test9, this, incBox)) return true;	return false;}bool COBB::ExtentsOverlap(float min1, float max1, float min2, float max2){	return !(min1 > max2 || min2 > max1);}void COBB::ComputeBoxExtents(D3DXVECTOR3 Axis, COBB *box, float &min, float &max){	D3DXVECTOR3 BoxPosition = D3DXVECTOR3(box->GetMin().m_fX, box->GetMin().m_fY, box->GetMin().m_fZ);	Vector halfsize = (GetMax() - GetMin()) * 0.5f;	float p = D3DXVec3Dot(&BoxPosition, &Axis);	float r0 = fabs(D3DXVec3Dot(&box->m_vDirections[0], &Axis)) * halfsize.m_fX;	float r1 = fabs(D3DXVec3Dot(&box->m_vDirections[1], &Axis)) * halfsize.m_fY;	float r2 = fabs(D3DXVec3Dot(&box->m_vDirections[2], &Axis)) * halfsize.m_fZ;	float r = r0 + r1 + r2;	min = p-r;	max = p+r;}bool COBB::AxisOverlap(D3DXVECTOR3 Axis, COBB *box1, COBB *box2){	float min1, max1;	float min2, max2;		ComputeBoxExtents(Axis, box1, min1, max1);	ComputeBoxExtents(Axis, box2, min2, max2);	return ExtentsOverlap(min1, max1, min2, max2);}
I didn't look at the rest of the code, but it looks like you have a couple of errors here:
m_vDirections[0] = D3DXVECTOR3(m_mOrientation._11, m_mOrientation._12, m_mOrientation._13);m_vDirections[1] = D3DXVECTOR3(m_mOrientation._21, m_mOrientation._22, m_mOrientation._23);m_vDirections[2] = D3DXVECTOR3(m_mOrientation._31, m_mOrientation._32, m_mOrientation._33);
some things look wrong. What's in your OBB structure?

Everything is better with Metal.

jyk: What was wrong with that code?

oliii: Here is basically all of my code. The Rotation/Creation/Translation all work "correctly".

.h
#pragma onceclass COBB : public CCollisionInterface{private:	Vector3 m_vMin;	Vector3 m_vMax;	Vector3 m_vObjectBounds[8];	D3DXMATRIX m_mOrientation;	Vector3 m_vMove;	bool ExtentsOverlap(float min1, float max1, float min2, float max2);	void ComputeBoxExtents(D3DXVECTOR3 Axis, COBB *box, float &min, float &max);	bool AxisOverlap(D3DXVECTOR3 Axis, COBB *box1, COBB *box2);public:	COBB();	~COBB();	D3DXVECTOR3 m_vDirections[3];	void SetupCollisionBox(float fX, float fY, float fZ, float fWidth, float fHeight, float fDepth);	bool CheckCollisionWithOBB(COBB *incBox);	void Translate(float fX, float fY, float fZ);		void Rotate(float fX, float fY, float fZ);	COBB *GetCollisionBox() { return this;	}	Vector3 *GetObjectBounds() { return m_vObjectBounds; }	Vector3 GetMax() { return m_vMax; }	Vector3 GetMin() { return m_vMin; }	};#endif


.cpp
COBB::COBB(){	m_unCollisionType = OBB;	D3DXMatrixIdentity(&m_mOrientation);	m_vDirections[0] = D3DXVECTOR3(m_mOrientation._11, m_mOrientation._12, m_mOrientation._13);	m_vDirections[1] = D3DXVECTOR3(m_mOrientation._22, m_mOrientation._22, m_mOrientation._23);	m_vDirections[2] = D3DXVECTOR3(m_mOrientation._33, m_mOrientation._32, m_mOrientation._33);}COBB::~COBB(){}void COBB::SetupCollisionBox(float fX, float fY, float fZ, float fWidth, float fHeight, float fDepth){	m_vMin = Vector3(fX, fY, fZ);	m_vMax = Vector3(fX + fWidth, fY + fHeight, fZ + fDepth);	m_vObjectBounds[0] = Vector3D(m_vMin.GetX(), m_vMin.GetY(), m_vMin.GetZ());		// xyz	m_vObjectBounds[1] = Vector3D(m_vMax.GetX(), m_vMin.GetY(), m_vMin.GetZ());		// Xyz	m_vObjectBounds[2] = Vector3D(m_vMin.GetX(), m_vMax.GetY(), m_vMin.GetZ());		// xYz	m_vObjectBounds[3] = Vector3D(m_vMax.GetX(), m_vMax.GetY(), m_vMin.GetZ());		// XYz	m_vObjectBounds[4] = Vector3D(m_vMin.GetX(), m_vMin.GetY(), m_vMax.GetZ());		// xyZ	m_vObjectBounds[5] = Vector3D(m_vMax.GetX(), m_vMin.GetY(), m_vMax.GetZ());		// XyZ	m_vObjectBounds[6] = Vector3D(m_vMin.GetX(), m_vMax.GetY(), m_vMax.GetZ());		// xYZ	m_vObjectBounds[7] = Vector3D(m_vMax.GetX(), m_vMax.GetY(), m_vMax.GetZ());		// XYZ}bool COBB::CheckCollisionWithOBB(COBB *incBox){	if(!AxisOverlap(this->m_vDirections[0], this, incBox)) return true;	if(!AxisOverlap(this->m_vDirections[1], this, incBox)) return true;	if(!AxisOverlap(this->m_vDirections[2], this, incBox)) return true;	if(!AxisOverlap(incBox->m_vDirections[0], this, incBox)) return true;	if(!AxisOverlap(incBox->m_vDirections[0], this, incBox)) return true;	if(!AxisOverlap(incBox->m_vDirections[0], this, incBox)) return true;	D3DXVECTOR3 BoxOnePosition = D3DXVECTOR3(GetMin().m_fX, GetMin().m_fY, GetMin().m_fZ);	D3DXVECTOR3 BoxTwoPosition = D3DXVECTOR3(incBox->GetMin().m_fX, incBox->GetMin().m_fY, incBox->GetMin().m_fZ);	D3DXVECTOR3 Test1, Test2, Test3, Test4, Test5, Test6, Test7, Test8, Test9;	D3DXVec3Cross(&Test1, &this->m_vDirections[0], &incBox->m_vDirections[0]);	D3DXVec3Cross(&Test2, &this->m_vDirections[0], &incBox->m_vDirections[1]);	D3DXVec3Cross(&Test3, &this->m_vDirections[0], &incBox->m_vDirections[2]);	D3DXVec3Cross(&Test4, &this->m_vDirections[1], &incBox->m_vDirections[0]);	D3DXVec3Cross(&Test5, &this->m_vDirections[1], &incBox->m_vDirections[1]);	D3DXVec3Cross(&Test6, &this->m_vDirections[1], &incBox->m_vDirections[2]);	D3DXVec3Cross(&Test7, &this->m_vDirections[2], &incBox->m_vDirections[0]);	D3DXVec3Cross(&Test8, &this->m_vDirections[2], &incBox->m_vDirections[1]);	D3DXVec3Cross(&Test9, &this->m_vDirections[2], &incBox->m_vDirections[2]);		if(!AxisOverlap(Test1, this, incBox)) return true;	if(!AxisOverlap(Test2, this, incBox)) return true;	if(!AxisOverlap(Test3, this, incBox)) return true;	if(!AxisOverlap(Test4, this, incBox)) return true;	if(!AxisOverlap(Test5, this, incBox)) return true;	if(!AxisOverlap(Test6, this, incBox)) return true;	if(!AxisOverlap(Test7, this, incBox)) return true;	if(!AxisOverlap(Test8, this, incBox)) return true;	if(!AxisOverlap(Test9, this, incBox)) return true;	return false;}bool COBB::ExtentsOverlap(float min1, float max1, float min2, float max2){	return !(min1 > max2 || min2 > max1);}void COBB::ComputeBoxExtents(D3DXVECTOR3 Axis, COBB *box, float &min, float &max){	D3DXVECTOR3 BoxPosition = D3DXVECTOR3(box->GetMin().m_fX, box->GetMin().m_fY, box->GetMin().m_fZ);	Vector3 halfsize = (GetMax() - GetMin()) * 0.5f;	float p = D3DXVec3Dot(&BoxPosition, &Axis);	float r0 = fabs(D3DXVec3Dot(&box->m_vDirections[0], &Axis)) * halfsize.m_fX;	float r1 = fabs(D3DXVec3Dot(&box->m_vDirections[1], &Axis)) * halfsize.m_fY;	float r2 = fabs(D3DXVec3Dot(&box->m_vDirections[2], &Axis)) * halfsize.m_fZ;	float r = r0 + r1 + r2;	min = p-r;	max = p+r;}bool COBB::AxisOverlap(D3DXVECTOR3 Axis, COBB *box1, COBB *box2){	float min1, max1;	float min2, max2;		ComputeBoxExtents(Axis, box1, min1, max1);	ComputeBoxExtents(Axis, box2, min2, max2);	return ExtentsOverlap(min1, max1, min2, max2);}void COBB::Translate(float fX, float fY, float fZ){	m_vMax += Vector3D(fX, fY, fZ);	m_vMin += Vector3D(fX, fY, fZ);	m_vMove += Vector3D(fX, fY, fZ);	for(unsigned int i = 0; i < 8; ++i)		m_vObjectBounds += Vector3D(fX, fY, fZ);}void COBB::Rotate(float fX, float fY, float fZ){	D3DXMATRIX mRotation;	D3DXMatrixRotationYawPitchRoll(&mRotation, fY, fX, fZ);	m_mOrientation = mRotation;	m_vDirections[0] = D3DXVECTOR3(m_mOrientation._11, m_mOrientation._12, m_mOrientation._13);	m_vDirections[1] = D3DXVECTOR3(m_mOrientation._22, m_mOrientation._22, m_mOrientation._23);	m_vDirections[2] = D3DXVECTOR3(m_mOrientation._33, m_mOrientation._32, m_mOrientation._33);	//m_vDirections[0] = D3DXVECTOR3(m_mOrientation._11, m_mOrientation._21, m_mOrientation._31);	//m_vDirections[1] = D3DXVECTOR3(m_mOrientation._12, m_mOrientation._22, m_mOrientation._32);	//m_vDirections[2] = D3DXVECTOR3(m_mOrientation._13, m_mOrientation._23, m_mOrientation._33);	D3DXVECTOR3 vInput[8];	D3DXVECTOR3 CoR = D3DXVECTOR3(m_vMin.GetX(), m_vMin.GetY(), m_vMin.GetZ()); // centre of rotation?	for(int i = 0; i < 8; i ++)	{		vInput = D3DXVECTOR3(m_vObjectBounds.GetX(), m_vObjectBounds.GetY(), m_vObjectBounds.GetZ());		vInput -= CoR;		D3DXVec3TransformCoord(&vInput, &vInput, &mRotation);		vInput = vInput + CoR;		m_vObjectBounds = Vector3D(vInput.x, vInput.y, vInput.z);;	}	//m_vMin = m_vMax = corner[0];	m_vMin = Vector3(vInput[0].x, vInput[0].y, vInput[0].z);	m_vMax = m_vMin;	for(int i = 1; i < 8; i ++)	{		if(vInput.x < m_vMin.GetX()) 			m_vMin.SetX(vInput.x);		else if(vInput.x > m_vMax.GetX()) 			m_vMax.SetX(vInput.x); 		if(vInput.y < m_vMin.GetY()) 			m_vMin.SetY(vInput.y); 		else if(vInput.y > m_vMax.GetY()) 			m_vMax.SetY(vInput.y); 		if(vInput.z < m_vMin.GetZ()) 			m_vMin.SetZ(vInput.z); 		else if(vInput.z > m_vMax.GetZ()) 			m_vMax.SetZ(vInput.z); 	}}
your matrix axes are wrong...

either this
	m_vDirections[0] = D3DXVECTOR3(m_mOrientation._11, m_mOrientation._21, m_mOrientation._31);	m_vDirections[1] = D3DXVECTOR3(m_mOrientation._12, m_mOrientation._22, m_mOrientation._32);	m_vDirections[2] = D3DXVECTOR3(m_mOrientation._13, m_mOrientation._23, m_mOrientation._33);


or this

	m_vDirections[0] = D3DXVECTOR3(m_mOrientation._11, m_mOrientation._12, m_mOrientation._13);	m_vDirections[1] = D3DXVECTOR3(m_mOrientation._21, m_mOrientation._22, m_mOrientation._23);	m_vDirections[2] = D3DXVECTOR3(m_mOrientation._31, m_mOrientation._32, m_mOrientation._33);


try this for the compute box extents

void COBB::ComputeBoxExtents(D3DXVECTOR3 Axis, COBB *box, float &min, float &max){	D3DXVECTOR3 BoxPosition = D3DXVECTOR3(box->GetMin().m_fX, box->GetMin().m_fY, box->GetMin().m_fZ);	Vector3 halfsize = (GetMax() - GetMin()) * 0.5f;	Vector3 centre   = (GetMax() + GetMin()) * 0.5f;	float p = D3DXVec3Dot(&centre, &Axis);	float r0 = fabs(D3DXVec3Dot(&box->m_vDirections[0], &Axis)) * halfsize.m_fX;	float r1 = fabs(D3DXVec3Dot(&box->m_vDirections[1], &Axis)) * halfsize.m_fY;	float r2 = fabs(D3DXVec3Dot(&box->m_vDirections[2], &Axis)) * halfsize.m_fZ;	float r = r0 + r1 + r2;	min = p-r;	max = p+r;}


but really, you're obb is just wrong... it should be a centre position, an halfsize vector, and three directional vectors. Using min-max will just add too many complications.

Everything is better with Metal.

your matrix axes are wrong...

either this
	m_vDirections[0] = D3DXVECTOR3(m_mOrientation._11, m_mOrientation._21, m_mOrientation._31);	m_vDirections[1] = D3DXVECTOR3(m_mOrientation._12, m_mOrientation._22, m_mOrientation._32);	m_vDirections[2] = D3DXVECTOR3(m_mOrientation._13, m_mOrientation._23, m_mOrientation._33);


or this

	m_vDirections[0] = D3DXVECTOR3(m_mOrientation._11, m_mOrientation._12, m_mOrientation._13);	m_vDirections[1] = D3DXVECTOR3(m_mOrientation._21, m_mOrientation._22, m_mOrientation._23);	m_vDirections[2] = D3DXVECTOR3(m_mOrientation._31, m_mOrientation._32, m_mOrientation._33);


try this for the compute box extents

void COBB::ComputeBoxExtents(D3DXVECTOR3 Axis, COBB *box, float &min, float &max){	D3DXVECTOR3 BoxPosition = D3DXVECTOR3(box->GetMin().m_fX, box->GetMin().m_fY, box->GetMin().m_fZ);	Vector3 halfsize = (GetMax() - GetMin()) * 0.5f;	Vector3 centre   = (GetMax() + GetMin()) * 0.5f;	float p = D3DXVec3Dot(&centre, &Axis);	float r0 = fabs(D3DXVec3Dot(&box->m_vDirections[0], &Axis)) * halfsize.m_fX;	float r1 = fabs(D3DXVec3Dot(&box->m_vDirections[1], &Axis)) * halfsize.m_fY;	float r2 = fabs(D3DXVec3Dot(&box->m_vDirections[2], &Axis)) * halfsize.m_fZ;	float r = r0 + r1 + r2;	min = p-r;	max = p+r;}


but really, you're obb is just wrong... it should be a centre position, an halfsize vector, and three directional vectors. Using min-max will just add too many complications. I really cant understand how you transform works at all. The min-max are just there in object space. When you object moves in the world, the aabb then becomes on obb like this one :

#pragma onceclass COBB : public CCollisionInterface{public:	COBB(const Vector& min, const Vector max);	~COBB();	bool CheckCollisionWithOBB(COBB *incBox);		void SetOrientation(float fX, float fY, float fZ);	void SetOrientation(float pitch, float yaw, float roll);	COBB *GetCollisionBox() { return this;	}	Vector3 *GetObjectBounds() { return m_vObjectBounds; }private:	bool ExtentsOverlap(float min1, float max1, float min2, float max2);	void ComputeBoxExtents(const Vector3& Axis, float &min, float &max);	bool AxisOverlap(D3DXVECTOR3 Axis, COBB *box1, COBB *box2);	void ComputeBounds();	void ComputeCentre();	Vector3     m_vDirections[3];	Vector3     m_vCentre;	Vector3     m_vHalfSize;	Vector3     m_vObjectBounds[8];	Vector3     m_vOffset;	Vector3     m_vPosition;};



COBB::COBB(const Vector3& min, const Vector3& max){	m_vDirections[0]  = Vector3(1, 0, 0);	m_vDirections[1]  = Vector3(0, 1, 0);	m_vDirections[2]  = Vector3(0, 0, 1);	m_vHalfSize       = (max - min) * 0.5f;	m_vOffset         = (max + min) * 0.5f;	m_vPosition       = Vector3(0, 0, 0);	ComputeCentre();	ComputeBounds();}void COBB::ComputeCentre(){	m_vCentre  = m_vPosition;	m_vCentre += m_vDirections[0] * m_vOffset.m_fX;	m_vCentre += m_vDirections[1] * m_vOffset.m_fY;	m_vCentre += m_vDirections[2] * m_vOffset.m_fZ;}void COBB::ComputeBounds(){	static const Vector3 s_corners[8] = 	{ 		Vector3(-1, -1, -1), 		Vector3(-1, -1,  1), 		Vector3( 1, -1,  1), 		Vector3( 1, -1, -1), 		Vector3(-1,  1, -1), 		Vector3(-1,  1,  1), 		Vector3( 1,  1,  1), 		Vector3( 1,  1, -1), 	};	for(int i = 0; i < 8; i ++)	{		m_vObjectBounds  = m_vCentre;		m_vObjectBounds += m_vDirections[0] * m_vHalfSize.m_fX * s_corners.m_fX;		m_vObjectBounds += m_vDirections[1] * m_vHalfSize.m_fY * s_corners.m_fY;		m_vObjectBounds += m_vDirections[2] * m_vHalfSize.m_fZ * s_corners.m_fZ;	}}void COBB::SetPosition(float fX, float fY, float fZ){	m_vPosition = Vector3(fX, fY, fZ);	ComputeCentre();	ComputeBounds();}void COBB::SetOrientation(float pitch, float yaw, float roll){	D3DXMATRIX mRotation;	D3DXMatrixRotationYawPitchRoll(&mRotation, yaw, pitch, roll);	m_vDirections[0] = Vector3(mRotation._11, mRotation._21, mRotation._31);	m_vDirections[1] = Vector3(mRotation._12, mRotation._22, mRotation._32);	m_vDirections[2] = Vector3(mRotation._13, mRotation._23, mRotation._33);	ComputeCentre();	ComputeBounds();}void COBB::ComputeBoxExtents(const Vector3& Axis, float &min, float &max){	float p  = Axis.DotProduct(m_vCentre);	float r0 = fabs(Axis.DotProduct(m_vDirections[0]) * m_vHalfSize.m_fX;	float r1 = fabs(Axis.DotProduct(m_vDirections[1]) * m_vHalfSize.m_fY;	float r2 = fabs(Axis.DotProduct(m_vDirections[2]) * m_vHalfSize.m_fZ;	float r = r0 + r1 + r2;	min = p-r;	max = p+r;}bool COBB::AxisOverlap(const Vector3& Axis, COBB *box1, COBB *box2){	float axis_length_squared = Axis.DotProduct(Axis);	// check for degenerate axes. skip the test.	if(axis_length_squared < 1.0E-6f)		return true;	float min1, max1;	float min2, max2;		box1->ComputeBoxExtents(Axis, min1, max1);	box2->ComputeBoxExtents(Axis, min2, max2);	return ExtentsOverlap(min1, max1, min2, max2);}

Everything is better with Metal.

This topic is closed to new replies.

Advertisement