bool CollisionVolume::testCollision(const VC3 &objectPosition, const VC3 &angles, CollisionData &collisionData, float epsilon)
{
	QUAT rotation = getRotation(angles);
	//rotation.MakeFromAngles(0, -yRotation, 0);
	
	Matrix tm;
	tm.CreateRotationMatrix(rotation);

	// Everything's relative etc
	VC3 rayOrigin = tm.GetTransformedVector(collisionData.rayOrigin - objectPosition) + objectPosition;
	VC3 rayDirection = tm.GetWithoutTranslation().GetTransformedVector(collisionData.rayDirection);

	if(!data->possibleCollision(objectPosition, collisionData))
		return false;
	if(!data->accurateCollision(objectPosition, collisionData, rayOrigin, rayDirection))
		return false;

	float collisionDistance = objectPosition.GetRangeTo(collisionData.rayOrigin);
	VC3 pos = tm.GetInverse().GetTransformedVector(collisionData.collisionPosition - objectPosition) + objectPosition;

	if(collisionData.hasCollision)
	if(collisionDistance > pos.GetRangeTo(collisionData.rayOrigin))
		return false;

	collisionData.rayLength = collisionDistance;
	collisionData.hasCollision = true;
	collisionData.collisionPosition = objectPosition;
	collisionData.objectData = data->data;

	return true;
}
		bool SphereCollision(const VC3 &pos, float radius, Storm3D_CollisionInfo &info, bool accurate)
		{
			if(pos.GetRangeTo(position) < radius + getRadius())
			{
				info.hit = true;
				return true;
			}

			return false;
		}
	float getAmount(const VC3 &pos, float angle, const IStorm3D_Terrain &terrain, float rayHeight) const
	{
		float fovRadians = fov * (3.1415927f / 180.f);
		int index = int(RAY_AMOUNT * (angle + fovRadians) / (2.f * fovRadians));
		assert(index >= 0 && index < RAY_AMOUNT);

		float distance = pos.GetRangeTo(position);
		if(!lengthOk[index])
		{
			float rayAngle = -fovRadians + float(index) * ((2 * fovRadians) / (RAY_AMOUNT - 1));

			VC3 rayDir = direction;
			float x = rayDir.x;
			float z = rayDir.z;

			rayDir.x = x * cosf(rayAngle) + z * sinf(rayAngle);
			rayDir.y = 0;
			rayDir.z = -x * sinf(rayAngle) + z * cosf(rayAngle);
			rayDir.Normalize();

			Storm3D_CollisionInfo cInfo;
			ObstacleCollisionInfo oInfo;
			terrain.rayTrace(position + VC3(0,rayHeight,0) + (rayDir * .5f), rayDir, range, cInfo, oInfo, true, true);

			if(oInfo.hit && oInfo.hitAmount > 0)
				length[index] = oInfo.ranges[0];
			else if(cInfo.hit)
				length[index] = cInfo.range;
			else
				length[index] = range;

			lengthOk[index] = true;
		}

		if(distance > length[index])
			return 0;

		return 1.f - distance / range;
	}
//! Render projection
void Storm3D_FakeSpotlight::renderProjection()
{
	if(BUFFER_WIDTH <= 0 || BUFFER_HEIGHT <= 0)
		return;

	if(!data->renderTarget)
		return;

	VC3 position = data->properties.position;
	position.y -= data->plane.height;

	const VC2 &min = data->plane.min;
	const VC2 &max = data->plane.max;
	float range = data->properties.range * sqrtf(2.f);

	VC3 a = position;
	a.x += min.x;
	a.z += min.y;
	float ad = a.GetRangeTo(position) / range;
	VC3 b = position;
	b.x += min.x;
	b.z += max.y;
	float bd = b.GetRangeTo(position) / range;
	VC3 c = position;
	c.x += max.x;
	c.z += min.y;
	float cd = c.GetRangeTo(position) / range;
	VC3 d = position;
	d.x += max.x;
	d.z += max.y;
	float dd = d.GetRangeTo(position) / range;
	VC3 e = position;
	float ed = 0.f;

	ad = bd = cd = dd = 1.f;

	float buffer[] = 
	{
		a.x, a.y, a.z,  ad, ad,  ad, 1.0f,
		b.x, b.y, b.z,  bd, bd,  bd, 1.0f,
		c.x, c.y, c.z,  cd, cd,  cd, 1.0f,
		d.x, d.y, d.z,  dd, dd,  dd, 1.0f,
		e.x, e.y, e.z,  ed, ed,  ed, 1.0f
	};

	memcpy(data->vertexBuffer.lock(), buffer, 5 * 7 * sizeof(float));
	data->vertexBuffer.unlock();
	data->vertexBuffer.apply(0);

	Storm3D_ShaderManager *manager = Storm3D_ShaderManager::GetSingleton();
	manager->setTextureTm(data->properties.shaderProjection[0]);
	manager->setSpot(COL(), data->properties.position, data->properties.direction, data->properties.range, .1f);

	D3DXMATRIX identity;
	D3DXMatrixIdentity(identity);

	manager->setSpotTarget(data->properties.targetProjection);
	manager->SetWorldTransform(identity);

	glEnable(GL_ALPHA_TEST);
	glAlphaFunc(GL_GREATER, 1.0f/255.0f);

	{
		//float factor = data->fadeFactor + data->fogFactor * (1.f - data->fadeFactor);
		//float factor = data->fadeFactor * data->fogFactor;
		//float factor = data->fadeFactor;
		float factor = (1.f - data->fadeFactor) + data->fogFactor * (data->fadeFactor);
		float c0[4] = { factor, factor, factor, 1.f };
		glProgramEnvParameter4fvARB(GL_FRAGMENT_PROGRAM_ARB, 0, c0);

		float fogFactor = data->fogFactor;
		float c2[4] = { fogFactor, fogFactor, fogFactor, 1.f };
		glProgramEnvParameter4fvARB(GL_FRAGMENT_PROGRAM_ARB, 2, c2);
	}

	{
		float xd = 1.f / float(data->renderTarget->color->getWidth());
		float yd = 1.f / float(data->renderTarget->color->getHeight());
		float xd1 = xd * 1.5f;
		float yd1 = yd * 1.5f;
		float xd2 = xd * 2.5f;
		float yd2 = yd * 2.5f;

		/*
		float xd = 1.f / float(sourceDesc.Width);
		float yd = 1.f / float(sourceDesc.Height);
		float xd1 = xd * 0.5f;
		float yd1 = yd * 0.5f;
		float xd2 = xd * 1.5f;
		float yd2 = yd * 1.5f;
		*/

		float deltas1[4] = { -xd2, -yd2, 0, 0 };
		float deltas2[4] = { -xd1,  yd2, 0, 0 };
		float deltas3[4] = {  xd1, -yd1, 0, 0 };
		float deltas4[4] = {  xd2,  yd1, 0, 0 };

		glProgramEnvParameter4fvARB(GL_VERTEX_PROGRAM_ARB, 5, deltas1);
		glProgramEnvParameter4fvARB(GL_VERTEX_PROGRAM_ARB, 6, deltas2);
		glProgramEnvParameter4fvARB(GL_VERTEX_PROGRAM_ARB, 7, deltas3);
		glProgramEnvParameter4fvARB(GL_VERTEX_PROGRAM_ARB, 8, deltas4);
	}

	data->shadowVertexShader->apply(); // fake_shadow_plane_vertex_shader.txt
	data->indexBuffer->render(4, 5);
}