1643 lines
46 KiB
ObjectPascal
1643 lines
46 KiB
ObjectPascal
// SPDX-License-Identifier: LGPL-3.0-linking-exception
|
|||
|
|
unit BGRAScene3D;
|
||
|
|
|
||
|
|
{$mode objfpc}{$H+}
|
||
|
|
|
||
|
|
interface
|
||
|
|
|
||
|
|
uses
|
||
|
|
BGRAClasses, SysUtils, BGRABitmapTypes, BGRAColorInt,
|
||
|
|
BGRASSE, BGRAMatrix3D,
|
||
|
|
BGRASceneTypes, BGRARenderer3D;
|
||
|
|
|
||
|
|
type
|
||
|
|
TProjection3D = BGRAMatrix3D.TProjection3D;
|
||
|
|
TLightingNormal3D = BGRASceneTypes.TLightingNormal3D;
|
||
|
|
TLightingInterpolation3D = BGRASceneTypes.TLightingInterpolation3D;
|
||
|
|
TAntialiasingMode3D = BGRASceneTypes.TAntialiasingMode3D;
|
||
|
|
TPerspectiveMode3D = BGRASceneTypes.TPerspectiveMode3D;
|
||
|
|
TRenderingOptions = BGRASceneTypes.TRenderingOptions;
|
||
|
|
|
||
|
|
IBGRAVertex3D = BGRASceneTypes.IBGRAVertex3D;
|
||
|
|
IBGRANormal3D = BGRASceneTypes.IBGRANormal3D;
|
||
|
|
IBGRALight3D = BGRASceneTypes.IBGRALight3D;
|
||
|
|
IBGRADirectionalLight3D = BGRASceneTypes.IBGRADirectionalLight3D;
|
||
|
|
IBGRAPointLight3D = BGRASceneTypes.IBGRAPointLight3D;
|
||
|
|
IBGRAMaterial3D = BGRASceneTypes.IBGRAMaterial3D;
|
||
|
|
IBGRAFace3D = BGRASceneTypes.IBGRAFace3D;
|
||
|
|
IBGRAPart3D = BGRASceneTypes.IBGRAPart3D;
|
||
|
|
IBGRAObject3D = BGRASceneTypes.IBGRAObject3D;
|
||
|
|
|
||
|
|
arrayOfIBGRAVertex3D = BGRASceneTypes.arrayOfIBGRAVertex3D;
|
||
|
|
|
||
|
|
const
|
||
|
|
lnNone = BGRASceneTypes.lnNone;
|
||
|
|
lnFace = BGRASceneTypes.lnFace;
|
||
|
|
lnVertex = BGRASceneTypes.lnVertex;
|
||
|
|
lnFaceVertexMix = BGRASceneTypes.lnFaceVertexMix;
|
||
|
|
|
||
|
|
liLowQuality = BGRASceneTypes.liLowQuality;
|
||
|
|
liSpecularHighQuality = BGRASceneTypes.liSpecularHighQuality;
|
||
|
|
liAlwaysHighQuality = BGRASceneTypes.liAlwaysHighQuality;
|
||
|
|
|
||
|
|
am3dNone = BGRASceneTypes.am3dNone;
|
||
|
|
am3dMultishape = BGRASceneTypes.am3dMultishape;
|
||
|
|
am3dResample = BGRASceneTypes.am3dResample;
|
||
|
|
|
||
|
|
pmLinearMapping = BGRASceneTypes.pmLinearMapping;
|
||
|
|
pmPerspectiveMapping = BGRASceneTypes.pmPerspectiveMapping;
|
||
|
|
pmZBuffer = BGRASceneTypes.pmZBuffer;
|
||
|
|
|
||
|
|
type
|
||
|
|
|
||
|
|
{ TCamera3D }
|
||
|
|
|
||
|
|
TCamera3D = class
|
||
|
|
private
|
||
|
|
procedure ComputeMatrix;
|
||
|
|
function GetLookWhere: TPoint3D;
|
||
|
|
function GetMatrix: TMatrix3D;
|
||
|
|
function GetViewPoint: TPoint3D;
|
||
|
|
procedure SetMatrix(AValue: TMatrix3D);
|
||
|
|
procedure SetViewPoint(AValue: TPoint3D);
|
||
|
|
protected
|
||
|
|
FMatrix: TMatrix3D;
|
||
|
|
FMatrixComputed: boolean;
|
||
|
|
FViewPoint: TPoint3D_128;
|
||
|
|
FLookWhere, FTopDir: TPoint3D_128;
|
||
|
|
public
|
||
|
|
procedure LookAt(AWhere: TPoint3D; ATopDir: TPoint3D);
|
||
|
|
procedure LookDown(angleDeg: single);
|
||
|
|
procedure LookLeft(angleDeg: single);
|
||
|
|
procedure LookRight(angleDeg: single);
|
||
|
|
procedure LookUp(angleDeg: single);
|
||
|
|
property ViewPoint: TPoint3D read GetViewPoint write SetViewPoint;
|
||
|
|
property LookWhere: TPoint3D read GetLookWhere;
|
||
|
|
property Matrix: TMatrix3D read GetMatrix write SetMatrix;
|
||
|
|
end;
|
||
|
|
|
||
|
|
{ TBGRAScene3D }
|
||
|
|
|
||
|
|
TBGRAScene3D = class
|
||
|
|
private
|
||
|
|
FSurface: TBGRACustomBitmap; //destination of software renderer
|
||
|
|
FViewCenter: TPointF; //where origin is drawn
|
||
|
|
FAutoViewCenter: boolean; //use middle of the screen
|
||
|
|
FZoom: TPointF; //how much the drawing is zoomed
|
||
|
|
FAutoZoom: Boolean; //display 1 as 80% of surface size
|
||
|
|
FProjection: TProjection3D; //current projection
|
||
|
|
FRenderedFaceCount: integer; //current counter of rendered faces
|
||
|
|
|
||
|
|
FCamera: TCamera3D;
|
||
|
|
|
||
|
|
FObjects: array of IBGRAObject3D;
|
||
|
|
FObjectCount: integer;
|
||
|
|
FMaterials: array of IBGRAMaterial3D;
|
||
|
|
FMaterialCount: integer;
|
||
|
|
FDefaultMaterial : IBGRAMaterial3D;
|
||
|
|
|
||
|
|
FAmbiantLightColorF: TColorF; //lightness without light sources
|
||
|
|
FLights: TList; //individual light sources
|
||
|
|
|
||
|
|
function GetAmbiantLightColorF: TColorF;
|
||
|
|
function GetAmbiantLightness: single;
|
||
|
|
function GetAmbiantLightColor: TBGRAPixel;
|
||
|
|
function GetFaceCount: integer;
|
||
|
|
function GetLight(AIndex: integer): IBGRALight3D;
|
||
|
|
function GetLightCount: integer;
|
||
|
|
function GetMaterial(AIndex: integer): IBGRAMaterial3D;
|
||
|
|
function GetNormalCount: integer;
|
||
|
|
function GetObject(AIndex: integer): IBGRAObject3D;
|
||
|
|
function GetVertexCount: integer;
|
||
|
|
function GetViewCenter: TPointF;
|
||
|
|
function GetViewPoint: TPoint3D;
|
||
|
|
function GetZoom: TPointF;
|
||
|
|
procedure SetAmbiantLightColorF(const AValue: TColorF);
|
||
|
|
procedure SetAmbiantLightness(const AValue: single);
|
||
|
|
procedure SetAmbiantLightColor(const AValue: TBGRAPixel);
|
||
|
|
procedure SetAutoViewCenter(const AValue: boolean);
|
||
|
|
procedure SetAutoZoom(const AValue: boolean);
|
||
|
|
procedure SetViewCenter(const AValue: TPointF);
|
||
|
|
procedure SetViewPoint(const AValue: TPoint3D);
|
||
|
|
procedure ComputeView(ScaleX,ScaleY: single);
|
||
|
|
function ComputeCoordinate(AViewCoord: TPoint3D_128): TPointF;
|
||
|
|
procedure AddObject(AObj: IBGRAObject3D);
|
||
|
|
procedure AddLight(ALight: TObject);
|
||
|
|
procedure AddMaterial(AMaterial: IBGRAMaterial3D);
|
||
|
|
procedure Init;
|
||
|
|
|
||
|
|
protected
|
||
|
|
FRenderer: TCustomRenderer3D;
|
||
|
|
FMaterialLibrariesFetched: array of string;
|
||
|
|
FTexturesFetched: array of record
|
||
|
|
Name: string;
|
||
|
|
Bitmap: TBGRACustomBitmap;
|
||
|
|
end;
|
||
|
|
procedure UseMaterial(AMaterialName: string; AFace: IBGRAFace3D); virtual;
|
||
|
|
function LoadBitmapFromFileUTF8(AFilenameUTF8: string): TBGRACustomBitmap; virtual;
|
||
|
|
function FetchTexture(AName: string; out texSize: TPointF): IBGRAScanner; virtual;
|
||
|
|
procedure HandleFetchException(AException: Exception); virtual;
|
||
|
|
procedure DoRender; virtual;
|
||
|
|
procedure DoClear; virtual;
|
||
|
|
function GetRenderWidth: integer;
|
||
|
|
function GetRenderHeight: integer;
|
||
|
|
procedure OnMaterialTextureChanged({%H-}ASender: TObject); virtual;
|
||
|
|
procedure SetDefaultMaterial(AValue: IBGRAMaterial3D);
|
||
|
|
procedure InvalidateMaterial;
|
||
|
|
|
||
|
|
public
|
||
|
|
DefaultLightingNormal: TLightingNormal3D;
|
||
|
|
RenderingOptions: TRenderingOptions;
|
||
|
|
UnknownColor: TBGRAPixel;
|
||
|
|
FetchDirectory: string;
|
||
|
|
FetchThrowsException: boolean;
|
||
|
|
|
||
|
|
constructor Create; overload;
|
||
|
|
constructor Create(ASurface: TBGRACustomBitmap); overload;
|
||
|
|
destructor Destroy; override;
|
||
|
|
procedure Clear; virtual;
|
||
|
|
function FetchObject(AName: string; SwapFacesOrientation: boolean = true): IBGRAObject3D;
|
||
|
|
procedure FetchMaterials(ALibraryName: string); virtual;
|
||
|
|
function LoadObjectFromFile(AFilename: string; SwapFacesOrientation: boolean = true): IBGRAObject3D;
|
||
|
|
function LoadObjectFromFileUTF8(AFilename: string; SwapFacesOrientation: boolean = true): IBGRAObject3D;
|
||
|
|
function LoadObjectFromStream(AStream: TStream; SwapFacesOrientation: boolean = true): IBGRAObject3D;
|
||
|
|
procedure LoadMaterialsFromFile(AFilename: string);
|
||
|
|
procedure LoadMaterialsFromFileUTF8(AFilename: string);
|
||
|
|
procedure LoadMaterialsFromStream(AStream: TStream);
|
||
|
|
procedure LookAt(AWhere: TPoint3D; ATopDir: TPoint3D);
|
||
|
|
procedure LookLeft(angleDeg: single);
|
||
|
|
procedure LookRight(angleDeg: single);
|
||
|
|
procedure LookUp(angleDeg: single);
|
||
|
|
procedure LookDown(angleDeg: single);
|
||
|
|
procedure Render; overload; virtual;
|
||
|
|
procedure Render(ARenderer: TCustomRenderer3D); overload;
|
||
|
|
function CreateObject: IBGRAObject3D; overload;
|
||
|
|
function CreateObject(ATexture: IBGRAScanner): IBGRAObject3D; overload;
|
||
|
|
function CreateObject(AColor: TBGRAPixel): IBGRAObject3D; overload;
|
||
|
|
function CreateSphere(ARadius: Single; AHorizPrecision: integer = 8; AVerticalPrecision : integer = 6): IBGRAObject3D; overload;
|
||
|
|
function CreateSphere(ARadius: Single; AColor: TBGRAPixel; AHorizPrecision: integer = 8; AVerticalPrecision : integer = 6): IBGRAObject3D; overload;
|
||
|
|
function CreateHalfSphere(ARadius: Single; AHorizPrecision: integer = 6; AVerticalPrecision : integer = 6): IBGRAObject3D; overload;
|
||
|
|
function CreateHalfSphere(ARadius: Single; AColor: TBGRAPixel; AHorizPrecision: integer = 6; AVerticalPrecision : integer = 6): IBGRAObject3D; overload;
|
||
|
|
procedure RemoveObject(AObject: IBGRAObject3D);
|
||
|
|
function AddDirectionalLight(ADirection: TPoint3D; ALightness: single = 1; AMinIntensity : single = 0): IBGRADirectionalLight3D; overload;
|
||
|
|
function AddDirectionalLight(ADirection: TPoint3D; AColor: TBGRAPixel; AMinIntensity: single = 0): IBGRADirectionalLight3D; overload;
|
||
|
|
function AddPointLight(AVertex: IBGRAVertex3D; AOptimalDistance: single; ALightness: single = 1; AMinIntensity : single = 0): IBGRAPointLight3D; overload;
|
||
|
|
function AddPointLight(AVertex: IBGRAVertex3D; AOptimalDistance: single; AColor: TBGRAPixel; AMinIntensity: single = 0): IBGRAPointLight3D; overload;
|
||
|
|
procedure RemoveLight(ALight: IBGRALight3D);
|
||
|
|
procedure SetZoom(value: Single); overload;
|
||
|
|
procedure SetZoom(value: TPointF); overload;
|
||
|
|
function CreateMaterial: IBGRAMaterial3D; overload;
|
||
|
|
function CreateMaterial(ASpecularIndex: integer): IBGRAMaterial3D; overload;
|
||
|
|
function GetMaterialByName(AName: string): IBGRAMaterial3D;
|
||
|
|
procedure UpdateMaterials; virtual;
|
||
|
|
procedure UpdateMaterial(AMaterialName: string); virtual;
|
||
|
|
procedure ForEachVertex(ACallback: TVertex3DCallback);
|
||
|
|
procedure ForEachFace(ACallback: TFace3DCallback);
|
||
|
|
function MakeLightList: TList;
|
||
|
|
|
||
|
|
property ViewCenter: TPointF read GetViewCenter write SetViewCenter;
|
||
|
|
property AutoViewCenter: boolean read FAutoViewCenter write SetAutoViewCenter;
|
||
|
|
property AutoZoom: boolean read FAutoZoom write SetAutoZoom;
|
||
|
|
property Surface: TBGRACustomBitmap read FSurface write FSurface;
|
||
|
|
property Object3D[AIndex: integer]: IBGRAObject3D read GetObject;
|
||
|
|
property Object3DCount: integer read FObjectCount;
|
||
|
|
property VertexCount: integer read GetVertexCount;
|
||
|
|
property NormalCount: integer read GetNormalCount;
|
||
|
|
property FaceCount: integer read GetFaceCount;
|
||
|
|
property Zoom: TPointF read GetZoom write SetZoom;
|
||
|
|
property AmbiantLightness: single read GetAmbiantLightness write SetAmbiantLightness;
|
||
|
|
property AmbiantLightColor: TBGRAPixel read GetAmbiantLightColor write SetAmbiantLightColor;
|
||
|
|
property AmbiantLightColorF: TColorF read GetAmbiantLightColorF write SetAmbiantLightColorF;
|
||
|
|
property LightCount: integer read GetLightCount;
|
||
|
|
property Light[AIndex: integer]: IBGRALight3D read GetLight;
|
||
|
|
property ViewPoint: TPoint3D read GetViewPoint write SetViewPoint;
|
||
|
|
property RenderedFaceCount : integer read FRenderedFaceCount;
|
||
|
|
property Material[AIndex: integer] : IBGRAMaterial3D read GetMaterial;
|
||
|
|
property MaterialCount: integer read FMaterialCount;
|
||
|
|
property Camera: TCamera3D read FCamera;
|
||
|
|
property DefaultMaterial: IBGRAMaterial3D read FDefaultMaterial write SetDefaultMaterial;
|
||
|
|
end;
|
||
|
|
|
||
|
|
implementation
|
||
|
|
|
||
|
|
uses BGRACoordPool3D, BGRAUTF8;
|
||
|
|
|
||
|
|
{$i lightingclasses3d.inc}
|
||
|
|
{$i vertex3d.inc}
|
||
|
|
{$i face3d.inc}
|
||
|
|
{$i part3d.inc}
|
||
|
|
{$i object3d.inc}
|
||
|
|
{$i shapes3d.inc}
|
||
|
|
|
||
|
|
{ TCamera3D }
|
||
|
|
|
||
|
|
function TCamera3D.GetLookWhere: TPoint3D;
|
||
|
|
begin
|
||
|
|
result := Point3D(FLookWhere);
|
||
|
|
end;
|
||
|
|
|
||
|
|
function TCamera3D.GetMatrix: TMatrix3D;
|
||
|
|
begin
|
||
|
|
if not FMatrixComputed then
|
||
|
|
begin
|
||
|
|
ComputeMatrix;
|
||
|
|
FMatrixComputed := true;
|
||
|
|
end;
|
||
|
|
result := FMatrix;
|
||
|
|
end;
|
||
|
|
|
||
|
|
function TCamera3D.GetViewPoint: TPoint3D;
|
||
|
|
begin
|
||
|
|
result := Point3D(FViewPoint);
|
||
|
|
end;
|
||
|
|
|
||
|
|
procedure TCamera3D.SetMatrix(AValue: TMatrix3D);
|
||
|
|
begin
|
||
|
|
FMatrix := AValue;
|
||
|
|
FMatrixComputed:= true;
|
||
|
|
FViewPoint := Point3D_128(FMatrix[1,4],FMatrix[2,4],FMatrix[3,4]);
|
||
|
|
end;
|
||
|
|
|
||
|
|
procedure TCamera3D.SetViewPoint(AValue: TPoint3D);
|
||
|
|
begin
|
||
|
|
FViewPoint := Point3D_128(AValue);
|
||
|
|
FMatrix[1,4] := FViewPoint.x;
|
||
|
|
FMatrix[2,4] := FViewPoint.y;
|
||
|
|
FMatrix[3,4] := FViewPoint.z;
|
||
|
|
FMatrixComputed := false;
|
||
|
|
end;
|
||
|
|
|
||
|
|
procedure TCamera3D.ComputeMatrix;
|
||
|
|
var ZDir, XDir, YDir: TPoint3D_128;
|
||
|
|
begin
|
||
|
|
if IsPoint3D_128_Zero(FTopDir) then exit;
|
||
|
|
YDir := -FTopDir;
|
||
|
|
Normalize3D_128(YDir);
|
||
|
|
|
||
|
|
ZDir := FLookWhere-FViewPoint;
|
||
|
|
if IsPoint3D_128_Zero(ZDir) then exit;
|
||
|
|
Normalize3D_128(ZDir);
|
||
|
|
|
||
|
|
VectProduct3D_128(YDir,ZDir,XDir);
|
||
|
|
VectProduct3D_128(ZDir,XDir,YDir); //correct Y dir
|
||
|
|
Normalize3D_128(XDir);
|
||
|
|
Normalize3D_128(YDir);
|
||
|
|
|
||
|
|
FMatrix := Matrix3D(XDir,YDir,ZDir,FViewPoint);
|
||
|
|
FMatrix := MatrixInverse3D(FMatrix);
|
||
|
|
end;
|
||
|
|
|
||
|
|
procedure TCamera3D.LookAt(AWhere: TPoint3D; ATopDir: TPoint3D);
|
||
|
|
begin
|
||
|
|
FLookWhere := Point3D_128(AWhere);
|
||
|
|
FTopDir := Point3D_128(ATopDir);
|
||
|
|
FMatrixComputed := false;
|
||
|
|
end;
|
||
|
|
|
||
|
|
procedure TCamera3D.LookLeft(angleDeg: single);
|
||
|
|
var m,inv: TMatrix3D;
|
||
|
|
begin
|
||
|
|
inv := MatrixInverse3D(Matrix);
|
||
|
|
m := MatrixRotateY(angleDeg*Pi/180);
|
||
|
|
FLookWhere := inv*m*Matrix*FLookWhere;
|
||
|
|
FMatrixComputed := false;
|
||
|
|
end;
|
||
|
|
|
||
|
|
procedure TCamera3D.LookRight(angleDeg: single);
|
||
|
|
begin
|
||
|
|
LookLeft(-angleDeg);
|
||
|
|
end;
|
||
|
|
|
||
|
|
procedure TCamera3D.LookUp(angleDeg: single);
|
||
|
|
var m,inv: TMatrix3D;
|
||
|
|
begin
|
||
|
|
inv := MatrixInverse3D(Matrix);
|
||
|
|
m := MatrixRotateX(-angleDeg*Pi/180);
|
||
|
|
FLookWhere := inv*m*Matrix*FLookWhere;
|
||
|
|
FMatrixComputed := false;
|
||
|
|
end;
|
||
|
|
|
||
|
|
procedure TCamera3D.LookDown(angleDeg: single);
|
||
|
|
begin
|
||
|
|
LookUp(-angleDeg);
|
||
|
|
end;
|
||
|
|
|
||
|
|
|
||
|
|
{ TBGRAScene3D }
|
||
|
|
|
||
|
|
function TBGRAScene3D.GetViewCenter: TPointF;
|
||
|
|
begin
|
||
|
|
if FAutoViewCenter then
|
||
|
|
begin
|
||
|
|
result := PointF((GetRenderWidth-1)/2,(GetRenderHeight-1)/2)
|
||
|
|
end
|
||
|
|
else
|
||
|
|
result := FViewCenter;
|
||
|
|
end;
|
||
|
|
|
||
|
|
function TBGRAScene3D.GetViewPoint: TPoint3D;
|
||
|
|
begin
|
||
|
|
result := Camera.ViewPoint;
|
||
|
|
end;
|
||
|
|
|
||
|
|
function TBGRAScene3D.GetZoom: TPointF;
|
||
|
|
var size: single;
|
||
|
|
begin
|
||
|
|
if FAutoZoom then
|
||
|
|
begin
|
||
|
|
Size := sqrt(GetRenderWidth*GetRenderHeight)*0.8;
|
||
|
|
if Size = 0 then
|
||
|
|
result := PointF(1,1)
|
||
|
|
else
|
||
|
|
result := PointF(size,size);
|
||
|
|
end else
|
||
|
|
result := FZoom;
|
||
|
|
end;
|
||
|
|
|
||
|
|
procedure TBGRAScene3D.SetAmbiantLightColorF(const AValue: TColorF);
|
||
|
|
begin
|
||
|
|
FAmbiantLightColorF := AValue;
|
||
|
|
end;
|
||
|
|
|
||
|
|
procedure TBGRAScene3D.SetAmbiantLightness(const AValue: single);
|
||
|
|
begin
|
||
|
|
FAmbiantLightColorF := ColorF(AValue, AValue, AValue, 1);
|
||
|
|
end;
|
||
|
|
|
||
|
|
procedure TBGRAScene3D.SetAmbiantLightColor(const AValue: TBGRAPixel);
|
||
|
|
begin
|
||
|
|
FAmbiantLightColorF := ColorInt65536ToColorF(BGRAToColorInt(AValue,True));
|
||
|
|
end;
|
||
|
|
|
||
|
|
function TBGRAScene3D.GetObject(AIndex: integer): IBGRAObject3D;
|
||
|
|
begin
|
||
|
|
if (AIndex < 0) or (AIndex >= FObjectCount) then
|
||
|
|
raise exception.Create('Index out of bounds');
|
||
|
|
result := FObjects[AIndex];
|
||
|
|
end;
|
||
|
|
|
||
|
|
function TBGRAScene3D.GetVertexCount: integer;
|
||
|
|
var i: integer;
|
||
|
|
begin
|
||
|
|
result := 0;
|
||
|
|
for i := 0 to Object3DCount-1 do
|
||
|
|
inc(result, Object3D[i].TotalVertexCount);
|
||
|
|
end;
|
||
|
|
|
||
|
|
function TBGRAScene3D.GetAmbiantLightColor: TBGRAPixel;
|
||
|
|
begin
|
||
|
|
result := ColorIntToBGRA(ColorFToColorInt65536(FAmbiantLightColorF),True);
|
||
|
|
end;
|
||
|
|
|
||
|
|
function TBGRAScene3D.GetFaceCount: integer;
|
||
|
|
var i: integer;
|
||
|
|
begin
|
||
|
|
result := 0;
|
||
|
|
for i := 0 to Object3DCount-1 do
|
||
|
|
inc(result, Object3D[i].FaceCount);
|
||
|
|
end;
|
||
|
|
|
||
|
|
function TBGRAScene3D.GetLight(AIndex: integer): IBGRALight3D;
|
||
|
|
begin
|
||
|
|
if (AIndex < 0) or (AIndex >= FLights.Count) then
|
||
|
|
result := nil
|
||
|
|
else
|
||
|
|
result := TBGRALight3D(FLights[AIndex]);
|
||
|
|
end;
|
||
|
|
|
||
|
|
function TBGRAScene3D.GetLightCount: integer;
|
||
|
|
begin
|
||
|
|
result := FLights.Count;
|
||
|
|
end;
|
||
|
|
|
||
|
|
function TBGRAScene3D.GetMaterial(AIndex: integer): IBGRAMaterial3D;
|
||
|
|
begin
|
||
|
|
if (AIndex < 0) or (AIndex >= FMaterialCount) then
|
||
|
|
raise exception.Create('Index out of bounds');
|
||
|
|
result := FMaterials[AIndex];
|
||
|
|
end;
|
||
|
|
|
||
|
|
function TBGRAScene3D.GetNormalCount: integer;
|
||
|
|
var i: integer;
|
||
|
|
begin
|
||
|
|
result := 0;
|
||
|
|
for i := 0 to Object3DCount-1 do
|
||
|
|
inc(result, Object3D[i].TotalNormalCount);
|
||
|
|
end;
|
||
|
|
|
||
|
|
function TBGRAScene3D.GetAmbiantLightness: single;
|
||
|
|
begin
|
||
|
|
result := (FAmbiantLightColorF[1]+FAmbiantLightColorF[2]+FAmbiantLightColorF[3])/3;
|
||
|
|
end;
|
||
|
|
|
||
|
|
function TBGRAScene3D.GetAmbiantLightColorF: TColorF;
|
||
|
|
begin
|
||
|
|
result := FAmbiantLightColorF;
|
||
|
|
end;
|
||
|
|
|
||
|
|
procedure TBGRAScene3D.SetAutoViewCenter(const AValue: boolean);
|
||
|
|
begin
|
||
|
|
if FAutoViewCenter=AValue then exit;
|
||
|
|
if not AValue then
|
||
|
|
FViewCenter := ViewCenter;
|
||
|
|
FAutoViewCenter:=AValue;
|
||
|
|
end;
|
||
|
|
|
||
|
|
procedure TBGRAScene3D.SetAutoZoom(const AValue: boolean);
|
||
|
|
begin
|
||
|
|
if FAutoZoom=AValue then exit;
|
||
|
|
if not AValue then
|
||
|
|
FZoom := Zoom;
|
||
|
|
FAutoZoom:=AValue;
|
||
|
|
end;
|
||
|
|
|
||
|
|
procedure TBGRAScene3D.SetDefaultMaterial(AValue: IBGRAMaterial3D);
|
||
|
|
begin
|
||
|
|
if FDefaultMaterial=AValue then Exit;
|
||
|
|
FDefaultMaterial:=AValue;
|
||
|
|
InvalidateMaterial;
|
||
|
|
end;
|
||
|
|
|
||
|
|
procedure TBGRAScene3D.SetViewCenter(const AValue: TPointF);
|
||
|
|
begin
|
||
|
|
FViewCenter := AValue;
|
||
|
|
FAutoViewCenter:= False;
|
||
|
|
end;
|
||
|
|
|
||
|
|
procedure TBGRAScene3D.SetViewPoint(const AValue: TPoint3D);
|
||
|
|
begin
|
||
|
|
Camera.ViewPoint := AValue;
|
||
|
|
end;
|
||
|
|
|
||
|
|
procedure TBGRAScene3D.AddObject(AObj: IBGRAObject3D);
|
||
|
|
begin
|
||
|
|
if FObjectCount = length(FObjects) then
|
||
|
|
setlength(FObjects, FObjectCount*2+1);
|
||
|
|
FObjects[FObjectCount] := AObj;
|
||
|
|
inc(FObjectCount);
|
||
|
|
end;
|
||
|
|
|
||
|
|
procedure TBGRAScene3D.AddLight(ALight: TObject);
|
||
|
|
begin
|
||
|
|
FLights.Add(ALight);
|
||
|
|
IBGRALight3D(TBGRALight3D(ALight))._AddRef;
|
||
|
|
end;
|
||
|
|
|
||
|
|
procedure TBGRAScene3D.AddMaterial(AMaterial: IBGRAMaterial3D);
|
||
|
|
begin
|
||
|
|
if FMaterialCount = length(FMaterials) then
|
||
|
|
setlength(FMaterials, FMaterialCount*2+1);
|
||
|
|
FMaterials[FMaterialCount] := AMaterial;
|
||
|
|
inc(FMaterialCount);
|
||
|
|
end;
|
||
|
|
|
||
|
|
procedure TBGRAScene3D.Init;
|
||
|
|
begin
|
||
|
|
UnknownColor := BGRA(0,128,255);
|
||
|
|
FAutoZoom := True;
|
||
|
|
FAutoViewCenter := True;
|
||
|
|
|
||
|
|
FCamera := TCamera3D.Create;
|
||
|
|
Camera.ViewPoint := Point3D(0,0,-100);
|
||
|
|
Camera.LookAt(Point3D(0,0,0), Point3D(0,-1,0));
|
||
|
|
with RenderingOptions do
|
||
|
|
begin
|
||
|
|
TextureInterpolation := False;
|
||
|
|
PerspectiveMode := pmPerspectiveMapping;
|
||
|
|
LightingInterpolation := liSpecularHighQuality;
|
||
|
|
AntialiasingMode := am3dNone;
|
||
|
|
AntialiasingResampleLevel := 2;
|
||
|
|
end;
|
||
|
|
AmbiantLightness := 1;
|
||
|
|
AmbiantLightColor := BGRAWhite;
|
||
|
|
DefaultLightingNormal := lnFaceVertexMix;
|
||
|
|
FLights := TList.Create;
|
||
|
|
FRenderedFaceCount:= 0;
|
||
|
|
FMaterialCount := 0;
|
||
|
|
FObjectCount := 0;
|
||
|
|
DefaultMaterial := CreateMaterial;
|
||
|
|
RenderingOptions.MinZ := 1;
|
||
|
|
end;
|
||
|
|
|
||
|
|
constructor TBGRAScene3D.Create;
|
||
|
|
begin
|
||
|
|
Init;
|
||
|
|
end;
|
||
|
|
|
||
|
|
constructor TBGRAScene3D.Create(ASurface: TBGRACustomBitmap);
|
||
|
|
begin
|
||
|
|
FSurface := ASurface;
|
||
|
|
Init;
|
||
|
|
end;
|
||
|
|
|
||
|
|
destructor TBGRAScene3D.Destroy;
|
||
|
|
var
|
||
|
|
i: Integer;
|
||
|
|
begin
|
||
|
|
DoClear;
|
||
|
|
FreeAndNil(FLights);
|
||
|
|
FreeAndNil(FCamera);
|
||
|
|
for i := 0 to high(FTexturesFetched) do
|
||
|
|
FTexturesFetched[i].Bitmap.Free;
|
||
|
|
inherited Destroy;
|
||
|
|
end;
|
||
|
|
|
||
|
|
procedure TBGRAScene3D.Clear;
|
||
|
|
begin
|
||
|
|
DoClear;
|
||
|
|
DefaultMaterial := CreateMaterial;
|
||
|
|
end;
|
||
|
|
|
||
|
|
function TBGRAScene3D.FetchObject(AName: string; SwapFacesOrientation: boolean
|
||
|
|
): IBGRAObject3D;
|
||
|
|
begin
|
||
|
|
if FetchDirectory = '' then raise exception.Create('Please define first the FetchDirectory');
|
||
|
|
try
|
||
|
|
result := LoadObjectFromFileUTF8(ConcatPaths([FetchDirectory,AName]), SwapFacesOrientation);
|
||
|
|
except
|
||
|
|
on ex:Exception do
|
||
|
|
HandleFetchException(ex);
|
||
|
|
end;
|
||
|
|
end;
|
||
|
|
|
||
|
|
procedure TBGRAScene3D.UseMaterial(AMaterialName: string; AFace: IBGRAFace3D);
|
||
|
|
|
||
|
|
function ParseColor(text: string): TBGRAPixel;
|
||
|
|
var
|
||
|
|
color,tempColor: TBGRAPixel;
|
||
|
|
begin
|
||
|
|
color := UnknownColor;
|
||
|
|
|
||
|
|
if copy(text,1,2) = 'dk' then
|
||
|
|
begin
|
||
|
|
tempcolor := ParseColor(copy(text,3,length(text)-2));
|
||
|
|
tempcolor := MergeBGRA(tempcolor,3,BGRABlack,1);
|
||
|
|
color := StrToBGRA('dark'+copy(text,3,length(text)-2),tempcolor);
|
||
|
|
end;
|
||
|
|
if copy(text,1,2) = 'lt' then
|
||
|
|
begin
|
||
|
|
tempcolor := ParseColor(copy(text,3,length(text)-2));
|
||
|
|
tempcolor := MergeBGRA(tempcolor,3,BGRAWhite,1);
|
||
|
|
color := StrToBGRA('light'+copy(text,3,length(text)-2),tempcolor);
|
||
|
|
end;
|
||
|
|
Color := StrToBGRA(StringReplace(text,'deep','dark',[]),Color);
|
||
|
|
Color := StrToBGRA(StringReplace(text,'dark','deep',[]),Color);
|
||
|
|
Color := StrToBGRA(text,Color);
|
||
|
|
result := color;
|
||
|
|
end;
|
||
|
|
|
||
|
|
var
|
||
|
|
mat: IBGRAMaterial3D;
|
||
|
|
c: TBGRAPixel;
|
||
|
|
begin
|
||
|
|
mat := GetMaterialByName(AMaterialName);
|
||
|
|
if mat = nil then
|
||
|
|
begin
|
||
|
|
mat := CreateMaterial;
|
||
|
|
mat.Name := AMaterialName;
|
||
|
|
c := ParseColor(AMaterialName);
|
||
|
|
mat.AmbiantColor := c;
|
||
|
|
mat.DiffuseColor := c;
|
||
|
|
end;
|
||
|
|
AFace.Material := mat;
|
||
|
|
end;
|
||
|
|
|
||
|
|
function TBGRAScene3D.LoadBitmapFromFileUTF8(AFilenameUTF8: string): TBGRACustomBitmap;
|
||
|
|
begin
|
||
|
|
result := BGRABitmapFactory.Create(AfileNameUTF8,True);
|
||
|
|
end;
|
||
|
|
|
||
|
|
function TBGRAScene3D.FetchTexture(AName: string; out texSize: TPointF): IBGRAScanner;
|
||
|
|
var
|
||
|
|
i: Integer;
|
||
|
|
bmp: TBGRACustomBitmap;
|
||
|
|
begin
|
||
|
|
bmp := nil;
|
||
|
|
for i := 0 to high(FTexturesFetched) do
|
||
|
|
if FTexturesFetched[i].Name = AName then
|
||
|
|
begin
|
||
|
|
bmp := FTexturesFetched[i].Bitmap;
|
||
|
|
result := bmp;
|
||
|
|
texSize := PointF(bmp.Width,bmp.Height);
|
||
|
|
exit;
|
||
|
|
end;
|
||
|
|
if FetchDirectory <> '' then
|
||
|
|
begin
|
||
|
|
try
|
||
|
|
bmp := LoadBitmapFromFileUTF8(ConcatPaths([FetchDirectory,AName]));
|
||
|
|
except
|
||
|
|
on ex:Exception do
|
||
|
|
HandleFetchException(ex);
|
||
|
|
end;
|
||
|
|
end;
|
||
|
|
if bmp = nil then
|
||
|
|
begin
|
||
|
|
result := nil;
|
||
|
|
texSize := PointF(1,1);
|
||
|
|
end else
|
||
|
|
begin
|
||
|
|
setlength(FTexturesFetched, length(FTexturesFetched)+1);
|
||
|
|
FTexturesFetched[high(FTexturesFetched)].Name := AName;
|
||
|
|
FTexturesFetched[high(FTexturesFetched)].Bitmap := bmp;
|
||
|
|
result := bmp;
|
||
|
|
texSize := PointF(bmp.Width,bmp.Height);
|
||
|
|
end;
|
||
|
|
end;
|
||
|
|
|
||
|
|
procedure TBGRAScene3D.FetchMaterials(ALibraryName: string);
|
||
|
|
var
|
||
|
|
i: Integer;
|
||
|
|
begin
|
||
|
|
if FetchDirectory <> '' then
|
||
|
|
begin
|
||
|
|
for i := 0 to high(FMaterialLibrariesFetched) do
|
||
|
|
if FMaterialLibrariesFetched[i]=ALibraryName then exit;
|
||
|
|
setlength(FMaterialLibrariesFetched,length(FMaterialLibrariesFetched)+1);
|
||
|
|
FMaterialLibrariesFetched[high(FMaterialLibrariesFetched)] := ALibraryName;
|
||
|
|
try
|
||
|
|
LoadMaterialsFromFile(ConcatPaths([FetchDirectory,ALibraryName]));
|
||
|
|
except
|
||
|
|
on ex:Exception do
|
||
|
|
HandleFetchException(ex);
|
||
|
|
end;
|
||
|
|
end;
|
||
|
|
end;
|
||
|
|
|
||
|
|
procedure TBGRAScene3D.HandleFetchException(AException: Exception);
|
||
|
|
begin
|
||
|
|
if FetchThrowsException then
|
||
|
|
raise AException;
|
||
|
|
end;
|
||
|
|
|
||
|
|
procedure TBGRAScene3D.DoClear;
|
||
|
|
var i: integer;
|
||
|
|
begin
|
||
|
|
for i := 0 to FLights.Count-1 do
|
||
|
|
TBGRALight3D(FLights[i]).ReleaseInterface;
|
||
|
|
FLights.Clear;
|
||
|
|
|
||
|
|
for i := 0 to FObjectCount-1 do
|
||
|
|
begin
|
||
|
|
FObjects[i].Clear;
|
||
|
|
FObjects[i] := nil;
|
||
|
|
end;
|
||
|
|
FObjects := nil;
|
||
|
|
FObjectCount := 0;
|
||
|
|
|
||
|
|
FMaterials := nil;
|
||
|
|
FMaterialCount := 0;
|
||
|
|
DefaultMaterial := nil;
|
||
|
|
end;
|
||
|
|
|
||
|
|
function TBGRAScene3D.GetRenderWidth: integer;
|
||
|
|
begin
|
||
|
|
if Assigned(FRenderer) then
|
||
|
|
result := FRenderer.SurfaceWidth
|
||
|
|
else
|
||
|
|
if Assigned(FSurface) then
|
||
|
|
result := FSurface.Width
|
||
|
|
else
|
||
|
|
result := 0;
|
||
|
|
end;
|
||
|
|
|
||
|
|
function TBGRAScene3D.GetRenderHeight: integer;
|
||
|
|
begin
|
||
|
|
if Assigned(FRenderer) then
|
||
|
|
result := FRenderer.SurfaceHeight
|
||
|
|
else
|
||
|
|
if Assigned(FSurface) then
|
||
|
|
result := FSurface.Height
|
||
|
|
else
|
||
|
|
result := 0;
|
||
|
|
end;
|
||
|
|
|
||
|
|
procedure TBGRAScene3D.OnMaterialTextureChanged(ASender: TObject);
|
||
|
|
begin
|
||
|
|
InvalidateMaterial;
|
||
|
|
end;
|
||
|
|
|
||
|
|
procedure TBGRAScene3D.InvalidateMaterial;
|
||
|
|
var
|
||
|
|
i: Integer;
|
||
|
|
begin
|
||
|
|
for i := 0 to FObjectCount-1 do
|
||
|
|
FObjects[i].InvalidateMaterial;
|
||
|
|
end;
|
||
|
|
|
||
|
|
function TBGRAScene3D.LoadObjectFromFile(AFilename: string; SwapFacesOrientation: boolean): IBGRAObject3D;
|
||
|
|
begin
|
||
|
|
result := LoadObjectFromFileUTF8(SysToUTF8(AFilename), SwapFacesOrientation);
|
||
|
|
end;
|
||
|
|
|
||
|
|
function TBGRAScene3D.LoadObjectFromFileUTF8(AFilename: string;
|
||
|
|
SwapFacesOrientation: boolean): IBGRAObject3D;
|
||
|
|
var source: TFileStreamUTF8;
|
||
|
|
begin
|
||
|
|
source := TFileStreamUTF8.Create(AFilename,fmOpenRead,fmShareDenyWrite);
|
||
|
|
try
|
||
|
|
result := LoadObjectFromStream(source,SwapFacesOrientation);
|
||
|
|
finally
|
||
|
|
source.free;
|
||
|
|
end;
|
||
|
|
end;
|
||
|
|
|
||
|
|
function TBGRAScene3D.LoadObjectFromStream(AStream: TStream;
|
||
|
|
SwapFacesOrientation: boolean): IBGRAObject3D;
|
||
|
|
var s: string;
|
||
|
|
secondValue,thirdValue: string;
|
||
|
|
|
||
|
|
function GetNextToken: string;
|
||
|
|
var idxStart,idxEnd,idxSlash: integer;
|
||
|
|
begin
|
||
|
|
idxStart := 1;
|
||
|
|
while (idxStart <= length(s)) and (s[idxStart]in[' ',#9]) do inc(idxStart);
|
||
|
|
if idxStart > length(s) then
|
||
|
|
begin
|
||
|
|
result := '';
|
||
|
|
exit;
|
||
|
|
end;
|
||
|
|
idxEnd := idxStart;
|
||
|
|
while (idxEnd < length(s)) and not (s[idxEnd+1]in[' ',#9]) do inc(idxEnd);
|
||
|
|
result := copy(s,idxStart, idxEnd-idxStart+1);
|
||
|
|
delete(s,1,idxEnd);
|
||
|
|
idxSlash := pos('/',result);
|
||
|
|
if idxSlash <> 0 then
|
||
|
|
begin
|
||
|
|
secondValue:= copy(result,idxSlash+1,length(result)-idxSlash);
|
||
|
|
result := copy(result,1,idxSlash-1);
|
||
|
|
idxSlash:= pos('/',secondValue);
|
||
|
|
if idxSlash <> 0 then
|
||
|
|
begin
|
||
|
|
thirdValue:= copy(secondValue,idxSlash+1,length(secondValue)-idxSlash);
|
||
|
|
secondValue:= copy(secondValue,1,idxSlash-1);
|
||
|
|
end else
|
||
|
|
thirdValue:= '';
|
||
|
|
end else
|
||
|
|
begin
|
||
|
|
secondValue:= '';
|
||
|
|
thirdValue:= '';
|
||
|
|
end;
|
||
|
|
end;
|
||
|
|
|
||
|
|
type
|
||
|
|
TFaceVertexExtra = record
|
||
|
|
normal: IBGRANormal3D;
|
||
|
|
texCoord: TPointF;
|
||
|
|
end;
|
||
|
|
|
||
|
|
var lineType : string;
|
||
|
|
x,y,z : single;
|
||
|
|
code : integer;
|
||
|
|
faceVertices: array of IBGRAVertex3D;
|
||
|
|
faceExtra: array of TFaceVertexExtra;
|
||
|
|
NbFaceVertices,v,v2,v3,i: integer;
|
||
|
|
tempV: IBGRAVertex3D;
|
||
|
|
tempN: TFaceVertexExtra;
|
||
|
|
materialname: string;
|
||
|
|
face: IBGRAFace3D;
|
||
|
|
lines: TStringList;
|
||
|
|
lineIndex: integer;
|
||
|
|
texCoords: array of TPointF;
|
||
|
|
nbTexCoords: integer;
|
||
|
|
|
||
|
|
begin
|
||
|
|
lines := TStringList.Create;
|
||
|
|
lines.LoadFromStream(AStream);
|
||
|
|
result := CreateObject;
|
||
|
|
faceVertices := nil;
|
||
|
|
faceExtra := nil;
|
||
|
|
NbFaceVertices:= 0;
|
||
|
|
materialname := 'default';
|
||
|
|
lineIndex := 0;
|
||
|
|
texCoords := nil;
|
||
|
|
nbTexCoords:= 0;
|
||
|
|
while lineIndex < lines.Count do
|
||
|
|
begin
|
||
|
|
s := lines[lineIndex];
|
||
|
|
if pos('#',s) <> 0 then
|
||
|
|
s := copy(s,1,pos('#',s)-1);
|
||
|
|
inc(lineIndex);
|
||
|
|
lineType := GetNextToken;
|
||
|
|
if lineType = 'v' then
|
||
|
|
begin
|
||
|
|
val(GetNextToken,x,code);
|
||
|
|
val(GetNextToken,y,code);
|
||
|
|
val(GetNextToken,z,code);
|
||
|
|
result.MainPart.Add(x,y,z);
|
||
|
|
end else
|
||
|
|
if lineType = 'vt' then
|
||
|
|
begin
|
||
|
|
val(GetNextToken,x,code);
|
||
|
|
val(GetNextToken,y,code);
|
||
|
|
if nbTexCoords >= length(texCoords) then
|
||
|
|
setlength(texCoords, length(texCoords)*2+1);
|
||
|
|
texCoords[nbTexCoords] := PointF(x,y);
|
||
|
|
inc(nbTexCoords);
|
||
|
|
end else
|
||
|
|
if lineType = 'vn' then
|
||
|
|
begin
|
||
|
|
val(GetNextToken,x,code);
|
||
|
|
val(GetNextToken,y,code);
|
||
|
|
val(GetNextToken,z,code);
|
||
|
|
result.MainPart.AddNormal(x,y,z);
|
||
|
|
result.LightingNormal := lnVertex;
|
||
|
|
end else
|
||
|
|
if lineType = 'mtllib' then
|
||
|
|
FetchMaterials(trim(s))
|
||
|
|
else
|
||
|
|
if lineType = 'usemtl' then
|
||
|
|
materialname := trim(s)
|
||
|
|
else
|
||
|
|
if lineType = 'f' then
|
||
|
|
begin
|
||
|
|
NbFaceVertices:= 0;
|
||
|
|
repeat
|
||
|
|
val(GetNextToken,v,code);
|
||
|
|
if (code = 0) and (v < 0) then v := result.MainPart.VertexCount+1+v;
|
||
|
|
if (code = 0) and (v >= 1) and (v <= result.MainPart.VertexCount) then
|
||
|
|
begin
|
||
|
|
if length(faceVertices) = NbFaceVertices then
|
||
|
|
begin
|
||
|
|
setlength(faceVertices, length(faceVertices)*2+1);
|
||
|
|
setlength(faceExtra, length(faceExtra)*2+1);
|
||
|
|
end;
|
||
|
|
faceVertices[NbFaceVertices] := result.MainPart.Vertex[v-1];
|
||
|
|
val(secondValue,v2,code);
|
||
|
|
if (code = 0) and (v2 < 0) then v2 := nbTexCoords+1+v2;
|
||
|
|
if (code = 0) and (v2 >= 1) and (v2-1 < nbTexCoords) then
|
||
|
|
faceExtra[NbFaceVertices].texCoord := texCoords[v2-1]
|
||
|
|
else if nbTexCoords > v-1 then
|
||
|
|
faceExtra[NbFaceVertices].texCoord := texCoords[v-1]
|
||
|
|
else
|
||
|
|
faceExtra[NbFaceVertices].texCoord := PointF(0,0);
|
||
|
|
val(thirdValue,v3,code);
|
||
|
|
if (code = 0) and (v3 < 0) then v3 := result.MainPart.NormalCount+1+v3;
|
||
|
|
if code = 0 then
|
||
|
|
faceExtra[NbFaceVertices].normal := result.MainPart.Normal[v3-1]
|
||
|
|
else if result.MainPart.NormalCount > v-1 then
|
||
|
|
faceExtra[NbFaceVertices].normal := result.MainPart.Normal[v-1]
|
||
|
|
else
|
||
|
|
faceExtra[NbFaceVertices].normal := nil;
|
||
|
|
inc(NbFaceVertices);
|
||
|
|
end else break;
|
||
|
|
until false;
|
||
|
|
if NbFaceVertices > 2 then
|
||
|
|
begin
|
||
|
|
if SwapFacesOrientation then
|
||
|
|
for i := 0 to NbFaceVertices div 2-1 do
|
||
|
|
begin
|
||
|
|
tempV := faceVertices[i];
|
||
|
|
faceVertices[i] := faceVertices[NbFaceVertices-1-i];
|
||
|
|
faceVertices[NbFaceVertices-1-i] := tempV;
|
||
|
|
tempN := faceExtra[i];
|
||
|
|
faceExtra[i] := faceExtra[NbFaceVertices-1-i];
|
||
|
|
faceExtra[NbFaceVertices-1-i] := tempN;
|
||
|
|
end;
|
||
|
|
face := result.AddFace(slice(faceVertices,NbFaceVertices));
|
||
|
|
for i := 0 to NbFaceVertices-1 do
|
||
|
|
begin
|
||
|
|
face.SetNormal(i, faceExtra[i].normal);
|
||
|
|
face.SetTexCoord(i, faceExtra[i].texCoord);
|
||
|
|
end;
|
||
|
|
face.MaterialName := materialname;
|
||
|
|
end;
|
||
|
|
end;
|
||
|
|
end;
|
||
|
|
lines.Free;
|
||
|
|
end;
|
||
|
|
|
||
|
|
procedure TBGRAScene3D.LoadMaterialsFromFile(AFilename: string);
|
||
|
|
var source: TFileStreamUTF8;
|
||
|
|
begin
|
||
|
|
source := TFileStreamUTF8.Create(SysToUTF8(AFilename),fmOpenRead,fmShareDenyWrite);
|
||
|
|
try
|
||
|
|
LoadMaterialsFromStream(source);
|
||
|
|
finally
|
||
|
|
source.free;
|
||
|
|
end;
|
||
|
|
end;
|
||
|
|
|
||
|
|
procedure TBGRAScene3D.LoadMaterialsFromFileUTF8(AFilename: string);
|
||
|
|
var source: TFileStreamUTF8;
|
||
|
|
begin
|
||
|
|
source := TFileStreamUTF8.Create(AFilename,fmOpenRead,fmShareDenyWrite);
|
||
|
|
try
|
||
|
|
LoadMaterialsFromStream(source);
|
||
|
|
finally
|
||
|
|
source.free;
|
||
|
|
end;
|
||
|
|
end;
|
||
|
|
|
||
|
|
procedure TBGRAScene3D.LoadMaterialsFromStream(AStream: TStream);
|
||
|
|
var
|
||
|
|
s: String;
|
||
|
|
|
||
|
|
function GetNextToken: string;
|
||
|
|
var idxStart,idxEnd: integer;
|
||
|
|
begin
|
||
|
|
idxStart := 1;
|
||
|
|
while (idxStart <= length(s)) and (s[idxStart]in[#9,' ']) do inc(idxStart);
|
||
|
|
if idxStart > length(s) then
|
||
|
|
begin
|
||
|
|
result := '';
|
||
|
|
exit;
|
||
|
|
end;
|
||
|
|
idxEnd := idxStart;
|
||
|
|
while (idxEnd < length(s)) and not (s[idxEnd+1]in[#9,' ']) do inc(idxEnd);
|
||
|
|
result := copy(s,idxStart, idxEnd-idxStart+1);
|
||
|
|
delete(s,1,idxEnd);
|
||
|
|
end;
|
||
|
|
|
||
|
|
function GetSingle: single;
|
||
|
|
var {%H-}code: integer;
|
||
|
|
begin
|
||
|
|
val(GetNextToken,result,{%H-}code);
|
||
|
|
end;
|
||
|
|
|
||
|
|
function GetColorF: TColorF;
|
||
|
|
var r,g,b: single;
|
||
|
|
{%H-}code: integer;
|
||
|
|
begin
|
||
|
|
val(GetNextToken,r,{%H-}code);
|
||
|
|
val(GetNextToken,g,{%H-}code);
|
||
|
|
val(GetNextToken,b,{%H-}code);
|
||
|
|
result := ColorF(r,g,b,1);
|
||
|
|
end;
|
||
|
|
|
||
|
|
var
|
||
|
|
lines: TStringList;
|
||
|
|
lineIndex: integer;
|
||
|
|
lineType: String;
|
||
|
|
currentMaterial: IBGRAMaterial3D;
|
||
|
|
materialName: string;
|
||
|
|
texZoom: TPointF;
|
||
|
|
v: single;
|
||
|
|
|
||
|
|
begin
|
||
|
|
lines := TStringList.Create;
|
||
|
|
lines.LoadFromStream(AStream);
|
||
|
|
lineIndex := 0;
|
||
|
|
while lineIndex < lines.Count do
|
||
|
|
begin
|
||
|
|
s := lines[lineIndex];
|
||
|
|
if pos('#',s) <> 0 then
|
||
|
|
s := copy(s,1,pos('#',s)-1);
|
||
|
|
inc(lineIndex);
|
||
|
|
lineType := GetNextToken;
|
||
|
|
if lineType = 'newmtl' then
|
||
|
|
begin
|
||
|
|
materialName := trim(s);
|
||
|
|
currentMaterial := GetMaterialByName(materialName);
|
||
|
|
if currentMaterial = nil then
|
||
|
|
begin
|
||
|
|
currentMaterial := CreateMaterial;
|
||
|
|
currentMaterial.Name := materialName;
|
||
|
|
end;
|
||
|
|
end else
|
||
|
|
if currentMaterial <> nil then
|
||
|
|
begin
|
||
|
|
if lineType = 'Ka' then currentMaterial.AmbiantColorF := GetColorF else
|
||
|
|
if lineType = 'Kd' then currentMaterial.DiffuseColorF := GetColorF else
|
||
|
|
if lineType = 'Ks' then currentMaterial.SpecularColorF := GetColorF else
|
||
|
|
if (lineType = 'map_Ka') or (lineType = 'map_Kd') then
|
||
|
|
begin
|
||
|
|
currentMaterial.Texture := FetchTexture(trim(s),texZoom);
|
||
|
|
texZoom.y := -texZoom.y;
|
||
|
|
currentMaterial.TextureZoom := texZoom;
|
||
|
|
end else
|
||
|
|
if lineType = 'Ns' then currentMaterial.SpecularIndex := round(GetSingle) else
|
||
|
|
if lineType = 'd' then
|
||
|
|
begin
|
||
|
|
v := GetSingle;
|
||
|
|
if v > 1 then
|
||
|
|
currentMaterial.SimpleAlpha := 255
|
||
|
|
else if v < 0 then
|
||
|
|
currentMaterial.SimpleAlpha := 0
|
||
|
|
else
|
||
|
|
currentMaterial.SimpleAlpha := round(v*255);
|
||
|
|
end;
|
||
|
|
end;
|
||
|
|
end;
|
||
|
|
lines.Free;
|
||
|
|
end;
|
||
|
|
|
||
|
|
procedure TBGRAScene3D.LookAt(AWhere: TPoint3D; ATopDir: TPoint3D);
|
||
|
|
begin
|
||
|
|
Camera.LookAt(AWhere,ATopDir);
|
||
|
|
end;
|
||
|
|
|
||
|
|
procedure TBGRAScene3D.LookLeft(angleDeg: single);
|
||
|
|
begin
|
||
|
|
Camera.LookLeft(angleDeg);
|
||
|
|
end;
|
||
|
|
|
||
|
|
procedure TBGRAScene3D.LookRight(angleDeg: single);
|
||
|
|
begin
|
||
|
|
Camera.LookRight(angleDeg);
|
||
|
|
end;
|
||
|
|
|
||
|
|
procedure TBGRAScene3D.LookUp(angleDeg: single);
|
||
|
|
begin
|
||
|
|
Camera.LookUp(angleDeg);
|
||
|
|
end;
|
||
|
|
|
||
|
|
procedure TBGRAScene3D.LookDown(angleDeg: single);
|
||
|
|
begin
|
||
|
|
Camera.LookDown(angleDeg);
|
||
|
|
end;
|
||
|
|
|
||
|
|
procedure TBGRAScene3D.Render;
|
||
|
|
begin
|
||
|
|
FRenderer := TBGRARenderer3D.Create(FSurface, RenderingOptions,
|
||
|
|
FAmbiantLightColorF,
|
||
|
|
FLights);
|
||
|
|
DoRender;
|
||
|
|
FreeAndNil(FRenderer);
|
||
|
|
end;
|
||
|
|
|
||
|
|
procedure TBGRAScene3D.Render(ARenderer: TCustomRenderer3D);
|
||
|
|
begin
|
||
|
|
FRenderer := ARenderer;
|
||
|
|
DoRender;
|
||
|
|
FRenderer := nil;
|
||
|
|
end;
|
||
|
|
|
||
|
|
procedure TBGRAScene3D.ComputeView(ScaleX,ScaleY: single);
|
||
|
|
var
|
||
|
|
i: Integer;
|
||
|
|
begin
|
||
|
|
FProjection.Zoom := Zoom;
|
||
|
|
FProjection.Zoom.X := FProjection.Zoom.X * ScaleX;
|
||
|
|
FProjection.Zoom.Y := FProjection.Zoom.Y * ScaleY;
|
||
|
|
FProjection.Center := ViewCenter;
|
||
|
|
FProjection.Center.X := FProjection.Center.X * ScaleX;
|
||
|
|
FProjection.Center.Y := FProjection.Center.Y * ScaleY;
|
||
|
|
for i := 0 to FObjectCount-1 do
|
||
|
|
FObjects[i].ComputeWithMatrix(Camera.Matrix, FProjection);
|
||
|
|
end;
|
||
|
|
|
||
|
|
function TBGRAScene3D.ComputeCoordinate(AViewCoord: TPoint3D_128): TPointF;
|
||
|
|
var InvZ: single;
|
||
|
|
begin
|
||
|
|
if AViewCoord.z > 0 then
|
||
|
|
begin
|
||
|
|
InvZ := 1/AViewCoord.z;
|
||
|
|
result := PointF(AViewCoord.x*InvZ*FProjection.Zoom.x + FProjection.Center.x,
|
||
|
|
AViewCoord.y*InvZ*FProjection.Zoom.Y + FProjection.Center.y);
|
||
|
|
end else
|
||
|
|
result := PointF(0,0);
|
||
|
|
end;
|
||
|
|
|
||
|
|
type
|
||
|
|
arrayOfTBGRAFace3D = array of TBGRAFace3D;
|
||
|
|
|
||
|
|
procedure InsertionSortFaces(var a: arrayOfTBGRAFace3D);
|
||
|
|
var i,j: integer;
|
||
|
|
temp: TBGRAFace3D;
|
||
|
|
begin
|
||
|
|
for i := 1 to high(a) do
|
||
|
|
begin
|
||
|
|
Temp := a[i];
|
||
|
|
j := i;
|
||
|
|
while (j>0) and (a[j-1].ViewCenterZ > Temp.ViewCenterZ) do
|
||
|
|
begin
|
||
|
|
a[j] := a[j-1];
|
||
|
|
dec(j);
|
||
|
|
end;
|
||
|
|
a[j] := Temp;
|
||
|
|
end;
|
||
|
|
end;
|
||
|
|
|
||
|
|
function PartitionFaces(var a: arrayOfTBGRAFace3D; left,right: integer): integer;
|
||
|
|
|
||
|
|
procedure Swap(idx1,idx2: integer); inline;
|
||
|
|
var temp: TBGRAFace3D;
|
||
|
|
begin
|
||
|
|
temp := a[idx1];
|
||
|
|
a[idx1] := a[idx2];
|
||
|
|
a[idx2] := temp;
|
||
|
|
end;
|
||
|
|
|
||
|
|
var pivotIndex: integer;
|
||
|
|
pivotValue: TBGRAFace3D;
|
||
|
|
storeIndex: integer;
|
||
|
|
i: integer;
|
||
|
|
|
||
|
|
begin
|
||
|
|
pivotIndex := left + random(right-left+1);
|
||
|
|
pivotValue := a[pivotIndex];
|
||
|
|
swap(pivotIndex,right);
|
||
|
|
storeIndex := left;
|
||
|
|
for i := left to right-1 do
|
||
|
|
if a[i].ViewCenterZ <= pivotValue.ViewCenterZ then
|
||
|
|
begin
|
||
|
|
swap(i,storeIndex);
|
||
|
|
inc(storeIndex);
|
||
|
|
end;
|
||
|
|
swap(storeIndex,right);
|
||
|
|
result := storeIndex;
|
||
|
|
end;
|
||
|
|
|
||
|
|
procedure QuickSortFaces(var a: arrayOfTBGRAFace3D; left,right: integer);
|
||
|
|
var pivotNewIndex: integer;
|
||
|
|
begin
|
||
|
|
if right > left+9 then
|
||
|
|
begin
|
||
|
|
pivotNewIndex := PartitionFaces(a,left,right);
|
||
|
|
QuickSortFaces(a,left,pivotNewIndex-1);
|
||
|
|
QuickSortFaces(a,pivotNewIndex+1,right);
|
||
|
|
end;
|
||
|
|
end;
|
||
|
|
|
||
|
|
procedure SortFaces(var a: arrayOfTBGRAFace3D);
|
||
|
|
begin
|
||
|
|
if length(a) < 10 then InsertionSortFaces(a) else
|
||
|
|
begin
|
||
|
|
QuickSortFaces(a,0,high(a));
|
||
|
|
InsertionSortFaces(a);
|
||
|
|
end;
|
||
|
|
end;
|
||
|
|
|
||
|
|
function IsPolyVisible(const p : array of TPointF; ori: integer = 1) : boolean;
|
||
|
|
var i: integer;
|
||
|
|
begin
|
||
|
|
i := 0;
|
||
|
|
while i<=high(p)-2 do
|
||
|
|
begin
|
||
|
|
if ori*
|
||
|
|
( (p[i+1].x-p[i].x)*(p[i+2].y-p[i].y) -
|
||
|
|
(p[i+1].y-p[i].y)*(p[i+2].x-p[i].x)) > 0 then
|
||
|
|
begin
|
||
|
|
result := true;
|
||
|
|
exit;
|
||
|
|
end;
|
||
|
|
inc(i);
|
||
|
|
end;
|
||
|
|
result := false;
|
||
|
|
end;
|
||
|
|
|
||
|
|
procedure TBGRAScene3D.DoRender;
|
||
|
|
var
|
||
|
|
LFaces: array of TBGRAFace3D;
|
||
|
|
LFaceOpaque: array of boolean;
|
||
|
|
LFaceCount: integer;
|
||
|
|
|
||
|
|
procedure PrepareFaces;
|
||
|
|
var i,j, LFaceIndex: integer;
|
||
|
|
obj: IBGRAObject3D;
|
||
|
|
begin
|
||
|
|
LFaces := nil;
|
||
|
|
LFaceCount := 0;
|
||
|
|
for i := 0 to FObjectCount-1 do
|
||
|
|
begin
|
||
|
|
obj := FObjects[i];
|
||
|
|
inc(LFaceCount, obj.GetFaceCount);
|
||
|
|
obj.Update;
|
||
|
|
end;
|
||
|
|
setlength(LFaces, LFaceCount);
|
||
|
|
LFaceIndex := 0;
|
||
|
|
for i := 0 to FObjectCount-1 do
|
||
|
|
with FObjects[i] do
|
||
|
|
begin
|
||
|
|
for j := 0 to GetFaceCount-1 do
|
||
|
|
begin
|
||
|
|
LFaces[LFaceIndex] := TBGRAFace3D(GetFace(j).GetAsObject);
|
||
|
|
inc(LFaceIndex);
|
||
|
|
end;
|
||
|
|
end;
|
||
|
|
end;
|
||
|
|
|
||
|
|
var
|
||
|
|
faceDesc: TFaceRenderingDescription;
|
||
|
|
LVertices: array of TBGRAVertex3D;
|
||
|
|
|
||
|
|
procedure DrawFace(numFace: integer);
|
||
|
|
var
|
||
|
|
j,k: Integer;
|
||
|
|
VCount,NewVCount: integer;
|
||
|
|
NegNormals: boolean;
|
||
|
|
LastVisibleVertex: integer;
|
||
|
|
|
||
|
|
procedure AddZIntermediate(n1,n2: integer);
|
||
|
|
var t: single;
|
||
|
|
v1,v2: TBGRAVertex3D;
|
||
|
|
begin
|
||
|
|
v1 := LVertices[n1];
|
||
|
|
v2 := LVertices[n2];
|
||
|
|
t := (RenderingOptions.MinZ - v1.ViewCoord.z)/(v2.ViewCoord.z - v1.ViewCoord.z);
|
||
|
|
LVertices[NewVCount] := nil; //computed
|
||
|
|
|
||
|
|
faceDesc.Colors[NewVCount] := MergeBGRA(faceDesc.Colors[n1],round((1-t)*65536),faceDesc.Colors[n2],round(t*65536));
|
||
|
|
faceDesc.TexCoords[NewVCount] := faceDesc.TexCoords[n1]*(1-t) + faceDesc.TexCoords[n2]*t;
|
||
|
|
faceDesc.Positions3D[NewVCount] := faceDesc.Positions3D[n1]*(1-t) + faceDesc.Positions3D[n2]*t;
|
||
|
|
faceDesc.Normals3D[NewVCount] := faceDesc.Normals3D[n1]*(1-t) + faceDesc.Normals3D[n2]*t;
|
||
|
|
faceDesc.Projections[NewVCount] := ComputeCoordinate(faceDesc.Positions3D[NewVCount]);
|
||
|
|
inc(NewVCount);
|
||
|
|
end;
|
||
|
|
|
||
|
|
procedure LoadVertex(idxL: integer; idxV: integer);
|
||
|
|
var vertexDesc: PBGRAFaceVertexDescription;
|
||
|
|
tempV: TBGRAVertex3D;
|
||
|
|
begin
|
||
|
|
with LFaces[numFace] do
|
||
|
|
begin
|
||
|
|
vertexDesc := VertexDescription[idxV];
|
||
|
|
with vertexDesc^ do
|
||
|
|
begin
|
||
|
|
tempV := TBGRAVertex3D(vertex.GetAsObject);
|
||
|
|
LVertices[idxL] := tempV;
|
||
|
|
|
||
|
|
faceDesc.Colors[idxL] := ActualColor;
|
||
|
|
faceDesc.TexCoords[idxL] := ActualTexCoord;
|
||
|
|
|
||
|
|
with tempV.CoordData^ do
|
||
|
|
begin
|
||
|
|
faceDesc.Positions3D[idxL] := viewCoord;
|
||
|
|
facedesc.Normals3D[idxL] := viewNormal;
|
||
|
|
faceDesc.Projections[idxL] := projectedCoord;
|
||
|
|
end;
|
||
|
|
if Normal <> nil then
|
||
|
|
facedesc.Normals3D[idxL] := Normal.ViewNormal_128;
|
||
|
|
Normalize3D_128(facedesc.Normals3D[idxL]);
|
||
|
|
end;
|
||
|
|
end;
|
||
|
|
end;
|
||
|
|
|
||
|
|
begin
|
||
|
|
with LFaces[numFace] do
|
||
|
|
begin
|
||
|
|
VCount := VertexCount;
|
||
|
|
if VCount < 3 then exit;
|
||
|
|
|
||
|
|
faceDesc.NormalsMode := Object3D.LightingNormal;
|
||
|
|
|
||
|
|
faceDesc.Material := ActualMaterial;
|
||
|
|
if faceDesc.Material = nil then exit;
|
||
|
|
faceDesc.Texture := ActualTexture;
|
||
|
|
|
||
|
|
if length(LVertices) < VCount+3 then //keep margin for z-clip
|
||
|
|
begin
|
||
|
|
setlength(LVertices, (VCount+3)*2);
|
||
|
|
setlength(faceDesc.Colors, length(LVertices));
|
||
|
|
setlength(faceDesc.TexCoords, length(LVertices));
|
||
|
|
setlength(faceDesc.Projections, length(LVertices));
|
||
|
|
setlength(faceDesc.Positions3D, length(LVertices));
|
||
|
|
setlength(faceDesc.Normals3D, length(LVertices));
|
||
|
|
end;
|
||
|
|
|
||
|
|
if FRenderer.HandlesNearClipping then
|
||
|
|
begin
|
||
|
|
for j := 0 to VCount-1 do
|
||
|
|
LoadVertex(j,j);
|
||
|
|
end else
|
||
|
|
begin
|
||
|
|
NewVCount := 0;
|
||
|
|
LastVisibleVertex := -1;
|
||
|
|
for k := VCount-1 downto 0 do
|
||
|
|
if Vertex[k].ViewCoordZ >= RenderingOptions.MinZ then
|
||
|
|
begin
|
||
|
|
LastVisibleVertex := k;
|
||
|
|
break;
|
||
|
|
end;
|
||
|
|
if LastVisibleVertex = -1 then exit;
|
||
|
|
|
||
|
|
k := VCount-1;
|
||
|
|
for j := 0 to VCount-1 do
|
||
|
|
begin
|
||
|
|
if Vertex[j].ViewCoordZ >= RenderingOptions.MinZ then
|
||
|
|
begin
|
||
|
|
if k <> LastVisibleVertex then //one or more vertices is out
|
||
|
|
begin
|
||
|
|
LoadVertex(NewVCount+1, LastVisibleVertex);
|
||
|
|
LoadVertex(NewVCount+2, (LastVisibleVertex+1) mod VertexCount);
|
||
|
|
AddZIntermediate(NewVCount+1,NewVCount+2);
|
||
|
|
|
||
|
|
LoadVertex(NewVCount+1, j);
|
||
|
|
LoadVertex(NewVCount+2, k);
|
||
|
|
|
||
|
|
AddZIntermediate(NewVCount+1,NewVCount+2);
|
||
|
|
inc(NewVCount);
|
||
|
|
end else
|
||
|
|
begin
|
||
|
|
LoadVertex(NewVCount, j);
|
||
|
|
inc(NewVCount);
|
||
|
|
end;
|
||
|
|
LastVisibleVertex := j;
|
||
|
|
end;
|
||
|
|
k := j;
|
||
|
|
end;
|
||
|
|
VCount := NewVCount;
|
||
|
|
if VCount < 3 then exit; //after z-clipping
|
||
|
|
end;
|
||
|
|
|
||
|
|
if not FRenderer.HandlesFaceCulling then
|
||
|
|
begin
|
||
|
|
if not IsPolyVisible(slice(faceDesc.Projections,VCount)) then
|
||
|
|
begin
|
||
|
|
if not Biface then exit;
|
||
|
|
NegNormals := True;
|
||
|
|
end else
|
||
|
|
begin
|
||
|
|
NegNormals := False;
|
||
|
|
end;
|
||
|
|
end else
|
||
|
|
NegNormals := false;
|
||
|
|
|
||
|
|
//compute normals
|
||
|
|
case faceDesc.NormalsMode of
|
||
|
|
lnFace: for j := 0 to VCount-1 do
|
||
|
|
faceDesc.Normals3D[j] := ViewNormal_128;
|
||
|
|
lnFaceVertexMix:
|
||
|
|
for j := 0 to VCount-1 do
|
||
|
|
begin
|
||
|
|
faceDesc.Normals3D[j].Offset(ViewNormal_128);
|
||
|
|
Normalize3D_128(faceDesc.Normals3D[j]);
|
||
|
|
end;
|
||
|
|
end;
|
||
|
|
if NegNormals then
|
||
|
|
for j := 0 to VCount-1 do
|
||
|
|
faceDesc.Normals3D[j] := -faceDesc.Normals3D[j];
|
||
|
|
|
||
|
|
if LightThroughFactorOverride then
|
||
|
|
faceDesc.LightThroughFactor := LightThroughFactor
|
||
|
|
else
|
||
|
|
faceDesc.LightThroughFactor := faceDesc.Material.GetLightThroughFactor;
|
||
|
|
|
||
|
|
faceDesc.NbVertices:= VCount;
|
||
|
|
faceDesc.Biface := Biface;
|
||
|
|
|
||
|
|
if FRenderer.RenderFace(faceDesc, @ComputeCoordinate) then
|
||
|
|
inc(FRenderedFaceCount);
|
||
|
|
end;
|
||
|
|
end;
|
||
|
|
|
||
|
|
var i,j: integer;
|
||
|
|
|
||
|
|
begin
|
||
|
|
FRenderedFaceCount:= 0;
|
||
|
|
|
||
|
|
PrepareFaces;
|
||
|
|
ComputeView(FRenderer.GlobalScale,FRenderer.GlobalScale);
|
||
|
|
FRenderer.Projection := FProjection;
|
||
|
|
|
||
|
|
SortFaces(LFaces);
|
||
|
|
LVertices := nil;
|
||
|
|
|
||
|
|
//if there is a Z-Buffer, it is possible to avoid drawing things that
|
||
|
|
//are hidden by opaque faces by drawing first all opaque faces
|
||
|
|
if FRenderer.HasZBuffer then
|
||
|
|
begin
|
||
|
|
setlength(LFaceOpaque, length(LFaces));
|
||
|
|
for i := 0 to High(LFaces) do
|
||
|
|
begin
|
||
|
|
if (LFaces[i].Texture = nil) then
|
||
|
|
begin
|
||
|
|
LFaceOpaque[i] := true;
|
||
|
|
with LFaces[i] do
|
||
|
|
for j := 0 to VertexCount-1 do
|
||
|
|
if VertexColor[j].alpha <> 255 then
|
||
|
|
begin
|
||
|
|
LFaceOpaque[i] := false;
|
||
|
|
break;
|
||
|
|
end;
|
||
|
|
end else
|
||
|
|
LFaceOpaque[i] := true;
|
||
|
|
end;
|
||
|
|
|
||
|
|
//draw near opaque faces first
|
||
|
|
for i := 0 to High(LFaces) do
|
||
|
|
if LFaceOpaque[i] then DrawFace(i);
|
||
|
|
|
||
|
|
//draw other faces
|
||
|
|
for i := High(LFaces) downto 0 do
|
||
|
|
if not LFaceOpaque[i] then DrawFace(i);
|
||
|
|
end else
|
||
|
|
begin
|
||
|
|
for i := High(LFaces) downto 0 do
|
||
|
|
DrawFace(i);
|
||
|
|
end;
|
||
|
|
end;
|
||
|
|
|
||
|
|
function TBGRAScene3D.CreateObject: IBGRAObject3D;
|
||
|
|
begin
|
||
|
|
result := TBGRAObject3D.Create(self);
|
||
|
|
AddObject(result);
|
||
|
|
end;
|
||
|
|
|
||
|
|
function TBGRAScene3D.CreateObject(ATexture: IBGRAScanner): IBGRAObject3D;
|
||
|
|
begin
|
||
|
|
result := TBGRAObject3D.Create(self);
|
||
|
|
result.Texture := ATexture;
|
||
|
|
AddObject(result);
|
||
|
|
end;
|
||
|
|
|
||
|
|
function TBGRAScene3D.CreateObject(AColor: TBGRAPixel): IBGRAObject3D;
|
||
|
|
begin
|
||
|
|
result := TBGRAObject3D.Create(self);
|
||
|
|
result.Color := AColor;
|
||
|
|
AddObject(result);
|
||
|
|
end;
|
||
|
|
|
||
|
|
function TBGRAScene3D.CreateSphere(ARadius: Single; AHorizPrecision: integer; AVerticalPrecision : integer): IBGRAObject3D;
|
||
|
|
begin
|
||
|
|
result := TBGRASphere3D.Create(self, ARadius, AHorizPrecision, AVerticalPrecision);
|
||
|
|
AddObject(result);
|
||
|
|
end;
|
||
|
|
|
||
|
|
function TBGRAScene3D.CreateSphere(ARadius: Single; AColor: TBGRAPixel; AHorizPrecision: integer; AVerticalPrecision : integer): IBGRAObject3D;
|
||
|
|
begin
|
||
|
|
result := TBGRASphere3D.Create(self, ARadius, AHorizPrecision, AVerticalPrecision);
|
||
|
|
result.Color := AColor;
|
||
|
|
AddObject(result);
|
||
|
|
end;
|
||
|
|
|
||
|
|
function TBGRAScene3D.CreateHalfSphere(ARadius: Single;
|
||
|
|
AHorizPrecision: integer; AVerticalPrecision: integer): IBGRAObject3D;
|
||
|
|
begin
|
||
|
|
result := TBGRASphere3D.Create(self, ARadius, AHorizPrecision, AVerticalPrecision, True);
|
||
|
|
AddObject(result);
|
||
|
|
end;
|
||
|
|
|
||
|
|
function TBGRAScene3D.CreateHalfSphere(ARadius: Single; AColor: TBGRAPixel;
|
||
|
|
AHorizPrecision: integer; AVerticalPrecision: integer): IBGRAObject3D;
|
||
|
|
begin
|
||
|
|
result := TBGRASphere3D.Create(self, ARadius, AHorizPrecision, AVerticalPrecision, True);
|
||
|
|
result.Color := AColor;
|
||
|
|
AddObject(result);
|
||
|
|
end;
|
||
|
|
|
||
|
|
procedure TBGRAScene3D.RemoveObject(AObject: IBGRAObject3D);
|
||
|
|
var
|
||
|
|
i,j: Integer;
|
||
|
|
begin
|
||
|
|
for i := FObjectCount-1 downto 0 do
|
||
|
|
if FObjects[i] = AObject then
|
||
|
|
begin
|
||
|
|
dec(FObjectCount);
|
||
|
|
FObjects[i] := nil;
|
||
|
|
for j := i to FObjectCount-1 do
|
||
|
|
FObjects[j] := FObjects[j+1];
|
||
|
|
end;
|
||
|
|
end;
|
||
|
|
|
||
|
|
function TBGRAScene3D.AddDirectionalLight(ADirection: TPoint3D;
|
||
|
|
ALightness: single; AMinIntensity: single): IBGRADirectionalLight3D;
|
||
|
|
var lightObj: TBGRADirectionalLight3D;
|
||
|
|
begin
|
||
|
|
lightObj := TBGRADirectionalLight3D.Create(ADirection);
|
||
|
|
result := lightObj;
|
||
|
|
result.ColorF := ColorF(ALightness,ALightness,ALightness,1);
|
||
|
|
result.MinIntensity := AMinIntensity;
|
||
|
|
AddLight(lightObj);
|
||
|
|
end;
|
||
|
|
|
||
|
|
function TBGRAScene3D.AddPointLight(AVertex: IBGRAVertex3D;
|
||
|
|
AOptimalDistance: single; ALightness: single; AMinIntensity: single
|
||
|
|
): IBGRAPointLight3D;
|
||
|
|
var lightObj: TBGRAPointLight3D;
|
||
|
|
begin
|
||
|
|
lightObj := TBGRAPointLight3D.Create(AVertex, ALightness*sqr(AOptimalDistance));
|
||
|
|
result := lightObj;
|
||
|
|
result.MinIntensity := AMinIntensity;
|
||
|
|
AddLight(lightObj);
|
||
|
|
end;
|
||
|
|
|
||
|
|
function TBGRAScene3D.AddDirectionalLight(ADirection: TPoint3D;
|
||
|
|
AColor: TBGRAPixel; AMinIntensity: single): IBGRADirectionalLight3D;
|
||
|
|
var lightObj: TBGRADirectionalLight3D;
|
||
|
|
begin
|
||
|
|
lightObj := TBGRADirectionalLight3D.Create(ADirection);
|
||
|
|
result := lightObj;
|
||
|
|
result.MinIntensity := AMinIntensity;
|
||
|
|
result.Color := AColor;
|
||
|
|
AddLight(lightObj);
|
||
|
|
end;
|
||
|
|
|
||
|
|
function TBGRAScene3D.AddPointLight(AVertex: IBGRAVertex3D;
|
||
|
|
AOptimalDistance: single; AColor: TBGRAPixel; AMinIntensity: single
|
||
|
|
): IBGRAPointLight3D;
|
||
|
|
var lightObj: TBGRAPointLight3D;
|
||
|
|
begin
|
||
|
|
lightObj := TBGRAPointLight3D.Create(AVertex,sqr(AOptimalDistance));
|
||
|
|
result := lightObj;
|
||
|
|
result.Color := AColor;
|
||
|
|
result.MinIntensity := AMinIntensity;
|
||
|
|
AddLight(lightObj);
|
||
|
|
end;
|
||
|
|
|
||
|
|
procedure TBGRAScene3D.RemoveLight(ALight: IBGRALight3D);
|
||
|
|
var idx: integer;
|
||
|
|
begin
|
||
|
|
idx := FLights.IndexOf(ALight.GetAsObject);
|
||
|
|
if idx <> -1 then
|
||
|
|
begin
|
||
|
|
ALight._Release;
|
||
|
|
FLights.Delete(Idx);
|
||
|
|
end;
|
||
|
|
end;
|
||
|
|
|
||
|
|
procedure TBGRAScene3D.SetZoom(value: Single);
|
||
|
|
begin
|
||
|
|
SetZoom(PointF(value,value));
|
||
|
|
end;
|
||
|
|
|
||
|
|
procedure TBGRAScene3D.SetZoom(value: TPointF);
|
||
|
|
begin
|
||
|
|
FZoom := value;
|
||
|
|
FAutoZoom := false;
|
||
|
|
end;
|
||
|
|
|
||
|
|
function TBGRAScene3D.CreateMaterial: IBGRAMaterial3D;
|
||
|
|
var m: TBGRAMaterial3D;
|
||
|
|
begin
|
||
|
|
m := TBGRAMaterial3D.Create;
|
||
|
|
m.OnTextureChanged := @OnMaterialTextureChanged;
|
||
|
|
result := m;
|
||
|
|
AddMaterial(result);
|
||
|
|
end;
|
||
|
|
|
||
|
|
function TBGRAScene3D.CreateMaterial(ASpecularIndex: integer): IBGRAMaterial3D;
|
||
|
|
var m: TBGRAMaterial3D;
|
||
|
|
begin
|
||
|
|
m := TBGRAMaterial3D.Create;
|
||
|
|
m.SetSpecularIndex(ASpecularIndex);
|
||
|
|
m.SetSpecularColor(BGRAWhite);
|
||
|
|
m.OnTextureChanged := @OnMaterialTextureChanged;
|
||
|
|
result := m;
|
||
|
|
AddMaterial(result);
|
||
|
|
end;
|
||
|
|
|
||
|
|
function TBGRAScene3D.GetMaterialByName(AName: string): IBGRAMaterial3D;
|
||
|
|
var i: integer;
|
||
|
|
begin
|
||
|
|
for i := 0 to MaterialCount-1 do
|
||
|
|
if AName = Material[i].Name then
|
||
|
|
begin
|
||
|
|
result := Material[i];
|
||
|
|
exit;
|
||
|
|
end;
|
||
|
|
result := nil;
|
||
|
|
end;
|
||
|
|
|
||
|
|
procedure TBGRAScene3D.UpdateMaterials;
|
||
|
|
var i,j: integer;
|
||
|
|
obj: IBGRAObject3D;
|
||
|
|
face: IBGRAFace3D;
|
||
|
|
begin
|
||
|
|
for i := 0 to Object3DCount-1 do
|
||
|
|
begin
|
||
|
|
obj := Object3D[i];
|
||
|
|
for j := 0 to obj.FaceCount-1 do
|
||
|
|
begin
|
||
|
|
face := obj.Face[j];
|
||
|
|
if face.MaterialName <> '' then
|
||
|
|
UseMaterial(face.MaterialName,face);
|
||
|
|
end;
|
||
|
|
end;
|
||
|
|
end;
|
||
|
|
|
||
|
|
procedure TBGRAScene3D.UpdateMaterial(AMaterialName: string);
|
||
|
|
var i,j: integer;
|
||
|
|
obj: IBGRAObject3D;
|
||
|
|
face: IBGRAFace3D;
|
||
|
|
begin
|
||
|
|
for i := 0 to Object3DCount-1 do
|
||
|
|
begin
|
||
|
|
obj := Object3D[i];
|
||
|
|
for j := 0 to obj.FaceCount-1 do
|
||
|
|
begin
|
||
|
|
face := obj.Face[j];
|
||
|
|
if face.MaterialName = AMaterialName then
|
||
|
|
UseMaterial(face.MaterialName,face);
|
||
|
|
end;
|
||
|
|
end;
|
||
|
|
end;
|
||
|
|
|
||
|
|
procedure TBGRAScene3D.ForEachVertex(ACallback: TVertex3DCallback);
|
||
|
|
var i: integer;
|
||
|
|
begin
|
||
|
|
for i := 0 to Object3DCount-1 do
|
||
|
|
Object3D[i].ForEachVertex(ACallback);
|
||
|
|
end;
|
||
|
|
|
||
|
|
procedure TBGRAScene3D.ForEachFace(ACallback: TFace3DCallback);
|
||
|
|
var i: integer;
|
||
|
|
begin
|
||
|
|
for i := 0 to Object3DCount-1 do
|
||
|
|
Object3D[i].ForEachFace(ACallback);
|
||
|
|
end;
|
||
|
|
|
||
|
|
function TBGRAScene3D.MakeLightList: TList;
|
||
|
|
var i: integer;
|
||
|
|
begin
|
||
|
|
result := TList.Create;
|
||
|
|
for i := 0 to FLights.Count-1 do
|
||
|
|
result.Add(FLights[i]);
|
||
|
|
end;
|
||
|
|
|
||
|
|
initialization
|
||
|
|
|
||
|
|
Randomize;
|
||
|
|
|
||
|
|
end.
|
||
|
|
|