blobShadow.cpp
Engine/source/lighting/common/blobShadow.cpp
Public Variables
Detailed Description
Public Variables
Box3F gBlobShadowBox
Point3F gBlobShadowPoly [4]
SphereF gBlobShadowSphere
1 2//----------------------------------------------------------------------------- 3// Copyright (c) 2012 GarageGames, LLC 4// 5// Permission is hereby granted, free of charge, to any person obtaining a copy 6// of this software and associated documentation files (the "Software"), to 7// deal in the Software without restriction, including without limitation the 8// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 9// sell copies of the Software, and to permit persons to whom the Software is 10// furnished to do so, subject to the following conditions: 11// 12// The above copyright notice and this permission notice shall be included in 13// all copies or substantial portions of the Software. 14// 15// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 20// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 21// IN THE SOFTWARE. 22//----------------------------------------------------------------------------- 23 24#include "platform/platform.h" 25#include "lighting/common/blobShadow.h" 26 27#include "gfx/primBuilder.h" 28#include "gfx/gfxTextureManager.h" 29#include "gfx/bitmap/gBitmap.h" 30#include "math/mathUtils.h" 31#include "lighting/lightInfo.h" 32#include "lighting/lightingInterfaces.h" 33#include "T3D/shapeBase.h" 34#include "scene/sceneManager.h" 35#include "lighting/lightManager.h" 36#include "ts/tsMesh.h" 37 38DepthSortList BlobShadow::smDepthSortList; 39GFXTexHandle BlobShadow::smGenericShadowTexture = NULL; 40S32 BlobShadow::smGenericShadowDim = 32; 41U32 BlobShadow::smShadowMask = TerrainObjectType; 42F32 BlobShadow::smGenericRadiusSkew = 0.4f; // shrink radius of shape when it always uses generic shadow... 43 44Box3F gBlobShadowBox; 45SphereF gBlobShadowSphere; 46Point3F gBlobShadowPoly[4]; 47 48//-------------------------------------------------------------- 49 50BlobShadow::BlobShadow(SceneObject* parentObject, LightInfo* light, TSShapeInstance* shapeInstance) 51{ 52 mParentObject = parentObject; 53 mShapeBase = dynamic_cast<ShapeBase*>(parentObject); 54 mParentLight = light; 55 mShapeInstance = shapeInstance; 56 mRadius = 0.0f; 57 mLastRenderTime = 0; 58 mDepthBias = -0.0002f; 59 mInvShadowDistance = 1.0f; 60 generateGenericShadowBitmap(smGenericShadowDim); 61 setupStateBlocks(); 62} 63 64void BlobShadow::setupStateBlocks() 65{ 66 GFXStateBlockDesc sh; 67 sh.cullDefined = true; 68 sh.cullMode = GFXCullNone; 69 sh.zDefined = true; 70 sh.zEnable = true; 71 sh.zWriteEnable = false; 72 73 sh.zBias = mDepthBias; 74 sh.blendDefined = true; 75 sh.blendEnable = true; 76 sh.blendSrc = GFXBlendSrcAlpha; 77 sh.blendDest = GFXBlendInvSrcAlpha; 78 sh.alphaDefined = true; 79 sh.alphaTestEnable = true; 80 sh.alphaTestFunc = GFXCmpGreater; 81 sh.alphaTestRef = 0; 82 sh.samplersDefined = true; 83 sh.samplers[0] = GFXSamplerStateDesc::getClampLinear(); 84 mShadowSB = GFX->createStateBlock(sh); 85} 86 87BlobShadow::~BlobShadow() 88{ 89 mShadowBuffer = NULL; 90} 91 92bool BlobShadow::shouldRender(F32 camDist) 93{ 94 Point3F lightDir; 95 96 if (mShapeBase && mShapeBase->getFadeVal() < TSMesh::VISIBILITY_EPSILON) 97 return false; 98 99 F32 shadowLen = 10.0f * mShapeInstance->getShape()->mRadius; 100 Point3F pos = mShapeInstance->getShape()->center; 101 102 // this is a bit of a hack...move generic shadows towards feet/base of shape 103 pos *= 0.5f; 104 pos.convolve(mParentObject->getScale()); 105 mParentObject->getRenderTransform().mulP(pos); 106 if(mParentLight->getType() == LightInfo::Vector) 107 { 108 lightDir = mParentLight->getDirection(); 109 } 110 else 111 { 112 lightDir = pos - mParentLight->getPosition(); 113 lightDir.normalize(); 114 } 115 116 // pos is where shadow will be centered (in world space) 117 setRadius(mShapeInstance, mParentObject->getScale()); 118 bool render = prepare(pos, lightDir, shadowLen); 119 return render; 120} 121 122void BlobShadow::generateGenericShadowBitmap(S32 dim) 123{ 124 if(smGenericShadowTexture) 125 return; 126 GBitmap * bitmap = new GBitmap(dim,dim,false,GFXFormatR8G8B8A8); 127 U8 * bits = bitmap->getWritableBits(); 128 dMemset(bits, 0, dim*dim*4); 129 S32 center = dim >> 1; 130 F32 invRadiusSq = 1.0f / (F32)(center*center); 131 F32 tmpF; 132 for (S32 i=0; i<dim; i++) 133 { 134 for (S32 j=0; j<dim; j++) 135 { 136 tmpF = (F32)((i-center)*(i-center)+(j-center)*(j-center)) * invRadiusSq; 137 U8 val = tmpF>0.99f ? 0 : (U8)(180.0f*(1.0f-tmpF)); // 180 out of 255 max 138 bits[(i*dim*4)+(j*4)+3] = val; 139 } 140 } 141 142 smGenericShadowTexture.set( bitmap, &GFXStaticTextureSRGBProfile, true, "BlobShadow" ); 143} 144 145//-------------------------------------------------------------- 146 147void BlobShadow::setLightMatrices(const Point3F & lightDir, const Point3F & pos) 148{ 149 AssertFatal(mDot(lightDir,lightDir)>0.0001f,"BlobShadow::setLightDir: light direction must be a non-zero vector."); 150 151 // construct light matrix 152 Point3F x,z; 153 if (mFabs(lightDir.z)>0.001f) 154 { 155 // mCross(Point3F(1,0,0),lightDir,&z); 156 z.x = 0.0f; 157 z.y = lightDir.z; 158 z.z = -lightDir.y; 159 z.normalize(); 160 mCross(lightDir,z,&x); 161 } 162 else 163 { 164 mCross(lightDir,Point3F(0,0,1),&x); 165 x.normalize(); 166 mCross(x,lightDir,&z); 167 } 168 169 mLightToWorld.identity(); 170 mLightToWorld.setColumn(0,x); 171 mLightToWorld.setColumn(1,lightDir); 172 mLightToWorld.setColumn(2,z); 173 mLightToWorld.setColumn(3,pos); 174 175 mWorldToLight = mLightToWorld; 176 mWorldToLight.inverse(); 177} 178 179void BlobShadow::setRadius(F32 radius) 180{ 181 mRadius = radius; 182} 183 184void BlobShadow::setRadius(TSShapeInstance * shapeInstance, const Point3F & scale) 185{ 186 const Box3F & bounds = shapeInstance->getShape()->mBounds; 187 F32 dx = 0.5f * (bounds.maxExtents.x-bounds.minExtents.x) * scale.x; 188 F32 dy = 0.5f * (bounds.maxExtents.y-bounds.minExtents.y) * scale.y; 189 F32 dz = 0.5f * (bounds.maxExtents.z-bounds.minExtents.z) * scale.z; 190 mRadius = mSqrt(dx*dx+dy*dy+dz*dz); 191} 192 193 194//-------------------------------------------------------------- 195 196bool BlobShadow::prepare(const Point3F & pos, Point3F lightDir, F32 shadowLen) 197{ 198 if (mPartition.empty()) 199 { 200 // -------------------------------------- 201 // 1. 202 F32 dirMult = (1.0f) * (1.0f); 203 if (dirMult < 0.99f) 204 { 205 lightDir.z *= dirMult; 206 lightDir.z -= 1.0f - dirMult; 207 } 208 lightDir.normalize(); 209 shadowLen *= (1.0f) * (1.0f); 210 211 // -------------------------------------- 212 // 2. get polys 213 F32 radius = mRadius; 214 radius *= smGenericRadiusSkew; 215 buildPartition(pos,lightDir,radius,shadowLen); 216 } 217 if (mPartition.empty()) 218 // no need to draw shadow if nothing to cast it onto 219 return false; 220 221 return true; 222} 223 224//-------------------------------------------------------------- 225 226void BlobShadow::buildPartition(const Point3F & p, const Point3F & lightDir, F32 radius, F32 shadowLen) 227{ 228 setLightMatrices(lightDir,p); 229 230 Point3F extent(2.0f*radius,shadowLen,2.0f*radius); 231 smDepthSortList.clear(); 232 smDepthSortList.set(mWorldToLight,extent); 233 smDepthSortList.setInterestNormal(lightDir); 234 235 if (shadowLen<1.0f) 236 // no point in even this short of a shadow... 237 shadowLen = 1.0f; 238 mInvShadowDistance = 1.0f / shadowLen; 239 240 // build world space box and sphere around shadow 241 242 Point3F x,y,z; 243 mLightToWorld.getColumn(0,&x); 244 mLightToWorld.getColumn(1,&y); 245 mLightToWorld.getColumn(2,&z); 246 x *= radius; 247 y *= shadowLen; 248 z *= radius; 249 gBlobShadowBox.maxExtents.set(mFabs(x.x)+mFabs(y.x)+mFabs(z.x), 250 mFabs(x.y)+mFabs(y.y)+mFabs(z.y), 251 mFabs(x.z)+mFabs(y.z)+mFabs(z.z)); 252 y *= 0.5f; 253 gBlobShadowSphere.radius = gBlobShadowBox.maxExtents.len(); 254 gBlobShadowSphere.center = p + y; 255 gBlobShadowBox.minExtents = y + p - gBlobShadowBox.maxExtents; 256 gBlobShadowBox.maxExtents += y + p; 257 258 // get polys 259 260 gClientContainer.findObjects(STATIC_COLLISION_TYPEMASK, BlobShadow::collisionCallback, this); 261 262 // setup partition list 263 gBlobShadowPoly[0].set(-radius,0,-radius); 264 gBlobShadowPoly[1].set(-radius,0, radius); 265 gBlobShadowPoly[2].set( radius,0, radius); 266 gBlobShadowPoly[3].set( radius,0,-radius); 267 268 mPartition.clear(); 269 mPartitionVerts.clear(); 270 smDepthSortList.depthPartition(gBlobShadowPoly,4,mPartition,mPartitionVerts); 271 272 if(mPartitionVerts.empty()) 273 return; 274 275 // Find the rough distance of the shadow verts 276 // from the object position and use that to scale 277 // the visibleAlpha so that the shadow fades out 278 // the further away from you it gets 279 F32 dist = 0.0f; 280 281 // Calculate the center of the partition verts 282 Point3F shadowCenter(0.0f, 0.0f, 0.0f); 283 for (U32 i = 0; i < mPartitionVerts.size(); i++) 284 shadowCenter += mPartitionVerts[i]; 285 286 shadowCenter /= mPartitionVerts.size(); 287 288 mLightToWorld.mulP(shadowCenter); 289 290 dist = (p - shadowCenter).len(); 291 292 // now set up tverts & colors 293 mShadowBuffer.set(GFX, mPartitionVerts.size(), GFXBufferTypeVolatile); 294 mShadowBuffer.lock(); 295 296 F32 visibleAlpha = 255.0f; 297 if (mShapeBase && mShapeBase->getFadeVal()) 298 visibleAlpha = mClampF(255.0f * mShapeBase->getFadeVal(), 0, 255); 299 visibleAlpha *= 1.0f - (dist / gBlobShadowSphere.radius); 300 F32 invRadius = 1.0f / radius; 301 for (S32 i=0; i<mPartitionVerts.size(); i++) 302 { 303 Point3F vert = mPartitionVerts[i]; 304 mShadowBuffer[i].point.set(vert); 305 mShadowBuffer[i].color.set(255, 255, 255, visibleAlpha); 306 mShadowBuffer[i].texCoord.set(0.5f + 0.5f * mPartitionVerts[i].x * invRadius, 0.5f + 0.5f * mPartitionVerts[i].z * invRadius); 307 }; 308 309 mShadowBuffer.unlock(); 310} 311 312//-------------------------------------------------------------- 313 314void BlobShadow::collisionCallback(SceneObject * obj, void* thisPtr) 315{ 316 if (obj->getWorldBox().isOverlapped(gBlobShadowBox)) 317 { 318 // only interiors clip... 319 ClippedPolyList::allowClipping = (obj->getTypeMask() & LIGHTMGR->getSceneLightingInterface()->mClippingMask) != 0; 320 obj->buildPolyList(PLC_Collision,&smDepthSortList,gBlobShadowBox,gBlobShadowSphere); 321 ClippedPolyList::allowClipping = true; 322 } 323} 324 325//-------------------------------------------------------------- 326 327void BlobShadow::render( F32 camDist, const TSRenderState &rdata ) 328{ 329 mLastRenderTime = Platform::getRealMilliseconds(); 330 GFX->pushWorldMatrix(); 331 MatrixF world = GFX->getWorldMatrix(); 332 world.mul(mLightToWorld); 333 GFX->setWorldMatrix(world); 334 335 GFX->setupGenericShaders(GFXDevice::GSModColorTexture); 336 337 GFX->setStateBlock(mShadowSB); 338 GFX->setTexture(0, smGenericShadowTexture); 339 GFX->setVertexBuffer(mShadowBuffer); 340 341 for(U32 p=0; p<mPartition.size(); p++) 342 GFX->drawPrimitive(GFXTriangleStrip, mPartition[p].vertexStart, (mPartition[p].vertexCount - 2)); 343 344 // This is a bad nasty hack which forces the shadow to reconstruct itself every frame. 345 mPartition.clear(); 346 347 GFX->popWorldMatrix(); 348} 349 350void BlobShadow::deleteGenericShadowBitmap() 351{ 352 smGenericShadowTexture = NULL; 353} 354