LZScene

Форк
0
/
GLScene.pas 
9491 строка · 271.7 Кб
1
//
2
// This unit is part of the GLScene Engine https://github.com/glscene
3
//
4
{
5
   Base classes and structures for GLScene.
6
}
7
unit GLScene;
8

9
interface
10

11
{$I GLScene.inc}
12

13
uses
14
  Classes, 
15
  SysUtils, 
16
  Graphics,  
17
  Controls,
18
  LCLType,
19
  OpenGLTokens, 
20
  GLStrings,
21
  GLContext, 
22
  GLVectorGeometry, 
23
  GLXCollection, 
24
  GLSilhouette,
25
  GLPersistentClasses, 
26
  GLState, 
27
  GLGraphics, 
28
  GLGeometryBB, 
29
  GLCrossPlatform,
30
  GLVectorLists, 
31
  GLTexture, 
32
  GLColor, 
33
  GLBaseClasses, 
34
  GLCoordinates,
35
  GLRenderContextInfo, 
36
  GLMaterial, 
37
  GLTextureFormat, 
38
  GLSelection,
39
  XOpenGL, 
40
  GLVectorTypes, 
41
  GLApplicationFileIO,
42
  GLUtils,  
43
  GLSLog;
44

45

46

47
type
48
  {Defines which features are taken from the master object. }
49
  TGLProxyObjectOption = (pooEffects, pooObjects, pooTransformation);
50
  TGLProxyObjectOptions = set of TGLProxyObjectOption;
51

52
  TGLCameraInvarianceMode = (cimNone, cimPosition, cimOrientation);
53

54
  TGLSceneViewerMode = (svmDisabled, svmDefault, svmNavigation, svmGizmo);
55

56
const
57
  cDefaultProxyOptions = [pooEffects, pooObjects, pooTransformation];
58
  GLSCENE_REVISION = '$Revision: 6695$';
59
  GLSCENE_VERSION = '1.5.0.%s';
60

61
type
62

63
  TNormalDirection = (ndInside, ndOutside);
64

65
  // used to describe only the changes in an object,
66
  // which have to be reflected in the scene
67
  TObjectChange = (ocTransformation, ocAbsoluteMatrix, ocInvAbsoluteMatrix,
68
    ocStructure);
69
  TObjectChanges = set of TObjectChange;
70

71
  TObjectBBChange = (oBBcChild, oBBcStructure);
72
  TObjectBBChanges = set of TObjectBBChange;
73

74
  // flags for design notification
75
  TSceneOperation = (soAdd, soRemove, soMove, soRename, soSelect, soBeginUpdate,
76
    soEndUpdate);
77

78
  {Options for the rendering context.
79
     roSoftwareMode: force software rendering.
80
     roDoubleBuffer: enables double-buffering. 
81
     roRenderToWindows: ignored (legacy). 
82
     roTwoSideLighting: enables two-side lighting model. 
83
     roStereo: enables stereo support in the driver (dunno if it works,
84
         I don't have a stereo device to test...) 
85
     roDestinationAlpha: request an Alpha channel for the rendered output 
86
     roNoColorBuffer: don't request a color buffer (color depth setting ignored) 
87
     roNoColorBufferClear: do not clear the color buffer automatically, if the
88
         whole viewer is fully repainted each frame, this can improve framerate 
89
     roNoSwapBuffers: don't perform RenderingContext.SwapBuffers after rendering
90
     roNoDepthBufferClear: do not clear the depth buffer automatically. Useful for
91
         early-z culling. 
92
     roForwardContext: force OpenGL forward context }
93
  TContextOption = (roSoftwareMode, roDoubleBuffer, roStencilBuffer,
94
    roRenderToWindow, roTwoSideLighting, roStereo,
95
    roDestinationAlpha, roNoColorBuffer, roNoColorBufferClear,
96
    roNoSwapBuffers, roNoDepthBufferClear, roDebugContext,
97
    roForwardContext, roOpenGL_ES2_Context);
98
  TContextOptions = set of TContextOption;
99

100
  // IDs for limit determination
101
  TLimitType = (limClipPlanes, limEvalOrder, limLights, limListNesting,
102
    limModelViewStack, limNameStack, limPixelMapTable, limProjectionStack,
103
    limTextureSize, limTextureStack, limViewportDims, limAccumAlphaBits,
104
    limAccumBlueBits, limAccumGreenBits, limAccumRedBits, limAlphaBits,
105
    limAuxBuffers, limBlueBits, limGreenBits, limRedBits, limIndexBits,
106
    limStereo, limDoubleBuffer, limSubpixelBits, limDepthBits, limStencilBits,
107
    limNbTextureUnits);
108

109
  TGLBaseSceneObject = class;
110
  TGLSceneObjectClass = class of TGLBaseSceneObject;
111
  TGLCustomSceneObject = class;
112
  TGLScene = class;
113
  TGLBehaviour = class;
114
  TGLBehaviourClass = class of TGLBehaviour;
115
  TGLBehaviours = class;
116
  TGLObjectEffect = class;
117
  TGLObjectEffectClass = class of TGLObjectEffect;
118
  TGLObjectEffects = class;
119
  TGLSceneBuffer = class;
120

121
  {Possible styles/options for a GLScene object.
122
     Allowed styles are: 
123
      osDirectDraw : object shall not make use of compiled call lists, but issue
124
        direct calls each time a render should be performed.
125
      osIgnoreDepthBuffer : object is rendered with depth test disabled,
126
        this is true for its children too.
127
      osNoVisibilityCulling : whatever the VisibilityCulling setting,
128
        it will be ignored and the object rendered
129
       }
130
  TGLObjectStyle = (
131
    osDirectDraw,
132
    osIgnoreDepthBuffer,
133
    osNoVisibilityCulling);
134
  TGLObjectStyles = set of TGLObjectStyle;
135

136
  {Interface to objects that need initialization  }
137
  IGLInitializable = interface
138
    ['{EA40AE8E-79B3-42F5-ADF1-7A901B665E12}']
139
    procedure InitializeObject(ASender: TObject; const ARci:
140
      TGLRenderContextInfo);
141
  end;
142

143
  // TGLInitializableObjectList
144
  //
145
  { Just a list of objects that support IGLInitializable. }
146
  TGLInitializableObjectList = class(TList)
147
  private
148
    function GetItems(const Index: Integer): IGLInitializable;
149
    procedure PutItems(const Index: Integer; const Value: IGLInitializable);
150
  public
151
    function Add(const Item: IGLInitializable): Integer;
152
    property Items[const Index: Integer]: IGLInitializable read GetItems write
153
    PutItems; default;
154
  end;
155

156
  {Base class for all scene objects.
157
     A scene object is part of scene hierarchy (each scene object can have
158
     multiple children), this hierarchy primarily defines transformations
159
     (each child coordinates are relative to its parent), but is also used
160
     for depth-sorting, bounding and visibility culling purposes.
161
     Subclasses implement either visual scene objects (that are made to be
162
     visible at runtime, like a Cube) or structural objects (that influence
163
     rendering or are used for varied structural manipulations,
164
     like the ProxyObject).
165
     To add children at runtime, use the AddNewChild method of TGLBaseSceneObject;
166
     other children manipulations methods and properties are provided (to browse,
167
     move and delete them). Using the regular TComponent methods is not
168
     encouraged. }
169

170
  TGLBaseSceneObject = class(TGLCoordinatesUpdateAbleComponent)
171
  private
172
    FAbsoluteMatrix, FInvAbsoluteMatrix: PMatrix;
173
    FLocalMatrix: PMatrix;
174
    FObjectStyle: TGLObjectStyles;
175
    FListHandle: TGLListHandle; // created on 1st use
176
    FPosition: TGLCoordinates;
177
    FDirection, FUp: TGLCoordinates;
178
    FScaling: TGLCoordinates;
179
    FChanges: TObjectChanges;
180
    FParent: TGLBaseSceneObject;
181
    FScene: TGLScene;
182
    FBBChanges: TObjectBBChanges;
183
    FBoundingBoxPersonalUnscaled: THmgBoundingBox;
184
    FBoundingBoxOfChildren: THmgBoundingBox;
185
    FBoundingBoxIncludingChildren: THmgBoundingBox;
186
    FChildren: TPersistentObjectList; // created on 1st use
187
    FVisible: Boolean;
188
    FUpdateCount: Integer;
189
    FShowAxes: Boolean;
190
    FRotation: TGLCoordinates; // current rotation angles
191
    FIsCalculating: Boolean;
192
    FObjectsSorting: TGLObjectsSorting;
193
    FVisibilityCulling: TGLVisibilityCulling;
194
    FOnProgress: TGLProgressEvent;
195
    FOnAddedToParent: TNotifyEvent;
196
    FGLBehaviours: TGLBehaviours;
197
    FGLObjectEffects: TGLObjectEffects;
198
    FPickable: Boolean;
199
    FOnPicked: TNotifyEvent;
200
    FTagObject: TObject;
201
    FTagFloat: Single;
202

203
    //  FOriginalFiler: TFiler;   //used to allow persistent events in behaviours & effects
204
    {If somebody could look at DefineProperties, ReadBehaviours, ReadEffects and verify code
205
    is safe to use then it could be uncommented}
206
    function Get(Index: Integer): TGLBaseSceneObject;
207
    function GetCount: Integer;
208
    function GetIndex: Integer;
209
    procedure SetParent(const val: TGLBaseSceneObject);
210
    procedure SetIndex(aValue: Integer);
211
    procedure SetDirection(AVector: TGLCoordinates);
212
    procedure SetUp(AVector: TGLCoordinates);
213
    function GetMatrix: TMatrix;
214
    procedure SetMatrix(const aValue: TMatrix);
215
    procedure SetPosition(APosition: TGLCoordinates);
216
    procedure SetPitchAngle(AValue: Single);
217
    procedure SetRollAngle(AValue: Single);
218
    procedure SetTurnAngle(AValue: Single);
219
    procedure SetRotation(aRotation: TGLCoordinates);
220
    function GetPitchAngle: Single;
221
    function GetTurnAngle: Single;
222
    function GetRollAngle: Single;
223
    procedure SetShowAxes(AValue: Boolean);
224
    procedure SetScaling(AValue: TGLCoordinates);
225
    procedure SetObjectsSorting(const val: TGLObjectsSorting);
226
    procedure SetVisibilityCulling(const val: TGLVisibilityCulling);
227
    procedure SetBehaviours(const val: TGLBehaviours);
228
    function GetBehaviours: TGLBehaviours;
229
    procedure SetEffects(const val: TGLObjectEffects);
230
    function GetEffects: TGLObjectEffects;
231
    function GetAbsoluteAffineScale: TAffineVector;
232
    function GetAbsoluteScale: TVector;
233
    procedure SetAbsoluteAffineScale(const Value: TAffineVector);
234
    procedure SetAbsoluteScale(const Value: TVector);
235
    function GetAbsoluteMatrix: TMatrix;
236
    procedure SetAbsoluteMatrix(const Value: TMatrix);
237
    procedure SetBBChanges(const Value: TObjectBBChanges);
238
  protected
239
    procedure Loaded; override;
240
    procedure SetScene(const Value: TGLScene); virtual;
241
    procedure DefineProperties(Filer: TFiler); override;
242
    procedure WriteBehaviours(stream: TStream);
243
    procedure ReadBehaviours(stream: TStream);
244
    procedure WriteEffects(stream: TStream);
245
    procedure ReadEffects(stream: TStream);
246
    procedure WriteRotations(stream: TStream);
247
    procedure ReadRotations(stream: TStream);
248
    function GetVisible: Boolean; virtual;
249
    function GetPickable: Boolean; virtual;
250
    procedure SetVisible(aValue: Boolean); virtual;
251
    procedure SetPickable(aValue: Boolean); virtual;
252
    procedure SetAbsolutePosition(const v: TVector);
253
    function GetAbsolutePosition: TVector;
254
    procedure SetAbsoluteUp(const v: TVector);
255
    function GetAbsoluteUp: TVector;
256
    procedure SetAbsoluteDirection(const v: TVector);
257
    function GetAbsoluteDirection: TVector;
258
    function GetAbsoluteAffinePosition: TAffineVector;
259
    procedure SetAbsoluteAffinePosition(const Value: TAffineVector);
260
    procedure SetAbsoluteAffineUp(const v: TAffineVector);
261
    function GetAbsoluteAffineUp: TAffineVector;
262
    procedure SetAbsoluteAffineDirection(const v: TAffineVector);
263
    function GetAbsoluteAffineDirection: TAffineVector;
264
    procedure RecTransformationChanged;
265
    procedure DrawAxes(var rci: TGLRenderContextInfo; pattern: Word);
266
    procedure GetChildren(AProc: TGetChildProc; Root: TComponent); override;
267
    // Should the object be considered as blended for sorting purposes?
268
    function Blended: Boolean; virtual;
269
    procedure RebuildMatrix;
270
    procedure SetName(const NewName: TComponentName); override;
271
    procedure SetParentComponent(Value: TComponent); override;
272
    procedure DestroyHandle; dynamic;
273
    procedure DestroyHandles;
274
    procedure DeleteChildCameras;
275
    procedure DoOnAddedToParent; virtual;
276

277
    { Used to re-calculate BoundingBoxes every time we need it.
278
       GetLocalUnscaleBB() must return the local BB, not the axis-aligned one.
279

280
       By default it is calculated from AxisAlignedBoundingBoxUnscaled and
281
       BarycenterAbsolutePosition, but for most objects there is a more
282
       efficient method, that's why it is virtual. }
283
    procedure CalculateBoundingBoxPersonalUnscaled(var ANewBoundingBox:
284
      THmgBoundingBox); virtual;
285
  public
286
     
287
    constructor Create(AOwner: TComponent); override;
288
    constructor CreateAsChild(aParentOwner: TGLBaseSceneObject);
289
    destructor Destroy; override;
290
    procedure Assign(Source: TPersistent); override;
291

292
    { Controls and adjusts internal optimizations based on object's style.
293
       Advanced user only. }
294
    property ObjectStyle: TGLObjectStyles read FObjectStyle write FObjectStyle;
295

296
    { Returns the handle to the object's build list.
297
       Use with caution! Some objects don't support buildlists! }
298
    function GetHandle(var rci: TGLRenderContextInfo): Cardinal; virtual;
299
    function ListHandleAllocated: Boolean;
300

301
    { The local transformation (relative to parent).
302
       If you're *sure* the local matrix is up-to-date, you may use LocalMatrix
303
       for quicker access. }
304
    property Matrix: TMatrix read GetMatrix write SetMatrix;
305
    { See Matrix. }
306
    function MatrixAsAddress: PMatrix;
307
    { Holds the local transformation (relative to parent).
308
       If you're not *sure* the local matrix is up-to-date, use Matrix property. }
309
    property LocalMatrix: PMatrix read FLocalMatrix;
310
    { Forces the local matrix to the specified value.
311
       AbsoluteMatrix, InverseMatrix, etc. will honour that change, but
312
       may become invalid if the specified matrix isn't orthonormal (can
313
       be used for specific rendering or projection effects). 
314
       The local matrix will be reset by the next TransformationChanged,
315
       position or attitude change. }
316
    procedure ForceLocalMatrix(const aMatrix: TMatrix);
317

318
    { See AbsoluteMatrix. }
319
    function AbsoluteMatrixAsAddress: PMatrix;
320
    { Holds the absolute transformation matrix.
321
       If you're not *sure* the absolute matrix is up-to-date,
322
       use the AbsoluteMatrix property, this one may be nil... }
323
    property DirectAbsoluteMatrix: PMatrix read FAbsoluteMatrix;
324

325
    { Calculates the object's absolute inverse matrix.
326
       Multiplying an absolute coordinate with this matrix gives a local coordinate.
327
       The current implem uses transposition(AbsoluteMatrix), which is true
328
       unless you're using some scaling... }
329
    function InvAbsoluteMatrix: TMatrix;
330
    { See InvAbsoluteMatrix. }
331
    function InvAbsoluteMatrixAsAddress: PMatrix;
332

333
    { The object's absolute matrix by composing all local matrices.
334
       Multiplying a local coordinate with this matrix gives an absolute coordinate. }
335
    property AbsoluteMatrix: TMatrix read GetAbsoluteMatrix write
336
      SetAbsoluteMatrix;
337

338
    { Direction vector in absolute coordinates. }
339
    property AbsoluteDirection: TVector read GetAbsoluteDirection write
340
      SetAbsoluteDirection;
341
    property AbsoluteAffineDirection: TAffineVector read
342
      GetAbsoluteAffineDirection write SetAbsoluteAffineDirection;
343

344
    { Scale vector in absolute coordinates.
345
       Warning: SetAbsoluteScale() does not work correctly at the moment. }
346
    property AbsoluteScale: TVector read GetAbsoluteScale write
347
      SetAbsoluteScale;
348
    property AbsoluteAffineScale: TAffineVector read GetAbsoluteAffineScale write
349
      SetAbsoluteAffineScale;
350

351
    { Up vector in absolute coordinates. }
352
    property AbsoluteUp: TVector read GetAbsoluteUp write SetAbsoluteUp;
353
    property AbsoluteAffineUp: TAffineVector read GetAbsoluteAffineUp write
354
      SetAbsoluteAffineUp;
355

356
    { Calculate the right vector in absolute coordinates. }
357
    function AbsoluteRight: TVector;
358

359
    { Calculate the left vector in absolute coordinates. }
360
    function AbsoluteLeft: TVector;
361

362
    { Computes and allows to set the object's absolute coordinates. }
363
    property AbsolutePosition: TVector read GetAbsolutePosition write
364
      SetAbsolutePosition;
365
    property AbsoluteAffinePosition: TAffineVector read GetAbsoluteAffinePosition
366
      write SetAbsoluteAffinePosition;
367
    function AbsolutePositionAsAddress: PVector;
368

369
    { Returns the Absolute X Vector expressed in local coordinates. }
370
    function AbsoluteXVector: TVector;
371
    { Returns the Absolute Y Vector expressed in local coordinates. }
372
    function AbsoluteYVector: TVector;
373
    { Returns the Absolute Z Vector expressed in local coordinates. }
374
    function AbsoluteZVector: TVector;
375

376
    { Converts a vector/point from absolute coordinates to local coordinates. }
377
    function AbsoluteToLocal(const v: TVector): TVector; overload;
378
    { Converts a vector from absolute coordinates to local coordinates. }
379
    function AbsoluteToLocal(const v: TAffineVector): TAffineVector; overload;
380
    { Converts a vector/point from local coordinates to absolute coordinates. }
381
    function LocalToAbsolute(const v: TVector): TVector; overload;
382
    { Converts a vector from local coordinates to absolute coordinates. }
383
    function LocalToAbsolute(const v: TAffineVector): TAffineVector; overload;
384

385
    { Returns the Right vector (based on Up and Direction) }
386
    function Right: TVector;
387
    { Returns the Left vector (based on Up and Direction) }
388
    function LeftVector: TVector;
389

390
    { Returns the Right vector (based on Up and Direction) }
391
    function AffineRight: TAffineVector;
392
    { Returns the Left vector (based on Up and Direction) }
393
    function AffineLeftVector: TAffineVector;
394

395
    { Calculates the object's square distance to a point/object.
396
       pt is assumed to be in absolute coordinates,
397
       AbsolutePosition is considered as being the object position. }
398
    function SqrDistanceTo(anObject: TGLBaseSceneObject): Single; overload;
399
    function SqrDistanceTo(const pt: TVector): Single; overload;
400
    function SqrDistanceTo(const pt: TAffineVector): Single; overload;
401

402
    { Computes the object's distance to a point/object.
403
       Only objects AbsolutePositions are considered. }
404
    function DistanceTo(anObject: TGLBaseSceneObject): Single; overload;
405
    function DistanceTo(const pt: TAffineVector): Single; overload;
406
    function DistanceTo(const pt: TVector): Single; overload;
407

408
    { Calculates the object's barycenter in absolute coordinates.
409
       Default behaviour is to consider Barycenter=AbsolutePosition
410
       (whatever the number of children). 
411
       SubClasses where AbsolutePosition is not the barycenter should
412
       override this method as it is used for distance calculation, during
413
       rendering for instance, and may lead to visual inconsistencies. }
414
    function BarycenterAbsolutePosition: TVector; virtual;
415
    { Calculates the object's barycenter distance to a point. }
416
    function BarycenterSqrDistanceTo(const pt: TVector): Single;
417

418
    { Shall returns the object's axis aligned extensions.
419
       The dimensions are measured from object center and are expressed
420
       <i>with</i> scale accounted for, in the object's coordinates
421
       (not in absolute coordinates).
422
       Default value is half the object's Scale.  }
423
    function AxisAlignedDimensions: TVector; virtual;
424
    function AxisAlignedDimensionsUnscaled: TVector; virtual;
425
    {Calculates and return the AABB for the object.
426
       The AABB is currently calculated from the BB.
427
       There is  no  caching scheme for them. }
428
    function AxisAlignedBoundingBox(const AIncludeChilden: Boolean = True): TAABB;
429
    function AxisAlignedBoundingBoxUnscaled(const AIncludeChilden: Boolean = True): TAABB;
430
    function AxisAlignedBoundingBoxAbsolute(const AIncludeChilden: Boolean =
431
      True; const AUseBaryCenter: Boolean = False): TAABB;
432

433
    {Advanced AABB functions that use a caching scheme.
434
       Also they include children and use BaryCenter. }
435
    function AxisAlignedBoundingBoxEx: TAABB;
436
    function AxisAlignedBoundingBoxAbsoluteEx: TAABB;
437

438
    {Calculates and return the Bounding Box for the object.
439
       The BB is calculated  each  time this method is invoked,
440
       based on the AxisAlignedDimensions of the object and that of its
441
       children.
442
       There is  no  caching scheme for them. }
443
    function BoundingBox(const AIncludeChilden: Boolean = True; const
444
      AUseBaryCenter: Boolean = False): THmgBoundingBox;
445
    function BoundingBoxUnscaled(const AIncludeChilden: Boolean = True; const
446
      AUseBaryCenter: Boolean = False): THmgBoundingBox;
447
    function BoundingBoxAbsolute(const AIncludeChilden: Boolean = True; const
448
      AUseBaryCenter: Boolean = False): THmgBoundingBox;
449

450
    {Advanced BB functions that use a caching scheme.
451
       Also they include children and use BaryCenter. }
452
    function BoundingBoxPersonalUnscaledEx: THmgBoundingBox;
453
    function BoundingBoxOfChildrenEx: THmgBoundingBox;
454
    function BoundingBoxIncludingChildrenEx: THmgBoundingBox;
455

456
    {Max distance of corners of the BoundingBox. }
457
    function BoundingSphereRadius: Single;
458
    function BoundingSphereRadiusUnscaled: Single;
459

460
    {Indicates if a point is within an object. 
461
       Given coordinate is an absolute coordinate.
462
       Linear or surfacic objects shall always return False. 
463
       Default value is based on AxisAlignedDimension and a cube bounding. }
464
    function PointInObject(const point: TVector): Boolean; virtual;
465
    {Request to determine an intersection with a casted ray. 
466
       Given coordinates & vector are in absolute coordinates, rayVector
467
       must be normalized.
468
       rayStart may be a point inside the object, allowing retrieval of
469
       the multiple intersects of the ray. 
470
       When intersectXXX parameters are nil (default) implementation should
471
       take advantage of this to optimize calculus, if not, and an intersect
472
       is found, non nil parameters should be defined. 
473
       The intersectNormal needs NOT be normalized by the implementations. 
474
       Default value is based on bounding sphere. }
475
    function RayCastIntersect(const rayStart, rayVector: TVector;
476
      intersectPoint: PVector = nil;
477
      intersectNormal: PVector = nil): Boolean; virtual;
478
    {Request to generate silhouette outlines.
479
       Default implementation assumes the objects is a sphere of
480
       AxisAlignedDimensionUnscaled size. Subclasses may choose to return
481
       nil instead, which will be understood as an empty silhouette. }
482
    function GenerateSilhouette(const silhouetteParameters:
483
      TGLSilhouetteParameters): TGLSilhouette; virtual;
484

485
    property Children[Index: Integer]: TGLBaseSceneObject read Get; default;
486
    property Count: Integer read GetCount;
487
    property Index: Integer read GetIndex write SetIndex;
488
    // Create a new scene object and add it to this object as new child
489
    function AddNewChild(AChild: TGLSceneObjectClass): TGLBaseSceneObject; dynamic;
490
    // Create a new scene object and add it to this object as first child
491
    function AddNewChildFirst(AChild: TGLSceneObjectClass): TGLBaseSceneObject; dynamic;
492
    procedure AddChild(AChild: TGLBaseSceneObject); dynamic;
493
    function GetOrCreateBehaviour(aBehaviour: TGLBehaviourClass): TGLBehaviour;
494
    function AddNewBehaviour(aBehaviour: TGLBehaviourClass): TGLBehaviour;
495

496
    function GetOrCreateEffect(anEffect: TGLObjectEffectClass): TGLObjectEffect;
497
    function AddNewEffect(anEffect: TGLObjectEffectClass): TGLObjectEffect;
498

499
    function HasSubChildren: Boolean;
500
    procedure DeleteChildren; dynamic;
501
    procedure Insert(AIndex: Integer; AChild: TGLBaseSceneObject); dynamic;
502
    {Takes a scene object out of the child list, but doesn't destroy it.
503
       If 'KeepChildren' is true its children will be kept as new children
504
       in this scene object. }
505
    procedure Remove(aChild: TGLBaseSceneObject; keepChildren: Boolean); dynamic;
506
    function IndexOfChild(aChild: TGLBaseSceneObject): Integer;
507
    function FindChild(const aName: string; ownChildrenOnly: Boolean):
508
      TGLBaseSceneObject;
509
    {The "safe" version of this procedure checks if indexes are inside
510
       the list. If not, no exception if raised. }
511
    procedure ExchangeChildrenSafe(anIndex1, anIndex2: Integer);
512
    {The "regular" version of this procedure does not perform any checks
513
       and calls FChildren.Exchange directly. User should/can perform range
514
       checks manualy. }
515
    procedure ExchangeChildren(anIndex1, anIndex2: Integer);
516
    {These procedures are safe. }
517
    procedure MoveChildUp(anIndex: Integer);
518
    procedure MoveChildDown(anIndex: Integer);
519
    procedure MoveChildFirst(anIndex: Integer);
520
    procedure MoveChildLast(anIndex: Integer);
521
    procedure DoProgress(const progressTime: TProgressTimes); override;
522
    procedure MoveTo(newParent: TGLBaseSceneObject); dynamic;
523
    procedure MoveUp;
524
    procedure MoveDown;
525
    procedure MoveFirst;
526
    procedure MoveLast;
527
    procedure BeginUpdate; virtual;
528
    procedure EndUpdate; virtual;
529
    {Make object-specific geometry description here.
530
       Subclasses should MAINTAIN OpenGL states (restore the states if
531
       they were altered). }
532
    procedure BuildList(var rci: TGLRenderContextInfo); virtual;
533
    function GetParentComponent: TComponent; override;
534
    function HasParent: Boolean; override;
535
    function IsUpdating: Boolean;
536
    // Moves the object along the Up vector (move up/down)
537
    procedure Lift(ADistance: Single);
538
    // Moves the object along the direction vector
539
    procedure Move(ADistance: Single);
540
    // Translates the object
541
    procedure Translate(tx, ty, tz: Single);
542
    procedure MoveObjectAround(anObject: TGLBaseSceneObject;
543
      pitchDelta, turnDelta: Single);
544
    procedure MoveObjectAllAround(anObject: TGLBaseSceneObject;
545
      pitchDelta, turnDelta: Single);
546
    procedure Pitch(angle: Single);
547
    procedure Roll(angle: Single);
548
    procedure Turn(angle: Single);
549

550
    { Sets all rotations to zero and restores default Direction/Up.
551
       Using this function then applying roll/pitch/turn in the order that
552
       suits you, you can give an "absolute" meaning to rotation angles
553
       (they are still applied locally though).
554
       Scale and Position are not affected. }
555
    procedure ResetRotations;
556
    {Reset rotations and applies them back in the specified order. }
557
    procedure ResetAndPitchTurnRoll(const degX, degY, degZ: Single);
558

559
    {Applies rotations around absolute X, Y and Z axis.  }
560
    procedure RotateAbsolute(const rx, ry, rz: Single); overload;
561
    {Applies rotations around the absolute given vector (angle in degrees).  }
562
    procedure RotateAbsolute(const axis: TAffineVector; angle: Single);
563
      overload;
564
    // Moves camera along the right vector (move left and right)
565
    procedure Slide(ADistance: Single);
566
    // Orients the object toward a target object
567
    procedure PointTo(const ATargetObject: TGLBaseSceneObject; const AUpVector:
568
      TVector); overload;
569
    // Orients the object toward a target absolute position
570
    procedure PointTo(const AAbsolutePosition, AUpVector: TVector); overload;
571

572
    procedure Render(var ARci: TGLRenderContextInfo);
573
    procedure DoRender(var ARci: TGLRenderContextInfo;
574
      ARenderSelf, ARenderChildren: Boolean); virtual;
575
    procedure RenderChildren(firstChildIndex, lastChildIndex: Integer;
576
      var rci: TGLRenderContextInfo); virtual;
577

578
    procedure StructureChanged; dynamic;
579
    procedure ClearStructureChanged;
580
    // Recalculate an orthonormal system
581
    procedure CoordinateChanged(Sender: TGLCustomCoordinates); override;
582
    procedure TransformationChanged;
583
    procedure NotifyChange(Sender: TObject); override;
584
    property Rotation: TGLCoordinates read FRotation write SetRotation;
585
    property PitchAngle: Single read GetPitchAngle write SetPitchAngle;
586
    property RollAngle: Single read GetRollAngle write SetRollAngle;
587
    property TurnAngle: Single read GetTurnAngle write SetTurnAngle;
588

589
    property ShowAxes: Boolean read FShowAxes write SetShowAxes default False;
590

591
    property Changes: TObjectChanges read FChanges;
592
    property BBChanges: TObjectBBChanges read fBBChanges write SetBBChanges;
593
    property Parent: TGLBaseSceneObject read FParent write SetParent;
594
    property Position: TGLCoordinates read FPosition write SetPosition;
595
    property Direction: TGLCoordinates read FDirection write SetDirection;
596
    property Up: TGLCoordinates read FUp write SetUp;
597
    property Scale: TGLCoordinates read FScaling write SetScaling;
598
    property Scene: TGLScene read FScene;
599
    property Visible: Boolean read FVisible write SetVisible default True;
600
    property Pickable: Boolean read FPickable write SetPickable default True;
601
    property ObjectsSorting: TGLObjectsSorting read FObjectsSorting write
602
      SetObjectsSorting default osInherited;
603
    property VisibilityCulling: TGLVisibilityCulling read FVisibilityCulling
604
      write SetVisibilityCulling default vcInherited;
605
    property OnProgress: TGLProgressEvent read FOnProgress write FOnProgress;
606
    property OnPicked: TNotifyEvent read FOnPicked write FOnPicked;
607
    property OnAddedToParent: TNotifyEvent read FOnAddedToParent write
608
      FOnAddedToParent;
609
    property Behaviours: TGLBehaviours read GetBehaviours write SetBehaviours
610
      stored False;
611
    property Effects: TGLObjectEffects read GetEffects write SetEffects stored
612
      False;
613
    property TagObject: TObject read FTagObject write FTagObject;
614
  published
615
    property TagFloat: Single read FTagFloat write FTagFloat;
616
  end;
617

618
  {Base class for implementing behaviours in TGLScene.
619
     Behaviours are regrouped in a collection attached to a TGLBaseSceneObject,
620
     and are part of the "Progress" chain of events. Behaviours allows clean
621
     application of time-based alterations to objects (movements, shape or
622
     texture changes...).
623
     Since behaviours are implemented as classes, there are basicly two kinds
624
     of strategies for subclasses :
625
      stand-alone : the subclass does it all, and holds all necessary data
626
        (covers animation, inertia etc.)
627
      proxy : the subclass is an interface to and external, shared operator
628
        (like gravity, force-field effects etc.)
629
      
630
     Some behaviours may be cooperative (like force-fields affects inertia)
631
     or unique (e.g. only one inertia behaviour per object). 
632
     NOTES : 
633
      Don't forget to override the ReadFromFiler/WriteToFiler persistence
634
        methods if you add data in a subclass !
635
      Subclasses must be registered using the RegisterXCollectionItemClass
636
        function }
637
  TGLBaseBehaviour = class(TGLXCollectionItem)
638
  protected
639
    procedure SetName(const val: string); override;
640
    {Override this function to write subclass data. }
641
    procedure WriteToFiler(writer: TWriter); override;
642
    {Override this function to read subclass data. }
643
    procedure ReadFromFiler(reader: TReader); override;
644
    {Returns the TGLBaseSceneObject on which the behaviour should be applied.
645
       Does NOT check for nil owners. }
646
    function OwnerBaseSceneObject: TGLBaseSceneObject;
647
  public
648
    constructor Create(aOwner: TGLXCollection); override;
649
    destructor Destroy; override;
650
    procedure DoProgress(const progressTime: TProgressTimes); virtual;
651
  end;
652

653
  {Ancestor for non-rendering behaviours.
654
     This class shall never receive any properties, it's just here to differentiate
655
     rendereing and non-rendering behaviours. Rendereing behaviours are named
656
     "TGLObjectEffect", non-rendering effects (like inertia) are simply named
657
     "TGLBehaviour". }
658
  TGLBehaviour = class(TGLBaseBehaviour)
659
  end;
660

661
  {Holds a list of TGLBehaviour objects.
662
     This object expects itself to be owned by a TGLBaseSceneObject.
663
     As a TGLXCollection (and contrary to a TCollection), this list can contain
664
     objects of varying class, the only constraint being that they should all
665
     be TGLBehaviour subclasses. }
666
  TGLBehaviours = class(TGLXCollection)
667
  protected
668
    function GetBehaviour(index: Integer): TGLBehaviour;
669
  public
670
    constructor Create(aOwner: TPersistent); override;
671
    function GetNamePath: string; override;
672
    class function ItemsClass: TGLXCollectionItemClass; override;
673
    property Behaviour[index: Integer]: TGLBehaviour read GetBehaviour; default;
674
    function CanAdd(aClass: TGLXCollectionItemClass): Boolean; override;
675
    procedure DoProgress(const progressTimes: TProgressTimes);
676
  end;
677

678
  {A rendering effect that can be applied to SceneObjects.
679
     ObjectEffect is a subclass of behaviour that gets a chance to Render
680
     an object-related special effect.
681
     TGLObjectEffect should not be used as base class for custom effects,
682
     instead you should use the following base classes : 
683
      TGLObjectPreEffect is rendered before owner object render
684
      TGLObjectPostEffect is rendered after the owner object render
685
      TGLObjectAfterEffect is rendered at the end of the scene rendering
686
       NOTES : 
687
      Don't forget to override the ReadFromFiler/WriteToFiler persistence
688
        methods if you add data in a subclass !
689
      Subclasses must be registered using the RegisterXCollectionItemClass
690
        function }
691

692
//   TGLObjectEffectClass = class of TGLObjectEffect;
693

694
  TGLObjectEffect = class(TGLBaseBehaviour)
695
  protected
696
    {Override this function to write subclass data. }
697
    procedure WriteToFiler(writer: TWriter); override;
698
    {Override this function to read subclass data. }
699
    procedure ReadFromFiler(reader: TReader); override;
700
  public
701
     
702
    procedure Render(var rci: TGLRenderContextInfo); virtual;
703
  end;
704

705
  {An object effect that gets rendered before owner object's render.
706
     The current OpenGL matrices and material are that of the owner object. }
707
  TGLObjectPreEffect = class(TGLObjectEffect)
708
  end;
709

710
  {An object effect that gets rendered after owner object's render.
711
     The current OpenGL matrices and material are that of the owner object. }
712
  TGLObjectPostEffect = class(TGLObjectEffect)
713
  end;
714

715
  {An object effect that gets rendered at scene's end.
716
     No particular OpenGL matrices or material should be assumed. }
717
  TGLObjectAfterEffect = class(TGLObjectEffect)
718
  end;
719

720
  {Holds a list of object effects.
721
     This object expects itself to be owned by a TGLBaseSceneObject.  }
722
  TGLObjectEffects = class(TGLXCollection)
723
  protected
724
    function GetEffect(index: Integer): TGLObjectEffect;
725
  public
726
    constructor Create(aOwner: TPersistent); override;
727
    function GetNamePath: string; override;
728
    class function ItemsClass: TGLXCollectionItemClass; override;
729
    property ObjectEffect[index: Integer]: TGLObjectEffect read GetEffect;
730
    default;
731
    function CanAdd(aClass: TGLXCollectionItemClass): Boolean; override;
732
    procedure DoProgress(const progressTime: TProgressTimes);
733
    procedure RenderPreEffects(var rci: TGLRenderContextInfo);
734
    { Also take care of registering after effects with the GLSceneViewer. }
735
    procedure RenderPostEffects(var rci: TGLRenderContextInfo);
736
  end;
737

738
  {Extended base scene object class with a material property.
739
     The material allows defining a color and texture for the object,
740
     see TGLMaterial. }
741
  TGLCustomSceneObject = class(TGLBaseSceneObject)
742
  private
743
    FMaterial: TGLMaterial;
744
    FHint: string;
745
  protected
746
    function Blended: Boolean; override;
747
    procedure SetGLMaterial(AValue: TGLMaterial);
748
    procedure DestroyHandle; override;
749
    procedure Loaded; override;
750
  public
751
    constructor Create(AOwner: TComponent); override;
752
    destructor Destroy; override;
753
    procedure Assign(Source: TPersistent); override;
754
    procedure DoRender(var ARci: TGLRenderContextInfo;
755
      ARenderSelf, ARenderChildren: Boolean); override;
756
    property Material: TGLMaterial read FMaterial write SeTGLMaterial;
757
    property Hint: string read FHint write FHint;
758
  end;
759

760
  {This class shall be used only as a hierarchy root.
761
     It exists only as a container and shall never be rotated/scaled etc. as
762
     the class type is used in parenting optimizations.
763
     Shall never implement or add any functionality, the "Create" override
764
     only take cares of disabling the build list. }
765
  TGLSceneRootObject = class(TGLBaseSceneObject)
766
  public
767
    constructor Create(AOwner: TComponent); override;
768
  end;
769

770
  {Base class for objects that do not have a published "material".
771
     Note that the material is available in public properties, but isn't
772
     applied automatically before invoking BuildList.
773
     Subclassing should be reserved to structural objects and objects that
774
     have no material of their own. }
775
  TGLImmaterialSceneObject = class(TGLCustomSceneObject)
776
  public
777
     
778
    procedure DoRender(var ARci: TGLRenderContextInfo;
779
      ARenderSelf, ARenderChildren: Boolean); override;
780
  published
781
    property ObjectsSorting;
782
    property VisibilityCulling;
783
    property Direction;
784
    property PitchAngle;
785
    property Position;
786
    property RollAngle;
787
    property Scale;
788
    property ShowAxes;
789
    property TurnAngle;
790
    property Up;
791
    property Visible;
792
    property Pickable;
793
    property OnProgress;
794
    property OnPicked;
795
    property Behaviours;
796
    property Effects;
797
    property Hint;
798
  end;
799

800
  {Base class for camera invariant objects.
801
     Camera invariant objects bypass camera settings, such as camera
802
     position (object is always centered on camera) or camera orientation
803
     (object always has same orientation as camera). }
804
  TGLCameraInvariantObject = class(TGLImmaterialSceneObject)
805
  private
806
    FCamInvarianceMode: TGLCameraInvarianceMode;
807
  protected
808
    procedure SetCamInvarianceMode(const val: TGLCameraInvarianceMode);
809
    property CamInvarianceMode: TGLCameraInvarianceMode read FCamInvarianceMode
810
      write SetCamInvarianceMode;
811
  public
812
    constructor Create(AOwner: TComponent); override;
813
    procedure Assign(Source: TPersistent); override;
814
    procedure DoRender(var ARci: TGLRenderContextInfo;
815
      ARenderSelf, ARenderChildren: Boolean); override;
816
  end;
817

818
  {Base class for standard scene objects. Publishes the Material property. }
819
  TGLSceneObject = class(TGLCustomSceneObject)
820
  published
821
    property Material;
822
    property ObjectsSorting;
823
    property VisibilityCulling;
824
    property Direction;
825
    property PitchAngle;
826
    property Position;
827
    property RollAngle;
828
    property Scale;
829
    property ShowAxes;
830
    property TurnAngle;
831
    property Up;
832
    property Visible;
833
    property Pickable;
834
    property OnProgress;
835
    property OnPicked;
836
    property Behaviours;
837
    property Effects;
838
    property Hint;
839
  end;
840

841
  {Event for user-specific rendering in a TGLDirectOpenGL object. }
842
  TDirectRenderEvent = procedure(Sender: TObject; var rci: TGLRenderContextInfo)
843
    of object;
844

845
  {Provides a way to issue direct OpenGL calls during the rendering.
846
     You can use this object to do your specific rendering task in its OnRender
847
     event. The OpenGL calls shall restore the OpenGL states they found when
848
     entering, or exclusively use the GLMisc utility functions to alter the
849
     states. }
850
  TGLDirectOpenGL = class(TGLImmaterialSceneObject)
851
  private
852

853
    FUseBuildList: Boolean;
854
    FOnRender: TDirectRenderEvent;
855
    FBlend: Boolean;
856
  protected
857
    procedure SetUseBuildList(const val: Boolean);
858
    function Blended: Boolean; override;
859
    procedure SetBlend(const val: Boolean);
860
  public
861
     
862
    constructor Create(AOwner: TComponent); override;
863

864
    procedure Assign(Source: TPersistent); override;
865
    procedure BuildList(var rci: TGLRenderContextInfo); override;
866

867
    function AxisAlignedDimensionsUnscaled: TVector; override;
868
  published
869
    {Specifies if a build list be made.
870
       If True, GLScene will generate a build list (OpenGL-side cache),
871
       ie. OnRender will only be invoked once for the first render, or after
872
       a StructureChanged call. This is suitable for "static" geometry and
873
       will usually speed up rendering of things that don't change.
874
       If false, OnRender will be invoked for each render. This is suitable
875
       for dynamic geometry (things that change often or constantly). }
876
    property UseBuildList: Boolean read FUseBuildList write SetUseBuildList;
877
    {Place your specific OpenGL code here.
878
       The OpenGL calls shall restore the OpenGL states they found when
879
       entering, or exclusively use the GLMisc utility functions to alter
880
       the states.  }
881
    property OnRender: TDirectRenderEvent read FOnRender write FOnRender;
882
    { Defines if the object uses blending.
883
       This property will allow direct opengl objects to be flagged as
884
       blended for object sorting purposes. }
885
    property Blend: Boolean read FBlend write SetBlend;
886
  end;
887

888
  {Scene object that allows other objects to issue rendering at some point.
889
     This object is used to specify a render point for which other components
890
     have (rendering) tasks to perform. It doesn't render anything itself
891
     and is invisible, but other components can register and be notified
892
     when the point is reached in the rendering phase. 
893
     Callbacks must be explicitly unregistered. }
894
  TGLRenderPoint = class(TGLImmaterialSceneObject)
895
  private
896
     
897
    FCallBacks: array of TDirectRenderEvent;
898
    FFreeCallBacks: array of TNotifyEvent;
899
  public
900
    constructor Create(AOwner: TComponent); override;
901
    destructor Destroy; override;
902
    procedure BuildList(var rci: TGLRenderContextInfo); override;
903

904
    procedure RegisterCallBack(renderEvent: TDirectRenderEvent;
905
      renderPointFreed: TNotifyEvent);
906
    procedure UnRegisterCallBack(renderEvent: TDirectRenderEvent);
907
    procedure Clear;
908
  end;
909

910
  {A full proxy object.
911
     This object literally uses another object's Render method to do its own
912
     rendering, however, it has a coordinate system and a life of its own.
913
     Use it for duplicates of an object. }
914
  TGLProxyObject = class(TGLBaseSceneObject)
915
  private
916
    FMasterObject: TGLBaseSceneObject;
917
    FProxyOptions: TGLProxyObjectOptions;
918
  protected
919
    FRendering: Boolean;
920
    procedure Notification(AComponent: TComponent; Operation: TOperation);
921
      override;
922
    procedure SetMasterObject(const val: TGLBaseSceneObject); virtual;
923
    procedure SetProxyOptions(const val: TGLProxyObjectOptions);
924
  public
925
    constructor Create(AOwner: TComponent); override;
926
    destructor Destroy; override;
927
    procedure Assign(Source: TPersistent); override;
928
    procedure DoRender(var ARci: TGLRenderContextInfo;
929
      ARenderSelf, ARenderChildren: Boolean); override;
930
    function BarycenterAbsolutePosition: TVector; override;
931
    function AxisAlignedDimensions: TVector; override;
932
    function AxisAlignedDimensionsUnscaled: TVector; override;
933
    function RayCastIntersect(const rayStart, rayVector: TVector;
934
      intersectPoint: PVector = nil;
935
      intersectNormal: PVector = nil): Boolean; override;
936
    function GenerateSilhouette(const silhouetteParameters:
937
      TGLSilhouetteParameters): TGLSilhouette; override;
938

939
  published
940
    {Specifies the Master object which will be proxy'ed. }
941
    property MasterObject: TGLBaseSceneObject read FMasterObject write
942
      SetMasterObject;
943
    {Specifies how and what is proxy'ed. }
944
    property ProxyOptions: TGLProxyObjectOptions read FProxyOptions write
945
      SetProxyOptions default cDefaultProxyOptions;
946
    property ObjectsSorting;
947
    property Direction;
948
    property PitchAngle;
949
    property Position;
950
    property RollAngle;
951
    property Scale;
952
    property ShowAxes;
953
    property TurnAngle;
954
    property Up;
955
    property Visible;
956
    property Pickable;
957
    property OnProgress;
958
    property OnPicked;
959
    property Behaviours;
960
  end;
961

962
  TGLProxyObjectClass = class of TGLProxyObject;
963

964
  {Defines the various styles for lightsources.
965
      lsSpot : a spot light, oriented and with a cutoff zone (note that if
966
        cutoff is 180, the spot is rendered as an omni source)
967
      lsOmni : an omnidirectionnal source, punctual and sending light in
968
        all directions uniformously
969
      lsParallel : a parallel light, oriented as the light source is (this
970
        type of light can help speed up rendering)}
971
  TLightStyle = (lsSpot, lsOmni, lsParallel, lsParallelSpot);
972

973
  {Standard light source.
974
     The standard GLScene light source covers spotlights, omnidirectionnal and
975
     parallel sources (see TLightStyle). 
976
     Lights are colored, have distance attenuation parameters and are turned
977
     on/off through their Shining property.
978
     Lightsources are managed in a specific object by the TGLScene for rendering
979
     purposes. The maximum number of light source in a scene is limited by the
980
     OpenGL implementation (8 lights are supported under most ICDs), though the
981
     more light you use, the slower rendering may get. If you want to render
982
     many more light/lightsource, you may have to resort to other techniques
983
     like lightmapping. }
984
  TGLLightSource = class(TGLBaseSceneObject)
985
  private
986
    FLightID: Cardinal;
987
    FSpotDirection: TGLCoordinates;
988
    FSpotExponent, FSpotCutOff: Single;
989
    FConstAttenuation, FLinearAttenuation, FQuadraticAttenuation: Single;
990
    FShining: Boolean;
991
    FAmbient, FDiffuse, FSpecular: TGLColor;
992
    FLightStyle: TLightStyle;
993

994
  protected
995
    procedure SetAmbient(AValue: TGLColor);
996
    procedure SetDiffuse(AValue: TGLColor);
997
    procedure SetSpecular(AValue: TGLColor);
998
    procedure SetConstAttenuation(AValue: Single);
999
    procedure SetLinearAttenuation(AValue: Single);
1000
    procedure SetQuadraticAttenuation(AValue: Single);
1001
    procedure SetShining(AValue: Boolean);
1002
    procedure SetSpotDirection(AVector: TGLCoordinates);
1003
    procedure SetSpotExponent(AValue: Single);
1004
    procedure SetSpotCutOff(const val: Single);
1005
    procedure SetLightStyle(const val: TLightStyle);
1006

1007
  public
1008
    constructor Create(AOwner: TComponent); override;
1009
    destructor Destroy; override;
1010
    procedure DoRender(var ARci: TGLRenderContextInfo;
1011
      ARenderSelf, ARenderChildren: Boolean); override;
1012
    // light sources have different handle types than normal scene objects
1013
    function GetHandle(var rci: TGLRenderContextInfo): Cardinal; override;
1014
    function RayCastIntersect(const rayStart, rayVector: TVector;
1015
      intersectPoint: PVector = nil;
1016
      intersectNormal: PVector = nil): Boolean; override;
1017
    procedure CoordinateChanged(Sender: TGLCustomCoordinates); override;
1018
    function GenerateSilhouette(const silhouetteParameters:
1019
      TGLSilhouetteParameters): TGLSilhouette; override;
1020
    property LightID: Cardinal read FLightID;
1021
    function Attenuated: Boolean;
1022
  published
1023
    property Ambient: TGLColor read FAmbient write SetAmbient;
1024
    property ConstAttenuation: Single read FConstAttenuation write
1025
      SetConstAttenuation;
1026
    property Diffuse: TGLColor read FDiffuse write SetDiffuse;
1027
    property LinearAttenuation: Single read FLinearAttenuation write
1028
      SetLinearAttenuation;
1029
    property QuadraticAttenuation: Single read FQuadraticAttenuation write
1030
      SetQuadraticAttenuation;
1031
    property Position;
1032
    property LightStyle: TLightStyle read FLightStyle write SetLightStyle default
1033
      lsSpot;
1034
    property Shining: Boolean read FShining write SetShining default True;
1035
    property Specular: TGLColor read FSpecular write SetSpecular;
1036
    property SpotCutOff: Single read FSpotCutOff write SetSpotCutOff;
1037
    property SpotDirection: TGLCoordinates read FSpotDirection write
1038
      SetSpotDirection;
1039
    property SpotExponent: Single read FSpotExponent write SetSpotExponent;
1040
    property OnProgress;
1041
  end;
1042

1043
  TGLCameraStyle = (csPerspective, csOrthogonal, csOrtho2D, csCustom,
1044
    csInfinitePerspective, csPerspectiveKeepFOV);
1045

1046
  TGLCameraKeepFOVMode = (ckmHorizontalFOV, ckmVerticalFOV);
1047

1048
  TOnCustomPerspective = procedure(const viewport: TRectangle;
1049
    width, height: Integer; DPI: Integer;
1050
    var viewPortRadius: Single) of object;
1051

1052
  {Camera object.
1053
     This object is commonly referred by TGLSceneViewer and defines a position,
1054
     direction, focal length, depth of view... all the properties needed for
1055
     defining a point of view and optical characteristics. }
1056
  TGLCamera = class(TGLBaseSceneObject)
1057
  private
1058
    FFocalLength: Single;
1059
    FDepthOfView: Single;
1060
    FNearPlane: Single; // nearest distance to the camera
1061
    FNearPlaneBias: Single; // scaling bias applied to near plane
1062
    FViewPortRadius: Single; // viewport bounding radius per distance unit
1063
    FTargetObject: TGLBaseSceneObject;
1064
    FLastDirection: TVector; // Not persistent
1065
    FCameraStyle: TGLCameraStyle;
1066
    FKeepFOVMode: TGLCameraKeepFOVMode;
1067
    FSceneScale: Single;
1068
    FDeferredApply: TNotifyEvent;
1069
    FOnCustomPerspective: TOnCustomPerspective;
1070
    FDesign: Boolean;
1071
    FFOVY, FFOVX: Double;
1072
  protected
1073
    procedure Notification(AComponent: TComponent; Operation: TOperation); override;
1074
    procedure SetTargetObject(const val: TGLBaseSceneObject);
1075
    procedure SetDepthOfView(AValue: Single);
1076
    procedure SetFocalLength(AValue: Single);
1077
    procedure SetCameraStyle(const val: TGLCameraStyle);
1078
    procedure SetKeepFOVMode(const val: TGLCameraKeepFOVMode);
1079
    procedure SetSceneScale(value: Single);
1080
    function StoreSceneScale: Boolean;
1081
    procedure SetNearPlaneBias(value: Single);
1082
    function StoreNearPlaneBias: Boolean;
1083
  public
1084
    constructor Create(aOwner: TComponent); override;
1085
    destructor Destroy; override;
1086
    procedure Assign(Source: TPersistent); override;
1087
    {Nearest clipping plane for the frustum.
1088
       This value depends on the FocalLength and DepthOfView fields and
1089
       is calculated to minimize Z-Buffer crawling as suggested by the
1090
       OpenGL documentation. }
1091
    property NearPlane: Single read FNearPlane;
1092
    // Apply camera transformation
1093
    procedure Apply;
1094
    procedure DoRender(var ARci: TGLRenderContextInfo;
1095
      ARenderSelf, ARenderChildren: Boolean); override;
1096
    function RayCastIntersect(const rayStart, rayVector: TVector;
1097
      intersectPoint: PVector = nil;
1098
      intersectNormal: PVector = nil): Boolean; override;
1099
    procedure ApplyPerspective(const AViewport: TRectangle;
1100
      AWidth, AHeight: Integer; ADPI: Integer);
1101
    procedure AutoLeveling(Factor: Single);
1102
    procedure Reset(aSceneBuffer: TGLSceneBuffer);
1103
    // Position the camera so that the whole scene can be seen
1104
    procedure ZoomAll(aSceneBuffer: TGLSceneBuffer);
1105

1106
    procedure RotateObject(obj: TGLBaseSceneObject; pitchDelta, turnDelta: Single; rollDelta: Single = 0);
1107
    procedure RotateTarget(pitchDelta, turnDelta: Single; rollDelta: Single = 0);
1108
    {Change camera's position to make it move around its target.
1109
       If TargetObject is nil, nothing happens. This method helps in quickly
1110
       implementing camera controls. Camera's Up and Direction properties are unchanged.
1111
       Angle deltas are in degrees, camera parent's coordinates should be identity.
1112
       Tip : make the camera a child of a "target" dummycube and make
1113
       it a target the dummycube. Now, to pan across the scene, just move
1114
       the dummycube, to change viewing angle, use this method. }
1115
    procedure MoveAroundTarget(pitchDelta, turnDelta: Single);
1116
    {Change camera's position to make it move all around its target.
1117
       If TargetObject is nil, nothing happens. This method helps in quickly
1118
       implementing camera controls. Camera's Up and Direction properties are changed.
1119
       Angle deltas are in degrees. }
1120
    procedure MoveAllAroundTarget(pitchDelta, turnDelta :Single);
1121
    {Moves the camera in eye space coordinates. }
1122
    procedure MoveInEyeSpace(forwardDistance, rightDistance, upDistance: Single);
1123
    { Moves the target in eye space coordinates. }
1124
    procedure MoveTargetInEyeSpace(forwardDistance, rightDistance, upDistance:
1125
      Single);
1126
    { Computes the absolute vector corresponding to the eye-space translations. }
1127
    function AbsoluteEyeSpaceVector(forwardDistance, rightDistance, upDistance:
1128
      Single): TVector;
1129
    { Adjusts distance from camera to target by applying a ratio.
1130
       If TargetObject is nil, nothing happens. This method helps in quickly
1131
       implementing camera controls. Only the camera's position is changed. }
1132
    procedure AdjustDistanceToTarget(distanceRatio: Single);
1133
    { Returns the distance from camera to target.
1134
       If TargetObject is nil, returns 1. }
1135
    function DistanceToTarget: Single;
1136
    { Computes the absolute normalized vector to the camera target.
1137
       If no target is defined, AbsoluteDirection is returned. }
1138
    function AbsoluteVectorToTarget: TVector;
1139
    { Computes the absolute normalized right vector to the camera target.
1140
       If no target is defined, AbsoluteRight is returned. }
1141
    function AbsoluteRightVectorToTarget: TVector;
1142
    { Computes the absolute normalized up vector to the camera target.
1143
       If no target is defined, AbsoluteUpt is returned. }
1144
    function AbsoluteUpVectorToTarget: TVector;
1145
    { Calculate an absolute translation vector from a screen vector.
1146
       Ratio is applied to both screen delta, planeNormal should be the
1147
       translation plane's normal. }
1148
    function ScreenDeltaToVector(deltaX, deltaY: Integer; ratio: Single;
1149
      const planeNormal: TVector): TVector;
1150
    { Same as ScreenDeltaToVector but optimized for XY plane. }
1151
    function ScreenDeltaToVectorXY(deltaX, deltaY: Integer; ratio: Single):
1152
      TVector;
1153
    { Same as ScreenDeltaToVector but optimized for XZ plane. }
1154
    function ScreenDeltaToVectorXZ(deltaX, deltaY: Integer; ratio: Single):
1155
      TVector;
1156
    { Same as ScreenDeltaToVector but optimized for YZ plane. }
1157
    function ScreenDeltaToVectorYZ(deltaX, deltaY: Integer; ratio: Single):
1158
      TVector;
1159
    { Returns true if a point is in front of the camera. }
1160
    function PointInFront(const point: TVector): boolean; overload;
1161
    { Calculates the field of view in degrees, given a viewport dimension
1162
    (width or height). F.i. you may wish to use the minimum of the two. }
1163
    function GetFieldOfView(const AViewportDimension: single): single;
1164
    { Sets the FocalLength in degrees, given a field of view and a viewport
1165
    dimension (width or height). }
1166
    procedure SetFieldOfView(const AFieldOfView, AViewportDimension: single);
1167
  published
1168
     
1169
    { Depth of field/view.
1170
       Adjusts the maximum distance, beyond which objects will be clipped
1171
       (ie. not visisble).
1172
       You must adjust this value if you are experiencing disappearing
1173
       objects (increase the value) of Z-Buffer crawling (decrease the
1174
       value). Z-Buffer crawling happens when depth of view is too large
1175
       and the Z-Buffer precision cannot account for all that depth
1176
       accurately : objects farther overlap closer objects and vice-versa.
1177
       Note that this value is ignored in cSOrtho2D mode. }
1178
    property DepthOfView: Single read FDepthOfView write SetDepthOfView;
1179
    { Focal Length of the camera.
1180
       Adjusting this value allows for lens zooming effects (use SceneScale
1181
       for linear zooming). This property affects near/far planes clipping. }
1182
    property FocalLength: Single read FFocalLength write SetFocalLength;
1183
    { Scene scaling for camera point.
1184
       This is a linear 2D scaling of the camera's output, allows for
1185
       linear zooming (use FocalLength for lens zooming). }
1186
    property SceneScale: Single read FSceneScale write SetSceneScale stored
1187
      StoreSceneScale;
1188
    { Scaling bias applied to near-plane calculation.
1189
       Values inferior to one will move the nearplane nearer, and also
1190
       reduce medium/long range Z-Buffer precision, values superior
1191
       to one will move the nearplane farther, and also improve medium/long
1192
       range Z-Buffer precision. }
1193
    property NearPlaneBias: Single read FNearPlaneBias write SetNearPlaneBias
1194
      stored StoreNearPlaneBias;
1195
    { If set, camera will point to this object.
1196
       When camera is pointing an object, the Direction vector is ignored
1197
       and the Up vector is used as an absolute vector to the up. }
1198
    property TargetObject: TGLBaseSceneObject read FTargetObject write
1199
      SetTargetObject;
1200
    { Adjust the camera style.
1201
       Three styles are available : 
1202
        csPerspective, the default value for perspective projection
1203
        csOrthogonal, for orthogonal (or isometric) projection.
1204
        csOrtho2D, setups orthogonal 2D projection in which 1 unit
1205
          (in x or y) represents 1 pixel.
1206
        csInfinitePerspective, for perspective view without depth limit.
1207
        csKeepCamAnglePerspective, for perspective view with keeping aspect on view resize.
1208
        csCustom, setup is deferred to the OnCustomPerspective event.
1209
         }
1210
    property CameraStyle: TGLCameraStyle read FCameraStyle write SetCameraStyle
1211
      default csPerspective;
1212

1213
    { Keep camera angle mode. 
1214
       When CameraStyle is csKeepCamAnglePerspective, select which camera angle you want to keep.
1215
        kaHeight, for Keep Height oriented camera angle
1216
        kaWidth,  for Keep Width oriented camera angle
1217
       }
1218
    property KeepFOVMode: TGLCameraKeepFOVMode read FKeepFOVMode
1219
      write SetKeepFOVMode default ckmHorizontalFOV;
1220

1221
    { Custom perspective event.
1222
       This event allows you to specify your custom perpective, either
1223
       with a glFrustrum, a glOrtho or whatever method suits you. 
1224
       You must compute viewPortRadius for culling to work. 
1225
       This event is only called if CameraStyle is csCustom. }
1226
    property OnCustomPerspective: TOnCustomPerspective read FOnCustomPerspective
1227
      write FOnCustomPerspective;
1228

1229
    property Position;
1230
    property Direction;
1231
    property Up;
1232
    property OnProgress;
1233
  end;
1234

1235
  // TGLScene
1236
  //
1237
  { Scene object.
1238
     The scene contains the scene description (lights, geometry...), which is
1239
     basicly a hierarchical scene graph made of TGLBaseSceneObject. It will
1240
     usually contain one or more TGLCamera object, which can be referred by
1241
     a Viewer component for rendering purposes.
1242
     The scene's objects can be accessed directly from Delphi code (as regular
1243
     components), but those are edited with a specific editor (double-click
1244
     on the TGLScene component at design-time to invoke it). To add objects
1245
     at runtime, use the AddNewChild method of TGLBaseSceneObject. }
1246
  TGLScene = class(TGLUpdateAbleComponent)
1247
  private
1248
     
1249
    FUpdateCount: Integer;
1250
    FObjects: TGLSceneRootObject;
1251
    FBaseContext: TGLContext; //reference, not owned!
1252
    FLights, FBuffers: TPersistentObjectList;
1253
    FCurrentGLCamera: TGLCamera;
1254
    FCurrentBuffer: TGLSceneBuffer;
1255
    FObjectsSorting: TGLObjectsSorting;
1256
    FVisibilityCulling: TGLVisibilityCulling;
1257
    FOnBeforeProgress: TGLProgressEvent;
1258
    FOnProgress: TGLProgressEvent;
1259
    FCurrentDeltaTime: Double;
1260
    FInitializableObjects: TGLInitializableObjectList;
1261

1262
  protected
1263
     
1264
    procedure AddLight(aLight: TGLLightSource);
1265
    procedure RemoveLight(aLight: TGLLightSource);
1266
    // Adds all lights in the subtree (anObj included)
1267
    procedure AddLights(anObj: TGLBaseSceneObject);
1268
    // Removes all lights in the subtree (anObj included)
1269
    procedure RemoveLights(anObj: TGLBaseSceneObject);
1270

1271
    procedure GetChildren(AProc: TGetChildProc; Root: TComponent); override;
1272
    procedure SetChildOrder(AChild: TComponent; Order: Integer); override;
1273
    procedure SetObjectsSorting(const val: TGLObjectsSorting);
1274
    procedure SetVisibilityCulling(const val: TGLVisibilityCulling);
1275

1276
    procedure ReadState(Reader: TReader); override;
1277
  public
1278
     
1279
    constructor Create(AOwner: TComponent); override;
1280
    destructor Destroy; override;
1281

1282
    procedure BeginUpdate;
1283
    procedure EndUpdate;
1284
    function IsUpdating: Boolean;
1285

1286
    procedure AddBuffer(aBuffer: TGLSceneBuffer);
1287
    procedure RemoveBuffer(aBuffer: TGLSceneBuffer);
1288
    procedure SetupLights(maxLights: Integer);
1289
    procedure NotifyChange(Sender: TObject); override;
1290
    procedure Progress(const deltaTime, newTime: Double);
1291

1292
    function FindSceneObject(const AName: string): TGLBaseSceneObject;
1293
    { Calculates, finds and returns the first object intercepted by the ray.
1294
       Returns nil if no intersection was found. This function will be
1295
       accurate only for objects that overrided their RayCastIntersect
1296
       method with accurate code, otherwise, bounding sphere intersections
1297
       will be returned. }
1298
    function RayCastIntersect(const rayStart, rayVector: TVector;
1299
      intersectPoint: PVector = nil;
1300
      intersectNormal: PVector = nil): TGLBaseSceneObject; virtual;
1301

1302
    procedure ShutdownAllLights;
1303

1304
    { Saves the scene to a file (recommended extension : .GLS) }
1305
    procedure SaveToFile(const fileName: string);
1306
    { Load the scene from a file.
1307
       Existing objects/lights/cameras are freed, then the file is loaded. 
1308
       Delphi's IDE is not handling this behaviour properly yet, ie. if
1309
       you load a scene in the IDE, objects will be properly loaded, but
1310
       no declare will be placed in the code. }
1311
    procedure LoadFromFile(const fileName: string);
1312

1313
    procedure SaveToStream(aStream: TStream);
1314
    procedure LoadFromStream(aStream: TStream);
1315

1316
    { Saves the scene to a text file }
1317
    procedure SaveToTextFile(const fileName: string);
1318
    { Load the scene from a text files.
1319
       See LoadFromFile for details. }
1320
    procedure LoadFromTextFile(const fileName: string);
1321

1322
    property CurrentGLCamera: TGLCamera read FCurrentGLCamera;
1323
    property Lights: TPersistentObjectList read FLights;
1324
    property Objects: TGLSceneRootObject read FObjects;
1325
    property CurrentBuffer: TGLSceneBuffer read FCurrentBuffer;
1326

1327
    { List of objects that request to be initialized when rendering context is active.
1328
      They are removed automaticly from this list once initialized. }
1329
    property InitializableObjects: TGLInitializableObjectList read
1330
      FInitializableObjects;
1331
    property CurrentDeltaTime: Double read FCurrentDeltaTime;
1332
  published
1333
     
1334
    { Defines default ObjectSorting option for scene objects. }
1335
    property ObjectsSorting: TGLObjectsSorting read FObjectsSorting write
1336
      SetObjectsSorting default osRenderBlendedLast;
1337
    { Defines default VisibilityCulling option for scene objects. }
1338
    property VisibilityCulling: TGLVisibilityCulling read FVisibilityCulling
1339
      write SetVisibilityCulling default vcNone;
1340
    property OnBeforeProgress: TGLProgressEvent read FOnBeforeProgress write FOnBeforeProgress;
1341
    property OnProgress: TGLProgressEvent read FOnProgress write FOnProgress;
1342
  end;
1343

1344
  // TFogMode
1345
  //
1346
  TFogMode = (fmLinear, fmExp, fmExp2);
1347

1348
  // TFogDistance
1349
  //
1350
  { Fog distance calculation mode.
1351
      
1352
      fdDefault: let OpenGL use its default formula
1353
      fdEyeRadial: uses radial "true" distance (best quality)
1354
      fdEyePlane: uses the distance to the projection plane
1355
                 (same as Z-Buffer, faster)
1356
      Requires support of GL_NV_fog_distance extension, otherwise,
1357
     it is ignored. }
1358
  TFogDistance = (fdDefault, fdEyeRadial, fdEyePlane);
1359

1360
  // TGLFogEnvironment
1361
  //
1362
  { Parameters for fog environment in a scene.
1363
     The fog descibed by this object is a distance-based fog, ie. the "intensity"
1364
     of the fog is given by a formula depending solely on the distance, this
1365
     intensity is used for blending to a fixed color. }
1366
  TGLFogEnvironment = class(TGLUpdateAbleObject)
1367
  private
1368
     
1369
    FSceneBuffer: TGLSceneBuffer;
1370
    FFogColor: TGLColor; // alpha value means the fog density
1371
    FFogStart, FFogEnd: Single;
1372
    FFogMode: TFogMode;
1373
    FFogDistance: TFogDistance;
1374

1375
  protected
1376
     
1377
    procedure SetFogColor(Value: TGLColor);
1378
    procedure SetFogStart(Value: Single);
1379
    procedure SetFogEnd(Value: Single);
1380
    procedure SetFogMode(Value: TFogMode);
1381
    procedure SetFogDistance(const val: TFogDistance);
1382

1383
  public
1384
     
1385
    constructor Create(AOwner: TPersistent); override;
1386
    destructor Destroy; override;
1387

1388
    procedure ApplyFog;
1389
    procedure Assign(Source: TPersistent); override;
1390

1391
    function IsAtDefaultValues: Boolean;
1392

1393
  published
1394
     
1395
    { Color of the fog when it is at 100% intensity. }
1396
    property FogColor: TGLColor read FFogColor write SetFogColor;
1397
    { Minimum distance for fog, what is closer is not affected. }
1398
    property FogStart: Single read FFogStart write SetFogStart;
1399
    { Maximum distance for fog, what is farther is at 100% fog intensity. }
1400
    property FogEnd: Single read FFogEnd write SetFogEnd;
1401
    { The formula used for converting distance to fog intensity. }
1402
    property FogMode: TFogMode read FFogMode write SetFogMode default fmLinear;
1403
    { Adjusts the formula used for calculating fog distances.
1404
       This option is honoured if and only if the OpenGL ICD supports the
1405
       GL_NV_fog_distance extension, otherwise, it is ignored. 
1406
           fdDefault: let OpenGL use its default formula
1407
           fdEyeRadial: uses radial "true" distance (best quality)
1408
           fdEyePlane: uses the distance to the projection plane
1409
             (same as Z-Buffer, faster)
1410
         }
1411
    property FogDistance: TFogDistance read FFogDistance write SetFogDistance
1412
      default fdDefault;
1413
  end;
1414

1415
  // TGLDepthPrecision
1416
  //
1417
  TGLDepthPrecision = (dpDefault, dp16bits, dp24bits, dp32bits);
1418

1419
  // TGLColorDepth
1420
  //
1421
  TGLColorDepth = (cdDefault, cd8bits, cd16bits, cd24bits, cdFloat64bits,
1422
    cdFloat128bits); // float_type
1423

1424
  // TGLShadeModel
1425
  //
1426
  TGLShadeModel = (smDefault, smSmooth, smFlat);
1427

1428
  // TGLSceneBuffer
1429
  //
1430
  { Encapsulates an OpenGL frame/rendering buffer. }
1431
  TGLSceneBuffer = class(TGLUpdateAbleObject)
1432
  private
1433
     
1434
    // Internal state
1435
    FRendering: Boolean;
1436
    FRenderingContext: TGLContext;
1437
    FAfterRenderEffects: TPersistentObjectList;
1438
    FViewMatrixStack: array of TMatrix;
1439
    FProjectionMatrixStack: array of TMatrix;
1440
    FBaseProjectionMatrix: TMatrix;
1441
    FCameraAbsolutePosition: TVector;
1442
    FViewPort: TRectangle;
1443
    FSelector: TGLBaseSelectTechnique;
1444

1445
    // Options & User Properties
1446
    FFaceCulling, FFogEnable, FLighting: Boolean;
1447
    FDepthTest: Boolean;
1448
    FBackgroundColor: TColor;
1449
    FBackgroundAlpha: Single;
1450
    FAmbientColor: TGLColor;
1451
    FAntiAliasing: TGLAntiAliasing;
1452
    FDepthPrecision: TGLDepthPrecision;
1453
    FColorDepth: TGLColorDepth;
1454
    FContextOptions: TContextOptions;
1455
    FShadeModel: TGLShadeModel;
1456
    FRenderDPI: Integer;
1457
    FFogEnvironment: TGLFogEnvironment;
1458
    FAccumBufferBits: Integer;
1459
    FLayer: TGLContextLayer;
1460

1461
    // Cameras
1462
    FCamera: TGLCamera;
1463

1464
    // Freezing
1465
    FFreezeBuffer: Pointer;
1466
    FFreezed: Boolean;
1467
    FFreezedViewPort: TRectangle;
1468

1469
    // Monitoring
1470
    FFrameCount: Longint;
1471
    FFramesPerSecond: Single;
1472
    FFirstPerfCounter: Int64;
1473
    FLastFrameTime: Single;
1474

1475
    // Events
1476
    FOnChange: TNotifyEvent;
1477
    FOnStructuralChange: TNotifyEvent;
1478
    FOnPrepareGLContext: TNotifyEvent;
1479

1480
    FBeforeRender: TNotifyEvent;
1481
    FViewerBeforeRender: TNotifyEvent;
1482
    FPostRender: TNotifyEvent;
1483
    FAfterRender: TNotifyEvent;
1484
    FInitiateRendering: TDirectRenderEvent;
1485
    FWrapUpRendering: TDirectRenderEvent;
1486
    procedure SetLayer(const Value: TGLContextLayer);
1487

1488
  protected
1489
     
1490
    procedure SetBackgroundColor(AColor: TColor);
1491
    procedure SetBackgroundAlpha(alpha: Single);
1492
    procedure SetAmbientColor(AColor: TGLColor);
1493
    function GetLimit(Which: TLimitType): Integer;
1494
    procedure SetCamera(ACamera: TGLCamera);
1495
    procedure SetContextOptions(Options: TContextOptions);
1496
    procedure SetDepthTest(AValue: Boolean);
1497
    procedure SetFaceCulling(AValue: Boolean);
1498
    procedure SetLighting(AValue: Boolean);
1499
    procedure SetAntiAliasing(const val: TGLAntiAliasing);
1500
    procedure SetDepthPrecision(const val: TGLDepthPrecision);
1501
    procedure SetColorDepth(const val: TGLColorDepth);
1502
    procedure SetShadeModel(const val: TGLShadeModel);
1503
    procedure SetFogEnable(AValue: Boolean);
1504
    procedure SetGLFogEnvironment(AValue: TGLFogEnvironment);
1505
    function StoreFog: Boolean;
1506
    procedure SetAccumBufferBits(const val: Integer);
1507

1508
    procedure PrepareRenderingMatrices(const aViewPort: TRectangle;
1509
      resolution: Integer; pickingRect: PGLRect = nil);
1510
    procedure DoBaseRender(const aViewPort: TRectangle; resolution: Integer;
1511
      drawState: TDrawState; baseObject: TGLBaseSceneObject);
1512

1513
    procedure SetupRenderingContext(context: TGLContext);
1514
    procedure SetupRCOptions(context: TGLContext);
1515
    procedure PrepareGLContext;
1516

1517
    procedure DoChange;
1518
    procedure DoStructuralChange;
1519

1520
    // DPI for current/last render
1521
    property RenderDPI: Integer read FRenderDPI;
1522

1523
    property OnPrepareGLContext: TNotifyEvent read FOnPrepareGLContext write
1524
      FOnPrepareGLContext;
1525

1526
  public
1527
     
1528
    constructor Create(AOwner: TPersistent); override;
1529
    destructor Destroy; override;
1530

1531
    procedure NotifyChange(Sender: TObject); override;
1532

1533
    procedure CreateRC(AWindowHandle: HWND; memoryContext: Boolean;
1534
      BufferCount: integer = 1); overload;
1535
    procedure ClearBuffers;
1536
    procedure DestroyRC;
1537
    function RCInstantiated: Boolean;
1538
    procedure Resize(newLeft, newTop, newWidth, newHeight: Integer);
1539
    // Indicates hardware acceleration support
1540
    function Acceleration: TGLContextAcceleration;
1541

1542
    // ViewPort for current/last render
1543
    property ViewPort: TRectangle read FViewPort;
1544

1545
    // Fills the PickList with objects in Rect area
1546
    procedure PickObjects(const rect: TGLRect; pickList: TGLPickList;
1547
      objectCountGuess: Integer);
1548
    { Returns a PickList with objects in Rect area.
1549
       Returned list should be freed by caller. 
1550
       Objects are sorted by depth (nearest objects first). }
1551
    function GetPickedObjects(const rect: TGLRect; objectCountGuess: Integer =
1552
      64): TGLPickList;
1553
    // Returns the nearest object at x, y coordinates or nil if there is none
1554
    function GetPickedObject(x, y: Integer): TGLBaseSceneObject;
1555

1556
    // Returns the color of the pixel at x, y in the frame buffer
1557
    function GetPixelColor(x, y: Integer): TColor;
1558
    { Returns the raw depth (Z buffer) of the pixel at x, y in the frame buffer.
1559
       This value does not map to the actual eye-object distance, but to
1560
       a depth buffer value in the [0; 1] range. }
1561
    function GetPixelDepth(x, y: Integer): Single;
1562
    { Converts a raw depth (Z buffer value) to frustrum distance.
1563
       This calculation is only accurate for the pixel at the centre of the viewer,
1564
       because it does not take into account that the corners of the frustrum
1565
       are further from the eye than its centre. }
1566
    function PixelDepthToDistance(aDepth: Single): Single;
1567
    { Converts a raw depth (Z buffer value) to world distance.
1568
       It also compensates for the fact that the corners of the frustrum
1569
       are further from the eye, than its centre.}
1570
    function PixelToDistance(x, y: integer): Single;
1571
    { Design time notification }
1572
    procedure NotifyMouseMove(Shift: TShiftState; X, Y: Integer);
1573

1574
    { Renders the scene on the viewer.
1575
       You do not need to call this method, unless you explicitly want a
1576
       render at a specific time. If you just want the control to get
1577
       refreshed, use Invalidate instead. }
1578
    procedure Render(baseObject: TGLBaseSceneObject); overload;
1579
    procedure Render; overload;
1580
    procedure RenderScene(aScene: TGLScene;
1581
      const viewPortSizeX, viewPortSizeY: Integer;
1582
      drawState: TDrawState;
1583
      baseObject: TGLBaseSceneObject);
1584
    { Render the scene to a bitmap at given DPI.
1585
      DPI = "dots per inch".
1586
      The "magic" DPI of the screen is 96 under Windows. }
1587
    procedure RenderToBitmap(ABitmap: TGLBitmap; DPI: Integer = 0);
1588
    { Render the scene to a bitmap at given DPI and saves it to a file.
1589
       DPI = "dots per inch".
1590
       The "magic" DPI of the screen is 96 under Windows. }
1591
    procedure RenderToFile(const AFile: string; DPI: Integer = 0); overload;
1592
    { Renders to bitmap of given size, then saves it to a file.
1593
       DPI is adjusted to make the bitmap similar to the viewer. }
1594
    procedure RenderToFile(const AFile: string; bmpWidth, bmpHeight: Integer);
1595
      overload;
1596
    { Creates a TGLBitmap32 that is a snapshot of current OpenGL content.
1597
       When possible, use this function instead of RenderToBitmap, it won't
1598
       request a redraw and will be significantly faster.
1599
       The returned TGLBitmap32 should be freed by calling code. }
1600
    function CreateSnapShot: TGLImage;
1601
    { Creates a VCL bitmap that is a snapshot of current OpenGL content. }
1602
    function CreateSnapShotBitmap: TGLBitmap;
1603
    procedure CopyToTexture(aTexture: TGLTexture); overload;
1604
    procedure CopyToTexture(aTexture: TGLTexture; xSrc, ySrc, AWidth, AHeight:
1605
      Integer;
1606
      xDest, yDest: Integer; glCubeFace: TGLEnum = 0); overload;
1607
    { Save as raw float data to a file }
1608
    procedure SaveAsFloatToFile(const aFilename: string);
1609
    { Event reserved for viewer-specific uses.  }
1610
    property ViewerBeforeRender: TNotifyEvent read FViewerBeforeRender write
1611
      FViewerBeforeRender stored False;
1612
    procedure SetViewPort(X, Y, W, H: Integer);
1613
    function Width: Integer;
1614
    function Height: Integer;
1615

1616
    { Indicates if the Viewer is "frozen". }
1617
    property Freezed: Boolean read FFreezed;
1618
    { Freezes rendering leaving the last rendered scene on the buffer. This
1619
       is usefull in windowed applications for temporarily stoping rendering
1620
       (when moving the window, for example). }
1621
    procedure Freeze;
1622
    { Restarts rendering after it was freezed. }
1623
    procedure Melt;
1624

1625
    { Displays a window with info on current OpenGL ICD and context. }
1626
    procedure ShowInfo(Modal: boolean = false);
1627

1628
    { Currently Rendering? }
1629
    property Rendering: Boolean read FRendering;
1630

1631
    { Adjusts background alpha channel. }
1632
    property BackgroundAlpha: Single read FBackgroundAlpha write
1633
      SetBackgroundAlpha;
1634
    { Returns the projection matrix in use or used for the last rendering. }
1635
    function ProjectionMatrix: TMatrix; deprecated;
1636
    { Returns the view matrix in use or used for the last rendering. }
1637
    function ViewMatrix: TMatrix; deprecated;
1638
    function ModelMatrix: TMatrix; deprecated;
1639

1640
    { Returns the base projection matrix in use or used for the last rendering.
1641
       The "base" projection is (as of now) either identity or the pick
1642
       matrix, ie. it is the matrix on which the perspective or orthogonal
1643
       matrix gets applied. }
1644
    property BaseProjectionMatrix: TMatrix read FBaseProjectionMatrix;
1645

1646
    { Back up current View matrix and replace it with newMatrix.
1647
       This method has no effect on the OpenGL matrix, only on the Buffer's
1648
       matrix, and is intended for special effects rendering. }
1649
    procedure PushViewMatrix(const newMatrix: TMatrix); deprecated;
1650
    { Restore a View matrix previously pushed. }
1651
    procedure PopViewMatrix; deprecated;
1652

1653
    procedure PushProjectionMatrix(const newMatrix: TMatrix); deprecated;
1654
    procedure PopProjectionMatrix;  deprecated;
1655

1656
    { Converts a screen pixel coordinate into 3D coordinates for orthogonal projection.
1657
       This function accepts standard canvas coordinates, with (0,0) being
1658
       the top left corner, and returns, when the camera is in orthogonal
1659
       mode, the corresponding 3D world point that is in the camera's plane. }
1660
    function OrthoScreenToWorld(screenX, screenY: Integer): TAffineVector;
1661
      overload;
1662
    { Converts a screen coordinate into world (3D) coordinates.
1663
       This methods wraps a call to gluUnProject.
1664
       Note that screen coord (0,0) is the lower left corner. }
1665
    function ScreenToWorld(const aPoint: TAffineVector): TAffineVector; overload;
1666
    function ScreenToWorld(const aPoint: TVector): TVector; overload;
1667
    { Converts a screen pixel coordinate into 3D world coordinates.
1668
       This function accepts standard canvas coordinates, with (0,0) being
1669
       the top left corner. }
1670
    function ScreenToWorld(screenX, screenY: Integer): TAffineVector; overload;
1671
    { Converts an absolute world coordinate into screen coordinate.
1672
       This methods wraps a call to gluProject.
1673
       Note that screen coord (0,0) is the lower left corner. }
1674
    function WorldToScreen(const aPoint: TAffineVector): TAffineVector;
1675
      overload;
1676
    function WorldToScreen(const aPoint: TVector): TVector; overload;
1677
    { Converts a set of point absolute world coordinates into screen coordinates. }
1678
    procedure WorldToScreen(points: PVector; nbPoints: Integer); overload;
1679
    { Calculates the 3D vector corresponding to a 2D screen coordinate.
1680
       The vector originates from the camera's absolute position and is
1681
       expressed in absolute coordinates.
1682
       Note that screen coord (0,0) is the lower left corner. }
1683
    function ScreenToVector(const aPoint: TAffineVector): TAffineVector;
1684
      overload;
1685
    function ScreenToVector(const aPoint: TVector): TVector; overload;
1686
    function ScreenToVector(const x, y: Integer): TVector; overload;
1687
    { Calculates the 2D screen coordinate of a vector from the camera's
1688
       absolute position and is expressed in absolute coordinates.
1689
       Note that screen coord (0,0) is the lower left corner. }
1690
    function VectorToScreen(const VectToCam: TAffineVector): TAffineVector;
1691
    { Calculates intersection between a plane and screen vector.
1692
       If an intersection is found, returns True and places result in
1693
       intersectPoint. }
1694
    function ScreenVectorIntersectWithPlane(
1695
      const aScreenPoint: TVector;
1696
      const planePoint, planeNormal: TVector;
1697
      var intersectPoint: TVector): Boolean;
1698
    { Calculates intersection between plane XY and screen vector.
1699
       If an intersection is found, returns True and places result in
1700
       intersectPoint. }
1701
    function ScreenVectorIntersectWithPlaneXY(
1702
      const aScreenPoint: TVector; const z: Single;
1703
      var intersectPoint: TVector): Boolean;
1704
    { Calculates intersection between plane YZ and screen vector.
1705
       If an intersection is found, returns True and places result in
1706
       intersectPoint. }
1707
    function ScreenVectorIntersectWithPlaneYZ(
1708
      const aScreenPoint: TVector; const x: Single;
1709
      var intersectPoint: TVector): Boolean;
1710
    { Calculates intersection between plane XZ and screen vector.
1711
       If an intersection is found, returns True and places result in
1712
       intersectPoint. }
1713
    function ScreenVectorIntersectWithPlaneXZ(
1714
      const aScreenPoint: TVector; const y: Single;
1715
      var intersectPoint: TVector): Boolean;
1716
    { Calculates a 3D coordinate from screen position and ZBuffer.
1717
       This function returns a world absolute coordinate from a 2D point
1718
       in the viewer, the depth being extracted from the ZBuffer data
1719
       (DepthTesting and ZBuffer must be enabled for this function to work). 
1720
       Note that ZBuffer precision is not linear and can be quite low on
1721
       some boards (either from compression or resolution approximations). }
1722
    function PixelRayToWorld(x, y: Integer): TAffineVector;
1723
    { Time (in second) spent to issue rendering order for the last frame.
1724
       Be aware that since execution by the hardware isn't synchronous,
1725
       this value may not be an accurate measurement of the time it took
1726
       to render the last frame, it's a measurement of only the time it
1727
       took to issue rendering orders. }
1728
    property LastFrameTime: Single read FLastFrameTime;
1729
    { Current FramesPerSecond rendering speed.
1730
       You must keep the renderer busy to get accurate figures from this
1731
       property. 
1732
       This is an average value, to reset the counter, call
1733
       ResetPerfomanceMonitor. }
1734
    property FramesPerSecond: Single read FFramesPerSecond;
1735
    { Resets the perfomance monitor and begin a new statistics set.
1736
       See FramesPerSecond. }
1737
    procedure ResetPerformanceMonitor;
1738

1739
    { Retrieve one of the OpenGL limits for the current viewer.
1740
       Limits include max texture size, OpenGL stack depth, etc. }
1741
    property LimitOf[Which: TLimitType]: Integer read GetLimit;
1742
    { Current rendering context.
1743
       The context is a wrapper around platform-specific contexts
1744
       (see TGLContext) and takes care of context activation and handle
1745
       management. }
1746
    property RenderingContext: TGLContext read FRenderingContext;
1747
    { The camera from which the scene is rendered.
1748
       A camera is an object you can add and define in a TGLScene component. }
1749
    property Camera: TGLCamera read FCamera write SetCamera;
1750
    { Specifies the layer plane that the rendering context is bound to. }
1751
    property Layer: TGLContextLayer read FLayer write SetLayer
1752
      default clMainPlane;
1753
  published
1754
     
1755
    { Fog environment options.
1756
       See TGLFogEnvironment. }
1757
    property FogEnvironment: TGLFogEnvironment read FFogEnvironment write
1758
      SetGLFogEnvironment stored StoreFog;
1759
    { Color used for filling the background prior to any rendering. }
1760
    property BackgroundColor: TColor read FBackgroundColor write
1761
      SetBackgroundColor default clBtnFace;
1762
    { Scene ambient color vector.
1763
       This ambient color is defined independantly from all lightsources,
1764
       which can have their own ambient components. }
1765
    property AmbientColor: TGLColor read FAmbientColor write SetAmbientColor;
1766

1767
    { Context options allows to setup specifics of the rendering context.
1768
       Not all contexts support all options. }
1769
    property ContextOptions: TContextOptions read FContextOptions write
1770
      SetContextOptions default [roDoubleBuffer, roRenderToWindow, roDebugContext];
1771
    { Number of precision bits for the accumulation buffer. }
1772
    property AccumBufferBits: Integer read FAccumBufferBits write
1773
      SetAccumBufferBits default 0;
1774
    { DepthTest enabling.
1775
       When DepthTest is enabled, objects closer to the camera will hide
1776
       farther ones (via use of Z-Buffering). 
1777
       When DepthTest is disabled, the latest objects drawn/rendered overlap
1778
       all previous objects, whatever their distance to the camera. 
1779
       Even when DepthTest is enabled, objects may chose to ignore depth
1780
       testing through the osIgnoreDepthBuffer of their ObjectStyle property. }
1781
    property DepthTest: Boolean read FDepthTest write SetDepthTest default True;
1782
    { Enable or disable face culling in the renderer.
1783
       Face culling is used in hidden faces removal algorithms : each face
1784
       is given a normal or 'outside' direction. When face culling is enabled,
1785
       only faces whose normal points towards the observer are rendered. }
1786
    property FaceCulling: Boolean read FFaceCulling write SetFaceCulling default
1787
      True;
1788
    { Toggle to enable or disable the fog settings. }
1789
    property FogEnable: Boolean read FFogEnable write SetFogEnable default
1790
      False;
1791
    { Toggle to enable or disable lighting calculations.
1792
       When lighting is enabled, objects will be lit according to lightsources,
1793
       when lighting is disabled, objects are rendered in their own colors,
1794
       without any shading.
1795
       Lighting does NOT generate shadows in OpenGL. }
1796
    property Lighting: Boolean read FLighting write SetLighting default True;
1797
    { AntiAliasing option.
1798
       Ignored if not hardware supported, currently based on ARB_multisample. }
1799
    property AntiAliasing: TGLAntiAliasing read FAntiAliasing write
1800
      SetAntiAliasing default aaDefault;
1801
    { Depth buffer precision.
1802
       Default is highest available (below and including 24 bits) }
1803
    property DepthPrecision: TGLDepthPrecision read FDepthPrecision write
1804
      SetDepthPrecision default dpDefault;
1805
    { Color buffer depth.
1806
       Default depth buffer is highest available (below and including 24 bits) }
1807
    property ColorDepth: TGLColorDepth read FColorDepth write SetColorDepth
1808
      default cdDefault;
1809
    { Shade model.
1810
       Default is "Smooth". }
1811
    property ShadeModel: TGLShadeModel read FShadeModel write SetShadeModel
1812
      default smDefault;
1813

1814
    { Indicates a change in the scene or buffer options.
1815
       A simple re-render is enough to take into account the changes. }
1816
    property OnChange: TNotifyEvent read FOnChange write FOnChange stored False;
1817
    { Indicates a structural change in the scene or buffer options.
1818
       A reconstruction of the RC is necessary to take into account the
1819
       changes (this may lead to a driver switch or lengthy operations). }
1820
    property OnStructuralChange: TNotifyEvent read FOnStructuralChange write
1821
      FOnStructuralChange stored False;
1822

1823
    { Triggered before the scene's objects get rendered.
1824
       You may use this event to execute your own OpenGL rendering
1825
       (usually background stuff). }
1826
    property BeforeRender: TNotifyEvent read FBeforeRender write FBeforeRender
1827
      stored False;
1828
    { Triggered after BeforeRender, before rendering objects.
1829
       This one is fired after the rci has been initialized and can be used
1830
       to alter it or perform early renderings that require an rci,
1831
       the Sender is the buffer. }
1832
    property InitiateRendering: TDirectRenderEvent read FInitiateRendering write
1833
      FInitiateRendering stored False;
1834
    { Triggered after rendering all scene objects, before PostRender.
1835
       This is the last point after which the rci becomes unavailable,
1836
       the Sender is the buffer. }
1837
    property WrapUpRendering: TDirectRenderEvent read FWrapUpRendering write
1838
      FWrapUpRendering stored False;
1839
    { Triggered just after all the scene's objects have been rendered.
1840
       The OpenGL context is still active in this event, and you may use it
1841
       to execute your own OpenGL rendering (usually for HUD, 2D overlays
1842
       or after effects). }
1843
    property PostRender: TNotifyEvent read FPostRender write FPostRender stored
1844
      False;
1845
    { Called after rendering.
1846
       You cannot issue OpenGL calls in this event, if you want to do your own
1847
       OpenGL stuff, use the PostRender event. }
1848
    property AfterRender: TNotifyEvent read FAfterRender write FAfterRender
1849
      stored False;
1850
  end;
1851

1852
  // TGLNonVisualViewer
1853
  //
1854
  { Base class for non-visual viewer.
1855
     Non-visual viewer may actually render visuals, but they are non-visual
1856
     (ie. non interactive) at design time. Such viewers include memory
1857
     or full-screen viewers. }
1858
  TGLNonVisualViewer = class(TComponent)
1859
  private
1860
     
1861
    FBuffer: TGLSceneBuffer;
1862
    FWidth, FHeight: Integer;
1863
    FCubeMapRotIdx: Integer;
1864
    FCubeMapZNear, FCubeMapZFar: Single;
1865
    FCubeMapTranslation: TAffineVector;
1866
    //FCreateTexture : Boolean;
1867

1868
  protected
1869
     
1870
    procedure SetBeforeRender(const val: TNotifyEvent);
1871
    function GetBeforeRender: TNotifyEvent;
1872
    procedure SetPostRender(const val: TNotifyEvent);
1873
    function GetPostRender: TNotifyEvent;
1874
    procedure SetAfterRender(const val: TNotifyEvent);
1875
    function GetAfterRender: TNotifyEvent;
1876
    procedure SetCamera(const val: TGLCamera);
1877
    function GetCamera: TGLCamera;
1878
    procedure SetBuffer(const val: TGLSceneBuffer);
1879
    procedure SetWidth(const val: Integer);
1880
    procedure SetHeight(const val: Integer);
1881

1882
    procedure SetupCubeMapCamera(Sender: TObject);
1883
    procedure DoOnPrepareGLContext(Sender: TObject);
1884
    procedure PrepareGLContext; dynamic;
1885
    procedure DoBufferChange(Sender: TObject); virtual;
1886
    procedure DoBufferStructuralChange(Sender: TObject); virtual;
1887

1888
  public
1889
     
1890
    constructor Create(AOwner: TComponent); override;
1891
    destructor Destroy; override;
1892

1893
    procedure Notification(AComponent: TComponent; Operation: TOperation);
1894
      override;
1895

1896
    procedure Render(baseObject: TGLBaseSceneObject = nil); virtual; abstract;
1897
    procedure CopyToTexture(aTexture: TGLTexture); overload; virtual;
1898
    procedure CopyToTexture(aTexture: TGLTexture; xSrc, ySrc, width, height:
1899
      Integer;
1900
      xDest, yDest: Integer); overload;
1901
    { CopyToTexture for Multiple-Render-Target }
1902
    procedure CopyToTextureMRT(aTexture: TGLTexture; BufferIndex: integer);
1903
      overload; virtual;
1904
    procedure CopyToTextureMRT(aTexture: TGLTexture; xSrc, ySrc, width, height:
1905
      Integer;
1906
      xDest, yDest: Integer; BufferIndex: integer); overload;
1907
    { Renders the 6 texture maps from a scene.
1908
       The viewer is used to render the 6 images, one for each face
1909
       of the cube, from the absolute position of the camera.
1910
       This does NOT alter the content of the Pictures in the image,
1911
       and will only change or define the content of textures as
1912
       registered by OpenGL. }
1913
    procedure RenderCubeMapTextures(cubeMapTexture: TGLTexture;
1914
      zNear: Single = 0;
1915
      zFar: Single = 0);
1916
  published
1917
    {Camera from which the scene is rendered. }
1918
    property Camera: TGLCamera read GetCamera write SetCamera;
1919
    property Width: Integer read FWidth write SetWidth default 256;
1920
    property Height: Integer read FHeight write SetHeight default 256;
1921
    {Triggered before the scene's objects get rendered.
1922
       You may use this event to execute your own OpenGL rendering. }
1923
    property BeforeRender: TNotifyEvent read GetBeforeRender write SetBeforeRender;
1924
    {Triggered just after all the scene's objects have been rendered.
1925
       The OpenGL context is still active in this event, and you may use it
1926
       to execute your own OpenGL rendering. }
1927
    property PostRender: TNotifyEvent read GetPostRender write SetPostRender;
1928
    { Called after rendering.
1929
       You cannot issue OpenGL calls in this event, if you want to do your own
1930
       OpenGL stuff, use the PostRender event. }
1931
    property AfterRender: TNotifyEvent read GetAfterRender write SetAfterRender;
1932

1933
    { Access to buffer properties. }
1934
    property Buffer: TGLSceneBuffer read FBuffer write SetBuffer;
1935
  end;
1936

1937
  {Component to render a scene to memory only.
1938
     This component curently requires that the OpenGL ICD supports the
1939
     WGL_ARB_pbuffer extension (indirectly). }
1940
  TGLMemoryViewer = class(TGLNonVisualViewer)
1941
  private
1942
    FBufferCount: integer;
1943
    procedure SetBufferCount(const Value: integer);
1944
  public
1945
    constructor Create(AOwner: TComponent); override;
1946
    procedure InstantiateRenderingContext;
1947
    procedure Render(baseObject: TGLBaseSceneObject = nil); override;
1948
  published
1949
    {Set BufferCount > 1 for multiple render targets.
1950
       Users should check if the corresponding extension (GL_ATI_draw_buffers)
1951
       is supported. Current hardware limit is BufferCount = 4. }
1952
    property BufferCount: integer read FBufferCount write SetBufferCount default 1;
1953
  end;
1954

1955
  TInvokeInfoForm = procedure(aSceneBuffer: TGLSceneBuffer; Modal: boolean);
1956

1957
  { Register an event handler triggered by any TGLBaseSceneObject Name change.
1958
     *INCOMPLETE*, currently allows for only 1 (one) event, and is used by
1959
     GLSceneEdit in the IDE. }
1960
procedure RegisterGLBaseSceneObjectNameChangeEvent(notifyEvent: TNotifyEvent);
1961
{Deregister an event handler triggered by any TGLBaseSceneObject Name change.
1962
   See RegisterGLBaseSceneObjectNameChangeEvent. }
1963
procedure DeRegisterGLBaseSceneObjectNameChangeEvent(notifyEvent: TNotifyEvent);
1964
{ Register an event handler triggered by any TGLBehaviour Name change.
1965
   *INCOMPLETE*, currently allows for only 1 (one) event, and is used by
1966
   FBehavioursEditor in the IDE. }
1967
procedure RegisterGLBehaviourNameChangeEvent(notifyEvent: TNotifyEvent);
1968
{ Deregister an event handler triggered by any TGLBaseSceneObject Name change.
1969
   See RegisterGLBaseSceneObjectNameChangeEvent. }
1970
procedure DeRegisterGLBehaviourNameChangeEvent(notifyEvent: TNotifyEvent);
1971

1972
{ Issues OpenGL calls for drawing X, Y, Z axes in a standard style. }
1973
procedure AxesBuildList(var rci: TGLRenderContextInfo; pattern: Word; AxisLen:
1974
  Single);
1975

1976
{Registers the procedure call used to invoke the info form. }
1977
procedure RegisterInfoForm(infoForm: TInvokeInfoForm);
1978
procedure InvokeInfoForm(aSceneBuffer: TGLSceneBuffer; Modal: boolean);
1979

1980
function GetCurrentRenderingObject: TGLBaseSceneObject;
1981

1982
//------------------------------------------------------------------------------
1983
//------------------------------------------------------------------------------
1984
//------------------------------------------------------------------------------
1985
implementation
1986
//------------------------------------------------------------------------------
1987
//------------------------------------------------------------------------------
1988
//------------------------------------------------------------------------------
1989

1990
var
1991
  vCounterFrequency: Int64;
1992
{$IFNDEF GLS_MULTITHREAD}
1993
var
1994
{$ELSE}
1995
threadvar
1996
{$ENDIF}
1997
  vCurrentRenderingObject: TGLBaseSceneObject;
1998

1999
function GetCurrentRenderingObject: TGLBaseSceneObject;
2000
begin
2001
  Result := vCurrentRenderingObject;
2002
end;
2003

2004
  // AxesBuildList
2005
  //
2006

2007
procedure AxesBuildList(var rci: TGLRenderContextInfo; pattern: Word; axisLen:
2008
  Single);
2009
begin
2010
{$IFDEF GLS_OPENGL_DEBUG}
2011
  if GL.GREMEDY_string_marker then
2012
    GL.StringMarkerGREMEDY(13, 'AxesBuildList');
2013
{$ENDIF}
2014
  with rci.GLStates do
2015
  begin
2016
    Disable(stLighting);
2017
    if not rci.ignoreBlendingRequests then
2018
    begin
2019
      Enable(stBlend);
2020
      SetBlendFunc(bfSrcAlpha, bfOneMinusSrcAlpha);
2021
    end;
2022
    LineWidth := 1;
2023
    Enable(stLineStipple);
2024
    LineStippleFactor := 1;
2025
    LineStipplePattern := Pattern;
2026
    DepthWriteMask := True;
2027
    DepthFunc := cfLEqual;
2028
    if rci.bufferDepthTest then
2029
      Enable(stDepthTest);
2030
  end;
2031
  GL.Begin_(GL_LINES);
2032
  GL.Color3f(0.5, 0.0, 0.0);
2033
  GL.Vertex3f(0, 0, 0);
2034
  GL.Vertex3f(-AxisLen, 0, 0);
2035
  GL.Color3f(1.0, 0.0, 0.0);
2036
  GL.Vertex3f(0, 0, 0);
2037
  GL.Vertex3f(AxisLen, 0, 0);
2038
  GL.Color3f(0.0, 0.5, 0.0);
2039
  GL.Vertex3f(0, 0, 0);
2040
  GL.Vertex3f(0, -AxisLen, 0);
2041
  GL.Color3f(0.0, 1.0, 0.0);
2042
  GL.Vertex3f(0, 0, 0);
2043
  GL.Vertex3f(0, AxisLen, 0);
2044
  GL.Color3f(0.0, 0.0, 0.5);
2045
  GL.Vertex3f(0, 0, 0);
2046
  GL.Vertex3f(0, 0, -AxisLen);
2047
  GL.Color3f(0.0, 0.0, 1.0);
2048
  GL.Vertex3f(0, 0, 0);
2049
  GL.Vertex3f(0, 0, AxisLen);
2050
  GL.End_;
2051
end;
2052

2053
// RegisterInfoForm
2054
//
2055
var
2056
  vInfoForm: TInvokeInfoForm = nil;
2057

2058
procedure RegisterInfoForm(infoForm: TInvokeInfoForm);
2059
begin
2060
  vInfoForm := infoForm;
2061
end;
2062

2063
// InvokeInfoForm
2064
//
2065

2066
procedure InvokeInfoForm(aSceneBuffer: TGLSceneBuffer; Modal: boolean);
2067
begin
2068
  if Assigned(vInfoForm) then
2069
    vInfoForm(aSceneBuffer, Modal)
2070
  else
2071
    InformationDlg('InfoForm not available.');
2072
end;
2073

2074
//------------------ internal global routines ----------------------------------
2075

2076
var
2077
  vGLBaseSceneObjectNameChangeEvent: TNotifyEvent;
2078
  vGLBehaviourNameChangeEvent: TNotifyEvent;
2079

2080
  // RegisterGLBaseSceneObjectNameChangeEvent
2081
  //
2082

2083
procedure RegisterGLBaseSceneObjectNameChangeEvent(notifyEvent: TNotifyEvent);
2084
begin
2085
  vGLBaseSceneObjectNameChangeEvent := notifyEvent;
2086
end;
2087

2088
// DeRegisterGLBaseSceneObjectNameChangeEvent
2089
//
2090

2091
procedure DeRegisterGLBaseSceneObjectNameChangeEvent(notifyEvent: TNotifyEvent);
2092
begin
2093
  vGLBaseSceneObjectNameChangeEvent := nil;
2094
end;
2095

2096
// RegisterGLBehaviourNameChangeEvent
2097
//
2098

2099
procedure RegisterGLBehaviourNameChangeEvent(notifyEvent: TNotifyEvent);
2100
begin
2101
  vGLBehaviourNameChangeEvent := notifyEvent;
2102
end;
2103

2104
// DeRegisterGLBehaviourNameChangeEvent
2105
//
2106

2107
procedure DeRegisterGLBehaviourNameChangeEvent(notifyEvent: TNotifyEvent);
2108
begin
2109
  vGLBehaviourNameChangeEvent := nil;
2110
end;
2111

2112
// ------------------
2113
// ------------------ TGLBaseSceneObject ------------------
2114
// ------------------
2115

2116
// Create
2117
//
2118

2119
constructor TGLBaseSceneObject.Create(AOwner: TComponent);
2120
begin
2121
  inherited Create(AOwner);
2122
  FObjectStyle := [];
2123
  FChanges := [ocTransformation, ocStructure,
2124
    ocAbsoluteMatrix, ocInvAbsoluteMatrix];
2125
  FPosition := TGLCoordinates.CreateInitialized(Self, NullHmgPoint, csPoint);
2126
  FRotation := TGLCoordinates.CreateInitialized(Self, NullHmgVector, csVector);
2127
  FDirection := TGLCoordinates.CreateInitialized(Self, ZHmgVector, csVector);
2128
  FUp := TGLCoordinates.CreateInitialized(Self, YHmgVector, csVector);
2129
  FScaling := TGLCoordinates.CreateInitialized(Self, XYZHmgVector, csVector);
2130
  GetMem(FLocalMatrix, SizeOf(TMatrix));
2131
  FLocalMatrix^ := IdentityHmgMatrix;
2132
  FVisible := True;
2133
  FPickable := True;
2134
  FObjectsSorting := osInherited;
2135
  FVisibilityCulling := vcInherited;
2136

2137
  fBBChanges := [oBBcChild, oBBcStructure];
2138
  FBoundingBoxPersonalUnscaled := NullBoundingBox;
2139
  FBoundingBoxOfChildren := NullBoundingBox;
2140
  FBoundingBoxIncludingChildren := NullBoundingBox;
2141
end;
2142

2143
// CreateAsChild
2144
//
2145

2146
constructor TGLBaseSceneObject.CreateAsChild(aParentOwner: TGLBaseSceneObject);
2147
begin
2148
  Create(aParentOwner);
2149
  aParentOwner.AddChild(Self);
2150
end;
2151

2152
// Destroy
2153
//
2154

2155
destructor TGLBaseSceneObject.Destroy;
2156
begin
2157
  DeleteChildCameras;
2158
  if assigned(FLocalMatrix) then
2159
    FreeMem(FLocalMatrix, SizeOf(TMatrix));
2160
  if assigned(FAbsoluteMatrix) then
2161
    // This bug have coming surely from a bad commit file.
2162
    FreeMem(FAbsoluteMatrix, SizeOf(TMatrix) * 2);
2163
  // k00m memory fix and remove some leak of the old version.
2164
  FGLObjectEffects.Free;
2165
  FGLBehaviours.Free;
2166
  FListHandle.Free;
2167
  FPosition.Free;
2168
  FRotation.Free;
2169
  FDirection.Free;
2170
  FUp.Free;
2171
  FScaling.Free;
2172
  if Assigned(FParent) then
2173
    FParent.Remove(Self, False);
2174
  if Assigned(FChildren) then
2175
  begin
2176
    DeleteChildren;
2177
    FChildren.Free;
2178
  end;
2179
  inherited Destroy;
2180
end;
2181

2182
// GetHandle
2183
//
2184

2185
function TGLBaseSceneObject.GetHandle(var rci: TGLRenderContextInfo): Cardinal;
2186
begin
2187
  if not Assigned(FListHandle) then
2188
    FListHandle := TGLListHandle.Create;
2189
  Result := FListHandle.Handle;
2190
  if Result = 0 then
2191
    Result := FListHandle.AllocateHandle;
2192

2193
  if ocStructure in FChanges then
2194
  begin
2195
    ClearStructureChanged;
2196
    FListHandle.NotifyChangesOfData;
2197
  end;
2198

2199
  if FListHandle.IsDataNeedUpdate then
2200
  begin
2201
    rci.GLStates.NewList(Result, GL_COMPILE);
2202
    try
2203
      BuildList(rci);
2204
    finally
2205
      rci.GLStates.EndList;
2206
    end;
2207
    FListHandle.NotifyDataUpdated;
2208
  end;
2209
end;
2210

2211
// ListHandleAllocated
2212
//
2213

2214
function TGLBaseSceneObject.ListHandleAllocated: Boolean;
2215
begin
2216
  Result := Assigned(FListHandle)
2217
    and (FListHandle.Handle <> 0)
2218
    and not (ocStructure in FChanges);
2219
end;
2220

2221
// DestroyHandle
2222
//
2223

2224
procedure TGLBaseSceneObject.DestroyHandle;
2225
begin
2226
  if Assigned(FListHandle) then
2227
    FListHandle.DestroyHandle;
2228
end;
2229

2230
// DestroyHandles
2231
//
2232

2233
procedure TGLBaseSceneObject.DestroyHandles;
2234
var
2235
  i: Integer;
2236
begin
2237
  for i := 0 to Count - 1 do
2238
    Children[i].DestroyHandles;
2239
  DestroyHandle;
2240
end;
2241

2242
// SetBBChanges
2243
//
2244

2245
procedure TGLBaseSceneObject.SetBBChanges(const Value: TObjectBBChanges);
2246
begin
2247
  if value <> fBBChanges then
2248
  begin
2249
    fBBChanges := Value;
2250
    if Assigned(FParent) then
2251
      FParent.BBChanges := FParent.BBChanges + [oBBcChild];
2252
  end;
2253
end;
2254

2255
// Blended
2256
//
2257

2258
function TGLBaseSceneObject.Blended: Boolean;
2259
begin
2260
  Result := False;
2261
end;
2262

2263
// BeginUpdate
2264
//
2265

2266
procedure TGLBaseSceneObject.BeginUpdate;
2267
begin
2268
  Inc(FUpdateCount);
2269
end;
2270

2271
// EndUpdate
2272
//
2273

2274
procedure TGLBaseSceneObject.EndUpdate;
2275
begin
2276
  if FUpdateCount > 0 then
2277
  begin
2278
    Dec(FUpdateCount);
2279
    if FUpdateCount = 0 then
2280
      NotifyChange(Self);
2281
  end
2282
  else
2283
    Assert(False, glsUnBalancedBeginEndUpdate);
2284
end;
2285

2286
// BuildList
2287
//
2288

2289
procedure TGLBaseSceneObject.BuildList(var rci: TGLRenderContextInfo);
2290
begin
2291
  // nothing
2292
end;
2293

2294
// DeleteChildCameras
2295
//
2296

2297
procedure TGLBaseSceneObject.DeleteChildCameras;
2298
var
2299
  i: Integer;
2300
  child: TGLBaseSceneObject;
2301
begin
2302
  i := 0;
2303
  if Assigned(FChildren) then
2304
    while i < FChildren.Count do
2305
    begin
2306
      child := TGLBaseSceneObject(FChildren.List^[i]);
2307
      child.DeleteChildCameras;
2308
      if child is TGLCamera then
2309
      begin
2310
        Remove(child, True);
2311
        child.Free;
2312
      end
2313
      else
2314
        Inc(i);
2315
    end;
2316
end;
2317

2318
// DeleteChildren
2319
//
2320

2321
procedure TGLBaseSceneObject.DeleteChildren;
2322
var
2323
  child: TGLBaseSceneObject;
2324
begin
2325
  DeleteChildCameras;
2326
  if Assigned(FScene) then
2327
    FScene.RemoveLights(Self);
2328
  if Assigned(FChildren) then
2329
    while FChildren.Count > 0 do
2330
    begin
2331
      child := TGLBaseSceneObject(FChildren.Pop);
2332
      child.FParent := nil;
2333
      child.Free;
2334
    end;
2335
  BBChanges := BBChanges + [oBBcChild];
2336
end;
2337

2338
// Loaded
2339
//
2340

2341
procedure TGLBaseSceneObject.Loaded;
2342
begin
2343
  inherited;
2344
  FPosition.W := 1;
2345
  if Assigned(FGLBehaviours) then
2346
    FGLBehaviours.Loaded;
2347
  if Assigned(FGLObjectEffects) then
2348
    FGLObjectEffects.Loaded;
2349
end;
2350

2351
// DefineProperties
2352
//
2353

2354
procedure TGLBaseSceneObject.DefineProperties(Filer: TFiler);
2355
begin
2356
  inherited;
2357
  {FOriginalFiler := Filer;}
2358

2359
  Filer.DefineBinaryProperty('BehavioursData',
2360
    ReadBehaviours, WriteBehaviours,
2361
    (Assigned(FGLBehaviours) and (FGLBehaviours.Count > 0)));
2362
  Filer.DefineBinaryProperty('EffectsData',
2363
    ReadEffects, WriteEffects,
2364
    (Assigned(FGLObjectEffects) and (FGLObjectEffects.Count > 0)));
2365
  {FOriginalFiler:=nil;}
2366
end;
2367

2368
// WriteBehaviours
2369
//
2370

2371
procedure TGLBaseSceneObject.WriteBehaviours(stream: TStream);
2372
var
2373
  writer: TWriter;
2374
begin
2375
  writer := TWriter.Create(stream, 16384);
2376
  try
2377
    Behaviours.WriteToFiler(writer);
2378
  finally
2379
    writer.Free;
2380
  end;
2381
end;
2382

2383
// ReadBehaviours
2384
//
2385

2386
procedure TGLBaseSceneObject.ReadBehaviours(stream: TStream);
2387
var
2388
  reader: TReader;
2389
begin
2390
  reader := TReader.Create(stream, 16384);
2391
  { with TReader(FOriginalFiler) do  }
2392
  try
2393
    {  reader.Root                 := Root;
2394
      reader.OnError              := OnError;
2395
      reader.OnFindMethod         := OnFindMethod;
2396
      reader.OnSetName            := OnSetName;
2397
      reader.OnReferenceName      := OnReferenceName;
2398
      reader.OnAncestorNotFound   := OnAncestorNotFound;
2399
      reader.OnCreateComponent    := OnCreateComponent;
2400
      reader.OnFindComponentClass := OnFindComponentClass;}
2401
    Behaviours.ReadFromFiler(reader);
2402
  finally
2403
    reader.Free;
2404
  end;
2405
end;
2406

2407
// WriteEffects
2408
//
2409

2410
procedure TGLBaseSceneObject.WriteEffects(stream: TStream);
2411
var
2412
  writer: TWriter;
2413
begin
2414
  writer := TWriter.Create(stream, 16384);
2415
  try
2416
    Effects.WriteToFiler(writer);
2417
  finally
2418
    writer.Free;
2419
  end;
2420
end;
2421

2422
// ReadEffects
2423
//
2424

2425
procedure TGLBaseSceneObject.ReadEffects(stream: TStream);
2426
var
2427
  reader: TReader;
2428
begin
2429
  reader := TReader.Create(stream, 16384);
2430
  {with TReader(FOriginalFiler) do }
2431
  try
2432
    { reader.Root                 := Root;
2433
     reader.OnError              := OnError;
2434
     reader.OnFindMethod         := OnFindMethod;
2435
     reader.OnSetName            := OnSetName;
2436
     reader.OnReferenceName      := OnReferenceName;
2437
     reader.OnAncestorNotFound   := OnAncestorNotFound;
2438
     reader.OnCreateComponent    := OnCreateComponent;
2439
     reader.OnFindComponentClass := OnFindComponentClass;   }
2440
    Effects.ReadFromFiler(reader);
2441
  finally
2442
    reader.Free;
2443
  end;
2444
end;
2445

2446
// WriteRotations
2447
//
2448

2449
procedure TGLBaseSceneObject.WriteRotations(stream: TStream);
2450
begin
2451
  stream.Write(FRotation.AsAddress^, 3 * SizeOf(TGLFloat));
2452
end;
2453

2454
// ReadRotations
2455
//
2456

2457
procedure TGLBaseSceneObject.ReadRotations(stream: TStream);
2458
begin
2459
  stream.Read(FRotation.AsAddress^, 3 * SizeOf(TGLFloat));
2460
end;
2461

2462
// DrawAxes
2463
//
2464

2465
procedure TGLBaseSceneObject.DrawAxes(var rci: TGLRenderContextInfo; pattern:
2466
  Word);
2467
begin
2468
  AxesBuildList(rci, Pattern, rci.rcci.farClippingDistance -
2469
    rci.rcci.nearClippingDistance);
2470
end;
2471

2472
// GetChildren
2473
//
2474

2475
procedure TGLBaseSceneObject.GetChildren(AProc: TGetChildProc; Root: TComponent);
2476
var
2477
  i: Integer;
2478
begin
2479
  if Assigned(FChildren) then
2480
    for i := 0 to FChildren.Count - 1 do
2481
      if not IsSubComponent(TComponent(FChildren.List^[i])) then
2482
        AProc(TComponent(FChildren.List^[i]));
2483
end;
2484

2485
// Get
2486
//
2487

2488
function TGLBaseSceneObject.Get(Index: Integer): TGLBaseSceneObject;
2489
begin
2490
  if Assigned(FChildren) then
2491
    Result := TGLBaseSceneObject(FChildren[Index])
2492
  else
2493
    Result := nil;
2494
end;
2495

2496
// GetCount
2497
//
2498

2499
function TGLBaseSceneObject.GetCount: Integer;
2500
begin
2501
  if Assigned(FChildren) then
2502
    Result := FChildren.Count
2503
  else
2504
    Result := 0;
2505
end;
2506

2507
// HasSubChildren
2508
//
2509

2510
function TGLBaseSceneObject.HasSubChildren: Boolean;
2511
var
2512
  I: Integer;
2513
begin
2514
  Result := False;
2515
  if Count <> 0 then
2516
    for I := 0 to Count - 1 do
2517
      if IsSubComponent(Children[i]) then
2518
      begin
2519
        Result := True;
2520
        Exit;
2521
      end;
2522
end;
2523

2524
// AddChild
2525
//
2526

2527
procedure TGLBaseSceneObject.AddChild(aChild: TGLBaseSceneObject);
2528
begin
2529
  if Assigned(FScene) then
2530
    FScene.AddLights(aChild);
2531
  if not Assigned(FChildren) then
2532
    FChildren := TPersistentObjectList.Create;
2533
  FChildren.Add(aChild);
2534
  aChild.FParent := Self;
2535
  aChild.SetScene(FScene);
2536
  TransformationChanged;
2537
  aChild.TransformationChanged;
2538
  aChild.DoOnAddedToParent;
2539
  BBChanges := BBChanges + [oBBcChild];
2540
end;
2541

2542
// AddNewChild
2543
//
2544

2545
function TGLBaseSceneObject.AddNewChild(aChild: TGLSceneObjectClass):
2546
  TGLBaseSceneObject;
2547
begin
2548
  Result := aChild.Create(Owner);
2549
  AddChild(Result);
2550
end;
2551

2552
// AddNewChildFirst
2553
//
2554

2555
function TGLBaseSceneObject.AddNewChildFirst(aChild: TGLSceneObjectClass):
2556
  TGLBaseSceneObject;
2557
begin
2558
  Result := aChild.Create(Owner);
2559
  Insert(0, Result);
2560
end;
2561

2562
// GetOrCreateBehaviour
2563
//
2564

2565
function TGLBaseSceneObject.GetOrCreateBehaviour(aBehaviour: TGLBehaviourClass):
2566
  TGLBehaviour;
2567
begin
2568
  Result := TGLBehaviour(Behaviours.GetOrCreate(aBehaviour));
2569
end;
2570

2571
// AddNewBehaviour
2572
//
2573

2574
function TGLBaseSceneObject.AddNewBehaviour(aBehaviour: TGLBehaviourClass):
2575
  TGLBehaviour;
2576
begin
2577
  Assert(Behaviours.CanAdd(aBehaviour));
2578
  result := aBehaviour.Create(Behaviours)
2579
end;
2580

2581
// GetOrCreateEffect
2582
//
2583

2584
function TGLBaseSceneObject.GetOrCreateEffect(anEffect: TGLObjectEffectClass):
2585
  TGLObjectEffect;
2586
begin
2587
  Result := TGLObjectEffect(Effects.GetOrCreate(anEffect));
2588
end;
2589

2590
// AddNewEffect
2591
//
2592

2593
function TGLBaseSceneObject.AddNewEffect(anEffect: TGLObjectEffectClass):
2594
  TGLObjectEffect;
2595
begin
2596
  Assert(Effects.CanAdd(anEffect));
2597
  result := anEffect.Create(Effects)
2598
end;
2599

2600
// RebuildMatrix
2601
//
2602

2603
procedure TGLBaseSceneObject.RebuildMatrix;
2604
begin
2605
  if ocTransformation in Changes then
2606
  begin
2607
    VectorScale(LeftVector, Scale.X, FLocalMatrix^.V[0]);
2608
    VectorScale(FUp.AsVector, Scale.Y, FLocalMatrix^.V[1]);
2609
    VectorScale(FDirection.AsVector, Scale.Z, FLocalMatrix^.V[2]);
2610
    SetVector(FLocalMatrix^.V[3], FPosition.AsVector);
2611
    Exclude(FChanges, ocTransformation);
2612
    Include(FChanges, ocAbsoluteMatrix);
2613
    Include(FChanges, ocInvAbsoluteMatrix);
2614
  end;
2615
end;
2616

2617
// ForceLocalMatrix
2618
//
2619

2620
procedure TGLBaseSceneObject.ForceLocalMatrix(const aMatrix: TMatrix);
2621
begin
2622
  FLocalMatrix^ := aMatrix;
2623
  Exclude(FChanges, ocTransformation);
2624
  Include(FChanges, ocAbsoluteMatrix);
2625
  Include(FChanges, ocInvAbsoluteMatrix);
2626
end;
2627

2628
// AbsoluteMatrixAsAddress
2629
//
2630

2631
function TGLBaseSceneObject.AbsoluteMatrixAsAddress: PMatrix;
2632
begin
2633
  if ocAbsoluteMatrix in FChanges then
2634
  begin
2635
    RebuildMatrix;
2636
    if not Assigned(FAbsoluteMatrix) then
2637
    begin
2638
      GetMem(FAbsoluteMatrix, SizeOf(TMatrix) * 2);
2639
      FInvAbsoluteMatrix := PMatrix(PtrUInt(FAbsoluteMatrix) + SizeOf(TMatrix));
2640
    end;
2641
    if Assigned(Parent) and (not (Parent is TGLSceneRootObject)) then
2642
    begin
2643
      MatrixMultiply(FLocalMatrix^,
2644
        TGLBaseSceneObject(Parent).AbsoluteMatrixAsAddress^,
2645
        FAbsoluteMatrix^);
2646
    end
2647
    else
2648
      FAbsoluteMatrix^ := FLocalMatrix^;
2649
    Exclude(FChanges, ocAbsoluteMatrix);
2650
    Include(FChanges, ocInvAbsoluteMatrix);
2651
  end;
2652
  Result := FAbsoluteMatrix;
2653
end;
2654

2655
// InvAbsoluteMatrix
2656
//
2657

2658
function TGLBaseSceneObject.InvAbsoluteMatrix: TMatrix;
2659
begin
2660
  Result := InvAbsoluteMatrixAsAddress^;
2661
end;
2662

2663
// InvAbsoluteMatrix
2664
//
2665

2666
function TGLBaseSceneObject.InvAbsoluteMatrixAsAddress: PMatrix;
2667
begin
2668
  if ocInvAbsoluteMatrix in FChanges then
2669
  begin
2670
    if VectorEquals(Scale.DirectVector, XYZHmgVector) then
2671
    begin
2672
      if not Assigned(FAbsoluteMatrix) then
2673
      begin
2674
        GetMem(FAbsoluteMatrix, SizeOf(TMatrix) * 2);
2675
        FInvAbsoluteMatrix := PMatrix(PtrUInt(FAbsoluteMatrix) +
2676
          SizeOf(TMatrix));
2677
      end;
2678
      RebuildMatrix;
2679
      if Parent <> nil then
2680
        FInvAbsoluteMatrix^ :=
2681
          MatrixMultiply(Parent.InvAbsoluteMatrixAsAddress^,
2682
          AnglePreservingMatrixInvert(FLocalMatrix^))
2683
      else
2684
        FInvAbsoluteMatrix^ := AnglePreservingMatrixInvert(FLocalMatrix^);
2685
    end
2686
    else
2687
    begin
2688
      FInvAbsoluteMatrix^ := AbsoluteMatrixAsAddress^;
2689
      InvertMatrix(FInvAbsoluteMatrix^);
2690
    end;
2691
    Exclude(FChanges, ocInvAbsoluteMatrix);
2692
  end;
2693
  Result := FInvAbsoluteMatrix;
2694
end;
2695

2696
// GetAbsoluteMatrix
2697
//
2698

2699
function TGLBaseSceneObject.GetAbsoluteMatrix: TMatrix;
2700
begin
2701
  Result := AbsoluteMatrixAsAddress^;
2702
end;
2703

2704
// SetAbsoluteMatrix
2705
//
2706

2707
procedure TGLBaseSceneObject.SetAbsoluteMatrix(const Value: TMatrix);
2708
begin
2709
  if not MatrixEquals(Value, FAbsoluteMatrix^) then
2710
  begin
2711
    FAbsoluteMatrix^ := Value;
2712
    if Parent <> nil then
2713
      SetMatrix(MatrixMultiply(FAbsoluteMatrix^,
2714
        Parent.InvAbsoluteMatrixAsAddress^))
2715
    else
2716
      SetMatrix(Value);
2717
  end;
2718
end;
2719

2720
// GetAbsoluteDirection
2721
//
2722

2723
function TGLBaseSceneObject.GetAbsoluteDirection: TVector;
2724
begin
2725
  Result := VectorNormalize(AbsoluteMatrixAsAddress^.V[2]);
2726
end;
2727

2728
// SetAbsoluteDirection
2729
//
2730

2731
procedure TGLBaseSceneObject.SetAbsoluteDirection(const v: TVector);
2732
begin
2733
  if Parent <> nil then
2734
    Direction.AsVector := Parent.AbsoluteToLocal(v)
2735
  else
2736
    Direction.AsVector := v;
2737
end;
2738

2739
// GetAbsoluteScale
2740
//
2741

2742
function TGLBaseSceneObject.GetAbsoluteScale: TVector;
2743
begin
2744
  Result.V[0] := AbsoluteMatrixAsAddress^.V[0].V[0];
2745
  Result.V[1] := AbsoluteMatrixAsAddress^.V[1].V[1];
2746
  Result.V[2] := AbsoluteMatrixAsAddress^.V[2].V[2];
2747

2748
  Result.V[3] := 0;
2749
end;
2750

2751
// SetAbsoluteScale
2752
//
2753

2754
procedure TGLBaseSceneObject.SetAbsoluteScale(const Value: TVector);
2755
begin
2756
  if Parent <> nil then
2757
    Scale.AsVector := Parent.AbsoluteToLocal(Value)
2758
  else
2759
    Scale.AsVector := Value;
2760
end;
2761

2762
// GetAbsoluteUp
2763
//
2764

2765
function TGLBaseSceneObject.GetAbsoluteUp: TVector;
2766
begin
2767
  Result := VectorNormalize(AbsoluteMatrixAsAddress^.V[1]);
2768
end;
2769

2770
// SetAbsoluteUp
2771
//
2772

2773
procedure TGLBaseSceneObject.SetAbsoluteUp(const v: TVector);
2774
begin
2775
  if Parent <> nil then
2776
    Up.AsVector := Parent.AbsoluteToLocal(v)
2777
  else
2778
    Up.AsVector := v;
2779
end;
2780

2781
// AbsoluteRight
2782
//
2783

2784
function TGLBaseSceneObject.AbsoluteRight: TVector;
2785
begin
2786
  Result := VectorNormalize(AbsoluteMatrixAsAddress^.V[0]);
2787
end;
2788

2789
// AbsoluteLeft
2790
//
2791

2792
function TGLBaseSceneObject.AbsoluteLeft: TVector;
2793
begin
2794
  Result := VectorNegate(AbsoluteRight);
2795
end;
2796

2797
// GetAbsolutePosition
2798
//
2799

2800
function TGLBaseSceneObject.GetAbsolutePosition: TVector;
2801
begin
2802
  Result := AbsoluteMatrixAsAddress^.V[3];
2803
end;
2804

2805
// SetAbsolutePosition
2806
//
2807

2808
procedure TGLBaseSceneObject.SetAbsolutePosition(const v: TVector);
2809
begin
2810
  if Assigned(Parent) then
2811
    Position.AsVector := Parent.AbsoluteToLocal(v)
2812
  else
2813
    Position.AsVector := v;
2814
end;
2815

2816
// AbsolutePositionAsAddress
2817
//
2818

2819
function TGLBaseSceneObject.AbsolutePositionAsAddress: PVector;
2820
begin
2821
  Result := @AbsoluteMatrixAsAddress^.V[3];
2822
end;
2823

2824
// AbsoluteXVector
2825
//
2826

2827
function TGLBaseSceneObject.AbsoluteXVector: TVector;
2828
begin
2829
  AbsoluteMatrixAsAddress;
2830
  SetVector(Result, PAffineVector(@FAbsoluteMatrix.V[0])^);
2831
end;
2832

2833
// AbsoluteYVector
2834
//
2835

2836
function TGLBaseSceneObject.AbsoluteYVector: TVector;
2837
begin
2838
  AbsoluteMatrixAsAddress;
2839
  SetVector(Result, PAffineVector(@FAbsoluteMatrix.V[1])^);
2840
end;
2841

2842
// AbsoluteZVector
2843
//
2844

2845
function TGLBaseSceneObject.AbsoluteZVector: TVector;
2846
begin
2847
  AbsoluteMatrixAsAddress;
2848
  SetVector(Result, PAffineVector(@FAbsoluteMatrix.V[2])^);
2849
end;
2850

2851
// AbsoluteToLocal (hmg)
2852
//
2853

2854
function TGLBaseSceneObject.AbsoluteToLocal(const v: TVector): TVector;
2855
begin
2856
  Result := VectorTransform(v, InvAbsoluteMatrixAsAddress^);
2857
end;
2858

2859
// AbsoluteToLocal (affine)
2860
//
2861

2862
function TGLBaseSceneObject.AbsoluteToLocal(const v: TAffineVector):
2863
  TAffineVector;
2864
begin
2865
  Result := VectorTransform(v, InvAbsoluteMatrixAsAddress^);
2866
end;
2867

2868
// LocalToAbsolute (hmg)
2869
//
2870

2871
function TGLBaseSceneObject.LocalToAbsolute(const v: TVector): TVector;
2872
begin
2873
  Result := VectorTransform(v, AbsoluteMatrixAsAddress^);
2874
end;
2875

2876
// LocalToAbsolute (affine)
2877
//
2878

2879
function TGLBaseSceneObject.LocalToAbsolute(const v: TAffineVector):
2880
  TAffineVector;
2881
begin
2882
  Result := VectorTransform(v, AbsoluteMatrixAsAddress^);
2883
end;
2884

2885
// Right
2886
//
2887

2888
function TGLBaseSceneObject.Right: TVector;
2889
begin
2890
  Result := VectorCrossProduct(FDirection.AsVector, FUp.AsVector);
2891
end;
2892

2893
// LeftVector
2894
//
2895

2896
function TGLBaseSceneObject.LeftVector: TVector;
2897
begin
2898
  Result := VectorCrossProduct(FUp.AsVector, FDirection.AsVector);
2899
end;
2900

2901
// BarycenterAbsolutePosition
2902
//
2903

2904
function TGLBaseSceneObject.BarycenterAbsolutePosition: TVector;
2905
begin
2906
  Result := AbsolutePosition;
2907
end;
2908

2909
// SqrDistanceTo (obj)
2910
//
2911

2912
function TGLBaseSceneObject.SqrDistanceTo(anObject: TGLBaseSceneObject): Single;
2913
begin
2914
  if Assigned(anObject) then
2915
    Result := VectorDistance2(AbsolutePosition, anObject.AbsolutePosition)
2916
  else
2917
    Result := 0;
2918
end;
2919

2920
// SqrDistanceTo (vec4)
2921
//
2922

2923
function TGLBaseSceneObject.SqrDistanceTo(const pt: TVector): Single;
2924
begin
2925
  Result := VectorDistance2(pt, AbsolutePosition);
2926
end;
2927

2928
// DistanceTo (obj)
2929
//
2930

2931
function TGLBaseSceneObject.DistanceTo(anObject: TGLBaseSceneObject): Single;
2932
begin
2933
  if Assigned(anObject) then
2934
    Result := VectorDistance(AbsolutePosition, anObject.AbsolutePosition)
2935
  else
2936
    Result := 0;
2937
end;
2938

2939
// DistanceTo (vec4)
2940
//
2941

2942
function TGLBaseSceneObject.DistanceTo(const pt: TVector): Single;
2943
begin
2944
  Result := VectorDistance(AbsolutePosition, pt);
2945
end;
2946

2947
// BarycenterSqrDistanceTo
2948
//
2949

2950
function TGLBaseSceneObject.BarycenterSqrDistanceTo(const pt: TVector): Single;
2951
var
2952
  d: TVector;
2953
begin
2954
  d := BarycenterAbsolutePosition;
2955
  Result := VectorDistance2(d, pt);
2956
end;
2957

2958
// AxisAlignedDimensions
2959
//
2960

2961
function TGLBaseSceneObject.AxisAlignedDimensions: TVector;
2962
begin
2963
  Result := AxisAlignedDimensionsUnscaled();
2964
  ScaleVector(Result, Scale.AsVector);
2965
end;
2966

2967
// AxisAlignedDimensionsUnscaled
2968
//
2969

2970
function TGLBaseSceneObject.AxisAlignedDimensionsUnscaled: TVector;
2971
begin
2972
  Result.V[0] := 0.5;
2973
  Result.V[1] := 0.5;
2974
  Result.V[2] := 0.5;
2975
  Result.V[3] := 0;
2976
end;
2977

2978
// AxisAlignedBoundingBox
2979
//
2980

2981
function TGLBaseSceneObject.AxisAlignedBoundingBox(
2982
  const AIncludeChilden: Boolean): TAABB;
2983
var
2984
  i: Integer;
2985
  aabb: TAABB;
2986
  child: TGLBaseSceneObject;
2987
begin
2988
  SetAABB(Result, AxisAlignedDimensionsUnscaled);
2989
  // not tested for child objects
2990
  if AIncludeChilden and Assigned(FChildren) then
2991
  begin
2992
    for i := 0 to FChildren.Count - 1 do
2993
    begin
2994
      child := TGLBaseSceneObject(FChildren.List^[i]);
2995
      aabb := child.AxisAlignedBoundingBoxUnscaled(AIncludeChilden);
2996
      AABBTransform(aabb, child.Matrix);
2997
      AddAABB(Result, aabb);
2998
    end;
2999
  end;
3000
  AABBScale(Result, Scale.AsAffineVector);
3001
end;
3002

3003
// AxisAlignedBoundingBoxUnscaled
3004
//
3005

3006
function TGLBaseSceneObject.AxisAlignedBoundingBoxUnscaled(
3007
  const AIncludeChilden: Boolean): TAABB;
3008
var
3009
  i: Integer;
3010
  aabb: TAABB;
3011
begin
3012
  SetAABB(Result, AxisAlignedDimensionsUnscaled);
3013
  //not tested for child objects
3014
  if AIncludeChilden and Assigned(FChildren) then
3015
  begin
3016
    for i := 0 to FChildren.Count - 1 do
3017
    begin
3018
      aabb :=
3019
        TGLBaseSceneObject(FChildren.List^[i]).AxisAlignedBoundingBoxUnscaled(AIncludeChilden);
3020
      AABBTransform(aabb, TGLBaseSceneObject(FChildren.List^[i]).Matrix);
3021
      AddAABB(Result, aabb);
3022
    end;
3023
  end;
3024
end;
3025

3026
// AxisAlignedBoundingBoxAbsolute
3027
//
3028

3029
function TGLBaseSceneObject.AxisAlignedBoundingBoxAbsolute(
3030
  const AIncludeChilden: Boolean; const AUseBaryCenter: Boolean): TAABB;
3031
begin
3032
  Result := BBToAABB(BoundingBoxAbsolute(AIncludeChilden, AUseBaryCenter));
3033
end;
3034

3035
// BoundingBox
3036
//
3037

3038
function TGLBaseSceneObject.BoundingBox(const AIncludeChilden: Boolean;
3039
  const AUseBaryCenter: Boolean): THmgBoundingBox;
3040
var
3041
  CurrentBaryOffset: TVector;
3042
begin
3043
  Result := AABBToBB(AxisAlignedBoundingBox(AIncludeChilden));
3044

3045
  // DaStr: code not tested...
3046
  if AUseBaryCenter then
3047
  begin
3048
    CurrentBaryOffset :=
3049
      VectorSubtract(AbsoluteToLocal(BarycenterAbsolutePosition),
3050
      Position.AsVector);
3051
    OffsetBBPoint(Result, CurrentBaryOffset);
3052
  end;
3053
end;
3054

3055
// BoundingBoxUnscaled
3056
//
3057

3058
function TGLBaseSceneObject.BoundingBoxUnscaled(
3059
  const AIncludeChilden: Boolean;
3060
  const AUseBaryCenter: Boolean): THmgBoundingBox;
3061
var
3062
  CurrentBaryOffset: TVector;
3063
begin
3064
  Result := AABBToBB(AxisAlignedBoundingBoxUnscaled(AIncludeChilden));
3065

3066
  // DaStr: code not tested...
3067
  if AUseBaryCenter then
3068
  begin
3069
    CurrentBaryOffset :=
3070
      VectorSubtract(AbsoluteToLocal(BarycenterAbsolutePosition),
3071
      Position.AsVector);
3072
    OffsetBBPoint(Result, CurrentBaryOffset);
3073
  end;
3074
end;
3075

3076
// BoundingBoxAbsolute
3077
//
3078

3079
function TGLBaseSceneObject.BoundingBoxAbsolute(
3080
  const AIncludeChilden: Boolean;
3081
  const AUseBaryCenter: Boolean): THmgBoundingBox;
3082
var
3083
  I: Integer;
3084
  CurrentBaryOffset: TVector;
3085
begin
3086
  Result := BoundingBoxUnscaled(AIncludeChilden, False);
3087
  for I := 0 to 7 do
3088
    Result.BBox[I] := LocalToAbsolute(Result.BBox[I]);
3089

3090
  if AUseBaryCenter then
3091
  begin
3092
    CurrentBaryOffset := VectorSubtract(BarycenterAbsolutePosition,
3093
      AbsolutePosition);
3094
    OffsetBBPoint(Result, CurrentBaryOffset);
3095
  end;
3096
end;
3097

3098
// BoundingSphereRadius
3099
//
3100

3101
function TGLBaseSceneObject.BoundingSphereRadius: Single;
3102
begin
3103
  Result := VectorLength(AxisAlignedDimensions);
3104
end;
3105

3106
// BoundingSphereRadiusUnscaled
3107
//
3108

3109
function TGLBaseSceneObject.BoundingSphereRadiusUnscaled: Single;
3110
begin
3111
  Result := VectorLength(AxisAlignedDimensionsUnscaled);
3112
end;
3113

3114
// PointInObject
3115
//
3116

3117
function TGLBaseSceneObject.PointInObject(const point: TVector): Boolean;
3118
var
3119
  localPt, dim: TVector;
3120
begin
3121
  dim := AxisAlignedDimensions;
3122
  localPt := VectorTransform(point, InvAbsoluteMatrix);
3123
  Result := (Abs(localPt.V[0] * Scale.X) <= dim.V[0]) and
3124
            (Abs(localPt.V[1] * Scale.Y) <= dim.V[1]) and
3125
            (Abs(localPt.V[2] * Scale.Z) <= dim.V[2]);
3126
end;
3127

3128
// CalculateBoundingBoxPersonalUnscaled
3129
//
3130

3131
procedure TGLBaseSceneObject.CalculateBoundingBoxPersonalUnscaled(var
3132
  ANewBoundingBox: THmgBoundingBox);
3133
begin
3134
  // Using the standard method to get the local BB.
3135
  ANewBoundingBox := AABBToBB(AxisAlignedBoundingBoxUnscaled(False));
3136
  OffsetBBPoint(ANewBoundingBox, AbsoluteToLocal(BarycenterAbsolutePosition));
3137
end;
3138

3139
// BoundingBoxPersonalUnscaledEx
3140
//
3141

3142
function TGLBaseSceneObject.BoundingBoxPersonalUnscaledEx: THmgBoundingBox;
3143
begin
3144
  if oBBcStructure in FBBChanges then
3145
  begin
3146
    CalculateBoundingBoxPersonalUnscaled(FBoundingBoxPersonalUnscaled);
3147
    Exclude(FBBChanges, oBBcStructure);
3148
  end;
3149
  Result := FBoundingBoxPersonalUnscaled;
3150
end;
3151

3152
// AxisAlignedBoundingBoxAbsoluteEx
3153
//
3154

3155
function TGLBaseSceneObject.AxisAlignedBoundingBoxAbsoluteEx: TAABB;
3156
var
3157
  pBB: THmgBoundingBox;
3158
begin
3159
  pBB := BoundingBoxIncludingChildrenEx;
3160
  BBTransform(pBB, AbsoluteMatrix);
3161
  Result := BBtoAABB(pBB);
3162
end;
3163

3164
// AxisAlignedBoundingBoxEx
3165
//
3166

3167
function TGLBaseSceneObject.AxisAlignedBoundingBoxEx: TAABB;
3168
begin
3169
  Result := BBtoAABB(BoundingBoxIncludingChildrenEx);
3170
  AABBScale(Result, Scale.AsAffineVector);
3171
end;
3172

3173
// BoundingBoxOfChildrenEx
3174
//
3175

3176
function TGLBaseSceneObject.BoundingBoxOfChildrenEx: THmgBoundingBox;
3177
var
3178
  i: Integer;
3179
  pBB: THmgBoundingBox;
3180
begin
3181
  if oBBcChild in FBBChanges then
3182
  begin
3183
    // Computing
3184
    FBoundingBoxOfChildren := NullBoundingBox;
3185
    if assigned(FChildren) then
3186
    begin
3187
      for i := 0 to FChildren.count - 1 do
3188
      begin
3189
        pBB :=
3190
          TGLBaseSceneObject(FChildren.List^[i]).BoundingBoxIncludingChildrenEx;
3191
        if not BoundingBoxesAreEqual(@pBB, @NullBoundingBox) then
3192
        begin
3193
          // transformation with local matrix
3194
          BBTransform(pbb, TGLBaseSceneObject(FChildren.List^[i]).Matrix);
3195
          if BoundingBoxesAreEqual(@FBoundingBoxOfChildren, @NullBoundingBox) then
3196
            FBoundingBoxOfChildren := pBB
3197
          else
3198
            AddBB(FBoundingBoxOfChildren, pBB);
3199
        end;
3200
      end;
3201
    end;
3202
    exclude(FBBChanges, oBBcChild);
3203
  end;
3204
  result := FBoundingBoxOfChildren;
3205
end;
3206

3207
// BoundingBoxIncludingChildrenEx
3208
//
3209

3210
function TGLBaseSceneObject.BoundingBoxIncludingChildrenEx: THmgBoundingBox;
3211
var
3212
  pBB: THmgBoundingBox;
3213
begin
3214
  if (oBBcStructure in FBBChanges) or
3215
    (oBBcChild in FBBChanges) then
3216
  begin
3217
    pBB := BoundingBoxPersonalUnscaledEx;
3218
    if BoundingBoxesAreEqual(@pBB, @NullBoundingBox) then
3219
      FBoundingBoxIncludingChildren := BoundingBoxOfChildrenEx
3220
    else
3221
    begin
3222
      FBoundingBoxIncludingChildren := pBB;
3223
      pBB := BoundingBoxOfChildrenEx;
3224
      if not BoundingBoxesAreEqual(@pBB, @NullBoundingBox) then
3225
        AddBB(FBoundingBoxIncludingChildren, pBB);
3226
    end;
3227
  end;
3228
  Result := FBoundingBoxIncludingChildren;
3229
end;
3230

3231
// RayCastIntersect
3232
//
3233

3234
function TGLBaseSceneObject.RayCastIntersect(const rayStart, rayVector: TVector;
3235
  intersectPoint: PVector = nil;
3236
  intersectNormal: PVector = nil): Boolean;
3237
var
3238
  i1, i2, absPos: TVector;
3239
begin
3240
  SetVector(absPos, AbsolutePosition);
3241
  if RayCastSphereIntersect(rayStart, rayVector, absPos, BoundingSphereRadius,
3242
    i1, i2) > 0 then
3243
  begin
3244
    Result := True;
3245
    if Assigned(intersectPoint) then
3246
      SetVector(intersectPoint^, i1);
3247
    if Assigned(intersectNormal) then
3248
    begin
3249
      SubtractVector(i1, absPos);
3250
      NormalizeVector(i1);
3251
      SetVector(intersectNormal^, i1);
3252
    end;
3253
  end
3254
  else
3255
    Result := False;
3256
end;
3257

3258
// GenerateSilhouette
3259
//
3260

3261
function TGLBaseSceneObject.GenerateSilhouette(const silhouetteParameters:
3262
  TGLSilhouetteParameters): TGLSilhouette;
3263
const
3264
  cNbSegments = 21;
3265
var
3266
  i, j: Integer;
3267
  d, r, vr, s, c, angleFactor: Single;
3268
  sVec, tVec: TAffineVector;
3269
begin
3270
  r := BoundingSphereRadiusUnscaled;
3271
  d := VectorLength(silhouetteParameters.SeenFrom);
3272
  // determine visible radius
3273
  case silhouetteParameters.Style of
3274
    ssOmni: vr := SphereVisibleRadius(d, r);
3275
    ssParallel: vr := r;
3276
  else
3277
    Assert(False);
3278
    vr := r;
3279
  end;
3280
  // determine a local orthonormal matrix, viewer-oriented
3281
  sVec := VectorCrossProduct(silhouetteParameters.SeenFrom, XVector);
3282
  if VectorLength(sVec) < 1e-3 then
3283
    sVec := VectorCrossProduct(silhouetteParameters.SeenFrom, YVector);
3284
  tVec := VectorCrossProduct(silhouetteParameters.SeenFrom, sVec);
3285
  NormalizeVector(sVec);
3286
  NormalizeVector(tVec);
3287
  // generate the silhouette (outline and capping)
3288
  Result := TGLSilhouette.Create;
3289
  angleFactor := (2 * PI) / cNbSegments;
3290
  vr := vr * 0.98;
3291
  for i := 0 to cNbSegments - 1 do
3292
  begin
3293
    SinCos(i * angleFactor, vr, s, c);
3294
    Result.Vertices.AddPoint(VectorCombine(sVec, tVec, s, c));
3295
    j := (i + 1) mod cNbSegments;
3296
    Result.Indices.Add(i, j);
3297
    if silhouetteParameters.CappingRequired then
3298
      Result.CapIndices.Add(cNbSegments, i, j)
3299
  end;
3300
  if silhouetteParameters.CappingRequired then
3301
    Result.Vertices.Add(NullHmgPoint);
3302
end;
3303

3304
 
3305
//
3306

3307
procedure TGLBaseSceneObject.Assign(Source: TPersistent);
3308
var
3309
  i: Integer;
3310
  child, newChild: TGLBaseSceneObject;
3311
begin
3312
  if Assigned(Source) and (Source is TGLBaseSceneObject) then
3313
  begin
3314
    DestroyHandles;
3315
    FVisible := TGLBaseSceneObject(Source).FVisible;
3316
    TGLBaseSceneObject(Source).RebuildMatrix;
3317
    SetMatrix(TGLBaseSceneObject(Source).FLocalMatrix^);
3318
    FShowAxes := TGLBaseSceneObject(Source).FShowAxes;
3319
    FObjectsSorting := TGLBaseSceneObject(Source).FObjectsSorting;
3320
    FVisibilityCulling := TGLBaseSceneObject(Source).FVisibilityCulling;
3321
    FRotation.Assign(TGLBaseSceneObject(Source).FRotation);
3322
    DeleteChildren;
3323
    if Assigned(Scene) then
3324
      Scene.BeginUpdate;
3325
    if Assigned(TGLBaseSceneObject(Source).FChildren) then
3326
    begin
3327
      for i := 0 to TGLBaseSceneObject(Source).FChildren.Count - 1 do
3328
      begin
3329
        child := TGLBaseSceneObject(TGLBaseSceneObject(Source).FChildren[i]);
3330
        newChild := AddNewChild(TGLSceneObjectClass(child.ClassType));
3331
        newChild.Assign(child);
3332
      end;
3333
    end;
3334
    if Assigned(Scene) then
3335
      Scene.EndUpdate;
3336
    OnProgress := TGLBaseSceneObject(Source).OnProgress;
3337
    if Assigned(TGLBaseSceneObject(Source).FGLBehaviours) then
3338
      Behaviours.Assign(TGLBaseSceneObject(Source).Behaviours)
3339
    else
3340
      FreeAndNil(FGLBehaviours);
3341
    if Assigned(TGLBaseSceneObject(Source).FGLObjectEffects) then
3342
      Effects.Assign(TGLBaseSceneObject(Source).Effects)
3343
    else
3344
      FreeAndNil(FGLObjectEffects);
3345
    Tag := TGLBaseSceneObject(Source).Tag;
3346
    FTagFloat := TGLBaseSceneObject(Source).FTagFloat;
3347
  end
3348
  else
3349
    inherited Assign(Source);
3350
end;
3351

3352
// IsUpdating
3353
//
3354

3355
function TGLBaseSceneObject.IsUpdating: Boolean;
3356
begin
3357
  Result := (FUpdateCount <> 0) or (csReading in ComponentState);
3358
end;
3359

3360
// GetParentComponent
3361
//
3362

3363
function TGLBaseSceneObject.GetParentComponent: TComponent;
3364
begin
3365
  if FParent is TGLSceneRootObject then
3366
    Result := FScene
3367
  else
3368
    Result := FParent;
3369
end;
3370

3371
// HasParent
3372
//
3373

3374
function TGLBaseSceneObject.HasParent: Boolean;
3375
begin
3376
  Result := assigned(FParent);
3377
end;
3378

3379
// Lift
3380
//
3381

3382
procedure TGLBaseSceneObject.Lift(aDistance: Single);
3383
begin
3384
  FPosition.AddScaledVector(aDistance, FUp.AsVector);
3385
  TransformationChanged;
3386
end;
3387

3388
// Move
3389
//
3390

3391
procedure TGLBaseSceneObject.Move(ADistance: Single);
3392
begin
3393
  FPosition.AddScaledVector(ADistance, FDirection.AsVector);
3394
  TransformationChanged;
3395
end;
3396

3397
// Slide
3398
//
3399

3400
procedure TGLBaseSceneObject.Slide(ADistance: Single);
3401
begin
3402
  FPosition.AddScaledVector(ADistance, Right);
3403
  TransformationChanged;
3404
end;
3405

3406
// ResetRotations
3407
//
3408

3409
procedure TGLBaseSceneObject.ResetRotations;
3410
begin
3411
  FillChar(FLocalMatrix^, SizeOf(TMatrix), 0);
3412
  FLocalMatrix^.V[0].V[0] := Scale.DirectX;
3413
  FLocalMatrix^.V[1].V[1] := Scale.DirectY;
3414
  FLocalMatrix^.V[2].V[2] := Scale.DirectZ;
3415
  SetVector(FLocalMatrix^.V[3], Position.DirectVector);
3416
  FRotation.DirectVector := NullHmgPoint;
3417
  FDirection.DirectVector := ZHmgVector;
3418
  FUp.DirectVector := YHmgVector;
3419
  TransformationChanged;
3420
  Exclude(FChanges, ocTransformation);
3421
end;
3422

3423
// ResetAndPitchTurnRoll
3424
//
3425

3426
procedure TGLBaseSceneObject.ResetAndPitchTurnRoll(const degX, degY, degZ:
3427
  Single);
3428
var
3429
  rotMatrix: TMatrix;
3430
  V: TVector;
3431
begin
3432
  ResetRotations;
3433
  // set DegX (Pitch)
3434
  rotMatrix := CreateRotationMatrix(Right, degX * cPIdiv180);
3435
  V := VectorTransform(FUp.AsVector, rotMatrix);
3436
  NormalizeVector(V);
3437
  FUp.DirectVector := V;
3438
  V := VectorTransform(FDirection.AsVector, rotMatrix);
3439
  NormalizeVector(V);
3440
  FDirection.DirectVector := V;
3441
  FRotation.DirectX := NormalizeDegAngle(DegX);
3442
  // set DegY (Turn)
3443
  rotMatrix := CreateRotationMatrix(FUp.AsVector, degY * cPIdiv180);
3444
  V := VectorTransform(FUp.AsVector, rotMatrix);
3445
  NormalizeVector(V);
3446
  FUp.DirectVector := V;
3447
  V := VectorTransform(FDirection.AsVector, rotMatrix);
3448
  NormalizeVector(V);
3449
  FDirection.DirectVector := V;
3450
  FRotation.DirectY := NormalizeDegAngle(DegY);
3451
  // set DegZ (Roll)
3452
  rotMatrix := CreateRotationMatrix(Direction.AsVector, degZ * cPIdiv180);
3453
  V := VectorTransform(FUp.AsVector, rotMatrix);
3454
  NormalizeVector(V);
3455
  FUp.DirectVector := V;
3456
  V := VectorTransform(FDirection.AsVector, rotMatrix);
3457
  NormalizeVector(V);
3458
  FDirection.DirectVector := V;
3459
  FRotation.DirectZ := NormalizeDegAngle(DegZ);
3460
  TransformationChanged;
3461
  NotifyChange(self);
3462
end;
3463

3464
// RotateAbsolute
3465
//
3466

3467
procedure TGLBaseSceneObject.RotateAbsolute(const rx, ry, rz: Single);
3468
var
3469
  resMat: TMatrix;
3470
  v: TAffineVector;
3471
begin
3472
  resMat := Matrix;
3473
  // No we build rotation matrices and use them to rotate the obj
3474
  if rx <> 0 then
3475
  begin
3476
    SetVector(v, AbsoluteToLocal(XVector));
3477
    resMat := MatrixMultiply(CreateRotationMatrix(v, -DegToRad(rx)), resMat);
3478
  end;
3479
  if ry <> 0 then
3480
  begin
3481
    SetVector(v, AbsoluteToLocal(YVector));
3482
    resMat := MatrixMultiply(CreateRotationMatrix(v, -DegToRad(ry)), resMat);
3483
  end;
3484
  if rz <> 0 then
3485
  begin
3486
    SetVector(v, AbsoluteToLocal(ZVector));
3487
    resMat := MatrixMultiply(CreateRotationMatrix(v, -DegToRad(rz)), resMat);
3488
  end;
3489
  Matrix := resMat;
3490
end;
3491

3492
// RotateAbsolute
3493
//
3494

3495
procedure TGLBaseSceneObject.RotateAbsolute(const axis: TAffineVector; angle:
3496
  Single);
3497
var
3498
  v: TAffineVector;
3499
begin
3500
  if angle <> 0 then
3501
  begin
3502
    SetVector(v, AbsoluteToLocal(axis));
3503
    Matrix := MatrixMultiply(CreateRotationMatrix(v, DegToRad(angle)), Matrix);
3504
  end;
3505
end;
3506

3507
// Pitch
3508
//
3509

3510
procedure TGLBaseSceneObject.Pitch(angle: Single);
3511
var
3512
  r: Single;
3513
  rightVector: TVector;
3514
begin
3515
  FIsCalculating := True;
3516
  try
3517
    angle := -DegToRad(angle);
3518
    rightVector := Right;
3519
    FUp.Rotate(rightVector, angle);
3520
    FUp.Normalize;
3521
    FDirection.Rotate(rightVector, angle);
3522
    FDirection.Normalize;
3523
    r := -RadToDeg(ArcTan2(FDirection.Y, VectorLength(FDirection.X,
3524
      FDirection.Z)));
3525
    if FDirection.X < 0 then
3526
      if FDirection.Y < 0 then
3527
        r := 180 - r
3528
      else
3529
        r := -180 - r;
3530
    FRotation.X := r;
3531
  finally
3532
    FIsCalculating := False;
3533
  end;
3534
  TransformationChanged;
3535
end;
3536

3537
// SetPitchAngle
3538
//
3539

3540
procedure TGLBaseSceneObject.SetPitchAngle(AValue: Single);
3541
var
3542
  diff: Single;
3543
  rotMatrix: TMatrix;
3544
begin
3545
  if AValue <> FRotation.X then
3546
  begin
3547
    if not (csLoading in ComponentState) then
3548
    begin
3549
      FIsCalculating := True;
3550
      try
3551
        diff := DegToRad(FRotation.X - AValue);
3552
        rotMatrix := CreateRotationMatrix(Right, diff);
3553
        FUp.DirectVector := VectorTransform(FUp.AsVector, rotMatrix);
3554
        FUp.Normalize;
3555
        FDirection.DirectVector := VectorTransform(FDirection.AsVector,
3556
          rotMatrix);
3557
        FDirection.Normalize;
3558
        TransformationChanged;
3559
      finally
3560
        FIsCalculating := False;
3561
      end;
3562
    end;
3563
    FRotation.DirectX := NormalizeDegAngle(AValue);
3564
  end;
3565
end;
3566

3567
// Roll
3568
//
3569

3570
procedure TGLBaseSceneObject.Roll(angle: Single);
3571
var
3572
  r: Single;
3573
  rightVector, directionVector: TVector;
3574
begin
3575
  FIsCalculating := True;
3576
  try
3577
    angle := DegToRad(angle);
3578
    directionVector := Direction.AsVector;
3579
    FUp.Rotate(directionVector, angle);
3580
    FUp.Normalize;
3581
    FDirection.Rotate(directionVector, angle);
3582
    FDirection.Normalize;
3583

3584
    // calculate new rotation angle from vectors
3585
    rightVector := Right;
3586
    r := -RadToDeg(ArcTan2(rightVector.V[1],
3587
              VectorLength(rightVector.V[0],
3588
                           rightVector.V[2])));
3589
    if rightVector.V[0] < 0 then
3590
      if rightVector.V[1] < 0 then
3591
        r := 180 - r
3592
      else
3593
        r := -180 - r;
3594
    FRotation.Z := r;
3595
  finally
3596
    FIsCalculating := False;
3597
  end;
3598
  TransformationChanged;
3599
end;
3600

3601
// SetRollAngle
3602
//
3603

3604
procedure TGLBaseSceneObject.SetRollAngle(AValue: Single);
3605
var
3606
  diff: Single;
3607
  rotMatrix: TMatrix;
3608
begin
3609
  if AValue <> FRotation.Z then
3610
  begin
3611
    if not (csLoading in ComponentState) then
3612
    begin
3613
      FIsCalculating := True;
3614
      try
3615
        diff := DegToRad(FRotation.Z - AValue);
3616
        rotMatrix := CreateRotationMatrix(Direction.AsVector, diff);
3617
        FUp.DirectVector := VectorTransform(FUp.AsVector, rotMatrix);
3618
        FUp.Normalize;
3619
        FDirection.DirectVector := VectorTransform(FDirection.AsVector,
3620
          rotMatrix);
3621
        FDirection.Normalize;
3622
        TransformationChanged;
3623
      finally
3624
        FIsCalculating := False;
3625
      end;
3626
    end;
3627
    FRotation.DirectZ := NormalizeDegAngle(AValue);
3628
  end;
3629
end;
3630

3631
// Turn
3632
//
3633

3634
procedure TGLBaseSceneObject.Turn(angle: Single);
3635
var
3636
  r: Single;
3637
  upVector: TVector;
3638
begin
3639
  FIsCalculating := True;
3640
  try
3641
    angle := DegToRad(angle);
3642
    upVector := Up.AsVector;
3643
    FUp.Rotate(upVector, angle);
3644
    FUp.Normalize;
3645
    FDirection.Rotate(upVector, angle);
3646
    FDirection.Normalize;
3647
    r := -RadToDeg(ArcTan2(FDirection.X, VectorLength(FDirection.Y,
3648
      FDirection.Z)));
3649
    if FDirection.X < 0 then
3650
      if FDirection.Y < 0 then
3651
        r := 180 - r
3652
      else
3653
        r := -180 - r;
3654
    FRotation.Y := r;
3655
  finally
3656
    FIsCalculating := False;
3657
  end;
3658
  TransformationChanged;
3659
end;
3660

3661
// SetTurnAngle
3662
//
3663

3664
procedure TGLBaseSceneObject.SetTurnAngle(AValue: Single);
3665
var
3666
  diff: Single;
3667
  rotMatrix: TMatrix;
3668
begin
3669
  if AValue <> FRotation.Y then
3670
  begin
3671
    if not (csLoading in ComponentState) then
3672
    begin
3673
      FIsCalculating := True;
3674
      try
3675
        diff := DegToRad(FRotation.Y - AValue);
3676
        rotMatrix := CreateRotationMatrix(Up.AsVector, diff);
3677
        FUp.DirectVector := VectorTransform(FUp.AsVector, rotMatrix);
3678
        FUp.Normalize;
3679
        FDirection.DirectVector := VectorTransform(FDirection.AsVector,
3680
          rotMatrix);
3681
        FDirection.Normalize;
3682
        TransformationChanged;
3683
      finally
3684
        FIsCalculating := False;
3685
      end;
3686
    end;
3687
    FRotation.DirectY := NormalizeDegAngle(AValue);
3688
  end;
3689
end;
3690

3691
procedure TGLBaseSceneObject.SetRotation(aRotation: TGLCoordinates);
3692
begin
3693
  FRotation.Assign(aRotation);
3694
  TransformationChanged;
3695
end;
3696

3697
function TGLBaseSceneObject.GetPitchAngle: Single;
3698
begin
3699
  Result := FRotation.X;
3700
end;
3701

3702
function TGLBaseSceneObject.GetTurnAngle: Single;
3703
begin
3704
  Result := FRotation.Y;
3705
end;
3706

3707
function TGLBaseSceneObject.GetRollAngle: Single;
3708
begin
3709
  Result := FRotation.Z;
3710
end;
3711

3712
procedure TGLBaseSceneObject.PointTo(const ATargetObject: TGLBaseSceneObject;
3713
  const AUpVector: TVector);
3714
begin
3715
  PointTo(ATargetObject.AbsolutePosition, AUpVector);
3716
end;
3717

3718
procedure TGLBaseSceneObject.PointTo(const AAbsolutePosition, AUpVector:
3719
  TVector);
3720
var
3721
  absDir, absRight, absUp: TVector;
3722
begin
3723
  // first compute absolute attitude for pointing
3724
  absDir := VectorSubtract(AAbsolutePosition, Self.AbsolutePosition);
3725
  NormalizeVector(absDir);
3726
  absRight := VectorCrossProduct(absDir, AUpVector);
3727
  NormalizeVector(absRight);
3728
  absUp := VectorCrossProduct(absRight, absDir);
3729
  // convert absolute to local and adjust object
3730
  if Parent <> nil then
3731
  begin
3732
    FDirection.AsVector := Parent.AbsoluteToLocal(absDir);
3733
    FUp.AsVector := Parent.AbsoluteToLocal(absUp);
3734
  end
3735
  else
3736
  begin
3737
    FDirection.AsVector := absDir;
3738
    FUp.AsVector := absUp;
3739
  end;
3740
  TransformationChanged
3741
end;
3742

3743
procedure TGLBaseSceneObject.SetShowAxes(AValue: Boolean);
3744
begin
3745
  if FShowAxes <> AValue then
3746
  begin
3747
    FShowAxes := AValue;
3748
    NotifyChange(Self);
3749
  end;
3750
end;
3751

3752
procedure TGLBaseSceneObject.SetScaling(AValue: TGLCoordinates);
3753
begin
3754
  FScaling.Assign(AValue);
3755
  TransformationChanged;
3756
end;
3757

3758
procedure TGLBaseSceneObject.SetName(const NewName: TComponentName);
3759
begin
3760
  if Name <> NewName then
3761
  begin
3762
    inherited SetName(NewName);
3763
    if Assigned(vGLBaseSceneObjectNameChangeEvent) then
3764
      vGLBaseSceneObjectNameChangeEvent(Self);
3765
  end;
3766
end;
3767

3768
procedure TGLBaseSceneObject.SetParent(const val: TGLBaseSceneObject);
3769
begin
3770
  MoveTo(val);
3771
end;
3772

3773
function TGLBaseSceneObject.GetIndex: Integer;
3774
begin
3775
  if Assigned(FParent) then
3776
    Result := FParent.FChildren.IndexOf(Self)
3777
  else
3778
    Result := -1;
3779
end;
3780

3781
procedure TGLBaseSceneObject.SetIndex(aValue: Integer);
3782
var
3783
  LCount: Integer;
3784
  parentBackup: TGLBaseSceneObject;
3785
begin
3786
  if Assigned(FParent) then
3787
  begin
3788
    if aValue < 0 then
3789
      aValue := 0;
3790
    LCount := FParent.Count;
3791
    if aValue >= LCount then
3792
      aValue := LCount - 1;
3793
    if aValue <> Index then
3794
    begin
3795
      if Assigned(FScene) then
3796
        FScene.BeginUpdate;
3797
      parentBackup := FParent;
3798
      parentBackup.Remove(Self, False);
3799
      parentBackup.Insert(AValue, Self);
3800
      if Assigned(FScene) then
3801
        FScene.EndUpdate;
3802
    end;
3803
  end;
3804
end;
3805

3806
procedure TGLBaseSceneObject.SetParentComponent(Value: TComponent);
3807
begin
3808
  inherited;
3809
  if Value = FParent then
3810
    Exit;
3811

3812
  if Value is TGLScene then
3813
    SetParent(TGLScene(Value).Objects)
3814
  else if Value is TGLBaseSceneObject then
3815
    SetParent(TGLBaseSceneObject(Value))
3816
  else
3817
    SetParent(nil);
3818
end;
3819

3820
procedure TGLBaseSceneObject.StructureChanged;
3821
begin
3822
  if not (ocStructure in FChanges) then
3823
  begin
3824
    Include(FChanges, ocStructure);
3825
    NotifyChange(Self);
3826
  end
3827
  else if osDirectDraw in ObjectStyle then
3828
    NotifyChange(Self);
3829
end;
3830

3831
procedure TGLBaseSceneObject.ClearStructureChanged;
3832
begin
3833
  Exclude(FChanges, ocStructure);
3834
  SetBBChanges(BBChanges + [oBBcStructure]);
3835
end;
3836

3837
procedure TGLBaseSceneObject.RecTransformationChanged;
3838
var
3839
  i: Integer;
3840
  list: PPointerObjectList;
3841
  matSet: TObjectChanges;
3842
begin
3843
  matSet := [ocAbsoluteMatrix, ocInvAbsoluteMatrix];
3844
  if matSet * FChanges <> matSet then
3845
  begin
3846
    FChanges := FChanges + matSet;
3847
    if Assigned(FChildren) then
3848
    begin
3849
      list := FChildren.List;
3850
      for i := 0 to FChildren.Count - 1 do
3851
        TGLBaseSceneObject(list^[i]).RecTransformationChanged;
3852
    end;
3853
  end;
3854
end;
3855

3856
procedure TGLBaseSceneObject.TransformationChanged;
3857
begin
3858
  if not (ocTransformation in FChanges) then
3859
  begin
3860
    Include(FChanges, ocTransformation);
3861
    RecTransformationChanged;
3862
    if not (csLoading in ComponentState) then
3863
      NotifyChange(Self);
3864
  end;
3865
end;
3866

3867
procedure TGLBaseSceneObject.MoveTo(newParent: TGLBaseSceneObject);
3868
begin
3869
  if newParent = FParent then
3870
    Exit;
3871
  if Assigned(FParent) then
3872
  begin
3873
    FParent.Remove(Self, False);
3874
    FParent := nil;
3875
  end;
3876
  if Assigned(newParent) then
3877
    newParent.AddChild(Self)
3878
  else
3879
    SetScene(nil);
3880
end;
3881

3882
procedure TGLBaseSceneObject.MoveUp;
3883
begin
3884
  if Assigned(parent) then
3885
    parent.MoveChildUp(parent.IndexOfChild(Self));
3886
end;
3887

3888
procedure TGLBaseSceneObject.MoveDown;
3889
begin
3890
  if Assigned(parent) then
3891
    parent.MoveChildDown(parent.IndexOfChild(Self));
3892
end;
3893

3894
procedure TGLBaseSceneObject.MoveFirst;
3895
begin
3896
  if Assigned(parent) then
3897
    parent.MoveChildFirst(parent.IndexOfChild(Self));
3898
end;
3899

3900
procedure TGLBaseSceneObject.MoveLast;
3901
begin
3902
  if Assigned(parent) then
3903
    parent.MoveChildLast(parent.IndexOfChild(Self));
3904
end;
3905

3906
procedure TGLBaseSceneObject.MoveObjectAround(anObject: TGLBaseSceneObject;
3907
  pitchDelta, turnDelta: Single);
3908
var
3909
  originalT2C, normalT2C, normalCameraRight, newPos: TVector;
3910
  pitchNow, dist: Single;
3911
begin
3912
  if Assigned(anObject) then
3913
  begin
3914
    // normalT2C points away from the direction the camera is looking
3915
    originalT2C := VectorSubtract(AbsolutePosition,
3916
      anObject.AbsolutePosition);
3917
    SetVector(normalT2C, originalT2C);
3918
    dist := VectorLength(normalT2C);
3919
    NormalizeVector(normalT2C);
3920
    // normalRight points to the camera's right
3921
    // the camera is pitching around this axis.
3922
    normalCameraRight := VectorCrossProduct(AbsoluteUp, normalT2C);
3923
    if VectorLength(normalCameraRight) < 0.001 then
3924
      SetVector(normalCameraRight, XVector) // arbitrary vector
3925
    else
3926
      NormalizeVector(normalCameraRight);
3927
    // calculate the current pitch.
3928
    // 0 is looking down and PI is looking up
3929
    pitchNow := ArcCos(VectorDotProduct(AbsoluteUp, normalT2C));
3930
    pitchNow := ClampValue(pitchNow + DegToRad(pitchDelta), 0 + 0.025, PI -
3931
      0.025);
3932
    // create a new vector pointing up and then rotate it down
3933
    // into the new position
3934
    SetVector(normalT2C, AbsoluteUp);
3935
    RotateVector(normalT2C, normalCameraRight, -pitchNow);
3936
    RotateVector(normalT2C, AbsoluteUp, -DegToRad(turnDelta));
3937
    ScaleVector(normalT2C, dist);
3938
    newPos := VectorAdd(AbsolutePosition, VectorSubtract(normalT2C,
3939
      originalT2C));
3940
    if Assigned(Parent) then
3941
      newPos := Parent.AbsoluteToLocal(newPos);
3942
    Position.AsVector := newPos;
3943
  end;
3944
end;
3945

3946
procedure TGLBaseSceneObject.MoveObjectAllAround(anObject: TGLBaseSceneObject;
3947
  pitchDelta, turnDelta: Single);
3948
var
3949
  upvector: TVector;
3950
  lookat : TVector;
3951
  rightvector : TVector;
3952
  tempvector: TVector;
3953
  T2C: TVector;
3954

3955
begin
3956

3957
  // if camera has got a target
3958
  if Assigned(anObject) then
3959
  begin
3960
    //vector camera to target
3961
    lookat := VectorNormalize(VectorSubtract(anObject.AbsolutePosition, AbsolutePosition));
3962
    //camera up vector
3963
    upvector := VectorNormalize(AbsoluteUp);
3964

3965
    // if upvector and lookat vector are colinear, it is necessary to compute new up vector
3966
    if Abs(VectorDotProduct(lookat,upvector))>0.99 then
3967
    begin
3968
      //X or Y vector use to generate upvector
3969
      SetVector(tempvector,1,0,0);
3970
      //if lookat is colinear to X vector use Y vector to generate upvector
3971
      if Abs(VectorDotProduct(tempvector,lookat))>0.99 then
3972
      begin
3973
        SetVector(tempvector,0,1,0);
3974
      end;
3975
      upvector:= VectorCrossProduct(tempvector,lookat);
3976
      rightvector := VectorCrossProduct(lookat,upvector);
3977
    end
3978
    else
3979
    begin
3980
      rightvector := VectorCrossProduct(lookat,upvector);
3981
      upvector:= VectorCrossProduct(rightvector,lookat);
3982
    end;
3983
    //now the up right and lookat vector are orthogonal
3984

3985
    // vector Target to camera
3986
    T2C:= VectorSubtract(AbsolutePosition,anObject.AbsolutePosition);
3987
    RotateVector(T2C,rightvector,DegToRad(-PitchDelta));
3988
    RotateVector(T2C,upvector,DegToRad(-TurnDelta));
3989
    AbsolutePosition := VectorAdd(anObject.AbsolutePosition, T2C);
3990

3991
    //now update new up vector
3992
    RotateVector(upvector,rightvector,DegToRad(-PitchDelta));
3993
    AbsoluteUp := upvector;
3994
    AbsoluteDirection := VectorSubtract(anObject.AbsolutePosition,AbsolutePosition);
3995

3996
  end;
3997
end;
3998

3999
procedure TGLBaseSceneObject.CoordinateChanged(Sender: TGLCustomCoordinates);
4000
var
4001
  rightVector: TVector;
4002
begin
4003
  if FIsCalculating then
4004
    Exit;
4005
  FIsCalculating := True;
4006
  try
4007
    if Sender = FDirection then
4008
    begin
4009
      if FDirection.VectorLength = 0 then
4010
        FDirection.DirectVector := ZHmgVector;
4011
      FDirection.Normalize;
4012
      // adjust up vector
4013
      rightVector := VectorCrossProduct(FDirection.AsVector, FUp.AsVector);
4014
      // Rightvector is zero if direction changed exactly by 90 degrees,
4015
      // in this case assume a default vector
4016
      if VectorLength(rightVector) < 1e-5 then
4017
      begin
4018
        rightVector := VectorCrossProduct(ZHmgVector, FUp.AsVector);
4019
        if VectorLength(rightVector) < 1e-5 then
4020
          rightVector := VectorCrossProduct(XHmgVector, FUp.AsVector);
4021
      end;
4022
      FUp.DirectVector := VectorCrossProduct(rightVector, FDirection.AsVector);
4023
      FUp.Normalize;
4024
    end
4025
    else if Sender = FUp then
4026
    begin
4027
      if FUp.VectorLength = 0 then
4028
        FUp.DirectVector := YHmgVector;
4029
      FUp.Normalize;
4030
      // adjust up vector
4031
      rightVector := VectorCrossProduct(FDirection.AsVector, FUp.AsVector);
4032
      // Rightvector is zero if direction changed exactly by 90 degrees,
4033
      // in this case assume a default vector
4034
      if VectorLength(rightVector) < 1e-5 then
4035
      begin
4036
        rightVector := VectorCrossProduct(ZHmgVector, FUp.AsVector);
4037
        if VectorLength(rightVector) < 1e-5 then
4038
          rightVector := VectorCrossProduct(XHmgVector, FUp.AsVector);
4039
      end;
4040
      FDirection.DirectVector := VectorCrossProduct(FUp.AsVector, RightVector);
4041
      FDirection.Normalize;
4042
    end;
4043
    TransformationChanged;
4044
  finally
4045
    FIsCalculating := False;
4046
  end;
4047
end;
4048

4049
procedure TGLBaseSceneObject.DoProgress(const progressTime: TProgressTimes);
4050
var
4051
  i: Integer;
4052
begin
4053
  if Assigned(FChildren) then
4054
    for i := FChildren.Count - 1 downto 0 do
4055
      TGLBaseSceneObject(FChildren.List^[i]).DoProgress(progressTime);
4056
  if Assigned(FGLBehaviours) then
4057
    FGLBehaviours.DoProgress(progressTime);
4058
  if Assigned(FGLObjectEffects) then
4059
    FGLObjectEffects.DoProgress(progressTime);
4060
  if Assigned(FOnProgress) then
4061
    with progressTime do
4062
      FOnProgress(Self, deltaTime, newTime);
4063
end;
4064

4065
procedure TGLBaseSceneObject.Insert(aIndex: Integer; aChild:
4066
  TGLBaseSceneObject);
4067
begin
4068
  if not Assigned(FChildren) then
4069
    FChildren := TPersistentObjectList.Create;
4070
  with FChildren do
4071
  begin
4072
    if Assigned(aChild.FParent) then
4073
      aChild.FParent.Remove(aChild, False);
4074
    Insert(aIndex, aChild);
4075
  end;
4076
  aChild.FParent := Self;
4077
  if AChild.FScene <> FScene then
4078
    AChild.DestroyHandles;
4079
  AChild.SetScene(FScene);
4080
  if Assigned(FScene) then
4081
    FScene.AddLights(aChild);
4082
  AChild.TransformationChanged;
4083

4084
  aChild.DoOnAddedToParent;
4085
end;
4086

4087
procedure TGLBaseSceneObject.Remove(aChild: TGLBaseSceneObject; keepChildren:
4088
  Boolean);
4089
var
4090
  I: Integer;
4091
begin
4092
  if not Assigned(FChildren) then
4093
    Exit;
4094
  if aChild.Parent = Self then
4095
  begin
4096
    if Assigned(FScene) then
4097
      FScene.RemoveLights(aChild);
4098
    if aChild.Owner = Self then
4099
      RemoveComponent(aChild);
4100
    FChildren.Remove(aChild);
4101
    aChild.FParent := nil;
4102
    if keepChildren then
4103
    begin
4104
      BeginUpdate;
4105
      if aChild.Count <> 0 then
4106
        for I := aChild.Count - 1 downto 0 do
4107
          if not IsSubComponent(aChild.Children[I]) then
4108
            aChild.Children[I].MoveTo(Self);
4109
      EndUpdate;
4110
    end
4111
    else
4112
      NotifyChange(Self);
4113
  end;
4114
end;
4115

4116
function TGLBaseSceneObject.IndexOfChild(aChild: TGLBaseSceneObject): Integer;
4117
begin
4118
  if Assigned(FChildren) then
4119
    Result := FChildren.IndexOf(aChild)
4120
  else
4121
    Result := -1;
4122
end;
4123

4124
function TGLBaseSceneObject.FindChild(const aName: string;
4125
  ownChildrenOnly: Boolean): TGLBaseSceneObject;
4126
var
4127
  i: integer;
4128
  res: TGLBaseSceneObject;
4129
begin
4130
  res := nil;
4131
  Result := nil;
4132
  if not Assigned(FChildren) then
4133
    Exit;
4134
  for i := 0 to FChildren.Count - 1 do
4135
  begin
4136
    if CompareText(TGLBaseSceneObject(FChildren[i]).Name, aName) = 0 then
4137
    begin
4138
      res := TGLBaseSceneObject(FChildren[i]);
4139
      Break;
4140
    end;
4141
  end;
4142
  if not ownChildrenOnly then
4143
  begin
4144
    for i := 0 to FChildren.Count - 1 do
4145
      with TGLBaseSceneObject(FChildren[i]) do
4146
      begin
4147
        Result := FindChild(aName, ownChildrenOnly);
4148
        if Assigned(Result) then
4149
          Break;
4150
      end;
4151
  end;
4152
  if not Assigned(Result) then
4153
    Result := res;
4154
end;
4155

4156
procedure TGLBaseSceneObject.ExchangeChildren(anIndex1, anIndex2: Integer);
4157
begin
4158
  Assert(Assigned(FChildren), 'No children found!');
4159
  FChildren.Exchange(anIndex1, anIndex2);
4160
  NotifyChange(Self);
4161
end;
4162

4163
procedure TGLBaseSceneObject.ExchangeChildrenSafe(anIndex1, anIndex2: Integer);
4164
begin
4165
  Assert(Assigned(FChildren), 'No children found!');
4166
  if (anIndex1 < FChildren.Count) and (anIndex2 < FChildren.Count) and
4167
    (anIndex1 > -1) and (anIndex2 > -1) and (anIndex1 <> anIndex2) then
4168
  begin
4169
    FChildren.Exchange(anIndex1, anIndex2);
4170
    NotifyChange(Self);
4171
  end;
4172
end;
4173

4174
procedure TGLBaseSceneObject.MoveChildUp(anIndex: Integer);
4175
begin
4176
  Assert(Assigned(FChildren), 'No children found!');
4177
  if anIndex > 0 then
4178
  begin
4179
    FChildren.Exchange(anIndex, anIndex - 1);
4180
    NotifyChange(Self);
4181
  end;
4182
end;
4183

4184
procedure TGLBaseSceneObject.MoveChildDown(anIndex: Integer);
4185
begin
4186
  Assert(Assigned(FChildren), 'No children found!');
4187
  if anIndex < FChildren.Count - 1 then
4188
  begin
4189
    FChildren.Exchange(anIndex, anIndex + 1);
4190
    NotifyChange(Self);
4191
  end;
4192
end;
4193

4194
procedure TGLBaseSceneObject.MoveChildFirst(anIndex: Integer);
4195
begin
4196
  Assert(Assigned(FChildren), 'No children found!');
4197
  if anIndex <> 0 then
4198
  begin
4199
    FChildren.Move(anIndex, 0);
4200
    NotifyChange(Self);
4201
  end;
4202
end;
4203

4204
procedure TGLBaseSceneObject.MoveChildLast(anIndex: Integer);
4205
begin
4206
  Assert(Assigned(FChildren), 'No children found!');
4207
  if anIndex <> FChildren.Count - 1 then
4208
  begin
4209
    FChildren.Move(anIndex, FChildren.Count - 1);
4210
    NotifyChange(Self);
4211
  end;
4212
end;
4213

4214
// Render
4215
//
4216

4217
procedure TGLBaseSceneObject.Render(var ARci: TGLRenderContextInfo);
4218
var
4219
  shouldRenderSelf, shouldRenderChildren: Boolean;
4220
  aabb: TAABB;
4221
  master: TObject;
4222
begin
4223
{$IFDEF GLS_OPENGL_DEBUG}
4224
  if GL.GREMEDY_string_marker then
4225
    GL.StringMarkerGREMEDY(
4226
      Length(Name) + Length('.Render'), PGLChar(TGLString(Name + '.Render')));
4227
{$ENDIF}
4228
  if (ARci.drawState = dsPicking) and not FPickable then
4229
    exit;
4230
  // visibility culling determination
4231
  if ARci.visibilityCulling in [vcObjectBased, vcHierarchical] then
4232
  begin
4233
    if ARci.visibilityCulling = vcObjectBased then
4234
    begin
4235
      shouldRenderSelf := (osNoVisibilityCulling in ObjectStyle)
4236
        or (not IsVolumeClipped(BarycenterAbsolutePosition,
4237
        BoundingSphereRadius,
4238
        ARci.rcci.frustum));
4239
      shouldRenderChildren := Assigned(FChildren);
4240
    end
4241
    else
4242
    begin // vcHierarchical
4243
      aabb := AxisAlignedBoundingBox;
4244
      shouldRenderSelf := (osNoVisibilityCulling in ObjectStyle)
4245
        or (not IsVolumeClipped(aabb.min, aabb.max, ARci.rcci.frustum));
4246
      shouldRenderChildren := shouldRenderSelf and Assigned(FChildren);
4247
    end;
4248
    if not (shouldRenderSelf or shouldRenderChildren) then
4249
      Exit;
4250
  end
4251
  else
4252
  begin
4253
    Assert(ARci.visibilityCulling in [vcNone, vcInherited],
4254
      'Unknown visibility culling option');
4255
    shouldRenderSelf := True;
4256
    shouldRenderChildren := Assigned(FChildren);
4257
  end;
4258

4259
  // Prepare Matrix and PickList stuff
4260
  ARci.PipelineTransformation.Push;
4261
  if ocTransformation in FChanges then
4262
    RebuildMatrix;
4263

4264
  if ARci.proxySubObject then
4265
    ARci.PipelineTransformation.ModelMatrix :=
4266
      MatrixMultiply(LocalMatrix^, ARci.PipelineTransformation.ModelMatrix)
4267
  else
4268
    ARci.PipelineTransformation.ModelMatrix := AbsoluteMatrix;
4269

4270
  master := nil;
4271
  if ARci.drawState = dsPicking then
4272
  begin
4273
    if ARci.proxySubObject then
4274
      master := TGLSceneBuffer(ARci.buffer).FSelector.CurrentObject;
4275
    TGLSceneBuffer(ARci.buffer).FSelector.CurrentObject := Self;
4276
  end;
4277

4278
  // Start rendering
4279
  if shouldRenderSelf then
4280
  begin
4281
    vCurrentRenderingObject := Self;
4282
{$IFNDEF GLS_OPTIMIZATIONS}
4283
    if FShowAxes then
4284
      DrawAxes(ARci, $CCCC);
4285
{$ENDIF}
4286
    if Assigned(FGLObjectEffects) and (FGLObjectEffects.Count > 0) then
4287
    begin
4288
      ARci.PipelineTransformation.Push;
4289
      FGLObjectEffects.RenderPreEffects(ARci);
4290
      ARci.PipelineTransformation.Pop;
4291

4292
      ARci.PipelineTransformation.Push;
4293
      if osIgnoreDepthBuffer in ObjectStyle then
4294
      begin
4295
        ARci.GLStates.Disable(stDepthTest);
4296
        DoRender(ARci, True, shouldRenderChildren);
4297
        ARci.GLStates.Enable(stDepthTest);
4298
      end
4299
      else
4300
        DoRender(ARci, True, shouldRenderChildren);
4301

4302
      FGLObjectEffects.RenderPostEffects(ARci);
4303
      ARci.PipelineTransformation.Pop;
4304
    end
4305
    else
4306
    begin
4307
      if osIgnoreDepthBuffer in ObjectStyle then
4308
      begin
4309
        ARci.GLStates.Disable(stDepthTest);
4310
        DoRender(ARci, True, shouldRenderChildren);
4311
        ARci.GLStates.Enable(stDepthTest);
4312
      end
4313
      else
4314
        DoRender(ARci, True, shouldRenderChildren);
4315

4316
    end;
4317
    vCurrentRenderingObject := nil;
4318
  end
4319
  else
4320
  begin
4321
    if (osIgnoreDepthBuffer in ObjectStyle) and
4322
      TGLSceneBuffer(ARCi.buffer).DepthTest then
4323
    begin
4324
      ARci.GLStates.Disable(stDepthTest);
4325
      DoRender(ARci, False, shouldRenderChildren);
4326
      ARci.GLStates.Enable(stDepthTest);
4327
    end
4328
    else
4329
      DoRender(ARci, False, shouldRenderChildren);
4330
  end;
4331
  // Pop Name & Matrix
4332
  if Assigned(master) then
4333
    TGLSceneBuffer(ARci.buffer).FSelector.CurrentObject := master;
4334
  ARci.PipelineTransformation.Pop;
4335
end;
4336

4337
// DoRender
4338
//
4339

4340
procedure TGLBaseSceneObject.DoRender(var ARci: TGLRenderContextInfo;
4341
  ARenderSelf, ARenderChildren: Boolean);
4342
begin
4343
  // start rendering self
4344
  if ARenderSelf then
4345
  begin
4346
    if (osDirectDraw in ObjectStyle) or ARci.amalgamating then
4347
      BuildList(ARci)
4348
    else
4349
      ARci.GLStates.CallList(GetHandle(ARci));
4350
  end;
4351
  // start rendering children (if any)
4352
  if ARenderChildren then
4353
    Self.RenderChildren(0, Count - 1, ARci);
4354
end;
4355

4356
// RenderChildren
4357
//
4358

4359
procedure TGLBaseSceneObject.RenderChildren(firstChildIndex, lastChildIndex:
4360
  Integer;
4361
  var rci: TGLRenderContextInfo);
4362
var
4363
  i: Integer;
4364
  objList: TPersistentObjectList;
4365
  distList: TSingleList;
4366
  plist: PPointerObjectList;
4367
  obj: TGLBaseSceneObject;
4368
  oldSorting: TGLObjectsSorting;
4369
  oldCulling: TGLVisibilityCulling;
4370
begin
4371
  if not Assigned(FChildren) then
4372
    Exit;
4373
  oldCulling := rci.visibilityCulling;
4374
  if Self.VisibilityCulling <> vcInherited then
4375
    rci.visibilityCulling := Self.VisibilityCulling;
4376
  if lastChildIndex = firstChildIndex then
4377
  begin
4378
    obj := TGLBaseSceneObject(FChildren.List^[firstChildIndex]);
4379
    if obj.Visible then
4380
      obj.Render(rci)
4381
  end
4382
  else if lastChildIndex > firstChildIndex then
4383
  begin
4384
    oldSorting := rci.objectsSorting;
4385
    if Self.ObjectsSorting <> osInherited then
4386
      rci.objectsSorting := Self.ObjectsSorting;
4387
    case rci.objectsSorting of
4388
      osNone:
4389
        begin
4390
          plist := FChildren.List;
4391
          for i := firstChildIndex to lastChildIndex do
4392
          begin
4393
            obj := TGLBaseSceneObject(plist^[i]);
4394
            if obj.Visible then
4395
              obj.Render(rci);
4396
          end;
4397
        end;
4398
      osRenderFarthestFirst, osRenderBlendedLast, osRenderNearestFirst:
4399
        begin
4400
          distList := TSingleList.Create;
4401
          objList := TPersistentObjectList.Create;
4402
          distList.GrowthDelta := lastChildIndex + 1; // no reallocations
4403
          objList.GrowthDelta := distList.GrowthDelta;
4404
          try
4405
            case rci.objectsSorting of
4406
              osRenderBlendedLast:
4407
                // render opaque stuff
4408
                for i := firstChildIndex to lastChildIndex do
4409
                begin
4410
                  obj := TGLBaseSceneObject(FChildren.List^[i]);
4411
                  if obj.Visible then
4412
                  begin
4413
                    if not obj.Blended then
4414
                      obj.Render(rci)
4415
                    else
4416
                    begin
4417
                      objList.Add(obj);
4418
                      distList.Add(1 +
4419
                        obj.BarycenterSqrDistanceTo(rci.cameraPosition));
4420
                    end;
4421
                  end;
4422
                end;
4423
              osRenderFarthestFirst:
4424
                for i := firstChildIndex to lastChildIndex do
4425
                begin
4426
                  obj := TGLBaseSceneObject(FChildren.List^[i]);
4427
                  if obj.Visible then
4428
                  begin
4429
                    objList.Add(obj);
4430
                    distList.Add(1 +
4431
                      obj.BarycenterSqrDistanceTo(rci.cameraPosition));
4432
                  end;
4433
                end;
4434
              osRenderNearestFirst:
4435
                for i := firstChildIndex to lastChildIndex do
4436
                begin
4437
                  obj := TGLBaseSceneObject(FChildren.List^[i]);
4438
                  if obj.Visible then
4439
                  begin
4440
                    objList.Add(obj);
4441
                    distList.Add(-1 -
4442
                      obj.BarycenterSqrDistanceTo(rci.cameraPosition));
4443
                  end;
4444
                end;
4445
            else
4446
              Assert(False);
4447
            end;
4448
            if distList.Count > 0 then
4449
            begin
4450
              if distList.Count > 1 then
4451
                FastQuickSortLists(0, distList.Count - 1, distList, objList);
4452
              plist := objList.List;
4453
              for i := objList.Count - 1 downto 0 do
4454
                TGLBaseSceneObject(plist^[i]).Render(rci);
4455
            end;
4456
          finally
4457
            objList.Free;
4458
            distList.Free;
4459
          end;
4460
        end;
4461
    else
4462
      Assert(False);
4463
    end;
4464
    rci.objectsSorting := oldSorting;
4465
  end;
4466
  rci.visibilityCulling := oldCulling;
4467
end;
4468

4469
// NotifyChange
4470
//
4471

4472
procedure TGLBaseSceneObject.NotifyChange(Sender: TObject);
4473
begin
4474
  if Assigned(FScene) and (not IsUpdating) then
4475
    FScene.NotifyChange(Self);
4476
end;
4477

4478
// GetMatrix
4479
//
4480

4481
function TGLBaseSceneObject.GetMatrix: TMatrix;
4482
begin
4483
  RebuildMatrix;
4484
  Result := FLocalMatrix^;
4485
end;
4486

4487
// MatrixAsAddress
4488
//
4489

4490
function TGLBaseSceneObject.MatrixAsAddress: PMatrix;
4491
begin
4492
  RebuildMatrix;
4493
  Result := FLocalMatrix;
4494
end;
4495

4496
// SetMatrix
4497
//
4498

4499
procedure TGLBaseSceneObject.SetMatrix(const aValue: TMatrix);
4500
begin
4501
  FLocalMatrix^ := aValue;
4502
  FDirection.DirectVector := VectorNormalize(FLocalMatrix^.V[2]);
4503
  FUp.DirectVector := VectorNormalize(FLocalMatrix^.V[1]);
4504
  Scale.SetVector(VectorLength(FLocalMatrix^.V[0]),
4505
    VectorLength(FLocalMatrix^.V[1]),
4506
    VectorLength(FLocalMatrix^.V[2]), 0);
4507
  FPosition.DirectVector := FLocalMatrix^.V[3];
4508
  TransformationChanged;
4509
end;
4510

4511
procedure TGLBaseSceneObject.SetPosition(APosition: TGLCoordinates);
4512
begin
4513
  FPosition.SetPoint(APosition.DirectX, APosition.DirectY, APosition.DirectZ);
4514
end;
4515

4516
procedure TGLBaseSceneObject.SetDirection(AVector: TGLCoordinates);
4517
begin
4518
  if not VectorIsNull(AVector.DirectVector) then
4519
    FDirection.SetVector(AVector.DirectX, AVector.DirectY, AVector.DirectZ);
4520
end;
4521

4522
procedure TGLBaseSceneObject.SetUp(AVector: TGLCoordinates);
4523
begin
4524
  if not VectorIsNull(AVector.DirectVector) then
4525
    FUp.SetVector(AVector.DirectX, AVector.DirectY, AVector.DirectZ);
4526
end;
4527

4528
function TGLBaseSceneObject.GetVisible: Boolean;
4529
begin
4530
  Result := FVisible;
4531
end;
4532

4533
function TGLBaseSceneObject.GetPickable: Boolean;
4534
begin
4535
  Result := FPickable;
4536
end;
4537

4538
// SetVisible
4539
//
4540

4541
procedure TGLBaseSceneObject.SetVisible(aValue: Boolean);
4542
begin
4543
  if FVisible <> aValue then
4544
  begin
4545
    FVisible := AValue;
4546
    NotifyChange(Self);
4547
  end;
4548
end;
4549

4550
// SetPickable
4551
//
4552

4553
procedure TGLBaseSceneObject.SetPickable(aValue: Boolean);
4554
begin
4555
  if FPickable <> aValue then
4556
  begin
4557
    FPickable := AValue;
4558
    NotifyChange(Self);
4559
  end;
4560
end;
4561

4562
// SetObjectsSorting
4563
//
4564

4565
procedure TGLBaseSceneObject.SetObjectsSorting(const val: TGLObjectsSorting);
4566
begin
4567
  if FObjectsSorting <> val then
4568
  begin
4569
    FObjectsSorting := val;
4570
    NotifyChange(Self);
4571
  end;
4572
end;
4573

4574
// SetVisibilityCulling
4575
//
4576

4577
procedure TGLBaseSceneObject.SetVisibilityCulling(const val:
4578
  TGLVisibilityCulling);
4579
begin
4580
  if FVisibilityCulling <> val then
4581
  begin
4582
    FVisibilityCulling := val;
4583
    NotifyChange(Self);
4584
  end;
4585
end;
4586

4587
// SetBehaviours
4588
//
4589

4590
procedure TGLBaseSceneObject.SetBehaviours(const val: TGLBehaviours);
4591
begin
4592
  Behaviours.Assign(val);
4593
end;
4594

4595
// GetBehaviours
4596
//
4597

4598
function TGLBaseSceneObject.GetBehaviours: TGLBehaviours;
4599
begin
4600
  if not Assigned(FGLBehaviours) then
4601
    FGLBehaviours := TGLBehaviours.Create(Self);
4602
  Result := FGLBehaviours;
4603
end;
4604

4605
// SetEffects
4606
//
4607

4608
procedure TGLBaseSceneObject.SetEffects(const val: TGLObjectEffects);
4609
begin
4610
  Effects.Assign(val);
4611
end;
4612

4613
// GetEffects
4614
//
4615

4616
function TGLBaseSceneObject.GetEffects: TGLObjectEffects;
4617
begin
4618
  if not Assigned(FGLObjectEffects) then
4619
    FGLObjectEffects := TGLObjectEffects.Create(Self);
4620
  Result := FGLObjectEffects;
4621
end;
4622

4623
// SetScene
4624
//
4625

4626
procedure TGLBaseSceneObject.SetScene(const value: TGLScene);
4627
var
4628
  i: Integer;
4629
begin
4630
  if value <> FScene then
4631
  begin
4632
    // must be freed, the new scene may be using a non-compatible RC
4633
    if FScene <> nil then
4634
      DestroyHandles;
4635
    FScene := value;
4636
    // propagate for childs
4637
    if Assigned(FChildren) then
4638
      for i := 0 to FChildren.Count - 1 do
4639
        Children[I].SetScene(FScene);
4640
  end;
4641
end;
4642

4643
// Translate
4644
//
4645

4646
procedure TGLBaseSceneObject.Translate(tx, ty, tz: Single);
4647
begin
4648
  FPosition.Translate(AffineVectorMake(tx, ty, tz));
4649
end;
4650

4651
// GetAbsoluteAffinePosition
4652
//
4653

4654
function TGLBaseSceneObject.GetAbsoluteAffinePosition: TAffineVector;
4655
var
4656
  temp: TVector;
4657
begin
4658
  temp := GetAbsolutePosition;
4659
  Result := AffineVectorMake(temp.V[0], temp.V[1], temp.V[2]);
4660
end;
4661

4662
// GetAbsoluteAffineDirection
4663
//
4664

4665
function TGLBaseSceneObject.GetAbsoluteAffineDirection: TAffineVector;
4666
var
4667
  temp: TVector;
4668
begin
4669
  temp := GetAbsoluteDirection;
4670
  Result := AffineVectorMake(temp.V[0], temp.V[1], temp.V[2]);
4671
end;
4672

4673
// GetAbsoluteAffineUp
4674
//
4675

4676
function TGLBaseSceneObject.GetAbsoluteAffineUp: TAffineVector;
4677
var
4678
  temp: TVector;
4679
begin
4680
  temp := GetAbsoluteUp;
4681
  Result := AffineVectorMake(temp.V[0], temp.V[1], temp.V[2]);
4682
end;
4683

4684
// SetAbsoluteAffinePosition
4685
//
4686

4687
procedure TGLBaseSceneObject.SetAbsoluteAffinePosition(const Value:
4688
  TAffineVector);
4689
begin
4690
  SetAbsolutePosition(VectorMake(Value, 1));
4691
end;
4692

4693
// SetAbsoluteAffineUp
4694
//
4695

4696
procedure TGLBaseSceneObject.SetAbsoluteAffineUp(const v: TAffineVector);
4697
begin
4698
  SetAbsoluteUp(VectorMake(v, 1));
4699
end;
4700

4701
// SetAbsoluteAffineDirection
4702
//
4703

4704
procedure TGLBaseSceneObject.SetAbsoluteAffineDirection(const v: TAffineVector);
4705
begin
4706
  SetAbsoluteDirection(VectorMake(v, 1));
4707
end;
4708

4709
// AffineLeftVector
4710
//
4711

4712
function TGLBaseSceneObject.AffineLeftVector: TAffineVector;
4713
begin
4714
  Result := AffineVectorMake(LeftVector);
4715
end;
4716

4717
// AffineRight
4718
//
4719

4720
function TGLBaseSceneObject.AffineRight: TAffineVector;
4721
begin
4722
  Result := AffineVectorMake(Right);
4723
end;
4724

4725
// DistanceTo
4726
//
4727

4728
function TGLBaseSceneObject.DistanceTo(const pt: TAffineVector): Single;
4729
begin
4730
  Result := VectorDistance(AbsoluteAffinePosition, pt);
4731
end;
4732

4733
// SqrDistanceTo
4734
//
4735

4736
function TGLBaseSceneObject.SqrDistanceTo(const pt: TAffineVector): Single;
4737
begin
4738
  Result := VectorDistance2(AbsoluteAffinePosition, pt);
4739
end;
4740

4741
// DoOnAddedToParent
4742
//
4743

4744
procedure TGLBaseSceneObject.DoOnAddedToParent;
4745
begin
4746
  if Assigned(FOnAddedToParent) then
4747
    FOnAddedToParent(self);
4748
end;
4749

4750
// GetAbsoluteAffineScale
4751
//
4752

4753
function TGLBaseSceneObject.GetAbsoluteAffineScale: TAffineVector;
4754
begin
4755
  Result := AffineVectorMake(GetAbsoluteScale);
4756
end;
4757

4758
// SetAbsoluteAffineScale
4759
//
4760

4761
procedure TGLBaseSceneObject.SetAbsoluteAffineScale(
4762
  const Value: TAffineVector);
4763
begin
4764
  SetAbsoluteScale(VectorMake(Value, GetAbsoluteScale.V[3]));
4765
end;
4766

4767
// ------------------
4768
// ------------------ TGLBaseBehaviour ------------------
4769
// ------------------
4770

4771
// Create
4772
//
4773

4774
constructor TGLBaseBehaviour.Create(aOwner: TGLXCollection);
4775
begin
4776
  inherited Create(aOwner);
4777
  // nothing more, yet
4778
end;
4779

4780
// Destroy
4781
//
4782

4783
destructor TGLBaseBehaviour.Destroy;
4784
begin
4785
  // nothing more, yet
4786
  inherited Destroy;
4787
end;
4788

4789
// SetName
4790
//
4791

4792
procedure TGLBaseBehaviour.SetName(const val: string);
4793
begin
4794
  inherited SetName(val);
4795
  if Assigned(vGLBehaviourNameChangeEvent) then
4796
    vGLBehaviourNameChangeEvent(Self);
4797
end;
4798

4799
// WriteToFiler
4800
//
4801

4802
procedure TGLBaseBehaviour.WriteToFiler(writer: TWriter);
4803
begin
4804
  inherited;
4805

4806
  with writer do
4807
  begin
4808
    WriteInteger(0); // Archive Version 0
4809
    // nothing more, yet
4810
  end;
4811
end;
4812

4813
// ReadFromFiler
4814
//
4815

4816
procedure TGLBaseBehaviour.ReadFromFiler(reader: TReader);
4817
begin
4818
  if Owner.ArchiveVersion > 0 then
4819
    inherited;
4820

4821
  with reader do
4822
  begin
4823
    if ReadInteger <> 0 then
4824
      Assert(False);
4825
    // nothing more, yet
4826
  end;
4827
end;
4828

4829
// OwnerBaseSceneObject
4830
//
4831

4832
function TGLBaseBehaviour.OwnerBaseSceneObject: TGLBaseSceneObject;
4833
begin
4834
  Result := TGLBaseSceneObject(Owner.Owner);
4835
end;
4836

4837
// DoProgress
4838
//
4839

4840
procedure TGLBaseBehaviour.DoProgress(const progressTime: TProgressTimes);
4841
begin
4842
  // does nothing
4843
end;
4844

4845
// ------------------
4846
// ------------------ TGLBehaviours ------------------
4847
// ------------------
4848

4849
// Create
4850
//
4851

4852
constructor TGLBehaviours.Create(aOwner: TPersistent);
4853
begin
4854
  Assert(aOwner is TGLBaseSceneObject);
4855
  inherited Create(aOwner);
4856
end;
4857

4858
// GetNamePath
4859
//
4860

4861
function TGLBehaviours.GetNamePath: string;
4862
var
4863
  s: string;
4864
begin
4865
  Result := ClassName;
4866
  if GetOwner = nil then
4867
    Exit;
4868
  s := GetOwner.GetNamePath;
4869
  if s = '' then
4870
    Exit;
4871
  Result := s + '.Behaviours';
4872
end;
4873

4874
// ItemsClass
4875
//
4876

4877
class function TGLBehaviours.ItemsClass: TGLXCollectionItemClass;
4878
begin
4879
  Result := TGLBehaviour;
4880
end;
4881

4882
// GetBehaviour
4883
//
4884

4885
function TGLBehaviours.GetBehaviour(index: Integer): TGLBehaviour;
4886
begin
4887
  Result := TGLBehaviour(Items[index]);
4888
end;
4889

4890
// CanAdd
4891
//
4892

4893
function TGLBehaviours.CanAdd(aClass: TGLXCollectionItemClass): Boolean;
4894
begin
4895
  Result := (not aClass.InheritsFrom(TGLObjectEffect)) and (inherited
4896
    CanAdd(aClass));
4897
end;
4898

4899
// DoProgress
4900
//
4901

4902
procedure TGLBehaviours.DoProgress(const progressTimes: TProgressTimes);
4903
var
4904
  i: Integer;
4905
begin
4906
  for i := 0 to Count - 1 do
4907
    TGLBehaviour(Items[i]).DoProgress(progressTimes);
4908
end;
4909

4910
// ------------------
4911
// ------------------ TGLObjectEffect ------------------
4912
// ------------------
4913

4914
// WriteToFiler
4915
//
4916

4917
procedure TGLObjectEffect.WriteToFiler(writer: TWriter);
4918
begin
4919
  inherited;
4920
  with writer do
4921
  begin
4922
    WriteInteger(0); // Archive Version 0
4923
    // nothing more, yet
4924
  end;
4925
end;
4926

4927
// ReadFromFiler
4928
//
4929

4930
procedure TGLObjectEffect.ReadFromFiler(reader: TReader);
4931
begin
4932
  if Owner.ArchiveVersion > 0 then
4933
    inherited;
4934

4935
  with reader do
4936
  begin
4937
    if ReadInteger <> 0 then
4938
      Assert(False);
4939
    // nothing more, yet
4940
  end;
4941
end;
4942

4943
// Render
4944
//
4945

4946
procedure TGLObjectEffect.Render(var rci: TGLRenderContextInfo);
4947
begin
4948
  // nothing here, this implem is just to avoid "abstract error"
4949
end;
4950

4951
// ------------------
4952
// ------------------ TGLObjectEffects ------------------
4953
// ------------------
4954

4955
// Create
4956
//
4957

4958
constructor TGLObjectEffects.Create(aOwner: TPersistent);
4959
begin
4960
  Assert(aOwner is TGLBaseSceneObject);
4961
  inherited Create(aOwner);
4962
end;
4963

4964
// GetNamePath
4965
//
4966

4967
function TGLObjectEffects.GetNamePath: string;
4968
var
4969
  s: string;
4970
begin
4971
  Result := ClassName;
4972
  if GetOwner = nil then
4973
    Exit;
4974
  s := GetOwner.GetNamePath;
4975
  if s = '' then
4976
    Exit;
4977
  Result := s + '.Effects';
4978
end;
4979

4980
// ItemsClass
4981
//
4982

4983
class function TGLObjectEffects.ItemsClass: TGLXCollectionItemClass;
4984
begin
4985
  Result := TGLObjectEffect;
4986
end;
4987

4988
// GetEffect
4989
//
4990

4991
function TGLObjectEffects.GetEffect(index: Integer): TGLObjectEffect;
4992
begin
4993
  Result := TGLObjectEffect(Items[index]);
4994
end;
4995

4996
// CanAdd
4997
//
4998

4999
function TGLObjectEffects.CanAdd(aClass: TGLXCollectionItemClass): Boolean;
5000
begin
5001
  Result := (aClass.InheritsFrom(TGLObjectEffect)) and (inherited
5002
    CanAdd(aClass));
5003
end;
5004

5005
// DoProgress
5006
//
5007

5008
procedure TGLObjectEffects.DoProgress(const progressTime: TProgressTimes);
5009
var
5010
  i: Integer;
5011
begin
5012
  for i := 0 to Count - 1 do
5013
    TGLObjectEffect(Items[i]).DoProgress(progressTime);
5014
end;
5015

5016
// RenderPreEffects
5017
//
5018

5019
procedure TGLObjectEffects.RenderPreEffects(var rci: TGLRenderContextInfo);
5020
var
5021
  i: Integer;
5022
  effect: TGLObjectEffect;
5023
begin
5024
  for i := 0 to Count - 1 do
5025
  begin
5026
    effect := TGLObjectEffect(Items[i]);
5027
    if effect is TGLObjectPreEffect then
5028
      effect.Render(rci);
5029
  end;
5030
end;
5031

5032
// RenderPostEffects
5033
//
5034

5035
procedure TGLObjectEffects.RenderPostEffects(var rci: TGLRenderContextInfo);
5036
var
5037
  i: Integer;
5038
  effect: TGLObjectEffect;
5039
begin
5040
  for i := 0 to Count - 1 do
5041
  begin
5042
    effect := TGLObjectEffect(Items[i]);
5043
    if effect is TGLObjectPostEffect then
5044
      effect.Render(rci)
5045
    else if Assigned(rci.afterRenderEffects) and (effect is TGLObjectAfterEffect) then
5046
      rci.afterRenderEffects.Add(effect);
5047
  end;
5048
end;
5049

5050
// ------------------
5051
// ------------------ TGLCustomSceneObject ------------------
5052
// ------------------
5053

5054
constructor TGLCustomSceneObject.Create(AOwner: TComponent);
5055
begin
5056
  inherited Create(AOwner);
5057
  FMaterial := TGLMaterial.Create(Self);
5058
end;
5059

5060
destructor TGLCustomSceneObject.Destroy;
5061
begin
5062
  inherited Destroy;
5063
  FMaterial.Free;
5064
end;
5065

5066
procedure TGLCustomSceneObject.Assign(Source: TPersistent);
5067
begin
5068
  if Source is TGLCustomSceneObject then
5069
  begin
5070
    FMaterial.Assign(TGLCustomSceneObject(Source).FMaterial);
5071
    FHint := TGLCustomSceneObject(Source).FHint;
5072
  end;
5073
  inherited Assign(Source);
5074
end;
5075

5076
function TGLCustomSceneObject.Blended: Boolean;
5077
begin
5078
  Result := Material.Blended;
5079
end;
5080

5081
procedure TGLCustomSceneObject.Loaded;
5082
begin
5083
  inherited;
5084
  FMaterial.Loaded;
5085
end;
5086

5087
procedure TGLCustomSceneObject.SetGLMaterial(AValue: TGLMaterial);
5088
begin
5089
  FMaterial.Assign(AValue);
5090
  NotifyChange(Self);
5091
end;
5092

5093
procedure TGLCustomSceneObject.DestroyHandle;
5094
begin
5095
  inherited;
5096
  FMaterial.DestroyHandles;
5097
end;
5098

5099
// DoRender
5100
//
5101

5102
procedure TGLCustomSceneObject.DoRender(var ARci: TGLRenderContextInfo;
5103
  ARenderSelf, ARenderChildren: Boolean);
5104
begin
5105
  // start rendering self
5106
  if ARenderSelf then
5107
    if ARci.ignoreMaterials then
5108
      if (osDirectDraw in ObjectStyle) or ARci.amalgamating then
5109
        BuildList(ARci)
5110
      else
5111
        ARci.GLStates.CallList(GetHandle(ARci))
5112
    else
5113
    begin
5114
      FMaterial.Apply(ARci);
5115
      repeat
5116
        if (osDirectDraw in ObjectStyle) or ARci.amalgamating then
5117
          BuildList(ARci)
5118
        else
5119
          ARci.GLStates.CallList(GetHandle(ARci));
5120
      until not FMaterial.UnApply(ARci);
5121
    end;
5122
  // start rendering children (if any)
5123
  if ARenderChildren then
5124
    Self.RenderChildren(0, Count - 1, ARci);
5125
end;
5126

5127
// ------------------
5128
// ------------------ TGLSceneRootObject ------------------
5129
// ------------------
5130

5131
constructor TGLSceneRootObject.Create(AOwner: TComponent);
5132
begin
5133
  Assert(AOwner is TGLScene);
5134
  inherited Create(AOwner);
5135
  ObjectStyle := ObjectStyle + [osDirectDraw];
5136
  FScene := TGLScene(AOwner);
5137
end;
5138

5139
// ------------------
5140
// ------------------ TGLCamera ------------------
5141
// ------------------
5142

5143
constructor TGLCamera.Create(aOwner: TComponent);
5144
begin
5145
  inherited Create(aOwner);
5146
  FFocalLength := 50;
5147
  FDepthOfView := 100;
5148
  FNearPlaneBias := 1;
5149
  FDirection.Initialize(VectorMake(0, 0, -1, 0));
5150
  FCameraStyle := csPerspective;
5151
  FSceneScale := 1;
5152
  FDesign := False;
5153
  FFOVY := -1;
5154
  FKeepFOVMode := ckmHorizontalFOV;
5155
end;
5156

5157
destructor TGLCamera.Destroy;
5158
begin
5159
  TargetObject := nil;
5160
  inherited;
5161
end;
5162

5163
procedure TGLCamera.Assign(Source: TPersistent);
5164
var
5165
  cam: TGLCamera;
5166
  dir: TVector;
5167
begin
5168
  if Assigned(Source) then
5169
  begin
5170
    inherited Assign(Source);
5171

5172
    if Source is TGLCamera then
5173
    begin
5174
      cam := TGLCamera(Source);
5175
      SetDepthOfView(cam.DepthOfView);
5176
      SetFocalLength(cam.FocalLength);
5177
      SetCameraStyle(cam.CameraStyle);
5178
      SetSceneScale(cam.SceneScale);
5179
      SetNearPlaneBias(cam.NearPlaneBias);
5180
      SetScene(cam.Scene);
5181
      SetKeepFOVMode(cam.FKeepFOVMode);
5182

5183
      if Parent <> nil then
5184
      begin
5185
        SetTargetObject(cam.TargetObject);
5186
      end
5187
      else // Design camera
5188
      begin
5189
        Position.AsVector := cam.AbsolutePosition;
5190
        if Assigned(cam.TargetObject) then
5191
        begin
5192
          VectorSubtract(cam.TargetObject.AbsolutePosition, AbsolutePosition, dir);
5193
          NormalizeVector(dir);
5194
          Direction.AsVector := dir;
5195
        end;
5196
      end;
5197
    end;
5198
  end;
5199
end;
5200

5201
function TGLCamera.AbsoluteVectorToTarget: TVector;
5202
begin
5203
  if TargetObject <> nil then
5204
  begin
5205
    VectorSubtract(TargetObject.AbsolutePosition, AbsolutePosition, Result);
5206
    NormalizeVector(Result);
5207
  end
5208
  else
5209
    Result := AbsoluteDirection;
5210
end;
5211

5212
function TGLCamera.AbsoluteRightVectorToTarget: TVector;
5213
begin
5214
  if TargetObject <> nil then
5215
  begin
5216
    VectorSubtract(TargetObject.AbsolutePosition, AbsolutePosition, Result);
5217
    Result := VectorCrossProduct(Result, AbsoluteUp);
5218
    NormalizeVector(Result);
5219
  end
5220
  else
5221
    Result := AbsoluteRight;
5222
end;
5223

5224
function TGLCamera.AbsoluteUpVectorToTarget: TVector;
5225
begin
5226
  if TargetObject <> nil then
5227
    Result := VectorCrossProduct(AbsoluteRightVectorToTarget,
5228
      AbsoluteVectorToTarget)
5229
  else
5230
    Result := AbsoluteUp;
5231
end;
5232

5233
procedure TGLCamera.Apply;
5234
var
5235
  v, d, v2: TVector;
5236
  absPos: TVector;
5237
  LM, mat: TMatrix;
5238
begin
5239
  if Assigned(FDeferredApply) then
5240
    FDeferredApply(Self)
5241
  else
5242
  begin
5243
    if Assigned(FTargetObject) then
5244
    begin
5245
      v := TargetObject.AbsolutePosition;
5246
      absPos := AbsolutePosition;
5247
      VectorSubtract(v, absPos, d);
5248
      NormalizeVector(d);
5249
      FLastDirection := d;
5250
      LM := CreateLookAtMatrix(absPos, v, Up.AsVector);
5251
    end
5252
    else
5253
    begin
5254
      if Assigned(Parent) then
5255
        mat := Parent.AbsoluteMatrix
5256
      else
5257
        mat := IdentityHmgMatrix;
5258
      absPos := AbsolutePosition;
5259
      v := VectorTransform(Direction.AsVector, mat);
5260
      FLastDirection := v;
5261
      d := VectorTransform(Up.AsVector, mat);
5262
      v2 := VectorAdd(absPos, v);
5263
      LM := CreateLookAtMatrix(absPos, v2, d);
5264
    end;
5265
    with CurrentGLContext.PipelineTransformation do
5266
      ViewMatrix := MatrixMultiply(LM, ViewMatrix);
5267
    ClearStructureChanged;
5268
  end;
5269
end;
5270

5271
procedure TGLCamera.ApplyPerspective(const AViewport: TRectangle;
5272
  AWidth, AHeight: Integer; ADPI: Integer);
5273
var
5274
  vLeft, vRight, vBottom, vTop, vFar: Single;
5275
  MaxDim, Ratio, f: Double;
5276
  xmax, ymax: Double;
5277
  mat: TMatrix;
5278
const
5279
  cEpsilon: Single = 1e-4;
5280

5281
  function IsPerspective(CamStyle: TGLCameraStyle): Boolean;
5282
  begin
5283
    Result := CamStyle in [csPerspective, csInfinitePerspective, csPerspectiveKeepFOV];
5284
  end;
5285

5286
begin
5287
  if (AWidth <= 0) or (AHeight <= 0) then
5288
    Exit;
5289

5290
  if CameraStyle = csOrtho2D then
5291
  begin
5292
    vLeft := 0;
5293
    vRight := AWidth;
5294
    vBottom := 0;
5295
    vTop := AHeight;
5296
    FNearPlane := -1;
5297
    vFar := 1;
5298
    mat := CreateOrthoMatrix(vLeft, vRight, vBottom, vTop, FNearPlane, vFar);
5299
    with CurrentGLContext.PipelineTransformation do
5300
      ProjectionMatrix := MatrixMultiply(mat, ProjectionMatrix);
5301
    FViewPortRadius := VectorLength(AWidth, AHeight) / 2;
5302
  end
5303
  else if CameraStyle = csCustom then
5304
  begin
5305
    FViewPortRadius := VectorLength(AWidth, AHeight) / 2;
5306
    if Assigned(FOnCustomPerspective) then
5307
      FOnCustomPerspective(AViewport, AWidth, AHeight, ADPI, FViewPortRadius);
5308
  end
5309
  else
5310
  begin
5311
    // determine biggest dimension and resolution (height or width)
5312
    MaxDim := AWidth;
5313
    if AHeight > MaxDim then
5314
      MaxDim := AHeight;
5315

5316
    // calculate near plane distance and extensions;
5317
    // Scene ratio is determined by the window ratio. The viewport is just a
5318
    // specific part of the entire window and has therefore no influence on the
5319
    // scene ratio. What we need to know, though, is the ratio between the window
5320
    // borders (left, top, right and bottom) and the viewport borders.
5321
    // Note: viewport.top is actually bottom, because the window (and viewport) origin
5322
    // in OGL is the lower left corner
5323

5324
    if IsPerspective(CameraStyle) then
5325
      f := FNearPlaneBias / (AWidth * FSceneScale)
5326
    else
5327
      f := 100 * FNearPlaneBias / (focalLength * AWidth * FSceneScale);
5328

5329
    // calculate window/viewport ratio for right extent
5330
    Ratio := (2 * AViewport.Width + 2 * AViewport.Left - AWidth) * f;
5331
    // calculate aspect ratio correct right value of the view frustum and take
5332
    // the window/viewport ratio also into account
5333
    vRight := Ratio * AWidth / (2 * MaxDim);
5334

5335
    // the same goes here for the other three extents
5336
    // left extent:
5337
    Ratio := (AWidth - 2 * AViewport.Left) * f;
5338
    vLeft := -Ratio * AWidth / (2 * MaxDim);
5339

5340
    if IsPerspective(CameraStyle) then
5341
      f := FNearPlaneBias / (AHeight * FSceneScale)
5342
    else
5343
      f := 100 * FNearPlaneBias / (focalLength * AHeight * FSceneScale);
5344

5345
    // top extent (keep in mind the origin is left lower corner):
5346
    Ratio := (2 * AViewport.Height + 2 * AViewport.Top - AHeight) * f;
5347
    vTop := Ratio * AHeight / (2 * MaxDim);
5348

5349
    // bottom extent:
5350
    Ratio := (AHeight - 2 * AViewport.Top) * f;
5351
    vBottom := -Ratio * AHeight / (2 * MaxDim);
5352

5353
    FNearPlane := FFocalLength * 2 * ADPI / (25.4 * MaxDim) * FNearPlaneBias;
5354
    vFar := FNearPlane + FDepthOfView;
5355

5356
    // finally create view frustum (perspective or orthogonal)
5357
    case CameraStyle of
5358
      csPerspective:
5359
        begin
5360
          mat := CreateMatrixFromFrustum(vLeft, vRight, vBottom, vTop, FNearPlane, vFar);
5361
        end;
5362
      csPerspectiveKeepFOV:
5363
        begin
5364
          if FFOVY < 0 then // Need Update FOV
5365
          begin
5366
            FFOVY := ArcTan2(vTop - vBottom, 2 * FNearPlane) * 2;
5367
            FFOVX := ArcTan2(vRight - vLeft, 2 * FNearPlane) * 2;
5368
          end;
5369

5370
          case FKeepFOVMode of
5371
            ckmVerticalFOV:
5372
            begin
5373
              ymax := FNearPlane * tan(FFOVY / 2);
5374
              xmax := ymax * AWidth / AHeight;
5375
            end;
5376
            ckmHorizontalFOV:
5377
            begin
5378
              xmax := FNearPlane * tan(FFOVX / 2);
5379
              ymax := xmax * AHeight / AWidth;
5380
            end;
5381
            else
5382
            begin
5383
              xmax := 0;
5384
              ymax := 0;
5385
              Assert(False, 'Unknown keep camera angle mode');
5386
            end;
5387
          end;
5388
          mat := CreateMatrixFromFrustum(-xmax, xmax, -ymax, ymax, FNearPlane, vFar);
5389
        end;
5390
      csInfinitePerspective:
5391
        begin
5392
          mat := IdentityHmgMatrix;
5393
          mat.V[0].V[0] := 2 * FNearPlane / (vRight - vLeft);
5394
          mat.V[1].V[1] := 2 * FNearPlane / (vTop - vBottom);
5395
          mat.V[2].V[0] := (vRight + vLeft) / (vRight - vLeft);
5396
          mat.V[2].V[1] := (vTop + vBottom) / (vTop - vBottom);
5397
          mat.V[2].V[2] := cEpsilon - 1;
5398
          mat.V[2].V[3] := -1;
5399
          mat.V[3].V[2] := FNearPlane * (cEpsilon - 2);
5400
          mat.V[3].V[3] := 0;
5401
        end;
5402
      csOrthogonal:
5403
        begin
5404
          mat := CreateOrthoMatrix(vLeft, vRight, vBottom, vTop, FNearPlane, vFar);
5405
        end;
5406
    else
5407
      Assert(False);
5408
    end;
5409

5410
    with CurrentGLContext.PipelineTransformation do
5411
      ProjectionMatrix := MatrixMultiply(mat, ProjectionMatrix);
5412

5413
    FViewPortRadius := VectorLength(vRight, vTop) / FNearPlane;
5414
  end;
5415
end;
5416

5417
//------------------------------------------------------------------------------
5418

5419
procedure TGLCamera.AutoLeveling(Factor: Single);
5420
var
5421
  rightVector, rotAxis: TVector;
5422
  angle: Single;
5423
begin
5424
  angle := RadToDeg(arccos(VectorDotProduct(FUp.AsVector, YVector)));
5425
  rotAxis := VectorCrossProduct(YHmgVector, FUp.AsVector);
5426
  if (angle > 1) and (VectorLength(rotAxis) > 0) then
5427
  begin
5428
    rightVector := VectorCrossProduct(FDirection.AsVector, FUp.AsVector);
5429
    FUp.Rotate(AffineVectorMake(rotAxis), Angle / (10 * Factor));
5430
    FUp.Normalize;
5431
    // adjust local coordinates
5432
    FDirection.DirectVector := VectorCrossProduct(FUp.AsVector, rightVector);
5433
    FRotation.Z := -RadToDeg(ArcTan2(RightVector.V[1],
5434
      VectorLength(RightVector.V[0], RightVector.V[2])));
5435
  end;
5436
end;
5437

5438
//------------------------------------------------------------------------------
5439

5440
procedure TGLCamera.Notification(AComponent: TComponent; Operation: TOperation);
5441
begin
5442
  if (Operation = opRemove) and (AComponent = FTargetObject) then
5443
    TargetObject := nil;
5444
  inherited;
5445
end;
5446

5447

5448
procedure TGLCamera.SetTargetObject(const val: TGLBaseSceneObject);
5449
begin
5450
  if (FTargetObject <> val) then
5451
  begin
5452
    if Assigned(FTargetObject) then
5453
      FTargetObject.RemoveFreeNotification(Self);
5454
    FTargetObject := val;
5455
    if Assigned(FTargetObject) then
5456
      FTargetObject.FreeNotification(Self);
5457
    if not (csLoading in ComponentState) then
5458
      TransformationChanged;
5459
  end;
5460
end;
5461

5462
procedure TGLCamera.Reset(aSceneBuffer: TGLSceneBuffer);
5463
var
5464
  Extent: Single;
5465
begin
5466
  FRotation.Z := 0;
5467
  FFocalLength := 50;
5468
  with aSceneBuffer do
5469
  begin
5470
    ApplyPerspective(FViewport, FViewport.Width, FViewport.Height, FRenderDPI);
5471
    FUp.DirectVector := YHmgVector;
5472
    if FViewport.Height < FViewport.Width then
5473
      Extent := FViewport.Height * 0.25
5474
    else
5475
      Extent := FViewport.Width * 0.25;
5476
  end;
5477
  FPosition.SetPoint(0, 0, FNearPlane * Extent);
5478
  FDirection.SetVector(0, 0, -1, 0);
5479
  TransformationChanged;
5480
end;
5481

5482
procedure TGLCamera.ZoomAll(aSceneBuffer: TGLSceneBuffer);
5483
var
5484
  extent: Single;
5485
begin
5486
  with aSceneBuffer do
5487
  begin
5488
    if FViewport.Height < FViewport.Width then
5489
      Extent := FViewport.Height * 0.25
5490
    else
5491
      Extent := FViewport.Width * 0.25;
5492
    FPosition.DirectVector := NullHmgPoint;
5493
    Move(-FNearPlane * Extent);
5494
    // let the camera look at the scene center
5495
    FDirection.SetVector(-FPosition.X, -FPosition.Y, -FPosition.Z, 0);
5496
  end;
5497
end;
5498

5499
procedure TGLCamera.RotateObject(obj: TGLBaseSceneObject; pitchDelta, turnDelta:
5500
  Single;
5501
  rollDelta: Single = 0);
5502
var
5503
  resMat: TMatrix;
5504
  vDir, vUp, vRight: TVector;
5505
  v: TAffineVector;
5506
  position1: TVEctor;
5507
  Scale1: TVector;
5508
begin
5509
  // First we need to compute the actual camera's vectors, which may not be
5510
  // directly available if we're in "targeting" mode
5511
  vUp := AbsoluteUp;
5512
  if TargetObject <> nil then
5513
  begin
5514
    vDir := AbsoluteVectorToTarget;
5515
    vRight := VectorCrossProduct(vDir, vUp);
5516
    vUp := VectorCrossProduct(vRight, vDir);
5517
  end
5518
  else
5519
  begin
5520
    vDir := AbsoluteDirection;
5521
    vRight := VectorCrossProduct(vDir, vUp);
5522
  end;
5523

5524
  //save scale & position info
5525
  Scale1 := obj.Scale.AsVector;
5526
  position1 := obj.Position.asVector;
5527
  resMat := obj.Matrix;
5528
  //get rid of scaling & location info
5529
  NormalizeMatrix(resMat);
5530
  // Now we build rotation matrices and use them to rotate the obj
5531
  if rollDelta <> 0 then
5532
  begin
5533
    SetVector(v, obj.AbsoluteToLocal(vDir));
5534
    resMat := MatrixMultiply(CreateRotationMatrix(v, DegToRad(rollDelta)),
5535
      resMat);
5536
  end;
5537
  if turnDelta <> 0 then
5538
  begin
5539
    SetVector(v, obj.AbsoluteToLocal(vUp));
5540
    resMat := MatrixMultiply(CreateRotationMatrix(v, DegToRad(turnDelta)),
5541
      resMat);
5542
  end;
5543
  if pitchDelta <> 0 then
5544
  begin
5545
    SetVector(v, obj.AbsoluteToLocal(vRight));
5546
    resMat := MatrixMultiply(CreateRotationMatrix(v, DegToRad(pitchDelta)),
5547
      resMat);
5548
  end;
5549
  obj.Matrix := resMat;
5550
  //restore scaling & rotation info
5551
  obj.Scale.AsVector := Scale1;
5552
  obj.Position.AsVector := Position1;
5553
end;
5554

5555
procedure TGLCamera.RotateTarget(pitchDelta, turnDelta: Single; rollDelta: Single
5556
  = 0);
5557
begin
5558
  if Assigned(FTargetObject) then
5559
    RotateObject(FTargetObject, pitchDelta, turnDelta, rollDelta)
5560
end;
5561

5562
procedure TGLCamera.MoveAroundTarget(pitchDelta, turnDelta: Single);
5563
begin
5564
  MoveObjectAround(FTargetObject, pitchDelta, turnDelta);
5565
end;
5566

5567
procedure TGLCamera.MoveAllAroundTarget(pitchDelta, turnDelta :Single);
5568
begin
5569
  MoveObjectAllAround(FTargetObject, pitchDelta, turnDelta);
5570
end;
5571

5572
procedure TGLCamera.MoveInEyeSpace(forwardDistance, rightDistance, upDistance:
5573
  Single);
5574
var
5575
  trVector: TVector;
5576
begin
5577
  trVector := AbsoluteEyeSpaceVector(forwardDistance, rightDistance,
5578
    upDistance);
5579
  if Assigned(Parent) then
5580
    Position.Translate(Parent.AbsoluteToLocal(trVector))
5581
  else
5582
    Position.Translate(trVector);
5583
end;
5584

5585
procedure TGLCamera.MoveTargetInEyeSpace(forwardDistance, rightDistance,
5586
  upDistance: Single);
5587
var
5588
  trVector: TVector;
5589
begin
5590
  if TargetObject <> nil then
5591
  begin
5592
    trVector := AbsoluteEyeSpaceVector(forwardDistance, rightDistance,
5593
      upDistance);
5594
    TargetObject.Position.Translate(TargetObject.Parent.AbsoluteToLocal(trVector));
5595
  end;
5596
end;
5597

5598
function TGLCamera.AbsoluteEyeSpaceVector(forwardDistance, rightDistance,
5599
  upDistance: Single): TVector;
5600
begin
5601
  Result := NullHmgVector;
5602
  if forwardDistance <> 0 then
5603
    CombineVector(Result, AbsoluteVectorToTarget, forwardDistance);
5604
  if rightDistance <> 0 then
5605
    CombineVector(Result, AbsoluteRightVectorToTarget, rightDistance);
5606
  if upDistance <> 0 then
5607
    CombineVector(Result, AbsoluteUpVectorToTarget, upDistance);
5608
end;
5609

5610
procedure TGLCamera.AdjustDistanceToTarget(distanceRatio: Single);
5611
var
5612
  vect: TVector;
5613
begin
5614
  if Assigned(FTargetObject) then
5615
  begin
5616
    // calculate vector from target to camera in absolute coordinates
5617
    vect := VectorSubtract(AbsolutePosition, TargetObject.AbsolutePosition);
5618
    // ratio -> translation vector
5619
    ScaleVector(vect, -(1 - distanceRatio));
5620
    AddVector(vect, AbsolutePosition);
5621
    if Assigned(Parent) then
5622
      vect := Parent.AbsoluteToLocal(vect);
5623
    Position.AsVector := vect;
5624
  end;
5625
end;
5626

5627
function TGLCamera.DistanceToTarget: Single;
5628
var
5629
  vect: TVector;
5630
begin
5631
  if Assigned(FTargetObject) then
5632
  begin
5633
    vect := VectorSubtract(AbsolutePosition, TargetObject.AbsolutePosition);
5634
    Result := VectorLength(vect);
5635
  end
5636
  else
5637
    Result := 1;
5638
end;
5639

5640
function TGLCamera.ScreenDeltaToVector(deltaX, deltaY: Integer; ratio: Single;
5641
  const planeNormal: TVector): TVector;
5642
var
5643
  screenY, screenX: TVector;
5644
  screenYoutOfPlaneComponent: Single;
5645
begin
5646
  // calculate projection of direction vector on the plane
5647
  if Assigned(FTargetObject) then
5648
    screenY := VectorSubtract(TargetObject.AbsolutePosition, AbsolutePosition)
5649
  else
5650
    screenY := Direction.AsVector;
5651
  screenYoutOfPlaneComponent := VectorDotProduct(screenY, planeNormal);
5652
  screenY := VectorCombine(screenY, planeNormal, 1,
5653
    -screenYoutOfPlaneComponent);
5654
  NormalizeVector(screenY);
5655
  // calc the screenX vector
5656
  screenX := VectorCrossProduct(screenY, planeNormal);
5657
  // and here, we're done
5658
  Result := VectorCombine(screenX, screenY, deltaX * ratio, deltaY * ratio);
5659
end;
5660

5661
function TGLCamera.ScreenDeltaToVectorXY(deltaX, deltaY: Integer; ratio:
5662
  Single): TVector;
5663
var
5664
  screenY: TVector;
5665
  dxr, dyr, d: Single;
5666
begin
5667
  // calculate projection of direction vector on the plane
5668
  if Assigned(FTargetObject) then
5669
    screenY := VectorSubtract(TargetObject.AbsolutePosition, AbsolutePosition)
5670
  else
5671
    screenY := Direction.AsVector;
5672
  d := VectorLength(screenY.V[0], screenY.V[1]);
5673
  if d <= 1e-10 then
5674
    d := ratio
5675
  else
5676
    d := ratio / d;
5677
  // and here, we're done
5678
  dxr := deltaX * d;
5679
  dyr := deltaY * d;
5680
  Result.V[0] := screenY.V[1] * dxr + screenY.V[0] * dyr;
5681
  Result.V[1] := screenY.V[1] * dyr - screenY.V[0] * dxr;
5682
  Result.V[2] := 0;
5683
  Result.V[3] := 0;
5684
end;
5685

5686
function TGLCamera.ScreenDeltaToVectorXZ(deltaX, deltaY: Integer; ratio:
5687
  Single): TVector;
5688
var
5689
  screenY: TVector;
5690
  d, dxr, dzr: Single;
5691
begin
5692
  // calculate the projection of direction vector on the plane
5693
  if Assigned(fTargetObject) then
5694
    screenY := VectorSubtract(TargetObject.AbsolutePosition, AbsolutePosition)
5695
  else
5696
    screenY := Direction.AsVector;
5697
  d := VectorLength(screenY.V[0], screenY.V[2]);
5698
  if d <= 1e-10 then
5699
    d := ratio
5700
  else
5701
    d := ratio / d;
5702
  dxr := deltaX * d;
5703
  dzr := deltaY * d;
5704
  Result.V[0] := -screenY.V[2] * dxr + screenY.V[0] * dzr;
5705
  Result.V[1] := 0;
5706
  Result.V[2] := screenY.V[2] * dzr + screenY.V[0] * dxr;
5707
  Result.V[3] := 0;
5708
end;
5709

5710
function TGLCamera.ScreenDeltaToVectorYZ(deltaX, deltaY: Integer; ratio:
5711
  Single): TVector;
5712
var
5713
  screenY: TVector;
5714
  d, dyr, dzr: single;
5715
begin
5716
  // calculate the projection of direction vector on the plane
5717
  if Assigned(fTargetObject) then
5718
    screenY := VectorSubtract(TargetObject.AbsolutePosition, AbsolutePosition)
5719
  else
5720
    screenY := Direction.AsVector;
5721
  d := VectorLength(screenY.V[1], screenY.V[2]);
5722
  if d <= 1e-10 then
5723
    d := ratio
5724
  else
5725
    d := ratio / d;
5726
  dyr := deltaX * d;
5727
  dzr := deltaY * d;
5728
  Result.V[0] := 0;
5729
  Result.V[1] := screenY.V[2] * dyr + screenY.V[1] * dzr;
5730
  Result.V[2] := screenY.V[2] * dzr - screenY.V[1] * dyr;
5731
  Result.V[3] := 0;
5732
end;
5733

5734
// PointInFront
5735
//
5736

5737
function TGLCamera.PointInFront(const point: TVector): boolean;
5738
begin
5739
  result := PointIsInHalfSpace(point, AbsolutePosition, AbsoluteDirection);
5740
end;
5741

5742
// SetDepthOfView
5743
//
5744

5745
procedure TGLCamera.SetDepthOfView(AValue: Single);
5746
begin
5747
  if FDepthOfView <> AValue then
5748
  begin
5749
    FDepthOfView := AValue;
5750
    FFOVY := - 1;
5751
    if not (csLoading in ComponentState) then
5752
      TransformationChanged;
5753
  end;
5754
end;
5755

5756
// SetFocalLength
5757
//
5758

5759
procedure TGLCamera.SetFocalLength(AValue: Single);
5760
begin
5761
  if AValue <= 0 then
5762
    AValue := 1;
5763
  if FFocalLength <> AValue then
5764
  begin
5765
    FFocalLength := AValue;
5766
    FFOVY := - 1;
5767
    if not (csLoading in ComponentState) then
5768
      TransformationChanged;
5769
  end;
5770
end;
5771

5772
// GetFieldOfView
5773
//
5774

5775
function TGLCamera.GetFieldOfView(const AViewportDimension: single): single;
5776
begin
5777
  if FFocalLength = 0 then
5778
    result := 0
5779
  else
5780
    result := RadToDeg(2 * ArcTan2(AViewportDimension * 0.5, FFocalLength));
5781
end;
5782

5783
// SetFieldOfView
5784
//
5785

5786
procedure TGLCamera.SetFieldOfView(const AFieldOfView,
5787
  AViewportDimension: single);
5788
begin
5789
  FocalLength := AViewportDimension / (2 * Tan(DegToRad(AFieldOfView / 2)));
5790
end;
5791

5792
// SetCameraStyle
5793
//
5794

5795
procedure TGLCamera.SetCameraStyle(const val: TGLCameraStyle);
5796
begin
5797
  if FCameraStyle <> val then
5798
  begin
5799
    FCameraStyle := val;
5800
    FFOVY := -1;
5801
    NotifyChange(Self);
5802
  end;
5803
end;
5804

5805
// SetKeepCamAngleMode
5806
//
5807

5808
procedure TGLCamera.SetKeepFOVMode(const val: TGLCameraKeepFOVMode);
5809
begin
5810
  if FKeepFOVMode <> val then
5811
  begin
5812
    FKeepFOVMode := val;
5813
    FFOVY := -1;
5814
    if FCameraStyle = csPerspectiveKeepFOV then
5815
      NotifyChange(Self);
5816
  end;
5817
end;
5818

5819
// SetSceneScale
5820
//
5821

5822
procedure TGLCamera.SetSceneScale(value: Single);
5823
begin
5824
  if value = 0 then
5825
    value := 1;
5826
  if FSceneScale <> value then
5827
  begin
5828
    FSceneScale := value;
5829
    FFOVY := -1;
5830
    NotifyChange(Self);
5831
  end;
5832
end;
5833

5834
// StoreSceneScale
5835
//
5836

5837
function TGLCamera.StoreSceneScale: Boolean;
5838
begin
5839
  Result := (FSceneScale <> 1);
5840
end;
5841

5842
// SetNearPlaneBias
5843
//
5844

5845
procedure TGLCamera.SetNearPlaneBias(value: Single);
5846
begin
5847
  if value <= 0 then
5848
    value := 1;
5849
  if FNearPlaneBias <> value then
5850
  begin
5851
    FNearPlaneBias := value;
5852
    FFOVY := -1;
5853
    NotifyChange(Self);
5854
  end;
5855
end;
5856

5857
// StoreNearPlaneBias
5858
//
5859

5860
function TGLCamera.StoreNearPlaneBias: Boolean;
5861
begin
5862
  Result := (FNearPlaneBias <> 1);
5863
end;
5864

5865
// DoRender
5866
//
5867

5868
procedure TGLCamera.DoRender(var ARci: TGLRenderContextInfo;
5869
  ARenderSelf, ARenderChildren: Boolean);
5870
begin
5871
  if ARenderChildren and (Count > 0) then
5872
    Self.RenderChildren(0, Count - 1, ARci);
5873
end;
5874

5875
// RayCastIntersect
5876
//
5877

5878
function TGLCamera.RayCastIntersect(const rayStart, rayVector: TVector;
5879
  intersectPoint: PVector = nil;
5880
  intersectNormal: PVector = nil): Boolean;
5881
begin
5882
  Result := False;
5883
end;
5884

5885
// ------------------
5886
// ------------------ TGLImmaterialSceneObject ------------------
5887
// ------------------
5888

5889
// DoRender
5890
//
5891

5892
procedure TGLImmaterialSceneObject.DoRender(var ARci: TGLRenderContextInfo;
5893
  ARenderSelf, ARenderChildren: Boolean);
5894
begin
5895
  // start rendering self
5896
  if ARenderSelf then
5897
  begin
5898
    if (osDirectDraw in ObjectStyle) or ARci.amalgamating then
5899
      BuildList(ARci)
5900
    else
5901
      ARci.GLStates.CallList(GetHandle(ARci));
5902
  end;
5903
  // start rendering children (if any)
5904
  if ARenderChildren then
5905
    Self.RenderChildren(0, Count - 1, ARci);
5906
end;
5907

5908
// ------------------
5909
// ------------------ TGLCameraInvariantObject ------------------
5910
// ------------------
5911

5912
// Create
5913
//
5914

5915
constructor TGLCameraInvariantObject.Create(AOwner: TComponent);
5916
begin
5917
  inherited;
5918
  FCamInvarianceMode := cimNone;
5919
end;
5920

5921
 
5922
//
5923

5924
procedure TGLCameraInvariantObject.Assign(Source: TPersistent);
5925
begin
5926
  if Source is TGLCameraInvariantObject then
5927
  begin
5928
    FCamInvarianceMode := TGLCameraInvariantObject(Source).FCamInvarianceMode;
5929
  end;
5930
  inherited Assign(Source);
5931
end;
5932

5933
// DoRender
5934
//
5935

5936
procedure TGLCameraInvariantObject.DoRender(var ARci: TGLRenderContextInfo;
5937
  ARenderSelf, ARenderChildren: Boolean);
5938
begin
5939
  if CamInvarianceMode <> cimNone then
5940
    with ARci.PipelineTransformation do
5941
    begin
5942
      Push;
5943
      try
5944
        // prepare
5945
        case CamInvarianceMode of
5946
          cimPosition:
5947
            begin
5948
              ViewMatrix := MatrixMultiply(
5949
                CreateTranslationMatrix(ARci.cameraPosition),
5950
                ARci.PipelineTransformation.ViewMatrix);
5951
            end;
5952
          cimOrientation:
5953
            begin
5954
              // makes the coordinates system more 'intuitive' (Z+ forward)
5955
              ViewMatrix := CreateScaleMatrix(Vector3fMake(1, -1, -1))
5956
            end;
5957
        else
5958
          Assert(False);
5959
        end;
5960
        // Apply local transform
5961
        ModelMatrix := LocalMatrix^;
5962

5963
        if ARenderSelf then
5964
        begin
5965
          if (osDirectDraw in ObjectStyle) or ARci.amalgamating then
5966
            BuildList(ARci)
5967
          else
5968
            ARci.GLStates.CallList(GetHandle(ARci));
5969
        end;
5970
        if ARenderChildren then
5971
          Self.RenderChildren(0, Count - 1, ARci);
5972
      finally
5973
        Pop;
5974
      end;
5975
    end
5976
  else
5977
    inherited;
5978
end;
5979

5980
// SetCamInvarianceMode
5981
//
5982

5983
procedure TGLCameraInvariantObject.SetCamInvarianceMode(const val:
5984
  TGLCameraInvarianceMode);
5985
begin
5986
  if FCamInvarianceMode <> val then
5987
  begin
5988
    FCamInvarianceMode := val;
5989
    NotifyChange(Self);
5990
  end;
5991
end;
5992

5993
// ------------------
5994
// ------------------ TGLDirectOpenGL ------------------
5995
// ------------------
5996

5997
// Create
5998
//
5999

6000
constructor TGLDirectOpenGL.Create(AOwner: TComponent);
6001
begin
6002
  inherited;
6003
  ObjectStyle := ObjectStyle + [osDirectDraw];
6004
  FBlend := False;
6005
end;
6006

6007
 
6008
//
6009

6010
procedure TGLDirectOpenGL.Assign(Source: TPersistent);
6011
begin
6012
  if Source is TGLDirectOpenGL then
6013
  begin
6014
    UseBuildList := TGLDirectOpenGL(Source).UseBuildList;
6015
    FOnRender := TGLDirectOpenGL(Source).FOnRender;
6016
    FBlend := TGLDirectOpenGL(Source).Blend;
6017
  end;
6018
  inherited Assign(Source);
6019
end;
6020

6021
// BuildList
6022
//
6023

6024
procedure TGLDirectOpenGL.BuildList(var rci: TGLRenderContextInfo);
6025
begin
6026
  if Assigned(FOnRender) then
6027
  begin
6028
    xgl.MapTexCoordToMain; // single texturing by default
6029
    OnRender(Self, rci);
6030
  end;
6031
end;
6032

6033
// AxisAlignedDimensionsUnscaled
6034
//
6035

6036
function TGLDirectOpenGL.AxisAlignedDimensionsUnscaled: TVector;
6037
begin
6038
  Result := NullHmgPoint;
6039
end;
6040

6041
// SetUseBuildList
6042
//
6043

6044
procedure TGLDirectOpenGL.SetUseBuildList(const val: Boolean);
6045
begin
6046
  if val <> FUseBuildList then
6047
  begin
6048
    FUseBuildList := val;
6049
    if val then
6050
      ObjectStyle := ObjectStyle - [osDirectDraw]
6051
    else
6052
      ObjectStyle := ObjectStyle + [osDirectDraw];
6053
  end;
6054
end;
6055

6056
// Blended
6057
//
6058

6059
function TGLDirectOpenGL.Blended: Boolean;
6060
begin
6061
  Result := FBlend;
6062
end;
6063

6064
// SetBlend
6065
//
6066

6067
procedure TGLDirectOpenGL.SetBlend(const val: Boolean);
6068
begin
6069
  if val <> FBlend then
6070
  begin
6071
    FBlend := val;
6072
    StructureChanged;
6073
  end;
6074
end;
6075

6076
// ------------------
6077
// ------------------ TGLRenderPoint ------------------
6078
// ------------------
6079

6080
// Create
6081
//
6082

6083
constructor TGLRenderPoint.Create(AOwner: TComponent);
6084
begin
6085
  inherited;
6086
  ObjectStyle := ObjectStyle + [osDirectDraw];
6087
end;
6088

6089
// Destroy
6090
//
6091

6092
destructor TGLRenderPoint.Destroy;
6093
begin
6094
  Clear;
6095
  inherited;
6096
end;
6097

6098
// BuildList
6099
//
6100

6101
procedure TGLRenderPoint.BuildList(var rci: TGLRenderContextInfo);
6102
var
6103
  i: Integer;
6104
begin
6105
  for i := 0 to High(FCallBacks) do
6106
    FCallBacks[i](Self, rci);
6107
end;
6108

6109
// RegisterCallBack
6110
//
6111

6112
procedure TGLRenderPoint.RegisterCallBack(renderEvent: TDirectRenderEvent;
6113
  renderPointFreed: TNotifyEvent);
6114
var
6115
  n: Integer;
6116
begin
6117
  n := Length(FCallBacks);
6118
  SetLength(FCallBacks, n + 1);
6119
  SetLength(FFreeCallBacks, n + 1);
6120
  FCallBacks[n] := renderEvent;
6121
  FFreeCallBacks[n] := renderPointFreed;
6122
end;
6123

6124
// UnRegisterCallBack
6125
//
6126

6127
procedure TGLRenderPoint.UnRegisterCallBack(renderEvent: TDirectRenderEvent);
6128
type
6129
  TEventContainer = record
6130
    event: TDirectRenderEvent;
6131
  end;
6132
var
6133
  i, j, n: Integer;
6134
  refContainer, listContainer: TEventContainer;
6135
begin
6136
  refContainer.event := renderEvent;
6137
  n := Length(FCallBacks);
6138
  for i := 0 to n - 1 do
6139
  begin
6140
    listContainer.event := FCallBacks[i];
6141
    if CompareMem(@listContainer, @refContainer, SizeOf(TEventContainer)) then
6142
    begin
6143
      for j := i + 1 to n - 1 do
6144
      begin
6145
        FCallBacks[j - 1] := FCallBacks[j];
6146
        FFreeCallBacks[j - 1] := FFreeCallBacks[j];
6147
      end;
6148
      SetLength(FCallBacks, n - 1);
6149
      SetLength(FFreeCallBacks, n - 1);
6150
      Break;
6151
    end;
6152
  end;
6153
end;
6154

6155
// BuildList
6156
//
6157

6158
procedure TGLRenderPoint.Clear;
6159
begin
6160
  while Length(FCallBacks) > 0 do
6161
  begin
6162
    FFreeCallBacks[High(FCallBacks)](Self);
6163
    SetLength(FCallBacks, Length(FCallBacks) - 1);
6164
  end;
6165
end;
6166

6167
// ------------------
6168
// ------------------ TGLProxyObject ------------------
6169
// ------------------
6170

6171
// Create
6172
//
6173

6174
constructor TGLProxyObject.Create(AOwner: TComponent);
6175
begin
6176
  inherited;
6177
  FProxyOptions := cDefaultProxyOptions;
6178
end;
6179

6180
// Destroy
6181
//
6182

6183
destructor TGLProxyObject.Destroy;
6184
begin
6185
  SetMasterObject(nil);
6186
  inherited;
6187
end;
6188

6189
 
6190
//
6191

6192
procedure TGLProxyObject.Assign(Source: TPersistent);
6193
begin
6194
  if Source is TGLProxyObject then
6195
  begin
6196
    SetMasterObject(TGLProxyObject(Source).MasterObject);
6197
  end;
6198
  inherited Assign(Source);
6199
end;
6200

6201
// Render
6202
//
6203

6204
procedure TGLProxyObject.DoRender(var ARci: TGLRenderContextInfo;
6205
  ARenderSelf, ARenderChildren: Boolean);
6206
var
6207
  gotMaster, masterGotEffects, oldProxySubObject: Boolean;
6208
begin
6209
  if FRendering then
6210
    Exit;
6211
  FRendering := True;
6212
  try
6213
    gotMaster := Assigned(FMasterObject);
6214
    masterGotEffects := gotMaster and (pooEffects in FProxyOptions)
6215
      and (FMasterObject.Effects.Count > 0);
6216
    if gotMaster then
6217
    begin
6218
      if pooObjects in FProxyOptions then
6219
      begin
6220
        oldProxySubObject := ARci.proxySubObject;
6221
        ARci.proxySubObject := True;
6222
        if pooTransformation in FProxyOptions then
6223
          with ARci.PipelineTransformation do
6224
            ModelMatrix := MatrixMultiply(FMasterObject.Matrix, ModelMatrix);
6225
        FMasterObject.DoRender(ARci, ARenderSelf, (FMasterObject.Count > 0));
6226
        ARci.proxySubObject := oldProxySubObject;
6227
      end;
6228
    end;
6229
    // now render self stuff (our children, our effects, etc.)
6230
    if ARenderChildren and (Count > 0) then
6231
      Self.RenderChildren(0, Count - 1, ARci);
6232
    if masterGotEffects then
6233
      FMasterObject.Effects.RenderPostEffects(ARci);
6234
  finally
6235
    FRendering := False;
6236
  end;
6237
  ClearStructureChanged;
6238
end;
6239

6240
// AxisAlignedDimensions
6241
//
6242

6243
function TGLProxyObject.AxisAlignedDimensions: TVector;
6244
begin
6245
  If Assigned(FMasterObject) then
6246
  begin
6247
    Result := FMasterObject.AxisAlignedDimensionsUnscaled;
6248
    If (pooTransformation in ProxyOptions) then
6249
      ScaleVector(Result,FMasterObject.Scale.AsVector)
6250
    else
6251
      ScaleVector(Result, Scale.AsVector);
6252
  end
6253
  else
6254
    Result := inherited AxisAlignedDimensions;
6255
end;
6256

6257
function TGLProxyObject.AxisAlignedDimensionsUnscaled: TVector;
6258
begin
6259
  if Assigned(FMasterObject) then
6260
  begin
6261
    Result := FMasterObject.AxisAlignedDimensionsUnscaled;
6262
  end
6263
  else
6264
    Result := inherited AxisAlignedDimensionsUnscaled;
6265
end;
6266

6267
// BarycenterAbsolutePosition
6268
//
6269

6270
function TGLProxyObject.BarycenterAbsolutePosition: TVector;
6271
var
6272
  lAdjustVector: TVector;
6273
begin
6274
  if Assigned(FMasterObject) then
6275
  begin
6276
    // Not entirely correct, but better than nothing...
6277
    lAdjustVector := VectorSubtract(FMasterObject.BarycenterAbsolutePosition,
6278
      FMasterObject.AbsolutePosition);
6279
    Position.AsVector := VectorAdd(Position.AsVector, lAdjustVector);
6280
    Result := AbsolutePosition;
6281
    Position.AsVector := VectorSubtract(Position.AsVector, lAdjustVector);
6282
  end
6283
  else
6284
    Result := inherited BarycenterAbsolutePosition;
6285
end;
6286

6287
// Notification
6288
//
6289

6290
procedure TGLProxyObject.Notification(AComponent: TComponent; Operation:
6291
  TOperation);
6292
begin
6293
  if (Operation = opRemove) and (AComponent = FMasterObject) then
6294
    MasterObject := nil;
6295
  inherited;
6296
end;
6297

6298
// SetMasterObject
6299
//
6300

6301
procedure TGLProxyObject.SetMasterObject(const val: TGLBaseSceneObject);
6302
begin
6303
  if FMasterObject <> val then
6304
  begin
6305
    if Assigned(FMasterObject) then
6306
      FMasterObject.RemoveFreeNotification(Self);
6307
    FMasterObject := val;
6308
    if Assigned(FMasterObject) then
6309
      FMasterObject.FreeNotification(Self);
6310
    StructureChanged;
6311
  end;
6312
end;
6313

6314
// SetProxyOptions
6315
//
6316

6317
procedure TGLProxyObject.SetProxyOptions(const val: TGLProxyObjectOptions);
6318
begin
6319
  if FProxyOptions <> val then
6320
  begin
6321
    FProxyOptions := val;
6322
    StructureChanged;
6323
  end;
6324
end;
6325

6326
// RayCastIntersect
6327
//
6328

6329
function TGLProxyObject.RayCastIntersect(const rayStart, rayVector: TVector;
6330
  intersectPoint: PVector = nil;
6331
  intersectNormal: PVector = nil): Boolean;
6332
var
6333
  localRayStart, localRayVector: TVector;
6334
begin
6335
  if Assigned(MasterObject) then
6336
  begin
6337
    SetVector(localRayStart, AbsoluteToLocal(rayStart));
6338
    SetVector(localRayStart, MasterObject.LocalToAbsolute(localRayStart));
6339
    SetVector(localRayVector, AbsoluteToLocal(rayVector));
6340
    SetVector(localRayVector, MasterObject.LocalToAbsolute(localRayVector));
6341
    NormalizeVector(localRayVector);
6342

6343
    Result := MasterObject.RayCastIntersect(localRayStart, localRayVector,
6344
      intersectPoint, intersectNormal);
6345
    if Result then
6346
    begin
6347
      if Assigned(intersectPoint) then
6348
      begin
6349
        SetVector(intersectPoint^,
6350
          MasterObject.AbsoluteToLocal(intersectPoint^));
6351
        SetVector(intersectPoint^, LocalToAbsolute(intersectPoint^));
6352
      end;
6353
      if Assigned(intersectNormal) then
6354
      begin
6355
        SetVector(intersectNormal^,
6356
          MasterObject.AbsoluteToLocal(intersectNormal^));
6357
        SetVector(intersectNormal^, LocalToAbsolute(intersectNormal^));
6358
      end;
6359
    end;
6360
  end
6361
  else
6362
    Result := False;
6363
end;
6364

6365
// GenerateSilhouette
6366
//
6367

6368
function TGLProxyObject.GenerateSilhouette(const silhouetteParameters:
6369
  TGLSilhouetteParameters): TGLSilhouette;
6370
begin
6371
  if Assigned(MasterObject) then
6372
    Result := MasterObject.GenerateSilhouette(silhouetteParameters)
6373
  else
6374
    Result := nil;
6375
end;
6376

6377
// ------------------
6378
// ------------------ TGLLightSource ------------------
6379
// ------------------
6380

6381
// Create
6382
//
6383

6384
constructor TGLLightSource.Create(AOwner: TComponent);
6385
begin
6386
  inherited Create(AOwner);
6387
  FShining := True;
6388
  FSpotDirection := TGLCoordinates.CreateInitialized(Self, VectorMake(0, 0, -1,0),csVector);
6389
  FConstAttenuation := 1;
6390
  FLinearAttenuation := 0;
6391
  FQuadraticAttenuation := 0;
6392
  FSpotCutOff := 180;
6393
  FSpotExponent := 0;
6394
  FLightStyle := lsSpot;
6395
  FAmbient := TGLColor.Create(Self);
6396
  FDiffuse := TGLColor.Create(Self);
6397
  FDiffuse.Initialize(clrWhite);
6398
  FSpecular := TGLColor.Create(Self);
6399
end;
6400

6401
// Destroy
6402
//
6403

6404
destructor TGLLightSource.Destroy;
6405
begin
6406
  FSpotDirection.Free;
6407
  FAmbient.Free;
6408
  FDiffuse.Free;
6409
  FSpecular.Free;
6410
  inherited Destroy;
6411
end;
6412

6413
// DoRender
6414
//
6415

6416
procedure TGLLightSource.DoRender(var ARci: TGLRenderContextInfo;
6417
  ARenderSelf, ARenderChildren: Boolean);
6418
begin
6419
  if ARenderChildren and Assigned(FChildren) then
6420
    Self.RenderChildren(0, Count - 1, ARci);
6421
end;
6422

6423
// RayCastIntersect
6424
//
6425

6426
function TGLLightSource.RayCastIntersect(const rayStart, rayVector: TVector;
6427
  intersectPoint: PVector = nil;
6428
  intersectNormal: PVector = nil): Boolean;
6429
begin
6430
  Result := False;
6431
end;
6432

6433
// CoordinateChanged
6434
//
6435

6436
procedure TGLLightSource.CoordinateChanged(Sender: TGLCustomCoordinates);
6437
begin
6438
  inherited;
6439
  if Sender = FSpotDirection then
6440
    TransformationChanged;
6441
end;
6442

6443
// GenerateSilhouette
6444
//
6445

6446
function TGLLightSource.GenerateSilhouette(const silhouetteParameters:
6447
  TGLSilhouetteParameters): TGLSilhouette;
6448
begin
6449
  Result := nil;
6450
end;
6451

6452
// GetHandle
6453
//
6454

6455
function TGLLightSource.GetHandle(var rci: TGLRenderContextInfo): Cardinal;
6456
begin
6457
  Result := 0;
6458
end;
6459

6460
// SetShining
6461
//
6462

6463
procedure TGLLightSource.SetShining(AValue: Boolean);
6464
begin
6465
  if AValue <> FShining then
6466
  begin
6467
    FShining := AValue;
6468
    NotifyChange(Self);
6469
  end;
6470
end;
6471

6472
// SetSpotDirection
6473
//
6474

6475
procedure TGLLightSource.SetSpotDirection(AVector: TGLCoordinates);
6476
begin
6477
  FSpotDirection.DirectVector := AVector.AsVector;
6478
  FSpotDirection.W := 0;
6479
  NotifyChange(Self);
6480
end;
6481

6482
// SetSpotExponent
6483
//
6484

6485
procedure TGLLightSource.SetSpotExponent(AValue: Single);
6486
begin
6487
  if FSpotExponent <> AValue then
6488
  begin
6489
    FSpotExponent := AValue;
6490
    NotifyChange(Self);
6491
  end;
6492
end;
6493

6494
// SetSpotCutOff
6495
//
6496

6497
procedure TGLLightSource.SetSpotCutOff(const val: Single);
6498
begin
6499
  if FSpotCutOff <> val then
6500
  begin
6501
    if ((val >= 0) and (val <= 90)) or (val = 180) then
6502
    begin
6503
      FSpotCutOff := val;
6504
      NotifyChange(Self);
6505
    end;
6506
  end;
6507
end;
6508

6509
// SetLightStyle
6510
//
6511

6512
procedure TGLLightSource.SetLightStyle(const val: TLightStyle);
6513
begin
6514
  if FLightStyle <> val then
6515
  begin
6516
    FLightStyle := val;
6517
    NotifyChange(Self);
6518
  end;
6519
end;
6520

6521
// SetAmbient
6522
//
6523

6524
procedure TGLLightSource.SetAmbient(AValue: TGLColor);
6525
begin
6526
  FAmbient.Color := AValue.Color;
6527
  NotifyChange(Self);
6528
end;
6529

6530
// SetDiffuse
6531
//
6532

6533
procedure TGLLightSource.SetDiffuse(AValue: TGLColor);
6534
begin
6535
  FDiffuse.Color := AValue.Color;
6536
  NotifyChange(Self);
6537
end;
6538

6539
// SetSpecular
6540
//
6541

6542
procedure TGLLightSource.SetSpecular(AValue: TGLColor);
6543
begin
6544
  FSpecular.Color := AValue.Color;
6545
  NotifyChange(Self);
6546
end;
6547

6548
// SetConstAttenuation
6549
//
6550

6551
procedure TGLLightSource.SetConstAttenuation(AValue: Single);
6552
begin
6553
  if FConstAttenuation <> AValue then
6554
  begin
6555
    FConstAttenuation := AValue;
6556
    NotifyChange(Self);
6557
  end;
6558
end;
6559

6560
// SetLinearAttenuation
6561
//
6562

6563
procedure TGLLightSource.SetLinearAttenuation(AValue: Single);
6564
begin
6565
  if FLinearAttenuation <> AValue then
6566
  begin
6567
    FLinearAttenuation := AValue;
6568
    NotifyChange(Self);
6569
  end;
6570
end;
6571

6572
// SetQuadraticAttenuation
6573
//
6574

6575
procedure TGLLightSource.SetQuadraticAttenuation(AValue: Single);
6576
begin
6577
  if FQuadraticAttenuation <> AValue then
6578
  begin
6579
    FQuadraticAttenuation := AValue;
6580
    NotifyChange(Self);
6581
  end;
6582
end;
6583

6584
// Attenuated
6585
//
6586

6587
function TGLLightSource.Attenuated: Boolean;
6588
begin
6589
  Result := (LightStyle <> lsParallel)
6590
    and ((ConstAttenuation <> 1) or (LinearAttenuation <> 0) or
6591
    (QuadraticAttenuation <> 0));
6592
end;
6593

6594
// ------------------
6595
// ------------------ TGLScene ------------------
6596
// ------------------
6597

6598
// Create
6599
//
6600

6601
constructor TGLScene.Create(AOwner: TComponent);
6602
begin
6603
  inherited;
6604
  // root creation
6605
  FCurrentBuffer := nil;
6606
  FObjects := TGLSceneRootObject.Create(Self);
6607
  FObjects.Name := 'ObjectRoot';
6608
  FLights := TPersistentObjectList.Create;
6609
  FObjectsSorting := osRenderBlendedLast;
6610
  FVisibilityCulling := vcNone;
6611
  // actual maximum number of lights is stored in TGLSceneViewer
6612
  FLights.Count := 8;
6613
  FInitializableObjects := TGLInitializableObjectList.Create;
6614
end;
6615

6616
// Destroy
6617
//
6618

6619
destructor TGLScene.Destroy;
6620
begin
6621
  InitializableObjects.Free;
6622
  FObjects.DestroyHandles;
6623
  FLights.Free;
6624
  FObjects.Free;
6625
  if Assigned(FBuffers) then FreeAndNil(FBuffers);
6626
  inherited Destroy;
6627
end;
6628

6629
// AddLight
6630
//
6631

6632
procedure TGLScene.AddLight(ALight: TGLLightSource);
6633
var
6634
  i: Integer;
6635
begin
6636
  for i := 0 to FLights.Count - 1 do
6637
    if FLights.List^[i] = nil then
6638
    begin
6639
      FLights.List^[i] := ALight;
6640
      ALight.FLightID := i;
6641
      Break;
6642
    end;
6643
end;
6644

6645
// RemoveLight
6646
//
6647

6648
procedure TGLScene.RemoveLight(ALight: TGLLightSource);
6649
var
6650
  idx: Integer;
6651
begin
6652
  idx := FLights.IndexOf(ALight);
6653
  if idx >= 0 then
6654
    FLights[idx] := nil;
6655
end;
6656

6657
// AddLights
6658
//
6659

6660
procedure TGLScene.AddLights(anObj: TGLBaseSceneObject);
6661
var
6662
  i: Integer;
6663
begin
6664
  if anObj is TGLLightSource then
6665
    AddLight(TGLLightSource(anObj));
6666
  for i := 0 to anObj.Count - 1 do
6667
    AddLights(anObj.Children[i]);
6668
end;
6669

6670
// RemoveLights
6671
//
6672

6673
procedure TGLScene.RemoveLights(anObj: TGLBaseSceneObject);
6674
var
6675
  i: Integer;
6676
begin
6677
  if anObj is TGLLightSource then
6678
    RemoveLight(TGLLightSource(anObj));
6679
  for i := 0 to anObj.Count - 1 do
6680
    RemoveLights(anObj.Children[i]);
6681
end;
6682

6683
// ShutdownAllLights
6684
//
6685

6686
procedure TGLScene.ShutdownAllLights;
6687

6688
  procedure DoShutdownLight(Obj: TGLBaseSceneObject);
6689
  var
6690
    i: integer;
6691
  begin
6692
    if Obj is TGLLightSource then
6693
      TGLLightSource(Obj).Shining := False;
6694
    for i := 0 to Obj.Count - 1 do
6695
      DoShutDownLight(Obj[i]);
6696
  end;
6697

6698
begin
6699
  DoShutdownLight(FObjects);
6700
end;
6701

6702
// AddBuffer
6703
//
6704

6705
procedure TGLScene.AddBuffer(aBuffer: TGLSceneBuffer);
6706
begin
6707
  if not Assigned(FBuffers) then
6708
    FBuffers := TPersistentObjectList.Create;
6709
  if FBuffers.IndexOf(aBuffer) < 0 then
6710
  begin
6711
    FBuffers.Add(aBuffer);
6712
    if FBaseContext = nil then
6713
      FBaseContext := TGLSceneBuffer(FBuffers[0]).RenderingContext;
6714
    if (FBuffers.Count > 1) and Assigned(FBaseContext) then
6715
      aBuffer.RenderingContext.ShareLists(FBaseContext);
6716
  end;
6717
end;
6718

6719
// RemoveBuffer
6720
//
6721

6722
procedure TGLScene.RemoveBuffer(aBuffer: TGLSceneBuffer);
6723
var
6724
  i: Integer;
6725
begin
6726
  if Assigned(FBuffers) then
6727
  begin
6728
    i := FBuffers.IndexOf(aBuffer);
6729
    if i >= 0 then
6730
    begin
6731
      if FBuffers.Count = 1 then
6732
      begin
6733
        FreeAndNil(FBuffers);
6734
        FBaseContext := nil;
6735
      end
6736
      else
6737
      begin
6738
        FBuffers.Delete(i);
6739
        FBaseContext := TGLSceneBuffer(FBuffers[0]).RenderingContext;
6740
      end;
6741
    end;
6742
  end;
6743
end;
6744

6745
// GetChildren
6746
//
6747

6748
procedure TGLScene.GetChildren(AProc: TGetChildProc; Root: TComponent);
6749
begin
6750
  FObjects.GetChildren(AProc, Root);
6751
end;
6752

6753
// SetChildOrder
6754
//
6755

6756
procedure TGLScene.SetChildOrder(AChild: TComponent; Order: Integer);
6757
begin
6758
  (AChild as TGLBaseSceneObject).Index := Order;
6759
end;
6760

6761
// IsUpdating
6762
//
6763

6764
function TGLScene.IsUpdating: Boolean;
6765
begin
6766
  Result := (FUpdateCount <> 0) or (csLoading in ComponentState) or (csDestroying
6767
    in ComponentState);
6768
end;
6769

6770
// BeginUpdate
6771
//
6772

6773
procedure TGLScene.BeginUpdate;
6774
begin
6775
  Inc(FUpdateCount);
6776
end;
6777

6778
// EndUpdate
6779
//
6780

6781
procedure TGLScene.EndUpdate;
6782
begin
6783
  Assert(FUpdateCount > 0);
6784
  Dec(FUpdateCount);
6785
  if FUpdateCount = 0 then
6786
    NotifyChange(Self);
6787
end;
6788

6789
// SetObjectsSorting
6790
//
6791

6792
procedure TGLScene.SetObjectsSorting(const val: TGLObjectsSorting);
6793
begin
6794
  if FObjectsSorting <> val then
6795
  begin
6796
    if val = osInherited then
6797
      FObjectsSorting := osRenderBlendedLast
6798
    else
6799
      FObjectsSorting := val;
6800
    NotifyChange(Self);
6801
  end;
6802
end;
6803

6804
// SetVisibilityCulling
6805
//
6806

6807
procedure TGLScene.SetVisibilityCulling(const val: TGLVisibilityCulling);
6808
begin
6809
  if FVisibilityCulling <> val then
6810
  begin
6811
    if val = vcInherited then
6812
      FVisibilityCulling := vcNone
6813
    else
6814
      FVisibilityCulling := val;
6815
    NotifyChange(Self);
6816
  end;
6817
end;
6818

6819
// ReadState
6820
//
6821

6822
procedure TGLScene.ReadState(Reader: TReader);
6823
var
6824
  SaveRoot: TComponent;
6825
begin
6826
  SaveRoot := Reader.Root;
6827
  try
6828
    if Owner <> nil then
6829
      Reader.Root := Owner;
6830
    inherited;
6831
  finally
6832
    Reader.Root := SaveRoot;
6833
  end;
6834
end;
6835

6836
// Progress
6837
//
6838

6839
procedure TGLScene.Progress(const deltaTime, newTime: Double);
6840
var
6841
  pt: TProgressTimes;
6842
begin
6843
  pt.deltaTime := deltaTime;
6844
  pt.newTime := newTime;
6845
  FCurrentDeltaTime := deltaTime;
6846
  if Assigned(FOnBeforeProgress) then
6847
   FOnBeforeProgress(Self, deltaTime, newTime);
6848
  FObjects.DoProgress(pt);
6849
  if Assigned(FOnProgress) then
6850
   FOnProgress(Self, deltaTime, newTime);
6851
end;
6852

6853
// SaveToFile
6854
//
6855

6856
procedure TGLScene.SaveToFile(const fileName: string);
6857
var
6858
  stream: TStream;
6859
begin
6860
  stream := CreateFileStream(fileName, fmCreate);
6861
  try
6862
    SaveToStream(stream);
6863
  finally
6864
    stream.Free;
6865
  end;
6866
end;
6867

6868
 
6869
//
6870

6871
procedure TGLScene.LoadFromFile(const fileName: string);
6872

6873
  procedure CheckResFileStream(Stream: TStream);
6874
  var
6875
    N: Integer;
6876
    B: Byte;
6877
  begin
6878
    N := Stream.Position;
6879
    Stream.Read(B, Sizeof(B));
6880
    Stream.Position := N;
6881
    if B = $FF then
6882
      Stream.ReadResHeader;
6883
  end;
6884

6885
var
6886
  stream: TStream;
6887
begin
6888
  stream := CreateFileStream(fileName, fmOpenRead);
6889
  try
6890
    CheckResFileStream(stream);
6891
    LoadFromStream(stream);
6892
  finally
6893
    stream.Free;
6894
  end;
6895
end;
6896

6897
// SaveToTextFile
6898
//
6899

6900
procedure TGLScene.SaveToTextFile(const fileName: string);
6901
var
6902
  mem: TMemoryStream;
6903
  fil: TStream;
6904
begin
6905
  mem := TMemoryStream.Create;
6906
  fil := CreateFileStream(fileName, fmCreate);
6907
  try
6908
    SaveToStream(mem);
6909
    mem.Position := 0;
6910
    ObjectBinaryToText(mem, fil);
6911
  finally
6912
    fil.Free;
6913
    mem.Free;
6914
  end;
6915
end;
6916

6917
// LoadFromTextFile
6918
//
6919

6920
procedure TGLScene.LoadFromTextFile(const fileName: string);
6921
var
6922
  Mem: TMemoryStream;
6923
  Fil: TStream;
6924
begin
6925
  Mem := TMemoryStream.Create;
6926
  Fil := CreateFileStream(fileName, fmOpenRead);
6927
  try
6928
    ObjectTextToBinary(Fil, Mem);
6929
    Mem.Position := 0;
6930
    LoadFromStream(Mem);
6931
  finally
6932
    Fil.Free;
6933
    Mem.Free;
6934
  end;
6935
end;
6936

6937
// LoadFromStream
6938
//
6939

6940
procedure TGLScene.LoadFromStream(aStream: TStream);
6941
var
6942
  fixups: TStringList;
6943
  i: Integer;
6944
  obj: TGLBaseSceneObject;
6945
begin
6946
  Fixups := TStringList.Create;
6947
  try
6948
    if Assigned(FBuffers) then
6949
    begin
6950
      for i := 0 to FBuffers.Count - 1 do
6951
        Fixups.AddObject(TGLSceneBuffer(FBuffers[i]).Camera.Name, FBuffers[i]);
6952
    end;
6953
    ShutdownAllLights;
6954
    // will remove Viewer from FBuffers
6955
    Objects.DeleteChildren;
6956
    aStream.ReadComponent(Self);
6957
    for i := 0 to Fixups.Count - 1 do
6958
    begin
6959
      obj := FindSceneObject(fixups[I]);
6960
      if obj is TGLCamera then
6961
        TGLSceneBuffer(Fixups.Objects[i]).Camera := TGLCamera(obj)
6962
      else { can assign default camera (if existing, of course) instead }
6963
        ;
6964
    end;
6965
  finally
6966
    Fixups.Free;
6967
  end;
6968
end;
6969

6970
// SaveToStream
6971
//
6972

6973
procedure TGLScene.SaveToStream(aStream: TStream);
6974
begin
6975
  aStream.WriteComponent(Self);
6976
end;
6977

6978
// FindSceneObject
6979
//
6980

6981
function TGLScene.FindSceneObject(const AName: string): TGLBaseSceneObject;
6982
begin
6983
  Result := FObjects.FindChild(AName, False);
6984
end;
6985

6986
// RayCastIntersect
6987
//
6988

6989
function TGLScene.RayCastIntersect(const rayStart, rayVector: TVector;
6990
  intersectPoint: PVector = nil;
6991
  intersectNormal: PVector = nil): TGLBaseSceneObject;
6992
var
6993
  bestDist2: Single;
6994
  bestHit: TGLBaseSceneObject;
6995
  iPoint, iNormal: TVector;
6996
  pINormal: PVector;
6997

6998
  function RecursiveDive(baseObject: TGLBaseSceneObject): TGLBaseSceneObject;
6999
  var
7000
    i: Integer;
7001
    curObj: TGLBaseSceneObject;
7002
    dist2: Single;
7003
    fNear, fFar: single;
7004
  begin
7005
    Result := nil;
7006
    for i := 0 to baseObject.Count - 1 do
7007
    begin
7008
      curObj := baseObject.Children[i];
7009
      if curObj.Visible then
7010
      begin
7011
        if RayCastAABBIntersect(rayStart, rayVector,
7012
          curObj.AxisAlignedBoundingBoxAbsoluteEx, fNear, fFar) then
7013
        begin
7014
          if fnear * fnear > bestDist2 then
7015
          begin
7016
            if not PointInAABB(rayStart, curObj.AxisAlignedBoundingBoxAbsoluteEx) then
7017
              continue;
7018
          end;
7019
          if curObj.RayCastIntersect(rayStart, rayVector, @iPoint, pINormal) then
7020
          begin
7021
            dist2 := VectorDistance2(rayStart, iPoint);
7022
            if dist2 < bestDist2 then
7023
            begin
7024
              bestHit := curObj;
7025
              bestDist2 := dist2;
7026
              if Assigned(intersectPoint) then
7027
                intersectPoint^ := iPoint;
7028
              if Assigned(intersectNormal) then
7029
                intersectNormal^ := iNormal;
7030
            end;
7031
          end;
7032
          RecursiveDive(curObj);
7033
        end;
7034
      end;
7035
    end;
7036
  end;
7037

7038
begin
7039
  bestDist2 := 1e20;
7040
  bestHit := nil;
7041
  if Assigned(intersectNormal) then
7042
    pINormal := @iNormal
7043
  else
7044
    pINormal := nil;
7045
  RecursiveDive(Objects);
7046
  Result := bestHit;
7047
end;
7048

7049
// NotifyChange
7050
//
7051

7052
procedure TGLScene.NotifyChange(Sender: TObject);
7053
var
7054
  i: Integer;
7055
begin
7056
  if (not IsUpdating) and Assigned(FBuffers) then
7057
    for i := 0 to FBuffers.Count - 1 do
7058
      TGLSceneBuffer(FBuffers[i]).NotifyChange(Self);
7059
end;
7060

7061
// SetupLights
7062
//
7063

7064
procedure TGLScene.SetupLights(maxLights: Integer);
7065
var
7066
  i: Integer;
7067
  lightSource: TGLLightSource;
7068
  nbLights: Integer;
7069
  lPos: TVector;
7070
begin
7071
  nbLights := FLights.Count;
7072
  if nbLights > maxLights then
7073
    nbLights := maxLights;
7074
  // setup all light sources
7075
  with CurrentGLContext.GLStates, CurrentGLContext.PipelineTransformation do
7076
  begin
7077
    for i := 0 to nbLights - 1 do
7078
    begin
7079
      lightSource := TGLLightSource(FLights[i]);
7080
      if Assigned(lightSource) then
7081
        with lightSource do
7082
        begin
7083
          LightEnabling[FLightID] := Shining;
7084
          if Shining then
7085
          begin
7086
            if FixedFunctionPipeLight then
7087
            begin
7088
              RebuildMatrix;
7089
              if LightStyle in [lsParallel, lsParallelSpot] then
7090
              begin
7091
                ModelMatrix := AbsoluteMatrix;
7092
                GL.Lightfv(GL_LIGHT0 + FLightID, GL_POSITION, SpotDirection.AsAddress);
7093
              end
7094
              else
7095
              begin
7096
                ModelMatrix := Parent.AbsoluteMatrix;
7097
                GL.Lightfv(GL_LIGHT0 + FLightID, GL_POSITION, Position.AsAddress);
7098
              end;
7099
              if LightStyle in [lsSpot, lsParallelSpot] then
7100
              begin
7101
                if FSpotCutOff <> 180 then
7102
                  GL.Lightfv(GL_LIGHT0 + FLightID, GL_SPOT_DIRECTION, FSpotDirection.AsAddress);
7103
              end;
7104
            end;
7105

7106
            lPos := lightSource.AbsolutePosition;
7107
            if LightStyle in [lsParallel, lsParallelSpot] then
7108
              lPos.V[3] := 0.0
7109
            else
7110
              lPos.V[3] := 1.0;
7111
            LightPosition[FLightID] := lPos;
7112
            LightSpotDirection[FLightID] := lightSource.SpotDirection.AsAffineVector;
7113

7114
            LightAmbient[FLightID] := FAmbient.Color;
7115
            LightDiffuse[FLightID] := FDiffuse.Color;
7116
            LightSpecular[FLightID] := FSpecular.Color;
7117

7118
            LightConstantAtten[FLightID] := FConstAttenuation;
7119
            LightLinearAtten[FLightID] := FLinearAttenuation;
7120
            LightQuadraticAtten[FLightID] := FQuadraticAttenuation;
7121

7122
            LightSpotExponent[FLightID] := FSpotExponent;
7123
            LightSpotCutoff[FLightID] := FSpotCutOff;
7124
          end;
7125
        end
7126
      else
7127
        LightEnabling[i] := False;
7128
    end;
7129
    // turn off other lights
7130
    for i := nbLights to maxLights - 1 do
7131
      LightEnabling[i] := False;
7132
    ModelMatrix := IdentityHmgMatrix;
7133
  end;
7134
end;
7135

7136
// ------------------
7137
// ------------------ TGLFogEnvironment ------------------
7138
// ------------------
7139

7140
// Note: The fog implementation is not conformal with the rest of the scene management
7141
//       because it is viewer bound not scene bound.
7142

7143
// Create
7144
//
7145

7146
constructor TGLFogEnvironment.Create(AOwner: TPersistent);
7147
begin
7148
  inherited;
7149
  FSceneBuffer := (AOwner as TGLSceneBuffer);
7150
  FFogColor := TGLColor.CreateInitialized(Self, clrBlack);
7151
  FFogMode := fmLinear;
7152
  FFogStart := 10;
7153
  FFogEnd := 1000;
7154
  FFogDistance := fdDefault;
7155
end;
7156

7157
// Destroy
7158
//
7159

7160
destructor TGLFogEnvironment.Destroy;
7161
begin
7162
  FFogColor.Free;
7163
  inherited Destroy;
7164
end;
7165

7166
// SetFogColor
7167
//
7168

7169
procedure TGLFogEnvironment.SetFogColor(Value: TGLColor);
7170
begin
7171
  if Assigned(Value) then
7172
  begin
7173
    FFogColor.Assign(Value);
7174
    NotifyChange(Self);
7175
  end;
7176
end;
7177

7178
// SetFogStart
7179
//
7180

7181
procedure TGLFogEnvironment.SetFogStart(Value: Single);
7182
begin
7183
  if Value <> FFogStart then
7184
  begin
7185
    FFogStart := Value;
7186
    NotifyChange(Self);
7187
  end;
7188
end;
7189

7190
// SetFogEnd
7191
//
7192

7193
procedure TGLFogEnvironment.SetFogEnd(Value: Single);
7194
begin
7195
  if Value <> FFogEnd then
7196
  begin
7197
    FFogEnd := Value;
7198
    NotifyChange(Self);
7199
  end;
7200
end;
7201

7202
 
7203
//
7204

7205
procedure TGLFogEnvironment.Assign(Source: TPersistent);
7206
begin
7207
  if Source is TGLFogEnvironment then
7208
  begin
7209
    FFogColor.Assign(TGLFogEnvironment(Source).FFogColor);
7210
    FFogStart := TGLFogEnvironment(Source).FFogStart;
7211
    FFogEnd := TGLFogEnvironment(Source).FFogEnd;
7212
    FFogMode := TGLFogEnvironment(Source).FFogMode;
7213
    FFogDistance := TGLFogEnvironment(Source).FFogDistance;
7214
    NotifyChange(Self);
7215
  end;
7216
  inherited;
7217
end;
7218

7219
// IsAtDefaultValues
7220
//
7221

7222
function TGLFogEnvironment.IsAtDefaultValues: Boolean;
7223
begin
7224
  Result := VectorEquals(FogColor.Color, FogColor.DefaultColor)
7225
    and (FogStart = 10)
7226
    and (FogEnd = 1000)
7227
    and (FogMode = fmLinear)
7228
    and (FogDistance = fdDefault);
7229
end;
7230

7231
// SetFogMode
7232
//
7233

7234
procedure TGLFogEnvironment.SetFogMode(Value: TFogMode);
7235
begin
7236
  if Value <> FFogMode then
7237
  begin
7238
    FFogMode := Value;
7239
    NotifyChange(Self);
7240
  end;
7241
end;
7242

7243
// SetFogDistance
7244
//
7245

7246
procedure TGLFogEnvironment.SetFogDistance(const val: TFogDistance);
7247
begin
7248
  if val <> FFogDistance then
7249
  begin
7250
    FFogDistance := val;
7251
    NotifyChange(Self);
7252
  end;
7253
end;
7254

7255
// ApplyFog
7256
//
7257
var
7258
  vImplemDependantFogDistanceDefault: Integer = -1;
7259

7260
procedure TGLFogEnvironment.ApplyFog;
7261
var
7262
  tempActivation: Boolean;
7263
begin
7264
  with FSceneBuffer do
7265
  begin
7266
    if not Assigned(FRenderingContext) then
7267
      Exit;
7268
    tempActivation := not FRenderingContext.Active;
7269
    if tempActivation then
7270
      FRenderingContext.Activate;
7271
  end;
7272

7273
  case FFogMode of
7274
    fmLinear: GL.Fogi(GL_FOG_MODE, GL_LINEAR);
7275
    fmExp:
7276
      begin
7277
        GL.Fogi(GL_FOG_MODE, GL_EXP);
7278
        GL.Fogf(GL_FOG_DENSITY, FFogColor.Alpha);
7279
      end;
7280
    fmExp2:
7281
      begin
7282
        GL.Fogi(GL_FOG_MODE, GL_EXP2);
7283
        GL.Fogf(GL_FOG_DENSITY, FFogColor.Alpha);
7284
      end;
7285
  end;
7286
  GL.Fogfv(GL_FOG_COLOR, FFogColor.AsAddress);
7287
  GL.Fogf(GL_FOG_START, FFogStart);
7288
  GL.Fogf(GL_FOG_END, FFogEnd);
7289
  if GL.NV_fog_distance then
7290
  begin
7291
    case FogDistance of
7292
      fdDefault:
7293
        begin
7294
          if vImplemDependantFogDistanceDefault = -1 then
7295
            GL.GetIntegerv(GL_FOG_DISTANCE_MODE_NV,
7296
              @vImplemDependantFogDistanceDefault)
7297
          else
7298
            GL.Fogi(GL_FOG_DISTANCE_MODE_NV, vImplemDependantFogDistanceDefault);
7299
        end;
7300
      fdEyePlane:
7301
        GL.Fogi(GL_FOG_DISTANCE_MODE_NV, GL_EYE_PLANE_ABSOLUTE_NV);
7302
      fdEyeRadial:
7303
        GL.Fogi(GL_FOG_DISTANCE_MODE_NV, GL_EYE_RADIAL_NV);
7304
    else
7305
      Assert(False);
7306
    end;
7307
  end;
7308

7309
  if tempActivation then
7310
    FSceneBuffer.RenderingContext.Deactivate;
7311
end;
7312

7313
// ------------------
7314
// ------------------ TGLSceneBuffer ------------------
7315
// ------------------
7316

7317
// Create
7318
//
7319

7320
constructor TGLSceneBuffer.Create(AOwner: TPersistent);
7321
begin
7322
  inherited Create(AOwner);
7323

7324
  // initialize private state variables
7325
  FFogEnvironment := TGLFogEnvironment.Create(Self);
7326
  FBackgroundColor := clBtnFace;
7327
  FBackgroundAlpha := 1;
7328
  FAmbientColor := TGLColor.CreateInitialized(Self, clrGray20);
7329
  FDepthTest := True;
7330
  FFaceCulling := True;
7331
  FLighting := True;
7332
  FAntiAliasing := aaDefault;
7333
  FDepthPrecision := dpDefault;
7334
  FColorDepth := cdDefault;
7335
  FShadeModel := smDefault;
7336
  FFogEnable := False;
7337
  FLayer := clMainPlane;
7338
  FAfterRenderEffects := TPersistentObjectList.Create;
7339

7340
  FContextOptions := [roDoubleBuffer, roRenderToWindow, roDebugContext];
7341

7342
  ResetPerformanceMonitor;
7343
end;
7344

7345
// Destroy
7346
//
7347

7348
destructor TGLSceneBuffer.Destroy;
7349
begin
7350
  Melt;
7351
  DestroyRC;
7352
  FAmbientColor.Free;
7353
  FAfterRenderEffects.Free;
7354
  FFogEnvironment.Free;
7355
  inherited Destroy;
7356
end;
7357

7358
// PrepareGLContext
7359
//
7360

7361
procedure TGLSceneBuffer.PrepareGLContext;
7362
begin
7363
  if Assigned(FOnPrepareGLContext) then
7364
    FOnPrepareGLContext(Self);
7365
end;
7366

7367
// SetupRCOptions
7368
//
7369

7370
procedure TGLSceneBuffer.SetupRCOptions(context: TGLContext);
7371
const
7372
  cColorDepthToColorBits: array[cdDefault..cdFloat128bits] of Integer =
7373
    (24, 8, 16, 24, 64, 128); // float_type
7374
  cDepthPrecisionToDepthBits: array[dpDefault..dp32bits] of Integer =
7375
    (24, 16, 24, 32);
7376
var
7377
  locOptions: TGLRCOptions;
7378
  locStencilBits, locAlphaBits, locColorBits: Integer;
7379
begin
7380
  locOptions := [];
7381

7382
  if roDoubleBuffer in ContextOptions then
7383
    locOptions := locOptions + [rcoDoubleBuffered];
7384
  if roStereo in ContextOptions then
7385
    locOptions := locOptions + [rcoStereo];
7386
  if roDebugContext in ContextOptions then
7387
    locOptions := locOptions + [rcoDebug];
7388
  if roOpenGL_ES2_Context in ContextOptions then
7389
    locOptions := locOptions + [rcoOGL_ES];
7390
  if roNoColorBuffer in ContextOptions then
7391
    locColorBits := 0
7392
  else
7393
    locColorBits := cColorDepthToColorBits[ColorDepth];
7394
  if roStencilBuffer in ContextOptions then
7395
    locStencilBits := 8
7396
  else
7397
    locStencilBits := 0;
7398
  if roDestinationAlpha in ContextOptions then
7399
    locAlphaBits := 8
7400
  else
7401
    locAlphaBits := 0;
7402
  with context do
7403
  begin
7404
    if roSoftwareMode in ContextOptions then
7405
      Acceleration := chaSoftware
7406
    else
7407
      Acceleration := chaHardware;
7408
    Options := locOptions;
7409
    ColorBits := locColorBits;
7410
    DepthBits := cDepthPrecisionToDepthBits[DepthPrecision];
7411
    StencilBits := locStencilBits;
7412
    AlphaBits := locAlphaBits;
7413
    AccumBits := AccumBufferBits;
7414
    AuxBuffers := 0;
7415
    AntiAliasing := Self.AntiAliasing;
7416
    Layer := Self.Layer;
7417
    GLStates.ForwardContext := roForwardContext in ContextOptions;
7418
    PrepareGLContext;
7419
  end;
7420
end;
7421

7422
procedure TGLSceneBuffer.CreateRC(AWindowHandle: HWND; memoryContext:
7423
  Boolean; BufferCount: Integer);
7424
begin
7425
  DestroyRC;
7426
  FRendering := True;
7427

7428
  try
7429
    // will be freed in DestroyWindowHandle
7430
    FRenderingContext := GLContextManager.CreateContext;
7431
    if not Assigned(FRenderingContext) then
7432
      raise Exception.Create('Failed to create RenderingContext.');
7433
    SetupRCOptions(FRenderingContext);
7434

7435
    if Assigned(FCamera) and Assigned(FCamera.FScene) then
7436
      FCamera.FScene.AddBuffer(Self);
7437

7438
    with FRenderingContext do
7439
    begin
7440
      try
7441
        if memoryContext then
7442
          CreateMemoryContext(AWindowHandle, FViewPort.Width, FViewPort.Height,
7443
            BufferCount)
7444
        else
7445
          CreateContext(AWindowHandle);
7446
      except
7447
        FreeAndNil(FRenderingContext);
7448
        raise;
7449
      end;
7450
    end;
7451
    FRenderingContext.Activate;
7452
    try
7453
      // this one should NOT be replaced with an assert
7454
      if not GL.VERSION_1_1 then
7455
      begin
7456
        GLSLogger.LogFatalError(glsWrongVersion);
7457
        Abort;
7458
      end;
7459
      // define viewport, this is necessary because the first WM_SIZE message
7460
      // is posted before the rendering context has been created
7461
      FRenderingContext.GLStates.ViewPort :=
7462
        Vector4iMake(FViewPort.Left, FViewPort.Top, FViewPort.Width, FViewPort.Height);
7463
      // set up initial context states
7464
      SetupRenderingContext(FRenderingContext);
7465
      FRenderingContext.GLStates.ColorClearValue :=
7466
        ConvertWinColor(FBackgroundColor);
7467
    finally
7468
      FRenderingContext.Deactivate;
7469
    end;
7470
  finally
7471
    FRendering := False;
7472
  end;
7473
end;
7474

7475
// DestroyRC
7476
//
7477

7478
procedure TGLSceneBuffer.DestroyRC;
7479
begin
7480
  if Assigned(FRenderingContext) then
7481
  begin
7482
    Melt;
7483
    // for some obscure reason, Mesa3D doesn't like this call... any help welcome
7484
    FreeAndNil(FSelector);
7485
    FreeAndNil(FRenderingContext);
7486
    if Assigned(FCamera) and Assigned(FCamera.FScene) then
7487
      FCamera.FScene.RemoveBuffer(Self);
7488
  end;
7489
end;
7490

7491
// RCInstantiated
7492
//
7493

7494
function TGLSceneBuffer.RCInstantiated: Boolean;
7495
begin
7496
  Result := Assigned(FRenderingContext);
7497
end;
7498

7499
// Resize
7500
//
7501

7502
procedure TGLSceneBuffer.Resize(newLeft, newTop, newWidth, newHeight: Integer);
7503
begin
7504
  if newWidth < 1 then
7505
    newWidth := 1;
7506
  if newHeight < 1 then
7507
    newHeight := 1;
7508
  FViewPort.Left := newLeft;
7509
  FViewPort.Top := newTop;
7510
  FViewPort.Width := newWidth;
7511
  FViewPort.Height := newHeight;
7512
  if Assigned(FRenderingContext) then
7513
  begin
7514
    FRenderingContext.Activate;
7515
    try
7516
      // Part of workaround for MS OpenGL "black borders" bug
7517
      FRenderingContext.GLStates.ViewPort :=
7518
        Vector4iMake(FViewPort.Left, FViewPort.Top, FViewPort.Width, FViewPort.Height);
7519
    finally
7520
      FRenderingContext.Deactivate;
7521
    end;
7522
  end;
7523
end;
7524

7525
// Acceleration
7526
//
7527

7528
function TGLSceneBuffer.Acceleration: TGLContextAcceleration;
7529
begin
7530
  if Assigned(FRenderingContext) then
7531
    Result := FRenderingContext.Acceleration
7532
  else
7533
    Result := chaUnknown;
7534
end;
7535

7536
// SetupRenderingContext
7537
//
7538

7539
procedure TGLSceneBuffer.SetupRenderingContext(context: TGLContext);
7540

7541
  procedure SetState(bool: Boolean; csState: TGLState);
7542
  begin
7543
    case bool of
7544
      true: context.GLStates.PerformEnable(csState);
7545
      false: context.GLStates.PerformDisable(csState);
7546
    end;
7547
  end;
7548

7549
var
7550
  LColorDepth: Cardinal;
7551
begin
7552
  if not Assigned(context) then
7553
    Exit;
7554

7555
  if not (roForwardContext in ContextOptions) then
7556
  begin
7557
    GL.LightModelfv(GL_LIGHT_MODEL_AMBIENT, FAmbientColor.AsAddress);
7558
    if roTwoSideLighting in FContextOptions then
7559
      GL.LightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_TRUE)
7560
    else
7561
      GL.LightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_FALSE);
7562
    GL.Hint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
7563
    case ShadeModel of
7564
      smDefault, smSmooth: GL.ShadeModel(GL_SMOOTH);
7565
      smFlat: GL.ShadeModel(GL_FLAT);
7566
    else
7567
      Assert(False, glsErrorEx + glsUnknownType);
7568
    end;
7569
  end;
7570

7571
  with context.GLStates do
7572
  begin
7573
    Enable(stNormalize);
7574
    SetState(DepthTest, stDepthTest);
7575
    SetState(FaceCulling, stCullFace);
7576
    SetState(Lighting, stLighting);
7577
    SetState(FogEnable, stFog);
7578
    if GL.ARB_depth_clamp then
7579
      Disable(stDepthClamp);
7580
    if not (roForwardContext in ContextOptions) then
7581
    begin
7582
      GL.GetIntegerv(GL_BLUE_BITS, @LColorDepth); // could've used red or green too
7583
      SetState((LColorDepth < 8), stDither);
7584
    end;
7585
    ResetAllGLTextureMatrix;
7586
  end;
7587
end;
7588

7589
// GetLimit
7590
//
7591

7592
function TGLSceneBuffer.GetLimit(Which: TLimitType): Integer;
7593
var
7594
  VP: array[0..1] of Double;
7595
begin
7596
  case Which of
7597
    limClipPlanes:
7598
      GL.GetIntegerv(GL_MAX_CLIP_PLANES, @Result);
7599
    limEvalOrder:
7600
      GL.GetIntegerv(GL_MAX_EVAL_ORDER, @Result);
7601
    limLights:
7602
      GL.GetIntegerv(GL_MAX_LIGHTS, @Result);
7603
    limListNesting:
7604
      GL.GetIntegerv(GL_MAX_LIST_NESTING, @Result);
7605
    limModelViewStack:
7606
      GL.GetIntegerv(GL_MAX_MODELVIEW_STACK_DEPTH, @Result);
7607
    limNameStack:
7608
      GL.GetIntegerv(GL_MAX_NAME_STACK_DEPTH, @Result);
7609
    limPixelMapTable:
7610
      GL.GetIntegerv(GL_MAX_PIXEL_MAP_TABLE, @Result);
7611
    limProjectionStack:
7612
      GL.GetIntegerv(GL_MAX_PROJECTION_STACK_DEPTH, @Result);
7613
    limTextureSize:
7614
      GL.GetIntegerv(GL_MAX_TEXTURE_SIZE, @Result);
7615
    limTextureStack:
7616
      GL.GetIntegerv(GL_MAX_TEXTURE_STACK_DEPTH, @Result);
7617
    limViewportDims:
7618
      begin
7619
        GL.GetDoublev(GL_MAX_VIEWPORT_DIMS, @VP);
7620
        if VP[0] > VP[1] then
7621
          Result := Round(VP[0])
7622
        else
7623
          Result := Round(VP[1]);
7624
      end;
7625
    limAccumAlphaBits:
7626
      GL.GetIntegerv(GL_ACCUM_ALPHA_BITS, @Result);
7627
    limAccumBlueBits:
7628
      GL.GetIntegerv(GL_ACCUM_BLUE_BITS, @Result);
7629
    limAccumGreenBits:
7630
      GL.GetIntegerv(GL_ACCUM_GREEN_BITS, @Result);
7631
    limAccumRedBits:
7632
      GL.GetIntegerv(GL_ACCUM_RED_BITS, @Result);
7633
    limAlphaBits:
7634
      GL.GetIntegerv(GL_ALPHA_BITS, @Result);
7635
    limAuxBuffers:
7636
      GL.GetIntegerv(GL_AUX_BUFFERS, @Result);
7637
    limDepthBits:
7638
      GL.GetIntegerv(GL_DEPTH_BITS, @Result);
7639
    limStencilBits:
7640
      GL.GetIntegerv(GL_STENCIL_BITS, @Result);
7641
    limBlueBits:
7642
      GL.GetIntegerv(GL_BLUE_BITS, @Result);
7643
    limGreenBits:
7644
      GL.GetIntegerv(GL_GREEN_BITS, @Result);
7645
    limRedBits:
7646
      GL.GetIntegerv(GL_RED_BITS, @Result);
7647
    limIndexBits:
7648
      GL.GetIntegerv(GL_INDEX_BITS, @Result);
7649
    limStereo:
7650
      GL.GetIntegerv(GL_STEREO, @Result);
7651
    limDoubleBuffer:
7652
      GL.GetIntegerv(GL_DOUBLEBUFFER, @Result);
7653
    limSubpixelBits:
7654
      GL.GetIntegerv(GL_SUBPIXEL_BITS, @Result);
7655
    limNbTextureUnits:
7656
      if GL.ARB_multitexture then
7657
        GL.GetIntegerv(GL_MAX_TEXTURE_UNITS_ARB, @Result)
7658
      else
7659
        Result := 1;
7660
  else
7661
    Result := 0;
7662
  end;
7663
end;
7664

7665
// RenderToFile
7666
//
7667

7668
procedure TGLSceneBuffer.RenderToFile(const aFile: string; DPI: Integer);
7669
var
7670
  aBitmap: TGLBitmap;
7671
  saveAllowed: Boolean;
7672
  fileName: string;
7673
begin
7674
  Assert((not FRendering), glsAlreadyRendering);
7675
  aBitmap := TGLBitmap.Create;
7676
  try
7677
    aBitmap.Width := FViewPort.Width;
7678
    aBitmap.Height := FViewPort.Height;
7679
    aBitmap.PixelFormat := glpf24Bit;
7680
    RenderToBitmap(ABitmap, DPI);
7681
    fileName := aFile;
7682
    if fileName = '' then
7683
      saveAllowed := SavePictureDialog(fileName)
7684
    else
7685
      saveAllowed := True;
7686
    if saveAllowed then
7687
    begin
7688
      if FileExists(fileName) then
7689
        saveAllowed := QuestionDlg(Format('Overwrite file %s?', [fileName]));
7690
      if saveAllowed then
7691
        aBitmap.SaveToFile(fileName);
7692
    end;
7693
  finally
7694
    aBitmap.Free;
7695
  end;
7696
end;
7697

7698
// RenderToFile
7699
//
7700

7701
procedure TGLSceneBuffer.RenderToFile(const AFile: string; bmpWidth, bmpHeight:
7702
  Integer);
7703
var
7704
  aBitmap: TGLBitmap;
7705
  saveAllowed: Boolean;
7706
  fileName: string;
7707
begin
7708
  Assert((not FRendering), glsAlreadyRendering);
7709
  aBitmap := TGLBitmap.Create;
7710
  try
7711
    aBitmap.Width := bmpWidth;
7712
    aBitmap.Height := bmpHeight;
7713
    aBitmap.PixelFormat := glpf24Bit;
7714
    RenderToBitmap(aBitmap,
7715
      (GetDeviceLogicalPixelsX(Cardinal(ABitmap.Canvas.Handle)) * bmpWidth) div
7716
      FViewPort.Width);
7717
    fileName := AFile;
7718
    if fileName = '' then
7719
      saveAllowed := SavePictureDialog(fileName)
7720
    else
7721
      saveAllowed := True;
7722
    if saveAllowed then
7723
    begin
7724
      if FileExists(fileName) then
7725
        saveAllowed := QuestionDlg(Format('Overwrite file %s?', [fileName]));
7726
      if SaveAllowed then
7727
        aBitmap.SaveToFile(fileName);
7728
    end;
7729
  finally
7730
    aBitmap.Free;
7731
  end;
7732
end;
7733

7734
// TGLBitmap32
7735
//
7736

7737
function TGLSceneBuffer.CreateSnapShot: TGLBitmap32;
7738
begin
7739
  Result := TGLBitmap32.Create;
7740
  Result.Width := FViewPort.Width;
7741
  Result.Height := FViewPort.Height;
7742
  if Assigned(Camera) and Assigned(Camera.Scene) then
7743
  begin
7744
    FRenderingContext.Activate;
7745
    try
7746
      Result.ReadPixels(Rect(0, 0, FViewPort.Width, FViewPort.Height));
7747
    finally
7748
      FRenderingContext.DeActivate;
7749
    end;
7750
  end;
7751
end;
7752

7753
// CreateSnapShotBitmap
7754
//
7755

7756
function TGLSceneBuffer.CreateSnapShotBitmap: TGLBitmap;
7757
var
7758
  bmp32: TGLBitmap32;
7759
begin
7760
  bmp32 := CreateSnapShot;
7761
  try
7762
    Result := bmp32.Create32BitsBitmap;
7763
  finally
7764
    bmp32.Free;
7765
  end;
7766
end;
7767

7768
// CopyToTexture
7769
//
7770

7771
procedure TGLSceneBuffer.CopyToTexture(aTexture: TGLTexture);
7772
begin
7773
  CopyToTexture(aTexture, 0, 0, Width, Height, 0, 0);
7774
end;
7775

7776
// CopyToTexture
7777
//
7778

7779
procedure TGLSceneBuffer.CopyToTexture(aTexture: TGLTexture;
7780
  xSrc, ySrc, AWidth, AHeight: Integer;
7781
  xDest, yDest: Integer;
7782
  glCubeFace: TGLEnum = 0);
7783
var
7784
  bindTarget: TGLTextureTarget;
7785
begin
7786
  if RenderingContext <> nil then
7787
  begin
7788
    RenderingContext.Activate;
7789
    try
7790
      if not (aTexture.Image is TGLBlankImage) then
7791
        aTexture.ImageClassName := TGLBlankImage.ClassName;
7792
      if aTexture.Image.Width <> AWidth then
7793
        TGLBlankImage(aTexture.Image).Width := AWidth;
7794
      if aTexture.Image.Height <> AHeight then
7795
        TGLBlankImage(aTexture.Image).Height := AHeight;
7796
      if aTexture.Image.Depth <> 0 then
7797
        TGLBlankImage(aTexture.Image).Depth := 0;
7798
      if TGLBlankImage(aTexture.Image).CubeMap <> (glCubeFace > 0) then
7799
        TGLBlankImage(aTexture.Image).CubeMap := (glCubeFace > 0);
7800

7801
      bindTarget := aTexture.Image.NativeTextureTarget;
7802
      RenderingContext.GLStates.TextureBinding[0, bindTarget] := aTexture.Handle;
7803
      if glCubeFace > 0 then
7804
        GL.CopyTexSubImage2D(glCubeFace,
7805
          0, xDest, yDest, xSrc, ySrc, AWidth, AHeight)
7806
      else
7807
        GL.CopyTexSubImage2D(DecodeGLTextureTarget(bindTarget),
7808
          0, xDest, yDest, xSrc, ySrc, AWidth, AHeight)
7809
    finally
7810
      RenderingContext.Deactivate;
7811
    end;
7812
  end;
7813
end;
7814

7815
procedure TGLSceneBuffer.SaveAsFloatToFile(const aFilename: string);
7816
var
7817
  Data: pointer;
7818
  DataSize: integer;
7819
  Stream: TMemoryStream;
7820
const
7821
  FloatSize = 4;
7822
begin
7823
  if Assigned(Camera) and Assigned(Camera.Scene) then
7824
  begin
7825
    DataSize := Width * Height * FloatSize * FloatSize;
7826
    GetMem(Data, DataSize);
7827
    FRenderingContext.Activate;
7828
    try
7829
      GL.ReadPixels(0, 0, Width, Height, GL_RGBA, GL_FLOAT, Data);
7830
      GL.CheckError;
7831

7832
      Stream := TMemoryStream.Create;
7833
      try
7834
        Stream.Write(Data^, DataSize);
7835
        Stream.SaveToFile(aFilename);
7836
      finally
7837
        Stream.Free;
7838
      end;
7839
    finally
7840
      FRenderingContext.DeActivate;
7841
      FreeMem(Data);
7842
    end;
7843
  end;
7844
end;
7845

7846
// SetViewPort
7847
//
7848

7849
procedure TGLSceneBuffer.SetViewPort(X, Y, W, H: Integer);
7850
begin
7851
  with FViewPort do
7852
  begin
7853
    Left := X;
7854
    Top := Y;
7855
    Width := W;
7856
    Height := H;
7857
  end;
7858
  NotifyChange(Self);
7859
end;
7860

7861
// Width
7862
//
7863

7864
function TGLSceneBuffer.Width: Integer;
7865
begin
7866
  Result := FViewPort.Width;
7867
end;
7868

7869
// Height
7870
//
7871

7872
function TGLSceneBuffer.Height: Integer;
7873
begin
7874
  Result := FViewPort.Height;
7875
end;
7876

7877
// Freeze
7878
//
7879

7880
procedure TGLSceneBuffer.Freeze;
7881
begin
7882
  if Freezed then
7883
    Exit;
7884
  if RenderingContext = nil then
7885
    Exit;
7886
  Render;
7887
  FFreezed := True;
7888
  RenderingContext.Activate;
7889
  try
7890
    FFreezeBuffer := AllocMem(FViewPort.Width * FViewPort.Height * 4);
7891
    GL.ReadPixels(0, 0, FViewport.Width, FViewPort.Height,
7892
      GL_RGBA, GL_UNSIGNED_BYTE, FFreezeBuffer);
7893
    FFreezedViewPort := FViewPort;
7894
  finally
7895
    RenderingContext.Deactivate;
7896
  end;
7897
end;
7898

7899
// Melt
7900
//
7901

7902
procedure TGLSceneBuffer.Melt;
7903
begin
7904
  if not Freezed then
7905
    Exit;
7906
  FreeMem(FFreezeBuffer);
7907
  FFreezeBuffer := nil;
7908
  FFreezed := False;
7909
end;
7910

7911
// RenderToBitmap
7912
//
7913

7914
procedure TGLSceneBuffer.RenderToBitmap(ABitmap: TGLBitmap; DPI: Integer);
7915
var
7916
  nativeContext: TGLContext;
7917
  aColorBits: Integer;
7918
begin
7919
  Assert((not FRendering), glsAlreadyRendering);
7920
  FRendering := True;
7921
  nativeContext := RenderingContext;
7922
  try
7923
    aColorBits := PixelFormatToColorBits(ABitmap.PixelFormat);
7924
    if aColorBits < 8 then
7925
      aColorBits := 8;
7926
    FRenderingContext := GLContextManager.CreateContext;
7927
    SetupRCOptions(FRenderingContext);
7928
    with FRenderingContext do
7929
    begin
7930
      Options := []; // no such things for bitmap rendering
7931
      ColorBits := aColorBits; // honour Bitmap's pixel depth
7932
      AntiAliasing := aaNone; // no AA for bitmap rendering
7933
      CreateContext(ABitmap.Canvas.Handle);
7934
    end;
7935
    try
7936
      FRenderingContext.Activate;
7937
      try
7938
        SetupRenderingContext(FRenderingContext);
7939
        FRenderingContext.GLStates.ColorClearValue := ConvertWinColor(FBackgroundColor);
7940
        // set the desired viewport and limit output to this rectangle
7941
        with FViewport do
7942
        begin
7943
          Left := 0;
7944
          Top := 0;
7945
          Width := ABitmap.Width;
7946
          Height := ABitmap.Height;
7947
          FRenderingContext.GLStates.ViewPort :=
7948
            Vector4iMake(Left, Top, Width, Height);
7949
        end;
7950
        ClearBuffers;
7951
        FRenderDPI := DPI;
7952
        if FRenderDPI = 0 then
7953
          FRenderDPI := GetDeviceLogicalPixelsX(ABitmap.Canvas.Handle);
7954
        // render
7955
        DoBaseRender(FViewport, FRenderDPI, dsPrinting, nil);
7956
        if nativeContext <> nil then
7957
          FViewport := TRectangle(nativeContext.GLStates.ViewPort);
7958
        GL.Finish;
7959
      finally
7960
        FRenderingContext.Deactivate;
7961
      end;
7962
    finally
7963
      FRenderingContext.Free;
7964
    end;
7965
  finally
7966
    FRenderingContext := nativeContext;
7967
    FRendering := False;
7968
  end;
7969
  if Assigned(FAfterRender) then
7970
    if Owner is TComponent then
7971
      if not (csDesigning in TComponent(Owner).ComponentState) then
7972
        FAfterRender(Self);
7973
end;
7974

7975
// ShowInfo
7976
//
7977

7978
procedure TGLSceneBuffer.ShowInfo(Modal: boolean);
7979
begin
7980
  if not Assigned(FRenderingContext) then
7981
    Exit;
7982
  // most info is available with active context only
7983
  FRenderingContext.Activate;
7984
  try
7985
    InvokeInfoForm(Self, Modal);
7986
  finally
7987
    FRenderingContext.Deactivate;
7988
  end;
7989
end;
7990

7991
// ResetPerformanceMonitor
7992
//
7993

7994
procedure TGLSceneBuffer.ResetPerformanceMonitor;
7995
begin
7996
  FFramesPerSecond := 0;
7997
  FFrameCount := 0;
7998
  FFirstPerfCounter := 0;
7999
end;
8000

8001
// PushViewMatrix
8002
//
8003

8004
procedure TGLSceneBuffer.PushViewMatrix(const newMatrix: TMatrix);
8005
var
8006
  n: Integer;
8007
begin
8008
  n := Length(FViewMatrixStack);
8009
  SetLength(FViewMatrixStack, n + 1);
8010
  FViewMatrixStack[n] := RenderingContext.PipelineTransformation.ViewMatrix;
8011
  RenderingContext.PipelineTransformation.ViewMatrix := newMatrix;
8012
end;
8013

8014
// PopModelViewMatrix
8015
//
8016

8017
procedure TGLSceneBuffer.PopViewMatrix;
8018
var
8019
  n: Integer;
8020
begin
8021
  n := High(FViewMatrixStack);
8022
  Assert(n >= 0, 'Unbalanced PopViewMatrix');
8023
  RenderingContext.PipelineTransformation.ViewMatrix := FViewMatrixStack[n];
8024
  SetLength(FViewMatrixStack, n);
8025
end;
8026

8027
// PushProjectionMatrix
8028
//
8029

8030
procedure TGLSceneBuffer.PushProjectionMatrix(const newMatrix: TMatrix);
8031
var
8032
  n: Integer;
8033
begin
8034
  n := Length(FProjectionMatrixStack);
8035
  SetLength(FProjectionMatrixStack, n + 1);
8036
  FProjectionMatrixStack[n] := RenderingContext.PipelineTransformation.ProjectionMatrix;
8037
  RenderingContext.PipelineTransformation.ProjectionMatrix := newMatrix;
8038
end;
8039

8040
// PopProjectionMatrix
8041
//
8042

8043
procedure TGLSceneBuffer.PopProjectionMatrix;
8044
var
8045
  n: Integer;
8046
begin
8047
  n := High(FProjectionMatrixStack);
8048
  Assert(n >= 0, 'Unbalanced PopProjectionMatrix');
8049
  RenderingContext.PipelineTransformation.ProjectionMatrix := FProjectionMatrixStack[n];
8050
  SetLength(FProjectionMatrixStack, n);
8051
end;
8052

8053
function TGLSceneBuffer.ProjectionMatrix;
8054
begin
8055
  Result := RenderingContext.PipelineTransformation.ProjectionMatrix;
8056
end;
8057

8058
function TGLSceneBuffer.ViewMatrix: TMatrix;
8059
begin
8060
  Result := RenderingContext.PipelineTransformation.ViewMatrix;
8061
end;
8062

8063
function TGLSceneBuffer.ModelMatrix: TMatrix;
8064
begin
8065
  Result := RenderingContext.PipelineTransformation.ModelMatrix;
8066
end;
8067

8068
// OrthoScreenToWorld
8069
//
8070

8071
function TGLSceneBuffer.OrthoScreenToWorld(screenX, screenY: Integer):
8072
  TAffineVector;
8073
var
8074
  camPos, camUp, camRight: TAffineVector;
8075
  f: Single;
8076
begin
8077
  if Assigned(FCamera) then
8078
  begin
8079
    SetVector(camPos, FCameraAbsolutePosition);
8080
    if Camera.TargetObject <> nil then
8081
    begin
8082
      SetVector(camUp, FCamera.AbsoluteUpVectorToTarget);
8083
      SetVector(camRight, FCamera.AbsoluteRightVectorToTarget);
8084
    end
8085
    else
8086
    begin
8087
      SetVector(camUp, Camera.AbsoluteUp);
8088
      SetVector(camRight, Camera.AbsoluteRight);
8089
    end;
8090
    f := 100 * FCamera.NearPlaneBias / (FCamera.FocalLength *
8091
      FCamera.SceneScale);
8092
    if FViewPort.Width > FViewPort.Height then
8093
      f := f / FViewPort.Width
8094
    else
8095
      f := f / FViewPort.Height;
8096
    SetVector(Result,
8097
      VectorCombine3(camPos, camUp, camRight, 1,
8098
      (screenY - (FViewPort.Height div 2)) * f,
8099
      (screenX - (FViewPort.Width div 2)) * f));
8100
  end
8101
  else
8102
    Result := NullVector;
8103
end;
8104

8105
// ScreenToWorld (affine)
8106
//
8107

8108
function TGLSceneBuffer.ScreenToWorld(const aPoint: TAffineVector):
8109
  TAffineVector;
8110
var
8111
  rslt: TVector;
8112
begin
8113
  if Assigned(FCamera)
8114
    and UnProject(
8115
    VectorMake(aPoint),
8116
    RenderingContext.PipelineTransformation.ViewProjectionMatrix,
8117
    PHomogeneousIntVector(@FViewPort)^,
8118
    rslt) then
8119
    Result := Vector3fMake(rslt)
8120
  else
8121
    Result := aPoint;
8122
end;
8123

8124
// ScreenToWorld (hmg)
8125
//
8126

8127
function TGLSceneBuffer.ScreenToWorld(const aPoint: TVector): TVector;
8128
begin
8129
  MakePoint(Result, ScreenToWorld(AffineVectorMake(aPoint)));
8130
end;
8131

8132
// ScreenToWorld (x, y)
8133
//
8134

8135
function TGLSceneBuffer.ScreenToWorld(screenX, screenY: Integer): TAffineVector;
8136
begin
8137
  Result := ScreenToWorld(AffineVectorMake(screenX, FViewPort.Height - screenY,
8138
    0));
8139
end;
8140

8141
// WorldToScreen
8142
//
8143

8144
function TGLSceneBuffer.WorldToScreen(const aPoint: TAffineVector):
8145
  TAffineVector;
8146
var
8147
  rslt: TVector;
8148
begin
8149
  RenderingContext.Activate;
8150
  try
8151
    PrepareRenderingMatrices(FViewPort, FRenderDPI);
8152
    if Assigned(FCamera)
8153
      and Project(
8154
      VectorMake(aPoint),
8155
      RenderingContext.PipelineTransformation.ViewProjectionMatrix,
8156
      TVector4i(FViewPort),
8157
      rslt) then
8158
      Result := Vector3fMake(rslt)
8159
    else
8160
      Result := aPoint;
8161
  finally
8162
    RenderingContext.Deactivate;
8163
  end;
8164
end;
8165

8166
// WorldToScreen
8167
//
8168

8169
function TGLSceneBuffer.WorldToScreen(const aPoint: TVector): TVector;
8170
begin
8171
  SetVector(Result, WorldToScreen(AffineVectorMake(aPoint)));
8172
end;
8173

8174
// WorldToScreen
8175
//
8176

8177
procedure TGLSceneBuffer.WorldToScreen(points: PVector; nbPoints: Integer);
8178
var
8179
  i: Integer;
8180
begin
8181
  if Assigned(FCamera) then
8182
  begin
8183
    for i := nbPoints - 1 downto 0 do
8184
    begin
8185
      Project(points^, RenderingContext.PipelineTransformation.ViewProjectionMatrix, PHomogeneousIntVector(@FViewPort)^, points^);
8186
      Inc(points);
8187
    end;
8188
  end;
8189
end;
8190

8191
// ScreenToVector (affine)
8192
//
8193

8194
function TGLSceneBuffer.ScreenToVector(const aPoint: TAffineVector):
8195
  TAffineVector;
8196
begin
8197
  Result := VectorSubtract(ScreenToWorld(aPoint),
8198
    PAffineVector(@FCameraAbsolutePosition)^);
8199
end;
8200

8201
// ScreenToVector (hmg)
8202
//
8203

8204
function TGLSceneBuffer.ScreenToVector(const aPoint: TVector): TVector;
8205
begin
8206
  SetVector(Result, VectorSubtract(ScreenToWorld(aPoint),
8207
    FCameraAbsolutePosition));
8208
  Result.V[3] := 0;
8209
end;
8210

8211
// ScreenToVector
8212
//
8213

8214
function TGLSceneBuffer.ScreenToVector(const x, y: Integer): TVector;
8215
var
8216
  av: TAffineVector;
8217
begin
8218
  av.V[0] := x;
8219
  av.V[1] := y;
8220
  av.V[2] := 0;
8221
  SetVector(Result, ScreenToVector(av));
8222
end;
8223

8224
// VectorToScreen
8225
//
8226

8227
function TGLSceneBuffer.VectorToScreen(const VectToCam: TAffineVector):
8228
  TAffineVector;
8229
begin
8230
  Result := WorldToScreen(VectorAdd(VectToCam,
8231
    PAffineVector(@FCameraAbsolutePosition)^));
8232
end;
8233

8234
// ScreenVectorIntersectWithPlane
8235
//
8236

8237
function TGLSceneBuffer.ScreenVectorIntersectWithPlane(
8238
  const aScreenPoint: TVector;
8239
  const planePoint, planeNormal: TVector;
8240
  var intersectPoint: TVector): Boolean;
8241
var
8242
  v: TVector;
8243
begin
8244
  if Assigned(FCamera) then
8245
  begin
8246
    SetVector(v, ScreenToVector(aScreenPoint));
8247
    Result := RayCastPlaneIntersect(FCameraAbsolutePosition,
8248
      v, planePoint, planeNormal, @intersectPoint);
8249
    intersectPoint.V[3] := 1;
8250
  end
8251
  else
8252
    Result := False;
8253
end;
8254

8255
// ScreenVectorIntersectWithPlaneXY
8256
//
8257

8258
function TGLSceneBuffer.ScreenVectorIntersectWithPlaneXY(
8259
  const aScreenPoint: TVector; const z: Single;
8260
  var intersectPoint: TVector): Boolean;
8261
begin
8262
  Result := ScreenVectorIntersectWithPlane(aScreenPoint, VectorMake(0, 0, z),
8263
    ZHmgVector, intersectPoint);
8264
  intersectPoint.V[3] := 0;
8265
end;
8266

8267
// ScreenVectorIntersectWithPlaneYZ
8268
//
8269

8270
function TGLSceneBuffer.ScreenVectorIntersectWithPlaneYZ(
8271
  const aScreenPoint: TVector; const x: Single;
8272
  var intersectPoint: TVector): Boolean;
8273
begin
8274
  Result := ScreenVectorIntersectWithPlane(aScreenPoint, VectorMake(x, 0, 0),
8275
    XHmgVector, intersectPoint);
8276
  intersectPoint.V[3] := 0;
8277
end;
8278

8279
// ScreenVectorIntersectWithPlaneXZ
8280
//
8281

8282
function TGLSceneBuffer.ScreenVectorIntersectWithPlaneXZ(
8283
  const aScreenPoint: TVector; const y: Single;
8284
  var intersectPoint: TVector): Boolean;
8285
begin
8286
  Result := ScreenVectorIntersectWithPlane(aScreenPoint, VectorMake(0, y, 0),
8287
    YHmgVector, intersectPoint);
8288
  intersectPoint.V[3] := 0;
8289
end;
8290

8291
// PixelRayToWorld
8292
//
8293

8294
function TGLSceneBuffer.PixelRayToWorld(x, y: Integer): TAffineVector;
8295
var
8296
  dov, np, fp, z, dst, wrpdst: Single;
8297
  vec, cam, targ, rayhit, pix: TAffineVector;
8298
  camAng: real;
8299
begin
8300
  if Camera.CameraStyle = csOrtho2D then
8301
    dov := 2
8302
  else
8303
    dov := Camera.DepthOfView;
8304
  np := Camera.NearPlane;
8305
  fp := Camera.NearPlane + dov;
8306
  z := GetPixelDepth(x, y);
8307
  dst := (fp * np) / (fp - z * dov); //calc from z-buffer value to world depth
8308
  //------------------------
8309
  //z:=1-(fp/d-1)/(fp/np-1);  //calc from world depth to z-buffer value
8310
  //------------------------
8311
  vec.V[0] := x;
8312
  vec.V[1] := FViewPort.Height - y;
8313
  vec.V[2] := 0;
8314
  vec := ScreenToVector(vec);
8315
  NormalizeVector(vec);
8316
  SetVector(cam, Camera.AbsolutePosition);
8317
  //targ:=Camera.TargetObject.Position.AsAffineVector;
8318
  //SubtractVector(targ,cam);
8319
  pix.V[0] := FViewPort.Width * 0.5;
8320
  pix.V[1] := FViewPort.Height * 0.5;
8321
  pix.V[2] := 0;
8322
  targ := self.ScreenToVector(pix);
8323

8324
  camAng := VectorAngleCosine(targ, vec);
8325
  wrpdst := dst / camAng;
8326
  rayhit := cam;
8327
  CombineVector(rayhit, vec, wrpdst);
8328
  result := rayhit;
8329
end;
8330

8331
// ClearBuffers
8332
//
8333

8334
procedure TGLSceneBuffer.ClearBuffers;
8335
var
8336
  bufferBits: TGLBitfield;
8337
begin
8338
  if roNoDepthBufferClear in ContextOptions then
8339
    bufferBits := 0
8340
  else
8341
  begin
8342
    bufferBits := GL_DEPTH_BUFFER_BIT;
8343
    CurrentGLContext.GLStates.DepthWriteMask := True;
8344
  end;
8345
  if ContextOptions * [roNoColorBuffer, roNoColorBufferClear] = [] then
8346
  begin
8347
    bufferBits := bufferBits or GL_COLOR_BUFFER_BIT;
8348
    CurrentGLContext.GLStates.SetColorMask(cAllColorComponents);
8349
  end;
8350
  if roStencilBuffer in ContextOptions then
8351
  begin
8352
    bufferBits := bufferBits or GL_STENCIL_BUFFER_BIT;
8353
  end;
8354
  GL.Clear(BufferBits);
8355
end;
8356

8357
// NotifyChange
8358
//
8359

8360
procedure TGLSceneBuffer.NotifyChange(Sender: TObject);
8361
begin
8362
  DoChange;
8363
end;
8364

8365
// PickObjects
8366
//
8367

8368
procedure TGLSceneBuffer.PickObjects(const rect: TGLRect; pickList: TGLPickList;
8369
  objectCountGuess: Integer);
8370
var
8371
  I: Integer;
8372
  obj: TGLBaseSceneObject;
8373
begin
8374
  if not Assigned(FCamera) then
8375
    Exit;
8376
  Assert((not FRendering), glsAlreadyRendering);
8377
  Assert(Assigned(PickList));
8378
  FRenderingContext.Activate;
8379
  FRendering := True;
8380
  try
8381
    // Create best selector which techniques is hardware can do
8382
    if not Assigned(FSelector) then
8383
      FSelector := GetBestSelectorClass.Create;
8384

8385
    xgl.MapTexCoordToNull; // turn off
8386
    PrepareRenderingMatrices(FViewPort, RenderDPI, @Rect);
8387
    FSelector.Hits := -1;
8388
    if objectCountGuess > 0 then
8389
      FSelector.ObjectCountGuess := objectCountGuess;
8390
    repeat
8391
      FSelector.Start;
8392
      // render the scene (in select mode, nothing is drawn)
8393
      FRenderDPI := 96;
8394
      if Assigned(FCamera) and Assigned(FCamera.FScene) then
8395
        RenderScene(FCamera.FScene, FViewPort.Width, FViewPort.Height,
8396
          dsPicking, nil);
8397
    until FSelector.Stop;
8398
    FSelector.FillPickingList(PickList);
8399
    for I := 0 to PickList.Count-1 do
8400
    begin
8401
      obj := TGLBaseSceneObject(PickList[I]);
8402
      if Assigned(obj.FOnPicked) then
8403
        obj.FOnPicked(obj);
8404
    end;
8405
  finally
8406
    FRendering := False;
8407
    FRenderingContext.Deactivate;
8408
  end;
8409
end;
8410

8411
// GetPickedObjects
8412
//
8413

8414
function TGLSceneBuffer.GetPickedObjects(const rect: TGLRect; objectCountGuess:
8415
  Integer = 64): TGLPickList;
8416
begin
8417
  Result := TGLPickList.Create(psMinDepth);
8418
  PickObjects(Rect, Result, objectCountGuess);
8419
end;
8420

8421
// GetPickedObject
8422
//
8423

8424
function TGLSceneBuffer.GetPickedObject(x, y: Integer): TGLBaseSceneObject;
8425
var
8426
  pkList: TGLPickList;
8427
begin
8428
  pkList := GetPickedObjects(Rect(x - 1, y - 1, x + 1, y + 1));
8429
  try
8430
    if pkList.Count > 0 then
8431
      Result := TGLBaseSceneObject(pkList.Hit[0])
8432
    else
8433
      Result := nil;
8434
  finally
8435
    pkList.Free;
8436
  end;
8437
end;
8438

8439
// GetPixelColor
8440
//
8441

8442
function TGLSceneBuffer.GetPixelColor(x, y: Integer): TColor;
8443
var
8444
  buf: array[0..2] of Byte;
8445
begin
8446
  if not Assigned(FCamera) then
8447
  begin
8448
    Result := 0;
8449
    Exit;
8450
  end;
8451
  FRenderingContext.Activate;
8452
  try
8453
    GL.ReadPixels(x, FViewPort.Height - y, 1, 1, GL_RGB, GL_UNSIGNED_BYTE,
8454
      @buf[0]);
8455
  finally
8456
    FRenderingContext.Deactivate;
8457
  end;
8458
  Result := RGB(buf[0], buf[1], buf[2]);
8459
end;
8460

8461
// GetPixelDepth
8462
//
8463

8464
function TGLSceneBuffer.GetPixelDepth(x, y: Integer): Single;
8465
begin
8466
  if not Assigned(FCamera) then
8467
  begin
8468
    Result := 0;
8469
    Exit;
8470
  end;
8471
  FRenderingContext.Activate;
8472
  try
8473
    GL.ReadPixels(x, FViewPort.Height - y, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT,
8474
      @Result);
8475
  finally
8476
    FRenderingContext.Deactivate;
8477
  end;
8478
end;
8479

8480
// PixelDepthToDistance
8481
//
8482

8483
function TGLSceneBuffer.PixelDepthToDistance(aDepth: Single): Single;
8484
var
8485
  dov, np, fp: Single;
8486
begin
8487
  if Camera.CameraStyle = csOrtho2D then
8488
    dov := 2
8489
  else
8490
    dov := Camera.DepthOfView; // Depth of View (from np to fp)
8491
  np := Camera.NearPlane; // Near plane distance
8492
  fp := np + dov; // Far plane distance
8493
  Result := (fp * np) / (fp - aDepth * dov);
8494
  // calculate world distance from z-buffer value
8495
end;
8496

8497
// PixelToDistance
8498
//
8499

8500
function TGLSceneBuffer.PixelToDistance(x, y: integer): Single;
8501
var
8502
  z, dov, np, fp, dst, camAng: Single;
8503
  norm, coord, vec: TAffineVector;
8504
begin
8505
  z := GetPixelDepth(x, y);
8506
  if Camera.CameraStyle = csOrtho2D then
8507
    dov := 2
8508
  else
8509
    dov := Camera.DepthOfView; // Depth of View (from np to fp)
8510
  np := Camera.NearPlane; // Near plane distance
8511
  fp := np + dov; // Far plane distance
8512
  dst := (np * fp) / (fp - z * dov);
8513
  //calculate from z-buffer value to frustrum depth
8514
  coord.V[0] := x;
8515
  coord.V[1] := y;
8516
  vec := self.ScreenToVector(coord); //get the pixel vector
8517
  coord.V[0] := FViewPort.Width div 2;
8518
  coord.V[1] := FViewPort.Height div 2;
8519
  norm := self.ScreenToVector(coord); //get the absolute camera direction
8520
  camAng := VectorAngleCosine(norm, vec);
8521
  Result := dst / camAng; //compensate for flat frustrum face
8522
end;
8523

8524
// NotifyMouseMove
8525
//
8526

8527
procedure TGLSceneBuffer.NotifyMouseMove(Shift: TShiftState; X, Y: Integer);
8528
begin
8529
  // Nothing
8530
end;
8531

8532
// PrepareRenderingMatrices
8533
//
8534

8535
procedure TGLSceneBuffer.PrepareRenderingMatrices(const aViewPort: TRectangle;
8536
  resolution: Integer; pickingRect: PGLRect = nil);
8537
begin
8538
  RenderingContext.PipelineTransformation.IdentityAll;
8539
  // setup projection matrix
8540
  if Assigned(pickingRect) then
8541
  begin
8542
    CurrentGLContext.PipelineTransformation.ProjectionMatrix := CreatePickMatrix(
8543
      (pickingRect^.Left + pickingRect^.Right) div 2,
8544
      FViewPort.Height - ((pickingRect^.Top + pickingRect^.Bottom) div 2),
8545
      Abs(pickingRect^.Right - pickingRect^.Left),
8546
      Abs(pickingRect^.Bottom - pickingRect^.Top),
8547
      TVector4i(FViewport));
8548
  end;
8549
  FBaseProjectionMatrix := CurrentGLContext.PipelineTransformation.ProjectionMatrix;
8550

8551
  if Assigned(FCamera) then
8552
  begin
8553
    FCamera.Scene.FCurrentGLCamera := FCamera;
8554
    // apply camera perpective
8555
    FCamera.ApplyPerspective(
8556
      aViewport,
8557
      FViewPort.Width,
8558
      FViewPort.Height,
8559
      resolution);
8560
    // setup model view matrix
8561
    // apply camera transformation (viewpoint)
8562
    FCamera.Apply;
8563
    FCameraAbsolutePosition := FCamera.AbsolutePosition;
8564
  end;
8565
end;
8566

8567
// DoBaseRender
8568
//
8569

8570
procedure TGLSceneBuffer.DoBaseRender(const aViewPort: TRectangle; resolution:
8571
  Integer;
8572
  drawState: TDrawState; baseObject: TGLBaseSceneObject);
8573
begin
8574
  with RenderingContext.GLStates do
8575
  begin
8576
    PrepareRenderingMatrices(aViewPort, resolution);
8577
    if not ForwardContext then
8578
    begin
8579
      xgl.MapTexCoordToNull; // force XGL rebind
8580
      xgl.MapTexCoordToMain;
8581
    end;
8582

8583
    if Assigned(FViewerBeforeRender) and (drawState <> dsPrinting) then
8584
      FViewerBeforeRender(Self);
8585
    if Assigned(FBeforeRender) then
8586
      if Owner is TComponent then
8587
        if not (csDesigning in TComponent(Owner).ComponentState) then
8588
          FBeforeRender(Self);
8589

8590
    if Assigned(FCamera) and Assigned(FCamera.FScene) then
8591
    begin
8592
      with FCamera.FScene do
8593
      begin
8594
        SetupLights(MaxLights);
8595
        if not ForwardContext then
8596
        begin
8597
          if FogEnable then
8598
          begin
8599
            Enable(stFog);
8600
            FogEnvironment.ApplyFog;
8601
          end
8602
          else
8603
            Disable(stFog);
8604
        end;
8605

8606
        RenderScene(FCamera.FScene, aViewPort.Width, aViewPort.Height,
8607
          drawState,
8608
          baseObject);
8609
      end;
8610
    end;
8611
    if Assigned(FPostRender) then
8612
      if Owner is TComponent then
8613
        if not (csDesigning in TComponent(Owner).ComponentState) then
8614
          FPostRender(Self);
8615
  end;
8616
  Assert(Length(FViewMatrixStack) = 0,
8617
    'Unbalance Push/PopViewMatrix.');
8618
  Assert(Length(FProjectionMatrixStack) = 0,
8619
    'Unbalance Push/PopProjectionMatrix.');
8620
end;
8621

8622
// Render
8623
//
8624

8625
procedure TGLSceneBuffer.Render;
8626
begin
8627
  Render(nil);
8628
end;
8629

8630
// Render
8631
//
8632

8633
procedure TGLSceneBuffer.Render(baseObject: TGLBaseSceneObject);
8634
var
8635
  perfCounter, framePerf: Int64;
8636
begin
8637
  if FRendering then
8638
    Exit;
8639
  if not Assigned(FRenderingContext) then
8640
    Exit;
8641

8642
  if Freezed and (FFreezeBuffer <> nil) then
8643
  begin
8644
    RenderingContext.Activate;
8645
    try
8646
      RenderingContext.GLStates.ColorClearValue :=
8647
        ConvertWinColor(FBackgroundColor, FBackgroundAlpha);
8648
      ClearBuffers;
8649
      GL.MatrixMode(GL_PROJECTION);
8650
      GL.LoadIdentity;
8651
      GL.MatrixMode(GL_MODELVIEW);
8652
      GL.LoadIdentity;
8653
      GL.RasterPos2f(-1, -1);
8654
      GL.DrawPixels(FFreezedViewPort.Width, FFreezedViewPort.Height,
8655
        GL_RGBA, GL_UNSIGNED_BYTE, FFreezeBuffer);
8656
      if not (roNoSwapBuffers in ContextOptions) then
8657
        RenderingContext.SwapBuffers;
8658
    finally
8659
      RenderingContext.Deactivate;
8660
    end;
8661
    Exit;
8662
  end;
8663

8664
  QueryPerformanceCounter(framePerf);
8665

8666
  if Assigned(FCamera) and Assigned(FCamera.FScene) then
8667
  begin
8668
    FCamera.AbsoluteMatrixAsAddress;
8669
    FCamera.FScene.AddBuffer(Self);
8670
  end;
8671

8672
  FRendering := True;
8673
  try
8674
    FRenderingContext.Activate;
8675
    try
8676
      if FFrameCount = 0 then
8677
        QueryPerformanceCounter(FFirstPerfCounter);
8678

8679
      FRenderDPI := 96; // default value for screen
8680
      GL.ClearError;
8681
      SetupRenderingContext(FRenderingContext);
8682
      // clear the buffers
8683
      FRenderingContext.GLStates.ColorClearValue :=
8684
        ConvertWinColor(FBackgroundColor, FBackgroundAlpha);
8685
      ClearBuffers;
8686
      GL.CheckError;
8687
      // render
8688
      DoBaseRender(FViewport, RenderDPI, dsRendering, baseObject);
8689

8690
      if not (roNoSwapBuffers in ContextOptions) then
8691
        RenderingContext.SwapBuffers;
8692

8693
      // yes, calculate average frames per second...
8694
      Inc(FFrameCount);
8695
      QueryPerformanceCounter(perfCounter);
8696
      FLastFrameTime := (perfCounter - framePerf) / vCounterFrequency;
8697
      Dec(perfCounter, FFirstPerfCounter);
8698
      if perfCounter > 0 then
8699
        FFramesPerSecond := (FFrameCount * vCounterFrequency) / perfCounter;
8700
      GL.CheckError;
8701
    finally
8702
      FRenderingContext.Deactivate;
8703
    end;
8704
    if Assigned(FAfterRender) and (Owner is TComponent) then
8705
      if not (csDesigning in TComponent(Owner).ComponentState) then
8706
        FAfterRender(Self);
8707
  finally
8708
    FRendering := False;
8709
  end;
8710
end;
8711

8712
// RenderScene
8713
//
8714

8715
procedure TGLSceneBuffer.RenderScene(aScene: TGLScene;
8716
  const viewPortSizeX, viewPortSizeY: Integer;
8717
  drawState: TDrawState;
8718
  baseObject: TGLBaseSceneObject);
8719

8720
var
8721
  i: Integer;
8722
  rci: TGLRenderContextInfo;
8723
  rightVector: TVector;
8724
begin
8725
  FAfterRenderEffects.Clear;
8726
  aScene.FCurrentBuffer := Self;
8727
  FillChar(rci, SizeOf(rci), 0);
8728
  rci.scene := aScene;
8729
  rci.buffer := Self;
8730
  rci.afterRenderEffects := FAfterRenderEffects;
8731
  rci.objectsSorting := aScene.ObjectsSorting;
8732
  rci.visibilityCulling := aScene.VisibilityCulling;
8733
  rci.bufferFaceCull := FFaceCulling;
8734
  rci.bufferLighting := FLighting;
8735
  rci.bufferFog := FFogEnable;
8736
  rci.bufferDepthTest := FDepthTest;
8737
  rci.drawState := drawState;
8738
  rci.sceneAmbientColor := FAmbientColor.Color;
8739
  rci.primitiveMask := cAllMeshPrimitive;
8740
  with FCamera do
8741
  begin
8742
    rci.cameraPosition := FCameraAbsolutePosition;
8743
    rci.cameraDirection := FLastDirection;
8744
    NormalizeVector(rci.cameraDirection);
8745
    rci.cameraDirection.V[3] := 0;
8746
    rightVector := VectorCrossProduct(rci.cameraDirection, Up.AsVector);
8747
    rci.cameraUp := VectorCrossProduct(rightVector, rci.cameraDirection);
8748
    NormalizeVector(rci.cameraUp);
8749

8750
    with rci.rcci do
8751
    begin
8752
      origin := rci.cameraPosition;
8753
      clippingDirection := rci.cameraDirection;
8754
      viewPortRadius := FViewPortRadius;
8755
      nearClippingDistance := FNearPlane;
8756
      farClippingDistance := FNearPlane + FDepthOfView;
8757
      frustum := RenderingContext.PipelineTransformation.Frustum;
8758
    end;
8759
  end;
8760
  rci.viewPortSize.cx := viewPortSizeX;
8761
  rci.viewPortSize.cy := viewPortSizeY;
8762
  rci.renderDPI := FRenderDPI;
8763
  rci.GLStates := RenderingContext.GLStates;
8764
  rci.PipelineTransformation := RenderingContext.PipelineTransformation;
8765
  rci.proxySubObject := False;
8766
  rci.ignoreMaterials := (roNoColorBuffer in FContextOptions)
8767
    or (rci.drawState = dsPicking);
8768
  rci.amalgamating := rci.drawState = dsPicking;
8769
  rci.GLStates.SetGLColorWriting(not rci.ignoreMaterials);
8770
  if Assigned(FInitiateRendering) then
8771
    FInitiateRendering(Self, rci);
8772

8773
  if aScene.InitializableObjects.Count <> 0 then
8774
  begin
8775
    // First initialize all objects and delete them from the list.
8776
    for I := aScene.InitializableObjects.Count - 1 downto 0 do
8777
    begin
8778
      aScene.InitializableObjects.Items[I].InitializeObject({Self?}aScene, rci);
8779
      aScene.InitializableObjects.Delete(I);
8780
    end;
8781
  end;
8782

8783
  if RenderingContext.IsPraparationNeed then
8784
    RenderingContext.PrepareHandlesData;
8785

8786
  if baseObject = nil then
8787
  begin
8788
    aScene.Objects.Render(rci);
8789
  end
8790
  else
8791
    baseObject.Render(rci);
8792
  rci.GLStates.SetGLColorWriting(True);
8793
  with FAfterRenderEffects do
8794
    if Count > 0 then
8795
      for i := 0 to Count - 1 do
8796
        TGLObjectAfterEffect(Items[i]).Render(rci);
8797
  if Assigned(FWrapUpRendering) then
8798
    FWrapUpRendering(Self, rci);
8799
end;
8800

8801
// SetBackgroundColor
8802
//
8803

8804
procedure TGLSceneBuffer.SetBackgroundColor(AColor: TColor);
8805
begin
8806
  if FBackgroundColor <> AColor then
8807
  begin
8808
    FBackgroundColor := AColor;
8809
    NotifyChange(Self);
8810
  end;
8811
end;
8812

8813
// SetBackgroundAlpha
8814
//
8815

8816
procedure TGLSceneBuffer.SetBackgroundAlpha(alpha: Single);
8817
begin
8818
  if FBackgroundAlpha <> alpha then
8819
  begin
8820
    FBackgroundAlpha := alpha;
8821
    NotifyChange(Self);
8822
  end;
8823
end;
8824

8825
// SetAmbientColor
8826
//
8827

8828
procedure TGLSceneBuffer.SetAmbientColor(AColor: TGLColor);
8829
begin
8830
  FAmbientColor.Assign(AColor);
8831
end;
8832

8833
// SetCamera
8834
//
8835

8836
procedure TGLSceneBuffer.SetCamera(ACamera: TGLCamera);
8837
begin
8838
  if FCamera <> ACamera then
8839
  begin
8840
    if Assigned(FCamera) then
8841
    begin
8842
      if Assigned(FCamera.FScene) then
8843
        FCamera.FScene.RemoveBuffer(Self);
8844
      FCamera := nil;
8845
    end;
8846
    if Assigned(ACamera) and Assigned(ACamera.FScene) then
8847
    begin
8848
      FCamera := ACamera;
8849
      FCamera.TransformationChanged;
8850
    end;
8851
    NotifyChange(Self);
8852
  end;
8853
end;
8854

8855
// SetContextOptions
8856
//
8857

8858
procedure TGLSceneBuffer.SetContextOptions(Options: TContextOptions);
8859
begin
8860
  if FContextOptions <> Options then
8861
  begin
8862
    FContextOptions := Options;
8863
    DoStructuralChange;
8864
  end;
8865
end;
8866

8867
// SetDepthTest
8868
//
8869

8870
procedure TGLSceneBuffer.SetDepthTest(AValue: Boolean);
8871
begin
8872
  if FDepthTest <> AValue then
8873
  begin
8874
    FDepthTest := AValue;
8875
    NotifyChange(Self);
8876
  end;
8877
end;
8878

8879
// SetFaceCulling
8880
//
8881

8882
procedure TGLSceneBuffer.SetFaceCulling(AValue: Boolean);
8883
begin
8884
  if FFaceCulling <> AValue then
8885
  begin
8886
    FFaceCulling := AValue;
8887
    NotifyChange(Self);
8888
  end;
8889
end;
8890

8891
procedure TGLSceneBuffer.SetLayer(const Value: TGLContextLayer);
8892
begin
8893
  if FLayer <> Value then
8894
  begin
8895
    FLayer := Value;
8896
    DoStructuralChange;
8897
  end;
8898
end;
8899

8900
procedure TGLSceneBuffer.SetLighting(aValue: Boolean);
8901
begin
8902
  if FLighting <> aValue then
8903
  begin
8904
    FLighting := aValue;
8905
    NotifyChange(Self);
8906
  end;
8907
end;
8908

8909
// SetAntiAliasing
8910
//
8911

8912
procedure TGLSceneBuffer.SetAntiAliasing(const val: TGLAntiAliasing);
8913
begin
8914
  if FAntiAliasing <> val then
8915
  begin
8916
    FAntiAliasing := val;
8917
    DoStructuralChange;
8918
  end;
8919
end;
8920

8921
// SetDepthPrecision
8922
//
8923

8924
procedure TGLSceneBuffer.SetDepthPrecision(const val: TGLDepthPrecision);
8925
begin
8926
  if FDepthPrecision <> val then
8927
  begin
8928
    FDepthPrecision := val;
8929
    DoStructuralChange;
8930
  end;
8931
end;
8932

8933
// SetColorDepth
8934
//
8935

8936
procedure TGLSceneBuffer.SetColorDepth(const val: TGLColorDepth);
8937
begin
8938
  if FColorDepth <> val then
8939
  begin
8940
    FColorDepth := val;
8941
    DoStructuralChange;
8942
  end;
8943
end;
8944

8945
// SetShadeModel
8946
//
8947

8948
procedure TGLSceneBuffer.SetShadeModel(const val: TGLShadeModel);
8949
begin
8950
  if FShadeModel <> val then
8951
  begin
8952
    FShadeModel := val;
8953
    NotifyChange(Self);
8954
  end;
8955
end;
8956

8957
// SetFogEnable
8958
//
8959

8960
procedure TGLSceneBuffer.SetFogEnable(AValue: Boolean);
8961
begin
8962
  if FFogEnable <> AValue then
8963
  begin
8964
    FFogEnable := AValue;
8965
    NotifyChange(Self);
8966
  end;
8967
end;
8968

8969
// SetGLFogEnvironment
8970
//
8971

8972
procedure TGLSceneBuffer.SetGLFogEnvironment(AValue: TGLFogEnvironment);
8973
begin
8974
  FFogEnvironment.Assign(AValue);
8975
  NotifyChange(Self);
8976
end;
8977

8978
// StoreFog
8979
//
8980

8981
function TGLSceneBuffer.StoreFog: Boolean;
8982
begin
8983
  Result := (not FFogEnvironment.IsAtDefaultValues);
8984
end;
8985

8986
// SetAccumBufferBits
8987
//
8988

8989
procedure TGLSceneBuffer.SetAccumBufferBits(const val: Integer);
8990
begin
8991
  if FAccumBufferBits <> val then
8992
  begin
8993
    FAccumBufferBits := val;
8994
    DoStructuralChange;
8995
  end;
8996
end;
8997

8998
// DoChange
8999
//
9000

9001
procedure TGLSceneBuffer.DoChange;
9002
begin
9003
  if (not FRendering) and Assigned(FOnChange) then
9004
    FOnChange(Self);
9005
end;
9006

9007
// DoStructuralChange
9008
//
9009

9010
procedure TGLSceneBuffer.DoStructuralChange;
9011
var
9012
  bCall: Boolean;
9013
begin
9014
  if Assigned(Owner) then
9015
    bCall := not (csLoading in TComponent(GetOwner).ComponentState)
9016
  else
9017
    bCall := True;
9018
  if bCall and Assigned(FOnStructuralChange) then
9019
    FOnStructuralChange(Self);
9020
end;
9021

9022
// ------------------
9023
// ------------------ TGLNonVisualViewer ------------------
9024
// ------------------
9025

9026
// Create
9027
//
9028

9029
constructor TGLNonVisualViewer.Create(AOwner: TComponent);
9030
begin
9031
  inherited Create(AOwner);
9032
  FWidth := 256;
9033
  FHeight := 256;
9034
  FBuffer := TGLSceneBuffer.Create(Self);
9035
  FBuffer.OnChange := DoBufferChange;
9036
  FBuffer.OnStructuralChange := DoBufferStructuralChange;
9037
  FBuffer.OnPrepareGLContext := DoOnPrepareGLContext;
9038
end;
9039

9040
// Destroy
9041
//
9042

9043
destructor TGLNonVisualViewer.Destroy;
9044
begin
9045
  FBuffer.Free;
9046
  inherited Destroy;
9047
end;
9048

9049
// Notification
9050
//
9051

9052
procedure TGLNonVisualViewer.Notification(AComponent: TComponent; Operation:
9053
  TOperation);
9054
begin
9055
  if (Operation = opRemove) and (AComponent = Camera) then
9056
    Camera := nil;
9057
  inherited;
9058
end;
9059

9060
// CopyToTexture
9061
//
9062

9063
procedure TGLNonVisualViewer.CopyToTexture(aTexture: TGLTexture);
9064
begin
9065
  CopyToTexture(aTexture, 0, 0, Width, Height, 0, 0);
9066
end;
9067

9068
// CopyToTexture
9069
//
9070

9071
procedure TGLNonVisualViewer.CopyToTexture(aTexture: TGLTexture;
9072
  xSrc, ySrc, width, height: Integer;
9073
  xDest, yDest: Integer);
9074
begin
9075
  Buffer.CopyToTexture(aTexture, xSrc, ySrc, width, height, xDest, yDest);
9076
end;
9077

9078
// CopyToTextureMRT
9079
//
9080

9081
procedure TGLNonVisualViewer.CopyToTextureMRT(aTexture: TGLTexture;
9082
  BufferIndex: integer);
9083
begin
9084
  CopyToTextureMRT(aTexture, 0, 0, Width, Height, 0, 0, BufferIndex);
9085
end;
9086

9087
// CopyToTextureMRT
9088
//
9089

9090
procedure TGLNonVisualViewer.CopyToTextureMRT(aTexture: TGLTexture; xSrc,
9091
  ySrc, width, height, xDest, yDest, BufferIndex: integer);
9092
var
9093
  target, handle: Integer;
9094
  buf: Pointer;
9095
  createTexture: Boolean;
9096

9097
  procedure CreateNewTexture;
9098
  begin
9099
    GetMem(buf, Width * Height * 4);
9100
    try // float_type
9101
      GL.ReadPixels(0, 0, Width, Height, GL_RGBA, GL_UNSIGNED_BYTE, buf);
9102
      case aTexture.MinFilter of
9103
        miNearest, miLinear:
9104
          GL.TexImage2d(target, 0, aTexture.OpenGLTextureFormat, Width, Height,
9105
            0, GL_RGBA, GL_UNSIGNED_BYTE, buf);
9106
      else
9107
        if GL.SGIS_generate_mipmap and (target = GL_TEXTURE_2D) then
9108
        begin
9109
          // hardware-accelerated when supported
9110
          GL.TexParameteri(target, GL_GENERATE_MIPMAP_SGIS, GL_TRUE);
9111
          GL.TexImage2d(target, 0, aTexture.OpenGLTextureFormat, Width, Height,
9112
            0, GL_RGBA, GL_UNSIGNED_BYTE, buf);
9113
        end
9114
        else
9115
        begin
9116
          GL.TexImage2d(target, 0, aTexture.OpenGLTextureFormat, Width, Height,
9117
            0, GL_RGBA, GL_UNSIGNED_BYTE, buf);
9118
          GL.GenerateMipmap(target);
9119
        end;
9120
      end;
9121
    finally
9122
      FreeMem(buf);
9123
    end;
9124
  end;
9125

9126
begin
9127
  if Buffer.RenderingContext <> nil then
9128
  begin
9129
    Buffer.RenderingContext.Activate;
9130
    try
9131
      target := DecodeGLTextureTarget(aTexture.Image.NativeTextureTarget);
9132

9133
      CreateTexture := true;
9134

9135
      if aTexture.IsFloatType then
9136
      begin // float_type special treatment
9137
        CreateTexture := false;
9138
        handle := aTexture.Handle;
9139
      end
9140
      else if (target <> GL_TEXTURE_CUBE_MAP_ARB) or (FCubeMapRotIdx = 0) then
9141
      begin
9142
        CreateTexture := not aTexture.IsHandleAllocated;
9143
        if CreateTexture then
9144
          handle := aTexture.AllocateHandle
9145
        else
9146
          handle := aTexture.Handle;
9147
      end
9148
      else
9149
        handle := aTexture.Handle;
9150

9151
      // For MRT
9152
      GL.ReadBuffer(MRT_BUFFERS[BufferIndex]);
9153

9154
      Buffer.RenderingContext.GLStates.TextureBinding[0,
9155
        EncodeGLTextureTarget(target)] := handle;
9156

9157
      if target = GL_TEXTURE_CUBE_MAP_ARB then
9158
        target := GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB + FCubeMapRotIdx;
9159

9160
      if CreateTexture then
9161
        CreateNewTexture
9162
      else
9163
        GL.CopyTexSubImage2D(target, 0, xDest, yDest, xSrc, ySrc, Width, Height);
9164

9165
      GL.ClearError;
9166
    finally
9167
      Buffer.RenderingContext.Deactivate;
9168
    end;
9169
  end;
9170
end;
9171

9172
// SetupCubeMapCamera
9173
//
9174

9175
procedure TGLNonVisualViewer.SetupCubeMapCamera(Sender: TObject);
9176

9177
const
9178
  cFaceMat: array[0..5] of TMatrix =
9179
  (
9180
    (X: (X:0; Y:0; Z:-1; W:0);
9181
     Y: (X:0; Y:-1; Z:0; W:0);
9182
     Z: (X:-1; Y:0; Z:0; W:0);
9183
     W: (X:0; Y:0; Z:0; W:1)),
9184
    (X:(X:2.4335928828e-08; Y:0; Z:1; W:0);
9185
     Y:(X:0; Y:-1; Z:0; W:0);
9186
     Z:(X:1; Y:0; Z:-2.4335928828e-08; W:0);
9187
     W:(X:0; Y:0; Z:0; W:1)),
9188
    (X:(X:1; Y:1.2167964414e-08; Z:-1.4805936071e-16; W:0);
9189
     Y:(X:0; Y:-1.2167964414e-08; Z:-1; W:0);
9190
     Z:(X:-1.2167964414e-08; Y:1; Z:-1.2167964414e-08; W:0);
9191
     W:(X:0; Y:0; Z:0; W:1)),
9192
    (X:(X:1; Y:-1.2167964414e-08; Z:-1.4805936071e-16; W:0);
9193
     Y:(X:0; Y:-1.2167964414e-08; Z:1; W:0);
9194
     Z:(X:-1.2167964414e-08; Y:-1; Z:-1.2167964414e-08; W:0);
9195
     W:(X:0; Y:0; Z:0; W:1)),
9196
    (X:(X:1; Y:0; Z:-1.2167964414e-08; W:0);
9197
     Y:(X:0; Y:-1; Z:0; W:0);
9198
     Z:(X:-1.2167964414e-08; Y:0; Z:-1; W:0);
9199
     W:(X:0; Y:0; Z:0; W:1)),
9200
    (X:(X:-1; Y:0; Z:-1.2167964414e-08; W:0);
9201
     Y:(X:0; Y:-1; Z:0; W:0);
9202
     Z:(X:-1.2167964414e-08; Y:0; Z:1; W:0);
9203
     W:(X:0; Y:0; Z:0; W:1))
9204
  );
9205

9206
var
9207
  TM: TMatrix;
9208
begin
9209
  // Setup appropriate FOV
9210
  with CurrentGLContext.PipelineTransformation do
9211
  begin
9212
    ProjectionMatrix := CreatePerspectiveMatrix(90, 1, FCubeMapZNear, FCubeMapZFar);
9213
    TM := CreateTranslationMatrix(FCubeMapTranslation);
9214
    ViewMatrix := MatrixMultiply(cFaceMat[FCubeMapRotIdx], TM);
9215
  end;
9216
end;
9217

9218
// RenderTextures
9219
//
9220

9221
procedure TGLNonVisualViewer.RenderCubeMapTextures(cubeMapTexture: TGLTexture;
9222
  zNear: Single = 0;
9223
  zFar: Single = 0);
9224
var
9225
  oldEvent: TNotifyEvent;
9226
begin
9227
  Assert((Width = Height), 'Memory Viewer must render to a square!');
9228
  Assert(Assigned(FBuffer.FCamera), 'Camera not specified');
9229
  Assert(Assigned(cubeMapTexture), 'Texture not specified');
9230

9231
  if zFar <= 0 then
9232
    zFar := FBuffer.FCamera.DepthOfView;
9233
  if zNear <= 0 then
9234
    zNear := zFar * 0.001;
9235

9236
  oldEvent := FBuffer.FCamera.FDeferredApply;
9237
  FBuffer.FCamera.FDeferredApply := SetupCubeMapCamera;
9238
  FCubeMapZNear := zNear;
9239
  FCubeMapZFar := zFar;
9240
  VectorScale(FBuffer.FCamera.AbsolutePosition, -1, FCubeMapTranslation);
9241
  try
9242
    FCubeMapRotIdx := 0;
9243
    while FCubeMapRotIdx < 6 do
9244
    begin
9245
      Render;
9246
      Buffer.CopyToTexture(cubeMapTexture, 0, 0, Width, Height, 0, 0,
9247
        GL_TEXTURE_CUBE_MAP_POSITIVE_X + FCubeMapRotIdx);
9248
      Inc(FCubeMapRotIdx);
9249
    end;
9250
  finally
9251
    FBuffer.FCamera.FDeferredApply := oldEvent;
9252
  end;
9253
end;
9254

9255
// SetBeforeRender
9256
//
9257

9258
procedure TGLNonVisualViewer.SetBeforeRender(const val: TNotifyEvent);
9259
begin
9260
  FBuffer.BeforeRender := val;
9261
end;
9262

9263
// GetBeforeRender
9264
//
9265

9266
function TGLNonVisualViewer.GetBeforeRender: TNotifyEvent;
9267
begin
9268
  Result := FBuffer.BeforeRender;
9269
end;
9270

9271
// SetPostRender
9272
//
9273

9274
procedure TGLNonVisualViewer.SetPostRender(const val: TNotifyEvent);
9275
begin
9276
  FBuffer.PostRender := val;
9277
end;
9278

9279
// GetPostRender
9280
//
9281

9282
function TGLNonVisualViewer.GetPostRender: TNotifyEvent;
9283
begin
9284
  Result := FBuffer.PostRender;
9285
end;
9286

9287
// SetAfterRender
9288
//
9289

9290
procedure TGLNonVisualViewer.SetAfterRender(const val: TNotifyEvent);
9291
begin
9292
  FBuffer.AfterRender := val;
9293
end;
9294

9295
// GetAfterRender
9296
//
9297

9298
function TGLNonVisualViewer.GetAfterRender: TNotifyEvent;
9299
begin
9300
  Result := FBuffer.AfterRender;
9301
end;
9302

9303
// SetCamera
9304
//
9305

9306
procedure TGLNonVisualViewer.SetCamera(const val: TGLCamera);
9307
begin
9308
  FBuffer.Camera := val;
9309
end;
9310

9311
// GetCamera
9312
//
9313

9314
function TGLNonVisualViewer.GetCamera: TGLCamera;
9315
begin
9316
  Result := FBuffer.Camera;
9317
end;
9318

9319
// SetBuffer
9320
//
9321

9322
procedure TGLNonVisualViewer.SetBuffer(const val: TGLSceneBuffer);
9323
begin
9324
  FBuffer.Assign(val);
9325
end;
9326

9327
// DoOnPrepareGLContext
9328
//
9329

9330
procedure TGLNonVisualViewer.DoOnPrepareGLContext(sender: TObject);
9331
begin
9332
  PrepareGLContext;
9333
end;
9334

9335
// PrepareGLContext
9336
//
9337

9338
procedure TGLNonVisualViewer.PrepareGLContext;
9339
begin
9340
  // nothing, reserved for subclasses
9341
end;
9342

9343
// DoBufferChange
9344
//
9345

9346
procedure TGLNonVisualViewer.DoBufferChange(Sender: TObject);
9347
begin
9348
  // nothing, reserved for subclasses
9349
end;
9350

9351
// DoBufferStructuralChange
9352
//
9353

9354
procedure TGLNonVisualViewer.DoBufferStructuralChange(Sender: TObject);
9355
begin
9356
  FBuffer.DestroyRC;
9357
end;
9358

9359
// SetWidth
9360
//
9361

9362
procedure TGLNonVisualViewer.SetWidth(const val: Integer);
9363
begin
9364
  if val <> FWidth then
9365
  begin
9366
    FWidth := val;
9367
    if FWidth < 1 then
9368
      FWidth := 1;
9369
    DoBufferStructuralChange(Self);
9370
  end;
9371
end;
9372

9373
// SetHeight
9374
//
9375

9376
procedure TGLNonVisualViewer.SetHeight(const val: Integer);
9377
begin
9378
  if val <> FHeight then
9379
  begin
9380
    FHeight := val;
9381
    if FHeight < 1 then
9382
      FHeight := 1;
9383
    DoBufferStructuralChange(Self);
9384
  end;
9385
end;
9386

9387
// ------------------
9388
// ------------------ TGLMemoryViewer ------------------
9389
// ------------------
9390

9391
// Create
9392
//
9393

9394
constructor TGLMemoryViewer.Create(AOwner: TComponent);
9395
begin
9396
  inherited Create(AOwner);
9397
  Width := 256;
9398
  Height := 256;
9399
  FBufferCount := 1;
9400
end;
9401

9402
// InstantiateRenderingContext
9403
//
9404

9405
procedure TGLMemoryViewer.InstantiateRenderingContext;
9406
begin
9407
  if FBuffer.RenderingContext = nil then
9408
  begin
9409
    FBuffer.SetViewPort(0, 0, Width, Height);
9410
    FBuffer.CreateRC(HWND(0), True, FBufferCount);
9411
  end;
9412
end;
9413

9414
// Render
9415
//
9416

9417
procedure TGLMemoryViewer.Render(baseObject: TGLBaseSceneObject = nil);
9418
begin
9419
  InstantiateRenderingContext;
9420
  FBuffer.Render(baseObject);
9421
end;
9422

9423
// SetBufferCount
9424
//
9425

9426
procedure TGLMemoryViewer.SetBufferCount(const Value: integer);
9427
//var
9428
//   MaxAxuBufCount : integer;
9429
const
9430
  MaxAxuBufCount = 4; // Current hardware limit = 4
9431
begin
9432
  if FBufferCount = Value then
9433
    exit;
9434
  FBufferCount := Value;
9435

9436
  if FBufferCount < 1 then
9437
    FBufferCount := 1;
9438

9439
  if FBufferCount > MaxAxuBufCount then
9440
    FBufferCount := MaxAxuBufCount;
9441

9442
  // Request a new Instantiation of RC on next render
9443
  FBuffer.DestroyRC;
9444
end;
9445

9446
// ------------------
9447
// ------------------ TGLInitializableObjectList ------------------
9448
// ------------------
9449

9450
// Add
9451
//
9452

9453
function TGLInitializableObjectList.Add(const Item: IGLInitializable): Integer;
9454
begin
9455
  Result := inherited Add(Pointer(Item));
9456
end;
9457

9458
// GetItems
9459
//
9460

9461
function TGLInitializableObjectList.GetItems(
9462
  const Index: Integer): IGLInitializable;
9463
begin
9464
  Result := IGLInitializable(inherited Get(Index));
9465
end;
9466

9467
// PutItems
9468
//
9469

9470
procedure TGLInitializableObjectList.PutItems(const Index: Integer;
9471
  const Value: IGLInitializable);
9472
begin
9473
  inherited Put(Index, Pointer(Value));
9474
end;
9475

9476
//------------------------------------------------------------------------------
9477
//------------------------------------------------------------------------------
9478
//------------------------------------------------------------------------------
9479
initialization
9480
  //------------------------------------------------------------------------------
9481
  //------------------------------------------------------------------------------
9482
  //------------------------------------------------------------------------------
9483

9484
  RegisterClasses([TGLLightSource, TGLCamera, TGLProxyObject,
9485
    TGLScene, TGLDirectOpenGL, TGLRenderPoint,
9486
      TGLMemoryViewer]);
9487

9488
  // preparation for high resolution timer
9489
  QueryPerformanceFrequency(vCounterFrequency);
9490

9491
end.
9492

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.