573 lines
16 KiB
C++
573 lines
16 KiB
C++
/* ScummVM - Graphic Adventure Engine
|
|
*
|
|
* ScummVM is the legal property of its developers, whose names
|
|
* are too numerous to list here. Please refer to the COPYRIGHT
|
|
* file distributed with this source distribution.
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*
|
|
*/
|
|
|
|
/*
|
|
* Copyright (C) 2006-2010 - Frictional Games
|
|
*
|
|
* This file is part of HPL1 Engine.
|
|
*/
|
|
|
|
#include "hpl1/engine/math/BoundingVolume.h"
|
|
|
|
#include "hpl1/engine/graphics/LowLevelGraphics.h"
|
|
#include "hpl1/engine/math/Math.h"
|
|
|
|
namespace hpl {
|
|
|
|
static constexpr cTriEdge kvBVEdges[12] = {
|
|
cTriEdge(1, 0, 0, 2),
|
|
cTriEdge(3, 1, 0, 5),
|
|
cTriEdge(2, 3, 0, 3),
|
|
cTriEdge(0, 2, 0, 4),
|
|
|
|
cTriEdge(0, 4, 4, 2),
|
|
cTriEdge(4, 6, 4, 1),
|
|
cTriEdge(6, 2, 4, 3),
|
|
cTriEdge(4, 5, 1, 2),
|
|
|
|
cTriEdge(5, 7, 1, 5),
|
|
cTriEdge(7, 6, 1, 3),
|
|
cTriEdge(1, 5, 2, 5),
|
|
cTriEdge(3, 7, 5, 3)};
|
|
|
|
static constexpr cVector3f globalNormals[6] = {
|
|
cVector3f(1, 0, 0),
|
|
cVector3f(-1, 0, 0),
|
|
|
|
cVector3f(0, 1, 0),
|
|
cVector3f(0, -1, 0),
|
|
|
|
cVector3f(0, 0, 1),
|
|
cVector3f(0, 0, -1)};
|
|
|
|
static bool globalfacingLight[6] = {false, false, false, false, false, false};
|
|
|
|
static const int kvFacePoints[6] = {0, 5, 5, 6, 4, 7};
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// SHADOW VOLUME
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
bool cShadowVolumeBV::CollideBoundingVolume(cBoundingVolume *aBV) {
|
|
// Do a simple sphere collide test
|
|
if (CollideBVSphere(aBV) == false)
|
|
return false;
|
|
|
|
return CollideBVAABB(aBV);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
bool cShadowVolumeBV::CollideBVSphere(cBoundingVolume *aBV) {
|
|
for (int i = 0; i < mlPlaneCount; ++i) {
|
|
float fDist = cMath::PlaneToPointDist(mvPlanes[i], aBV->GetWorldCenter());
|
|
|
|
if (fDist < -aBV->GetRadius()) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
bool cShadowVolumeBV::CollideBVAABB(cBoundingVolume *aBV) {
|
|
cVector3f vMax = aBV->GetMax();
|
|
cVector3f vMin = aBV->GetMin();
|
|
|
|
// Get the corners from the AAB
|
|
cVector3f vCorners[9] = {
|
|
cVector3f(vMax.x, vMax.y, vMax.z),
|
|
cVector3f(vMax.x, vMax.y, vMin.z),
|
|
cVector3f(vMax.x, vMin.y, vMax.z),
|
|
cVector3f(vMax.x, vMin.y, vMin.z),
|
|
|
|
cVector3f(vMin.x, vMax.y, vMax.z),
|
|
cVector3f(vMin.x, vMax.y, vMin.z),
|
|
cVector3f(vMin.x, vMin.y, vMax.z),
|
|
cVector3f(vMin.x, vMin.y, vMin.z),
|
|
|
|
// The "fuling", add center as well...
|
|
aBV->GetPosition()};
|
|
|
|
// Go through all the planes
|
|
for (int i = 0; i < mlPlaneCount; i++) {
|
|
int lInCount = 9;
|
|
// bool bIsIn = true;
|
|
|
|
for (int j = 0; j < 9; j++) {
|
|
float fDist = cMath::PlaneToPointDist(mvPlanes[i], vCorners[j]);
|
|
if (fDist < 0) {
|
|
lInCount--;
|
|
// bIsIn = false;
|
|
}
|
|
}
|
|
|
|
if (lInCount == 0)
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// CONSTRUCTORS
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
cBoundingVolume::cBoundingVolume() {
|
|
m_mtxTransform = cMatrixf::Identity;
|
|
|
|
mvLocalMax = 0;
|
|
mvLocalMin = 0;
|
|
|
|
mvPosition = 0;
|
|
mvPivot = 0;
|
|
mvSize = 0;
|
|
mfRadius = 0;
|
|
|
|
mbPositionUpdated = true;
|
|
mbSizeUpdated = true;
|
|
|
|
mShadowVolume.mvPoints.reserve(8 * 4);
|
|
mbShadowPlanesNeedUpdate = true;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// PUBLIC METHODS
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
cVector3f cBoundingVolume::GetMax() {
|
|
UpdateSize();
|
|
return mvWorldMax;
|
|
}
|
|
|
|
cVector3f cBoundingVolume::GetMin() {
|
|
UpdateSize();
|
|
return mvWorldMin;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
cVector3f cBoundingVolume::GetLocalMax() {
|
|
return mvLocalMax;
|
|
}
|
|
|
|
cVector3f cBoundingVolume::GetLocalMin() {
|
|
return mvLocalMin;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
void cBoundingVolume::SetLocalMinMax(const cVector3f &avMin, const cVector3f &avMax) {
|
|
mvLocalMax = avMax;
|
|
mvLocalMin = avMin;
|
|
|
|
mbSizeUpdated = true;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
cVector3f cBoundingVolume::GetLocalCenter() {
|
|
return mvPivot;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
cVector3f cBoundingVolume::GetWorldCenter() {
|
|
UpdateSize();
|
|
|
|
return m_mtxTransform.GetTranslation() + mvPivot;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
void cBoundingVolume::SetPosition(const cVector3f &avPos) {
|
|
m_mtxTransform.SetTranslation(avPos);
|
|
|
|
mbPositionUpdated = true;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
cVector3f cBoundingVolume::GetPosition() {
|
|
return m_mtxTransform.GetTranslation();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
void cBoundingVolume::SetTransform(const cMatrixf &a_mtxTransform) {
|
|
m_mtxTransform = a_mtxTransform;
|
|
|
|
mbSizeUpdated = true;
|
|
}
|
|
|
|
const cMatrixf &cBoundingVolume::GetTransform() {
|
|
return m_mtxTransform;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
void cBoundingVolume::SetSize(const cVector3f &avSize) {
|
|
mvLocalMax = avSize * 0.5;
|
|
mvLocalMin = avSize * -0.5;
|
|
|
|
mbSizeUpdated = true;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
cVector3f cBoundingVolume::GetSize() {
|
|
UpdateSize();
|
|
|
|
return mvSize;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
float cBoundingVolume::GetRadius() {
|
|
UpdateSize();
|
|
|
|
return mfRadius;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
cShadowVolumeBV *cBoundingVolume::GetShadowVolume(const cVector3f &avLightPos,
|
|
float afLightRange, bool abForceUpdate) {
|
|
if (cMath::PointBVCollision(avLightPos, *this))
|
|
return NULL;
|
|
|
|
if (!abForceUpdate && !mbShadowPlanesNeedUpdate)
|
|
return &mShadowVolume;
|
|
|
|
// Set size 0.
|
|
mShadowVolume.mvPoints.resize(0);
|
|
|
|
// Get the corners.
|
|
cVector3f vMax = GetMax();
|
|
cVector3f vMin = GetMin();
|
|
cVector3f vCorners[8];
|
|
vCorners[0] = cVector3f(vMax.x, vMax.y, vMax.z);
|
|
vCorners[1] = cVector3f(vMax.x, vMax.y, vMin.z);
|
|
vCorners[2] = cVector3f(vMax.x, vMin.y, vMax.z);
|
|
vCorners[3] = cVector3f(vMax.x, vMin.y, vMin.z);
|
|
|
|
vCorners[4] = cVector3f(vMin.x, vMax.y, vMax.z);
|
|
vCorners[5] = cVector3f(vMin.x, vMax.y, vMin.z);
|
|
vCorners[6] = cVector3f(vMin.x, vMin.y, vMax.z);
|
|
vCorners[7] = cVector3f(vMin.x, vMin.y, vMin.z);
|
|
|
|
/////////////////////////////////////////////////////////////////////
|
|
// Iterate the faces and check which ones are facing the light.
|
|
// int lNearPoint = -1;
|
|
mShadowVolume.mlPlaneCount = 0;
|
|
for (int face = 0; face < 6; face++) {
|
|
globalfacingLight[face] = cMath::Vector3Dot(globalNormals[face],
|
|
vCorners[kvFacePoints[face]] - avLightPos) < 0;
|
|
|
|
// Get a point for the near plane. (any edge point will do)
|
|
if (globalfacingLight[face]) {
|
|
mShadowVolume.mvPlanes[mShadowVolume.mlPlaneCount] = cPlanef(
|
|
globalNormals[face] * -1.0f, vCorners[kvFacePoints[face]]);
|
|
mShadowVolume.mlPlaneCount++;
|
|
}
|
|
}
|
|
|
|
mShadowVolume.mlCapPlanes = mShadowVolume.mlPlaneCount;
|
|
|
|
// The direction a point is pushed away in
|
|
cVector3f vDir;
|
|
|
|
// The length to push the shadow points.
|
|
float fPushLength = afLightRange * kSqrt2f;
|
|
|
|
//////////////////////////////////////////////////////////
|
|
// Iterate the edges and build quads from the silhouette
|
|
for (int edge = 0; edge < 12; edge++) {
|
|
const cTriEdge &Edge = kvBVEdges[edge];
|
|
|
|
const bool facingLight1 = globalfacingLight[Edge.tri1];
|
|
const bool facingLight2 = globalfacingLight[Edge.tri2];
|
|
|
|
if ((facingLight1 && !facingLight2) || (facingLight2 && !facingLight1)) {
|
|
if (facingLight1) {
|
|
mShadowVolume.mvPoints.push_back(vCorners[Edge.point1]);
|
|
mShadowVolume.mvPoints.push_back(vCorners[Edge.point2]);
|
|
|
|
vDir = (vCorners[Edge.point2] - avLightPos);
|
|
vDir.Normalise();
|
|
mShadowVolume.mvPoints.push_back(vCorners[Edge.point2] + vDir * fPushLength);
|
|
|
|
vDir = (vCorners[Edge.point1] - avLightPos);
|
|
vDir.Normalise();
|
|
mShadowVolume.mvPoints.push_back(vCorners[Edge.point1] + vDir * fPushLength);
|
|
} else {
|
|
mShadowVolume.mvPoints.push_back(vCorners[Edge.point2]);
|
|
mShadowVolume.mvPoints.push_back(vCorners[Edge.point1]);
|
|
|
|
vDir = (vCorners[Edge.point1] - avLightPos);
|
|
vDir.Normalise();
|
|
mShadowVolume.mvPoints.push_back(vCorners[Edge.point1] + vDir * fPushLength);
|
|
|
|
vDir = (vCorners[Edge.point2] - avLightPos);
|
|
vDir.Normalise();
|
|
mShadowVolume.mvPoints.push_back(vCorners[Edge.point2] + vDir * fPushLength);
|
|
}
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////////
|
|
// Create the side planes:
|
|
|
|
for (int i = 0; i < (int)mShadowVolume.mvPoints.size(); i += 4) {
|
|
// Normal should point inwards
|
|
cVector3f vNormal = cMath::Vector3Cross(
|
|
mShadowVolume.mvPoints[i + 1] - mShadowVolume.mvPoints[i + 0],
|
|
mShadowVolume.mvPoints[i + 2] - mShadowVolume.mvPoints[i + 0]);
|
|
mShadowVolume.mvPlanes[mShadowVolume.mlPlaneCount].FromNormalPoint(vNormal,
|
|
mShadowVolume.mvPoints[i + 0]);
|
|
mShadowVolume.mvPlanes[mShadowVolume.mlPlaneCount].Normalise();
|
|
|
|
mShadowVolume.mlPlaneCount++;
|
|
}
|
|
|
|
return &mShadowVolume;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
void cBoundingVolume::DrawEdges(const cVector3f &avLightPos, float afLightRange, iLowLevelGraphics *apLowLevelGraphics) {
|
|
cShadowVolumeBV *pVolume = GetShadowVolume(avLightPos, afLightRange, false);
|
|
|
|
apLowLevelGraphics->SetBlendActive(true);
|
|
apLowLevelGraphics->SetBlendFunc(eBlendFunc_One, eBlendFunc_One);
|
|
apLowLevelGraphics->SetDepthWriteActive(false);
|
|
tVertexVec vVtx;
|
|
vVtx.resize(4);
|
|
|
|
for (int capplane = 0; capplane < mShadowVolume.mlCapPlanes; capplane++) {
|
|
mShadowVolume.mvPlanes[capplane].CalcNormal();
|
|
apLowLevelGraphics->DrawLine(GetWorldCenter(), GetWorldCenter() + mShadowVolume.mvPlanes[capplane].normal * -0.5f, cColor(1, 1, 1, 1));
|
|
}
|
|
|
|
int lPlane = mShadowVolume.mlCapPlanes;
|
|
for (int quad = 0; quad < (int)pVolume->mvPoints.size(); quad += 4) {
|
|
|
|
for (int i = 0; i < 4; i++)
|
|
vVtx[i].pos = pVolume->mvPoints[quad + i];
|
|
|
|
apLowLevelGraphics->DrawQuad(vVtx, cColor(0.2f, 0, 0.2f));
|
|
|
|
cVector3f vCenter = (vVtx[1].pos + vVtx[0].pos) * 0.5f;
|
|
mShadowVolume.mvPlanes[lPlane].CalcNormal();
|
|
apLowLevelGraphics->DrawLine(vCenter, vCenter + mShadowVolume.mvPlanes[lPlane].normal * -0.5f, cColor(1, 1, 1, 1));
|
|
lPlane++;
|
|
}
|
|
|
|
apLowLevelGraphics->SetBlendActive(false);
|
|
apLowLevelGraphics->SetDepthWriteActive(true);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
void cBoundingVolume::AddArrayPoints(const float *apArray, int alNumOfVectors) {
|
|
cBVTempArray temp;
|
|
temp.mpArray = apArray;
|
|
temp.mlSize = alNumOfVectors;
|
|
|
|
mlstArrays.push_back(temp);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
void cBoundingVolume::CreateFromPoints(int alStride) {
|
|
mvLocalMax = cVector3f(-100000, -100000, -100000);
|
|
mvLocalMin = cVector3f(100000, 100000, 100000);
|
|
|
|
for (tBVTempArrayListIt it = mlstArrays.begin(); it != mlstArrays.end(); it++) {
|
|
// Loop through all the vectors and find min and max
|
|
const float *apVec = it->mpArray;
|
|
int lNumOfVectors = it->mlSize;
|
|
while (lNumOfVectors) {
|
|
// Min and max X
|
|
if (apVec[0] < mvLocalMin.x)
|
|
mvLocalMin.x = apVec[0];
|
|
if (apVec[0] > mvLocalMax.x)
|
|
mvLocalMax.x = apVec[0];
|
|
|
|
// Min and max Y
|
|
if (apVec[1] < mvLocalMin.y)
|
|
mvLocalMin.y = apVec[1];
|
|
if (apVec[1] > mvLocalMax.y)
|
|
mvLocalMax.y = apVec[1];
|
|
|
|
// Min and max Z
|
|
if (apVec[2] < mvLocalMin.z)
|
|
mvLocalMin.z = apVec[2];
|
|
if (apVec[2] > mvLocalMax.z)
|
|
mvLocalMax.z = apVec[2];
|
|
|
|
apVec += alStride;
|
|
lNumOfVectors--;
|
|
}
|
|
}
|
|
mlstArrays.clear();
|
|
|
|
// Update the used size
|
|
mbPositionUpdated = true;
|
|
mbSizeUpdated = true;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// PRIVATE METHODS
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
void cBoundingVolume::UpdateSize() {
|
|
if (mbSizeUpdated) {
|
|
cMatrixf mtxRot = m_mtxTransform.GetRotation();
|
|
|
|
// Transform the local corners
|
|
cVector3f vCorners[8];
|
|
vCorners[0] = cMath::MatrixMul(mtxRot, cVector3f(mvLocalMax.x, mvLocalMax.y, mvLocalMax.z));
|
|
vCorners[1] = cMath::MatrixMul(mtxRot, cVector3f(mvLocalMax.x, mvLocalMax.y, mvLocalMin.z));
|
|
vCorners[2] = cMath::MatrixMul(mtxRot, cVector3f(mvLocalMax.x, mvLocalMin.y, mvLocalMax.z));
|
|
vCorners[3] = cMath::MatrixMul(mtxRot, cVector3f(mvLocalMax.x, mvLocalMin.y, mvLocalMin.z));
|
|
|
|
vCorners[4] = cMath::MatrixMul(mtxRot, cVector3f(mvLocalMin.x, mvLocalMax.y, mvLocalMax.z));
|
|
vCorners[5] = cMath::MatrixMul(mtxRot, cVector3f(mvLocalMin.x, mvLocalMax.y, mvLocalMin.z));
|
|
vCorners[6] = cMath::MatrixMul(mtxRot, cVector3f(mvLocalMin.x, mvLocalMin.y, mvLocalMax.z));
|
|
vCorners[7] = cMath::MatrixMul(mtxRot, cVector3f(mvLocalMin.x, mvLocalMin.y, mvLocalMin.z));
|
|
|
|
mvMax = vCorners[0];
|
|
mvMin = vCorners[0];
|
|
|
|
// Calculate the transformed min and max
|
|
for (int i = 1; i < 8; i++) {
|
|
// X
|
|
if (vCorners[i].x < mvMin.x)
|
|
mvMin.x = vCorners[i].x;
|
|
else if (vCorners[i].x > mvMax.x)
|
|
mvMax.x = vCorners[i].x;
|
|
|
|
// Y
|
|
if (vCorners[i].y < mvMin.y)
|
|
mvMin.y = vCorners[i].y;
|
|
else if (vCorners[i].y > mvMax.y)
|
|
mvMax.y = vCorners[i].y;
|
|
|
|
// Z
|
|
if (vCorners[i].z < mvMin.z)
|
|
mvMin.z = vCorners[i].z;
|
|
else if (vCorners[i].z > mvMax.z)
|
|
mvMax.z = vCorners[i].z;
|
|
}
|
|
|
|
// Get the transformed size.
|
|
mvSize = mvMax - mvMin;
|
|
|
|
// Get the local pivot (or offset from origo).
|
|
mvPivot = mvMax - (mvSize * 0.5f);
|
|
|
|
// Get radius as pivot to localmax
|
|
mfRadius = cMath::Vector3Dist(mvPivot, mvMax);
|
|
|
|
mbSizeUpdated = false;
|
|
mbPositionUpdated = true;
|
|
}
|
|
|
|
if (mbPositionUpdated) {
|
|
mvWorldMax = m_mtxTransform.GetTranslation() + mvMax;
|
|
mvWorldMin = m_mtxTransform.GetTranslation() + mvMin;
|
|
|
|
mbPositionUpdated = false;
|
|
|
|
mbShadowPlanesNeedUpdate = true;
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// SERIALIZE CLASS
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
kBeginSerializeBase(cBoundingVolume)
|
|
kSerializeVar(m_mtxTransform, eSerializeType_Matrixf)
|
|
kSerializeVar(mvLocalMax, eSerializeType_Vector3f)
|
|
kSerializeVar(mvLocalMin, eSerializeType_Vector3f)
|
|
kSerializeVar(mvMax, eSerializeType_Vector3f)
|
|
kSerializeVar(mvMin, eSerializeType_Vector3f)
|
|
kSerializeVar(mvPivot, eSerializeType_Vector3f)
|
|
kSerializeVar(mvWorldMax, eSerializeType_Vector3f)
|
|
kSerializeVar(mvWorldMin, eSerializeType_Vector3f)
|
|
kSerializeVar(mvPosition, eSerializeType_Vector3f)
|
|
kSerializeVar(mvSize, eSerializeType_Vector3f)
|
|
kSerializeVar(mfRadius, eSerializeType_Float32)
|
|
kEndSerialize()
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
/* OLD "NEAR PLANE CODE"
|
|
int lNearPoint =-1;
|
|
for(int face=0; face< 6; face++)
|
|
{
|
|
gvFaces[face].facingLight = cMath::Vector3Dot(gvFaces[face].normal,
|
|
vCorners[kvFacePoints[face]] - avLightPos)<0;
|
|
|
|
//Get a point for the near plane. (any edge point will do)
|
|
if(gvFaces[face].facingLight && lNearPoint<0)
|
|
{
|
|
lNearPoint = kvFacePoints[face];
|
|
}
|
|
}
|
|
|
|
//Build the near plane for the shadow.
|
|
cVector3f vLightNormal = GetWorldCenter() - avLightPos;
|
|
mShadowVolume.mvPlanes[0] = cPlanef(vLightNormal,vCorners[lNearPoint]);
|
|
mShadowVolume.mvPlanes[0].Normalise();
|
|
|
|
|
|
//The direction a point is pushed away in
|
|
cVector3f vDir;
|
|
|
|
float fPushLength = afLightRange*kSqrt2f;
|
|
|
|
//The number of planes created.
|
|
mShadowVolume.mlPlaneCount =1;*/
|
|
} // namespace hpl
|