You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

204 lines
5.5 KiB

/*==LICENSE==*
CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011 Cyan Worlds, Inc.
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/>.
You can contact Cyan Worlds, Inc. by email legal@cyan.com
or by snail mail at:
Cyan Worlds, Inc.
14617 N Newport Hwy
Mead, WA 99021
*==LICENSE==*/
#include "hsTypes.h"
#include "plPointShadowMaster.h"
#include "plShadowSlave.h"
#include "plShadowCaster.h"
#include "plMessage/plShadowCastMsg.h"
#include "plLightInfo.h"
#include "hsMatrix44.h"
#include "hsBounds.h"
#include "hsFastMath.h"
static const hsScalar kMinMinZ = 1.f; // totally random arbitrary number (has to be > 0).
static inline void QuickNorm( hsVector3& a, hsScalar& b )
{
hsScalar len = hsFastMath::InvSqrtAppr((a).MagnitudeSquared());
a *= len;
b *= len;
}
static inline hsVector3 CrossProd(const hsVector3& a, const hsPoint3& b)
{
return hsVector3(a.fY*b.fZ - a.fZ*b.fY, a.fZ*b.fX - a.fX*b.fZ, a.fX*b.fY - a.fY*b.fX);
}
static inline void InverseOfPureRotTran(const hsMatrix44& src, hsMatrix44& inv)
{
inv = src;
// We know this is a pure rotation and translation matrix, so
// we won't have to do a full inverse. Okay kids, don't try this
// at home.
inv.fMap[0][1] = src.fMap[1][0];
inv.fMap[0][2] = src.fMap[2][0];
inv.fMap[1][0] = src.fMap[0][1];
inv.fMap[1][2] = src.fMap[2][1];
inv.fMap[2][0] = src.fMap[0][2];
inv.fMap[2][1] = src.fMap[1][2];
hsPoint3 newTran(-src.fMap[0][3], -src.fMap[1][3], -src.fMap[2][3]);
inv.fMap[0][3] = newTran.InnerProduct((hsVector3*)&inv.fMap[0][0]);
inv.fMap[1][3] = newTran.InnerProduct((hsVector3*)&inv.fMap[1][0]);
inv.fMap[2][3] = newTran.InnerProduct((hsVector3*)&inv.fMap[2][0]);
}
////////////////////////////////////////////////////////////////////////////////////
// Point first
////////////////////////////////////////////////////////////////////////////////////
plPointShadowMaster::plPointShadowMaster()
{
fLastUp.Set(0,0,0);
}
plPointShadowMaster::~plPointShadowMaster()
{
fIsectPool.SetCount(fIsectPool.GetNumAlloc());
int i;
for( i = 0; i < fIsectPool.GetCount(); i++ )
delete fIsectPool[i];
}
plShadowSlave* plPointShadowMaster::INewSlave(const plShadowCaster* caster)
{
return TRACKED_NEW plPointShadowSlave;
}
void plPointShadowMaster::IBeginRender()
{
plShadowMaster::IBeginRender();
fIsectPool.SetCount(0);
}
void plPointShadowMaster::IComputeWorldToLight(const hsBounds3Ext& bnd, plShadowSlave* slave) const
{
const hsScalar kMinMag = 0.5f;
hsPoint3 from = fLightInfo->GetLightToWorld().GetTranslate();
hsPoint3 at = bnd.GetCenter();
hsVector3 atToFrom(&from, &at);
hsScalar distSq = atToFrom.MagnitudeSquared();
atToFrom *= hsFastMath::InvSqrtAppr(distSq);
hsPoint2 depth;
bnd.TestPlane(atToFrom, depth);
hsScalar fromDepth = atToFrom.InnerProduct(from);
hsScalar dist = fromDepth - depth.fY;
static hsScalar kMinDist = 3.f;
if( dist < kMinDist )
{
atToFrom *= kMinDist - dist;
from += atToFrom;
}
hsVector3 up(0,0,1.f);
if( CrossProd(up, (at - from)).MagnitudeSquared() < kMinMag )
{
up.Set(0, 1.f, 0);
}
hsMatrix44 w2light;
w2light.MakeCamera(&from, &at, &up); // mf_flip_up - mf
hsMatrix44 light2w;
InverseOfPureRotTran(w2light, light2w);
#ifdef CHECK_INVERSE
hsMatrix44 inv;
w2light.GetInverse(&inv);
#endif // CHECK_INVERSE
slave->fWorldToLight = w2light;
slave->fLightToWorld = light2w;
}
void plPointShadowMaster::IComputeProjections(plShadowCastMsg* castMsg, plShadowSlave* slave) const
{
slave->fView.SetPerspective(true);
}
void plPointShadowMaster::IComputeISect(const hsBounds3Ext& bnd, plShadowSlave* slave) const
{
int iIsect = fIsectPool.GetCount();
fIsectPool.ExpandAndZero(iIsect+1);
if( !fIsectPool[iIsect] )
{
fIsectPool[iIsect] = TRACKED_NEW plBoundsIsect;
}
plBoundsIsect* isect = fIsectPool[iIsect];
const hsBounds3Ext& wBnd = slave->fWorldBounds;
isect->SetBounds(wBnd);
slave->fIsect = isect;
}
void plPointShadowMaster::IComputeBounds(const hsBounds3Ext& wBnd, plShadowSlave* slave) const
{
// Plan here is to look at the bounds in the slave's local space.
// Our slave's bounds will clearly contain the shadow caster's bounds. It will also
// contain the bnd's corners projected out along the ray from the slave's position
// through each corner. They will extend fAttenDist farther than the center point
// of the bound.
hsBounds3Ext sBnd = wBnd;
sBnd.Transform(&slave->fWorldToLight);
hsBounds3Ext bnd = sBnd;
hsScalar dist = sBnd.GetCenter().fZ;
dist += slave->fAttenDist;
hsScalar minZ = sBnd.GetMaxs().fZ;
hsPoint3 p(sBnd.GetMins().fX * dist / minZ, sBnd.GetMins().fY * dist / minZ, dist);
bnd.Union(&p);
p.Set(sBnd.GetMaxs().fX * dist / minZ, sBnd.GetMaxs().fY * dist / minZ, dist);
bnd.Union(&p);
bnd.Transform(&slave->fLightToWorld);
slave->fWorldBounds = bnd;
}