/*==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 . 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; }