123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950 |
- // SPDX-License-Identifier: GPL-3.0-only
- unit UObject3D;
- {$mode objfpc}{$H+}
- interface
- uses
- Classes, SysUtils, LResources, Forms, Controls, Graphics, Dialogs,
- StdCtrls, Spin, ExtCtrls, ComCtrls, BGRAVirtualScreen, BGRAKnob,
- BGRAImageList, BGRABitmap, BGRAScene3D, LazPaintType, BGRABitmapTypes,
- UConfig;
- const
- AntialiasingLevelWhenFixed = 2;
- AntialiasingLevelWhenMoving = 1;
- type
- { TScene }
- TScene = class(TBGRAScene3D)
- private
- FTextures: array of record
- Name: string;
- Texture: TBGRABitmap;
- Usage: integer;
- end;
- procedure NoTextures;
- public
- TexturePath: string;
- procedure ComputeTexCoord(AFace: IBGRAFace3D; AWidth, AHeight: integer);
- function FetchTexture(AName: string; out texSize: TPointF): IBGRAScanner; override;
- function FetchTextureAsBitmap(AName: string; ARelativePath: boolean): TBGRABitmap;
- procedure ReleaseTextureReference(ref: IBGRAScanner);
- procedure QueryTextureReference(ref: IBGRAScanner);
- procedure Clear; override;
- procedure FreeUnusedTextures;
- end;
- { TFObject3D }
- TFObject3D = class(TForm)
- BGRAImageList1: TBGRAImageList;
- BGRAKnob_Zoom: TBGRAKnob;
- BGRAView3D: TBGRAVirtualScreen;
- Button_LoadTex: TButton;
- Button_NoTex: TButton;
- Button_Cancel: TButton;
- Button_OK: TButton;
- CheckBox_Antialiasing: TCheckBox;
- CheckBox_Biface: TCheckBox;
- CheckBox_TextureInterp: TCheckBox;
- ColorDialog1: TColorDialog;
- ComboBox_Normals: TComboBox;
- GroupBox_SelectedMaterial: TGroupBox;
- GroupBox_SelectedLight: TGroupBox;
- Label_Color1: TLabel;
- Label_Materials: TLabel;
- Label_Lights: TLabel;
- Label_Zoom: TLabel;
- Label_LightingNormals: TLabel;
- Label_SpecularIndex: TLabel;
- Label_Color: TLabel;
- Label_Opacity: TLabel;
- Label_Width: TLabel;
- Label_Height: TLabel;
- ListBox_Materials: TListBox;
- ListBox_Lights: TListBox;
- OpenTextureDialog: TOpenDialog;
- PageControl1: TPageControl;
- PaintBox_LightPos: TPaintBox;
- Shape_MaterialColor: TShape;
- Shape_LightColor: TShape;
- SpinEdit_ColorOpacity: TSpinEdit;
- SpinEdit_Height: TSpinEdit;
- SpinEdit_SpecularIndex: TSpinEdit;
- Rendering: TTabSheet;
- Materials: TTabSheet;
- SpinEdit_Width: TSpinEdit;
- Lights: TTabSheet;
- ToolBar1: TToolBar;
- ToolAddDirectional: TToolButton;
- ToolPointLight: TToolButton;
- ToolRemoveSelectedLight: TToolButton;
- procedure BGRAKnob_ZoomValueChanged(Sender: TObject; Value: single);
- procedure BGRAView3DMouseDown(Sender: TObject; Button: TMouseButton;
- {%H-}Shift: TShiftState; X, Y: Integer);
- procedure BGRAView3DMouseMove(Sender: TObject; {%H-}Shift: TShiftState; X,
- Y: Integer);
- procedure BGRAView3DMouseUp(Sender: TObject; Button: TMouseButton;
- {%H-}Shift: TShiftState; {%H-}X, {%H-}Y: Integer);
- procedure BGRAView3DRedraw(Sender: TObject; Bitmap: TBGRABitmap);
- procedure BGRAView3DResize(Sender: TObject);
- procedure Button_LoadTexClick(Sender: TObject);
- procedure Button_NoTexClick(Sender: TObject);
- procedure CheckBox_AntialiasingChange(Sender: TObject);
- procedure CheckBox_BifaceChange(Sender: TObject);
- procedure CheckBox_TextureInterpChange(Sender: TObject);
- procedure ComboBox_NormalsChange(Sender: TObject);
- procedure FormCreate(Sender: TObject);
- procedure FormDestroy(Sender: TObject);
- procedure FormKeyPress(Sender: TObject; var Key: char);
- procedure FormResize(Sender: TObject);
- procedure FormShow(Sender: TObject);
- procedure ListBox_LightsKeyPress(Sender: TObject; var Key: char);
- procedure ListBox_LightsSelectionChange(Sender: TObject; User: boolean);
- procedure ListBox_MaterialsKeyPress(Sender: TObject; var Key: char);
- procedure ListBox_MaterialsSelectionChange(Sender: TObject; {%H-}User: boolean);
- procedure PaintBox_LightPosMouseDown(Sender: TObject; {%H-}Button: TMouseButton;
- {%H-}Shift: TShiftState; X, Y: Integer);
- procedure PaintBox_LightPosMouseMove(Sender: TObject; Shift: TShiftState;
- X, Y: Integer);
- procedure PaintBox_LightPosMouseUp(Sender: TObject; Button: TMouseButton;
- {%H-}Shift: TShiftState; {%H-}X, {%H-}Y: Integer);
- procedure PaintBox_LightPosPaint(Sender: TObject);
- procedure Shape_LightColorMouseUp(Sender: TObject; {%H-}Button: TMouseButton;
- {%H-}Shift: TShiftState; {%H-}X, {%H-}Y: Integer);
- procedure Shape_MaterialColorMouseDown(Sender: TObject;
- {%H-}Button: TMouseButton; {%H-}Shift: TShiftState; {%H-}X, {%H-}Y: Integer);
- procedure SpinEdit_ColorOpacityChange(Sender: TObject);
- procedure SpinEdit_ColorOpacityKeyPress(Sender: TObject; var Key: char);
- procedure SpinEdit_HeightKeyPress(Sender: TObject; var Key: char);
- procedure SpinEdit_SpecularIndexChange(Sender: TObject);
- procedure SpinEdit_SpecularIndexKeyPress(Sender: TObject; var Key: char);
- procedure SpinEdit_WidthKeyPress(Sender: TObject; var Key: char);
- procedure ToolAddDirectionalClick(Sender: TObject);
- procedure ToolPointLightClick(Sender: TObject);
- procedure ToolRemoveSelectedLightClick(Sender: TObject);
- private
- { private declarations }
- procedure UpdateTabSize;
- procedure CheckKey(var Key: char);
- procedure DoLoadTexture(AFilename: string);
- procedure DoFreeTexture;
- function MaterialHasTexture: boolean;
- procedure UpdateLightList;
- procedure UpdateSelectedLight;
- procedure SetLightPos(x,y: single);
- public
- { public declarations }
- scene : TScene;
- moving,rotating: boolean;
- moveOrigin: TPoint;
- previousAngle: single;
- previousZoom: single;
- materialIndex, lightIndex: integer;
- InnerTabBottomPadding: integer;
- Config: TLazPaintConfig;
- end;
- function ShowObject3DDlg({%H-}Instance: TLazPaintCustomInstance; filenameUTF8: string; maxWidth, maxHeight: integer): TBGRABitmap;
- implementation
- uses LazFileUtils, ugraph, LCScaleDPI, umac, ULoadImage, UFileSystem;
- const PointLightDist = 80;
- { TScene }
- procedure TScene.NoTextures;
- var i: integer;
- begin
- for i := 0 to MaterialCount-1 do
- begin
- ReleaseTextureReference(Material[i].Texture);
- Material[i].Texture := nil;
- end;
- end;
- procedure TScene.ComputeTexCoord(AFace: IBGRAFace3D; AWidth,AHeight: integer);
- var
- j: integer;
- p1,p2,p3,u,v: TPoint3d;
- min,max,pt: TPointF;
- factor: single;
- begin
- if AFace.VertexCount < 3 then exit;
- j := 0;
- p1 := AFace.Vertex[j].GetSceneCoord;
- repeat
- inc(j);
- if j >= AFace.VertexCount then exit;
- p2 := AFace.Vertex[j].GetSceneCoord;
- u := p2-p1;
- until u*u <> 0;
- Normalize3D(u);
- repeat
- inc(j);
- if j >= AFace.VertexCount then exit;
- p3 := AFace.Vertex[j].GetSceneCoord;
- v := p3-p2;
- v := v - u*(u*v);
- until v*v <> 0;
- Normalize3D(v);
- with AFace.Vertex[0] do
- begin
- pt := PointF((GetSceneCoord-p1)*u,(GetSceneCoord-p1)*v);
- min := pt;
- max := pt
- end;
- for j := 1 to AFace.VertexCount-1 do
- with AFace.Vertex[j] do
- begin
- pt := PointF((GetSceneCoord-p1)*u,(GetSceneCoord-p1)*v);
- if pt.x < min.x then min.x := pt.x else
- if pt.x > max.x then max.x := pt.x;
- if pt.y < min.y then min.y := pt.y else
- if pt.y > max.y then max.y := pt.y;
- end;
- if min.x = max.x then max.x := min.x+1;
- if min.y = max.y then max.y := min.y+1;
- factor := AWidth/(max.x-min.x);
- if AHeight/(max.y-min.y) < factor then factor := AHeight/(max.y-min.y);
- for j := 0 to AFace.VertexCount-1 do
- with AFace.Vertex[j] do
- begin
- pt := PointF((GetSceneCoord-p1)*u,(GetSceneCoord-p1)*v);
- pt := PointF((pt.x-min.x)*factor,(pt.y-min.y)*factor);
- AFace.TexCoord[j] := pt;
- end;
- end;
- function TScene.FetchTexture(AName: string; out texSize: TPointF): IBGRAScanner;
- var bmp: TBGRABitmap;
- begin
- bmp := FetchTextureAsBitmap(AName, True);
- result := bmp;
- texSize := PointF(bmp.Width,bmp.Height);
- end;
- function TScene.FetchTextureAsBitmap(AName: string; ARelativePath: boolean): TBGRABitmap;
- function AddTexture(AFilename: string): TBGRABitmap;
- var bmp: TBGRABitmap;
- begin
- bmp := nil;
- try
- bmp := LoadFlatImageUTF8(AFilename).bmp;
- except
- on ex:exception do ShowMessage(ex.Message);
- end;
- setlength(FTextures, length(FTextures)+1);
- FTextures[high(FTextures)].Name:= AName;
- FTextures[high(FTextures)].Texture:= bmp;
- result := bmp;
- end;
- var i: integer;
- begin
- for i := 0 to High(FTextures) do
- if FTextures[i].Name = AName then
- begin
- result := FTextures[i].Texture;
- exit;
- end;
- if ARelativePath and FileManager.FileExists(AppendPathDelim(TexturePath) + AName) then
- result := AddTexture(AppendPathDelim(TexturePath) + AName)
- else if not ARelativePath and FileManager.FileExists(AName) then
- result := AddTexture(AName)
- else
- result := AddTexture('');
- end;
- procedure TScene.ReleaseTextureReference(ref: IBGRAScanner);
- var i: integer;
- comp: IBGRAScanner;
- begin
- for i := 0 to high(FTextures) do
- begin
- comp := FTextures[i].Texture;
- if comp = ref then
- begin
- dec(FTextures[i].Usage);
- exit;
- end;
- end;
- end;
- procedure TScene.QueryTextureReference(ref: IBGRAScanner);
- var i: integer;
- comp: IBGRAScanner;
- begin
- for i := 0 to high(FTextures) do
- begin
- comp := FTextures[i].Texture;
- if comp = ref then
- begin
- inc(FTextures[i].Usage);
- exit;
- end;
- end;
- end;
- procedure TScene.Clear;
- begin
- NoTextures;
- FreeUnusedTextures;
- inherited Clear;
- end;
- procedure TScene.FreeUnusedTextures;
- var i,j,usage: integer;
- begin
- for i:= high(FTextures) downto 0 do
- begin
- if FTextures[i].Texture <> nil then
- begin
- usage := FTextures[i].Usage;
- if usage = 0 then
- begin
- FreeAndNil(FTextures[i].Texture);
- for j := i to high(FTextures)-1 do
- FTextures[j] := FTextures[j+1];
- setlength(FTextures,length(FTextures)-1);
- end;
- end;
- end;
- end;
- { TFObject3D }
- procedure TFObject3D.BGRAView3DRedraw(Sender: TObject;
- Bitmap: TBGRABitmap);
- begin
- DrawCheckers(Bitmap,rect(0,0,Bitmap.Width,Bitmap.Height));
- scene.Surface := Bitmap;
- scene.Render;
- end;
- procedure TFObject3D.BGRAView3DResize(Sender: TObject);
- begin
- BGRAView3D.DiscardBitmap;
- end;
- procedure TFObject3D.Button_LoadTexClick(Sender: TObject);
- begin
- if materialIndex <> -1 then
- begin
- OpenTextureDialog.InitialDir := Config.DefaultTextureDirectory;
- if OpenTextureDialog.Execute then
- begin
- DoLoadTexture(OpenTextureDialog.Filename);
- scene.FreeUnusedTextures;
- BGRAView3D.DiscardBitmap;
- end;
- end;
- end;
- procedure TFObject3D.Button_NoTexClick(Sender: TObject);
- begin
- if MaterialHasTexture then
- begin
- DoFreeTexture;
- scene.FreeUnusedTextures;
- BGRAView3D.DiscardBitmap;
- end;
- end;
- procedure TFObject3D.CheckBox_AntialiasingChange(Sender: TObject);
- begin
- if CheckBox_Antialiasing.Checked then
- scene.RenderingOptions.AntialiasingMode := am3dResample
- else
- scene.RenderingOptions.AntialiasingMode := am3dNone;
- BGRAView3D.RedrawBitmap;
- end;
- procedure TFObject3D.CheckBox_BifaceChange(Sender: TObject);
- begin
- if scene.Object3DCount > 0 then
- begin
- scene.Object3D[0].SetBiface(CheckBox_Biface.Checked);
- BGRAView3D.RedrawBitmap;
- end;
- end;
- procedure TFObject3D.BGRAView3DMouseDown(Sender: TObject; Button: TMouseButton;
- Shift: TShiftState; X, Y: Integer);
- begin
- if not rotating and (button = mbLeft) and (scene <> nil) then
- begin
- moving := true;
- moveOrigin := point(x,y);
- end else
- if not moving and (button = mbRight) and (scene <> nil) then
- begin
- rotating := true;
- previousAngle := ComputeAngle(x-BGRAView3D.Width/2,y-BGRAView3D.Height/2);
- end;
- end;
- procedure TFObject3D.BGRAKnob_ZoomValueChanged(Sender: TObject; Value: single);
- begin
- if scene.Object3DCount > 0 then
- begin
- scene.Object3D[0].MainPart.Scale(Value/previousZoom,false);
- BGRAView3D.DiscardBitmap;
- end;
- previousZoom := Value;
- end;
- procedure TFObject3D.BGRAView3DMouseMove(Sender: TObject; Shift: TShiftState;
- X, Y: Integer);
- var angle: single;
- begin
- if moving then
- begin
- if scene.Object3DCount > 0 then
- begin
- scene.RenderingOptions.AntialiasingResampleLevel := AntialiasingLevelWhenMoving;
- scene.Object3D[0].MainPart.RotateYDeg(-(X-moveOrigin.X),False);
- scene.Object3D[0].MainPart.RotateXDeg(Y-moveOrigin.Y,False);
- BGRAView3D.RedrawBitmap;
- end;
- moveOrigin := point(x,y);
- end else
- if rotating then
- begin
- angle := ComputeAngle(x-BGRAView3D.Width/2,y-BGRAView3D.Height/2);
- if scene.Object3DCount > 0 then
- begin
- scene.RenderingOptions.AntialiasingResampleLevel := AntialiasingLevelWhenMoving;
- scene.Object3D[0].MainPart.RotateZDeg(angle-previousAngle,False);
- BGRAView3D.RedrawBitmap;
- end;
- previousAngle := angle;
- end;
- end;
- procedure TFObject3D.BGRAView3DMouseUp(Sender: TObject; Button: TMouseButton;
- Shift: TShiftState; X, Y: Integer);
- begin
- if (button = mbLeft) and moving then
- begin
- moving := false;
- scene.RenderingOptions.AntialiasingResampleLevel := AntialiasingLevelWhenFixed;
- BGRAView3D.RedrawBitmap;
- end
- else if (button = mbRight) and rotating then
- begin
- rotating := false;
- scene.RenderingOptions.AntialiasingResampleLevel := AntialiasingLevelWhenFixed;
- BGRAView3D.RedrawBitmap;
- end;
- end;
- procedure TFObject3D.CheckBox_TextureInterpChange(Sender: TObject);
- begin
- scene.RenderingOptions.TextureInterpolation := checkbox_textureinterp.Checked;
- BGRAView3D.RedrawBitmap;
- end;
- procedure TFObject3D.ComboBox_NormalsChange(Sender: TObject);
- begin
- scene.DefaultLightingNormal := TLightingNormal3D(ComboBox_Normals.ItemIndex);
- BGRAView3D.RedrawBitmap;
- end;
- procedure TFObject3D.FormCreate(Sender: TObject);
- begin
- ScaleControl(Self,OriginalDPI);
- CheckOKCancelBtns(Button_OK,Button_Cancel);
- CheckSpinEdit(SpinEdit_ColorOpacity);
- CheckSpinEdit(SpinEdit_SpecularIndex);
- CheckSpinEdit(SpinEdit_Width);
- CheckSpinEdit(SpinEdit_Height);
- scene := TScene.Create;
- scene.DefaultLightingNormal := lnFaceVertexMix;
- with scene.RenderingOptions do
- begin
- LightingInterpolation := liAlwaysHighQuality;
- AntialiasingMode := am3dResample;
- AntialiasingResampleLevel := AntialiasingLevelWhenFixed;
- PerspectiveMode := pmZBuffer;
- TextureInterpolation := True;
- end;
- previousZoom := BGRAKnob_Zoom.Value;
- ComboBox_Normals.Items.Add('None');
- ComboBox_Normals.Items.Add('Flat faces');
- ComboBox_Normals.Items.Add('Rounded faces');
- ComboBox_Normals.Items.Add('Intermediate');
- materialIndex:= -1;
- InnerTabBottomPadding := PageControl1.Height - (GroupBox_SelectedMaterial.Top+GroupBox_SelectedMaterial.Height);
- UpdateTabSize;
- end;
- procedure TFObject3D.FormDestroy(Sender: TObject);
- begin
- scene.Free;
- end;
- procedure TFObject3D.FormKeyPress(Sender: TObject; var Key: char);
- begin
- CheckKey(Key);
- end;
- procedure TFObject3D.FormResize(Sender: TObject);
- begin
- UpdateTabSize;
- end;
- procedure TFObject3D.FormShow(Sender: TObject);
- var i: integer;
- begin
- ListBox_Materials.Clear;
- for i := 0 to scene.MaterialCount-1 do
- begin
- if scene.Material[i] = scene.DefaultMaterial then
- ListBox_Materials.Items.Add('<default>')
- else
- ListBox_Materials.Items.Add(scene.Material[i].Name);
- end;
- if ListBox_Materials.Items.Count > 0 then
- ListBox_Materials.ItemIndex := 0;
- if ComboBox_Normals.Items.Count > ord(scene.DefaultLightingNormal) then
- ComboBox_Normals.ItemIndex := ord(scene.DefaultLightingNormal);
- if scene.LightCount > 0 then
- lightIndex := 0
- else
- lightIndex := -1;
- UpdateLightList;
- UpdateSelectedLight;
- end;
- procedure TFObject3D.ListBox_LightsKeyPress(Sender: TObject; var Key: char);
- begin
- CheckKey(Key);
- end;
- procedure TFObject3D.ListBox_LightsSelectionChange(Sender: TObject;
- User: boolean);
- begin
- if User then UpdateSelectedLight;
- end;
- procedure TFObject3D.ListBox_MaterialsKeyPress(Sender: TObject; var Key: char);
- begin
- CheckKey(Key);
- end;
- procedure TFObject3D.ListBox_MaterialsSelectionChange(Sender: TObject;
- User: boolean);
- begin
- if ListBox_Materials.ItemIndex <> -1 then
- begin
- materialIndex := ListBox_Materials.ItemIndex;
- SpinEdit_SpecularIndex.Enabled := false;
- if scene.Material[materialIndex].SpecularOn then
- SpinEdit_SpecularIndex.Value := scene.Material[materialIndex].SpecularIndex
- else
- SpinEdit_SpecularIndex.Value := 0;
- SpinEdit_SpecularIndex.Enabled := true;
- Shape_MaterialColor.Brush.Color := BGRAToColor(scene.Material[materialIndex].SimpleColor);
- Shape_MaterialColor.Enabled := true;
- SpinEdit_ColorOpacity.Enabled := false;
- SpinEdit_ColorOpacity.Value := scene.Material[materialIndex].SimpleAlpha;
- SpinEdit_ColorOpacity.Enabled := true;
- end;
- end;
- procedure TFObject3D.PaintBox_LightPosMouseDown(Sender: TObject;
- Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
- begin
- if Button = mbLeft then SetLightPos(X/PaintBox_LightPos.Width,Y/PaintBox_LightPos.Height);
- end;
- procedure TFObject3D.PaintBox_LightPosMouseMove(Sender: TObject;
- Shift: TShiftState; X, Y: Integer);
- begin
- if ssLeft in Shift then
- SetLightPos(X/PaintBox_LightPos.Width,Y/PaintBox_LightPos.Height);
- end;
- procedure TFObject3D.PaintBox_LightPosMouseUp(Sender: TObject;
- Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
- begin
- if Button = mbLeft then
- begin
- scene.RenderingOptions.AntialiasingResampleLevel := AntialiasingLevelWhenFixed;
- BGRAView3D.RedrawBitmap;
- end;
- end;
- procedure TFObject3D.PaintBox_LightPosPaint(Sender: TObject);
- var x,y: integer;
- pt: TPointF;
- light: IBGRALight3D;
- begin
- if lightIndex <> -1 then
- begin
- light := scene.Light[lightIndex];
- if light.IsDirectional then
- begin
- with (light as IBGRADirectionalLight3D).Direction do
- pt := PointF(-x/2+0.5,-y/2+0.5);
- end else
- begin
- with (light as IBGRAPointLight3D).Vertex.GetSceneCoord do
- pt := PointF(x/2/PointLightDist+0.5,y/2/PointLightDist+0.5);
- end;
- with PaintBox_LightPos do
- begin
- x := round(pt.X*Width);
- y := round(pt.Y*Height);
- Canvas.Brush.Style := bsSolid;
- Canvas.Brush.Color:= clBtnFace;
- Canvas.Pen.Style := psSolid;
- Canvas.Pen.Color := clWindowText;
- Canvas.Rectangle(0,0,Width,Height);
- Canvas.Pen.Color := clBlack;
- Canvas.Brush.Style := bsSolid;
- Canvas.Brush.Color := clWhite;
- Canvas.Ellipse(x-3,y-3,x+4,y+4);
- end;
- end;
- end;
- procedure TFObject3D.Shape_LightColorMouseUp(Sender: TObject;
- Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
- begin
- if lightIndex <> -1 then
- begin
- ColorDialog1.Color := BGRAToColor(scene.Light[materialIndex].Color);
- if ColorDialog1.Execute then
- begin
- Shape_LightColor.Brush.Color := ColorDialog1.Color;
- scene.Light[lightIndex].Color := ColorToBGRA(ColorDialog1.Color);
- BGRAView3D.DiscardBitmap;
- end;
- end;
- end;
- procedure TFObject3D.Shape_MaterialColorMouseDown(Sender: TObject;
- Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
- begin
- if materialIndex <> -1 then
- begin
- ColorDialog1.Color := BGRAToColor(scene.Material[materialIndex].SimpleColor);
- if ColorDialog1.Execute then
- begin
- Shape_MaterialColor.Brush.Color := ColorDialog1.Color;
- scene.Material[materialIndex].SimpleColor := ColorToBGRA(ColorDialog1.Color,scene.Material[materialIndex].SimpleAlpha);
- BGRAView3D.DiscardBitmap;
- end;
- end;
- end;
- procedure TFObject3D.SpinEdit_ColorOpacityChange(Sender: TObject);
- begin
- if SpinEdit_ColorOpacity.Enabled and (materialIndex <> -1) then
- begin
- scene.Material[materialIndex].SimpleAlpha := SpinEdit_ColorOpacity.Value;
- BGRAView3D.DiscardBitmap;
- end;
- end;
- procedure TFObject3D.SpinEdit_ColorOpacityKeyPress(Sender: TObject;
- var Key: char);
- begin
- CheckKey(Key);
- end;
- procedure TFObject3D.SpinEdit_HeightKeyPress(Sender: TObject; var Key: char);
- begin
- CheckKey(Key);
- end;
- procedure TFObject3D.SpinEdit_SpecularIndexChange(Sender: TObject);
- begin
- if SpinEdit_SpecularIndex.Enabled and (materialIndex <> -1) then
- begin
- scene.Material[materialIndex].SpecularIndex := SpinEdit_SpecularIndex.Value;
- BGRAView3D.DiscardBitmap;
- end;
- end;
- procedure TFObject3D.SpinEdit_SpecularIndexKeyPress(Sender: TObject;
- var Key: char);
- begin
- CheckKey(Key);
- end;
- procedure TFObject3D.SpinEdit_WidthKeyPress(Sender: TObject; var Key: char);
- begin
- CheckKey(Key);
- end;
- procedure TFObject3D.ToolAddDirectionalClick(Sender: TObject);
- begin
- if scene.LightCount < 10 then
- begin
- scene.AddDirectionalLight(Point3D(1,1,1),BGRAWhite,-0.5); //add a directional light from top-left
- lightIndex:= scene.LightCount-1;
- UpdateLightList;
- UpdateSelectedLight;
- BGRAView3D.DiscardBitmap;
- end;
- end;
- procedure TFObject3D.ToolPointLightClick(Sender: TObject);
- const OneOverSqrt3 = 0.57735026918962576450914878050196;
- begin
- if scene.LightCount < 10 then
- begin
- scene.AddPointLight(scene.CreateObject.MainPart.Add(-OneOverSqrt3*PointLightDist,-OneOverSqrt3*PointLightDist,-OneOverSqrt3*PointLightDist),PointLightDist,BGRAWhite);
- lightIndex:= scene.LightCount-1;
- UpdateLightList;
- UpdateSelectedLight;
- BGRAView3D.DiscardBitmap;
- end;
- end;
- procedure TFObject3D.ToolRemoveSelectedLightClick(Sender: TObject);
- begin
- if lightIndex <> -1 then
- begin
- scene.RemoveLight(scene.Light[lightIndex]);
- if lightIndex >= scene.LightCount then dec(lightIndex);
- UpdateLightList;
- UpdateSelectedLight;
- BGRAView3D.DiscardBitmap;
- end;
- end;
- procedure TFObject3D.UpdateTabSize;
- begin
- GroupBox_SelectedMaterial.Top := PageControl1.Height - InnerTabBottomPadding - GroupBox_SelectedMaterial.Height;
- ListBox_Materials.Height := GroupBox_SelectedMaterial.Top-2-ListBox_Materials.Top;
- GroupBox_SelectedLight.Top := PageControl1.Height - InnerTabBottomPadding - GroupBox_SelectedLight.Height;
- ListBox_Lights.Height := GroupBox_SelectedLight.Top-2-ListBox_Lights.Top;
- end;
- procedure TFObject3D.CheckKey(var Key: char);
- begin
- if (Key = '+') or (Key = '-') then
- begin
- if Key = '+' then
- scene.Object3D[0].MainPart.Scale(1.1,false) else
- scene.Object3D[0].MainPart.Scale(1/1.1,false);
- Key := #0;
- BGRAView3D.DiscardBitmap;
- end;
- end;
- procedure TFObject3D.DoLoadTexture(AFilename: string);
- var
- mat: IBGRAMaterial3D;
- i,j: integer;
- bmp: TBGRABitmap;
- begin
- if materialIndex = -1 then exit;
- Config.SetDefaultTextureDirectory(ExtractFilePath(AFilename));
- mat := scene.Material[materialIndex];
- bmp := scene.FetchTextureAsBitmap(OpenTextureDialog.FileName,False);
- scene.ReleaseTextureReference(mat.Texture);
- scene.QueryTextureReference(bmp);
- mat.Texture := bmp;
- mat.TextureZoom := PointF(1,1);
- mat.SimpleColor := BGRAWhite;
- Shape_MaterialColor.Brush.Color := BGRAToColor(mat.SimpleColor);
- for i := 0 to scene.Object3DCount-1 do
- with scene.Object3D[i] do
- begin
- for j := 0 to FaceCount-1 do
- if Face[j].Material = mat then
- self.scene.ComputeTexCoord(Face[j],bmp.Width,bmp.Height);
- end;
- end;
- procedure TFObject3D.DoFreeTexture;
- var
- mat: IBGRAMaterial3D;
- begin
- if materialIndex = -1 then exit;
- mat := scene.Material[materialIndex];
- scene.ReleaseTextureReference(mat.Texture);
- mat.Texture := nil;
- mat.SimpleColor := scene.UnknownColor;
- Shape_MaterialColor.Brush.Color := BGRAToColor(mat.SimpleColor);
- end;
- function TFObject3D.MaterialHasTexture: boolean;
- begin
- if materialIndex <> -1 then
- result := scene.Material[materialIndex].Texture <> nil
- else
- result := false;
- end;
- procedure TFObject3D.UpdateLightList;
- var
- i: Integer;
- begin
- ListBox_Lights.Clear;
- for i := 0 to scene.LightCount-1 do
- begin
- if scene.Light[i].IsDirectional then
- ListBox_Lights.Items.Add('#'+inttostr(i+1)+' Directional')
- else
- ListBox_Lights.Items.Add('#'+inttostr(i+1)+' Point');
- end;
- ListBox_Lights.ItemIndex := lightIndex;
- end;
- procedure TFObject3D.UpdateSelectedLight;
- begin
- lightIndex:= ListBox_Lights.ItemIndex;
- if lightIndex <> -1 then
- begin
- GroupBox_SelectedLight.Enabled := true;
- Shape_LightColor.Brush.Color := BGRAToColor(scene.Light[lightIndex].Color);
- Shape_LightColor.Enabled := true;
- PaintBox_LightPos.Repaint;
- end else
- begin
- GroupBox_SelectedLight.Enabled := false;
- end;
- end;
- procedure TFObject3D.SetLightPos(x, y: single);
- var light: IBGRALight3D;
- xy,z: single;
- begin
- if lightIndex <> -1 then
- begin
- light := scene.Light[lightIndex];
- x := (x-0.5)*2;
- y := (y-0.5)*2;
- xy := sqrt(sqr(x)+sqr(y));
- if xy >= 1 then
- begin
- x /= xy;
- y /= xy;
- z := 0;
- end else
- z := sqrt(1-sqr(xy));
- if light.IsDirectional then
- (light as IBGRADirectionalLight3D).Direction := Point3D(-x,-y,z)
- else
- (light as IBGRAPointLight3D).Vertex.SceneCoord := Point3D(x*PointLightDist,y*PointLightDist,-z*PointLightDist);
- PaintBox_LightPos.Repaint;
- scene.RenderingOptions.AntialiasingResampleLevel := AntialiasingLevelWhenMoving;
- BGRAView3D.DiscardBitmap;
- end;
- end;
- function DoLoadObject(scene: TScene; filenameUTF8: string): boolean;
- var
- obj: IBGRAObject3D;
- r: single;
- matFile: string;
- i: integer;
- s: TStream;
- begin
- scene.TexturePath := ExtractFilePath(filenameUTF8);
- result := false;
- matFile := ChangeFileExt(filenameUTF8,'.mtl');
- if FileManager.FileExists(matFile) then
- begin
- s := FileManager.CreateFileStream(matFile, fmOpenRead);
- try
- scene.LoadMaterialsFromStream(s);
- finally
- s.Free;
- end;
- for i := 0 to scene.MaterialCount-1 do
- scene.QueryTextureReference(scene.Material[i].Texture);
- end;
- s := FileManager.CreateFileStream(filenameUTF8, fmOpenRead);
- try
- obj := scene.LoadObjectFromStream(s);
- finally
- s.Free;
- end;
- if obj <> nil then
- begin
- scene.DefaultLightingNormal := obj.LightingNormal;
- obj.ParentLighting := true; //set for the whole scene with the dialog box
- with obj do
- begin
- with MainPart.BoundingBox do
- MainPart.Translate((min+max)*(-1/2), False);
- r := MainPart.Radius;
- if r <> 0 then MainPart.Scale(50/r, False);
- MainPart.RotateXDeg(180-20, False);
- MainPart.RotateYDeg(-20, False);
- end;
- with scene do
- begin
- //set ambiant lightness to dark (1 is normal lightness)
- AmbiantLightness := 0.5;
- AddDirectionalLight(Point3D(1,1,1),BGRAWhite,-0.5); //add a directional light from top-left
- end;
- result := true;
- end;
- end;
- var
- f: TFObject3D;
- function ShowObject3DDlg(Instance: TLazPaintCustomInstance; filenameUTF8: string;
- maxWidth, maxHeight: integer): TBGRABitmap;
- begin
- if f = nil then
- begin
- f:= TFObject3D.Create(nil);
- f.Config := Instance.Config;
- end;
- result := nil;
- try
- if DoLoadObject(f.scene,filenameUTF8) then
- begin
- f.BGRAView3D.DiscardBitmap;
- f.SpinEdit_Width.MaxValue := maxWidth;
- f.SpinEdit_Width.Value := maxWidth;
- f.SpinEdit_Height.MaxValue := maxHeight;
- f.SpinEdit_Height.Value := maxHeight;
- if f.ShowModal = mrOK then
- begin
- result := TBGRABitmap.Create(f.SpinEdit_Width.Value,f.SpinEdit_Height.Value);
- f.scene.Surface := result;
- f.scene.RenderingOptions.AntialiasingResampleLevel := 5;
- f.scene.Render;
- end;
- end;
- except
- on ex:Exception do
- Instance.ShowError('ShowObject3DDlg',ex.Message);
- end;
- f.scene.Clear;
- end;
- {$R *.lfm}
- finalization
- FreeAndNil(f);
- end.
|