Light.cpp 81 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616
  1. /******************************************************************************/
  2. #include "stdafx.h"
  3. #define TEST_LIGHT_RECT (DEBUG && 0)
  4. #define FLAT_SHADOW_MAP 1 // TODO try implementing DX11 Cube Shadow Maps?
  5. #define LOCAL_SHADOW_MAP_FROM (1.0f/64) // 0.015625m, 1.5 cm
  6. namespace EE{
  7. /******************************************************************************
  8. Hardware Shadow Map RT is created with resolution:
  9. x=D.shadowMapSizeActual()*2
  10. y=D.shadowMapSizeActual()*3
  11. Like this (2x3):
  12. SS
  13. SS
  14. SS
  15. /******************************************************************************/
  16. static Matrix ShdMatrix [2]; // [Eye]
  17. static Matrix4 ShdMatrix4[6][2]; // [MapIndex][Eye]
  18. static Matrix4 HsmMatrix, HsmMatrixCone;
  19. static Memc<FloatIndex> LightImportance;
  20. Memc<Light> Lights;
  21. Light CurrentLight;
  22. static Bool CurrentLightOn [2];
  23. static Rect CurrentLightRect[2];
  24. /******************************************************************************/
  25. static inline Int HsmX (DIR_ENUM dir) {return dir& 1;}
  26. static inline Int HsmY (DIR_ENUM dir) {return dir>>1;}
  27. static inline Matrix4& HsmMatrixSet(DIR_ENUM dir)
  28. {
  29. Int x=HsmX(dir),
  30. y=HsmY(dir);
  31. HsmMatrix.pos.x=(x ? 0.75f : 0.25f) ;
  32. HsmMatrix.pos.y=Avg(0, 1.0f/3)+(1.0f/3)*y;
  33. return HsmMatrix;
  34. }
  35. static inline void MatrixFovScaleX(Matrix &matrix, Flt scale) {matrix.x.x/=scale; matrix.y.x/=scale; matrix.z.x/=scale; matrix.pos.x/=scale;}
  36. static inline void MatrixFovScaleY(Matrix &matrix, Flt scale) {matrix.x.y/=scale; matrix.y.y/=scale; matrix.z.y/=scale; matrix.pos.y/=scale;}
  37. static inline void MatrixFovScale (Matrix &matrix, Flt scale) {MatrixFovScaleX(matrix, scale); MatrixFovScaleY(matrix, scale);}
  38. /******************************************************************************/
  39. static inline void SetShadowOpacity(Flt opacity) {Sh.h_ShdOpacity->set(Vec2(opacity, 1-opacity));} // shd=Lerp(1, shd, shadow_opacity) -> shd=1*(1-shadow_opacity) + shd*(shadow_opacity) -> shd=shd*shadow_opacity + 1-shadow_opacity
  40. static inline void SetShdMatrix()
  41. {
  42. Sh.h_ShdMatrix ->set(ShdMatrix [Renderer._eye]);
  43. REP(6)Sh.h_ShdMatrix4[i]->set(ShdMatrix4[i][Renderer._eye]);
  44. }
  45. /******************************************************************************/
  46. INLINE Shader* GetShdDir (Int map_num, Bool clouds, Bool multi_sample) {Shader* &s=Sh.h_ShdDir[map_num-1][clouds][multi_sample]; if(SLOW_SHADER_LOAD && !s)s=Sh.getShdDir (map_num, clouds, multi_sample); return s;}
  47. INLINE Shader* GetShdPnt ( Bool multi_sample) {Shader* &s=Sh.h_ShdPnt [multi_sample]; if(SLOW_SHADER_LOAD && !s)s=Sh.getShdPnt ( multi_sample); return s;}
  48. INLINE Shader* GetShdCone( Bool multi_sample) {Shader* &s=Sh.h_ShdCone [multi_sample]; if(SLOW_SHADER_LOAD && !s)s=Sh.getShdCone( multi_sample); return s;}
  49. INLINE Shader* GetLightDir (Bool shadow, Bool multi_sample, Bool quality) {Shader* &s=Sh.h_LightDir [shadow] [multi_sample][quality]; if(SLOW_SHADER_LOAD && !s)s=Sh.getLightDir (shadow, multi_sample, quality); return s;}
  50. INLINE Shader* GetLightPnt (Bool shadow, Bool multi_sample, Bool quality) {Shader* &s=Sh.h_LightPnt [shadow] [multi_sample][quality]; if(SLOW_SHADER_LOAD && !s)s=Sh.getLightPnt (shadow, multi_sample, quality); return s;}
  51. INLINE Shader* GetLightSqr (Bool shadow, Bool multi_sample, Bool quality) {Shader* &s=Sh.h_LightSqr [shadow] [multi_sample][quality]; if(SLOW_SHADER_LOAD && !s)s=Sh.getLightSqr (shadow, multi_sample, quality); return s;}
  52. INLINE Shader* GetLightCone(Bool shadow, Bool image, Bool multi_sample, Bool quality) {Shader* &s=Sh.h_LightCone[shadow][image][multi_sample][quality]; if(SLOW_SHADER_LOAD && !s)s=Sh.getLightCone(shadow, image, multi_sample, quality); return s;}
  53. /******************************************************************************/
  54. static Flt ShadowStep(Int i, Int num) // 0..1
  55. {
  56. if(i==0 )return 0;
  57. if(i==num)return 1;
  58. if(D.shadow_step)return D.shadow_step(i, num);
  59. Flt s=Flt(i)/num;
  60. return Pow(s, D.shadowMapSplit().x + D.shadowMapSplit().y*s); // Pow(s, 2) == s*s produces results nearly identical to "Lerp(1/((1-s)*D.viewRange()), s, s);"
  61. }
  62. /******************************************************************************/
  63. static inline Flt GetBias() {return (D.shadowJitter() ? SQRT2 : SQRT2_2)/D.shadowMapSizeActual();}
  64. /******************************************************************************/
  65. static void ApplyViewSpaceBias(Flt &mp_z_z)
  66. {
  67. if(FovPerspective(D.viewFovMode())) // needed only for perspective because it can produce big errors
  68. {
  69. mp_z_z=ProjMatrix.z.z;
  70. ProjMatrix.z.z+=(REVERSE_DEPTH ? -1.0 : 1.0)/(1ull<<ImageTI[Renderer._ds->hwType()].d); // this adjusts the value that is responsible for 'LinearizeDepth' by moving everything back by 1 value (in depth buffer bit precision)
  71. if(ProjMatrix.z.z==mp_z_z) // if adding hasn't changed anything, then change by 1 bit (this can happen for small values)
  72. if(REVERSE_DEPTH)DecRealByBit(ProjMatrix.z.z);
  73. else IncRealByBit(ProjMatrix.z.z);
  74. SetProjMatrix();
  75. }
  76. }
  77. static void RestoreViewSpaceBias(Flt mp_z_z)
  78. {
  79. if(FovPerspective(D.viewFovMode())){ProjMatrix.z.z=mp_z_z; SetProjMatrix();}
  80. }
  81. void RendererClass::getShdRT()
  82. { // always do 'get' to call 'discard'
  83. Renderer._shd_1s.get(ImageRTDesc(Renderer._ds_1s->w(), Renderer._ds_1s->h(), IMAGERT_ONE));
  84. if(Renderer._ds->multiSample())Renderer._shd_ms.get(ImageRTDesc(Renderer._ds ->w(), Renderer._ds ->h(), IMAGERT_ONE, Renderer._ds->samples()));
  85. D.alpha(ALPHA_NONE);
  86. }
  87. static void GetLum()
  88. {
  89. Renderer._lum .get(ImageRTDesc(Renderer._col->w(), Renderer._col->h(), D.highPrecLumRT() ? IMAGERT_RGBA_H : IMAGERT_RGBA, Renderer._col->samples())); // here Alpha is used for specular
  90. if(Renderer._lum->multiSample())Renderer._lum_1s.get(ImageRTDesc(Renderer._lum->w(), Renderer._lum->h(), D.highPrecLumRT() ? IMAGERT_RGBA_H : IMAGERT_RGBA));else Renderer._lum_1s=Renderer._lum;
  91. }
  92. void RendererClass::getLumRT()
  93. {
  94. if(!_lum)
  95. {
  96. GetLum();
  97. if(_lum_1s!=_lum)_lum_1s->clearViewport();
  98. _lum ->clearViewport(_ao ? Vec4Zero : Vec4(D.ambientColor(), 0)); // if '_ao' is not available then set '_lum' to 'ambientColor' (set '_lum' instead of '_lum_1s' because it is the one that is read in both 1-sample and multi-sample ColLight shaders, if this is changed then adjust all clears to '_lum_1s' and '_lum' in this file)
  99. }
  100. }
  101. static Bool SetLum()
  102. {
  103. Bool set=!Renderer._lum; if(set)GetLum();
  104. Renderer.set(Renderer._lum_1s(), Renderer._ds_1s(), true, NEED_DEPTH_READ); // use DS because it may be used for 'D.depth2D' optimization and stencil tests, start with '_lum_1s' so '_lum' will be processed later, because at the end we still have to render ambient from 3d meshes to '_lum' this way we avoid changing RT's
  105. if(set)
  106. {
  107. D.depth2DOff(); D.clearCol((Renderer._lum_1s!=Renderer._lum || Renderer._ao) ? Vec4Zero : Vec4(D.ambientColor(), 0));
  108. D.depth2DOn ();
  109. }
  110. D.alpha(ALPHA_ADD); Sh.h_ImageNrm[0]->set(Renderer._nrm);
  111. return set;
  112. }
  113. static void GetWaterLum () {Renderer._water_lum.get(ImageRTDesc(Renderer._water_ds->w(), Renderer._water_ds->h(), IMAGERT_RGBA));} // here Alpha is used for specular
  114. void RendererClass::getWaterLumRT() {if(!_water_lum){GetWaterLum(); _water_lum->clearViewport(Vec4(D.ambientColor(), 0));}}
  115. static void SetWaterLum ()
  116. {
  117. Bool set=!Renderer._water_lum; if(set)GetWaterLum();
  118. Renderer.set(Renderer._water_lum(), Renderer._water_ds(), true, NEED_DEPTH_READ); // use DS because it may be used for 'D.depth2D' optimization and stencil tests
  119. if(set)
  120. {
  121. D.depth2DOff(); D.clearCol(Vec4(D.ambientColor(), 0));
  122. D.depth2DOn ();
  123. }
  124. D.alpha(ALPHA_ADD); Sh.h_ImageNrm[0]->set(Renderer._water_nrm);
  125. }
  126. static void MapSoft()
  127. {
  128. if(D.shadowSoft()) // this needs to be in sync with 'D.ambientSoft'
  129. {
  130. ImageRTDesc rt_desc(Renderer._shd_1s->w(), Renderer._shd_1s->h(), IMAGERT_ONE);
  131. if(D.shadowSoft()>=5)
  132. {
  133. ImageRTPtr temp; temp.get(rt_desc);
  134. Renderer.set( temp(), Renderer._ds_1s(), true, NEED_DEPTH_READ); REPS(Renderer._eye, Renderer._eye_num)if(CurrentLightOn[Renderer._eye])Sh.h_ShdBlurX->draw(Renderer._shd_1s, &CurrentLightRect[Renderer._eye]); // use DS because it may be used for 'D.depth2D' optimization
  135. Renderer.set(Renderer._shd_1s(), Renderer._ds_1s(), true, NEED_DEPTH_READ); Renderer._shd_1s->discard(); REPS(Renderer._eye, Renderer._eye_num)if(CurrentLightOn[Renderer._eye])Sh.h_ShdBlurY->draw( temp, &CurrentLightRect[Renderer._eye]); // use DS because it may be used for 'D.depth2D' optimization
  136. }else
  137. {
  138. ImageRTPtr src=Renderer._shd_1s; Renderer._shd_1s.get(rt_desc);
  139. Renderer.set(Renderer._shd_1s(), Renderer._ds_1s(), true, NEED_DEPTH_READ); // use DS because it may be used for 'D.depth2D' optimization
  140. REPS(Renderer._eye, Renderer._eye_num)if(CurrentLightOn[Renderer._eye])Sh.h_ShdBlur[D.shadowSoft()-1]->draw(src, &CurrentLightRect[Renderer._eye]);
  141. }
  142. }
  143. }
  144. static void RestoreLightSettings()
  145. {
  146. SetCam(ActiveCam.matrix, false); Frustum=FrustumMain;
  147. }
  148. static void RestoreShadowMapSettings()
  149. {
  150. D._view_active.set3DFrom(D._view_main).setProjMatrix(false);
  151. RestoreLightSettings();
  152. }
  153. /******************************************************************************/
  154. LightCone::LightCone(Flt length, C VecD &pos, C Vec &dir, C Vec &color, Flt vol, Flt vol_max)
  155. {
  156. T.pyramid.scale=1;
  157. T.pyramid.h =length;
  158. T.pyramid.setPosDir(pos, dir);
  159. T.color =color;
  160. T.falloff =0.5f;
  161. T.vol =vol;
  162. T.vol_max =vol_max;
  163. }
  164. /******************************************************************************/
  165. Flt LightPoint::range()C
  166. {
  167. return Sqrt(power/(4.0f/255));
  168. }
  169. /******************************************************************************/
  170. void LightDir ::add(Bool shadow , CPtr light_src ) { if(color.max() >EPS_COL && Renderer.firstPass()){Lights.New().set(T, shadow , light_src);}}
  171. void LightPoint::add(Flt shadow_opacity, CPtr light_src ) {Rect rect; if(color.max()*power >EPS_COL && toScreenRect(rect) && Renderer.firstPass()){Lights.New().set(T, rect, shadow_opacity, light_src);}}
  172. void LightSqr ::add(Flt shadow_opacity, CPtr light_src ) {Rect rect; if(color.max()*range >EPS_COL && toScreenRect(rect) && Renderer.firstPass()){Lights.New().set(T, rect, shadow_opacity, light_src);}}
  173. void LightCone ::add(Flt shadow_opacity, CPtr light_src, Image *image, Flt image_scale, C Color &image_add, Flt image_specular) {Rect rect; if(color.max()*pyramid.h>EPS_COL && toScreenRect(rect) && Renderer.firstPass())
  174. {
  175. Light &l=Lights.New();
  176. l.set(T, rect, shadow_opacity, light_src);
  177. l.image =image;
  178. l.image_scale =image_scale;
  179. l.image_add =image_add;
  180. l.image_specular=image_specular;
  181. }
  182. }
  183. /******************************************************************************/
  184. #pragma pack(push, 4)
  185. struct GpuLightDir
  186. {
  187. Vec dir, color;
  188. Flt spec, vol, vol_exponent, vol_steam;
  189. };
  190. struct GpuLightPoint
  191. {
  192. Flt power, vol, vol_max;
  193. Vec pos, color;
  194. Flt spec;
  195. };
  196. struct GpuLightSqr
  197. {
  198. Flt range, vol, vol_max;
  199. Vec pos, color;
  200. Flt spec;
  201. };
  202. struct GpuLightCone
  203. {
  204. Matrix3 mtrx; // must be at start because of ATI OpenGL bug
  205. Flt length, scale, vol, vol_max;
  206. Vec2 falloff;
  207. Vec pos, color;
  208. Flt spec;
  209. };
  210. #pragma pack(pop)
  211. void LightDir::set()
  212. {
  213. GpuLightDir l;
  214. l.dir .fromDivNormalized(dir, CamMatrix.orn()).chs();
  215. l.color =color;
  216. l.spec =color.max();
  217. l.vol =vol;
  218. l.vol_exponent=vol_exponent;
  219. l.vol_steam =vol_steam;
  220. Sh.h_Light_dir->set(l);
  221. }
  222. void LightPoint::set(Flt shadow_opacity)
  223. {
  224. GpuLightPoint l;
  225. l.power =power;
  226. l.vol =vol*shadow_opacity;
  227. l.vol_max=vol_max;
  228. l.pos .fromDivNormalized(pos, CamMatrix);
  229. l.color =color;
  230. l.spec =color.max();
  231. Sh.h_Light_pnt->set(l);
  232. }
  233. void LightSqr::set(Flt shadow_opacity)
  234. {
  235. GpuLightSqr l;
  236. l.range =range;
  237. l.vol =vol*shadow_opacity;
  238. l.vol_max=vol_max;
  239. l.pos .fromDivNormalized(pos, CamMatrix);
  240. l.color =color;
  241. l.spec =color.max();
  242. Sh.h_Light_sqr->set(l);
  243. }
  244. void LightCone::set(Flt shadow_opacity)
  245. {
  246. GpuLightCone l;
  247. // angular intensity = Length(pos)*l.falloff.x+l.falloff.y falloff=0 Y|\ falloff=1 Y| |
  248. // falloff=0 -> l.falloff.x=-1 l.falloff.y=1 | \ | |
  249. // falloff=1 -> l.falloff.x=-Inf l.falloff.y=Inf +--\X +--|X
  250. l.falloff.x =-1.0f/Mid(falloff, EPS, 1.0f);
  251. l.falloff.y =-l.falloff.x;
  252. l.vol = vol*shadow_opacity;
  253. l.vol_max = vol_max;
  254. l.color = color;
  255. l.spec = color.max();
  256. l.length = pyramid.h;
  257. l.scale = pyramid.scale;
  258. l.mtrx.x =-pyramid.cross()/pyramid.scale;
  259. l.mtrx.y =-pyramid.perp /pyramid.scale;
  260. l.mtrx.z =-pyramid.dir;
  261. l.mtrx. divNormalized( CamMatrix.orn());
  262. l.pos .fromDivNormalized(pyramid.pos, CamMatrix);
  263. Sh.h_Light_cone->set(l);
  264. }
  265. /******************************************************************************/
  266. enum SHADOW_MAP_FLAG
  267. {
  268. SM_FRUSTUM=0x1, // perform frustum tests
  269. SM_CLOUDS =0x2,
  270. };
  271. struct MatrixFovFrac
  272. {
  273. Vec2 fov; // no need for 'VecD2'
  274. MatrixM matrix;
  275. Rect frac; // 0..1 (fraction of the viewport that we actually need)
  276. };
  277. static void DrawShadowMap(DIR_ENUM dir, C MatrixM &cam_matrix, UInt flag, Flt view_from, Flt view_range, C Vec2 &fov, FOV_MODE fov_mode, Int border, Flt bias, C MatrixFovFrac *frustum=null)
  278. {
  279. #if FLAT_SHADOW_MAP
  280. {
  281. // apply cam matrix bias
  282. MatrixM cam_matrix_biased=cam_matrix;
  283. if(CurrentLight.type==LIGHT_DIR)cam_matrix_biased-=cam_matrix.z*bias;
  284. // set viewport
  285. RectI viewport_rect;
  286. if(CurrentLight.type==LIGHT_CONE)
  287. {
  288. viewport_rect.set(0, 0, D.shadowMapSizeActual()*2, D.shadowMapSizeActual()*2); // cone lights use 2x shadow map size
  289. }else
  290. {
  291. Int x=HsmX(dir),
  292. y=HsmY(dir);
  293. viewport_rect.set(x*D.shadowMapSizeActual(), y*D.shadowMapSizeActual(), (x+1)*D.shadowMapSizeActual(), (y+1)*D.shadowMapSizeActual());
  294. }
  295. if(frustum) // in 'D.shadowReduceFlicker', the needed area may not be the entire viewport rect, so use scissor
  296. D.clipAllow(RectI(Floor(viewport_rect.lerpX(frustum->frac.min.x)),
  297. Floor(viewport_rect.lerpY(frustum->frac.min.y)),
  298. Ceil (viewport_rect.lerpX(frustum->frac.max.x)),
  299. Ceil (viewport_rect.lerpY(frustum->frac.max.y)))&viewport_rect); // AND with original viewport in case frac is slightly <0 || >1 or after floor/ceil we get +-1 values
  300. viewport_rect.extend(-border);
  301. D._view_active.set(viewport_rect, view_from, view_range, fov, fov_mode);
  302. SetCam(cam_matrix_biased, false); // camera without frustum
  303. if(frustum)Frustum.set(D._view_active.range, frustum->fov, frustum->matrix);
  304. else Frustum.set(D._view_active.range, D._view_active.fov, cam_matrix);
  305. if(flag&SM_FRUSTUM)if(!FrustumMain(Frustum))return; // check if shadow frustum is visible (lies in main camera view frustum)
  306. D._view_active.setViewport(false).setProjMatrix(false);
  307. #if DX9 // on DX10+ and OpenGL clearing depth buffer clears it fully, so it can't be divided into steps
  308. if(CurrentLight.type!=LIGHT_DIR)D.clearDepth(); // depth buffer is cleared separately for local lights, directional light always uses all parts so it can be cleared all at once
  309. #endif
  310. // set matrix converting from shadow map to main camera space (required for tesselation - adaptive tesselation factors)
  311. Matrix temp; cam_matrix.divNormalized(ActiveCam.matrix, temp); // temp = cam_matrix/ActiveCam.matrix
  312. Sh.h_ShdMatrix->set(temp);
  313. D.samplerShadow(); D.bias(BIAS_SHADOW); D.depth(true); PrepareShadowInstances(); Renderer._render(); DrawShadowInstances();
  314. D.sampler2D (); D.bias(BIAS_ZERO);
  315. if((flag&SM_CLOUDS) && Renderer._cld_map.is()) // clouds are only for directional lights
  316. {
  317. Int x =HsmX(dir),
  318. y =HsmY(dir);
  319. Image *rt=Renderer._cur[0], *rtz=Renderer._cur_ds;
  320. #if DX9
  321. Byte col_write=D._col_write[0]; D.colWrite(COL_WRITE_RGBA); // restore color writing
  322. #endif
  323. Renderer .set (&Renderer._cld_map, null, false);
  324. D._view_active.set (RectI(x*D.cloudsMapSize(), y*D.cloudsMapSize(), (x+1)*D.cloudsMapSize(), (y+1)*D.cloudsMapSize()), view_from, view_range, fov, fov_mode).setViewport(false).setProjMatrix(false); // viewport
  325. SetCam (cam_matrix, true); // camera and frustum
  326. Clouds .shadowMap();
  327. Renderer .set (rt, rtz, false);
  328. #if DX9
  329. D.colWrite(col_write);
  330. #endif
  331. }
  332. }
  333. #else
  334. {
  335. Renderer .setCube (Renderer._shd_map_null, &Renderer._shd_map, dir);
  336. D._view_active.set (RectI(0, 0, Renderer.resW(), Renderer.resH()), view_from, view_range, fov, fov_mode).setViewport(false).setProjMatrix(false); // viewport
  337. SetCam (cam_matrix, true); // camera and frustum
  338. if(flag&SM_FRUSTUM)if(!FrustumMain (Frustum))return;
  339. Sh .clear (Vec4(D._view_active.range*2));
  340. D .clearDepth();
  341. D.sampler3D(); D.bias(BIAS_SHADOW); D.depth(true); PrepareShadowInstances(); Renderer._render(); DrawShadowInstances();
  342. D.sampler2D(); D.bias(BIAS_ZERO);
  343. if((flag&SM_CLOUDS) && Renderer._cld_map.is())
  344. {
  345. Renderer .setCube (Renderer._cld_map, null, dir);
  346. D._view_active.set (RectI(0, 0, Renderer.resW(), Renderer.resH()), view_from, view_range, fov, fov_mode).setViewport(false).setProjMatrix(false); // viewport
  347. SetCam (cam_matrix, true); // camera and frustum
  348. Clouds .shadowMap();
  349. }
  350. }
  351. #endif
  352. }
  353. /******************************************************************************/
  354. static void StartVol()
  355. {
  356. Bool clear=false;
  357. if(!Renderer._vol)
  358. {
  359. Renderer._vol.get(ImageRTDesc(Renderer.fxW()>>2, Renderer.fxH()>>2, IMAGERT_RGB)); // doesn't use Alpha
  360. clear=true;
  361. VL.load();
  362. }
  363. D .alpha(ALPHA_ADD);
  364. Renderer.set (Renderer._vol(), null, false); if(clear)D.clearCol();
  365. if(Renderer._stereo) // for stereoscopic we need to set correct eye, otherwise it was already set
  366. {
  367. //SetCam(EyeMatrix[Renderer._eye], false); this is not needed because all positions in shaders are based on ShdMatrix
  368. SetShdMatrix();
  369. Renderer.setEyeParams();
  370. }
  371. CurrentLight.set(); // call after setting the camera
  372. }
  373. static void ApplyVolumetric(LightDir &light, Int shd_map_num, Bool cloud_vol)
  374. {
  375. if(Renderer.hasVolLight() && light.vol>EPS_COL && shd_map_num>0)REPS(Renderer._eye, Renderer._eye_num)if(CurrentLightOn[Renderer._eye])
  376. {
  377. StartVol();
  378. VL.h_VolDir[shd_map_num-1][cloud_vol]->draw(Renderer._vol(), &CurrentLightRect[Renderer._eye]);
  379. }
  380. }
  381. static void ApplyVolumetric(LightPoint &light)
  382. {
  383. if(Renderer.hasVolLight() && light.vol>EPS_COL)REPS(Renderer._eye, Renderer._eye_num)if(CurrentLightOn[Renderer._eye])
  384. {
  385. StartVol();
  386. VL.h_Light_point_range->set(light.range());
  387. VL.h_VolPnt->draw(Renderer._vol(), &CurrentLightRect[Renderer._eye]);
  388. }
  389. }
  390. static void ApplyVolumetric(LightSqr &light)
  391. {
  392. if(Renderer.hasVolLight() && light.vol>EPS_COL)REPS(Renderer._eye, Renderer._eye_num)if(CurrentLightOn[Renderer._eye])
  393. {
  394. StartVol();
  395. VL.h_VolSqr->draw(Renderer._vol(), &CurrentLightRect[Renderer._eye]);
  396. }
  397. }
  398. static void ApplyVolumetric(LightCone &light)
  399. {
  400. if(Renderer.hasVolLight() && light.vol>EPS_COL)REPS(Renderer._eye, Renderer._eye_num)if(CurrentLightOn[Renderer._eye])
  401. {
  402. StartVol();
  403. VL.h_VolCone->draw(Renderer._vol(), &CurrentLightRect[Renderer._eye]);
  404. }
  405. }
  406. /******************************************************************************/
  407. static Bool StereoCurrentLightRect() // this relies on current Camera Matrix
  408. {
  409. if(!CurrentLight.toScreenRect(CurrentLight.rect))return false;
  410. // apply projection offset
  411. Flt po=ProjMatrixEyeOffset[Renderer._eye]*D.w()*0.5f;
  412. if(CurrentLight.rect.min.x>-D.w()+EPS)CurrentLight.rect.min.x+=po;
  413. if(CurrentLight.rect.max.x< D.w()-EPS)CurrentLight.rect.max.x+=po;
  414. // apply viewport offset
  415. Flt vo=D.w()*0.5f*SignBool(Renderer._eye!=0);
  416. CurrentLight.rect.min.x+=vo;
  417. CurrentLight.rect.max.x+=vo;
  418. // clamp and test if valid
  419. if(Renderer._eye){if(CurrentLight.rect.min.x<0)if(CurrentLight.rect.max.x>0)CurrentLight.rect.min.x=0;else return false;}
  420. else {if(CurrentLight.rect.max.x>0)if(CurrentLight.rect.min.x<0)CurrentLight.rect.max.x=0;else return false;}
  421. return true;
  422. }
  423. static Bool GetCurrentLightRect()
  424. {
  425. return Renderer._stereo ? StereoCurrentLightRect() : true;
  426. }
  427. static Bool SetLightEye(Bool shadow=false)
  428. {
  429. CurrentLightOn[Renderer._eye]=false;
  430. if(Renderer._stereo)
  431. {
  432. SetCam(EyeMatrix[Renderer._eye], false);
  433. if(!StereoCurrentLightRect())return false;
  434. Renderer.setEyeParams();
  435. }
  436. if(shadow)
  437. {
  438. SetShdMatrix();
  439. CurrentLightOn [Renderer._eye]=true;
  440. CurrentLightRect[Renderer._eye]=CurrentLight.rect;
  441. }else
  442. {
  443. CurrentLight.set();
  444. }
  445. return true;
  446. }
  447. /******************************************************************************/
  448. static DIR_ENUM ShdDirStepDir[6]=
  449. {
  450. DIR_FORWARD,
  451. DIR_BACK ,
  452. DIR_RIGHT ,
  453. DIR_LEFT ,
  454. DIR_UP ,
  455. DIR_DOWN ,
  456. };
  457. static Byte ConnectedPoints[8][3]= // contains the indexes of 3 other points that this point is connected to
  458. { // points connected to point #: (the first value is from the other side, the 2 next values are +1 and -1 modulo)
  459. {4, 1, 3}, // 0, example: point 0 is connected to point 4 from the other side and mod(0+1)=1 and mod(0-1)=3
  460. {5, 2, 0}, // 1
  461. {6, 3, 1}, // 2
  462. {7, 0, 2}, // 3
  463. {0, 5, 7}, // 4
  464. {1, 6, 4}, // 5
  465. {2, 7, 5}, // 6
  466. {3, 4, 6}, // 7
  467. };
  468. static Flt ShadowFrac(C VecD &start, C VecD &end)
  469. {
  470. return (start.y<=Renderer.lowest_visible_point) ? 0 // if start is below lowest visible point, then we don't need any shadows
  471. : (start.y<=end.y ) ? 1 // if start is below end (we're looking up, and not down, then we need full shadows)
  472. : PointOnPlaneStep(start.y-Renderer.lowest_visible_point, end.y-Renderer.lowest_visible_point);
  473. }
  474. static Bool ShadowMap(LightDir &light)
  475. {
  476. // init
  477. RENDER_MODE mode=Renderer(); Renderer.mode(RM_SHADOW);
  478. D.alpha(ALPHA_NONE); // disable alpha
  479. #if DX9
  480. Renderer.set(&Renderer._shd_map_null, &Renderer._shd_map, false); // on DX9 there always needs to be a RT set
  481. D.clearDepth( ); // clear all shadow map parts at once, because for directional lights we'll use them all anyway
  482. D.colWrite (0); // disable color writes
  483. #else
  484. Renderer.set(null, &Renderer._shd_map, false);
  485. D.clearDepth(); // clear all shadow map parts at once, because for directional lights we'll use them all anyway
  486. #endif
  487. Bool cloud_shd=false,
  488. cloud_vol=false;
  489. if(Clouds.draw && Clouds.volumetric.drawable() && Renderer._cld_map.is())
  490. {
  491. if(Clouds.volumetric.shadow>EPS_COL)cloud_shd=true;
  492. if(Renderer.hasVolLight() && CurrentLight.vol()>EPS_COL)cloud_vol=true;
  493. }
  494. // set light direction orientation
  495. Matrix3 orn; orn.setDir(light.dir); // set any orientation initially
  496. // set steps
  497. Bool point_clip =(Renderer.lowest_visible_point>-DBL_MAX),
  498. clip =D._clip; Rect clip_rect=D._clip_rect;
  499. Int steps =D.shadowMapNumActual();
  500. Int points=8, points_1=points-1;
  501. VecD point[8], point_temp[8*3], *point_ptr=(point_clip ? point_temp : point);
  502. UInt flag =((cloud_shd || cloud_vol) ? SM_CLOUDS : 0);
  503. Flt bias =GetBias(),
  504. range =Renderer._shd_range*SHADOW_MAP_DIR_RANGE_MUL, // view range of the shadow map render target (this needs to be increased in case there are shadow occluders located distant from the camera), TODO: make SHADOW_MAP_DIR_RANGE_MUL configurable?
  505. shd_from =D.viewFromActual(), // where does the shadow start in the main render target
  506. shd_to =Max(Renderer._shd_range, shd_from+0.01f), // where does the shadow end in the main render target (must be slightly larger than starting point)
  507. shd_from_frac=shd_from/D.viewRange(),
  508. shd_to_frac =shd_to /D.viewRange();
  509. if(point_clip && !D.shadowReduceFlicker())
  510. {
  511. Flt frac=shd_from_frac+0.01f/D.viewRange(); // must be slightly larger than starting point
  512. if(FrustumMain.points==5) // perspective (pyramid frustum)
  513. {
  514. MAX(frac, ShadowFrac(FrustumMain.point[0], FrustumMain.point[1]));
  515. MAX(frac, ShadowFrac(FrustumMain.point[0], FrustumMain.point[2]));
  516. MAX(frac, ShadowFrac(FrustumMain.point[0], FrustumMain.point[3]));
  517. MAX(frac, ShadowFrac(FrustumMain.point[0], FrustumMain.point[4]));
  518. }else // orthogonal (box frustum)
  519. {
  520. MAX(frac, ShadowFrac(FrustumMain.point[0], FrustumMain.point[4]));
  521. MAX(frac, ShadowFrac(FrustumMain.point[1], FrustumMain.point[5]));
  522. MAX(frac, ShadowFrac(FrustumMain.point[2], FrustumMain.point[6]));
  523. MAX(frac, ShadowFrac(FrustumMain.point[3], FrustumMain.point[7]));
  524. }
  525. MIN(shd_to_frac, frac);
  526. }
  527. Flt s0=Lerp(shd_from_frac, shd_to_frac, ShadowStep(0, steps));
  528. if(FrustumMain.points==5) // perspective (pyramid frustum)
  529. {
  530. point[0]=Lerp(FrustumMain.point[0], FrustumMain.point[1], s0);
  531. point[1]=Lerp(FrustumMain.point[0], FrustumMain.point[2], s0);
  532. point[2]=Lerp(FrustumMain.point[0], FrustumMain.point[3], s0);
  533. point[3]=Lerp(FrustumMain.point[0], FrustumMain.point[4], s0);
  534. }else // orthogonal (box frustum)
  535. {
  536. point[0]=Lerp(FrustumMain.point[0], FrustumMain.point[4], s0);
  537. point[1]=Lerp(FrustumMain.point[1], FrustumMain.point[5], s0);
  538. point[2]=Lerp(FrustumMain.point[2], FrustumMain.point[6], s0);
  539. point[3]=Lerp(FrustumMain.point[3], FrustumMain.point[7], s0);
  540. }
  541. FREP(steps) // go from start because 's0' and 'point' are already set for first step, and we calculate 's1' and 'point' for next steps in the loop
  542. {
  543. Flt //s0=Lerp(shd_from_frac, shd_to_frac, ShadowStep(i , steps)); // convert to shd_from_frac .. shd_to_frac, already calculated
  544. s1=Lerp(shd_from_frac, shd_to_frac, ShadowStep(i+1, steps)), // convert to shd_from_frac .. shd_to_frac
  545. view_main_max=s1*D.viewRange();
  546. Sh.h_ShdStep[i]->set(view_main_max); // this value specifies at what depth what shadow map index should be used
  547. if(FrustumMain.points==5) // perspective (pyramid frustum)
  548. {
  549. point[4]=Lerp(FrustumMain.point[0], FrustumMain.point[1], s1);
  550. point[5]=Lerp(FrustumMain.point[0], FrustumMain.point[2], s1);
  551. point[6]=Lerp(FrustumMain.point[0], FrustumMain.point[3], s1);
  552. point[7]=Lerp(FrustumMain.point[0], FrustumMain.point[4], s1);
  553. }else // orthogonal (box frustum)
  554. {
  555. point[4]=Lerp(FrustumMain.point[0], FrustumMain.point[4], s1);
  556. point[5]=Lerp(FrustumMain.point[1], FrustumMain.point[5], s1);
  557. point[6]=Lerp(FrustumMain.point[2], FrustumMain.point[6], s1);
  558. point[7]=Lerp(FrustumMain.point[3], FrustumMain.point[7], s1);
  559. }
  560. MatrixM light_matrix; light_matrix.orn()=orn;
  561. if(point_clip) // if we want to clip the point edges
  562. {
  563. points=0;
  564. REPA(point) // iterate all original points
  565. {
  566. C VecD &p=point[i];
  567. if(p.y>=Renderer.lowest_visible_point)point_temp[points++]=p;else // if the point is above visible area, then include it as it is
  568. { // if the point is below
  569. Dbl d=p.y-Renderer.lowest_visible_point; // calculate Y distance to the plane
  570. REPD(j, 3) // iterate all 3 connected points to this one
  571. {
  572. C VecD &p1=point[ConnectedPoints[i][j]]; // get j-th connected point to point 'i'
  573. if(p1.y>Renderer.lowest_visible_point) // other point must be above visible area (if both are below then we can just ignore this edge), here use > instead of >=, because if 'p1' is == then it's going to be included anyway in the check above, and the calculated point on plane below would be same as 'p1', so we would have the same point listed twice
  574. {
  575. Dbl d1=p1.y-Renderer.lowest_visible_point; // calculate Y distance to the plane
  576. point_temp[points++]=PointOnPlane(p, p1, d, d1); // add the point that is intersection of the edge and the plane
  577. }
  578. }
  579. }
  580. }
  581. if(points<3) // min 3 points required to construct a volume, otherwise we would have zero dimension frustum or even "points_1==-1"
  582. {
  583. light_matrix.pos.zero(); REPA(point)light_matrix.pos+=point[i]; light_matrix.pos/=Elms(point); // set position in the center of all points
  584. D._view_active.setFrom(0).setRange(range).setFov(1, FOV_ORTHO).setProjMatrix(false); // set 'ProjMatrix' projection matrix needed below to calculate correct transformation matrix, needed in case there will be some pixels outside of the 'Renderer.lowest_visible_point'
  585. goto skip;
  586. }
  587. points_1=points-1;
  588. }
  589. {
  590. // convert Frustum points onto 2D light orientation space
  591. VecD2 frustum_points_2D[Elms(point_temp)];
  592. REP(points){C VecD &p=point_ptr[i]; frustum_points_2D[i].set(Dot(p, light_matrix.x), Dot(p, light_matrix.y));}
  593. MatrixFovFrac frustum;
  594. VecD2 axis; if(BestFit(frustum_points_2D, points, axis)) // find best orientation alignment
  595. {
  596. frustum.matrix.y=axis.x*light_matrix.x + axis.y*light_matrix.y;
  597. frustum.matrix.x=Cross(frustum.matrix.y, light_matrix.z);
  598. }else
  599. {
  600. frustum.matrix.x=light_matrix.x;
  601. frustum.matrix.y=light_matrix.y;
  602. }
  603. if(!D.shadowReduceFlicker()) // we can use best fit matrix for light matrix only if 'shadowReduceFlicker' is disabled, because 'shadowReduceFlicker' works in a way that it always uses biggest worst possible alignment, so doing this would actually work against 'shadowReduceFlicker'
  604. {
  605. light_matrix.x=frustum.matrix.x;
  606. light_matrix.y=frustum.matrix.y;
  607. }
  608. // set sizes
  609. Dbl x[2], y[2], z[2];
  610. {
  611. // setup values from last point
  612. x[0]=x[1]=DistPointPlane(point_ptr[points_1], light_matrix.x);
  613. y[0]=y[1]=DistPointPlane(point_ptr[points_1], light_matrix.y);
  614. z[0]=z[1]=DistPointPlane(point_ptr[points_1], light_matrix.z);
  615. REP(points_1) // now iterate all remaining points and adjust the values
  616. {
  617. Dbl d=DistPointPlane(point_ptr[i], light_matrix.x); if(d<x[0])x[0]=d;else if(d>x[1])x[1]=d;
  618. d=DistPointPlane(point_ptr[i], light_matrix.y); if(d<y[0])y[0]=d;else if(d>y[1])y[1]=d;
  619. d=DistPointPlane(point_ptr[i], light_matrix.z); if(d<z[0])z[0]=d;else if(d>z[1])z[1]=d;
  620. }
  621. }
  622. // align sizes
  623. if(D.shadowReduceFlicker())
  624. {
  625. Dbl max_dist=Sqrt(Max(Dist2(point[0], point[6]), Dist2(point[4], point[6]))); // Max(diagonal back<->front, diagonal front<->front), here use 'point' instead of 'point_ptr' (we need original points)
  626. VecD2 center(Avg(x[0], x[1]), Avg(y[0], y[1]));
  627. {
  628. Dbl a=max_dist/D.shadowMapSizeActual();
  629. Dbl o=AlignTrunc(center.x, 1024.0); center.x-=o; center.x=AlignFloor(center.x, a); center.x+=o; // offsetting by 1024 helps with precision issues at very big distances (512 km), we use Trunc in this case, because we want to move towards zero
  630. o=AlignTrunc(center.y, 1024.0); center.y-=o; center.y=AlignFloor(center.y, a); center.y+=o;
  631. }
  632. Dbl max_dist_2=max_dist/2;
  633. Dbl min=center.x-max_dist_2; frustum.frac.setX((x[0]-min)/max_dist, (x[1]-min)/max_dist); x[0]=min; x[1]=center.x+max_dist_2;
  634. min=center.y-max_dist_2; frustum.frac.setY((y[0]-min)/max_dist, (y[1]-min)/max_dist); y[0]=min; y[1]=center.y+max_dist_2;
  635. }else
  636. {
  637. Dbl a=(x[1]-x[0])/D.shadowMapSizeActual(); x[0]=AlignFloor(x[0], a); x[1]=AlignFloor(x[1], a);
  638. a=(y[1]-y[0])/D.shadowMapSizeActual(); y[0]=AlignFloor(y[0], a); y[1]=AlignFloor(y[1], a);
  639. }
  640. // calculate fov and position
  641. Vec2 fov((x[1]-x[0])*0.5f, (y[1]-y[0])*0.5f); // no need for 'VecD2'
  642. light_matrix.pos=Avg(x[0], x[1])*light_matrix.x
  643. +Avg(y[0], y[1])*light_matrix.y
  644. +(z[1]-range)*light_matrix.z;
  645. // !! Warning: this modifies 'x' and 'y' to frustum fov !!
  646. if(D.shadowReduceFlicker()) // we need to set it only if we're going to use it
  647. {
  648. x[0]=x[1]=DistPointPlane(point_ptr[points_1], frustum.matrix.x);
  649. y[0]=y[1]=DistPointPlane(point_ptr[points_1], frustum.matrix.y);
  650. REP(points_1) // now iterate all remaining points and adjust the values
  651. {
  652. Dbl d=DistPointPlane(point_ptr[i], frustum.matrix.x); if(d<x[0])x[0]=d;else if(d>x[1])x[1]=d;
  653. d=DistPointPlane(point_ptr[i], frustum.matrix.y); if(d<y[0])y[0]=d;else if(d>y[1])y[1]=d;
  654. }
  655. frustum.fov.set((x[1]-x[0])*0.5f, (y[1]-y[0])*0.5f);
  656. frustum.matrix.z =light_matrix.z;
  657. frustum.matrix.pos=Avg(x[0], x[1])*frustum.matrix.x
  658. +Avg(y[0], y[1])*frustum.matrix.y
  659. +(z[1]-range)*frustum.matrix.z;
  660. }
  661. // draw shadow map
  662. Flt step_bias=fov.max()*bias
  663. +DepthError(0, range, range, false, ImageTI[Renderer._shd_map.hwType()].d);
  664. DrawShadowMap(ShdDirStepDir[i], light_matrix, flag, 0, range, fov, FOV_ORTHO, 0, step_bias, D.shadowReduceFlicker() ? &frustum : null); // set 'frustum' only if we need it, because if 'shadowReduceFlicker' is disabled, then we've already aligned the matrix and we don't need to do anything more
  665. }
  666. skip:
  667. // matrix, transform from camera space to light space, ShdMatrix=CamMatrix/LightMatrix
  668. // potentially we could do 'light_matrix.inverse(true);" and then CamMatrix*light_matrix, however 'divNormalized' is just as fast
  669. if(Renderer._stereo)
  670. {
  671. EyeMatrix[0].divNormalized(light_matrix, ShdMatrix[0]);
  672. EyeMatrix[1].divNormalized(light_matrix, ShdMatrix[1]);
  673. }else ActiveCam.matrix .divNormalized(light_matrix, ShdMatrix[0]);
  674. #if FLAT_SHADOW_MAP
  675. {
  676. Matrix4 mp; ProjMatrix.mul(HsmMatrixSet(ShdDirStepDir[i]), mp);
  677. REPS(Renderer._eye, Renderer._eye_num)ShdMatrix[Renderer._eye].mul(mp, ShdMatrix4[i][Renderer._eye]);
  678. }
  679. #else // cube shadow maps
  680. REPS(Renderer._eye, Renderer._eye_num)
  681. {
  682. // scale down from -fov..fov to -1..1 range
  683. Matrix m=ShdMatrix[Renderer._eye];
  684. MatrixFovScaleX(m, D._view_active.fov.x);
  685. MatrixFovScaleY(m, D._view_active.fov.y);
  686. Matrix4 &d=ShdMatrix4[i][Renderer._eye]; d=m;
  687. // copy 'z' to 'w' which will be used as final 'z' comparison value
  688. d.x .w=d.x .z;
  689. d.y .w=d.y .z;
  690. d.z .w=d.z .z;
  691. d.pos.w=d.pos.z;
  692. // apply bias
  693. d.pos.w-=step_bias;
  694. // adjust orientation for cube texture
  695. switch(ShdDirStepDir[i])
  696. {
  697. case DIR_FORWARD: d.x.z=0; d.y.z=0; d.z.z=0; d.pos.z= 1; break;
  698. case DIR_BACK : CHS(d.x.x); CHS(d.y.x); CHS(d.z.x); CHS(d.pos.x); d.x.z=0; d.y.z=0; d.z.z=0; d.pos.z=-1; break;
  699. case DIR_RIGHT : d.x.z=-d.x.x; d.y.z=-d.y.x; d.z.z=-d.z.x; d.pos.z=-d.pos.x; d.x.x=0; d.y.x=0; d.z.x=0; d.pos.x= 1; break;
  700. case DIR_LEFT : d.x.z= d.x.x; d.y.z= d.y.x; d.z.z= d.z.x; d.pos.z= d.pos.x; d.x.x=0; d.y.x=0; d.z.x=0; d.pos.x=-1; break;
  701. case DIR_UP : d.x.z=-d.x.y; d.y.z=-d.y.y; d.z.z=-d.z.y; d.pos.z=-d.pos.y; d.x.y=0; d.y.y=0; d.z.y=0; d.pos.y= 1; break;
  702. case DIR_DOWN : d.x.z= d.x.y; d.y.z= d.y.y; d.z.z= d.z.y; d.pos.z= d.pos.y; d.x.y=0; d.y.y=0; d.z.y=0; d.pos.y=-1; break;
  703. }
  704. }
  705. #endif
  706. s0=s1;
  707. CopyFastN(&point[0], &point[4], 4);
  708. }
  709. // restore settings
  710. Renderer.mode(mode);
  711. #if DX9
  712. D.colWrite(COL_WRITE_RGBA); // restore color writes
  713. #endif
  714. RestoreShadowMapSettings();
  715. D.clip(clip ? &clip_rect : null); // no need to reset 'D.clipAllow' because it will be reset in the nearest RT change
  716. return cloud_shd;
  717. }
  718. /******************************************************************************/
  719. static void ShadowMap(Flt range, VecD &pos)
  720. {
  721. RENDER_MODE mode=Renderer(); Renderer.mode(RM_SHADOW);
  722. D.alpha(ALPHA_NONE); // disable alpha
  723. #if DX9
  724. Renderer.set(&Renderer._shd_map_null, &Renderer._shd_map, false); // on DX9 there always needs to be a RT set
  725. D.colWrite(0); // disable color writes
  726. #else
  727. Renderer.set(null, &Renderer._shd_map, false);
  728. D.clearDepth(); // clear all at once, must be done here because DX10+ and GL depth clear always clears full depth buffer
  729. #endif
  730. Int border=0;
  731. #if FLAT_SHADOW_MAP
  732. {
  733. Flt detail=Sat(CurrentLight.rect.size().max() / Min(D.w2(), D.h2())) * D.shadowMapSizeLocal();
  734. Int half =D.shadowMapSizeActual()/2;
  735. border=Mid(half-RoundPos(detail*half), 0, half-1);
  736. }
  737. #endif
  738. Int map_size=D.shadowMapSizeActual()-border*2;
  739. // render to shadow maps
  740. {
  741. UInt flag=(FrustumMain(pos) ? 0 : SM_FRUSTUM); // if the light source is inside the main frustum, then we don't need to test all light frustums because they all will be required
  742. #if FLAT_SHADOW_MAP
  743. Vec2 fov =2*Atan(1+2.0f/map_size); // padd 2 pixels of shadow map, needed for correct filtering at borders of each of 6 shadow maps, this doesn't require adjusting any other parameters, because it affects 'ProjMatrix' which is used later for matrix calculation
  744. #else
  745. Vec2 fov =PI_2;
  746. #endif
  747. //Flt view_main_max=DistPointPlane(pos, ActiveCam.matrix.pos, ActiveCam.matrix.z)+range;
  748. DrawShadowMap(DIR_FORWARD, MatrixM().setPos (pos ), flag, LOCAL_SHADOW_MAP_FROM, range, fov, FOV_XY, border, 0);
  749. DrawShadowMap(DIR_BACK , MatrixM().setPosDir(pos, VecDir[DIR_BACK ], VecDir[DIR_UP ]), flag, LOCAL_SHADOW_MAP_FROM, range, fov, FOV_XY, border, 0);
  750. DrawShadowMap(DIR_LEFT , MatrixM().setPosDir(pos, VecDir[DIR_LEFT ], VecDir[DIR_UP ]), flag, LOCAL_SHADOW_MAP_FROM, range, fov, FOV_XY, border, 0);
  751. DrawShadowMap(DIR_RIGHT , MatrixM().setPosDir(pos, VecDir[DIR_RIGHT], VecDir[DIR_UP ]), flag, LOCAL_SHADOW_MAP_FROM, range, fov, FOV_XY, border, 0);
  752. DrawShadowMap(DIR_DOWN , MatrixM().setPosDir(pos, VecDir[DIR_DOWN ], VecDir[DIR_FORWARD]), flag, LOCAL_SHADOW_MAP_FROM, range, fov, FOV_XY, border, 0);
  753. DrawShadowMap(DIR_UP , MatrixM().setPosDir(pos, VecDir[DIR_UP ], VecDir[DIR_BACK ]), flag, LOCAL_SHADOW_MAP_FROM, range, fov, FOV_XY, border, 0);
  754. }
  755. // matrix, transform from camera space to light space, ShdMatrix=CamMatrix/LightMatrix, ShdMatrix=CamMatrix-pos
  756. if(Renderer._stereo)
  757. {
  758. ShdMatrix[0].orn()= EyeMatrix[0].orn(); ShdMatrix[0].pos= EyeMatrix[0].pos-pos;
  759. ShdMatrix[1].orn()= EyeMatrix[1].orn(); ShdMatrix[1].pos= EyeMatrix[1].pos-pos;
  760. }else{ShdMatrix[0].orn()=ActiveCam.matrix .orn(); ShdMatrix[0].pos=ActiveCam.matrix .pos-pos;}
  761. #if FLAT_SHADOW_MAP
  762. {
  763. Flt f=D.shadowMapSizeActual()/Flt(map_size), bias=1-GetBias(); // for local lights instead of offsetting camera by bias, we scale it, because the shadow map is perspective so it needs to depend on distance to light
  764. Matrix m;
  765. Matrix4 mp;
  766. ProjMatrix.mul(HsmMatrixSet(DIR_RIGHT ), mp); REPS(Renderer._eye, Renderer._eye_num){Matrix &s=ShdMatrix[Renderer._eye]; m=s; m.x.x=-s.x.z; m.y.x=-s.y.z; m.z.x=-s.z.z; m.pos.x=-s.pos.z; m.x.z= s.x.x; m.y.z= s.y.x; m.z.z= s.z.x; m.pos.z= s.pos.x; MatrixFovScale(m, f); m.scale(bias); m.mul(mp, ShdMatrix4[0][Renderer._eye]);} // right
  767. ProjMatrix.mul(HsmMatrixSet(DIR_LEFT ), mp); REPS(Renderer._eye, Renderer._eye_num){Matrix &s=ShdMatrix[Renderer._eye]; m=s; m.x.x= s.x.z; m.y.x= s.y.z; m.z.x= s.z.z; m.pos.x= s.pos.z; m.x.z=-s.x.x; m.y.z=-s.y.x; m.z.z=-s.z.x; m.pos.z=-s.pos.x; MatrixFovScale(m, f); m.scale(bias); m.mul(mp, ShdMatrix4[1][Renderer._eye]);} // left
  768. ProjMatrix.mul(HsmMatrixSet(DIR_UP ), mp); REPS(Renderer._eye, Renderer._eye_num){Matrix &s=ShdMatrix[Renderer._eye]; m=s; m.x.z= s.x.y; m.y.z= s.y.y; m.z.z= s.z.y; m.pos.z= s.pos.y; m.x.y=-s.x.z; m.y.y=-s.y.z; m.z.y=-s.z.z; m.pos.y=-s.pos.z; MatrixFovScale(m, f); m.scale(bias); m.mul(mp, ShdMatrix4[2][Renderer._eye]);} // up
  769. ProjMatrix.mul(HsmMatrixSet(DIR_DOWN ), mp); REPS(Renderer._eye, Renderer._eye_num){Matrix &s=ShdMatrix[Renderer._eye]; m=s; m.x.z=-s.x.y; m.y.z=-s.y.y; m.z.z=-s.z.y; m.pos.z=-s.pos.y; m.x.y= s.x.z; m.y.y= s.y.z; m.z.y= s.z.z; m.pos.y= s.pos.z; MatrixFovScale(m, f); m.scale(bias); m.mul(mp, ShdMatrix4[3][Renderer._eye]);} // down
  770. ProjMatrix.mul(HsmMatrixSet(DIR_BACK ), mp); REPS(Renderer._eye, Renderer._eye_num){Matrix &s=ShdMatrix[Renderer._eye]; m=s; CHS(m.x.x); CHS(m.y.x); CHS(m.z.x); CHS(m.pos.x); CHS(m.x.z); CHS(m.y.z); CHS(m.z.z); CHS(m.pos.z); MatrixFovScale(m, f); m.scale(bias); m.mul(mp, ShdMatrix4[5][Renderer._eye]);} // back
  771. ProjMatrix.mul(HsmMatrixSet(DIR_FORWARD), mp); REPS(Renderer._eye, Renderer._eye_num){Matrix &s=ShdMatrix[Renderer._eye]; m=s; MatrixFovScale(m, f); m.scale(bias); m.mul(mp, ShdMatrix4[4][Renderer._eye]);} // forward
  772. }
  773. #endif
  774. // restore settings
  775. Renderer.mode(mode); // restore rendering mode to correctly restore viewport
  776. #if DX9
  777. D.colWrite(COL_WRITE_RGBA); // restore color writes
  778. #endif
  779. RestoreShadowMapSettings();
  780. }
  781. /******************************************************************************/
  782. static void ShadowMap(LightCone &light)
  783. {
  784. RENDER_MODE mode=Renderer(); Renderer.mode(RM_SHADOW);
  785. D.alpha(ALPHA_NONE); // disable alpha
  786. #if DX9
  787. Renderer.set(&Renderer._shd_map_null, &Renderer._shd_map, false); // on DX9 there always needs to be a RT set
  788. D.colWrite(0); // disable color writes
  789. #else
  790. Renderer.set(null, &Renderer._shd_map, false);
  791. D.clearDepth(); // clear all at once, must be done here because DX10+ and GL depth clear always clears full depth buffer
  792. #endif
  793. Int map_size=D.shadowMapSizeActual(), border=0;
  794. #if FLAT_SHADOW_MAP
  795. {
  796. map_size*=2; // for FLAT_SHADOW_MAP Cone we can use 2x map size because we have 2x3 sized full shadow map
  797. Flt detail=Sat(CurrentLight.rect.size().max() / Min(D.w2(), D.h2())) * D.shadowMapSizeLocal() * (D.shadowMapSizeCone()*0.5f);
  798. Int half =map_size/2;
  799. border=Mid(half-RoundPos(detail*half), 0, half-1);
  800. map_size-=border*2;
  801. }
  802. #endif
  803. // shadow map
  804. MatrixM light_matrix=light.pyramid;
  805. //Flt view_main_max=DistPointPlane(light_matrix.pos, ActiveCam.matrix.pos, ActiveCam.matrix.z)+light.pyramid.h; // this is not precise but worst case scenario
  806. DrawShadowMap(DIR_FORWARD, light_matrix, 0, LOCAL_SHADOW_MAP_FROM, light.pyramid.h, Vec2(2*Atan(light.pyramid.scale)), FOV_XY, border, 0); // use DIR_FORWARD because cube map shadows require that
  807. // matrix, transform from camera space to light space, ShdMatrix=CamMatrix/LightMatrix
  808. // potentially we could do "light_matrix.inverse(true);" and then CamMatrix*LightMatrix, however 'divNormalized' is just as fast
  809. if(Renderer._stereo)
  810. {
  811. EyeMatrix[0].divNormalized(light_matrix, ShdMatrix[0]);
  812. EyeMatrix[1].divNormalized(light_matrix, ShdMatrix[1]);
  813. }else ActiveCam.matrix .divNormalized(light_matrix, ShdMatrix[0]);
  814. #if FLAT_SHADOW_MAP
  815. {
  816. Flt f=D.shadowMapSizeActual()/Flt(map_size), bias=1-GetBias(); // for local lights instead of offsetting camera by bias, we scale it, because the shadow map is perspective so it needs to depend on distance to light
  817. Matrix4 mp; ProjMatrix.mul(HsmMatrixCone, mp);
  818. REPS(Renderer._eye, Renderer._eye_num){Matrix m=ShdMatrix[Renderer._eye]; MatrixFovScale(m, f); m.scale(bias); m.mul(mp, ShdMatrix4[0][Renderer._eye]);}
  819. }
  820. #endif
  821. REPS(Renderer._eye, Renderer._eye_num)MatrixFovScale(ShdMatrix[Renderer._eye], light.pyramid.scale);
  822. // restore settings
  823. Renderer.mode(mode); // restore rendering mode to correctly restore viewport
  824. #if DX9
  825. D.colWrite(COL_WRITE_RGBA); // restore color writes
  826. #endif
  827. RestoreShadowMapSettings();
  828. }
  829. /******************************************************************************/
  830. Flt Light::range()C
  831. {
  832. switch(type)
  833. {
  834. case LIGHT_POINT: return point.range() ;
  835. case LIGHT_SQR : return sqr .range ;
  836. case LIGHT_CONE : return cone .pyramid.h;
  837. default : return 0 ;
  838. }
  839. }
  840. Flt Light::vol()C
  841. {
  842. switch(type)
  843. {
  844. case LIGHT_DIR : return dir .vol;
  845. case LIGHT_POINT: return point.vol;
  846. case LIGHT_SQR : return sqr .vol;
  847. case LIGHT_CONE : return cone .vol;
  848. default : return 0 ;
  849. }
  850. }
  851. VecD Light::pos()C
  852. {
  853. switch(type)
  854. {
  855. case LIGHT_POINT: return point.pos ;
  856. case LIGHT_SQR : return sqr .pos ;
  857. case LIGHT_CONE : return cone .pyramid.pos;
  858. default : return 0 ;
  859. }
  860. }
  861. Bool Light::toScreenRect(Rect &rect)C
  862. {
  863. switch(type)
  864. {
  865. case LIGHT_DIR : return dir .toScreenRect(rect);
  866. case LIGHT_POINT: return point.toScreenRect(rect);
  867. case LIGHT_SQR : return sqr .toScreenRect(rect);
  868. case LIGHT_CONE : return cone .toScreenRect(rect);
  869. default : return false;
  870. }
  871. }
  872. void Light::scalePower(Flt scale)
  873. {
  874. switch(type)
  875. {
  876. case LIGHT_DIR : dir .color *=scale; break;
  877. case LIGHT_POINT: point.power *=scale; break;
  878. case LIGHT_SQR : sqr .range *=scale; break;
  879. case LIGHT_CONE : cone .pyramid.h*=scale; break;
  880. }
  881. }
  882. /******************************************************************************/
  883. void Light::set()
  884. {
  885. switch(type)
  886. {
  887. case LIGHT_DIR : dir .set( ); break;
  888. case LIGHT_POINT: point.set(shadow_opacity); break;
  889. case LIGHT_SQR : sqr .set(shadow_opacity); break;
  890. case LIGHT_CONE : cone .set(shadow_opacity); break;
  891. }
  892. }
  893. /******************************************************************************/
  894. static Bool CanDoShadow()
  895. {
  896. return D.shadowMode() && D.shadowSupported() && Renderer._cur_type!=RT_SIMPLE && FovPerspective(D.viewFovMode());
  897. }
  898. void Light::set(LightDir &light, Bool shadow , CPtr light_src) {Zero(T); type=LIGHT_DIR ; dir =light; T.rect=D.viewRect(); T.shadow=(shadow && CanDoShadow()); T.shadow_opacity=(T.shadow ); T.src=light_src;}
  899. void Light::set(LightPoint &light, C Rect &rect, Flt shadow_opacity, CPtr light_src) {Zero(T); type=LIGHT_POINT; point=light; T.rect= rect; T.shadow=(shadow_opacity>EPS_COL && CanDoShadow()); T.shadow_opacity=(T.shadow ? Sat(shadow_opacity) : 0); T.src=light_src;}
  900. void Light::set(LightSqr &light, C Rect &rect, Flt shadow_opacity, CPtr light_src) {Zero(T); type=LIGHT_SQR ; sqr =light; T.rect= rect; T.shadow=(shadow_opacity>EPS_COL && CanDoShadow()); T.shadow_opacity=(T.shadow ? Sat(shadow_opacity) : 0); T.src=light_src;}
  901. void Light::set(LightCone &light, C Rect &rect, Flt shadow_opacity, CPtr light_src) {Zero(T); type=LIGHT_CONE ; cone =light; T.rect= rect; T.shadow=(shadow_opacity>EPS_COL && CanDoShadow()); T.shadow_opacity=(T.shadow ? Sat(shadow_opacity) : 0); T.src=light_src;}
  902. /******************************************************************************/
  903. void Light::draw()
  904. {
  905. // !! Here process water lum first, because after drawing all lights, we still have to apply ambient from meshes, so if we process water lum first, and then lum, then we don't need to change RT's to lum again when drawing ambient from meshes !!
  906. SetShadowOpacity(shadow_opacity);
  907. CurrentLight=T;
  908. switch(CurrentLight.type)
  909. {
  910. case LIGHT_DIR:
  911. {
  912. Int shd_map_num;
  913. Bool cloud=false;
  914. if(CurrentLight.shadow)
  915. {
  916. shd_map_num=D.shadowMapNumActual();
  917. cloud=ShadowMap(CurrentLight.dir);
  918. }
  919. D.depth2DOn();
  920. // water lum first, as noted above in the comments
  921. if(Renderer._water_nrm)
  922. {
  923. D.stencil(STENCIL_WATER_TEST, STENCIL_REF_WATER);
  924. Sh.h_ImageDepth->set(Renderer._water_ds); // set water depth
  925. if(CurrentLight.shadow)
  926. {
  927. // no need for view space bias, because we're calculating shadow for water surfaces, which by themself don't cast shadows and are usually above shadow surfaces
  928. Renderer.getShdRT();
  929. Renderer.set(Renderer._shd_1s(), Renderer._water_ds(), true, NEED_DEPTH_READ); // use DS because it may be used for 'D.depth2D' optimization and stencil tests
  930. REPS(Renderer._eye, Renderer._eye_num)if(SetLightEye(true))GetShdDir(Mid(shd_map_num, 1, 6), cloud, false)->draw(null, &CurrentLight.rect);
  931. }
  932. SetWaterLum();
  933. REPS(Renderer._eye, Renderer._eye_num)if(SetLightEye())GetLightDir(CurrentLight.shadow, false, true)->draw(Renderer._shd_1s, &CurrentLight.rect); // always use Quality Specular for Water
  934. Sh.h_ImageDepth->set(Renderer._ds_1s); // restore default depth
  935. D.stencil(STENCIL_NONE);
  936. }
  937. // lum
  938. if(CurrentLight.shadow)
  939. {
  940. Renderer.getShdRT();
  941. Flt mp_z_z; ApplyViewSpaceBias(mp_z_z);
  942. if(!Renderer._ds->multiSample())
  943. {
  944. Renderer.set(Renderer._shd_1s(), Renderer._ds_1s(), true, NEED_DEPTH_READ); // use DS because it may be used for 'D.depth2D' optimization
  945. REPS(Renderer._eye, Renderer._eye_num)if(SetLightEye(true))GetShdDir(shd_map_num, cloud, false)->draw(null, &CurrentLight.rect);
  946. }else
  947. { // we can ignore 'Renderer.hasStencilAttached' because we would have to apply for all samples of '_shd_ms' and '_shd_1s' which will happen anyway below
  948. Renderer.set(Renderer._shd_ms(), Renderer._ds (), true, NEED_DEPTH_READ); D.stencil(STENCIL_MSAA_TEST, STENCIL_REF_MSAA); REPS(Renderer._eye, Renderer._eye_num)if(SetLightEye(true))GetShdDir(shd_map_num, cloud, true )->draw(null, &CurrentLight.rect); // use DS because it may be used for 'D.depth2D' optimization
  949. Renderer.set(Renderer._shd_1s(), Renderer._ds_1s(), true, NEED_DEPTH_READ); D.stencil(STENCIL_NONE ); REPS(Renderer._eye, Renderer._eye_num)if(SetLightEye(true))GetShdDir(shd_map_num, cloud, false)->draw(null, &CurrentLight.rect); // use DS because it may be used for 'D.depth2D' optimization, for all stencil samples because they are needed for smoothing
  950. }
  951. RestoreViewSpaceBias(mp_z_z);
  952. MapSoft();
  953. ApplyVolumetric(CurrentLight.dir, shd_map_num, false); // TODO: cloud shd/vol is disabled
  954. }
  955. Bool clear=SetLum();
  956. if(!Renderer._ds->multiSample()) // 1-sample
  957. {
  958. REPS(Renderer._eye, Renderer._eye_num)if(SetLightEye())GetLightDir(CurrentLight.shadow, false, D.highPrecNrmCalcIs())->draw(Renderer._shd_1s, &CurrentLight.rect);
  959. #if TEST_LIGHT_RECT
  960. REPS(Renderer._eye, Renderer._eye_num)if(SetLightEye())Sh.clear(Vec4(0.3f, 0.3f, 0.3f, 0), &CurrentLight.rect);
  961. #endif
  962. }else // multi-sample
  963. {
  964. if(Renderer.hasStencilAttached()) // if we can use stencil tests, then process 1-sample pixels using 1-sample shader, if we can't use stencil then all pixels will be processed using multi-sample shader later below
  965. {
  966. D.stencil(STENCIL_MSAA_TEST, 0);
  967. REPS(Renderer._eye, Renderer._eye_num)if(SetLightEye())GetLightDir(CurrentLight.shadow, false, D.highPrecNrmCalcIs())->draw(Renderer._shd_1s, &CurrentLight.rect);
  968. }
  969. Renderer.set(Renderer._lum(), Renderer._ds(), true, NEED_DEPTH_READ); // use DS because it may be used for 'D.depth2D' optimization and stencil tests
  970. if(clear)
  971. {
  972. D.depth2DOff(); D.stencil(STENCIL_NONE); D.clearCol(Renderer._ao ? Vec4Zero : Vec4(D.ambientColor(), 0));
  973. D.depth2DOn ();
  974. }
  975. /*if(Renderer.hasStencilAttached()) not needed because stencil tests are disabled without stencil RT */D.stencil(STENCIL_MSAA_TEST, STENCIL_REF_MSAA);
  976. REPS(Renderer._eye, Renderer._eye_num)if(SetLightEye())GetLightDir(CurrentLight.shadow, true, D.highPrecNrmCalcIs())->draw(Renderer._shd_ms, &CurrentLight.rect);
  977. D.stencil(STENCIL_NONE);
  978. }
  979. }break;
  980. case LIGHT_POINT:
  981. {
  982. if(CurrentLight.shadow)ShadowMap(CurrentLight.point.range(), CurrentLight.point.pos);
  983. D.depth2DOn();
  984. // water lum first, as noted above in the comments
  985. if(Renderer._water_nrm)
  986. {
  987. D.stencil(STENCIL_WATER_TEST, STENCIL_REF_WATER);
  988. Sh.h_ImageDepth->set(Renderer._water_ds); // set water depth
  989. if(CurrentLight.shadow)
  990. {
  991. // no need for view space bias, because we're calculating shadow for water surfaces, which by themself don't cast shadows and are usually above shadow surfaces
  992. Renderer.getShdRT();
  993. Renderer.set(Renderer._shd_1s(), Renderer._water_ds(), true, NEED_DEPTH_READ); // use DS because it may be used for 'D.depth2D' optimization and stencil tests
  994. REPS(Renderer._eye, Renderer._eye_num)if(SetLightEye(true))GetShdPnt(false)->draw(null, &CurrentLight.rect);
  995. }
  996. SetWaterLum();
  997. REPS(Renderer._eye, Renderer._eye_num)if(SetLightEye())GetLightPnt(CurrentLight.shadow, false, true)->draw(Renderer._shd_1s, &CurrentLight.rect); // always use Quality Specular for Water
  998. Sh.h_ImageDepth->set(Renderer._ds_1s); // restore default depth
  999. D.stencil(STENCIL_NONE);
  1000. }
  1001. // lum
  1002. if(CurrentLight.shadow)
  1003. {
  1004. Renderer.getShdRT();
  1005. Flt mp_z_z; ApplyViewSpaceBias(mp_z_z);
  1006. if(!Renderer._ds->multiSample())
  1007. {
  1008. Renderer.set(Renderer._shd_1s(), Renderer._ds_1s(), true, NEED_DEPTH_READ); // use DS because it may be used for 'D.depth2D' optimization
  1009. REPS(Renderer._eye, Renderer._eye_num)if(SetLightEye(true))GetShdPnt(false)->draw(null, &CurrentLight.rect);
  1010. }else
  1011. { // we can ignore 'Renderer.hasStencilAttached' because we would have to apply for all samples of '_shd_ms' and '_shd_1s' which will happen anyway below
  1012. Renderer.set(Renderer._shd_ms(), Renderer._ds (), true, NEED_DEPTH_READ); D.stencil(STENCIL_MSAA_TEST, STENCIL_REF_MSAA); REPS(Renderer._eye, Renderer._eye_num)if(SetLightEye(true))GetShdPnt(true )->draw(null, &CurrentLight.rect); // use DS because it may be used for 'D.depth2D' optimization
  1013. Renderer.set(Renderer._shd_1s(), Renderer._ds_1s(), true, NEED_DEPTH_READ); D.stencil(STENCIL_NONE ); REPS(Renderer._eye, Renderer._eye_num)if(SetLightEye(true))GetShdPnt(false)->draw(null, &CurrentLight.rect); // use DS because it may be used for 'D.depth2D' optimization, for all stencil samples because they are needed for smoothing
  1014. }
  1015. RestoreViewSpaceBias(mp_z_z);
  1016. MapSoft();
  1017. ApplyVolumetric(CurrentLight.point);
  1018. }
  1019. Bool clear=SetLum();
  1020. if(!Renderer._ds->multiSample()) // 1-sample
  1021. {
  1022. REPS(Renderer._eye, Renderer._eye_num)if(SetLightEye())GetLightPnt(CurrentLight.shadow, false, D.highPrecNrmCalcIs())->draw(Renderer._shd_1s, &CurrentLight.rect);
  1023. #if TEST_LIGHT_RECT
  1024. REPS(Renderer._eye, Renderer._eye_num)if(SetLightEye())Sh.clear(Vec4(0.3f, 0.3f, 0.3f, 0), &CurrentLight.rect);
  1025. #endif
  1026. }else // multi-sample
  1027. {
  1028. if(Renderer.hasStencilAttached()) // if we can use stencil tests, then process 1-sample pixels using 1-sample shader, if we can't use stencil then all pixels will be processed using multi-sample shader later below
  1029. {
  1030. D.stencil(STENCIL_MSAA_TEST, 0);
  1031. REPS(Renderer._eye, Renderer._eye_num)if(SetLightEye())GetLightPnt(CurrentLight.shadow, false, D.highPrecNrmCalcIs())->draw(Renderer._shd_1s, &CurrentLight.rect);
  1032. }
  1033. Renderer.set(Renderer._lum(), Renderer._ds(), true, NEED_DEPTH_READ); // use DS because it may be used for 'D.depth2D' optimization and stencil tests
  1034. if(clear)
  1035. {
  1036. D.depth2DOff(); D.stencil(STENCIL_NONE); D.clearCol(Renderer._ao ? Vec4Zero : Vec4(D.ambientColor(), 0));
  1037. D.depth2DOn ();
  1038. }
  1039. /*if(Renderer.hasStencilAttached()) not needed because stencil tests are disabled without stencil RT */D.stencil(STENCIL_MSAA_TEST, STENCIL_REF_MSAA);
  1040. REPS(Renderer._eye, Renderer._eye_num)if(SetLightEye())GetLightPnt(CurrentLight.shadow, true, D.highPrecNrmCalcIs())->draw(Renderer._shd_ms, &CurrentLight.rect);
  1041. D.stencil(STENCIL_NONE);
  1042. }
  1043. }break;
  1044. case LIGHT_SQR:
  1045. {
  1046. if(CurrentLight.shadow)ShadowMap(CurrentLight.sqr.range, CurrentLight.sqr.pos);
  1047. D.depth2DOn();
  1048. // water lum first, as noted above in the comments
  1049. if(Renderer._water_nrm)
  1050. {
  1051. D.stencil(STENCIL_WATER_TEST, STENCIL_REF_WATER);
  1052. Sh.h_ImageDepth->set(Renderer._water_ds); // set water depth
  1053. if(CurrentLight.shadow)
  1054. {
  1055. // no need for view space bias, because we're calculating shadow for water surfaces, which by themself don't cast shadows and are usually above shadow surfaces
  1056. Renderer.getShdRT();
  1057. Renderer.set(Renderer._shd_1s(), Renderer._water_ds(), true, NEED_DEPTH_READ); // use DS because it may be used for 'D.depth2D' optimization and stencil tests
  1058. REPS(Renderer._eye, Renderer._eye_num)if(SetLightEye(true))GetShdPnt(false)->draw(null, &CurrentLight.rect);
  1059. }
  1060. SetWaterLum();
  1061. REPS(Renderer._eye, Renderer._eye_num)if(SetLightEye())GetLightSqr(CurrentLight.shadow, false, true)->draw(Renderer._shd_1s, &CurrentLight.rect); // always use Quality Specular for Water
  1062. Sh.h_ImageDepth->set(Renderer._ds_1s); // restore default depth
  1063. D.stencil(STENCIL_NONE);
  1064. }
  1065. // lum
  1066. if(CurrentLight.shadow)
  1067. {
  1068. Renderer.getShdRT();
  1069. Flt mp_z_z; ApplyViewSpaceBias(mp_z_z);
  1070. if(!Renderer._ds->multiSample())
  1071. {
  1072. Renderer.set(Renderer._shd_1s(), Renderer._ds_1s(), true, NEED_DEPTH_READ); // use DS because it may be used for 'D.depth2D' optimization
  1073. REPS(Renderer._eye, Renderer._eye_num)if(SetLightEye(true))GetShdPnt(false)->draw(null, &CurrentLight.rect);
  1074. }else
  1075. { // we can ignore 'Renderer.hasStencilAttached' because we would have to apply for all samples of '_shd_ms' and '_shd_1s' which will happen anyway below
  1076. Renderer.set(Renderer._shd_ms(), Renderer._ds (), true, NEED_DEPTH_READ); D.stencil(STENCIL_MSAA_TEST, STENCIL_REF_MSAA); REPS(Renderer._eye, Renderer._eye_num)if(SetLightEye(true))GetShdPnt(true )->draw(null, &CurrentLight.rect); // use DS because it may be used for 'D.depth2D' optimization
  1077. Renderer.set(Renderer._shd_1s(), Renderer._ds_1s(), true, NEED_DEPTH_READ); D.stencil(STENCIL_NONE ); REPS(Renderer._eye, Renderer._eye_num)if(SetLightEye(true))GetShdPnt(false)->draw(null, &CurrentLight.rect); // use DS because it may be used for 'D.depth2D' optimization, for all stencil samples because they are needed for smoothing
  1078. }
  1079. RestoreViewSpaceBias(mp_z_z);
  1080. MapSoft();
  1081. ApplyVolumetric(CurrentLight.sqr);
  1082. }
  1083. Bool clear=SetLum();
  1084. if(!Renderer._ds->multiSample()) // 1-sample
  1085. {
  1086. REPS(Renderer._eye, Renderer._eye_num)if(SetLightEye())GetLightSqr(CurrentLight.shadow, false, D.highPrecNrmCalcIs())->draw(Renderer._shd_1s, &CurrentLight.rect);
  1087. #if TEST_LIGHT_RECT
  1088. REPS(Renderer._eye, Renderer._eye_num)if(SetLightEye())Sh.clear(Vec4(0.3f, 0.3f, 0.3f, 0), &CurrentLight.rect);
  1089. #endif
  1090. }else // multi-sample
  1091. {
  1092. if(Renderer.hasStencilAttached()) // if we can use stencil tests, then process 1-sample pixels using 1-sample shader, if we can't use stencil then all pixels will be processed using multi-sample shader later below
  1093. {
  1094. D.stencil(STENCIL_MSAA_TEST, 0);
  1095. REPS(Renderer._eye, Renderer._eye_num)if(SetLightEye())GetLightSqr(CurrentLight.shadow, false, D.highPrecNrmCalcIs())->draw(Renderer._shd_1s, &CurrentLight.rect);
  1096. }
  1097. Renderer.set(Renderer._lum(), Renderer._ds(), true, NEED_DEPTH_READ); // use DS because it may be used for 'D.depth2D' optimization and stencil tests
  1098. if(clear)
  1099. {
  1100. D.depth2DOff(); D.stencil(STENCIL_NONE); D.clearCol(Renderer._ao ? Vec4Zero : Vec4(D.ambientColor(), 0));
  1101. D.depth2DOn ();
  1102. }
  1103. /*if(Renderer.hasStencilAttached()) not needed because stencil tests are disabled without stencil RT */D.stencil(STENCIL_MSAA_TEST, STENCIL_REF_MSAA);
  1104. REPS(Renderer._eye, Renderer._eye_num)if(SetLightEye())GetLightSqr(CurrentLight.shadow, true, D.highPrecNrmCalcIs())->draw(Renderer._shd_ms, &CurrentLight.rect);
  1105. D.stencil(STENCIL_NONE);
  1106. }
  1107. }break;
  1108. case LIGHT_CONE:
  1109. {
  1110. if(CurrentLight.shadow)ShadowMap(CurrentLight.cone);
  1111. D.depth2DOn();
  1112. // water lum first, as noted above in the comments
  1113. if(Renderer._water_nrm)
  1114. {
  1115. D.stencil(STENCIL_WATER_TEST, STENCIL_REF_WATER);
  1116. Sh.h_ImageDepth->set(Renderer._water_ds); // set water depth
  1117. if(CurrentLight.shadow)
  1118. {
  1119. // no need for view space bias, because we're calculating shadow for water surfaces, which by themself don't cast shadows and are usually above shadow surfaces
  1120. Renderer.getShdRT();
  1121. Renderer.set(Renderer._shd_1s(), Renderer._water_ds(), true, NEED_DEPTH_READ); // use DS because it may be used for 'D.depth2D' optimization and stencil tests
  1122. REPS(Renderer._eye, Renderer._eye_num)if(SetLightEye(true))GetShdCone(false)->draw(null, &CurrentLight.rect);
  1123. }
  1124. SetWaterLum();
  1125. REPS(Renderer._eye, Renderer._eye_num)if(SetLightEye())GetLightCone(CurrentLight.shadow, CurrentLight.image?1:0, false, true)->draw(Renderer._shd_1s, &CurrentLight.rect); // always use Quality Specular for Water
  1126. Sh.h_ImageDepth->set(Renderer._ds_1s); // restore default depth
  1127. D.stencil(STENCIL_NONE);
  1128. }
  1129. // lum
  1130. if(CurrentLight.shadow)
  1131. {
  1132. Renderer.getShdRT();
  1133. Flt mp_z_z; ApplyViewSpaceBias(mp_z_z);
  1134. if(!Renderer._ds->multiSample())
  1135. {
  1136. Renderer.set(Renderer._shd_1s(), Renderer._ds_1s(), true, NEED_DEPTH_READ); // use DS because it may be used for 'D.depth2D' optimization
  1137. REPS(Renderer._eye, Renderer._eye_num)if(SetLightEye(true))GetShdCone(false)->draw(null, &CurrentLight.rect);
  1138. }else
  1139. { // we can ignore 'Renderer.hasStencilAttached' because we would have to apply for all samples of '_shd_ms' and '_shd_1s' which will happen anyway below
  1140. Renderer.set(Renderer._shd_ms(), Renderer._ds (), true, NEED_DEPTH_READ); D.stencil(STENCIL_MSAA_TEST, STENCIL_REF_MSAA); REPS(Renderer._eye, Renderer._eye_num)if(SetLightEye(true))GetShdCone(true )->draw(null, &CurrentLight.rect); // use DS because it may be used for 'D.depth2D' optimization
  1141. Renderer.set(Renderer._shd_1s(), Renderer._ds_1s(), true, NEED_DEPTH_READ); D.stencil(STENCIL_NONE ); REPS(Renderer._eye, Renderer._eye_num)if(SetLightEye(true))GetShdCone(false)->draw(null, &CurrentLight.rect); // use DS because it may be used for 'D.depth2D' optimization, for all stencil samples because they are needed for smoothing
  1142. }
  1143. RestoreViewSpaceBias(mp_z_z);
  1144. MapSoft();
  1145. ApplyVolumetric(CurrentLight.cone);
  1146. }
  1147. if(CurrentLight.image)
  1148. {
  1149. Sh.h_LightMapScale ->set(CurrentLight.image_scale );
  1150. Sh.h_LightMapColAdd ->set(CurrentLight.image_add );
  1151. Sh.h_LightMapSpecular->set(CurrentLight.image_specular);
  1152. Sh.h_ImageCol[1] ->set(CurrentLight.image );
  1153. }
  1154. Bool clear=SetLum();
  1155. if(!Renderer._ds->multiSample()) // 1-sample
  1156. {
  1157. REPS(Renderer._eye, Renderer._eye_num)if(SetLightEye())GetLightCone(CurrentLight.shadow, CurrentLight.image?1:0, false, D.highPrecNrmCalcIs())->draw(Renderer._shd_1s, &CurrentLight.rect);
  1158. #if TEST_LIGHT_RECT
  1159. REPS(Renderer._eye, Renderer._eye_num)if(SetLightEye())Sh.clear(Vec4(0.3f, 0.3f, 0.3f, 0), &CurrentLight.rect);
  1160. #endif
  1161. }else // multi-sample
  1162. {
  1163. if(Renderer.hasStencilAttached()) // if we can use stencil tests, then process 1-sample pixels using 1-sample shader, if we can't use stencil then all pixels will be processed using multi-sample shader later below
  1164. {
  1165. D.stencil(STENCIL_MSAA_TEST, 0);
  1166. REPS(Renderer._eye, Renderer._eye_num)if(SetLightEye())GetLightCone(CurrentLight.shadow, CurrentLight.image?1:0, false, D.highPrecNrmCalcIs())->draw(Renderer._shd_1s, &CurrentLight.rect);
  1167. }
  1168. Renderer.set(Renderer._lum(), Renderer._ds(), true, NEED_DEPTH_READ); // use DS because it may be used for 'D.depth2D' optimization and stencil tests
  1169. if(clear)
  1170. {
  1171. D.depth2DOff(); D.stencil(STENCIL_NONE); D.clearCol(Renderer._ao ? Vec4Zero : Vec4(D.ambientColor(), 0));
  1172. D.depth2DOn ();
  1173. }
  1174. /*if(Renderer.hasStencilAttached()) not needed because stencil tests are disabled without stencil RT */D.stencil(STENCIL_MSAA_TEST, STENCIL_REF_MSAA);
  1175. REPS(Renderer._eye, Renderer._eye_num)if(SetLightEye())GetLightCone(CurrentLight.shadow, CurrentLight.image?1:0, true, D.highPrecNrmCalcIs())->draw(Renderer._shd_ms, &CurrentLight.rect);
  1176. D.stencil(STENCIL_NONE);
  1177. }
  1178. }break;
  1179. }
  1180. D.depth2DOff();
  1181. Renderer._shd_1s.clear();
  1182. Renderer._shd_ms.clear();
  1183. }
  1184. /******************************************************************************/
  1185. void Light::drawForward(Image *dest, ALPHA_MODE alpha)
  1186. {
  1187. SetShadowOpacity(shadow_opacity);
  1188. CurrentLight=T;
  1189. switch(CurrentLight.type)
  1190. {
  1191. case LIGHT_DIR:
  1192. {
  1193. Int shd_map_num;
  1194. if(CurrentLight.shadow)
  1195. {
  1196. shd_map_num=D.shadowMapNumActual();
  1197. ShadowMap(CurrentLight.dir);
  1198. Renderer._frst_light_offset=OFFSET(FRST, dir_shd[Mid(shd_map_num, 1, 6)-1]);
  1199. }else
  1200. {
  1201. Renderer._frst_light_offset=OFFSET(FRST, dir);
  1202. }
  1203. Renderer.set(dest, Renderer._ds(), true);
  1204. D.alpha(alpha);
  1205. D.set3D();
  1206. if(Renderer.firstPass())
  1207. {
  1208. D.stencil(STENCIL_ALWAYS_SET, 0);
  1209. }else
  1210. { // we need to generate list of objects
  1211. Renderer.mode(RM_PREPARE); Frustum.set(); Renderer._render(); // set frustum after setting render mode
  1212. D.clipAllow(true);
  1213. }
  1214. Renderer.mode(RM_SOLID);
  1215. REPS(Renderer._eye, Renderer._eye_num)
  1216. {
  1217. Renderer.setEyeViewport();
  1218. if(CurrentLight.shadow)SetShdMatrix();
  1219. CurrentLight.dir.set();
  1220. if(Renderer.secondaryPass())D.clip(Renderer._clip); // clip rendering to area affected by the light
  1221. DrawSolidInstances(); Renderer._render();
  1222. }
  1223. ClearSolidInstances();
  1224. D.set2D();
  1225. if(Renderer.firstPass()){D.stencil(STENCIL_NONE); Renderer.resolveDepth();}
  1226. // water lum
  1227. if(Renderer._water_nrm)
  1228. {
  1229. D.depth2DOn(); D.stencil(STENCIL_WATER_TEST, STENCIL_REF_WATER);
  1230. Sh.h_ImageDepth->set(Renderer._water_ds); // set water depth
  1231. if(CurrentLight.shadow)
  1232. {
  1233. // no need for view space bias, because we're calculating shadow for water surfaces, which by themself don't cast shadows and are usually above shadow surfaces
  1234. Renderer.getShdRT();
  1235. Renderer.set(Renderer._shd_1s(), Renderer._water_ds(), true, NEED_DEPTH_READ); // use DS because it may be used for 'D.depth2D' optimization and stencil tests
  1236. REPS(Renderer._eye, Renderer._eye_num)if(SetLightEye(true))GetShdDir(Mid(shd_map_num, 1, 6), false, false)->draw(null, &CurrentLight.rect);
  1237. }
  1238. SetWaterLum();
  1239. REPS(Renderer._eye, Renderer._eye_num)if(SetLightEye())GetLightDir(CurrentLight.shadow, false, true)->draw(Renderer._shd_1s, &CurrentLight.rect); // always use Quality Specular for Water
  1240. Sh.h_ImageDepth->set(Renderer._ds_1s); // restore default depth
  1241. D.depth2DOff(); D.stencil(STENCIL_NONE);
  1242. }
  1243. }break;
  1244. case LIGHT_POINT:
  1245. {
  1246. if(CurrentLight.shadow)
  1247. {
  1248. ShadowMap(CurrentLight.point.range(), CurrentLight.point.pos);
  1249. Renderer._frst_light_offset=OFFSET(FRST, pnt_shd);
  1250. }else
  1251. {
  1252. Renderer._frst_light_offset=OFFSET(FRST, pnt);
  1253. }
  1254. Renderer.set(dest, Renderer._ds(), true);
  1255. D.alpha(alpha);
  1256. D.set3D();
  1257. if(Renderer.firstPass())
  1258. {
  1259. D.stencil(STENCIL_ALWAYS_SET, 0);
  1260. }else
  1261. { // we need to generate list of objects
  1262. Renderer.mode(RM_PREPARE); Frustum.from(BoxD(CurrentLight.point.range(), CurrentLight.point.pos)); // set frustum after setting render mode
  1263. Renderer._render();
  1264. D.clipAllow(true);
  1265. }
  1266. Renderer.mode(RM_SOLID);
  1267. REPS(Renderer._eye, Renderer._eye_num)
  1268. {
  1269. Renderer.setEyeViewport();
  1270. GetCurrentLightRect(); // if(.. would require ClearSolidInstances afterwards, call this after setting viewport and camera
  1271. if(CurrentLight.shadow)SetShdMatrix();
  1272. CurrentLight.point.set(CurrentLight.shadow_opacity);
  1273. if(Renderer.secondaryPass())D.clip(CurrentLight.rect&Renderer._clip); // clip rendering to area affected by the light
  1274. DrawSolidInstances(); Renderer._render();
  1275. }
  1276. ClearSolidInstances();
  1277. D.set2D();
  1278. if(Renderer.firstPass()){D.stencil(STENCIL_NONE); Renderer.resolveDepth();}
  1279. // water lum
  1280. if(Renderer._water_nrm)
  1281. {
  1282. D.depth2DOn(); D.stencil(STENCIL_WATER_TEST, STENCIL_REF_WATER);
  1283. Sh.h_ImageDepth->set(Renderer._water_ds); // set water depth
  1284. if(CurrentLight.shadow)
  1285. {
  1286. // no need for view space bias, because we're calculating shadow for water surfaces, which by themself don't cast shadows and are usually above shadow surfaces
  1287. Renderer.getShdRT();
  1288. Renderer.set(Renderer._shd_1s(), Renderer._water_ds(), true, NEED_DEPTH_READ); // use DS because it may be used for 'D.depth2D' optimization and stencil tests
  1289. REPS(Renderer._eye, Renderer._eye_num)if(SetLightEye(true))GetShdPnt(false)->draw(null, &CurrentLight.rect);
  1290. }
  1291. SetWaterLum();
  1292. REPS(Renderer._eye, Renderer._eye_num)if(SetLightEye())GetLightPnt(CurrentLight.shadow, false, true)->draw(Renderer._shd_1s, &CurrentLight.rect); // always use Quality Specular for Water
  1293. Sh.h_ImageDepth->set(Renderer._ds_1s); // restore default depth
  1294. D.depth2DOff(); D.stencil(STENCIL_NONE);
  1295. }
  1296. }break;
  1297. case LIGHT_SQR:
  1298. {
  1299. if(CurrentLight.shadow)
  1300. {
  1301. ShadowMap(CurrentLight.sqr.range, CurrentLight.sqr.pos);
  1302. Renderer._frst_light_offset=OFFSET(FRST, sqr_shd);
  1303. }else
  1304. {
  1305. Renderer._frst_light_offset=OFFSET(FRST, sqr);
  1306. }
  1307. Renderer.set(dest, Renderer._ds(), true);
  1308. D.alpha(alpha);
  1309. D.set3D();
  1310. if(Renderer.firstPass())
  1311. {
  1312. D.stencil(STENCIL_ALWAYS_SET, 0);
  1313. }else
  1314. { // we need to generate list of objects
  1315. Renderer.mode(RM_PREPARE); Frustum.from(BoxD(CurrentLight.sqr.range, CurrentLight.sqr.pos)); // set frustum after setting render mode
  1316. Renderer._render();
  1317. D.clipAllow(true);
  1318. }
  1319. Renderer.mode(RM_SOLID);
  1320. REPS(Renderer._eye, Renderer._eye_num)
  1321. {
  1322. Renderer.setEyeViewport();
  1323. GetCurrentLightRect(); // if(.. would require ClearSolidInstances afterwards, call this after setting viewport and camera
  1324. if(CurrentLight.shadow)SetShdMatrix();
  1325. CurrentLight.sqr.set(CurrentLight.shadow_opacity);
  1326. if(Renderer.secondaryPass())D.clip(CurrentLight.rect&Renderer._clip); // clip rendering to area affected by the light
  1327. DrawSolidInstances(); Renderer._render();
  1328. }
  1329. ClearSolidInstances();
  1330. D.set2D();
  1331. if(Renderer.firstPass()){D.stencil(STENCIL_NONE); Renderer.resolveDepth();}
  1332. // water lum
  1333. if(Renderer._water_nrm)
  1334. {
  1335. D.depth2DOn(); D.stencil(STENCIL_WATER_TEST, STENCIL_REF_WATER);
  1336. Sh.h_ImageDepth->set(Renderer._water_ds); // set water depth
  1337. if(CurrentLight.shadow)
  1338. {
  1339. // no need for view space bias, because we're calculating shadow for water surfaces, which by themself don't cast shadows and are usually above shadow surfaces
  1340. Renderer.getShdRT();
  1341. Renderer.set(Renderer._shd_1s(), Renderer._water_ds(), true, NEED_DEPTH_READ); // use DS because it may be used for 'D.depth2D' optimization and stencil tests
  1342. REPS(Renderer._eye, Renderer._eye_num)if(SetLightEye(true))GetShdPnt(false)->draw(null, &CurrentLight.rect);
  1343. }
  1344. SetWaterLum();
  1345. REPS(Renderer._eye, Renderer._eye_num)if(SetLightEye())GetLightSqr(CurrentLight.shadow, false, true)->draw(Renderer._shd_1s, &CurrentLight.rect); // always use Quality Specular for Water
  1346. Sh.h_ImageDepth->set(Renderer._ds_1s); // restore default depth
  1347. D.depth2DOff(); D.stencil(STENCIL_NONE);
  1348. }
  1349. }break;
  1350. case LIGHT_CONE:
  1351. {
  1352. if(CurrentLight.shadow)
  1353. {
  1354. ShadowMap(CurrentLight.cone);
  1355. Renderer._frst_light_offset=OFFSET(FRST, cone_shd);
  1356. }else
  1357. {
  1358. Renderer._frst_light_offset=OFFSET(FRST, cone);
  1359. }
  1360. Renderer.set(dest, Renderer._ds(), true);
  1361. D.alpha(alpha);
  1362. D.set3D();
  1363. if(Renderer.firstPass())
  1364. {
  1365. D.stencil(STENCIL_ALWAYS_SET, 0);
  1366. }else
  1367. { // we need to generate list of objects
  1368. Renderer.mode(RM_PREPARE); Frustum.from(CurrentLight.cone.pyramid); // set frustum after setting render mode
  1369. Renderer._render();
  1370. D.clipAllow(true);
  1371. }
  1372. Renderer.mode(RM_SOLID);
  1373. REPS(Renderer._eye, Renderer._eye_num)
  1374. {
  1375. Renderer.setEyeViewport();
  1376. GetCurrentLightRect(); // if(.. would require ClearSolidInstances afterwards, call this after setting viewport and camera
  1377. if(CurrentLight.shadow)SetShdMatrix();
  1378. CurrentLight.cone.set(CurrentLight.shadow_opacity);
  1379. if(Renderer.secondaryPass())D.clip(CurrentLight.rect&Renderer._clip); // clip rendering to area affected by the light
  1380. DrawSolidInstances(); Renderer._render();
  1381. }
  1382. ClearSolidInstances();
  1383. D.set2D();
  1384. if(Renderer.firstPass()){D.stencil(STENCIL_NONE); Renderer.resolveDepth();}
  1385. // water lum
  1386. if(Renderer._water_nrm)
  1387. {
  1388. D.depth2DOn(); D.stencil(STENCIL_WATER_TEST, STENCIL_REF_WATER);
  1389. Sh.h_ImageDepth->set(Renderer._water_ds); // set water depth
  1390. if(CurrentLight.shadow)
  1391. {
  1392. // no need for view space bias, because we're calculating shadow for water surfaces, which by themself don't cast shadows and are usually above shadow surfaces
  1393. Renderer.getShdRT();
  1394. Renderer.set(Renderer._shd_1s(), Renderer._water_ds(), true, NEED_DEPTH_READ); // use DS because it may be used for 'D.depth2D' optimization and stencil tests
  1395. REPS(Renderer._eye, Renderer._eye_num)if(SetLightEye(true))GetShdCone(false)->draw(null, &CurrentLight.rect);
  1396. }
  1397. SetWaterLum();
  1398. REPS(Renderer._eye, Renderer._eye_num)if(SetLightEye())GetLightCone(CurrentLight.shadow, CurrentLight.image?1:0, false, true)->draw(Renderer._shd_1s, &CurrentLight.rect); // always use Quality Specular for Water
  1399. Sh.h_ImageDepth->set(Renderer._ds_1s); // restore default depth
  1400. D.depth2DOff(); D.stencil(STENCIL_NONE);
  1401. }
  1402. }break;
  1403. }
  1404. Renderer._shd_1s.clear();
  1405. Renderer._shd_ms.clear();
  1406. }
  1407. /******************************************************************************/
  1408. #define MAX_LIGHTS 255 // max amount of lights allowed when limiting lights
  1409. struct LightFade
  1410. {
  1411. Flt fade;
  1412. CPtr src ;
  1413. };
  1414. static LightFade LightFades[2][MAX_LIGHTS];
  1415. static Int LightFadesNum=0;
  1416. static Bool LightFadeIndex=false;
  1417. static Light LightTemp[MAX_LIGHTS];
  1418. void LimitLights()
  1419. {
  1420. if(Lights.elms() && D._max_lights)
  1421. {
  1422. MIN(D._max_lights, MAX_LIGHTS);
  1423. // limit number of lights
  1424. if(Lights.elms()>D._max_lights)
  1425. {
  1426. LightImportance.setNum(Lights.elms());
  1427. REPA(Lights)
  1428. {
  1429. Light &l=Lights[i];
  1430. LightImportance[i].f=((l.type==LIGHT_DIR) ? 1-l.dir.color.max() : 1+Dist2(ActiveCam.at, l.pos()));
  1431. LightImportance[i].i=i;
  1432. }
  1433. LightImportance.sort(FloatIndex::Compare);
  1434. REP(D._max_lights)LightTemp[i]=Lights[LightImportance[i].i];
  1435. Lights.setNum(D._max_lights); REPAO(Lights)=LightTemp[i];
  1436. }
  1437. // fade in
  1438. Int old_fades=LightFadesNum; LightFadesNum=0;
  1439. LightFade (&old_fade)[MAX_LIGHTS]=LightFades[ LightFadeIndex],
  1440. (&new_fade)[MAX_LIGHTS]=LightFades[!LightFadeIndex];
  1441. REPA(Lights)
  1442. {
  1443. Light &l=Lights[i];
  1444. if(l.src)
  1445. {
  1446. LightFade &lf=new_fade[LightFadesNum++];
  1447. lf.src =l.src;
  1448. lf.fade=Time.rd()*3; REP(old_fades)if(old_fade[i].src==l.src){lf.fade+=old_fade[i].fade; break;} SAT(lf.fade);
  1449. l.scalePower(lf.fade);
  1450. }
  1451. }
  1452. LightFadeIndex^=1;
  1453. }
  1454. }
  1455. void SortLights()
  1456. {
  1457. // put main directional light at start (this is needed for RM_BLEND_LIGHT*, RT_FORWARD and RT_SIMPLE)
  1458. if(Lights.elms()>1)
  1459. {
  1460. Int main =-1;
  1461. Flt power= 0; REPA(Lights)
  1462. {
  1463. Light &light=Lights[i];
  1464. if(light.type==LIGHT_DIR)
  1465. {
  1466. Flt p=light.dir.color.max();
  1467. if(main<0 || p>power){main=i; power=p;}
  1468. }
  1469. }
  1470. if(main>0)Swap(Lights[0], Lights[main]); // >0 already handles !=-1 (not found) and !=0 (no need to move)
  1471. }
  1472. }
  1473. void DrawLights()
  1474. {
  1475. if(Lights.elms())
  1476. {
  1477. REPAO(Lights).draw(); // apply in backward order to leave main shadow map in the end
  1478. RestoreLightSettings(); // restore this because camera matrix may have changed even when not rendering shadows, but when using stereoscopic rendering and setting cameras for each eye
  1479. MaterialClear();
  1480. }
  1481. }
  1482. /******************************************************************************/
  1483. void ShutLight() {Lights.del(); LightImportance.del();}
  1484. void InitLight()
  1485. {
  1486. HsmMatrix.x .x= 0.5f/2;
  1487. HsmMatrix.y .y=-0.5f/3;
  1488. HsmMatrix.z .z= 1;
  1489. HsmMatrix.pos.w= 1;
  1490. HsmMatrixCone=HsmMatrix;
  1491. HsmMatrixCone.pos.x=0.5f;
  1492. HsmMatrixCone.pos.y=Avg(0, 2.0f/3);
  1493. }
  1494. /******************************************************************************/
  1495. }
  1496. /******************************************************************************/