static void CalculateLisPSM( const CCameraEntity& cam, Light& light )
{
	CalculateOrthoShadow( cam, light );

	const SVector3& lightDir = light.mWorldMat.getAxisZ();
	const SVector3& viewDir = cam.mWorldMat.getAxisZ();
	double dotProd = lightDir.dot( viewDir );
	if( fabs(dotProd) >= 0.999 )
	{
		// degenerates to uniform shadow map
		return;
	}

	// calculate the hull of body B in world space
	HullFace bodyB;
	SMatrix4x4 invCamVP;
	D3DXMatrixInverse( &invCamVP, NULL, &cam.mWorldMat );
	invCamVP *= cam.getProjectionMatrix();
	D3DXMatrixInverse( &invCamVP, NULL, &invCamVP );

	CalculateFocusedLightHull( invCamVP, lightDir, gCasterBounds, bodyB );
	int zzz = bodyB.v.size();

	int i, j;
	/*
	Frustum camFrustum( cam.getProjectionMatrix() );
	std::vector<SVector3>	bodyB;
	bodyB.reserve( gSceneCasters.size()*8 + 8 );
	for( i = 0; i < 8; ++i )
		bodyB.push_back( camFrustum.pntList[i] );
	int ncasters = gSceneCasters.size();
	for( i = 0; i < ncasters; ++i )
	{
		const CAABox& aabb = gSceneCasters[i].aabb;
		for( j = 0; j < 8; ++j )
		{
			SVector3 p;
			p.x = (j&1) ? aabb.getMin().x : aabb.getMax().x;
			p.y = (j&2) ? aabb.getMin().y : aabb.getMax().y;
			p.z = (j&4) ? aabb.getMin().z : aabb.getMax().z;
			bodyB.push_back( p );
		}
	}
	*/
	
	// calculate basis of light space projection
	SVector3 ly = -lightDir;
	SVector3 lx = ly.cross( viewDir ).getNormalized();
	SVector3 lz = lx.cross( ly );
	SMatrix4x4 lightW;
	lightW.identify();
	lightW.getAxisX() = lx;
	lightW.getAxisY() = ly;
	lightW.getAxisZ() = lz;

	SMatrix4x4 lightV;
	D3DXMatrixInverse( &lightV, NULL, &lightW );

	// rotate bound body points from world into light projection space and calculate AABB there
	D3DXVec3TransformCoordArray( &bodyB.v[0], sizeof(SVector3), &bodyB.v[0], sizeof(SVector3), &lightV, bodyB.v.size() );
	CAABox bodyLBounds;
	bodyLBounds.setNull();
	for( i = 0; i < bodyB.v.size(); ++i )
		bodyLBounds.extend( bodyB.v[i] );

	float zextent = cam.getZFar() - cam.getZNear();
	float zLextent = bodyLBounds.getMax().z - bodyLBounds.getMin().z;
	if( zLextent < zextent )
		zextent = zLextent;
	
	// calculate free parameter N
	double sinGamma = sqrt( 1.0-dotProd*dotProd );
	const double n = ( cam.getZNear() + sqrt(cam.getZNear() * (cam.getZNear() + zextent)) ) / sinGamma;

	// origin in this light space: looking at center of bounds, from distance n
	SVector3 lightSpaceO = bodyLBounds.getCenter();
	lightSpaceO.z = bodyLBounds.getMin().z - n;

	// go through bound points in light space, and compute projected bound
	float maxx = 0.0f, maxy = 0.0f, maxz = 0.0f;
	for( i = 0; i < bodyB.v.size(); ++i )
	{
		SVector3 tmp = bodyB.v[i] - lightSpaceO;
		assert( tmp.z > 0.0f );
		maxx = max( maxx, fabsf(tmp.x / tmp.z) );
		maxy = max( maxy, fabsf(tmp.y / tmp.z) );
		maxz = max( maxz, tmp.z );
	}

	SVector3 lpos;
	D3DXVec3TransformCoord( &lpos, &lightSpaceO, &lightW );
	lightW.getOrigin() = lpos;

	SMatrix4x4 lightProj;
	D3DXMatrixPerspectiveLH( &lightProj, 2.0f*maxx*n, 2.0f*maxy*n, n, maxz );

	SMatrix4x4 lsPermute, lsOrtho;
	lsPermute._11 = 1.f; lsPermute._12 = 0.f; lsPermute._13 = 0.f; lsPermute._14 = 0.f;
	lsPermute._21 = 0.f; lsPermute._22 = 0.f; lsPermute._23 =-1.f; lsPermute._24 = 0.f;
	lsPermute._31 = 0.f; lsPermute._32 = 1.f; lsPermute._33 = 0.f; lsPermute._34 = 0.f;
	lsPermute._41 = 0.f; lsPermute._42 = -0.5f; lsPermute._43 = 1.5f; lsPermute._44 = 1.f;

	D3DXMatrixOrthoLH( &lsOrtho, 2.f, 1.f, 0.5f, 2.5f );
	lsPermute *= lsOrtho;
	lightProj *= lsPermute;

	G_RENDERCTX->getCamera().setCameraMatrix( lightW );

	SMatrix4x4 lightFinal = G_RENDERCTX->getCamera().getViewMatrix() * lightProj;

	// unit cube clipping
	/*
	{
		// receiver hull
		std::vector<SVector3> receiverPts;
		receiverPts.reserve( gSceneReceivers.size() * 8 );
		int nreceivers = gSceneReceivers.size();
		for( i = 0; i < nreceivers; ++i )
		{
			const CAABox& aabb = gSceneReceivers[i].aabb;
			for( j = 0; j < 8; ++j )
			{
				SVector3 p;
				p.x = (j&1) ? aabb.getMin().x : aabb.getMax().x;
				p.y = (j&2) ? aabb.getMin().y : aabb.getMax().y;
				p.z = (j&4) ? aabb.getMin().z : aabb.getMax().z;
				receiverPts.push_back( p );
			}
		}

		// transform to light post-perspective space
		D3DXVec3TransformCoordArray( &receiverPts[0], sizeof(SVector3), &receiverPts[0], sizeof(SVector3), &lightFinal, receiverPts.size() );
		CAABox recvBounds;
		recvBounds.setNull();
		for( i = 0; i < receiverPts.size(); ++i )
			recvBounds.extend( receiverPts[i] );
		
		recvBounds.getMax().x = min( 1.f, recvBounds.getMax().x );
		recvBounds.getMin().x = max(-1.f, recvBounds.getMin().x );
		recvBounds.getMax().y = min( 1.f, recvBounds.getMax().y );
		recvBounds.getMin().y = max(-1.f, recvBounds.getMin().y );
		float boxWidth = recvBounds.getMax().x - recvBounds.getMin().x;
		float boxHeight = recvBounds.getMax().y - recvBounds.getMin().y;

		if( !FLT_ALMOST_ZERO(boxWidth) && !FLT_ALMOST_ZERO(boxHeight) )
		{
			float boxX = ( recvBounds.getMax().x + recvBounds.getMin().x ) * 0.5f;
			float boxY = ( recvBounds.getMax().y + recvBounds.getMin().y ) * 0.5f;

			SMatrix4x4 clipMatrix(
				2.f/boxWidth,  0.f, 0.f, 0.f,
				0.f, 2.f/boxHeight, 0.f, 0.f,
				0.f,           0.f, 1.f, 0.f,
				-2.f*boxX/boxWidth, -2.f*boxY/boxHeight, 0.f, 1.f );
			lightProj *= clipMatrix;
		}
	}
	*/

	G_RENDERCTX->getCamera().setProjectionMatrix( lightProj );
}