123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956 |
- //
- // The graphics engine GLXEngine. The unit of GXScene for Delphi
- //
- unit GXS.ShadowVolume;
- (*
- Implements basic shadow volumes support.
- Be aware that only objects that support silhouette determination have a chance
- to cast correct shadows. Transparent/blended/shader objects among the receivers
- or the casters will be rendered incorrectly.
- *)
- interface
- {$I Stage.Defines.inc}
- uses
- Winapi.OpenGL,
- Winapi.OpenGLext,
- System.SysUtils,
- System.Classes,
- Stage.VectorTypes,
- Stage.VectorGeometry,
- Stage.PipelineTransform,
- GXS.VectorLists,
- GXS.PersistentClasses,
- GXS.GeometryBB,
- GXS.Scene,
- GXS.Context,
- GXS.Silhouette,
- GXS.State,
- GXS.Color,
- GXS.RenderContextInfo;
- type
- TgxShadowVolume = class;
- (* Determines when a shadow volume should generate a cap at the beginning and
- end of the volume. This is ONLY necessary when there's a chance that the
- camera could end up inside the shadow _or_ between the light source and
- the camera. If those two situations can't occur then not using capping is
- the best option.
- Note that if you use the capping, you must either set the depth of view of
- your camera to something very large (f.i. 1e9), or you could use the infinite
- mode (csInfinitePerspective) of your camera.
- svcDefault : Default behaviour
- svcAlways : Always generates caps
- svcNever : Never generates caps *)
- TgxShadowVolumeCapping = (svcDefault, svcAlways, svcNever);
- (* Determines when a caster should actually produce a shadow;
- scmAlways : Caster always produces a shadow, ignoring visibility
- scmVisible : Caster casts shadow if the object has visible=true
- scmRecursivelyVisible : Caster casts shadow if ancestors up the hierarchy
- all have visible=true
- scmParentVisible : Caster produces shadow if parent has visible=true
- scmParentRecursivelyVisible : Caster casts shadow if ancestors up the hierarchy
- all have visible=true, starting from the parent (ignoring own visible setting) *)
- TgxShadowCastingMode = (scmAlways, scmVisible, scmRecursivelyVisible,
- scmParentVisible, scmParentRecursivelyVisible);
- (* Specifies an individual shadow caster.
- Can be a light or an opaque object. *)
- TgxShadowVolumeCaster = class(TCollectionItem)
- private
- FCaster: TgxBaseSceneObject;
- FEffectiveRadius: Single;
- FCapping: TgxShadowVolumeCapping;
- FCastingMode: TgxShadowCastingMode;
- protected
- procedure SetCaster(const val: TgxBaseSceneObject);
- function GetGLShadowVolume: TgxShadowVolume;
- procedure RemoveNotification(aComponent: TComponent);
- function GetDisplayName: string; override;
- public
- constructor Create(ACollection: TCollection); override;
- destructor Destroy; override;
- procedure Assign(Source: TPersistent); override;
- { Shadow casting object. Can be an opaque object or a lightsource. }
- property Caster: TgxBaseSceneObject read FCaster write SetCaster;
- property GLShadowVolume: TgxShadowVolume read GetGLShadowVolume;
- published
- (* Radius beyond which the caster can be ignored.
- Zero (default value) means the caster can never be ignored. *)
- property EffectiveRadius: Single read FEffectiveRadius write FEffectiveRadius;
- (* Specifies if the shadow volume should be capped.
- Capping helps solve shadowing artefacts, at the cost of performance. *)
- property Capping: TgxShadowVolumeCapping read FCapping write FCapping default svcDefault;
- (* Determines when an object should cast a shadow or not. Typically, objects
- should only cast shadows when recursively visible. But if you're using
- dummy shadow casters which are less complex than their parent objects,
- you should use scmParentRecursivelyVisible.*)
- property CastingMode: TgxShadowCastingMode read FCastingMode write
- FCastingMode default scmRecursivelyVisible;
- end;
- // Specifies an individual shadow casting occluder.
- TgxShadowVolumeOccluder = class(TgxShadowVolumeCaster)
- published
- property Caster;
- end;
- // Specifies an individual shadow casting light.
- TgxShadowVolumeLight = class(TgxShadowVolumeCaster)
- private
- FSilhouettes: TgxPersistentObjectList;
- protected
- function GetLightSource: TgxLightSource;
- procedure SetLightSource(const ls: TgxLightSource);
- function GetCachedSilhouette(AIndex: Integer): TgxSilhouette; inline;
- procedure StoreCachedSilhouette(AIndex: Integer; ASil: TgxSilhouette);
- (* Compute and setup scissor clipping rect for the light.
- Returns true if a scissor rect was setup *)
- function SetupScissorRect(worldAABB: PAABB; var rci: TgxRenderContextInfo): Boolean;
- public
- constructor Create(ACollection: TCollection); override;
- destructor Destroy; override;
- procedure FlushSilhouetteCache;
- published
- // Shadow casting lightsource.
- property LightSource: TgxLightSource read GetLightSource write SetLightSource;
- end;
- // Collection of TgxShadowVolumeCaster.
- TgxShadowVolumeCasters = class(TOwnedCollection)
- protected
- function GetItems(index: Integer): TgxShadowVolumeCaster;
- procedure RemoveNotification(aComponent: TComponent);
- public
- function AddCaster(obj: TgxBaseSceneObject; effectiveRadius: Single = 0;
- CastingMode: TgxShadowCastingMode = scmRecursivelyVisible):
- TgxShadowVolumeCaster;
- procedure RemoveCaster(obj: TgxBaseSceneObject);
- function IndexOfCaster(obj: TgxBaseSceneObject): Integer;
- property Items[index: Integer]: TgxShadowVolumeCaster read GetItems; default;
- end;
- (* Shadow volume rendering options/optimizations.
- svoShowVolumes : make the shadow volumes visible
- svoDesignVisible : the shadow are visible at design-time
- svoCacheSilhouettes : cache shadow volume silhouettes, beneficial when
- some objects are static relatively to their light(s)
- svoScissorClips : use scissor clipping per light, beneficial when
- lights are attenuated and don't illuminate the whole scene
- svoWorldScissorClip : use scissor clipping for the world, beneficial
- when shadow receivers don't cover the whole viewer surface *)
- TgxShadowVolumeOption = (svoShowVolumes, svoCacheSilhouettes, svoScissorClips,
- svoWorldScissorClip, svoDesignVisible);
- TgxShadowVolumeOptions = set of TgxShadowVolumeOption;
- (* Shadow rendering modes.
- svmAccurate : will render the scene with ambient lighting only, then
- for each light will make a diffuse+specular pass
- svmDarkening : renders the scene with lighting on as usual, then darkens
- shadowed areas (i.e. inaccurate lighting, but will "shadow" objects
- that don't honour to diffuse or specular lighting)
- svmOff : no shadowing will take place *)
- TgxShadowVolumeMode = (svmAccurate, svmDarkening, svmOff);
- (* Simple shadow volumes.
- Shadow receiving objects are the ShadowVolume's children, shadow casters
- (opaque objects or lights) must be explicitly specified in the Casters
- collection.
- Shadow volumes require that the buffer allows stencil buffers,
- GLXceneViewer.Buffer.ContextOptions contain roStencinBuffer. Without stencil
- buffers, shadow volumes will not work properly.
- Another issue to look out for is the fact that shadow volume capping requires
- that the camera depth of view is either very high (fi 1e9) or that the
- camera style is csInfinitePerspective. *)
- TgxShadowVolume = class(TgxImmaterialSceneObject)
- private
- FActive: Boolean;
- FRendering: Boolean;
- FLights: TgxShadowVolumeCasters;
- FOccluders: TgxShadowVolumeCasters;
- FCapping: TgxShadowVolumeCapping;
- FOptions: TgxShadowVolumeOptions;
- FMode: TgxShadowVolumeMode;
- FDarkeningColor: TgxColor;
- protected
- procedure Notification(AComponent: TComponent; Operation: TOperation); override;
- procedure SetActive(const val: Boolean);
- procedure SetLights(const val: TgxShadowVolumeCasters);
- procedure SetOccluders(const val: TgxShadowVolumeCasters);
- procedure SetOptions(const val: TgxShadowVolumeOptions);
- procedure SetMode(const val: TgxShadowVolumeMode);
- procedure SetDarkeningColor(const val: TgxColor);
- public
- constructor Create(AOwner: TComponent); override;
- destructor Destroy; override;
- procedure DoRender(var ARci: TgxRenderContextInfo;
- ARenderSelf, ARenderChildren: Boolean); override;
- procedure Assign(Source: TPersistent); override;
- procedure FlushSilhouetteCache;
- published
- (* Determines if shadow volume rendering is active.
- When set to false, children will be rendered without any shadowing
- or multipass lighting. *)
- property Active: Boolean read FActive write SetActive default True;
- // Lights that cast shadow volumes.
- property Lights: TgxShadowVolumeCasters read FLights write SetLights;
- // Occluders that cast shadow volumes.
- property Occluders: TgxShadowVolumeCasters read FOccluders write SetOccluders;
- (* Specifies if the shadow volume should be capped.
- Capping helps solve shadowing artefacts, at the cost of performance. *)
- property Capping: TgxShadowVolumeCapping read FCapping write FCapping default
- svcAlways;
- // Shadow volume rendering options.
- property Options: TgxShadowVolumeOptions read FOptions write SetOptions
- default [svoCacheSilhouettes, svoScissorClips];
- // Shadow rendering mode.
- property Mode: TgxShadowVolumeMode read FMode write SetMode default svmAccurate;
- // Darkening color used in svmDarkening mode.
- property DarkeningColor: TgxColor read FDarkeningColor write SetDarkeningColor;
- end;
- //-------------------------------------------------------------
- implementation
- //-------------------------------------------------------------
- // ------------------
- // ------------------ TgxShadowVolumeCaster ------------------
- // ------------------
- constructor TgxShadowVolumeCaster.Create(ACollection: TCollection);
- begin
- inherited Create(ACollection);
- FCapping := svcDefault;
- FCastingMode := scmRecursivelyVisible;
- end;
- type
- // Required for Delphi 5 support.
- THackOwnedCollection = class(TOwnedCollection);
- function TgxShadowVolumeCaster.GetGLShadowVolume: TgxShadowVolume;
- begin
- Result := TgxShadowVolume(THackOwnedCollection(Collection).GetOwner);
- end;
- destructor TgxShadowVolumeCaster.Destroy;
- begin
- if Assigned(FCaster) then
- FCaster.RemoveFreeNotification(GLShadowVolume);
- inherited;
- end;
- procedure TgxShadowVolumeCaster.Assign(Source: TPersistent);
- begin
- if Source is TgxShadowVolumeCaster then
- begin
- FCaster := TgxShadowVolumeCaster(Source).FCaster;
- FEffectiveRadius := TgxShadowVolumeCaster(Source).FEffectiveRadius;
- FCapping := TgxShadowVolumeCaster(Source).FCapping;
- GetGLShadowVolume.StructureChanged;
- end;
- inherited;
- end;
- procedure TgxShadowVolumeCaster.SetCaster(const val: TgxBaseSceneObject);
- begin
- if FCaster <> val then
- begin
- if FCaster <> nil then
- FCaster.RemoveFreeNotification(GLShadowVolume);
- FCaster := val;
- if FCaster <> nil then
- FCaster.FreeNotification(GLShadowVolume);
- GetGLShadowVolume.StructureChanged;
- end;
- end;
- procedure TgxShadowVolumeCaster.RemoveNotification(aComponent: TComponent);
- begin
- if aComponent = FCaster then
- begin
- // No point in keeping the TgxShadowVolumeCaster once the FCaster has been
- // destroyed.
- FCaster := nil;
- Free;
- end;
- end;
- function TgxShadowVolumeCaster.GetDisplayName: string;
- begin
- if Assigned(FCaster) then
- begin
- if FCaster is TgxLightSource then
- Result := '[Light]'
- else
- Result := '[Object]';
- Result := Result + ' ' + FCaster.Name;
- if EffectiveRadius > 0 then
- Result := Result + Format(' (%.1f)', [EffectiveRadius]);
- end
- else
- Result := 'nil';
- end;
- // ------------------
- // ------------------ TgxShadowVolumeLight ------------------
- // ------------------
- constructor TgxShadowVolumeLight.Create(ACollection: TCollection);
- begin
- inherited Create(ACollection);
- FSilhouettes := TgxPersistentObjectList.Create;
- end;
- destructor TgxShadowVolumeLight.Destroy;
- begin
- FlushSilhouetteCache;
- FSilhouettes.Free;
- inherited;
- end;
- procedure TgxShadowVolumeLight.FlushSilhouetteCache;
- begin
- FSilhouettes.Clean;
- end;
- function TgxShadowVolumeLight.GetLightSource: TgxLightSource;
- begin
- Result := TgxLightSource(Caster);
- end;
- procedure TgxShadowVolumeLight.SetLightSource(const ls: TgxLightSource);
- begin
- SetCaster(ls);
- end;
- function TgxShadowVolumeLight.GetCachedSilhouette(AIndex: Integer):
- TgxSilhouette;
- begin
- if AIndex < FSilhouettes.Count then
- Result := TgxSilhouette(FSilhouettes[AIndex])
- else
- Result := nil;
- end;
- procedure TgxShadowVolumeLight.StoreCachedSilhouette(AIndex: Integer; ASil:
- TgxSilhouette);
- begin
- while AIndex >= FSilhouettes.Count do
- FSilhouettes.Add(nil);
- if ASil <> FSilhouettes[AIndex] then
- begin
- if assigned(FSilhouettes[AIndex]) then
- FSilhouettes[AIndex].Free;
- FSilhouettes[AIndex] := ASil;
- end;
- end;
- function TgxShadowVolumeLight.SetupScissorRect(worldAABB: PAABB; var rci:
- TgxRenderContextInfo): Boolean;
- var
- mvp: TMatrix4f;
- ls: TgxLightSource;
- aabb: TAABB;
- clipRect: TGClipRect;
- begin
- ls := LightSource;
- if (EffectiveRadius <= 0) or (not ls.Attenuated) then
- begin
- // non attenuated lights can't be clipped
- if not Assigned(worldAABB) then
- begin
- Result := False;
- Exit;
- end
- else
- aabb := worldAABB^;
- end
- else
- begin
- aabb := BSphereToAABB(ls.AbsolutePosition, EffectiveRadius);
- if Assigned(worldAABB) then
- aabb := AABBIntersection(aabb, worldAABB^);
- end;
- if PointInAABB(rci.cameraPosition, aabb) then
- begin
- // camera inside light volume radius, can't clip
- Result := False;
- Exit;
- end;
- // Calculate the window-space bounds of the light's bounding box.
- mvp := rci.PipelineTransformation.ViewProjectionMatrix^;
- clipRect := AABBToClipRect(aabb, mvp, rci.viewPortSize.cx,
- rci.viewPortSize.cy);
- if (clipRect.Right < 0) or (clipRect.Left > rci.viewPortSize.cx)
- or (clipRect.Top < 0) or (clipRect.Bottom > rci.viewPortSize.cy) then
- begin
- Result := False;
- Exit;
- end;
- with clipRect do
- glScissor(Round(Left), Round(Top), Round(Right - Left), Round(Bottom -
- Top));
- Result := True;
- end;
- // ------------------
- // ------------------ TgxShadowVolumeCasters ------------------
- // ------------------
- procedure TgxShadowVolumeCasters.RemoveNotification(aComponent: TComponent);
- var
- i: Integer;
- begin
- for i := Count - 1 downto 0 do
- Items[i].RemoveNotification(aComponent);
- end;
- function TgxShadowVolumeCasters.GetItems(index: Integer): TgxShadowVolumeCaster;
- begin
- Result := TgxShadowVolumeCaster(inherited Items[index]);
- end;
- function TgxShadowVolumeCasters.AddCaster(obj: TgxBaseSceneObject;
- effectiveRadius: Single = 0;
- CastingMode: TgxShadowCastingMode = scmRecursivelyVisible):
- TgxShadowVolumeCaster;
- var
- newCaster: TgxShadowVolumeCaster;
- begin
- newCaster := TgxShadowVolumeCaster(Add);
- newCaster.Caster := obj;
- newCaster.EffectiveRadius := effectiveRadius;
- newCaster.CastingMode := CastingMode;
- result := newCaster;
- end;
- procedure TgxShadowVolumeCasters.RemoveCaster(obj: TgxBaseSceneObject);
- var
- i: Integer;
- begin
- i := IndexOfCaster(obj);
- if i >= 0 then
- Delete(i);
- end;
- function TgxShadowVolumeCasters.IndexOfCaster(obj: TgxBaseSceneObject): Integer;
- var
- i: Integer;
- begin
- for i := 0 to Count - 1 do
- begin
- if Items[i].Caster = obj then
- begin
- Result := i;
- Exit;
- end;
- end;
- Result := -1;
- end;
- // ------------------
- // ------------------ TgxShadowVolume ------------------
- // ------------------
- constructor TgxShadowVolume.Create(AOwner: Tcomponent);
- begin
- inherited Create(AOwner);
- ObjectStyle := ObjectStyle - [osDirectDraw] + [osNoVisibilityCulling];
- FActive := True;
- FLights := TgxShadowVolumeCasters.Create(self, TgxShadowVolumeLight);
- FOccluders := TgxShadowVolumeCasters.Create(self, TgxShadowVolumeOccluder);
- FCapping := svcAlways;
- FMode := svmAccurate;
- FOptions := [svoCacheSilhouettes, svoScissorClips];
- FDarkeningColor := TgxColor.CreateInitialized(Self, VectorMake(0, 0, 0, 0.5));
- end;
- destructor TgxShadowVolume.Destroy;
- begin
- inherited;
- FDarkeningColor.Free;
- FLights.Free;
- FOccluders.Free;
- end;
- procedure TgxShadowVolume.Notification(AComponent: TComponent; Operation:
- TOperation);
- begin
- if Operation = opRemove then
- begin
- FLights.RemoveNotification(AComponent);
- FOccluders.RemoveNotification(AComponent);
- end;
- inherited;
- end;
- procedure TgxShadowVolume.Assign(Source: TPersistent);
- begin
- if Assigned(Source) and (Source is TgxShadowVolume) then
- begin
- FLights.Assign(TgxShadowVolume(Source).Lights);
- FOccluders.Assign(TgxShadowVolume(Source).Occluders);
- FCapping := TgxShadowVolume(Source).FCapping;
- StructureChanged;
- end;
- inherited Assign(Source);
- end;
- procedure TgxShadowVolume.FlushSilhouetteCache;
- var
- i: Integer;
- begin
- for i := 0 to Lights.Count - 1 do
- (Lights[i] as TgxShadowVolumeLight).FlushSilhouetteCache;
- end;
- procedure TgxShadowVolume.SetActive(const val: Boolean);
- begin
- if FActive <> val then
- begin
- FActive := val;
- StructureChanged;
- end;
- end;
- procedure TgxShadowVolume.SetLights(const val: TgxShadowVolumeCasters);
- begin
- Assert(val.ItemClass = TgxShadowVolumeLight);
- FLights.Assign(val);
- StructureChanged;
- end;
- procedure TgxShadowVolume.SetOccluders(const val: TgxShadowVolumeCasters);
- begin
- Assert(val.ItemClass = TgxShadowVolumeOccluder);
- FOccluders.Assign(val);
- StructureChanged;
- end;
- procedure TgxShadowVolume.SetOptions(const val: TgxShadowVolumeOptions);
- begin
- if FOptions <> val then
- begin
- FOptions := val;
- if not (svoCacheSilhouettes in FOptions) then
- FlushSilhouetteCache;
- StructureChanged;
- end;
- end;
- procedure TgxShadowVolume.SetMode(const val: TgxShadowVolumeMode);
- begin
- if FMode <> val then
- begin
- FMode := val;
- StructureChanged;
- end;
- end;
- procedure TgxShadowVolume.SetDarkeningColor(const val: TgxColor);
- begin
- FDarkeningColor.Assign(val);
- end;
- procedure TgxShadowVolume.DoRender(var ARci: TgxRenderContextInfo;
- ARenderSelf, ARenderChildren: Boolean);
- // Function that determines if an object is "recursively visible". It halts when
- // * it finds an invisible ancestor (=> invisible)
- // * it finds the root (=> visible)
- // * it finds the shadow volume as an ancestor (=> visible)
- //
- // This does _not_ mean that the object is actually visible on the screen
- function DirectHierarchicalVisibility(obj: TgxBaseSceneObject): boolean;
- var
- p: TgxBaseSceneObject;
- begin
- if not Assigned(obj) then
- begin
- Result := True;
- exit;
- end;
- if not obj.Visible then
- begin
- Result := False;
- Exit;
- end;
- p := obj.Parent;
- while Assigned(p) and (p <> obj) and (p <> Self) do
- begin
- if not p.Visible then
- begin
- Result := False;
- Exit;
- end;
- p := p.Parent;
- end;
- Result := True;
- end;
- var
- i, k: Integer;
- lightSource: TgxLightSource;
- lightCaster: TgxShadowVolumeLight;
- sil: TgxSilhouette;
- lightID: Cardinal;
- obj: TgxBaseSceneObject;
- caster: TgxShadowVolumeCaster;
- opaques, opaqueCapping: TList;
- silParams: TgxSilhouetteParameters;
- worldAABB: TAABB;
- pWorldAABB: PAABB;
- PM: TMatrix4f;
- begin
- if not Active then
- begin
- inherited;
- Exit;
- end;
- if FRendering then
- Exit;
- if not (ARenderSelf or ARenderChildren) then
- Exit;
- ClearStructureChanged;
- if ((csDesigning in ComponentState) and not (svoDesignVisible in Options))
- or (Mode = svmOff)
- or (ARci.drawState = dsPicking) then
- begin
- inherited;
- Exit;
- end;
- if svoWorldScissorClip in Options then
- begin
- // compute shadow receiving world AABB in absolute coordinates
- worldAABB := Self.AxisAlignedBoundingBox;
- AABBTransform(worldAABB, AbsoluteMatrix);
- pWorldAABB := @worldAABB;
- end
- else
- pWorldAABB := nil;
- opaques := TList.Create;
- opaqueCapping := TList.Create;
- FRendering := True;
- try
- // collect visible casters
- for i := 0 to Occluders.Count - 1 do
- begin
- caster := Occluders[i];
- obj := caster.Caster;
- if Assigned(obj)
- and
- // Determine when to render this object or not
- (
- (Caster.CastingMode = scmAlways) or
- ((Caster.CastingMode = scmVisible) and obj.Visible) or
- ((Caster.CastingMode = scmRecursivelyVisible) and
- DirectHierarchicalVisibility(obj)) or
- ((Caster.CastingMode = scmParentRecursivelyVisible) and
- DirectHierarchicalVisibility(obj.Parent)) or
- ((Caster.CastingMode = scmParentVisible) and (not Assigned(obj.Parent)
- or
- obj.Parent.Visible))
- )
- and ((caster.EffectiveRadius <= 0)
- or (obj.DistanceTo(ARci.cameraPosition) < caster.EffectiveRadius)) then
- begin
- opaques.Add(obj);
- opaqueCapping.Add(Pointer(Cardinal(ord((caster.Capping = svcAlways)
- or ((caster.Capping = svcDefault)
- and (Capping = svcAlways))))));
- end
- else
- begin
- opaques.Add(nil);
- opaqueCapping.Add(nil);
- end;
- end;
- // render the shadow volumes
- with ARci.gxStates do
- begin
- if Mode = svmAccurate then
- begin
- // first turn off all the shadow casting lights diffuse and specular
- for i := 0 to Lights.Count - 1 do
- begin
- lightCaster := TgxShadowVolumeLight(Lights[i]);
- lightSource := lightCaster.LightSource;
- if Assigned(lightSource) and (lightSource.Shining) then
- begin
- lightID := lightSource.LightID;
- LightDiffuse[lightID] := NullHmgVector;
- LightSpecular[lightID] := NullHmgVector;
- end;
- end;
- end;
- // render shadow receivers with ambient lighting
- // DanB - not sure why this doesn't render properly with these statements
- // where they were originally (after the RenderChildren call).
- Self.RenderChildren(0, Count - 1, ARci);
- ARci.ignoreBlendingRequests := True;
- ARci.ignoreDepthRequests := True;
- DepthWriteMask := False;
- Enable(stDepthTest);
- SetBlendFunc(bfSrcAlpha, bfOne);
- Disable(stAlphaTest);
- Enable(stStencilTest);
- // Disable all client states
- /// if GL_ARB_vertex_buffer_object then
- begin
- VertexArrayBinding := 0;
- ArrayBufferBinding := 0;
- ElementBufferBinding := 0;
- end;
- // turn off *all* lights
- for i := 0 to TgxScene(ARci.scene).Lights.Count - 1 do
- begin
- lightSource := (TgxScene(ARci.scene).Lights.Items[i]) as TgxLightSource;
- if Assigned(lightSource) and lightSource.Shining then
- LightEnabling[lightSource.LightID] := False;
- end;
- glLightModelfv(GL_LIGHT_MODEL_AMBIENT, @NullHmgPoint);
- ARci.PipelineTransformation.Push;
- // render contribution of all shadow casting lights
- for i := 0 to Lights.Count - 1 do
- begin
- lightCaster := TgxShadowVolumeLight(lights[i]);
- lightSource := lightCaster.LightSource;
- if (not Assigned(lightSource)) or (not lightSource.Shining) then
- Continue;
- lightID := lightSource.LightID;
- SetVector(silParams.LightDirection,
- lightSource.SpotDirection.DirectVector);
- case lightSource.LightStyle of
- lsParallel: silParams.Style := ssParallel
- else
- silParams.Style := ssOmni;
- end;
- silParams.CappingRequired := True;
- if Assigned(pWorldAABB) or (svoScissorClips in Options) then
- begin
- if lightCaster.SetupScissorRect(pWorldAABB, ARci) then
- Enable(stScissorTest)
- else
- Disable(stScissorTest);
- end;
- // clear the stencil and prepare for shadow volume pass
- glClear(GL_STENCIL_BUFFER_BIT);
- SetStencilFunc(cfAlways, 0, 255);
- DepthFunc := cfLess;
- if svoShowVolumes in Options then
- begin
- glColor3f(0.05 * i, 0.1, 0);
- Enable(stBlend);
- end
- else
- begin
- SetColorWriting(False);
- Disable(stBlend);
- end;
- Enable(stCullFace);
- Disable(stLighting);
- glEnableClientState(GL_VERTEX_ARRAY);
- SetPolygonOffset(1, 1);
- // for all opaque shadow casters
- for k := 0 to opaques.Count - 1 do
- begin
- obj := TgxBaseSceneObject(opaques[k]);
- if obj = nil then
- Continue;
- SetVector(silParams.SeenFrom,
- obj.AbsoluteToLocal(lightSource.AbsolutePosition));
- sil := lightCaster.GetCachedSilhouette(k);
- if (not Assigned(sil)) or (not CompareMem(@sil.Parameters, @silParams,
- SizeOf(silParams))) then
- begin
- sil := obj.GenerateSilhouette(silParams);
- sil.Parameters := silParams;
- // extrude vertices to infinity
- sil.ExtrudeVerticesToInfinity(silParams.SeenFrom);
- end;
- if Assigned(sil) then
- try
- // render the silhouette
- ARci.PipelineTransformation.SetModelMatrix(obj.AbsoluteMatrix);
- glVertexPointer(4, GL_FLOAT, 0, sil.Vertices.List);
- if Boolean(Cardinal(opaqueCapping[k])) then
- begin
- // z-fail
- /// if GL_EXT_compiled_vertex_array then
- glLockArraysEXT(0, sil.Vertices.Count);
- CullFaceMode := cmFront;
- SetStencilOp(soKeep, soIncr, soKeep);
- with sil do
- begin
- glDrawElements(GL_QUADS, Indices.Count, GL_UNSIGNED_INT,
- Indices.List);
- Enable(stPolygonOffsetFill);
- glDrawElements(GL_TRIANGLES, CapIndices.Count,
- GL_UNSIGNED_INT,
- CapIndices.List);
- Disable(stPolygonOffsetFill);
- end;
- CullFaceMode := cmBack;
- SetStencilOp(soKeep, soDecr, soKeep);
- with sil do
- begin
- glDrawElements(GL_QUADS, Indices.Count, GL_UNSIGNED_INT,
- Indices.List);
- Enable(stPolygonOffsetFill);
- glDrawElements(GL_TRIANGLES, CapIndices.Count,
- GL_UNSIGNED_INT,
- CapIndices.List);
- Disable(stPolygonOffsetFill);
- end;
- /// if GL_EXT_compiled_vertex_array then
- glUnlockArraysEXT;
- end
- else
- begin
- // z-pass
- CullFaceMode := cmBack;
- SetStencilOp(soKeep, soKeep, soIncr);
- glDrawElements(GL_QUADS, sil.Indices.Count, GL_UNSIGNED_INT,
- sil.Indices.List);
- CullFaceMode := cmFront;
- SetStencilOp(soKeep, soKeep, soDecr);
- glDrawElements(GL_QUADS, sil.Indices.Count, GL_UNSIGNED_INT,
- sil.Indices.List);
- end;
- finally
- if (svoCacheSilhouettes in Options) and (not (osDirectDraw in
- ObjectStyle)) then
- lightCaster.StoreCachedSilhouette(k, sil)
- else
- sil.Free;
- end;
- end;
- glDisableClientState(GL_VERTEX_ARRAY);
- // re-enable light's diffuse and specular, but no ambient
- LightEnabling[LightID] := True;
- LightAmbient[LightID] := NullHmgVector;
- LightDiffuse[LightID] := lightSource.Diffuse.Color;
- LightSpecular[LightID] := lightSource.Specular.Color;
- SetColorWriting(True);
- SetStencilOp(soKeep, soKeep, soKeep);
- Enable(stBlend);
- CullFaceMode := cmBack;
- if Mode = svmAccurate then
- begin
- SetStencilFunc(cfEqual, 0, 255);
- DepthFunc := cfEqual;
- Self.RenderChildren(0, Count - 1, ARci);
- end
- else
- begin
- SetStencilFunc(cfNotEqual, 0, 255);
- DepthFunc := cfAlways;
- SetBlendFunc(bfSrcAlpha, bfOneMinusSrcAlpha);
- glPushMatrix;
- glLoadIdentity;
- glMatrixMode(GL_PROJECTION);
- glPushMatrix;
- PM := CreateOrthoMatrix(0, 1, 1, 0, -1, 1);
- glLoadMatrixf(PGLFloat(@PM));
- glColor4fv(@FDarkeningColor.AsAddress^);
- glBegin(GL_QUADS);
- glVertex2f(0, 0);
- glVertex2f(0, 1);
- glVertex2f(1, 1);
- glVertex2f(1, 0);
- glEnd;
- glPopMatrix;
- glMatrixMode(GL_MODELVIEW);
- glPopMatrix;
- SetBlendFunc(bfSrcAlpha, bfOne);
- end;
- // disable light, but restore its ambient component
- LightEnabling[lightID] := False;
- LightAmbient[lightID] := lightSource.Ambient.Color;
- end; // for i
- ARci.PipelineTransformation.Pop;
- // restore OpenGL state
- glLightModelfv(GL_LIGHT_MODEL_AMBIENT, @ARci.sceneAmbientColor);
- Scene.SetupLights(ARci.gxStates.MaxLights);
- Disable(stStencilTest);
- SetPolygonOffset(0, 0);
- ARci.ignoreBlendingRequests := False;
- ARci.ignoreDepthRequests := False;
- end; // of with
- finally
- FRendering := False;
- opaques.Free;
- opaqueCapping.Free;
- end;
- end;
- //-------------------------------------------------------------
- initialization
- //-------------------------------------------------------------
- RegisterClasses([TgxShadowVolume]);
- end.
|