// // This unit is part of the GLScene Engine, http://glscene.org // unit GLPipelineTransformation; (* Pipeline transformations *) interface {$I GLScene.inc} uses Winapi.OpenGL, OpenGLTokens, GLVectorGeometry, GLVectorTypes, GLS.Logger; const MAX_MATRIX_STACK_DEPTH = 128; type TGLPipelineTransformationState = ( trsModelViewChanged, trsInvModelViewChanged, trsInvModelChanged, trsNormalModelChanged, trsViewProjChanged, trsFrustum ); TGLPipelineTransformationStates = set of TGLPipelineTransformationState; const cAllStatesChanged = [trsModelViewChanged, trsInvModelViewChanged, trsInvModelChanged, trsViewProjChanged, trsNormalModelChanged, trsFrustum]; type PTransformationRec = ^TTransformationRec; TTransformationRec = record FStates: TGLPipelineTransformationStates; FModelMatrix: TMatrix; FViewMatrix: TMatrix; FProjectionMatrix: TMatrix; FInvModelMatrix: TMatrix; FNormalModelMatrix: TAffineMatrix; FModelViewMatrix: TMatrix; FInvModelViewMatrix: TMatrix; FViewProjectionMatrix: TMatrix; FFrustum: TFrustum; end; type TOnMatricesPush = procedure() of object; TGLTransformation = class(TObject) private FStackPos: Integer; FStack: array of TTransformationRec; FLoadMatricesEnabled: Boolean; FOnPush: TOnMatricesPush; function GetModelMatrix: PMatrix; inline; function GetViewMatrix: PMatrix; inline; function GetProjectionMatrix: PMatrix; inline; function GetModelViewMatrix: PMatrix; inline; function GetInvModelViewMatrix: PMatrix; inline; function GetInvModelMatrix: PMatrix; inline; function GetNormalModelMatrix: PAffineMatrix; inline; function GetViewProjectionMatrix: PMatrix; inline; function GetFrustum: TFrustum; inline; protected procedure LoadModelViewMatrix; inline; procedure LoadProjectionMatrix; inline; procedure DoMatricesLoaded; inline; property OnPush: TOnMatricesPush read FOnPush write FOnPush; public constructor Create; procedure SetModelMatrix(const AMatrix: TMatrix); inline; procedure SetViewMatrix(const AMatrix: TMatrix); inline; procedure SetProjectionMatrix(const AMatrix: TMatrix); inline; procedure IdentityAll; inline; procedure Push(AValue: PTransformationRec); overload; procedure Push(); overload; inline; procedure Pop; procedure ReplaceFromStack; function StackTop: TTransformationRec; inline; property ModelMatrix: PMatrix read GetModelMatrix; property ViewMatrix: PMatrix read GetViewMatrix; property ProjectionMatrix: PMatrix read GetProjectionMatrix; property InvModelMatrix: PMatrix read GetInvModelMatrix; property ModelViewMatrix: PMatrix read GetModelViewMatrix; property NormalModelMatrix: PAffineMatrix read GetNormalModelMatrix; property InvModelViewMatrix: PMatrix read GetInvModelViewMatrix; property ViewProjectionMatrix: PMatrix read GetViewProjectionMatrix; property Frustum: TFrustum read GetFrustum; property LoadMatricesEnabled: Boolean read FLoadMatricesEnabled write FLoadMatricesEnabled; end; //===================================================================== implementation //===================================================================== uses GLContext; constructor TGLTransformation.Create; begin FStackPos := 0; SetLength(FStack, MAX_MATRIX_STACK_DEPTH); IdentityAll; end; procedure TGLTransformation.LoadProjectionMatrix; begin gl.MatrixMode(GL_PROJECTION); gl.LoadMatrixf(PGLFloat(@FStack[FStackPos].FProjectionMatrix)); gl.MatrixMode(GL_MODELVIEW); end; function TGLTransformation.GetModelViewMatrix: PMatrix; begin if trsModelViewChanged in FStack[FStackPos].FStates then begin FStack[FStackPos].FModelViewMatrix := MatrixMultiply(FStack[FStackPos].FModelMatrix, FStack[FStackPos].FViewMatrix); Exclude(FStack[FStackPos].FStates, trsModelViewChanged); end; Result := @FStack[FStackPos].FModelViewMatrix; end; procedure TGLTransformation.LoadModelViewMatrix; begin gl.LoadMatrixf(PGLFloat(GetModelViewMatrix)); end; procedure TGLTransformation.IdentityAll; begin with FStack[FStackPos] do begin FModelMatrix := IdentityHmgMatrix; FViewMatrix := IdentityHmgMatrix; FProjectionMatrix := IdentityHmgMatrix; FStates := cAllStatesChanged; end; if LoadMatricesEnabled then begin LoadModelViewMatrix; LoadProjectionMatrix; end; end; procedure TGLTransformation.DoMatricesLoaded; begin if Assigned(FOnPush) then FOnPush(); end; procedure TGLTransformation.Push; var prevPos: Integer; begin prevPos := FStackPos; Inc(FStackPos); FStack[FStackPos] := FStack[prevPos]; end; procedure TGLTransformation.Push(AValue: PTransformationRec); var prevPos: Integer; begin {$IFDEF USE_LOGGING} if FStackPos > MAX_MATRIX_STACK_DEPTH then begin GLSLogger.LogWarningFmt('Transformation stack overflow, more then %d values', [MAX_MATRIX_STACK_DEPTH]); end; {$ENDIF} prevPos := FStackPos; Inc(FStackPos); if Assigned(AValue) then begin FStack[FStackPos] := AValue^; if LoadMatricesEnabled then begin LoadModelViewMatrix; LoadProjectionMatrix; end; DoMatricesLoaded; end else FStack[FStackPos] := FStack[prevPos]; end; procedure TGLTransformation.Pop; begin {$IFDEF USE_LOGGING} if FStackPos = 0 then begin GLSLogger.LogError('Transformation stack underflow'); exit; end; {$ENDIF} Dec(FStackPos); if LoadMatricesEnabled then begin LoadModelViewMatrix; LoadProjectionMatrix; end; end; procedure TGLTransformation.ReplaceFromStack; var prevPos: Integer; begin {$IFDEF USE_LOGGING} if FStackPos = 0 then begin GLSLogger.LogError('Transformation stack underflow'); exit; end; {$ENDIF} prevPos := FStackPos - 1; FStack[FStackPos].FModelMatrix := FStack[prevPos].FModelMatrix; FStack[FStackPos].FViewMatrix:= FStack[prevPos].FViewMatrix; FStack[FStackPos].FProjectionMatrix:= FStack[prevPos].FProjectionMatrix; FStack[FStackPos].FStates := FStack[prevPos].FStates; if LoadMatricesEnabled then begin LoadModelViewMatrix; LoadProjectionMatrix; end; end; function TGLTransformation.GetModelMatrix: PMatrix; begin Result := @FStack[FStackPos].FModelMatrix; end; function TGLTransformation.GetViewMatrix: PMatrix; begin Result := @FStack[FStackPos].FViewMatrix; end; function TGLTransformation.GetProjectionMatrix: PMatrix; begin Result := @FStack[FStackPos].FProjectionMatrix; end; procedure TGLTransformation.SetModelMatrix(const AMatrix: TMatrix); begin FStack[FStackPos].FModelMatrix := AMatrix; FStack[FStackPos].FStates := FStack[FStackPos].FStates + [trsModelViewChanged, trsInvModelViewChanged, trsInvModelChanged, trsNormalModelChanged]; if LoadMatricesEnabled then LoadModelViewMatrix; end; procedure TGLTransformation.SetViewMatrix(const AMatrix: TMatrix); begin FStack[FStackPos].FViewMatrix:= AMatrix; FStack[FStackPos].FStates := FStack[FStackPos].FStates + [trsModelViewChanged, trsInvModelViewChanged, trsViewProjChanged, trsFrustum]; if LoadMatricesEnabled then LoadModelViewMatrix; end; function TGLTransformation.StackTop: TTransformationRec; begin Result := FStack[FStackPos]; end; procedure TGLTransformation.SetProjectionMatrix(const AMatrix: TMatrix); begin FStack[FStackPos].FProjectionMatrix := AMatrix; FStack[FStackPos].FStates := FStack[FStackPos].FStates + [trsViewProjChanged, trsFrustum]; if LoadMatricesEnabled then LoadProjectionMatrix; end; function TGLTransformation.GetInvModelViewMatrix: PMatrix; begin if trsInvModelViewChanged in FStack[FStackPos].FStates then begin FStack[FStackPos].FInvModelViewMatrix := GetModelViewMatrix^; InvertMatrix(FStack[FStackPos].FInvModelViewMatrix); Exclude(FStack[FStackPos].FStates, trsInvModelViewChanged); end; Result := @FStack[FStackPos].FInvModelViewMatrix; end; function TGLTransformation.GetInvModelMatrix: PMatrix; begin if trsInvModelChanged in FStack[FStackPos].FStates then begin FStack[FStackPos].FInvModelMatrix := MatrixInvert(FStack[FStackPos].FModelMatrix); Exclude(FStack[FStackPos].FStates, trsInvModelChanged); end; Result := @FStack[FStackPos].FInvModelMatrix; end; function TGLTransformation.GetNormalModelMatrix: PAffineMatrix; var M: TMatrix; begin if trsNormalModelChanged in FStack[FStackPos].FStates then begin M := FStack[FStackPos].FModelMatrix; NormalizeMatrix(M); SetMatrix(FStack[FStackPos].FNormalModelMatrix, M); Exclude(FStack[FStackPos].FStates, trsNormalModelChanged); end; Result := @FStack[FStackPos].FNormalModelMatrix; end; function TGLTransformation.GetViewProjectionMatrix: PMatrix; begin if trsViewProjChanged in FStack[FStackPos].FStates then begin FStack[FStackPos].FViewProjectionMatrix := MatrixMultiply(FStack[FStackPos].FViewMatrix, FStack[FStackPos].FProjectionMatrix); Exclude(FStack[FStackPos].FStates, trsViewProjChanged); end; Result := @FStack[FStackPos].FViewProjectionMatrix; end; function TGLTransformation.GetFrustum: TFrustum; begin if trsFrustum in FStack[FStackPos].FStates then begin FStack[FStackPos].FFrustum := ExtractFrustumFromModelViewProjection(GetViewProjectionMatrix^); Exclude(FStack[FStackPos].FStates, trsFrustum); end; Result := FStack[FStackPos].FFrustum; end; end.