genesis-3d_engine/Engine/app/apputil/shadowmaputil.cc

354 lines
13 KiB
C++
Raw Normal View History

/****************************************************************************
Copyright (c) 2011-2013,WebJet Business Division,CYOU
http://www.genesis-3d.com.cn
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
****************************************************************************/
#include "stdneb.h"
#include "shadowmaputil.h"
#include "math/frustum.h"
namespace AppUtil
{
void ShadowMapUtil::CalculateAABBPoints(Math::float4* aabbPoints, const Math::float4& sceneCenter, const Math::float4& sceneExtends)
{
//This map enables us to use a for loop and do vector math.
static const Math::float4 vExtentsMap[8] =
{
Math::float4(1.0f, 1.0f, -1.0f, 1.0f),
Math::float4(-1.0f, 1.0f, -1.0f, 1.0f),
Math::float4(1.0f, -1.0f, -1.0f, 1.0f),
Math::float4(-1.0f, -1.0f, -1.0f, 1.0f),
Math::float4(1.0f, 1.0f, 1.0f, 1.0f),
Math::float4(-1.0f, 1.0f, 1.0f, 1.0f),
Math::float4(1.0f, -1.0f, 1.0f, 1.0f),
Math::float4(-1.0f, -1.0f, 1.0f, 1.0f)
};
for( IndexT index = 0; index < 8; ++index )
{
aabbPoints[index] = Math::float4::multiply(vExtentsMap[index], sceneExtends) + sceneCenter;
}
}
void ShadowMapUtil::CreateFrustumPointsFromCascadeInterval(float fCascadeIntervalBegin, float fCascadeIntervalEnd, const Math::matrix44 &vProjection,Math::float4* pvCornerPointsWorld)
{
Math::frustum::ComputeFrustumFromProjection( pvCornerPointsWorld,fCascadeIntervalBegin,fCascadeIntervalEnd, vProjection, 0.0f != vProjection.mx[3][2]);
}
// From DX SDK
void ShadowMapUtil::ComputeNearAndFar( float& fNearPlane, float& fFarPlane, Math::float4 vLightCameraOrthographicMin, Math::float4 vLightCameraOrthographicMax, Math::float4* pvPointsInCameraView )
{
//--------------------------------------------------------------------------------------
// Used to compute an intersection of the orthographic projection and the Scene AABB
//--------------------------------------------------------------------------------------
struct Triangle
{
Math::float4 pt[3];
bool culled;
};
// Initialize the near and far planes
fNearPlane = S_C_FLOAT4MAX.x();
fFarPlane = -S_C_FLOAT4MAX.x();
Triangle triangleList[16];
IndexT iTriangleCnt = 1;
triangleList[0].pt[0] = pvPointsInCameraView[0];
triangleList[0].pt[1] = pvPointsInCameraView[1];
triangleList[0].pt[2] = pvPointsInCameraView[2];
triangleList[0].culled = false;
// These are the indices used to tesselate an AABB into a list of triangles.
static const IndexT iAABBTriIndexes[] =
{
0,1,2, 1,2,3,
4,5,6, 5,6,7,
0,2,4, 2,4,6,
1,3,5, 3,5,7,
0,1,4, 1,4,5,
2,3,6, 3,6,7
};
IndexT iPointPassesCollision[3];
// At a high level:
// 1. Iterate over all 12 triangles of the AABB.
// 2. Clip the triangles against each plane. Create new triangles as needed.
// 3. Find the min and max z values as the near and far plane.
//This is easier because the triangles are in camera spacing making the collisions tests simple comparisions.
float fLightCameraOrthographicMinX = vLightCameraOrthographicMin.x();
float fLightCameraOrthographicMaxX = vLightCameraOrthographicMax.x();
float fLightCameraOrthographicMinY = vLightCameraOrthographicMin.x();
float fLightCameraOrthographicMaxY = vLightCameraOrthographicMax.x();
for( IndexT AABBTriIter = 0; AABBTriIter < 12; ++AABBTriIter )
{
triangleList[0].pt[0] = pvPointsInCameraView[ iAABBTriIndexes[ AABBTriIter*3 + 0 ] ];
triangleList[0].pt[1] = pvPointsInCameraView[ iAABBTriIndexes[ AABBTriIter*3 + 1 ] ];
triangleList[0].pt[2] = pvPointsInCameraView[ iAABBTriIndexes[ AABBTriIter*3 + 2 ] ];
iTriangleCnt = 1;
triangleList[0].culled = false;
// Clip each invidual triangle against the 4 frustums. When ever a triangle is clipped into new triangles,
//add them to the list.
for( IndexT frustumPlaneIter = 0; frustumPlaneIter < 4; ++frustumPlaneIter )
{
Math::scalar fEdge;
IndexT iComponent;
if( frustumPlaneIter == 0 )
{
fEdge = fLightCameraOrthographicMinX; // todo make float temp
iComponent = 0;
}
else if( frustumPlaneIter == 1 )
{
fEdge = fLightCameraOrthographicMaxX;
iComponent = 0;
}
else if( frustumPlaneIter == 2 )
{
fEdge = fLightCameraOrthographicMinY;
iComponent = 1;
}
else
{
fEdge = fLightCameraOrthographicMaxY;
iComponent = 1;
}
for( IndexT triIter=0; triIter < iTriangleCnt; ++triIter )
{
// We don't delete triangles, so we skip those that have been culled.
if( !triangleList[triIter].culled )
{
IndexT iInsideVertCount = 0;
Math::float4 tempOrder;
// Test against the correct frustum plane.
// This could be written more compactly, but it would be harder to understand.
if( frustumPlaneIter == 0 )
{
for( IndexT triPtIter=0; triPtIter < 3; ++triPtIter )
{
if( triangleList[triIter].pt[triPtIter].x() >
vLightCameraOrthographicMin.x() )
{
iPointPassesCollision[triPtIter] = 1;
}
else
{
iPointPassesCollision[triPtIter] = 0;
}
iInsideVertCount += iPointPassesCollision[triPtIter];
}
}
else if( frustumPlaneIter == 1 )
{
for( IndexT triPtIter=0; triPtIter < 3; ++triPtIter )
{
if( triangleList[triIter].pt[triPtIter].x() <
vLightCameraOrthographicMax.x() )
{
iPointPassesCollision[triPtIter] = 1;
}
else
{
iPointPassesCollision[triPtIter] = 0;
}
iInsideVertCount += iPointPassesCollision[triPtIter];
}
}
else if( frustumPlaneIter == 2 )
{
for( IndexT triPtIter=0; triPtIter < 3; ++triPtIter )
{
if( triangleList[triIter].pt[triPtIter].y() >
vLightCameraOrthographicMin.y() )
{
iPointPassesCollision[triPtIter] = 1;
}
else
{
iPointPassesCollision[triPtIter] = 0;
}
iInsideVertCount += iPointPassesCollision[triPtIter];
}
}
else
{
for( IndexT triPtIter=0; triPtIter < 3; ++triPtIter )
{
if( triangleList[triIter].pt[triPtIter].y() <
vLightCameraOrthographicMax.y() )
{
iPointPassesCollision[triPtIter] = 1;
}
else
{
iPointPassesCollision[triPtIter] = 0;
}
iInsideVertCount += iPointPassesCollision[triPtIter];
}
}
// Move the points that pass the frustum test to the begining of the array.
if( iPointPassesCollision[1] && !iPointPassesCollision[0] )
{
tempOrder = triangleList[triIter].pt[0];
triangleList[triIter].pt[0] = triangleList[triIter].pt[1];
triangleList[triIter].pt[1] = tempOrder;
iPointPassesCollision[0] = 1;
iPointPassesCollision[1] = 0;
}
if( iPointPassesCollision[2] && !iPointPassesCollision[1] )
{
tempOrder = triangleList[triIter].pt[1];
triangleList[triIter].pt[1] = triangleList[triIter].pt[2];
triangleList[triIter].pt[2] = tempOrder;
iPointPassesCollision[1] = 1;
iPointPassesCollision[2] = 0;
}
if( iPointPassesCollision[1] && !iPointPassesCollision[0] )
{
tempOrder = triangleList[triIter].pt[0];
triangleList[triIter].pt[0] = triangleList[triIter].pt[1];
triangleList[triIter].pt[1] = tempOrder;
iPointPassesCollision[0] = 1;
iPointPassesCollision[1] = 0;
}
if( iInsideVertCount == 0 )
{ // All points failed. We're done,
triangleList[triIter].culled = true;
}
else if( iInsideVertCount == 1 )
{// One point passed. Clip the triangle against the Frustum plane
triangleList[triIter].culled = false;
//
Math::float4 vVert0ToVert1 = triangleList[triIter].pt[1] - triangleList[triIter].pt[0];
Math::float4 vVert0ToVert2 = triangleList[triIter].pt[2] - triangleList[triIter].pt[0];
// Find the collision ratio.
Math::scalar fHitPointTimeRatio = fEdge - triangleList[triIter].pt[0][iComponent] ;
// Calculate the distance along the vector as ratio of the hit ratio to the component.
Math::scalar fDistanceAlongVector01 = fHitPointTimeRatio / vVert0ToVert1[iComponent];
Math::scalar fDistanceAlongVector02 = fHitPointTimeRatio / vVert0ToVert2[iComponent];
// Add the point plus a percentage of the vector.
vVert0ToVert1 *= fDistanceAlongVector01;
vVert0ToVert1 += triangleList[triIter].pt[0];
vVert0ToVert2 *= fDistanceAlongVector02;
vVert0ToVert2 += triangleList[triIter].pt[0];
triangleList[triIter].pt[1] = vVert0ToVert2;
triangleList[triIter].pt[2] = vVert0ToVert1;
}
else if( iInsideVertCount == 2 )
{ // 2 in // tesselate into 2 triangles
// Copy the triangle\(if it exists) after the current triangle out of
// the way so we can override it with the new triangle we're inserting.
triangleList[iTriangleCnt] = triangleList[triIter+1];
triangleList[triIter].culled = false;
triangleList[triIter+1].culled = false;
// Get the vector from the outside point into the 2 inside points.
Math::float4 vVert2ToVert0 = triangleList[triIter].pt[0] - triangleList[triIter].pt[2];
Math::float4 vVert2ToVert1 = triangleList[triIter].pt[1] - triangleList[triIter].pt[2];
// Get the hit point ratio.
Math::scalar fHitPointTime_2_0 = fEdge - triangleList[triIter].pt[2][iComponent];
Math::scalar fDistanceAlongVector_2_0 = fHitPointTime_2_0 / vVert2ToVert0[iComponent];
// Calcaulte the new vert by adding the percentage of the vector plus point 2.
vVert2ToVert0 *= fDistanceAlongVector_2_0;
vVert2ToVert0 += triangleList[triIter].pt[2];
// Add a new triangle.
triangleList[triIter+1].pt[0] = triangleList[triIter].pt[0];
triangleList[triIter+1].pt[1] = triangleList[triIter].pt[1];
triangleList[triIter+1].pt[2] = vVert2ToVert0;
//Get the hit point ratio.
Math::scalar fHitPointTime_2_1 = fEdge - triangleList[triIter].pt[2][iComponent];
Math::scalar fDistanceAlongVector_2_1 = fHitPointTime_2_1 / vVert2ToVert1[iComponent];
vVert2ToVert1 *= fDistanceAlongVector_2_1;
vVert2ToVert1 += triangleList[triIter].pt[2];
triangleList[triIter].pt[0] = triangleList[triIter+1].pt[1];
triangleList[triIter].pt[1] = triangleList[triIter+1].pt[2];
triangleList[triIter].pt[2] = vVert2ToVert1;
// Cncrement triangle count and skip the triangle we just inserted.
++iTriangleCnt;
++triIter;
}
else
{ // all in
triangleList[triIter].culled = false;
}
}// end if !culled loop
}
}
for( IndexT index=0; index < iTriangleCnt; ++index )
{
if( !triangleList[index].culled )
{
// Set the near and far plan and the min and max z values respectivly.
for( int vertind = 0; vertind < 3; ++ vertind )
{
float fTriangleCoordZ = triangleList[index].pt[vertind].z();
if( fNearPlane > fTriangleCoordZ )
{
fNearPlane = fTriangleCoordZ;
}
if( fFarPlane <fTriangleCoordZ )
{
fFarPlane = fTriangleCoordZ;
}
}
}
}
}
}
void ShadowMapUtil::CalculateCloseSphereFromFrustum(const Math::float4 points[8], Math::float4& outCenter, float& outRadius)
{
Math::float4 halfFarLongVec = (points[0] - points[3]) * 0.5f;
Math::float4 halfNearLongVec = (points[4] - points[7]) * 0.5f;
float squareFarRadius = Math::float4::dot3(halfFarLongVec, halfFarLongVec);
float squareNearRadius = Math::float4::dot3(halfNearLongVec, halfNearLongVec);
float subZ = Math::n_abs( points[0].z() - points[4].z() );
float offsetZNear = 0.5f * ((squareFarRadius - squareNearRadius) / subZ + subZ);
float squareOffsetZNear = offsetZNear * offsetZNear;
outCenter = Math::float4(0.0f, 0.0f, points[4].z() - offsetZNear, 1.0f);
outRadius = Math::n_sqrt( squareOffsetZNear + squareNearRadius ) + 0.001f;
}
}