123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355 |
- //
- // The graphics engine GLXEngine. The unit of GXScene for Delphi
- //
- unit GXS.ShadowPlane;
- (*
- Implements a basic shadow plane.
- It is strongly recommended to read and understand the explanations in the
- materials/mirror demo before using this component.
- *)
- interface
- {$I Stage.Defines.inc}
- uses
- Winapi.OpenGL,
- Winapi.OpenGLext,
- System.Types,
- System.Classes,
- FMX.Effects,
- Stage.VectorTypes,
- GXS.PersistentClasses,
- Stage.VectorGeometry,
- Stage.Utils,
- GXS.ImageUtils,
- GXS.Scene,
- Stage.PipelineTransform,
- GXS.Context,
- GXS.Objects,
- GXS.Color,
- GXS.RenderContextInfo,
- GXS.State,
- Stage.TextureFormat;
- type
- TShadowPlaneOption = (spoUseStencil, spoScissor, spoTransparent, spoIgnoreZ);
- TShadowPlaneOptions = set of TShadowPlaneOption;
- const
- cDefaultShadowPlaneOptions = [spoUseStencil, spoScissor];
- type
- (* A simple shadow plane.
- This mirror requires a stencil buffer for optimal rendering!
- The object is a mix between a plane and a proxy object, in that the plane
- defines where the shadows are cast, while the proxy part is used to reference
- the objects that should be shadowing (it is legal to self-shadow, but no
- self-shadow visuals will be rendered).
- If stenciling isn't used, the shadow will 'paint' the ShadowColor instead
- of blending it transparently.
- You can have lower quality shadow geometry: add a dummycube, set it to
- invisible (so it won't be rendered in the "regular" pass), and under
- it place another visible dummycube under which you have all your
- low quality objects, use it as shadowing object. Apply the same movements
- to the low-quality objects that you apply to the visible, high-quality ones *)
- TgxShadowPlane = class(TgxPlane)
- private
- FRendering: Boolean;
- FShadowingObject: TgxBaseSceneObject;
- FShadowedLight: TgxLightSource;
- FShadowColor: TgxColor;
- FShadowOptions: TShadowPlaneOptions;
- FOnBeginRenderingShadows, FOnEndRenderingShadows: TNotifyEvent;
- protected
- procedure Notification(AComponent: TComponent; Operation: TOperation); override;
- procedure SetShadowingObject(const val: TgxBaseSceneObject);
- procedure SetShadowedLight(const val: TgxLightSource);
- procedure SetShadowColor(const val: TgxColor);
- procedure SetShadowOptions(const val: TShadowPlaneOptions);
- public
- constructor Create(AOwner: TComponent); override;
- destructor Destroy; override;
- procedure DoRender(var ARci: TgxRenderContextInfo;
- ARenderSelf, ARenderChildren: Boolean); override;
- procedure Assign(Source: TPersistent); override;
- published
- // Selects the object to mirror. If nil, the whole scene is mirrored.
- property ShadowingObject: TgxBaseSceneObject read FShadowingObject write SetShadowingObject;
- (* The light which casts shadows.
- The light must be enabled otherwise shadows won't be cast. *)
- property ShadowedLight: TgxLightSource read FShadowedLight write SetShadowedLight;
- (* The shadow's color.
- This color is transparently blended to make shadowed area darker. *)
- property ShadowColor: TgxColor read FShadowColor write SetShadowColor;
- (* Controls rendering options.
- spoUseStencil: plane area is stenciled, prevents shadowing
- objects to be visible on the sides of the mirror (stencil buffer
- must be active in the viewer too). It also allows shadows to
- be partial (blended).
- spoScissor: plane area is 'scissored', this should improve
- rendering speed by limiting rendering operations and fill rate,
- may have adverse effects on old hardware in rare cases
- spoTransparent: does not render the plane's material, may help
- improve performance if you're fillrate limited, are using the
- stencil, and your hardware has optimized stencil-only writes *)
- property ShadowOptions: TShadowPlaneOptions read FShadowOptions write SetShadowOptions default cDefaultShadowPlaneOptions;
- // Fired before the shadows are rendered.
- property OnBeginRenderingShadows: TNotifyEvent read FOnBeginRenderingShadows write FOnBeginRenderingShadows;
- // Fired after the shadows are rendered.
- property OnEndRenderingShadows: TNotifyEvent read FOnEndRenderingShadows write FOnEndRenderingShadows;
- end;
- //-------------------------------------------------------------
- implementation
- //-------------------------------------------------------------
- // ------------------
- // ------------------ TgxShadowPlane ------------------
- // ------------------
- constructor TgxShadowPlane.Create(AOwner: Tcomponent);
- const
- cDefaultShadowColor: TgxColorVector = (X:0; Y:0; Z:0; W:0.5);
- begin
- inherited Create(AOwner);
- FShadowOptions := cDefaultShadowPlaneOptions;
- ObjectStyle := ObjectStyle + [osDirectDraw];
- FShadowColor := TgxColor.CreateInitialized(Self, cDefaultShadowColor);
- end;
- destructor TgxShadowPlane.Destroy;
- begin
- inherited;
- FShadowColor.Free;
- end;
- procedure TgxShadowPlane.DoRender(var ARci: TgxRenderContextInfo;
- ARenderSelf, ARenderChildren: Boolean);
- var
- oldProxySubObject, oldIgnoreMaterials: Boolean;
- shadowMat: TMatrix4f;
- sr, ds: TRect;
- CurrentBuffer: TgxSceneBuffer;
- ModelMat: TMatrix4f;
- begin
- if FRendering then
- Exit;
- FRendering := True;
- try
- with ARci.gxStates do
- begin
- oldProxySubObject := ARci.proxySubObject;
- ARci.proxySubObject := True;
- CurrentBuffer := TgxSceneBuffer(ARci.buffer);
- if ARenderSelf and (VectorDotProduct(VectorSubtract(ARci.cameraPosition, AbsolutePosition), AbsoluteDirection) > 0) then
- begin
- if (spoScissor in ShadowOptions)
- and (PointDistance(ARci.cameraPosition) > BoundingSphereRadius) then
- begin
- sr := ScreenRect(CurrentBuffer);
- InflateGLRect(sr, 1, 1);
- ds := GetGLRect(0, 0, ARci.viewPortSize.cx, ARci.viewPortSize.cy);
- IntersectGLRect(sr, ds);
- glScissor(sr.Left, sr.Top, sr.Right - sr.Left, sr.Bottom - sr.Top);
- Enable(stScissorTest);
- end;
- if (spoUseStencil in ShadowOptions) then
- begin
- StencilClearValue := 0;
- glClear(GL_STENCIL_BUFFER_BIT);
- Enable(stStencilTest);
- SetStencilFunc(cfAlways, 1, 1);
- SetStencilOp(soReplace, soReplace, soReplace);
- end;
- // "Render" plane and stencil mask
- if (spoTransparent in ShadowOptions) then
- begin
- SetColorWriting(False);
- DepthWriteMask := False;
- BuildList(ARci);
- SetColorWriting(True);
- end
- else
- begin
- Material.Apply(ARci);
- repeat
- BuildList(ARci);
- until not Material.UnApply(ARci);
- end;
- // Setup depth options
- if spoIgnoreZ in ShadowOptions then
- Disable(stDepthTest)
- else
- Enable(stDepthTest);
- DepthFunc := cfLEqual;
- if Assigned(FShadowedLight) then
- begin
- ARci.PipelineTransformation.Push;
- case ShadowedLight.LightStyle of
- lsParallel:
- begin
- shadowMat := MakeShadowMatrix(AbsolutePosition, AbsoluteDirection,
- VectorScale(ShadowedLight.SpotDirection.AsVector, 1e10));
- end;
- else
- shadowMat := MakeShadowMatrix(AbsolutePosition, AbsoluteDirection,
- ShadowedLight.AbsolutePosition);
- end;
- ARci.PipelineTransformation.SetViewMatrix(MatrixMultiply(
- shadowMat,
- ARci.PipelineTransformation.ViewMatrix^));
- ARci.PipelineTransformation.SetModelMatrix(IdentityHmgMatrix);
- Disable(stCullFace);
- Enable(stNormalize);
- SetPolygonOffset(-1, -1);
- Enable(stPolygonOffsetFill);
- oldIgnoreMaterials := ARci.ignoreMaterials;
- ARci.ignoreMaterials := True;
- ActiveTextureEnabled[ttTexture2D] := False;
- Disable(stLighting);
- Disable(stFog);
- glColor4fv(@ShadowColor.AsAddress^);
- if (spoUseStencil in ShadowOptions) then
- begin
- Enable(stBlend);
- Disable(stAlphaTest);
- SetBlendFunc(bfSrcAlpha, bfOneMinusSrcAlpha);
- SetStencilFunc(cfEqual, 1, 1);
- SetStencilOp(soKeep, soKeep, soZero);
- end
- else
- Disable(stBlend);
- if Assigned(FOnBeginRenderingShadows) then
- FOnBeginRenderingShadows(Self);
- if Assigned(FShadowingObject) then
- begin
- ModelMat := IdentityHmgMatrix;
- if FShadowingObject.Parent <> nil then
- MatrixMultiply(ModelMat, FShadowingObject.Parent.AbsoluteMatrix, ModelMat);
- MatrixMultiply(ModelMat, FShadowingObject.LocalMatrix^, ModelMat);
- ARci.PipelineTransformation.SetModelMatrix(ModelMat);
- FShadowingObject.DoRender(ARci, True, (FShadowingObject.Count > 0));
- end
- else
- begin
- Scene.Objects.DoRender(ARci, True, True);
- end;
- if Assigned(FOnEndRenderingShadows) then
- FOnEndRenderingShadows(Self);
- ARci.ignoreMaterials := oldIgnoreMaterials;
- // Restore to "normal"
- ARci.PipelineTransformation.Pop;
- end;
- Disable(stStencilTest);
- Disable(stScissorTest);
- Disable(stPolygonOffsetFill);
- end;
- ARci.proxySubObject := oldProxySubObject;
- if ARenderChildren and (Count > 0) then
- Self.RenderChildren(0, Count - 1, ARci);
- end;
- finally
- FRendering := False;
- end;
- end;
- procedure TgxShadowPlane.Notification(AComponent: TComponent; Operation: TOperation);
- begin
- if Operation = opRemove then
- begin
- if AComponent = FShadowingObject then
- ShadowingObject := nil
- else if AComponent = FShadowedLight then
- ShadowedLight := nil;
- end;
- inherited;
- end;
- procedure TgxShadowPlane.SetShadowingObject(const val: TgxBaseSceneObject);
- begin
- if FShadowingObject <> val then
- begin
- if Assigned(FShadowingObject) then
- FShadowingObject.RemoveFreeNotification(Self);
- FShadowingObject := val;
- if Assigned(FShadowingObject) then
- FShadowingObject.FreeNotification(Self);
- NotifyChange(Self);
- end;
- end;
- procedure TgxShadowPlane.SetShadowedLight(const val: TgxLightSource);
- begin
- if FShadowedLight <> val then
- begin
- if Assigned(FShadowedLight) then
- FShadowedLight.RemoveFreeNotification(Self);
- FShadowedLight := val;
- if Assigned(FShadowedLight) then
- FShadowedLight.FreeNotification(Self);
- NotifyChange(Self);
- end;
- end;
- procedure TgxShadowPlane.SetShadowColor(const val: TgxColor);
- begin
- FShadowColor.Assign(val);
- end;
- procedure TgxShadowPlane.Assign(Source: TPersistent);
- begin
- if Assigned(Source) and (Source is TgxShadowPlane) then
- begin
- FShadowOptions := TgxShadowPlane(Source).FShadowOptions;
- ShadowingObject := TgxShadowPlane(Source).ShadowingObject;
- ShadowedLight := TgxShadowPlane(Source).ShadowedLight;
- ShadowColor := TgxShadowPlane(Source).ShadowColor;
- end;
- inherited Assign(Source);
- end;
- procedure TgxShadowPlane.SetShadowOptions(const val: TShadowPlaneOptions);
- begin
- if FShadowOptions <> val then
- begin
- FShadowOptions := val;
- NotifyChange(Self);
- end;
- end;
- //-------------------------------------------------------------
- initialization
- //-------------------------------------------------------------
- RegisterClasses([TgxShadowPlane]);
- end.
|