blobShadow.cpp 11 KB

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