2
// This unit is part of the GLScene Engine https://github.com/glscene
5
Implements a basic, stencil-based mirror (as in Mark Kilgard's demo).
7
It is strongly recommended to read and understand the explanations in the
8
materials/mirror demo before using this component.
11
23/08/10 - Yar - Added OpenGLTokens to uses, replaced OpenGL1x functions to OpenGLAdapter
12
22/04/10 - Yar - Fixes after GLState revision
13
05/03/10 - DanB - More state added to TGLStateCache
14
15/12/08- Paul Robello - corrected call to FOnEndRenderingMirrors
15
06/06/07 - DaStr - Added GLColor to uses (BugtrackerID = 1732211)
16
30/03/07 - DaStr - Added $I GLScene.inc
17
28/03/07 - DaStr - Renamed parameters in some methods
18
(thanks Burkhard Carstens) (Bugtracker ID = 1678658)
19
18/07/04 - Orlando - added custom shapes
20
13/02/03 - DanB - added TGLMirror.AxisAlignedDimensionsUnscaled
21
13/11/02 - EG - Fixed TGLMirror.DoRender transform
22
06/11/02 - EG - Fixed Stencil setup
23
30/10/02 - EG - Added OnBegin/EndRenderingMirrors
24
25/10/02 - EG - Fixed Stencil cleanup
25
22/02/01 - EG - Fixed change notification,
26
Fixed special effects support (PFX, etc.)
27
07/12/01 - EG - Creation
38
GLScene, GLVectorGeometry, OpenGLAdapter, OpenGLTokens, GLContext,
39
GLMaterial, GLColor, GLRenderContextInfo,
48
TMirrorOption = (moUseStencil, moOpaque, moMirrorPlaneClip, moClearZBuffer);
49
TMirrorOptions = set of TMirrorOption;
52
cDefaultMirrorOptions = [moUseStencil];
57
TMirrorShapes = (msRect, msDisk);
61
{ A simple plane mirror.
62
This mirror requires a stencil buffer for optimal rendering!
63
The object is a mix between a plane and a proxy object, in that the plane
64
defines the mirror's surface, while the proxy part is used to reference
65
the objects that should be mirrored (it is legal to self-mirror, but no
66
self-mirror visuals will be rendered).
67
It is strongly recommended to read and understand the explanations in the
68
materials/mirror demo before using this component. }
69
TGLMirror = class(TGLSceneObject)
73
FMirrorObject: TGLBaseSceneObject;
74
FWidth, FHeight: TGLFloat;
75
FMirrorOptions: TMirrorOptions;
76
FOnBeginRenderingMirrors, FOnEndRenderingMirrors: TNotifyEvent;
78
FShape: TMirrorShapes; //ORL
79
FRadius: TGLFloat; //ORL
80
FSlices: TGLInt; //ORL
84
procedure Notification(AComponent: TComponent; Operation: TOperation);
86
procedure SetMirrorObject(const val: TGLBaseSceneObject);
87
procedure SetMirrorOptions(const val: TMirrorOptions);
88
procedure ClearZBufferArea(aBuffer: TGLSceneBuffer);
90
procedure SetHeight(AValue: TGLFloat);
91
procedure SetWidth(AValue: TGLFloat);
93
procedure SetRadius(const aValue: Single); //ORL
94
procedure SetSlices(const aValue: TGLInt); //ORL
95
procedure SetShape(aValue: TMirrorShapes); //ORL
96
function GetRadius: single; //ORL
97
function GetSlices: TGLInt; //ORL
101
constructor Create(AOwner: TComponent); override;
103
procedure DoRender(var ARci: TGLRenderContextInfo;
104
ARenderSelf, ARenderChildren: Boolean); override;
105
procedure BuildList(var ARci: TGLRenderContextInfo); override;
107
procedure Assign(Source: TPersistent); override;
108
function AxisAlignedDimensionsUnscaled: TVector; override;
112
{ Selects the object to mirror.
113
If nil, the whole scene is mirrored. }
114
property MirrorObject: TGLBaseSceneObject read FMirrorObject write
116
{ Controls rendering options.
118
moUseStencil: mirror area is stenciled, prevents reflected
119
objects to be visible on the sides of the mirror (stencil buffer
120
must be active in the viewer)
121
moOpaque: mirror is opaque (ie. painted with background color)
122
moMirrorPlaneClip: a ClipPlane is defined to prevent reflections
123
from popping out of the mirror (for objects behind or halfway through)
124
moClearZBuffer: mirror area's ZBuffer is cleared so that background
125
objects don't interfere with reflected objects (reflected objects
126
must be rendered AFTER the mirror in the hierarchy). Works only
127
along with stenciling.
130
property MirrorOptions: TMirrorOptions read FMirrorOptions write
131
SetMirrorOptions default cDefaultMirrorOptions;
133
property Height: TGLFloat read FHeight write SetHeight;
134
property Width: TGLFloat read FWidth write SetWidth;
136
{ Fired before the object's mirror images are rendered. }
137
property OnBeginRenderingMirrors: TNotifyEvent read FOnBeginRenderingMirrors
138
write FOnBeginRenderingMirrors;
139
{ Fired after the object's mirror images are rendered. }
140
property OnEndRenderingMirrors: TNotifyEvent read FOnEndRenderingMirrors
141
write FOnEndRenderingMirrors;
143
property Radius: TGLFloat read FRadius write SetRadius; //ORL
144
property Slices: TGLInt read FSlices write SetSlices default 16; //ORL
145
property Shape: TMirrorShapes read FShape write SetShape default msRect;
149
//-------------------------------------------------------------
150
//-------------------------------------------------------------
151
//-------------------------------------------------------------
153
//-------------------------------------------------------------
154
//-------------------------------------------------------------
155
//-------------------------------------------------------------
158
// ------------------ TGLMirror ------------------
164
constructor TGLMirror.Create(AOwner: Tcomponent);
166
inherited Create(AOwner);
169
FMirrorOptions := cDefaultMirrorOptions;
170
ObjectStyle := ObjectStyle + [osDirectDraw];
171
Material.FrontProperties.Diffuse.Initialize(VectorMake(1, 1, 1, 0.1));
172
Material.BlendingMode := bmTransparency;
176
Shape := msRect; //ORL
182
procedure TGLMirror.DoRender(var ARci: TGLRenderContextInfo;
183
ARenderSelf, ARenderChildren: Boolean);
185
oldProxySubObject: Boolean;
186
refMat, curMat, ModelMat: TMatrix;
187
clipPlane: TDoubleHmgPlane;
188
bgColor: TColorVector;
189
cameraPosBackup, cameraDirectionBackup: TVector;
190
CurrentBuffer: TGLSceneBuffer;
196
oldProxySubObject := ARci.proxySubObject;
197
ARci.proxySubObject := True;
198
CurrentBuffer := TGLSceneBuffer(ARci.buffer);
200
if VectorDotProduct(VectorSubtract(ARci.cameraPosition, AbsolutePosition),
201
AbsoluteDirection) > 0 then
202
with ARci.GLStates do
205
// "Render" stencil mask
206
if MirrorOptions <> [] then
208
if (moUseStencil in MirrorOptions) then
210
Enable(stStencilTest);
211
ARci.GLStates.StencilClearValue := 0;
212
GL.Clear(GL_STENCIL_BUFFER_BIT);
213
SetStencilFunc(cfAlways, 1, 1);
214
SetStencilOp(soReplace, soZero, soReplace);
216
if (moOpaque in MirrorOptions) then
218
bgColor := ConvertWinColor(CurrentBuffer.BackgroundColor);
219
ARci.GLStates.SetGLMaterialColors(cmFront, bgColor, clrBlack,
220
clrBlack, clrBlack, 0);
223
SetGLColorWriting(False);
226
DepthWriteMask := False;
230
DepthWriteMask := True;
231
if (moUseStencil in MirrorOptions) then
233
SetStencilFunc(cfEqual, 1, 1);
234
SetStencilOp(soKeep, soKeep, soKeep);
237
if (moClearZBuffer in MirrorOptions) then
238
ClearZBufferArea(CurrentBuffer);
240
if not (moOpaque in MirrorOptions) then
241
SetGLColorWriting(True);
244
ARci.PipelineTransformation.Push;
245
ARci.PipelineTransformation.ModelMatrix := IdentityHmgMatrix;
250
if moMirrorPlaneClip in MirrorOptions then
252
GL.Enable(GL_CLIP_PLANE0);
253
SetPlane(clipPlane, PlaneMake(AffineVectorMake(AbsolutePosition),
254
VectorNegate(AffineVectorMake(AbsoluteDirection))));
255
GL.ClipPlane(GL_CLIP_PLANE0, @clipPlane);
259
refMat := MakeReflectionMatrix(
260
AffineVectorMake(AbsolutePosition),
261
AffineVectorMake(AbsoluteDirection));
262
curMat := MatrixMultiply(refMat, ARci.PipelineTransformation.ViewMatrix);
263
ARci.PipelineTransformation.ViewMatrix := curMat;
264
Scene.SetupLights(CurrentBuffer.LimitOf[limLights]);
266
// mirror geometry and render master
267
cameraPosBackup := ARci.cameraPosition;
268
cameraDirectionBackup := ARci.cameraDirection;
269
ARci.cameraPosition := VectorTransform(ARci.cameraPosition, refMat);
270
ARci.cameraDirection := VectorTransform(ARci.cameraDirection, refMat);
272
// temporary fix? (some objects don't respect culling options, or ?)
273
CullFaceMode := cmFront;
274
if Assigned(FOnBeginRenderingMirrors) then
275
FOnBeginRenderingMirrors(Self);
276
if Assigned(FMirrorObject) then
278
ModelMat := IdentityHmgMatrix;
279
if FMirrorObject.Parent <> nil then
280
MatrixMultiply(ModelMat, FMirrorObject.Parent.AbsoluteMatrix, ModelMat);
281
MatrixMultiply(ModelMat, FMirrorObject.LocalMatrix^, ModelMat);
282
ARci.PipelineTransformation.ModelMatrix := ModelMat;
283
FMirrorObject.DoRender(ARci, ARenderSelf, FMirrorObject.Count > 0);
287
Scene.Objects.DoRender(ARci, ARenderSelf, True);
289
if Assigned(FOnEndRenderingMirrors) then
290
FOnEndRenderingMirrors(Self);
292
// Restore to "normal"
293
ARci.cameraPosition := cameraPosBackup;
294
ARci.cameraDirection := cameraDirectionBackup;
295
ARci.GLStates.CullFaceMode := cmBack;
296
ARci.PipelineTransformation.ReplaceFromStack;
297
Scene.SetupLights(CurrentBuffer.LimitOf[limLights]);
298
ARci.PipelineTransformation.Pop;
299
if moMirrorPlaneClip in MirrorOptions then
300
GL.Disable(GL_CLIP_PLANE0);
301
ARci.GLStates.Disable(stStencilTest);
303
ARci.proxySubObject := oldProxySubObject;
305
// start rendering self
308
Material.Apply(ARci);
311
until not Material.UnApply(ARci);
316
if ARenderChildren then
317
Self.RenderChildren(0, Count - 1, ARci);
319
if Assigned(FMirrorObject) then
320
FMirrorObject.Effects.RenderPostEffects(ARci);
329
procedure TGLMirror.BuildList(var ARci: TGLRenderContextInfo);
332
quadric: PGLUquadricObj;
334
if msRect = FShape then
338
GL.Normal3fv(@ZVector);
340
GL.Vertex3f(hw, hh, 0);
341
GL.Vertex3f(-hw, hh, 0);
342
GL.Vertex3f(-hw, -hh, 0);
343
GL.Vertex3f(hw, -hh, 0);
348
quadric := gluNewQuadric;
349
gluDisk(Quadric, 0, FRadius, FSlices, 1); //radius. slices, loops
356
procedure TGLMirror.ClearZBufferArea(aBuffer: TGLSceneBuffer);
364
worldMat := Self.AbsoluteMatrix;
365
GL.MatrixMode(GL_PROJECTION);
368
GL.Ortho(0, Width, 0, Height, 1, -1);
369
GL.MatrixMode(GL_MODELVIEW);
372
with aBuffer.RenderingContext.GLStates do
374
DepthFunc := cfAlways;
375
SetGLColorWriting(False);
379
p := WorldToScreen(VectorTransform(AffineVectorMake(Self.Width * 0.5,
380
Self.Height * 0.5, 0), worldMat));
381
GL.Vertex3f(p.V[0], p.V[1], 0.999);
382
p := WorldToScreen(VectorTransform(AffineVectorMake(-Self.Width * 0.5,
383
Self.Height * 0.5, 0), worldMat));
384
GL.Vertex3f(p.V[0], p.V[1], 0.999);
385
p := WorldToScreen(VectorTransform(AffineVectorMake(-Self.Width * 0.5,
386
-Self.Height * 0.5, 0), worldMat));
387
GL.Vertex3f(p.V[0], p.V[1], 0.999);
388
p := WorldToScreen(VectorTransform(AffineVectorMake(Self.Width * 0.5,
389
-Self.Height * 0.5, 0), worldMat));
390
GL.Vertex3f(p.V[0], p.V[1], 0.999);
393
with aBuffer.RenderingContext.GLStates do
396
SetGLColorWriting(True);
399
GL.MatrixMode(GL_PROJECTION);
401
GL.MatrixMode(GL_MODELVIEW);
409
procedure TGLMirror.Notification(AComponent: TComponent; Operation: TOperation);
411
if (Operation = opRemove) and (AComponent = FMirrorObject) then
419
procedure TGLMirror.SetMirrorObject(const val: TGLBaseSceneObject);
421
if FMirrorObject <> val then
423
if Assigned(FMirrorObject) then
424
FMirrorObject.RemoveFreeNotification(Self);
425
FMirrorObject := val;
426
if Assigned(FMirrorObject) then
427
FMirrorObject.FreeNotification(Self);
435
procedure TGLMirror.SetWidth(AValue: TGLFloat);
437
if AValue <> FWidth then
447
procedure TGLMirror.SetHeight(AValue: TGLFloat);
449
if AValue <> FHeight then
459
procedure TGLMirror.Assign(Source: TPersistent);
461
if Assigned(Source) and (Source is TGLMirror) then
463
FWidth := TGLMirror(Source).FWidth;
464
FHeight := TGLMirror(Source).FHeight;
465
FMirrorOptions := TGLMirror(Source).FMirrorOptions;
466
MirrorObject := TGLMirror(Source).MirrorObject;
468
inherited Assign(Source);
471
// AxisAlignedDimensions
474
function TGLMirror.AxisAlignedDimensionsUnscaled: TVector;
476
Result := VectorMake(0.5 * Abs(FWidth),
477
0.5 * Abs(FHeight), 0);
483
procedure TGLMirror.SetMirrorOptions(const val: TMirrorOptions);
485
if FMirrorOptions <> val then
487
FMirrorOptions := val;
497
procedure TGLMirror.SetRadius(const aValue: Single);
499
if aValue <> FRadius then
509
function TGLMirror.GetRadius: single;
517
procedure TGLMirror.SetSlices(const aValue: TGLInt);
519
if aValue <> FSlices then
533
function TGLMirror.GetSlices: TGLInt;
541
procedure TGLMirror.SetShape(aValue: TMirrorShapes);
543
if aValue <> FShape then
550
//-------------------------------------------------------------
551
//-------------------------------------------------------------
552
//-------------------------------------------------------------
554
//-------------------------------------------------------------
555
//-------------------------------------------------------------
556
//-------------------------------------------------------------
558
RegisterClasses([TGLMirror]);