/* 
 * 
 * Confidential Information of Telekinesys Research Limited (t/a Havok).  Not for disclosure or distribution without Havok's
 * prior written consent.This software contains code, techniques and know-how which is confidential and proprietary to Havok.
 * Level 2 and Level 3 source code contains trade secrets of Havok. Havok Software (C) Copyright 1999-2008 Telekinesys Research Limited t/a Havok. All Rights Reserved. Use of this software is subject to the terms of an end user license agreement.
 * 
 */


#include <Demos/demos.h>

#if defined(HK_PLATFORM_HAS_SPU)

#include <Common/Visualize/hkDebugDisplay.h>

#include <Demos/Physics/Api/Collide/CollisionCallbacks/CollisionEventsFromSpu/CollisionEventsFromSpuDemo.h>


CollisionEventsFromSpuDemo::CollisionEventsFromSpuDemo(hkDemoEnvironment* env) 
	: hkDefaultPhysicsDemo(env) 
{
	//
	// Setup the camera
	//
	{
		hkVector4 from(3.0f, 4.0f, 8.0f);
		hkVector4 to(0.0f, 1.0f, 0.0f);
		hkVector4 up(0.0f, 1.0f, 0.0f);
		setupDefaultCameras( env, from, to, up );
	}

	//
	// Create the world
	//
	{
		hkpWorldCinfo info;
		info.setupSolverInfo(hkpWorldCinfo::SOLVER_TYPE_4ITERS_MEDIUM); 
		info.setBroadPhaseWorldSize( 100.0f );
		info.m_simulationType = info.SIMULATION_TYPE_CONTINUOUS;

		// Turn off deactivation so we can see continuous contact point processing
		info.m_enableDeactivation = false;
		m_world = new hkpWorld( info );
		m_world->lock();

		hkpAgentRegisterUtil::registerAllAgents(m_world->getCollisionDispatcher());

		setupGraphics();
	}

	//
	// create a collision callback utility for the spu
	//
	{
		const int totalSizeOfEventUtil = 1024; 
		m_collisionCallbackUtil = hkSpuCollisionCallbackUtil::createSpuCollisionCallbackUtil( totalSizeOfEventUtil );
	}

	//
	// Create the floor
	//
	{
		hkpRigidBodyCinfo info;
		hkVector4 fixedBoxSize(5.0f, 0.5f , 5.0f );
		hkpBoxShape* fixedBoxShape = new hkpBoxShape( fixedBoxSize , 0 );

		info.m_shape = fixedBoxShape;
		info.m_motionType = hkpMotion::MOTION_FIXED;

		// Create fixed box
		hkpRigidBody* floor = new hkpRigidBody(info);
		m_world->addEntity(floor);

		floor->removeReference();
		fixedBoxShape->removeReference();
	}

	//
	// Create 3 moving boxes
	//
	{
		hkpRigidBodyCinfo boxInfo;
		hkVector4 boxSize( .5f, .5f ,.5f );
		boxInfo.m_shape = new hkpBoxShape( boxSize , 0 );

		// Compute the box inertia tensor
		hkpInertiaTensorComputer::setShapeVolumeMassProperties( boxInfo.m_shape, 1.0f, boxInfo );

		boxInfo.m_qualityType = HK_COLLIDABLE_QUALITY_DEBRIS;
		boxInfo.m_motionType = hkpMotion::MOTION_BOX_INERTIA;

		hkVector4 axis(1.0f, 2.0f, 3.0f);
		axis.normalize3();
		boxInfo.m_rotation.setAxisAngle(axis, -0.7f);
		boxInfo.m_processContactCallbackDelay = 0;

		//
		// we will now create 3 different boxes; each box has a different combination of the
		// contactPointAdded and contactPointProcess event filter set:
		// box 0: return only contactPointAdded events
		// box 1: return only contactPointProcess events
		// box 2: return both contactPointAdded & contactPointProcess events
		//
		hkReal deltaX = 3.0f;
		hkReal positionX = -3.0f;
		hkUint8 eventFilter = 1;
		hkpEntity::SpuCollisionCallbackEventFilter eventFilters[3] = {	hkpEntity::SPU_SEND_CONTACT_POINT_ADDED,
																		hkpEntity::SPU_SEND_CONTACT_POINT_PROCESS,
																		hkpEntity::SpuCollisionCallbackEventFilter(hkpEntity::SPU_SEND_CONTACT_POINT_ADDED|hkpEntity::SPU_SEND_CONTACT_POINT_PROCESS)
																	};
		{
			for (int i = 0; i < 3; i++)
			{
				boxInfo.m_position.set(positionX, 5.0f, 0.0f);

				hkpRigidBody* boxRigidBody = new hkpRigidBody( boxInfo );
				m_world->addEntity( boxRigidBody );
				boxRigidBody->removeReference();

				//
				// register spu collision callback utility with our rigid body;
				// use '1' as userFilter id (default is 0x01, so we will get events for every collision between this entity and any entity without a dedicated utility)
				//
				boxRigidBody->setSpuCollisionCallbackUtil(m_collisionCallbackUtil, eventFilters[i], 0x01);

				positionX += deltaX;
				eventFilter++;
			}
		}

		boxInfo.m_shape->removeReference();
	}

	m_world->unlock();
}


hkDemo::Result CollisionEventsFromSpuDemo::stepDemo()
{
	hkDemo::Result result = hkDefaultPhysicsDemo::stepDemo();

	// process the collision events that have been sent down from the spu
	m_world->lock();
	m_collisionCallbackUtil->fireCallbacks(m_world, this);
	m_world->unlock();

	return result;
}


void CollisionEventsFromSpuDemo::contactPointAddedCallbackFromSpu(hkSpuCollisionCallbackUtil::ContactPointAddedEvent* event)
{
	//
	// draw the contact point as a little red star
	//
	{
		const hkVector4& start = event->m_contactPoint.getPosition();
		for ( int i = 0; i < 20; i++ )
		{
			hkVector4 dir( hkMath::sin( i * 1.0f ), hkMath::cos( i * 1.0f ), hkMath::sin(i * 5.0f ) );
			dir.setMul4(0.3f, dir);
			hkVector4 end;		end.setAdd4(start, dir);
			HK_DISPLAY_LINE(start, end, hkColor::RED);
		}
	}
}


void CollisionEventsFromSpuDemo::contactPointProcessCallbackFromSpu(hkSpuCollisionCallbackUtil::ContactPointProcessEvent* event)
{
	for (int i = 0; i < event->m_numContactPoints; i++ )
	{
		// draw the contact points and normals in white
		{
			const hkVector4& start = event->m_contactPoints[i].getPosition();
			hkVector4 normal       = event->m_contactPoints[i].getNormal();

			// For ease of display only, we'll always draw the normal "up" (it points from entity 'B'
			// to entity 'A', but the order of A,B is arbitrary) so that we can see it. Thus, if it's 
			// pointing "down", flip its direction (and scale), only for display.
			normal.mul4(3.0f * normal(1));
			HK_DISPLAY_ARROW(start, normal, hkColor::GREEN);
		}
	}
}


#if defined(HK_COMPILER_MWERKS)
#	pragma force_active on
#	pragma fullpath_file on
#endif


static const char helpString[] = \
	"This demo demonstrates how to forward collision events from the spu to the ppu (if the collision detection is running on the spu)." \
	"The left box will only report 'contactPointAdded' events, the box in the middle will only " \
	"report 'contactPointProcess' events whereas the right box will report both event types.";


HK_DECLARE_DEMO(CollisionEventsFromSpuDemo, PRIME, "Using Collision Points 'added' and 'processed' callbacks on the SPU.", helpString);


#endif

/*
* Havok SDK - NO SOURCE PC DOWNLOAD, BUILD(#20080529)
*
* Confidential Information of Havok.  (C) Copyright 1999-2008 
* Telekinesys Research Limited t/a Havok. All Rights Reserved. The Havok
* Logo, and the Havok buzzsaw logo are trademarks of Havok.  Title, ownership
* rights, and intellectual property rights in the Havok software remain in
* Havok and/or its suppliers.
*
* Use of this software for evaluation purposes is subject to and indicates 
* acceptance of the End User licence Agreement for this product. A copy of 
* the license is included with this software and is also available at
* www.havok.com/tryhavok
*
*/
