Jump to content
  • Advertisement
isu diss

Add 'ground' to this physics system?

Recommended Posts

I've been trying to build a physics engine for my game. the problem is I can't simulate the ground. The physics engine is written things I know and I'm trying to extend Impulse engine by Randy Gaul.

Plase help me?

Rigidbody.cpp

#include "RigidBody.h"


RigidBody::RigidBody(AABB *b, float M, float SF, float DF, float E, XMMATRIX IBody, XMVECTOR x0, XMVECTOR v0, XMVECTOR L0, XMMATRIX R0)
{
	box = b;
	Mass = M;
	Static_Friction = SF;
	Dynamic_Friction = DF;
	Restitution = E;
	IBodyInverse = XMMatrixInverse(NULL, IBody);
	x = x0;
	v = v0;
	L = L0;
	I0_Inverse = XMMatrixMultiply(XMMatrixTranspose(R0), IBodyInverse);
	I0_Inverse = XMMatrixMultiply(I0_Inverse, R0);
	//IInverse = XMMatrixIdentity();
	R = XMMatrixIdentity();
	Omega = XMVector4Transform(L0, I0_Inverse);
	q = XMQuaternionRotationMatrix(R0);
	F_Total = XMVectorSet(0, 0, 0, 0);
	Tau_Total = XMVectorSet(0, 0, 0, 0);
}

float RigidBody::GetStaticFriction()
{
	return Static_Friction;
}

float RigidBody::GetDynamicFriction()
{
	return Dynamic_Friction;
}

float RigidBody::GetRestitution()
{
	return Restitution;
}

float RigidBody::GetMass()
{
	return Mass;
}

XMMATRIX RigidBody::GetIInverse()
{
	return IInverse;
}
/*
void RigidBody::SetPosition(XMVECTOR Pos)
{
	x = Pos;
}
*/
void RigidBody::SetPosition(XMVECTOR tmp)
{
	x += tmp;
}

XMVECTOR RigidBody::GetPosition()
{
	return x;
}


XMMATRIX RigidBody::GetOrientation()
{
	return R;
}

XMVECTOR RigidBody::GetVelocityAtPoint(XMVECTOR p)
{
	return (v + XMVector3Cross(Omega, (p - x)));
}

XMVECTOR RigidBody::GetAngularVelocity()
{
	return Omega;
}

void RigidBody::AddForce(XMVECTOR Force)
{
	F_Total += Force;
}

void RigidBody::AddImpulse(XMVECTOR Impulse)
{
	v += Impulse*(1/Mass);
}

void RigidBody::AddTorque(XMVECTOR Torque)
{
	Tau_Total += Torque;
}

void RigidBody::AddImpulsiveTorque(XMVECTOR ImpulsiveTorque)
{
	Omega += XMVector4Transform(ImpulsiveTorque, IInverse);
}


void RigidBody::UpdateAABB()
{	
	float C[3], R[3];
	float Center[3], Radius[3];

	if (boxupdated)
	{
		delete boxupdated;
		boxupdated = NULL;
	}

	float Orientation[3][3], Translation[3];
	for (int i=0; i<3; i++)
	{
		for (int j=0; j<3; j++)
		{
			Orientation[i][j] = GetOrientation().r[i].m128_f32[j];
		}
	}
	Translation[0] = GetPosition().m128_f32[0];
	Translation[1] = GetPosition().m128_f32[1];
	Translation[2] = GetPosition().m128_f32[2];

	Center[0] = box->GetCenter().x;
	Center[1] = box->GetCenter().y;
	Center[2] = box->GetCenter().z;

	Radius[0] = box->GetRadius().x;
	Radius[1] = box->GetRadius().y;
	Radius[2] = box->GetRadius().z;

	for (int i=0; i<3; i++)
	{
		C[i] = Translation[i];
		R[i] = 0.0f;
		for (int j=0; j<3; j++)
		{
			C[i] += Orientation[i][j] * Center[j];
			R[i] += abs(Orientation[i][j]) * Radius[j];
		}
	}

	boxupdated = new AABB(XMFLOAT3(C[0],C[1],C[2]), XMFLOAT3(R[0], R[1], R[2]));

}

void RigidBody::Update(float h)
{
	x += h*v;
	v += h*(F_Total/Mass);
	XMVECTOR Omegaq = XMQuaternionMultiply(q, XMVectorSet(Omega.m128_f32[0], Omega.m128_f32[1], Omega.m128_f32[2], 0));
	q += 0.5f*h*Omegaq;
	L += h*Tau_Total;
	q = XMQuaternionNormalize(q);
	R = XMMatrixRotationQuaternion(q);
	IInverse = XMMatrixMultiply(XMMatrixTranspose(R), IBodyInverse);
	IInverse = XMMatrixMultiply(IInverse, R);
	Omega = XMVector4Transform(L, IInverse);
	UpdateAABB();
	F_Total = XMVectorSet(0, 0, 0, 0);
	Tau_Total = XMVectorSet(0, 0, 0, 0);
}

RigidBody::~RigidBody(void)
{
	
}

Manifold.cpp

#include "Manifold.h"

Manifold::Manifold(RigidBody *a, RigidBody *b)
{
	A = a;
	B = b;
	contact_count=0;

}

vector<XMFLOAT3> Manifold::SHClipPolygon(vector<XMFLOAT3>& Polygon, Plane& refPlane)
{
	vector<XMFLOAT3> Out;
	
	XMVECTOR Vertex1 = XMLoadFloat3(&Polygon.back());
	float Distance1 = XMVector3Dot(Vertex1, XMLoadFloat3(&refPlane.GetNormal())).m128_f32[0] - refPlane.GetOffset();
	
	for (int Index = 0; Index < Polygon.size(); Index++)
	{
		XMVECTOR Vertex2 = XMLoadFloat3(&Polygon[Index]);

		float Distance2 = XMVector3Dot(Vertex2, XMLoadFloat3(&refPlane.GetNormal())).m128_f32[0] - refPlane.GetOffset();
	
		if ( Distance1 <= 0.0f && Distance2 <= 0.0f )
		{
			// Both vertices are behind the plane - keep vertex2
			XMFLOAT3 tmp;
			XMStoreFloat3(&tmp, Vertex2);
			Out.push_back(tmp);
		}
		else if ( Distance1 <= 0.0f && Distance2 > 0.0f )
		{
			// Vertex1 is behind of the plane, vertex2 is in front -> intersection point
			float Fraction = Distance1 / ( Distance1 - Distance2 );
			XMVECTOR IntersectionPoint = Vertex1 + Fraction * (Vertex2 - Vertex1);

			// Keep intersection point
			XMFLOAT3 tmp;
			XMStoreFloat3(&tmp, IntersectionPoint);
			Out.push_back(tmp);
		}
		else if ( Distance2 <= 0.0f && Distance1 > 0.0f )
		{
			// Vertex2 is behind of the plane, vertex1 is in front -> intersection point
			float Fraction = Distance1 / ( Distance1 - Distance2 );
			XMVECTOR IntersectionPoint = Vertex1 + Fraction * (Vertex2 - Vertex1);
	
			// Keep intersection point
			XMFLOAT3 tmp1, tmp2;
			XMStoreFloat3(&tmp1, IntersectionPoint);
			Out.push_back(tmp1);
	
			// And also keep vertex2
			XMStoreFloat3(&tmp2, Vertex2);
			Out.push_back(tmp2);
		}
	
		// Keep vertex2 as starting vertex for next edge
		Vertex1 = Vertex2;
		Distance1 = Distance2;
	}
	return Out;
}

bool Manifold::TestAxis(XMVECTOR axis, XMVECTOR& mtd, float& mtd2)
{
	// intervals along axis
	float mina, maxa;
	float minb, maxb;
	A->boxupdated->supportInterval(axis, mina, maxa);
	B->boxupdated->supportInterval(axis, minb, maxb);

	// calculate the two possible overlap ranges.
	// either we overlap on the left or right sides.
	float d0 = (maxb - mina); // 'left' side
	float d1 = (maxa - minb); // 'right' side

	// the axis length squared
	float axis_length_squared = XMVector3Dot(axis, axis).m128_f32[0];
		
	// axis is degenerate. ignore.
	if(axis_length_squared < 1.0e-8f)
		return true;
		
	// intervals do not overlap. no intersection.
	if(d0 < 0.0f || d1 < 0.0f)
		return false;
		
	// find out if we overlap on the 'right' or 'left' of the object.
	float overlap = (d0 < d1)? d0 : -d1;

	// the mtd vector for that axis
	XMVECTOR sep = axis * (overlap / axis_length_squared);

	// the mtd vector length squared.
	float sep_length_squared = XMVector3Dot(sep, sep).m128_f32[0];

	// if that vector is smaller than our computed MTD (or the mtd hasn't been computed yet)
	// use that vector as our current mtd.
	if(sep_length_squared < mtd2 || mtd2 < 0.0f)
	{
		mtd2 = sep_length_squared;
		mtd  = sep;
	}
	return true;
}


bool Manifold::AABBAABBIntersection()
{
	XMVECTOR mtd = XMVectorSet(0, 0, 0, 0);
	float mtd2 = -1.0f;

	// test faces of A
	for(int i=0; i<A->boxupdated->supportFaceCount(); i++)
	{
		if(!TestAxis(A->boxupdated->supportFaceDirection(i), mtd, mtd2))
			return false;
	}

	// test faces of B
	for(int i=0; i<B->boxupdated->supportFaceCount(); i++)
	{
		if(!TestAxis(B->boxupdated->supportFaceDirection(i), mtd, mtd2))
			return false;
	}

	// no intersections were ever tested.
	if(mtd2 < 0.0f)
		return false;

	float Penetration = sqrt(mtd2);
	XMVECTOR CollisionNormal = XMVector3Normalize(mtd);
	XMFLOAT3 b_vertices[] = {	XMFLOAT3(B->boxupdated->GetCenter().x-B->boxupdated->GetRadius().x, B->boxupdated->GetCenter().y-B->boxupdated->GetRadius().y, B->boxupdated->GetCenter().z-B->boxupdated->GetRadius().z),
								XMFLOAT3(B->boxupdated->GetCenter().x-B->boxupdated->GetRadius().x, B->boxupdated->GetCenter().y+B->boxupdated->GetRadius().y, B->boxupdated->GetCenter().z-B->boxupdated->GetRadius().z),
								XMFLOAT3(B->boxupdated->GetCenter().x+B->boxupdated->GetRadius().x, B->boxupdated->GetCenter().y-B->boxupdated->GetRadius().y, B->boxupdated->GetCenter().z-B->boxupdated->GetRadius().z),
								XMFLOAT3(B->boxupdated->GetCenter().x+B->boxupdated->GetRadius().x, B->boxupdated->GetCenter().y+B->boxupdated->GetRadius().y, B->boxupdated->GetCenter().z-B->boxupdated->GetRadius().z),
								XMFLOAT3(B->boxupdated->GetCenter().x+B->boxupdated->GetRadius().x, B->boxupdated->GetCenter().y-B->boxupdated->GetRadius().y, B->boxupdated->GetCenter().z+B->boxupdated->GetRadius().z),
								XMFLOAT3(B->boxupdated->GetCenter().x+B->boxupdated->GetRadius().x, B->boxupdated->GetCenter().y+B->boxupdated->GetRadius().y, B->boxupdated->GetCenter().z+B->boxupdated->GetRadius().z),
								XMFLOAT3(B->boxupdated->GetCenter().x-B->boxupdated->GetRadius().x, B->boxupdated->GetCenter().y-B->boxupdated->GetRadius().y, B->boxupdated->GetCenter().z+B->boxupdated->GetRadius().z),
								XMFLOAT3(B->boxupdated->GetCenter().x-B->boxupdated->GetRadius().x, B->boxupdated->GetCenter().y+B->boxupdated->GetRadius().y, B->boxupdated->GetCenter().z+B->boxupdated->GetRadius().z)
							};

	std::vector <XMFLOAT3> IncidentFace;
	if (CollisionNormal.m128_f32[0] == 0.0f && CollisionNormal.m128_f32[1] == 0.0f && CollisionNormal.m128_f32[2] == -1.0f)
	{
		IncidentFace.push_back(b_vertices[2]);
		IncidentFace.push_back(b_vertices[3]);
		IncidentFace.push_back(b_vertices[1]);
		IncidentFace.push_back(b_vertices[0]);
	}
	else
	if (CollisionNormal.m128_f32[0] == 0.0f && CollisionNormal.m128_f32[1] == 0.0f && CollisionNormal.m128_f32[2] == 1.0f)
	{
		IncidentFace.push_back(b_vertices[6]);
		IncidentFace.push_back(b_vertices[7]);
		IncidentFace.push_back(b_vertices[5]);
		IncidentFace.push_back(b_vertices[4]);
	}
	else
	if (CollisionNormal.m128_f32[0] == 1.0f && CollisionNormal.m128_f32[1] == 0.0f && CollisionNormal.m128_f32[2] == 0.0f)
	{
		IncidentFace.push_back(b_vertices[4]);
		IncidentFace.push_back(b_vertices[5]);
		IncidentFace.push_back(b_vertices[3]);
		IncidentFace.push_back(b_vertices[2]);
	}
	else
	if (CollisionNormal.m128_f32[0] == -1.0f && CollisionNormal.m128_f32[1] == 0.0f && CollisionNormal.m128_f32[2] == 0.0f)
	{
		IncidentFace.push_back(b_vertices[0]);
		IncidentFace.push_back(b_vertices[1]);
		IncidentFace.push_back(b_vertices[7]);
		IncidentFace.push_back(b_vertices[6]);
	}
	else
	if (CollisionNormal.m128_f32[0] == 0.0f && CollisionNormal.m128_f32[1] == 1.0f && CollisionNormal.m128_f32[2] == 0.0f)
	{
		IncidentFace.push_back(b_vertices[3]);
		IncidentFace.push_back(b_vertices[5]);
		IncidentFace.push_back(b_vertices[7]);
		IncidentFace.push_back(b_vertices[1]);
	}
	else
	if (CollisionNormal.m128_f32[0] == 0.0f && CollisionNormal.m128_f32[1] == -1.0f && CollisionNormal.m128_f32[2] == 0.0f)
	{
		IncidentFace.push_back(b_vertices[0]);
		IncidentFace.push_back(b_vertices[6]);
		IncidentFace.push_back(b_vertices[4]);
		IncidentFace.push_back(b_vertices[2]);
	}

	std::vector <Plane> ReferenceFace;
	if ((CollisionNormal.m128_f32[0] == 1.0f && CollisionNormal.m128_f32[1] == 0.0f && CollisionNormal.m128_f32[2] == 0.0f) || (CollisionNormal.m128_f32[0] == -1.0f && CollisionNormal.m128_f32[1] == 0.0f && CollisionNormal.m128_f32[2] == 0.0f))
	{
		ReferenceFace.push_back(Plane(XMFLOAT3(0,0,-1), -(A->boxupdated->GetCenter().z-A->boxupdated->GetRadius().z)));
		ReferenceFace.push_back(Plane(XMFLOAT3(0,1,0), (A->boxupdated->GetCenter().y+A->boxupdated->GetRadius().y)));
		ReferenceFace.push_back(Plane(XMFLOAT3(0,0,1), (A->boxupdated->GetCenter().z+A->boxupdated->GetRadius().z)));
		ReferenceFace.push_back(Plane(XMFLOAT3(0,-1,0), -(A->boxupdated->GetCenter().y-A->boxupdated->GetRadius().y)));
	}
	else
	if ((CollisionNormal.m128_f32[0] == 0.0f && CollisionNormal.m128_f32[1] == 1.0f && CollisionNormal.m128_f32[2] == 0.0f) || (CollisionNormal.m128_f32[0] == 0.0f && CollisionNormal.m128_f32[1] == -1.0f && CollisionNormal.m128_f32[2] == 0.0f))
	{	
		ReferenceFace.push_back(Plane(XMFLOAT3(1,0,0), (A->boxupdated->GetCenter().x+A->boxupdated->GetRadius().x)));
		ReferenceFace.push_back(Plane(XMFLOAT3(0,0,1), (A->boxupdated->GetCenter().z+A->boxupdated->GetRadius().z)));
		ReferenceFace.push_back(Plane(XMFLOAT3(-1,0,0), -(A->boxupdated->GetCenter().x-A->boxupdated->GetRadius().x)));
		ReferenceFace.push_back(Plane(XMFLOAT3(0,0,-1), -(A->boxupdated->GetCenter().z-A->boxupdated->GetRadius().z)));
	}
	else
	if ((CollisionNormal.m128_f32[0] == 0.0f && CollisionNormal.m128_f32[1] == 0.0f && CollisionNormal.m128_f32[2] == 1.0f) || (CollisionNormal.m128_f32[0] == 0.0f && CollisionNormal.m128_f32[1] == 0.0f && CollisionNormal.m128_f32[2] == -1.0f))
	{
		ReferenceFace.push_back(Plane(XMFLOAT3(1,0,0), (A->boxupdated->GetCenter().x+A->boxupdated->GetRadius().x)));
		ReferenceFace.push_back(Plane(XMFLOAT3(0,1,0), (A->boxupdated->GetCenter().y+A->boxupdated->GetRadius().y)));
		ReferenceFace.push_back(Plane(XMFLOAT3(-1,0,0), -(A->boxupdated->GetCenter().x-A->boxupdated->GetRadius().x)));
		ReferenceFace.push_back(Plane(XMFLOAT3(0,-1,0), -(A->boxupdated->GetCenter().y-A->boxupdated->GetRadius().y)));
	}
	
	vector <XMFLOAT3> ContactPoints;
	ContactPoints = SHClipPolygon(IncidentFace, ReferenceFace[0]);
	contact_count++;
	ContactPoints = SHClipPolygon(ContactPoints, ReferenceFace[1]);
	contact_count++;
	ContactPoints = SHClipPolygon(ContactPoints, ReferenceFace[2]);
	contact_count++;
	ContactPoints = SHClipPolygon(ContactPoints, ReferenceFace[3]);
	contact_count++;

	normal = CollisionNormal;
	contacts[0]=ContactPoints[0];
	contacts[1]=ContactPoints[1];
	contacts[2]=ContactPoints[2];
	contacts[3]=ContactPoints[3];

   penetration = Penetration;

	IncidentFace.clear();
	vector<Plane>().swap(ReferenceFace);
	return true;
}

void Manifold::Initialize( void )
{
  // Calculate average restitution
  e = min( A->GetRestitution(), B->GetRestitution() );

  // Calculate static and dynamic friction
  sf = sqrt( A->GetStaticFriction() * B->GetStaticFriction() );
  df = sqrt( A->GetDynamicFriction() * B->GetDynamicFriction() );
}

void Manifold::ApplyImpulse( void )
{
  for(int i = 0; i < contact_count; ++i)
  {
	  XMVECTOR p = XMVectorSet(contacts[i].x,contacts[i].y,contacts[i].z, 1.0f);
	  
	XMVECTOR padot = A->GetVelocityAtPoint(p);
	XMVECTOR pbdot = B->GetVelocityAtPoint(p);

	XMVECTOR ra = (p - A->GetPosition());
	XMVECTOR rb = (p - B->GetPosition());
	XMVECTOR vrel = XMVector3Dot(normal, (padot - pbdot));
 
	if (vrel.m128_f32[0] > 0)
		return;
	float numerator = (-(1.0f + e)*vrel.m128_f32[0]);
	float term1 = (1.0f / A->GetMass());
	float term2 = (1.0f / B->GetMass());
	XMVECTOR term3 = XMVector3Dot(normal, XMVector3Cross(XMVector4Transform(XMVector3Cross(ra, normal), A->GetIInverse()), ra));
	XMVECTOR term4 = XMVector3Dot(normal, XMVector3Cross(XMVector4Transform(XMVector3Cross(rb, normal), B->GetIInverse()), rb));
	float j = (numerator / (term1+ term2 + term3.m128_f32[0] + term4.m128_f32[0]));
	j /= contact_count;
	XMVECTOR f = (j*normal);
	A->AddImpulse(f);
	B->AddImpulse(-f);
	A->AddImpulsiveTorque(XMVector3Cross(ra, f));
	B->AddImpulsiveTorque(-XMVector3Cross(rb, f));

	XMVECTOR rv = (padot - pbdot);
	XMVECTOR t = rv - (normal * vrel.m128_f32[0]);
    t = XMVector3Normalize(t);

    // j tangent magnitude
    float jt = -XMVector3Dot(rv, t).m128_f32[0];
    jt /= (term1+ term2 + term3.m128_f32[0] + term4.m128_f32[0]);
	jt /= contact_count;

    // Don't apply tiny friction impulses
    if(jt == 0.0f)
      return;

    // Coulumb's law
    XMVECTOR tangentImpulse;
    if(abs(jt) < j * sf)
      tangentImpulse = t * jt;
    else
      tangentImpulse = t * -j * df;

    // Apply friction impulse
	A->AddImpulse(tangentImpulse);
	B->AddImpulse(-tangentImpulse);
	A->AddImpulsiveTorque(XMVector3Cross(ra, tangentImpulse));
	B->AddImpulsiveTorque(-XMVector3Cross(rb, tangentImpulse));

  }
}

void Manifold::PositionalCorrection( void )
{
  float k_slop = 0.05f; // Penetration allowance
  float percent = 0.4f; // Penetration percentage to correct
  XMVECTOR correction = (max( penetration - k_slop, 0.0f ) / ((1/A->GetMass()) + (1/B->GetMass()))) * normal * percent;
  A->SetPosition( correction * (1/A->GetMass()));
  B->SetPosition( -correction * (1/B->GetMass()));
}

Manifold::~Manifold(void)
{
}
#include "World.h"

World::World(float dt, float gravity, int iterations)
{
	//dt = dt;
	g = gravity ;
	Iterations = iterations;
}

void World::Clear()
{
	Bodies.clear();
	Contacts.clear();
}


void World::Step(float dt)
{

	for(int i = 0; i < Bodies.size( ); ++i)
	{
		Bodies[i]->Update(dt);

	}
  // Generate new collision info
  Contacts.clear( );
  for(int i = 0; i < Bodies.size( ); ++i)
  {
    RigidBody *A = Bodies[i];

    for(int j = i + 1; j < Bodies.size( ); ++j)
    {
      RigidBody *B = Bodies[j];
      if((1/A->GetMass()) == 0.0f && (1/B->GetMass()) == 0.0f)
        continue;
      Manifold m( A, B );
      if (m.AABBAABBIntersection( ))
	  {
      if(m.contact_count)
        Contacts.emplace_back( m );
	  }
    }
  }

  for(int i = 0; i < Contacts.size( ); ++i)
	 Contacts[i].Initialize( );

  // Solve collisions
  for(int j = 0; j < Iterations; ++j)
    for(int i = 0; i < Contacts.size( ); ++i)
		Contacts[i].ApplyImpulse( );

  // Correct positions
  for(int i = 0; i < Contacts.size( ); ++i)
	Contacts[i].PositionalCorrection( );

}

RigidBody *World::Add(RigidBody *body)
{
	Bodies.push_back(body);
	return body;
}


World::~World(void)
{
	Clear();
}

Adding ball and pitch(ground) to my system.

XMMATRIX IBody = XMMatrixSet(	 0,		0,		0,		 0,
								 0,		0,		0,		 0,
								 0,		0,		0,		 0,
								 0,		0,		0,		 1);
rbPitch = myphisx.Add(new RigidBody(new AABB(XMFLOAT3(0,0,0), XMFLOAT3(1024,2,1024)), .5f, .3f, .0f, FLT_MAX, IBody, XMVectorSet(25, 0, -160, 1), XMVectorSet(0, 0, 0, 0), XMVectorSet(0, 0, 0, 0), XMMatrixIdentity()));

float I_tmp = (0.4f)*(CB_Mass*(CB_Radius*CB_Radius));
		XMMATRIX IBody = XMMatrixSet(I_tmp,		0,		0,		 0,
										 0,		I_tmp,	0,		 0,
										 0,		0,		I_tmp,	 0,
										 0,		0,		0,		 1);

rbBall = myphisx.Add(new RigidBody(new AABB(XMFLOAT3(0, 0, 0), XMFLOAT3(CB_Radius, CB_Radius, CB_Radius)), .5f,.3f,.4f, CB_Mass, IBody, XMVectorSet(17, 50, -375, 1), XMVectorSet(0, -10, 80.0f, 0), XMVectorSet(-8e-4f, 0, 0, 0), XMMatrixRotationZ(XM_PIDIV2)));

 

Share this post


Link to post
Share on other sites
Advertisement
Posted (edited)

Just set the inverse mass of the ground body to zero and the inverse inertia to zero matrix.

Usually in a physic engines you have the concept of a body type. The type is usually either static, keyframed/kinematic, or dynamic. It defines the transform exchange between the game entity and the rigid body. Static means the rigid body and entity are spawned in sync at the same location and never touched again - nothing todo during ticks. Keyframed means the entity transform is authoritative and sets the transform/velocity of the body each simulation tick. Dynamic means the rigid body copies the simulated transform back into the associated entity after the world was stepped. A kind of canonical physics update looks therefore something like this:

for ( all *non sleeping* keyframed bodies b )
{
    b->SetVelocityFromKeyframe( entity->transform(), dt );
}

mWorld->Step( dt );

for ( all *non sleeping* dynamic bodies b )
{
    entity->setTransform( b->transform() );
}

For good performance you only want to touch active (non-sleeping) bodies. It is up to the game code (physics integration layer) to maintain such a list.

SetVelocityFromKeyframe() computes a *center of mass* linear velocity and corresponding angular velocity to reach the target transform. I leave this as an exercise to the reader..

In your physics engine you would now set the body type to static. This also sets the inverse mass and inertia to zero, This is usually enough to make this work in the solver.

Edited by Dirk Gregorius

Share this post


Link to post
Share on other sites
Posted (edited)

Old: What I did is set IBody to zero matrix and mass to FLT_MAX. my system gives debugging error "vector subscript out of range"

Latest: I handled every 1/0.0fs and inv intertia cases now positional correction works but applyimpulse gives "vector subscript out of range" error

Edited by isu diss

Share this post


Link to post
Share on other sites
Posted (edited)

It's much more easier for you to take a look at an existing game physics engine and try to port it while trying to understand the underlying mathematics and physics.

There are a couple of engines available supporting some of the features Dirk has mentioned earlier, such as static, keyframed/kinematic, and dynamics rigid bodies. But my favourite are some that implements those in a minimalist way:

Box2D - https://github.com/erincatto/Box2D

Bounce - https://github.com/irlanrobson/bounce

Bounce Lite - https://github.com/irlanrobson/bounce_lite

Those are easy to understand and port in comparison with some engines like e.g. Bullet or PhysX.

Remember that in 3D you'll need to update the world inertia tensor after solving a position constraint since this tensor a function of the position. I have seen some game engines skipping this step. That is a good approximation and easy to implement. This is a non-linear problem anyways. However, it is not mathematically correct. So in practice I update everything after solving a position constraint. This is a very manageable task. It might help with stacking.

I'm not a big fan of self-advertising, but take a look at Bounce solvers for good examples of this.

Bounce Lite is a small version of Bounce. It doesn't solves position constraints. Rather it uses something called constraint stabilization.

 

Edited by Irlan Robson

Share this post


Link to post
Share on other sites

Just store the inverse mass and inverse inertia as body state. This is better defined as the non-movable bodies just map to zero (and zero matrix). Don't play around with FLT_MAX or INF or anything like this.

Share this post


Link to post
Share on other sites

Ground problem is resolved. Some rigidbodies' contacts are not accurate therefore they don't stack well(e.g. Bails don't place on the stumps' groove, I'm making a cricket game). Can anyone give some hints to extend box2d lite's 2D collision resolution into 3D? I think my way of generating contacts doesn't work for every case(^^^ manifold.cpp). I'm so desperate. I don't wanna use someone's engine.

Share this post


Link to post
Share on other sites
Posted (edited)

Did you check Bounce Lite? It's essentially a 3D port of Box2D Lite.

Bounce Lite only supports impulse caching. Bounce supports impulse caching or contact re-clipping/rebuilding. Those are essential features in order to get a stable stacking using a small number of iterations. The latter being particularly important in 3D since the main problems here involve rotations.

So basically in order for friction and stacking to work stably you need to cache impulses. Otherwise you will need lots of velocity constraint solver iterations since you don't have an initial guess. For deep contacts in 3D you also need frame coherence as mentioned above. Those are somewhat quite involving but is relatively easy to implement.

Don't get desperate! Fortunately these are well understood and solved problems. All this is implemented in Bounce Lite and Bounce.

You definitely need a reference code in order to get things working. Then you can add more features to your library. But please make sure you understand every aspect of the engine. It becomes much easier to debug something you can understand.

 

Edited by Irlan Robson

Share this post


Link to post
Share on other sites
Posted (edited)

A good hint if you really want to implement this from scratch is to start simple. Begin with a sphere on a plane. Then add more spheres to test stacking. And there you go...

These portions of code implements contact resolution. This is just a matter of looping over each contact sequentially solving velocity constraints. Note that this is not a naive impulse approach. This is a more mathematically correct way of applying impulses. Don't play around with some extreme simplifications or hacks around there such as the naive impulse approach if you really want to get this working reliably for multiple bodies.

https://github.com/irlanrobson/bounce_lite/blob/master/src/bounce_lite/dynamics/contacts/contact_solver.cpp

https://github.com/irlanrobson/bounce_lite/blob/master/src/bounce_lite/dynamics/contacts/contact.cpp

All this code is called from an island. But you don't need islands in your simple engine. So you can skip this feature.

https://github.com/irlanrobson/bounce_lite/blob/master/src/bounce_lite/dynamics/island.cpp

With Box2D Lite and Bounce Lite and on the side you should be able to successfully implement you simple engine of dreams.

Erin gave a nice talk about constraints in 2014. This should get you introduced to the topic. The accompanying material also contains some MATLAB code.

http://box2d.org/files/GDC2014/GDC2014_ErinCatto.zip

Edited by Irlan Robson

Share this post


Link to post
Share on other sites
Posted (edited)

A little bit off-topic, but just for completeness though.

The function Dirk mentioned SetVelocityFromKeyframe() can be done by applying the finite difference method.

If the two keyframed transforms corresponds to the center of mass and rotation transform of the associated body then the formulas for the linear and angular velocity applying one finite difference are:

v = (c2 - c1) / dt
w = (2 * (q2 - q1) * conjugate(q1)) / dt

You need make sure the interpolated quaternion is along the shortest path between q1 and q2. So you need to check the polarity. This can be done by flipping the sign of one of the quaternions according to the sign of their dot product as we do in keyframe interpolation. Hence, our final quaternion formula can be:

s = sign(dot(q1, q2))
w = (2 * (q2 - s * q1) * conjugate(s * q1)) / dt

Then you set the linear and angular velocity of the kinematic body to the above before stepping a world.

Edited by Irlan Robson

Share this post


Link to post
Share on other sites

@Irlan RobsonThank you sir for your input. Can you please look into my AABBAABBIntersection function in Manifold.cpp? The way I constructed that function, I feel It's fixed not flexible like yours in bounce lite. I'm still studying your code. How do I do that?

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

GameDev.net is your game development community. Create an account for your GameDev Portfolio and participate in the largest developer community in the games industry.

Sign me up!