mirror of
				https://gitlab.com/freepascal.org/lazarus/lazarus.git
				synced 2025-11-04 02:40:00 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			465 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			ObjectPascal
		
	
	
	
	
	
			
		
		
	
	
			465 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			ObjectPascal
		
	
	
	
	
	
{
 | 
						|
 *****************************************************************************
 | 
						|
 *                                                                           *
 | 
						|
 *  See the file COPYING.modifiedLGPL, included in this distribution,        *
 | 
						|
 *  for details about the copyright.                                         *
 | 
						|
 *                                                                           *
 | 
						|
 *  This program is distributed in the hope that it will be useful,          *
 | 
						|
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of           *
 | 
						|
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.                     *
 | 
						|
 *                                                                           *
 | 
						|
 *****************************************************************************
 | 
						|
 | 
						|
  Author: Mattias Gaertner
 | 
						|
 | 
						|
  Abstract:
 | 
						|
    TOpenGLControl is a LCL control with an opengl context.
 | 
						|
    It works under the following platforms:
 | 
						|
      - gtk with glx    : full
 | 
						|
      - gtk2 with glx   : full
 | 
						|
      - carbon with agl : full
 | 
						|
      - windows with wgl: full
 | 
						|
}
 | 
						|
unit OpenGLContext;
 | 
						|
 | 
						|
{$mode objfpc}{$H+}
 | 
						|
 | 
						|
{$IFDEF LCLGTK}
 | 
						|
  {$DEFINE UseGtkGLX}
 | 
						|
  {$DEFINE OpenGLTargetDefined}
 | 
						|
{$ENDIF}
 | 
						|
{$IFDEF LCLGTK2}
 | 
						|
  {$DEFINE UseGtk2GLX}
 | 
						|
  {$DEFINE OpenGLTargetDefined}
 | 
						|
{$ENDIF}
 | 
						|
{$IFDEF LCLCarbon}
 | 
						|
  {$DEFINE UseCarbonAGL}
 | 
						|
  {$DEFINE OpenGLTargetDefined}
 | 
						|
{$ENDIF}
 | 
						|
{$IFDEF LCLWin32}
 | 
						|
  {$DEFINE UseWin32WGL}
 | 
						|
  {$DEFINE OpenGLTargetDefined}
 | 
						|
{$ENDIF}
 | 
						|
{$IFNDEF OpenGLTargetDefined}
 | 
						|
  {$ERROR this target is not yet supported}
 | 
						|
{$ENDIF}
 | 
						|
 | 
						|
interface
 | 
						|
 | 
						|
uses
 | 
						|
  Classes, SysUtils, LCLProc, Forms, Controls, LCLType, LCLIntf, LResources,
 | 
						|
  Graphics, LMessages, WSLCLClasses, WSControls,
 | 
						|
{$IFDEF UseGtkGLX}
 | 
						|
  GLGtkGlxContext;
 | 
						|
{$ENDIF}
 | 
						|
{$IFDEF UseGtk2GLX}
 | 
						|
  GLGtkGlxContext;
 | 
						|
{$ENDIF}
 | 
						|
{$IFDEF UseCarbonAGL}
 | 
						|
  GLCarbonAGLContext;
 | 
						|
{$ENDIF}
 | 
						|
{$IFDEF UseWin32WGL}
 | 
						|
  GLWin32WGLContext;
 | 
						|
{$ENDIF}
 | 
						|
 | 
						|
 | 
						|
type
 | 
						|
  TOpenGlCtrlMakeCurrentEvent = procedure(Sender: TObject;
 | 
						|
                                          var Allow: boolean) of object;
 | 
						|
 | 
						|
  { TCustomOpenGLControl }
 | 
						|
  { Sharing:
 | 
						|
    You can share opengl contexts. For example:
 | 
						|
    Assume OpenGLControl2 and OpenGLControl3 should share the same as
 | 
						|
    OpenGLControl1. Then set
 | 
						|
    
 | 
						|
        OpenGLControl2.SharedControl:=OpenGLControl1;
 | 
						|
        OpenGLControl3.SharedControl:=OpenGLControl1;
 | 
						|
 | 
						|
     After this OpenGLControl1.SharingControlCount will be two and
 | 
						|
     OpenGLControl1.SharingControls will contain OpenGLControl2 and
 | 
						|
     OpenGLControl3.
 | 
						|
    }
 | 
						|
 | 
						|
  TCustomOpenGLControl = class(TWinControl)
 | 
						|
  private
 | 
						|
    FAutoResizeViewport: boolean;
 | 
						|
    FCanvas: TCanvas; // only valid at designtime
 | 
						|
    FDoubleBuffered: boolean;
 | 
						|
    FFrameDiffTime: integer;
 | 
						|
    FOnMakeCurrent: TOpenGlCtrlMakeCurrentEvent;
 | 
						|
    FOnPaint: TNotifyEvent;
 | 
						|
    FCurrentFrameTime: integer; // in msec
 | 
						|
    FLastFrameTime: integer; // in msec
 | 
						|
    FRGBA: boolean;
 | 
						|
    FSharedOpenGLControl: TCustomOpenGLControl;
 | 
						|
    FSharingOpenGlControls: TList;
 | 
						|
    function GetSharingControls(Index: integer): TCustomOpenGLControl;
 | 
						|
    procedure SetAutoResizeViewport(const AValue: boolean);
 | 
						|
    procedure SetDoubleBuffered(const AValue: boolean);
 | 
						|
    procedure SetRGBA(const AValue: boolean);
 | 
						|
    procedure SetSharedControl(const AValue: TCustomOpenGLControl);
 | 
						|
  protected
 | 
						|
    procedure WMPaint(var Message: TLMPaint); message LM_PAINT;
 | 
						|
    procedure WMSize(var Message: TLMSize); message LM_SIZE;
 | 
						|
    procedure UpdateFrameTimeDiff;
 | 
						|
    procedure OpenGLAttributesChanged;
 | 
						|
    procedure EraseBackground(DC: HDC); override;
 | 
						|
  public
 | 
						|
    constructor Create(TheOwner: TComponent); override;
 | 
						|
    destructor Destroy; override;
 | 
						|
    Procedure Paint; virtual;
 | 
						|
    procedure RealizeBounds; override;
 | 
						|
    procedure DoOnPaint; virtual;
 | 
						|
    procedure SwapBuffers; virtual;
 | 
						|
    function MakeCurrent(SaveOldToStack: boolean = false): boolean; virtual;
 | 
						|
    function RestoreOldOpenGLControl: boolean;
 | 
						|
    function SharingControlCount: integer;
 | 
						|
    property SharingControls[Index: integer]: TCustomOpenGLControl read GetSharingControls;
 | 
						|
    procedure Invalidate; override;
 | 
						|
  public
 | 
						|
    property FrameDiffTimeInMSecs: integer read FFrameDiffTime;
 | 
						|
    property OnMakeCurrent: TOpenGlCtrlMakeCurrentEvent read FOnMakeCurrent
 | 
						|
                                                       write FOnMakeCurrent;
 | 
						|
    property OnPaint: TNotifyEvent read FOnPaint write FOnPaint;
 | 
						|
    property SharedControl: TCustomOpenGLControl read FSharedOpenGLControl
 | 
						|
                                                 write SetSharedControl;
 | 
						|
    property AutoResizeViewport: boolean read FAutoResizeViewport
 | 
						|
                                         write SetAutoResizeViewport default false;
 | 
						|
    property DoubleBuffered: boolean read FDoubleBuffered write SetDoubleBuffered default true;
 | 
						|
    property RGBA: boolean read FRGBA write SetRGBA default true;
 | 
						|
  end;
 | 
						|
 | 
						|
  { TOpenGLControl }
 | 
						|
 | 
						|
  TOpenGLControl = class(TCustomOpenGLControl)
 | 
						|
  published
 | 
						|
    property Align;
 | 
						|
    property Anchors;
 | 
						|
    property AutoResizeViewport;
 | 
						|
    property BorderSpacing;
 | 
						|
    property Enabled;
 | 
						|
    property OnChangeBounds;
 | 
						|
    property OnClick;
 | 
						|
    property OnConstrainedResize;
 | 
						|
    property OnDblClick;
 | 
						|
    property OnEnter;
 | 
						|
    property OnExit;
 | 
						|
    property OnKeyDown;
 | 
						|
    property OnKeyPress;
 | 
						|
    property OnKeyUp;
 | 
						|
    property OnMouseDown;
 | 
						|
    property OnMouseEnter;
 | 
						|
    property OnMouseLeave;
 | 
						|
    property OnMouseMove;
 | 
						|
    property OnMouseUp;
 | 
						|
    property OnMouseWheel;
 | 
						|
    property OnMouseWheelDown;
 | 
						|
    property OnMouseWheelUp;
 | 
						|
    property OnPaint;
 | 
						|
    property OnResize;
 | 
						|
    property OnShowHint;
 | 
						|
    property PopupMenu;
 | 
						|
    property ShowHint;
 | 
						|
    property Visible;
 | 
						|
  end;
 | 
						|
 | 
						|
  { TWSOpenGLControl }
 | 
						|
 | 
						|
  TWSOpenGLControl = class(TWidgetSetWSWinControl)
 | 
						|
  public
 | 
						|
    class function CreateHandle(const AWinControl: TWinControl;
 | 
						|
                                const AParams: TCreateParams): HWND; override;
 | 
						|
    class procedure DestroyHandle(const AWinControl: TWinControl); override;
 | 
						|
  end;
 | 
						|
 | 
						|
 | 
						|
 | 
						|
procedure Register;
 | 
						|
 | 
						|
 | 
						|
implementation
 | 
						|
 | 
						|
var
 | 
						|
  OpenGLControlStack: TList = nil;
 | 
						|
 | 
						|
procedure Register;
 | 
						|
begin
 | 
						|
  RegisterComponents('OpenGL',[TOpenGLControl]);
 | 
						|
end;
 | 
						|
 | 
						|
{ TCustomOpenGLControl }
 | 
						|
 | 
						|
function TCustomOpenGLControl.GetSharingControls(Index: integer
 | 
						|
  ): TCustomOpenGLControl;
 | 
						|
begin
 | 
						|
  Result:=TCustomOpenGLControl(FSharingOpenGlControls[Index]);
 | 
						|
end;
 | 
						|
 | 
						|
procedure TCustomOpenGLControl.SetAutoResizeViewport(const AValue: boolean);
 | 
						|
begin
 | 
						|
  if FAutoResizeViewport=AValue then exit;
 | 
						|
  FAutoResizeViewport:=AValue;
 | 
						|
  if AutoResizeViewport
 | 
						|
  and ([csLoading,csDestroying]*ComponentState=[])
 | 
						|
  and IsVisible and HandleAllocated
 | 
						|
  and MakeCurrent then
 | 
						|
    LOpenGLViewport(0,0,Width,Height);
 | 
						|
end;
 | 
						|
 | 
						|
procedure TCustomOpenGLControl.SetDoubleBuffered(const AValue: boolean);
 | 
						|
begin
 | 
						|
  if FDoubleBuffered=AValue then exit;
 | 
						|
  FDoubleBuffered:=AValue;
 | 
						|
  OpenGLAttributesChanged;
 | 
						|
end;
 | 
						|
 | 
						|
procedure TCustomOpenGLControl.SetRGBA(const AValue: boolean);
 | 
						|
begin
 | 
						|
  if FRGBA=AValue then exit;
 | 
						|
  FRGBA:=AValue;
 | 
						|
  OpenGLAttributesChanged;
 | 
						|
end;
 | 
						|
 | 
						|
procedure TCustomOpenGLControl.SetSharedControl(
 | 
						|
  const AValue: TCustomOpenGLControl);
 | 
						|
begin
 | 
						|
  if FSharedOpenGLControl=AValue then exit;
 | 
						|
  if AValue=Self then
 | 
						|
    Raise Exception.Create('A control can not be shared by itself.');
 | 
						|
  // unshare old
 | 
						|
  if (AValue<>nil) and (AValue.SharedControl<>nil) then
 | 
						|
    Raise Exception.Create('Target control is sharing too. A sharing control can not be shared.');
 | 
						|
  if FSharedOpenGLControl<>nil then
 | 
						|
    FSharedOpenGLControl.FSharingOpenGlControls.Remove(Self);
 | 
						|
  // share new
 | 
						|
  if (AValue<>nil) and (csDestroying in AValue.ComponentState) then
 | 
						|
    FSharedOpenGLControl:=nil
 | 
						|
  else begin
 | 
						|
    FSharedOpenGLControl:=AValue;
 | 
						|
    if (FSharedOpenGLControl<>nil) then begin
 | 
						|
      if FSharedOpenGLControl.FSharingOpenGlControls=nil then
 | 
						|
        FSharedOpenGLControl.FSharingOpenGlControls:=TList.Create;
 | 
						|
      FSharedOpenGLControl.FSharingOpenGlControls.Add(Self);
 | 
						|
    end;
 | 
						|
  end;
 | 
						|
  // recreate handle if needed
 | 
						|
  if HandleAllocated and (not (csDesigning in ComponentState)) then
 | 
						|
    ReCreateWnd(Self);
 | 
						|
end;
 | 
						|
 | 
						|
procedure TCustomOpenGLControl.WMPaint(var Message: TLMPaint);
 | 
						|
begin
 | 
						|
  Include(FControlState, csCustomPaint);
 | 
						|
  inherited WMPaint(Message);
 | 
						|
  //debugln('TCustomGTKGLAreaControl.WMPaint A ',dbgsName(Self),' ',dbgsName(FCanvas));
 | 
						|
  if (csDesigning in ComponentState) and (FCanvas<>nil) then begin
 | 
						|
    with FCanvas do begin
 | 
						|
      if Message.DC <> 0 then
 | 
						|
        Handle := Message.DC;
 | 
						|
      Brush.Color:=clLtGray;
 | 
						|
      Pen.Color:=clRed;
 | 
						|
      Rectangle(0,0,Self.Width-1,Self.Height-1);
 | 
						|
      MoveTo(0,0);
 | 
						|
      LineTo(Self.Width,Self.Height);
 | 
						|
      MoveTo(0,Self.Height);
 | 
						|
      LineTo(Self.Width,0);
 | 
						|
      if Message.DC <> 0 then
 | 
						|
        Handle := 0;
 | 
						|
    end;
 | 
						|
  end else begin
 | 
						|
    Paint;
 | 
						|
  end;
 | 
						|
  Exclude(FControlState, csCustomPaint);
 | 
						|
end;
 | 
						|
 | 
						|
procedure TCustomOpenGLControl.WMSize(var Message: TLMSize);
 | 
						|
begin
 | 
						|
  if (Message.SizeType and Size_SourceIsInterface)>0 then
 | 
						|
    DoOnResize;
 | 
						|
end;
 | 
						|
 | 
						|
procedure TCustomOpenGLControl.UpdateFrameTimeDiff;
 | 
						|
begin
 | 
						|
  FCurrentFrameTime:=integer(GetTickCount);
 | 
						|
  if FLastFrameTime=0 then
 | 
						|
    FLastFrameTime:=FCurrentFrameTime;
 | 
						|
  // calculate time since last call:
 | 
						|
  FFrameDiffTime:=FCurrentFrameTime-FLastFrameTime;
 | 
						|
  // if the counter is reset restart:
 | 
						|
  if (FFrameDiffTime<0) then FFrameDiffTime:=1;
 | 
						|
  FLastFrameTime:=FCurrentFrameTime;
 | 
						|
end;
 | 
						|
 | 
						|
procedure TCustomOpenGLControl.OpenGLAttributesChanged;
 | 
						|
begin
 | 
						|
  if HandleAllocated
 | 
						|
  and ([csLoading,csDesigning,csDestroying]*ComponentState=[]) then
 | 
						|
    RecreateWnd(Self);
 | 
						|
end;
 | 
						|
 | 
						|
procedure TCustomOpenGLControl.EraseBackground(DC: HDC);
 | 
						|
begin
 | 
						|
  if DC=0 then ;
 | 
						|
  // everything is painted, so erasing the background is not needed
 | 
						|
end;
 | 
						|
 | 
						|
constructor TCustomOpenGLControl.Create(TheOwner: TComponent);
 | 
						|
begin
 | 
						|
  inherited Create(TheOwner);
 | 
						|
  FDoubleBuffered:=true;
 | 
						|
  FRGBA:=true;
 | 
						|
  ControlStyle:=ControlStyle-[csSetCaption];
 | 
						|
  if (csDesigning in ComponentState) then begin
 | 
						|
    FCanvas := TControlCanvas.Create;
 | 
						|
    TControlCanvas(FCanvas).Control := Self;
 | 
						|
  end else
 | 
						|
    FCompStyle:=csNonLCL;
 | 
						|
  SetInitialBounds(0, 0, 160, 90);
 | 
						|
end;
 | 
						|
 | 
						|
destructor TCustomOpenGLControl.Destroy;
 | 
						|
begin
 | 
						|
  if FSharingOpenGlControls<>nil then begin
 | 
						|
    while SharingControlCount>0 do
 | 
						|
      SharingControls[SharingControlCount-1].SharedControl:=nil;
 | 
						|
    FreeAndNil(FSharingOpenGlControls);
 | 
						|
  end;
 | 
						|
  SharedControl:=nil;
 | 
						|
  if OpenGLControlStack<>nil then begin
 | 
						|
    OpenGLControlStack.Remove(Self);
 | 
						|
    if OpenGLControlStack.Count=0 then
 | 
						|
      FreeAndNil(OpenGLControlStack);
 | 
						|
  end;
 | 
						|
  FCanvas.Free;
 | 
						|
  FCanvas:=nil;
 | 
						|
  inherited Destroy;
 | 
						|
end;
 | 
						|
 | 
						|
procedure TCustomOpenGLControl.Paint;
 | 
						|
begin
 | 
						|
  if IsVisible and HandleAllocated then begin
 | 
						|
    UpdateFrameTimeDiff;
 | 
						|
    if ([csDesigning,csDestroying]*ComponentState=[]) then begin
 | 
						|
      if not MakeCurrent then exit;
 | 
						|
      if AutoResizeViewport then
 | 
						|
        LOpenGLViewport(0,0,Width,Height);
 | 
						|
    end;
 | 
						|
    DoOnPaint;
 | 
						|
  end;
 | 
						|
end;
 | 
						|
 | 
						|
procedure TCustomOpenGLControl.RealizeBounds;
 | 
						|
begin
 | 
						|
  if IsVisible and HandleAllocated
 | 
						|
  and ([csDesigning,csDestroying]*ComponentState=[]) and MakeCurrent then begin
 | 
						|
    if AutoResizeViewport then
 | 
						|
      LOpenGLViewport(0,0,Width,Height);
 | 
						|
  end;
 | 
						|
  inherited RealizeBounds;
 | 
						|
end;
 | 
						|
 | 
						|
procedure TCustomOpenGLControl.DoOnPaint;
 | 
						|
begin
 | 
						|
  if Assigned(OnPaint) then OnPaint(Self);
 | 
						|
end;
 | 
						|
 | 
						|
procedure TCustomOpenGLControl.SwapBuffers;
 | 
						|
begin
 | 
						|
  LOpenGLSwapBuffers(Handle);
 | 
						|
end;
 | 
						|
 | 
						|
function TCustomOpenGLControl.MakeCurrent(SaveOldToStack: boolean): boolean;
 | 
						|
var
 | 
						|
  Allowed: Boolean;
 | 
						|
begin
 | 
						|
  if csDesigning in ComponentState then exit(false);
 | 
						|
  if Assigned(FOnMakeCurrent) then begin
 | 
						|
    Allowed:=true;
 | 
						|
    OnMakeCurrent(Self,Allowed);
 | 
						|
    if not Allowed then begin
 | 
						|
      Result:=False;
 | 
						|
      exit;
 | 
						|
    end;
 | 
						|
  end;
 | 
						|
  // make current
 | 
						|
  Result:=LOpenGLMakeCurrent(Handle);
 | 
						|
  if Result and SaveOldToStack then begin
 | 
						|
    // on success push on stack
 | 
						|
    if OpenGLControlStack=nil then
 | 
						|
      OpenGLControlStack:=TList.Create;
 | 
						|
    OpenGLControlStack.Add(Self);
 | 
						|
  end;
 | 
						|
end;
 | 
						|
 | 
						|
function TCustomOpenGLControl.RestoreOldOpenGLControl: boolean;
 | 
						|
var
 | 
						|
  RestoredControl: TCustomOpenGLControl;
 | 
						|
begin
 | 
						|
  Result:=false;
 | 
						|
  // check if the current context is on stack
 | 
						|
  if (OpenGLControlStack=nil) or (OpenGLControlStack.Count=0) then exit;
 | 
						|
  // pop
 | 
						|
  OpenGLControlStack.Delete(OpenGLControlStack.Count-1);
 | 
						|
  // make old control the current control
 | 
						|
  if OpenGLControlStack.Count>0 then begin
 | 
						|
    RestoredControl:=
 | 
						|
      TCustomOpenGLControl(OpenGLControlStack[OpenGLControlStack.Count-1]);
 | 
						|
    if (not LOpenGLMakeCurrent(RestoredControl.Handle)) then
 | 
						|
      exit;
 | 
						|
  end else begin
 | 
						|
    FreeAndNil(OpenGLControlStack);
 | 
						|
  end;
 | 
						|
  Result:=true;
 | 
						|
end;
 | 
						|
 | 
						|
function TCustomOpenGLControl.SharingControlCount: integer;
 | 
						|
begin
 | 
						|
  if FSharingOpenGlControls=nil then
 | 
						|
    Result:=0
 | 
						|
  else
 | 
						|
    Result:=FSharingOpenGlControls.Count;
 | 
						|
end;
 | 
						|
 | 
						|
procedure TCustomOpenGLControl.Invalidate;
 | 
						|
begin
 | 
						|
  if csCustomPaint in FControlState then exit;
 | 
						|
  inherited Invalidate;
 | 
						|
end;
 | 
						|
 | 
						|
{ TWSOpenGLControl }
 | 
						|
 | 
						|
class function TWSOpenGLControl.CreateHandle(const AWinControl: TWinControl;
 | 
						|
  const AParams: TCreateParams): HWND;
 | 
						|
var
 | 
						|
  OpenGlControl: TCustomOpenGLControl;
 | 
						|
  AttrControl: TCustomOpenGLControl;
 | 
						|
begin
 | 
						|
  if csDesigning in AWinControl.ComponentState then
 | 
						|
    Result:=inherited CreateHandle(AWinControl,AParams)
 | 
						|
  else begin
 | 
						|
    OpenGlControl:=AWinControl as TCustomOpenGLControl;
 | 
						|
    if OpenGlControl.SharedControl<>nil then
 | 
						|
      AttrControl:=OpenGlControl.SharedControl
 | 
						|
    else
 | 
						|
      AttrControl:=OpenGlControl;
 | 
						|
    Result:=LOpenGLCreateContext(OpenGlControl,WSPrivate,
 | 
						|
                                 OpenGlControl.SharedControl,
 | 
						|
                                 AttrControl.DoubleBuffered,AttrControl.RGBA,
 | 
						|
                                 AParams);
 | 
						|
  end;
 | 
						|
end;
 | 
						|
 | 
						|
class procedure TWSOpenGLControl.DestroyHandle(const AWinControl: TWinControl);
 | 
						|
begin
 | 
						|
  LOpenGLDestroyContextInfo(AWinControl);
 | 
						|
  inherited DestroyHandle(AWinControl);
 | 
						|
end;
 | 
						|
 | 
						|
initialization
 | 
						|
  RegisterWSComponent(TCustomOpenGLControl,TWSOpenGLControl);
 | 
						|
  {$I openglcontext.lrs}
 | 
						|
 | 
						|
end.
 | 
						|
 |