Files
kam-mobile/vendor/kam_remake/src/ext/BGRABitmap/bgrascene3d.pas
T

1643 lines
46 KiB
ObjectPascal
Raw Normal View History

2026-09-12 16:50:30 +05:00
// 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.