GXS.ShadowVolume.pas 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956
  1. //
  2. // The graphics engine GLXEngine. The unit of GXScene for Delphi
  3. //
  4. unit GXS.ShadowVolume;
  5. (*
  6. Implements basic shadow volumes support.
  7. Be aware that only objects that support silhouette determination have a chance
  8. to cast correct shadows. Transparent/blended/shader objects among the receivers
  9. or the casters will be rendered incorrectly.
  10. *)
  11. interface
  12. {$I Stage.Defines.inc}
  13. uses
  14. Winapi.OpenGL,
  15. Winapi.OpenGLext,
  16. System.SysUtils,
  17. System.Classes,
  18. Stage.VectorTypes,
  19. Stage.VectorGeometry,
  20. Stage.PipelineTransform,
  21. GXS.VectorLists,
  22. GXS.PersistentClasses,
  23. GXS.GeometryBB,
  24. GXS.Scene,
  25. GXS.Context,
  26. GXS.Silhouette,
  27. GXS.State,
  28. GXS.Color,
  29. GXS.RenderContextInfo;
  30. type
  31. TgxShadowVolume = class;
  32. (* Determines when a shadow volume should generate a cap at the beginning and
  33. end of the volume. This is ONLY necessary when there's a chance that the
  34. camera could end up inside the shadow _or_ between the light source and
  35. the camera. If those two situations can't occur then not using capping is
  36. the best option.
  37. Note that if you use the capping, you must either set the depth of view of
  38. your camera to something very large (f.i. 1e9), or you could use the infinite
  39. mode (csInfinitePerspective) of your camera.
  40. svcDefault : Default behaviour
  41. svcAlways : Always generates caps
  42. svcNever : Never generates caps *)
  43. TgxShadowVolumeCapping = (svcDefault, svcAlways, svcNever);
  44. (* Determines when a caster should actually produce a shadow;
  45. scmAlways : Caster always produces a shadow, ignoring visibility
  46. scmVisible : Caster casts shadow if the object has visible=true
  47. scmRecursivelyVisible : Caster casts shadow if ancestors up the hierarchy
  48. all have visible=true
  49. scmParentVisible : Caster produces shadow if parent has visible=true
  50. scmParentRecursivelyVisible : Caster casts shadow if ancestors up the hierarchy
  51. all have visible=true, starting from the parent (ignoring own visible setting) *)
  52. TgxShadowCastingMode = (scmAlways, scmVisible, scmRecursivelyVisible,
  53. scmParentVisible, scmParentRecursivelyVisible);
  54. (* Specifies an individual shadow caster.
  55. Can be a light or an opaque object. *)
  56. TgxShadowVolumeCaster = class(TCollectionItem)
  57. private
  58. FCaster: TgxBaseSceneObject;
  59. FEffectiveRadius: Single;
  60. FCapping: TgxShadowVolumeCapping;
  61. FCastingMode: TgxShadowCastingMode;
  62. protected
  63. procedure SetCaster(const val: TgxBaseSceneObject);
  64. function GetGLShadowVolume: TgxShadowVolume;
  65. procedure RemoveNotification(aComponent: TComponent);
  66. function GetDisplayName: string; override;
  67. public
  68. constructor Create(ACollection: TCollection); override;
  69. destructor Destroy; override;
  70. procedure Assign(Source: TPersistent); override;
  71. { Shadow casting object. Can be an opaque object or a lightsource. }
  72. property Caster: TgxBaseSceneObject read FCaster write SetCaster;
  73. property GLShadowVolume: TgxShadowVolume read GetGLShadowVolume;
  74. published
  75. (* Radius beyond which the caster can be ignored.
  76. Zero (default value) means the caster can never be ignored. *)
  77. property EffectiveRadius: Single read FEffectiveRadius write FEffectiveRadius;
  78. (* Specifies if the shadow volume should be capped.
  79. Capping helps solve shadowing artefacts, at the cost of performance. *)
  80. property Capping: TgxShadowVolumeCapping read FCapping write FCapping default svcDefault;
  81. (* Determines when an object should cast a shadow or not. Typically, objects
  82. should only cast shadows when recursively visible. But if you're using
  83. dummy shadow casters which are less complex than their parent objects,
  84. you should use scmParentRecursivelyVisible.*)
  85. property CastingMode: TgxShadowCastingMode read FCastingMode write
  86. FCastingMode default scmRecursivelyVisible;
  87. end;
  88. // Specifies an individual shadow casting occluder.
  89. TgxShadowVolumeOccluder = class(TgxShadowVolumeCaster)
  90. published
  91. property Caster;
  92. end;
  93. // Specifies an individual shadow casting light.
  94. TgxShadowVolumeLight = class(TgxShadowVolumeCaster)
  95. private
  96. FSilhouettes: TgxPersistentObjectList;
  97. protected
  98. function GetLightSource: TgxLightSource;
  99. procedure SetLightSource(const ls: TgxLightSource);
  100. function GetCachedSilhouette(AIndex: Integer): TgxSilhouette; inline;
  101. procedure StoreCachedSilhouette(AIndex: Integer; ASil: TgxSilhouette);
  102. (* Compute and setup scissor clipping rect for the light.
  103. Returns true if a scissor rect was setup *)
  104. function SetupScissorRect(worldAABB: PAABB; var rci: TgxRenderContextInfo): Boolean;
  105. public
  106. constructor Create(ACollection: TCollection); override;
  107. destructor Destroy; override;
  108. procedure FlushSilhouetteCache;
  109. published
  110. // Shadow casting lightsource.
  111. property LightSource: TgxLightSource read GetLightSource write SetLightSource;
  112. end;
  113. // Collection of TgxShadowVolumeCaster.
  114. TgxShadowVolumeCasters = class(TOwnedCollection)
  115. protected
  116. function GetItems(index: Integer): TgxShadowVolumeCaster;
  117. procedure RemoveNotification(aComponent: TComponent);
  118. public
  119. function AddCaster(obj: TgxBaseSceneObject; effectiveRadius: Single = 0;
  120. CastingMode: TgxShadowCastingMode = scmRecursivelyVisible):
  121. TgxShadowVolumeCaster;
  122. procedure RemoveCaster(obj: TgxBaseSceneObject);
  123. function IndexOfCaster(obj: TgxBaseSceneObject): Integer;
  124. property Items[index: Integer]: TgxShadowVolumeCaster read GetItems; default;
  125. end;
  126. (* Shadow volume rendering options/optimizations.
  127. svoShowVolumes : make the shadow volumes visible
  128. svoDesignVisible : the shadow are visible at design-time
  129. svoCacheSilhouettes : cache shadow volume silhouettes, beneficial when
  130. some objects are static relatively to their light(s)
  131. svoScissorClips : use scissor clipping per light, beneficial when
  132. lights are attenuated and don't illuminate the whole scene
  133. svoWorldScissorClip : use scissor clipping for the world, beneficial
  134. when shadow receivers don't cover the whole viewer surface *)
  135. TgxShadowVolumeOption = (svoShowVolumes, svoCacheSilhouettes, svoScissorClips,
  136. svoWorldScissorClip, svoDesignVisible);
  137. TgxShadowVolumeOptions = set of TgxShadowVolumeOption;
  138. (* Shadow rendering modes.
  139. svmAccurate : will render the scene with ambient lighting only, then
  140. for each light will make a diffuse+specular pass
  141. svmDarkening : renders the scene with lighting on as usual, then darkens
  142. shadowed areas (i.e. inaccurate lighting, but will "shadow" objects
  143. that don't honour to diffuse or specular lighting)
  144. svmOff : no shadowing will take place *)
  145. TgxShadowVolumeMode = (svmAccurate, svmDarkening, svmOff);
  146. (* Simple shadow volumes.
  147. Shadow receiving objects are the ShadowVolume's children, shadow casters
  148. (opaque objects or lights) must be explicitly specified in the Casters
  149. collection.
  150. Shadow volumes require that the buffer allows stencil buffers,
  151. GLXceneViewer.Buffer.ContextOptions contain roStencinBuffer. Without stencil
  152. buffers, shadow volumes will not work properly.
  153. Another issue to look out for is the fact that shadow volume capping requires
  154. that the camera depth of view is either very high (fi 1e9) or that the
  155. camera style is csInfinitePerspective. *)
  156. TgxShadowVolume = class(TgxImmaterialSceneObject)
  157. private
  158. FActive: Boolean;
  159. FRendering: Boolean;
  160. FLights: TgxShadowVolumeCasters;
  161. FOccluders: TgxShadowVolumeCasters;
  162. FCapping: TgxShadowVolumeCapping;
  163. FOptions: TgxShadowVolumeOptions;
  164. FMode: TgxShadowVolumeMode;
  165. FDarkeningColor: TgxColor;
  166. protected
  167. procedure Notification(AComponent: TComponent; Operation: TOperation); override;
  168. procedure SetActive(const val: Boolean);
  169. procedure SetLights(const val: TgxShadowVolumeCasters);
  170. procedure SetOccluders(const val: TgxShadowVolumeCasters);
  171. procedure SetOptions(const val: TgxShadowVolumeOptions);
  172. procedure SetMode(const val: TgxShadowVolumeMode);
  173. procedure SetDarkeningColor(const val: TgxColor);
  174. public
  175. constructor Create(AOwner: TComponent); override;
  176. destructor Destroy; override;
  177. procedure DoRender(var ARci: TgxRenderContextInfo;
  178. ARenderSelf, ARenderChildren: Boolean); override;
  179. procedure Assign(Source: TPersistent); override;
  180. procedure FlushSilhouetteCache;
  181. published
  182. (* Determines if shadow volume rendering is active.
  183. When set to false, children will be rendered without any shadowing
  184. or multipass lighting. *)
  185. property Active: Boolean read FActive write SetActive default True;
  186. // Lights that cast shadow volumes.
  187. property Lights: TgxShadowVolumeCasters read FLights write SetLights;
  188. // Occluders that cast shadow volumes.
  189. property Occluders: TgxShadowVolumeCasters read FOccluders write SetOccluders;
  190. (* Specifies if the shadow volume should be capped.
  191. Capping helps solve shadowing artefacts, at the cost of performance. *)
  192. property Capping: TgxShadowVolumeCapping read FCapping write FCapping default
  193. svcAlways;
  194. // Shadow volume rendering options.
  195. property Options: TgxShadowVolumeOptions read FOptions write SetOptions
  196. default [svoCacheSilhouettes, svoScissorClips];
  197. // Shadow rendering mode.
  198. property Mode: TgxShadowVolumeMode read FMode write SetMode default svmAccurate;
  199. // Darkening color used in svmDarkening mode.
  200. property DarkeningColor: TgxColor read FDarkeningColor write SetDarkeningColor;
  201. end;
  202. //-------------------------------------------------------------
  203. implementation
  204. //-------------------------------------------------------------
  205. // ------------------
  206. // ------------------ TgxShadowVolumeCaster ------------------
  207. // ------------------
  208. constructor TgxShadowVolumeCaster.Create(ACollection: TCollection);
  209. begin
  210. inherited Create(ACollection);
  211. FCapping := svcDefault;
  212. FCastingMode := scmRecursivelyVisible;
  213. end;
  214. type
  215. // Required for Delphi 5 support.
  216. THackOwnedCollection = class(TOwnedCollection);
  217. function TgxShadowVolumeCaster.GetGLShadowVolume: TgxShadowVolume;
  218. begin
  219. Result := TgxShadowVolume(THackOwnedCollection(Collection).GetOwner);
  220. end;
  221. destructor TgxShadowVolumeCaster.Destroy;
  222. begin
  223. if Assigned(FCaster) then
  224. FCaster.RemoveFreeNotification(GLShadowVolume);
  225. inherited;
  226. end;
  227. procedure TgxShadowVolumeCaster.Assign(Source: TPersistent);
  228. begin
  229. if Source is TgxShadowVolumeCaster then
  230. begin
  231. FCaster := TgxShadowVolumeCaster(Source).FCaster;
  232. FEffectiveRadius := TgxShadowVolumeCaster(Source).FEffectiveRadius;
  233. FCapping := TgxShadowVolumeCaster(Source).FCapping;
  234. GetGLShadowVolume.StructureChanged;
  235. end;
  236. inherited;
  237. end;
  238. procedure TgxShadowVolumeCaster.SetCaster(const val: TgxBaseSceneObject);
  239. begin
  240. if FCaster <> val then
  241. begin
  242. if FCaster <> nil then
  243. FCaster.RemoveFreeNotification(GLShadowVolume);
  244. FCaster := val;
  245. if FCaster <> nil then
  246. FCaster.FreeNotification(GLShadowVolume);
  247. GetGLShadowVolume.StructureChanged;
  248. end;
  249. end;
  250. procedure TgxShadowVolumeCaster.RemoveNotification(aComponent: TComponent);
  251. begin
  252. if aComponent = FCaster then
  253. begin
  254. // No point in keeping the TgxShadowVolumeCaster once the FCaster has been
  255. // destroyed.
  256. FCaster := nil;
  257. Free;
  258. end;
  259. end;
  260. function TgxShadowVolumeCaster.GetDisplayName: string;
  261. begin
  262. if Assigned(FCaster) then
  263. begin
  264. if FCaster is TgxLightSource then
  265. Result := '[Light]'
  266. else
  267. Result := '[Object]';
  268. Result := Result + ' ' + FCaster.Name;
  269. if EffectiveRadius > 0 then
  270. Result := Result + Format(' (%.1f)', [EffectiveRadius]);
  271. end
  272. else
  273. Result := 'nil';
  274. end;
  275. // ------------------
  276. // ------------------ TgxShadowVolumeLight ------------------
  277. // ------------------
  278. constructor TgxShadowVolumeLight.Create(ACollection: TCollection);
  279. begin
  280. inherited Create(ACollection);
  281. FSilhouettes := TgxPersistentObjectList.Create;
  282. end;
  283. destructor TgxShadowVolumeLight.Destroy;
  284. begin
  285. FlushSilhouetteCache;
  286. FSilhouettes.Free;
  287. inherited;
  288. end;
  289. procedure TgxShadowVolumeLight.FlushSilhouetteCache;
  290. begin
  291. FSilhouettes.Clean;
  292. end;
  293. function TgxShadowVolumeLight.GetLightSource: TgxLightSource;
  294. begin
  295. Result := TgxLightSource(Caster);
  296. end;
  297. procedure TgxShadowVolumeLight.SetLightSource(const ls: TgxLightSource);
  298. begin
  299. SetCaster(ls);
  300. end;
  301. function TgxShadowVolumeLight.GetCachedSilhouette(AIndex: Integer):
  302. TgxSilhouette;
  303. begin
  304. if AIndex < FSilhouettes.Count then
  305. Result := TgxSilhouette(FSilhouettes[AIndex])
  306. else
  307. Result := nil;
  308. end;
  309. procedure TgxShadowVolumeLight.StoreCachedSilhouette(AIndex: Integer; ASil:
  310. TgxSilhouette);
  311. begin
  312. while AIndex >= FSilhouettes.Count do
  313. FSilhouettes.Add(nil);
  314. if ASil <> FSilhouettes[AIndex] then
  315. begin
  316. if assigned(FSilhouettes[AIndex]) then
  317. FSilhouettes[AIndex].Free;
  318. FSilhouettes[AIndex] := ASil;
  319. end;
  320. end;
  321. function TgxShadowVolumeLight.SetupScissorRect(worldAABB: PAABB; var rci:
  322. TgxRenderContextInfo): Boolean;
  323. var
  324. mvp: TMatrix4f;
  325. ls: TgxLightSource;
  326. aabb: TAABB;
  327. clipRect: TGClipRect;
  328. begin
  329. ls := LightSource;
  330. if (EffectiveRadius <= 0) or (not ls.Attenuated) then
  331. begin
  332. // non attenuated lights can't be clipped
  333. if not Assigned(worldAABB) then
  334. begin
  335. Result := False;
  336. Exit;
  337. end
  338. else
  339. aabb := worldAABB^;
  340. end
  341. else
  342. begin
  343. aabb := BSphereToAABB(ls.AbsolutePosition, EffectiveRadius);
  344. if Assigned(worldAABB) then
  345. aabb := AABBIntersection(aabb, worldAABB^);
  346. end;
  347. if PointInAABB(rci.cameraPosition, aabb) then
  348. begin
  349. // camera inside light volume radius, can't clip
  350. Result := False;
  351. Exit;
  352. end;
  353. // Calculate the window-space bounds of the light's bounding box.
  354. mvp := rci.PipelineTransformation.ViewProjectionMatrix^;
  355. clipRect := AABBToClipRect(aabb, mvp, rci.viewPortSize.cx,
  356. rci.viewPortSize.cy);
  357. if (clipRect.Right < 0) or (clipRect.Left > rci.viewPortSize.cx)
  358. or (clipRect.Top < 0) or (clipRect.Bottom > rci.viewPortSize.cy) then
  359. begin
  360. Result := False;
  361. Exit;
  362. end;
  363. with clipRect do
  364. glScissor(Round(Left), Round(Top), Round(Right - Left), Round(Bottom -
  365. Top));
  366. Result := True;
  367. end;
  368. // ------------------
  369. // ------------------ TgxShadowVolumeCasters ------------------
  370. // ------------------
  371. procedure TgxShadowVolumeCasters.RemoveNotification(aComponent: TComponent);
  372. var
  373. i: Integer;
  374. begin
  375. for i := Count - 1 downto 0 do
  376. Items[i].RemoveNotification(aComponent);
  377. end;
  378. function TgxShadowVolumeCasters.GetItems(index: Integer): TgxShadowVolumeCaster;
  379. begin
  380. Result := TgxShadowVolumeCaster(inherited Items[index]);
  381. end;
  382. function TgxShadowVolumeCasters.AddCaster(obj: TgxBaseSceneObject;
  383. effectiveRadius: Single = 0;
  384. CastingMode: TgxShadowCastingMode = scmRecursivelyVisible):
  385. TgxShadowVolumeCaster;
  386. var
  387. newCaster: TgxShadowVolumeCaster;
  388. begin
  389. newCaster := TgxShadowVolumeCaster(Add);
  390. newCaster.Caster := obj;
  391. newCaster.EffectiveRadius := effectiveRadius;
  392. newCaster.CastingMode := CastingMode;
  393. result := newCaster;
  394. end;
  395. procedure TgxShadowVolumeCasters.RemoveCaster(obj: TgxBaseSceneObject);
  396. var
  397. i: Integer;
  398. begin
  399. i := IndexOfCaster(obj);
  400. if i >= 0 then
  401. Delete(i);
  402. end;
  403. function TgxShadowVolumeCasters.IndexOfCaster(obj: TgxBaseSceneObject): Integer;
  404. var
  405. i: Integer;
  406. begin
  407. for i := 0 to Count - 1 do
  408. begin
  409. if Items[i].Caster = obj then
  410. begin
  411. Result := i;
  412. Exit;
  413. end;
  414. end;
  415. Result := -1;
  416. end;
  417. // ------------------
  418. // ------------------ TgxShadowVolume ------------------
  419. // ------------------
  420. constructor TgxShadowVolume.Create(AOwner: Tcomponent);
  421. begin
  422. inherited Create(AOwner);
  423. ObjectStyle := ObjectStyle - [osDirectDraw] + [osNoVisibilityCulling];
  424. FActive := True;
  425. FLights := TgxShadowVolumeCasters.Create(self, TgxShadowVolumeLight);
  426. FOccluders := TgxShadowVolumeCasters.Create(self, TgxShadowVolumeOccluder);
  427. FCapping := svcAlways;
  428. FMode := svmAccurate;
  429. FOptions := [svoCacheSilhouettes, svoScissorClips];
  430. FDarkeningColor := TgxColor.CreateInitialized(Self, VectorMake(0, 0, 0, 0.5));
  431. end;
  432. destructor TgxShadowVolume.Destroy;
  433. begin
  434. inherited;
  435. FDarkeningColor.Free;
  436. FLights.Free;
  437. FOccluders.Free;
  438. end;
  439. procedure TgxShadowVolume.Notification(AComponent: TComponent; Operation:
  440. TOperation);
  441. begin
  442. if Operation = opRemove then
  443. begin
  444. FLights.RemoveNotification(AComponent);
  445. FOccluders.RemoveNotification(AComponent);
  446. end;
  447. inherited;
  448. end;
  449. procedure TgxShadowVolume.Assign(Source: TPersistent);
  450. begin
  451. if Assigned(Source) and (Source is TgxShadowVolume) then
  452. begin
  453. FLights.Assign(TgxShadowVolume(Source).Lights);
  454. FOccluders.Assign(TgxShadowVolume(Source).Occluders);
  455. FCapping := TgxShadowVolume(Source).FCapping;
  456. StructureChanged;
  457. end;
  458. inherited Assign(Source);
  459. end;
  460. procedure TgxShadowVolume.FlushSilhouetteCache;
  461. var
  462. i: Integer;
  463. begin
  464. for i := 0 to Lights.Count - 1 do
  465. (Lights[i] as TgxShadowVolumeLight).FlushSilhouetteCache;
  466. end;
  467. procedure TgxShadowVolume.SetActive(const val: Boolean);
  468. begin
  469. if FActive <> val then
  470. begin
  471. FActive := val;
  472. StructureChanged;
  473. end;
  474. end;
  475. procedure TgxShadowVolume.SetLights(const val: TgxShadowVolumeCasters);
  476. begin
  477. Assert(val.ItemClass = TgxShadowVolumeLight);
  478. FLights.Assign(val);
  479. StructureChanged;
  480. end;
  481. procedure TgxShadowVolume.SetOccluders(const val: TgxShadowVolumeCasters);
  482. begin
  483. Assert(val.ItemClass = TgxShadowVolumeOccluder);
  484. FOccluders.Assign(val);
  485. StructureChanged;
  486. end;
  487. procedure TgxShadowVolume.SetOptions(const val: TgxShadowVolumeOptions);
  488. begin
  489. if FOptions <> val then
  490. begin
  491. FOptions := val;
  492. if not (svoCacheSilhouettes in FOptions) then
  493. FlushSilhouetteCache;
  494. StructureChanged;
  495. end;
  496. end;
  497. procedure TgxShadowVolume.SetMode(const val: TgxShadowVolumeMode);
  498. begin
  499. if FMode <> val then
  500. begin
  501. FMode := val;
  502. StructureChanged;
  503. end;
  504. end;
  505. procedure TgxShadowVolume.SetDarkeningColor(const val: TgxColor);
  506. begin
  507. FDarkeningColor.Assign(val);
  508. end;
  509. procedure TgxShadowVolume.DoRender(var ARci: TgxRenderContextInfo;
  510. ARenderSelf, ARenderChildren: Boolean);
  511. // Function that determines if an object is "recursively visible". It halts when
  512. // * it finds an invisible ancestor (=> invisible)
  513. // * it finds the root (=> visible)
  514. // * it finds the shadow volume as an ancestor (=> visible)
  515. //
  516. // This does _not_ mean that the object is actually visible on the screen
  517. function DirectHierarchicalVisibility(obj: TgxBaseSceneObject): boolean;
  518. var
  519. p: TgxBaseSceneObject;
  520. begin
  521. if not Assigned(obj) then
  522. begin
  523. Result := True;
  524. exit;
  525. end;
  526. if not obj.Visible then
  527. begin
  528. Result := False;
  529. Exit;
  530. end;
  531. p := obj.Parent;
  532. while Assigned(p) and (p <> obj) and (p <> Self) do
  533. begin
  534. if not p.Visible then
  535. begin
  536. Result := False;
  537. Exit;
  538. end;
  539. p := p.Parent;
  540. end;
  541. Result := True;
  542. end;
  543. var
  544. i, k: Integer;
  545. lightSource: TgxLightSource;
  546. lightCaster: TgxShadowVolumeLight;
  547. sil: TgxSilhouette;
  548. lightID: Cardinal;
  549. obj: TgxBaseSceneObject;
  550. caster: TgxShadowVolumeCaster;
  551. opaques, opaqueCapping: TList;
  552. silParams: TgxSilhouetteParameters;
  553. worldAABB: TAABB;
  554. pWorldAABB: PAABB;
  555. PM: TMatrix4f;
  556. begin
  557. if not Active then
  558. begin
  559. inherited;
  560. Exit;
  561. end;
  562. if FRendering then
  563. Exit;
  564. if not (ARenderSelf or ARenderChildren) then
  565. Exit;
  566. ClearStructureChanged;
  567. if ((csDesigning in ComponentState) and not (svoDesignVisible in Options))
  568. or (Mode = svmOff)
  569. or (ARci.drawState = dsPicking) then
  570. begin
  571. inherited;
  572. Exit;
  573. end;
  574. if svoWorldScissorClip in Options then
  575. begin
  576. // compute shadow receiving world AABB in absolute coordinates
  577. worldAABB := Self.AxisAlignedBoundingBox;
  578. AABBTransform(worldAABB, AbsoluteMatrix);
  579. pWorldAABB := @worldAABB;
  580. end
  581. else
  582. pWorldAABB := nil;
  583. opaques := TList.Create;
  584. opaqueCapping := TList.Create;
  585. FRendering := True;
  586. try
  587. // collect visible casters
  588. for i := 0 to Occluders.Count - 1 do
  589. begin
  590. caster := Occluders[i];
  591. obj := caster.Caster;
  592. if Assigned(obj)
  593. and
  594. // Determine when to render this object or not
  595. (
  596. (Caster.CastingMode = scmAlways) or
  597. ((Caster.CastingMode = scmVisible) and obj.Visible) or
  598. ((Caster.CastingMode = scmRecursivelyVisible) and
  599. DirectHierarchicalVisibility(obj)) or
  600. ((Caster.CastingMode = scmParentRecursivelyVisible) and
  601. DirectHierarchicalVisibility(obj.Parent)) or
  602. ((Caster.CastingMode = scmParentVisible) and (not Assigned(obj.Parent)
  603. or
  604. obj.Parent.Visible))
  605. )
  606. and ((caster.EffectiveRadius <= 0)
  607. or (obj.DistanceTo(ARci.cameraPosition) < caster.EffectiveRadius)) then
  608. begin
  609. opaques.Add(obj);
  610. opaqueCapping.Add(Pointer(Cardinal(ord((caster.Capping = svcAlways)
  611. or ((caster.Capping = svcDefault)
  612. and (Capping = svcAlways))))));
  613. end
  614. else
  615. begin
  616. opaques.Add(nil);
  617. opaqueCapping.Add(nil);
  618. end;
  619. end;
  620. // render the shadow volumes
  621. with ARci.gxStates do
  622. begin
  623. if Mode = svmAccurate then
  624. begin
  625. // first turn off all the shadow casting lights diffuse and specular
  626. for i := 0 to Lights.Count - 1 do
  627. begin
  628. lightCaster := TgxShadowVolumeLight(Lights[i]);
  629. lightSource := lightCaster.LightSource;
  630. if Assigned(lightSource) and (lightSource.Shining) then
  631. begin
  632. lightID := lightSource.LightID;
  633. LightDiffuse[lightID] := NullHmgVector;
  634. LightSpecular[lightID] := NullHmgVector;
  635. end;
  636. end;
  637. end;
  638. // render shadow receivers with ambient lighting
  639. // DanB - not sure why this doesn't render properly with these statements
  640. // where they were originally (after the RenderChildren call).
  641. Self.RenderChildren(0, Count - 1, ARci);
  642. ARci.ignoreBlendingRequests := True;
  643. ARci.ignoreDepthRequests := True;
  644. DepthWriteMask := False;
  645. Enable(stDepthTest);
  646. SetBlendFunc(bfSrcAlpha, bfOne);
  647. Disable(stAlphaTest);
  648. Enable(stStencilTest);
  649. // Disable all client states
  650. /// if GL_ARB_vertex_buffer_object then
  651. begin
  652. VertexArrayBinding := 0;
  653. ArrayBufferBinding := 0;
  654. ElementBufferBinding := 0;
  655. end;
  656. // turn off *all* lights
  657. for i := 0 to TgxScene(ARci.scene).Lights.Count - 1 do
  658. begin
  659. lightSource := (TgxScene(ARci.scene).Lights.Items[i]) as TgxLightSource;
  660. if Assigned(lightSource) and lightSource.Shining then
  661. LightEnabling[lightSource.LightID] := False;
  662. end;
  663. glLightModelfv(GL_LIGHT_MODEL_AMBIENT, @NullHmgPoint);
  664. ARci.PipelineTransformation.Push;
  665. // render contribution of all shadow casting lights
  666. for i := 0 to Lights.Count - 1 do
  667. begin
  668. lightCaster := TgxShadowVolumeLight(lights[i]);
  669. lightSource := lightCaster.LightSource;
  670. if (not Assigned(lightSource)) or (not lightSource.Shining) then
  671. Continue;
  672. lightID := lightSource.LightID;
  673. SetVector(silParams.LightDirection,
  674. lightSource.SpotDirection.DirectVector);
  675. case lightSource.LightStyle of
  676. lsParallel: silParams.Style := ssParallel
  677. else
  678. silParams.Style := ssOmni;
  679. end;
  680. silParams.CappingRequired := True;
  681. if Assigned(pWorldAABB) or (svoScissorClips in Options) then
  682. begin
  683. if lightCaster.SetupScissorRect(pWorldAABB, ARci) then
  684. Enable(stScissorTest)
  685. else
  686. Disable(stScissorTest);
  687. end;
  688. // clear the stencil and prepare for shadow volume pass
  689. glClear(GL_STENCIL_BUFFER_BIT);
  690. SetStencilFunc(cfAlways, 0, 255);
  691. DepthFunc := cfLess;
  692. if svoShowVolumes in Options then
  693. begin
  694. glColor3f(0.05 * i, 0.1, 0);
  695. Enable(stBlend);
  696. end
  697. else
  698. begin
  699. SetColorWriting(False);
  700. Disable(stBlend);
  701. end;
  702. Enable(stCullFace);
  703. Disable(stLighting);
  704. glEnableClientState(GL_VERTEX_ARRAY);
  705. SetPolygonOffset(1, 1);
  706. // for all opaque shadow casters
  707. for k := 0 to opaques.Count - 1 do
  708. begin
  709. obj := TgxBaseSceneObject(opaques[k]);
  710. if obj = nil then
  711. Continue;
  712. SetVector(silParams.SeenFrom,
  713. obj.AbsoluteToLocal(lightSource.AbsolutePosition));
  714. sil := lightCaster.GetCachedSilhouette(k);
  715. if (not Assigned(sil)) or (not CompareMem(@sil.Parameters, @silParams,
  716. SizeOf(silParams))) then
  717. begin
  718. sil := obj.GenerateSilhouette(silParams);
  719. sil.Parameters := silParams;
  720. // extrude vertices to infinity
  721. sil.ExtrudeVerticesToInfinity(silParams.SeenFrom);
  722. end;
  723. if Assigned(sil) then
  724. try
  725. // render the silhouette
  726. ARci.PipelineTransformation.SetModelMatrix(obj.AbsoluteMatrix);
  727. glVertexPointer(4, GL_FLOAT, 0, sil.Vertices.List);
  728. if Boolean(Cardinal(opaqueCapping[k])) then
  729. begin
  730. // z-fail
  731. /// if GL_EXT_compiled_vertex_array then
  732. glLockArraysEXT(0, sil.Vertices.Count);
  733. CullFaceMode := cmFront;
  734. SetStencilOp(soKeep, soIncr, soKeep);
  735. with sil do
  736. begin
  737. glDrawElements(GL_QUADS, Indices.Count, GL_UNSIGNED_INT,
  738. Indices.List);
  739. Enable(stPolygonOffsetFill);
  740. glDrawElements(GL_TRIANGLES, CapIndices.Count,
  741. GL_UNSIGNED_INT,
  742. CapIndices.List);
  743. Disable(stPolygonOffsetFill);
  744. end;
  745. CullFaceMode := cmBack;
  746. SetStencilOp(soKeep, soDecr, soKeep);
  747. with sil do
  748. begin
  749. glDrawElements(GL_QUADS, Indices.Count, GL_UNSIGNED_INT,
  750. Indices.List);
  751. Enable(stPolygonOffsetFill);
  752. glDrawElements(GL_TRIANGLES, CapIndices.Count,
  753. GL_UNSIGNED_INT,
  754. CapIndices.List);
  755. Disable(stPolygonOffsetFill);
  756. end;
  757. /// if GL_EXT_compiled_vertex_array then
  758. glUnlockArraysEXT;
  759. end
  760. else
  761. begin
  762. // z-pass
  763. CullFaceMode := cmBack;
  764. SetStencilOp(soKeep, soKeep, soIncr);
  765. glDrawElements(GL_QUADS, sil.Indices.Count, GL_UNSIGNED_INT,
  766. sil.Indices.List);
  767. CullFaceMode := cmFront;
  768. SetStencilOp(soKeep, soKeep, soDecr);
  769. glDrawElements(GL_QUADS, sil.Indices.Count, GL_UNSIGNED_INT,
  770. sil.Indices.List);
  771. end;
  772. finally
  773. if (svoCacheSilhouettes in Options) and (not (osDirectDraw in
  774. ObjectStyle)) then
  775. lightCaster.StoreCachedSilhouette(k, sil)
  776. else
  777. sil.Free;
  778. end;
  779. end;
  780. glDisableClientState(GL_VERTEX_ARRAY);
  781. // re-enable light's diffuse and specular, but no ambient
  782. LightEnabling[LightID] := True;
  783. LightAmbient[LightID] := NullHmgVector;
  784. LightDiffuse[LightID] := lightSource.Diffuse.Color;
  785. LightSpecular[LightID] := lightSource.Specular.Color;
  786. SetColorWriting(True);
  787. SetStencilOp(soKeep, soKeep, soKeep);
  788. Enable(stBlend);
  789. CullFaceMode := cmBack;
  790. if Mode = svmAccurate then
  791. begin
  792. SetStencilFunc(cfEqual, 0, 255);
  793. DepthFunc := cfEqual;
  794. Self.RenderChildren(0, Count - 1, ARci);
  795. end
  796. else
  797. begin
  798. SetStencilFunc(cfNotEqual, 0, 255);
  799. DepthFunc := cfAlways;
  800. SetBlendFunc(bfSrcAlpha, bfOneMinusSrcAlpha);
  801. glPushMatrix;
  802. glLoadIdentity;
  803. glMatrixMode(GL_PROJECTION);
  804. glPushMatrix;
  805. PM := CreateOrthoMatrix(0, 1, 1, 0, -1, 1);
  806. glLoadMatrixf(PGLFloat(@PM));
  807. glColor4fv(@FDarkeningColor.AsAddress^);
  808. glBegin(GL_QUADS);
  809. glVertex2f(0, 0);
  810. glVertex2f(0, 1);
  811. glVertex2f(1, 1);
  812. glVertex2f(1, 0);
  813. glEnd;
  814. glPopMatrix;
  815. glMatrixMode(GL_MODELVIEW);
  816. glPopMatrix;
  817. SetBlendFunc(bfSrcAlpha, bfOne);
  818. end;
  819. // disable light, but restore its ambient component
  820. LightEnabling[lightID] := False;
  821. LightAmbient[lightID] := lightSource.Ambient.Color;
  822. end; // for i
  823. ARci.PipelineTransformation.Pop;
  824. // restore OpenGL state
  825. glLightModelfv(GL_LIGHT_MODEL_AMBIENT, @ARci.sceneAmbientColor);
  826. Scene.SetupLights(ARci.gxStates.MaxLights);
  827. Disable(stStencilTest);
  828. SetPolygonOffset(0, 0);
  829. ARci.ignoreBlendingRequests := False;
  830. ARci.ignoreDepthRequests := False;
  831. end; // of with
  832. finally
  833. FRendering := False;
  834. opaques.Free;
  835. opaqueCapping.Free;
  836. end;
  837. end;
  838. //-------------------------------------------------------------
  839. initialization
  840. //-------------------------------------------------------------
  841. RegisterClasses([TgxShadowVolume]);
  842. end.