dynamicshadowmanager.cpp 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306
  1. /*
  2. ** Command & Conquer Renegade(tm)
  3. ** Copyright 2025 Electronic Arts Inc.
  4. **
  5. ** This program is free software: you can redistribute it and/or modify
  6. ** it under the terms of the GNU General Public License as published by
  7. ** the Free Software Foundation, either version 3 of the License, or
  8. ** (at your option) any later version.
  9. **
  10. ** This program is distributed in the hope that it will be useful,
  11. ** but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. ** GNU General Public License for more details.
  14. **
  15. ** You should have received a copy of the GNU General Public License
  16. ** along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. */
  18. /***********************************************************************************************
  19. *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
  20. ***********************************************************************************************
  21. * *
  22. * Project Name : WWPhys *
  23. * *
  24. * $Archive:: /Commando/Code/wwphys/dynamicshadowmanager.cpp $*
  25. * *
  26. * Original Author:: Greg Hjelstrom *
  27. * *
  28. * $Author:: Greg_h $*
  29. * *
  30. * $Modtime:: 12/07/01 4:39p $*
  31. * *
  32. * $Revision:: 14 $*
  33. * *
  34. *---------------------------------------------------------------------------------------------*
  35. * Functions: *
  36. * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  37. #include "dynamicshadowmanager.h"
  38. #include "chunkio.h"
  39. #include "phys.h"
  40. #include "dyntexproject.h"
  41. #include "pscene.h"
  42. #include "physcoltest.h"
  43. #include "light.h"
  44. #include "texture.h"
  45. #include "physresourcemgr.h"
  46. #define SINGLE_SHADOW_CODE 1
  47. DynamicShadowManagerClass::DynamicShadowManagerClass(PhysClass & parent) :
  48. Parent(parent),
  49. Shadow(NULL),
  50. ShadowNearZ(-1.0f),
  51. ShadowFarZ(-1.0f),
  52. ForceUseBlobBox(false),
  53. BlobBoxProjectionScale(1,1,1)
  54. {
  55. }
  56. DynamicShadowManagerClass::~DynamicShadowManagerClass(void)
  57. {
  58. Release_Shadow();
  59. }
  60. void DynamicShadowManagerClass::Update_Shadow(void)
  61. {
  62. #if SINGLE_SHADOW_CODE
  63. /*
  64. ** Shadow Update
  65. ** - if shadows are off, release projector and RETURN
  66. ** - find dominant light source (or multiple sources?)
  67. ** - if no light sources, set projector intensity to 0.0 and RETURN
  68. ** - for each light source
  69. ** - re-use projector from previous frame or allocate a new projector
  70. ** - initialize the projection parameters, depending on: blob/real,point/directional
  71. ** - set the shadow's intensity by attenuating it with distance from lightsource
  72. */
  73. PhysicsSceneClass * scene = PhysicsSceneClass::Get_Instance();
  74. PhysicsSceneClass::ShadowEnum shadow_mode = scene->Get_Shadow_Mode();
  75. float near_atten;
  76. float far_atten;
  77. scene->Get_Shadow_Attenuation(&near_atten,&far_atten);
  78. RenderObjClass * model = Parent.Peek_Model();
  79. /*
  80. ** Grab the bounding box info based on whether we are using blobs or real projections
  81. ** use blobs IF(the mode is BLOBS or (the mode is BLOBS_PLUS and we're not the 'star'))
  82. */
  83. AABoxClass objbox;
  84. bool use_blob = (shadow_mode == PhysicsSceneClass::SHADOW_MODE_BLOBS) ||
  85. ((shadow_mode == PhysicsSceneClass::SHADOW_MODE_BLOBS_PLUS) && (!Parent.Is_Force_Projection_Shadow_Enabled()));
  86. if (use_blob) {
  87. Parent.Get_Shadow_Blob_Box(&objbox);
  88. } else {
  89. if (ForceUseBlobBox) {
  90. Parent.Get_Shadow_Blob_Box(&objbox);
  91. objbox.Extent.Scale(BlobBoxProjectionScale);
  92. } else {
  93. model->Get_Obj_Space_Bounding_Box(objbox);
  94. }
  95. }
  96. /*
  97. ** Check if the center of the bounding box is well beyond the shadow fading
  98. ** distance. If so, we will exit early
  99. */
  100. Vector3 position;
  101. Matrix3D::Transform_Vector(Parent.Get_Transform(),objbox.Center,&position);
  102. float shadow_shutoff2 = far_atten * 1.3f * far_atten * 1.3f;
  103. float shadow_dist2 = (position - scene->Get_Last_Camera_Position()).Length2();
  104. /*
  105. ** If shadow generation is disabled or we are farther than 1.2x the shadow attenuation distance,
  106. ** release any projector that we may have and return
  107. */
  108. if ( (Parent.Do_Any_Effects_Suppress_Shadows()) ||
  109. (Parent.Is_Shadow_Generation_Enabled() == false) ||
  110. (shadow_mode == PhysicsSceneClass::SHADOW_MODE_NONE) ||
  111. (shadow_dist2 > shadow_shutoff2) ||
  112. (model == NULL) ||
  113. (model->Is_Hidden()) ||
  114. (objbox.Extent.Length2() < 0.1f) )
  115. {
  116. if (Shadow != NULL) {
  117. scene->Remove_Dynamic_Texture_Projector(Shadow);
  118. Shadow->Release_Ref();
  119. Shadow = NULL;
  120. }
  121. return;
  122. }
  123. /*
  124. ** Find dominant light source. First check if sun is available, then find closest
  125. ** static light.
  126. */
  127. bool found_light = false;
  128. Vector3 sunlight;
  129. scene->Get_Sun_Light_Vector(&sunlight);
  130. if (Parent.Is_In_The_Sun()) {
  131. /*
  132. ** We can see the sunlight so set our projector up for it.
  133. ** Our shadow is attenuated only by distance from the camera so
  134. ** set the intensity to 'normal' and set the flag which causes
  135. ** the physics scene to attenuate it with distance.
  136. */
  137. Allocate_Shadow();
  138. /*
  139. ** Save parameters in the projector so it can continue to
  140. ** update later if it needs to fade out
  141. */
  142. LightClass * sun = scene->Get_Sun_Light();
  143. Shadow->Enable_Perspective(false);
  144. Shadow->Set_Light_Source_ID((uint32)sun);
  145. Shadow->Set_Light_Vector(sunlight);
  146. sun->Release_Ref();
  147. found_light = true;
  148. } else {
  149. #pragma message ("(gth) Disabling local shadows")
  150. #if 0
  151. /*
  152. ** We couldn't use the sunlight so now we look for the nearest
  153. ** local light source which casts shadows. If we find one, initialize
  154. ** our shadow projector with it.
  155. */
  156. NonRefPhysListClass lightlist;
  157. scene->Collect_Lights(position,true,false,&lightlist);
  158. if (!lightlist.Is_Empty()) {
  159. /*
  160. ** Ensure that a shadow is allocated!
  161. */
  162. Allocate_Shadow();
  163. LightClass * best_light = NULL;
  164. NonRefPhysListIterator it(&lightlist);
  165. for (it.First(); !it.Is_Done(); it.Next()) {
  166. best_light = (LightClass *)(it.Peek_Obj()->Peek_Model());
  167. break;
  168. }
  169. if (best_light) {
  170. #if TRUE_PERSPECTIVE_SHADOWS // This code uses true perspective projection for local light sources
  171. Shadow->Enable_Perspective(true);
  172. Shadow->Set_Light_Source_ID((uint32)best_light);
  173. Shadow->Set_Light_Vector(best_light->Get_Position());
  174. #else // This code uses an orthographic approximation
  175. Shadow->Enable_Perspective(false);
  176. Shadow->Set_Light_Source_ID((uint32)best_light);
  177. Vector3 direction;
  178. Get_Position(&direction);
  179. direction -= best_light->Get_Position();
  180. direction.Normalize();
  181. Shadow->Set_Light_Vector(direction);
  182. #endif
  183. found_light = true;
  184. DEBUG_RENDER_VECTOR(position,best_light->Get_Position()-position,Vector3(1,1,1));
  185. }
  186. }
  187. #endif //0
  188. }
  189. if (found_light) {
  190. /*
  191. ** We have a shadow and a light so update the projection parameters.
  192. ** If we are using blob shadows, just plug in the blob texture and
  193. ** clear the dirty flag. Otherwise, mark the texture dirty so it will
  194. ** be re-generated if the shadow actually gets projected onto something
  195. */
  196. Shadow->Update_Projection(objbox,Parent.Get_Transform(),ShadowNearZ,ShadowFarZ);
  197. Shadow->Set_Intensity(scene->Get_Shadow_Normal_Intensity());
  198. if (use_blob) {
  199. TextureClass * shadow_texture = PhysResourceMgrClass::Get_Shadow_Blob_Texture();
  200. WWASSERT(shadow_texture != NULL);
  201. Shadow->Set_Texture(shadow_texture);
  202. shadow_texture->Release_Ref();
  203. Shadow->Set_Texture_Dirty(false);
  204. } else {
  205. Shadow->Set_Texture_Dirty(true);
  206. }
  207. } else {
  208. /*
  209. ** If we have a shadow but we don't want it any more, wait until
  210. ** it fades out before we release it
  211. */
  212. if (Shadow) {
  213. Shadow->Set_Intensity(0.0f);
  214. if (!Shadow->Is_Intensity_Zero()) {
  215. Shadow->Update_Projection(objbox,Parent.Get_Transform(),ShadowNearZ,ShadowFarZ);
  216. }
  217. }
  218. }
  219. #else
  220. /*
  221. ** - Collect all lights that want to and can cast a shadow with this object (raytest, etc)
  222. ** - Reduce number of lights in list to MaxShadowsPerObject
  223. ** - Create new empty shadow object list
  224. ** - For each light
  225. ** - Try to find shadow which used this light from prev-frame's shadow list, if found
  226. ** remove it from prev-frame's list and put in current frame's shadow list
  227. ** Else create a new shadow object, immediately set its intensity to zero (since it is turning on)
  228. ** - Initialize projector with light parameters
  229. ** - For each shadow still in prev-frame's list
  230. ** - If intensity is zero, destroy it.
  231. ** Else
  232. ** - Set target intensity to zero
  233. ** - Update projection parameters, move into this frame's shadow list
  234. */
  235. #endif
  236. }
  237. void DynamicShadowManagerClass::Allocate_Shadow(void)
  238. {
  239. if (Shadow == NULL) {
  240. PhysicsSceneClass * scene = PhysicsSceneClass::Get_Instance();
  241. Shadow = NEW_REF(DynTexProjectClass,(&Parent));
  242. Shadow->Enable_Attenuation(true);
  243. Shadow->Enable_Depth_Gradient(true);
  244. Shadow->Enable_Affect_Dynamic_Objects(false);
  245. Shadow->Set_Intensity(scene->Get_Shadow_Normal_Intensity(),true);
  246. Shadow->Peek_Material_Pass()->Enable_On_Translucent_Meshes(false);
  247. scene->Add_Dynamic_Texture_Projector(Shadow);
  248. }
  249. }
  250. void DynamicShadowManagerClass::Release_Shadow(void)
  251. {
  252. if (Shadow) {
  253. if (PhysicsSceneClass::Get_Instance()->Contains(Shadow)) {
  254. PhysicsSceneClass::Get_Instance()->Remove_Dynamic_Texture_Projector(Shadow);
  255. }
  256. Shadow->Release_Ref();
  257. Shadow = NULL;
  258. }
  259. }