/* 
 * 
 * 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>
#include <Demos/Physics/Api/Collide/RayCasting/RayCastFiltering/RaycastFilteringDemo.h>

/// Need some shapes
#include <Physics/Collide/Shape/Convex/Box/hkpBoxShape.h>
#include <Physics/Collide/Shape/Compound/Tree/Mopp/hkpMoppUtility.h>
#include <Physics/Collide/Shape/Compound/Collection/ExtendedMeshShape/hkpExtendedMeshShape.h>

#include <Physics/Collide/Shape/Query/hkpShapeRayCastInput.h>
#include <Physics/Collide/Query/CastUtil/hkpWorldRayCastOutput.h>

/// We need to display the results of the raycast to confirm correctness.
#include <Common/Visualize/hkDebugDisplay.h>
#include <Demos/DemoCommon/DemoFramework/hkTextDisplay.h>

RaycastFilteringDemo::RaycastFilteringDemo(hkDemoEnvironment* env)
	:	hkDefaultPhysicsDemo(env), 
		m_time(0.0f)
{

	//	
	// Setup the camera.
	//
	{
		hkVector4 from(-8.0f, 25.0f, 20.0f);
		hkVector4 to  (0.0f, 0.0f, 0.0f);
		hkVector4 up  (0.0f, 1.0f, 0.0f);
		setupDefaultCameras(env, from, to, up);

		// disable back face culling
		setGraphicsState(HKG_ENABLED_CULLFACE, false);
	}



	//
	// Create the world.
	//
	{
		hkpWorldCinfo info;
		info.setBroadPhaseWorldSize( 100.0f );

		m_world = new hkpWorld(info);
		m_world->lock();

		setupGraphics();
	}

	//
	//	Create the filter: every layer collides only with itself except layer 0, which collides with everything
	//
	{
			hkpGroupFilter* filter = new hkpGroupFilter();
			filter->disableCollisionsUsingBitfield( 0xfffffffe, 0xfffffffe );
			for (int i = 0; i < 32; i++ )
			{
				filter->enableCollisionsBetween( i, i );
			}
			m_world->setCollisionFilter( filter );
			filter->removeReference();
	}


	//
	//	Create a row of colored rigid bodies
	//
	{
		hkVector4 halfExtents(.5f, .5f, .5f );
		hkpShape* shape = new hkpBoxShape( halfExtents , 0 );

		int colors[4] = { hkColor::RED, hkColor::GREEN, hkColor::BLUE, hkColor::YELLOW };
		for (int i = 0; i < 4; i++ )
		{
			hkpRigidBodyCinfo ci;
			ci.m_motionType = hkpMotion::MOTION_FIXED;
			ci.m_shape = shape;
			ci.m_collisionFilterInfo = hkpGroupFilter::calcFilterInfo(i+1);

			for (int j = 0; j < 4; j++ )
			{
				hkReal fx = i * 3.0f;	// we need to declare these variables to avoid a bug in the VS2005 compiler
				hkReal fz = j * 2.0f + 2.0f;
				ci.m_position.set( fx, 0.f, fz);

				hkpRigidBody* body = new hkpRigidBody( ci );
				m_world->addEntity(body);
				body->removeReference();

				// show the objects in nice transparent colors
				colors[i] &= ~hkColor::rgbFromChars( 0, 0, 0 );
				colors[i] |= hkColor::rgbFromChars( 0, 0, 0, 120 );
				HK_SET_OBJECT_COLOR((hkUlong)body->getCollidable(), colors[i]);
			}
		}
		shape->removeReference();
	}

	//
	//	Create a mopp shape, which looks like __--__--__--__--
	//
	hkpShape* moppShape;
	{
		//
		//	Set our vertices and indices
		//  Note: we reuse the index triples in an overlapping way to
		//  get a triangle strip
		//
		{
			for (short i = 0; i < NUM_VERTICES; i++ )
			{
				m_indices[i] = i;
				m_vertices[i].x = ( (i+2)>>2 )  * 1.5f - 2.0f;
				m_vertices[i].y = ( (i>>2) & 1) * 1.0f - 0.5f;
				m_vertices[i].z = ( (i>>0) & 1) * (-9.5f);
				m_vertices[i].w = 0.f;
			}
		}
		//
		//	Set our materialIndices per triangle
		//
		{
			for (short i = 0; i < NUM_TRIANGLES; i++ )
			{
				m_materialIndices[i] = char(i>>3);
			}
		}
		//
		// Our material lib
		//
		{
			for ( int i = 0; i < 4; i++)
			{
				m_materials[i].m_filterInfo = hkpGroupFilter::calcFilterInfo(i+1);
			}
		}

		hkpExtendedMeshShape* mesh = new hkpExtendedMeshShape();
		mesh->setRadius(0.05f);
		{
			hkpExtendedMeshShape::TrianglesSubpart part;

			part.m_vertexBase = &m_vertices[0].x;
			part.m_vertexStriding = sizeof(MyVector4);
			part.m_numVertices = NUM_VERTICES;

			part.m_indexBase = m_indices;
			part.m_indexStriding = sizeof( hkUint16);
			part.m_numTriangleShapes = NUM_TRIANGLES;
			part.m_stridingType = hkpExtendedMeshShape::INDICES_INT16;

			part.m_materialIndexStridingType = hkpExtendedMeshShape::MATERIAL_INDICES_INT8;
			part.m_materialIndexBase = m_materialIndices;
			part.m_materialIndexStriding = sizeof(hkUint8);

			part.m_materialBase = m_materials;
			part.m_materialStriding = hkSizeOf( hkpMeshMaterial );
			part.m_numMaterials = 4;

			mesh->addTrianglesSubpart( part );
		}

		hkpMoppCompilerInput mci;
		hkpMoppCode* code = hkpMoppUtility::buildCode( mesh ,mci);

		moppShape = new hkpMoppBvTreeShape( mesh, code );

		mesh->removeReference();
		code->removeReference();
	}

	//
	//	Create the landscape body
	//
	{
		hkpRigidBodyCinfo ci;
		ci.m_motionType = hkpMotion::MOTION_FIXED;
		ci.m_shape = moppShape;
		hkpRigidBody* body = new hkpRigidBody( ci );
		m_world->addEntity(body);
		body->removeReference();
	}
	moppShape->removeReference();

	m_world->unlock();
}


hkDemo::Result RaycastFilteringDemo::stepDemo()
{
	m_world->lock();

	//
	// shoot two times four colored rays.
	//  1. each ray should only hit the object with the same color
	//  2. each ray should only hit the triangles (which are at the same x-ccord as the object)
	//
	m_time += m_timestep * 3.0f;

	int colors[4] = { hkColor::RED, hkColor::GREEN, hkColor::BLUE, hkColor::YELLOW };

	for (int i = -1; i <= 1 ; i+=2 )
	{
		for (int j = 0; j < 4; j++ )
		{
			//
			//	Setup the ray coordinates
			//
			hkpWorldRayCastInput ray;
			{
				ray.m_from.set(-4.f, 0.f, i*(1+j) * 2.0f);
				ray.m_to.set ( 25.f, 0.f, i*(1+j) * 2.0f + 0.5f * hkMath::cos(m_time));
				ray.m_filterInfo = hkpGroupFilter::calcFilterInfo(j+1);
				ray.m_enableShapeCollectionFilter = true;
			}

			//
			//	Do the raycaster
			//
			hkpWorldRayCastOutput output;
			{
				m_world->castRay( ray, output );
			}

			//
			//	Check the hit
			//
			hkVector4 intersectionPointWorld;
			intersectionPointWorld.setInterpolate4( ray.m_from, ray.m_to, output.m_hitFraction );		
			HK_DISPLAY_LINE( ray.m_from, intersectionPointWorld, colors[j]);
		}
	}

	m_world->unlock();

	return hkDefaultPhysicsDemo::stepDemo();
}



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

static const char helpString[] = \
"This demo illustrates the use of raycast filtering. " \
"On one side of the demo, the ray cast hits only objects of the same " \
"collision layer (indicated by the same color as the ray). " \
"On the other side, the ray is shot against a hkpMeshShape,  " \
"where it only hits selected triangles. " \
"This selection of triangles is achieved by adding material " \
"information to the mesh, which includes also collision filter " \
"data.";

HK_DECLARE_DEMO(RaycastFilteringDemo, PRIME, "Raycast filtering", helpString);

/*
* 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
*
*/
