123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483 |
- //
- // The graphics engine GLXEngine. The unit of GXScene for Delphi
- //
- unit GXS.Mirror;
- (*
- Implements a basic, stencil-based mirror (as in Mark Kilgard's demo).
- 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,
- System.Classes,
- GXS.XCollection,
- GXS.PersistentClasses,
- GXS.Scene,
- Stage.PipelineTransform,
- Stage.VectorGeometry,
- GXS.Context,
- GXS.Material,
- GXS.Color,
- GXS.RenderContextInfo,
- GXS.State,
- Stage.VectorTypes;
- type
- TMirrorOption = (moUseStencil, moOpaque, moMirrorPlaneClip, moClearZBuffer);
- TMirrorOptions = set of TMirrorOption;
- const
- cDefaultMirrorOptions = [moUseStencil];
- type
- TMirrorShapes = (msRect, msDisk);
- { A simple plane mirror.
- 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 the mirror's surface, while the proxy part is used to reference
- the objects that should be mirrored (it is legal to self-mirror, but no
- self-mirror visuals will be rendered).
- It is strongly recommended to read and understand the explanations in the
- materials/mirror demo before using this component. }
- TgxMirror = class(TgxSceneObject)
- private
- FRendering: Boolean;
- FMirrorObject: TgxBaseSceneObject;
- FWidth, FHeight: Single;
- FMirrorOptions: TMirrorOptions;
- FOnBeginRenderingMirrors, FOnEndRenderingMirrors: TNotifyEvent;
- FShape: TMirrorShapes; //ORL
- FRadius: Single; //ORL
- FSlices: Integer; //ORL
- protected
- procedure Notification(AComponent: TComponent; Operation: TOperation); override;
- procedure SetMirrorObject(const val: TgxBaseSceneObject);
- procedure SetMirrorOptions(const val: TMirrorOptions);
- procedure ClearZBufferArea(aBuffer: TgxSceneBuffer);
- procedure SetHeight(AValue: Single);
- procedure SetWidth(AValue: Single);
- procedure SetRadius(const aValue: Single); //ORL
- procedure SetSlices(const aValue: Integer); //ORL
- procedure SetShape(aValue: TMirrorShapes); //ORL
- function GetRadius: Single; //ORL
- function GetSlices: Integer; //ORL
- public
- constructor Create(AOwner: TComponent); override;
- procedure DoRender(var ARci: TgxRenderContextInfo;
- ARenderSelf, ARenderChildren: Boolean); override;
- procedure BuildList(var ARci: TgxRenderContextInfo); override;
- procedure Assign(Source: TPersistent); override;
- function AxisAlignedDimensionsUnscaled: TVector4f; override;
- published
- { Selects the object to mirror. If nil, the whole scene is mirrored. }
- property MirrorObject: TgxBaseSceneObject read FMirrorObject write
- SetMirrorObject;
- { Controls rendering options.
- moUseStencil: mirror area is stenciled, prevents reflected
- objects to be visible on the sides of the mirror (stencil buffer
- must be active in the viewer)
- moOpaque: mirror is opaque (ie. painted with background color)
- moMirrorPlaneClip: a ClipPlane is defined to prevent reflections
- from popping out of the mirror (for objects behind or halfway through)
- moClearZBuffer: mirror area's ZBuffer is cleared so that background
- objects don't interfere with reflected objects (reflected objects
- must be rendered AFTER the mirror in the hierarchy). Works only
- along with stenciling. }
- property MirrorOptions: TMirrorOptions read FMirrorOptions write
- SetMirrorOptions default cDefaultMirrorOptions;
- property Height: Single read FHeight write SetHeight;
- property Width: Single read FWidth write SetWidth;
- { Fired before the object's mirror images are rendered. }
- property OnBeginRenderingMirrors: TNotifyEvent read FOnBeginRenderingMirrors
- write FOnBeginRenderingMirrors;
- { Fired after the object's mirror images are rendered. }
- property OnEndRenderingMirrors: TNotifyEvent read FOnEndRenderingMirrors
- write FOnEndRenderingMirrors;
- property Radius: Single read FRadius write SetRadius; //ORL
- property Slices: GLint read FSlices write SetSlices default 16; //ORL
- property Shape: TMirrorShapes read FShape write SetShape default msRect;
- //ORL
- end;
- //-------------------------------------------------------------
- implementation
- //-------------------------------------------------------------
- // ------------------
- // ------------------ TgxMirror ------------------
- // ------------------
- constructor TgxMirror.Create(AOwner: Tcomponent);
- begin
- inherited Create(AOwner);
- FWidth := 1;
- FHeight := 1;
- FMirrorOptions := cDefaultMirrorOptions;
- ObjectStyle := ObjectStyle + [osDirectDraw];
- Material.FrontProperties.Diffuse.Initialize(VectorMake(1, 1, 1, 0.1));
- Material.BlendingMode := bmTransparency;
- FRadius := 1; //ORL
- FSlices := 16; //ORL
- Shape := msRect; //ORL
- end;
- procedure TgxMirror.DoRender(var ARci: TgxRenderContextInfo;
- ARenderSelf, ARenderChildren: Boolean);
- var
- oldProxySubObject: Boolean;
- refMat, curMat, ModelMat: TMatrix4f;
- clipPlane: TDoubleHmgPlane;
- bgColor: TgxColorVector;
- cameraPosBackup, cameraDirectionBackup: TVector4f;
- CurrentBuffer: TgxSceneBuffer;
- begin
- if FRendering then
- Exit;
- FRendering := True;
- try
- oldProxySubObject := ARci.proxySubObject;
- ARci.proxySubObject := True;
- CurrentBuffer := TgxSceneBuffer(ARci.buffer);
- if VectorDotProduct(VectorSubtract(ARci.cameraPosition, AbsolutePosition),
- AbsoluteDirection) > 0 then
- with ARci.gxStates do
- begin
- // "Render" stencil mask
- if MirrorOptions <> [] then
- begin
- if (moUseStencil in MirrorOptions) then
- begin
- Enable(stStencilTest);
- ARci.gxStates.StencilClearValue := 0;
- glClear(GL_STENCIL_BUFFER_BIT);
- SetStencilFunc(cfAlways, 1, 1);
- SetStencilOp(soReplace, soZero, soReplace);
- end;
- if (moOpaque in MirrorOptions) then
- begin
- bgColor := ConvertWinColor(CurrentBuffer.BackgroundColor);
- ARci.gxStates.SetMaterialColors(cmFront, bgColor, clrBlack,
- clrBlack, clrBlack, 0);
- end
- else
- SetColorWriting(False);
- Enable(stDepthTest);
- DepthWriteMask := False;
- BuildList(ARci);
- DepthWriteMask := True;
- if (moUseStencil in MirrorOptions) then
- begin
- SetStencilFunc(cfEqual, 1, 1);
- SetStencilOp(soKeep, soKeep, soKeep);
- end;
- if (moClearZBuffer in MirrorOptions) then
- ClearZBufferArea(CurrentBuffer);
- if not (moOpaque in MirrorOptions) then
- SetColorWriting(True);
- end;
- ARci.PipelineTransformation.Push;
- ARci.PipelineTransformation.SetModelMatrix(IdentityHmgMatrix);
- Disable(stCullFace);
- Enable(stNormalize);
- if moMirrorPlaneClip in MirrorOptions then
- begin
- glEnable(GL_CLIP_PLANE0);
- SetPlane(clipPlane, PlaneMake(AffineVectorMake(AbsolutePosition),
- VectorNegate(AffineVectorMake(AbsoluteDirection))));
- glClipPlane(GL_CLIP_PLANE0, @clipPlane);
- end;
- // Mirror lights
- refMat := MakeReflectionMatrix(
- AffineVectorMake(AbsolutePosition),
- AffineVectorMake(AbsoluteDirection));
- curMat := MatrixMultiply(refMat, ARci.PipelineTransformation.ViewMatrix^);
- ARci.PipelineTransformation.SetViewMatrix(curMat);
- Scene.SetupLights(CurrentBuffer.LimitOf[limLights]);
- // mirror geometry and render master
- cameraPosBackup := ARci.cameraPosition;
- cameraDirectionBackup := ARci.cameraDirection;
- ARci.cameraPosition := VectorTransform(ARci.cameraPosition, refMat);
- ARci.cameraDirection := VectorTransform(ARci.cameraDirection, refMat);
- // temporary fix? (some objects don't respect culling options, or ?)
- CullFaceMode := cmFront;
- if Assigned(FOnBeginRenderingMirrors) then
- FOnBeginRenderingMirrors(Self);
- if Assigned(FMirrorObject) then
- begin
- ModelMat := IdentityHmgMatrix;
- if FMirrorObject.Parent <> nil then
- MatrixMultiply(ModelMat, FMirrorObject.Parent.AbsoluteMatrix, ModelMat);
- MatrixMultiply(ModelMat, FMirrorObject.LocalMatrix^, ModelMat);
- ARci.PipelineTransformation.SetModelMatrix(ModelMat);
- FMirrorObject.DoRender(ARci, ARenderSelf, FMirrorObject.Count > 0);
- end
- else
- begin
- Scene.Objects.DoRender(ARci, ARenderSelf, True);
- end;
- if Assigned(FOnEndRenderingMirrors) then
- FOnEndRenderingMirrors(Self);
- // Restore to "normal"
- ARci.cameraPosition := cameraPosBackup;
- ARci.cameraDirection := cameraDirectionBackup;
- ARci.gxStates.CullFaceMode := cmBack;
- ARci.PipelineTransformation.ReplaceFromStack;
- Scene.SetupLights(CurrentBuffer.LimitOf[limLights]);
- ARci.PipelineTransformation.Pop;
- if moMirrorPlaneClip in MirrorOptions then
- glDisable(GL_CLIP_PLANE0);
- ARci.gxStates.Disable(stStencilTest);
- ARci.proxySubObject := oldProxySubObject;
- // start rendering self
- if ARenderSelf then
- begin
- Material.Apply(ARci);
- repeat
- BuildList(ARci);
- until not Material.UnApply(ARci);
- end;
- end;
- if ARenderChildren then
- Self.RenderChildren(0, Count - 1, ARci);
- if Assigned(FMirrorObject) then
- FMirrorObject.Effects.RenderPostEffects(ARci);
- finally
- FRendering := False;
- end;
- end;
- procedure TgxMirror.BuildList(var ARci: TgxRenderContextInfo);
- var
- hw, hh: Single;
- quadric: GLUquadricObj;
- begin
- if msRect = FShape then
- begin
- hw := FWidth * 0.5;
- hh := FHeight * 0.5;
- glNormal3fv(@ZVector);
- glBegin(GL_QUADS);
- glVertex3f(hw, hh, 0);
- glVertex3f(-hw, hh, 0);
- glVertex3f(-hw, -hh, 0);
- glVertex3f(hw, -hh, 0);
- glEnd;
- end
- else
- begin
- quadric := gluNewQuadric;
- gluDisk(Quadric, 0, FRadius, FSlices, 1); //radius. slices, loops
- end;
- end;
- procedure TgxMirror.ClearZBufferArea(aBuffer: TgxSceneBuffer);
- var
- worldMat: TMatrix4f;
- p: TAffineVector;
- begin
- with aBuffer do
- begin
- glPushMatrix;
- worldMat := Self.AbsoluteMatrix;
- glMatrixMode(GL_PROJECTION);
- glPushMatrix;
- glLoadIdentity;
- glOrtho(0, Width, 0, Height, 1, -1);
- glMatrixMode(GL_MODELVIEW);
- glLoadIdentity;
- with aBuffer.RenderingContext.gxStates do
- begin
- DepthFunc := cfAlways;
- SetColorWriting(False);
- end;
- glBegin(GL_QUADS);
- p := WorldToScreen(VectorTransform(AffineVectorMake(Self.Width * 0.5,
- Self.Height * 0.5, 0), worldMat));
- glVertex3f(p.X, p.Y, 0.999);
- p := WorldToScreen(VectorTransform(AffineVectorMake(-Self.Width * 0.5,
- Self.Height * 0.5, 0), worldMat));
- glVertex3f(p.X, p.Y, 0.999);
- p := WorldToScreen(VectorTransform(AffineVectorMake(-Self.Width * 0.5,
- -Self.Height * 0.5, 0), worldMat));
- glVertex3f(p.X, p.Y, 0.999);
- p := WorldToScreen(VectorTransform(AffineVectorMake(Self.Width * 0.5,
- -Self.Height * 0.5, 0), worldMat));
- glVertex3f(p.X, p.Y, 0.999);
- glEnd;
- with aBuffer.RenderingContext.gxStates do
- begin
- DepthFunc := cfLess;
- SetColorWriting(True);
- end;
- glMatrixMode(GL_PROJECTION);
- glPopMatrix;
- glMatrixMode(GL_MODELVIEW);
- glPopMatrix;
- end;
- end;
- procedure TgxMirror.Notification(AComponent: TComponent; Operation: TOperation);
- begin
- if (Operation = opRemove) and (AComponent = FMirrorObject) then
- MirrorObject := nil;
- inherited;
- end;
- procedure TgxMirror.SetMirrorObject(const val: TgxBaseSceneObject);
- begin
- if FMirrorObject <> val then
- begin
- if Assigned(FMirrorObject) then
- FMirrorObject.RemoveFreeNotification(Self);
- FMirrorObject := val;
- if Assigned(FMirrorObject) then
- FMirrorObject.FreeNotification(Self);
- NotifyChange(Self);
- end;
- end;
- procedure TgxMirror.SetWidth(AValue: Single);
- begin
- if AValue <> FWidth then
- begin
- FWidth := AValue;
- NotifyChange(Self);
- end;
- end;
- procedure TgxMirror.SetHeight(AValue: Single);
- begin
- if AValue <> FHeight then
- begin
- FHeight := AValue;
- NotifyChange(Self);
- end;
- end;
- procedure TgxMirror.Assign(Source: TPersistent);
- begin
- if Assigned(Source) and (Source is TgxMirror) then
- begin
- FWidth := TgxMirror(Source).FWidth;
- FHeight := TgxMirror(Source).FHeight;
- FMirrorOptions := TgxMirror(Source).FMirrorOptions;
- MirrorObject := TgxMirror(Source).MirrorObject;
- end;
- inherited Assign(Source);
- end;
- function TgxMirror.AxisAlignedDimensionsUnscaled: TVector4f;
- begin
- Result := VectorMake(0.5 * Abs(FWidth),
- 0.5 * Abs(FHeight), 0);
- end;
- procedure TgxMirror.SetMirrorOptions(const val: TMirrorOptions);
- begin
- if FMirrorOptions <> val then
- begin
- FMirrorOptions := val;
- NotifyChange(Self);
- end;
- end;
- //ORL add-ons
- procedure TgxMirror.SetRadius(const aValue: Single);
- begin
- if aValue <> FRadius then
- begin
- FRadius := aValue;
- StructureChanged;
- end;
- end;
- function TgxMirror.GetRadius: single;
- begin
- result := FRadius;
- end;
- procedure TgxMirror.SetSlices(const aValue: GLint);
- begin
- if aValue <> FSlices then
- begin
- if aValue > 2 then
- FSlices := aValue;
- StructureChanged;
- end
- else
- begin
- end;
- end;
- function TgxMirror.GetSlices: GLint;
- begin
- result := FSlices;
- end;
- procedure TgxMirror.SetShape(aValue: TMirrorShapes);
- begin
- if aValue <> FShape then
- begin
- FShape := aValue;
- StructureChanged;
- end;
- end;
- //-------------------------------------------------------------
- initialization
- //-------------------------------------------------------------
- RegisterClasses([TgxMirror]);
- end.
|