uobject3d.pas 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951
  1. // SPDX-License-Identifier: GPL-3.0-only
  2. unit UObject3D;
  3. {$mode objfpc}{$H+}
  4. interface
  5. uses
  6. Classes, SysUtils, LResources, Forms, Controls, Graphics, Dialogs,
  7. StdCtrls, Spin, ExtCtrls, ComCtrls, BGRAVirtualScreen, BGRAKnob,
  8. BGRAImageList, BGRABitmap, BGRAScene3D, LazPaintType, BGRABitmapTypes,
  9. UConfig;
  10. const
  11. AntialiasingLevelWhenFixed = 2;
  12. AntialiasingLevelWhenMoving = 1;
  13. type
  14. { TScene }
  15. TScene = class(TBGRAScene3D)
  16. private
  17. FTextures: array of record
  18. Name: string;
  19. Texture: TBGRABitmap;
  20. Usage: integer;
  21. end;
  22. procedure NoTextures;
  23. public
  24. TexturePath: string;
  25. procedure ComputeTexCoord(AFace: IBGRAFace3D; AWidth, AHeight: integer);
  26. function FetchTexture(AName: string; out texSize: TPointF): IBGRAScanner; override;
  27. function FetchTextureAsBitmap(AName: string; ARelativePath: boolean): TBGRABitmap;
  28. procedure ReleaseTextureReference(ref: IBGRAScanner);
  29. procedure QueryTextureReference(ref: IBGRAScanner);
  30. procedure Clear; override;
  31. procedure FreeUnusedTextures;
  32. end;
  33. { TFObject3D }
  34. TFObject3D = class(TForm)
  35. BGRAImageList1: TBGRAImageList;
  36. BGRAKnob_Zoom: TBGRAKnob;
  37. BGRAView3D: TBGRAVirtualScreen;
  38. Button_LoadTex: TButton;
  39. Button_NoTex: TButton;
  40. Button_Cancel: TButton;
  41. Button_OK: TButton;
  42. CheckBox_Antialiasing: TCheckBox;
  43. CheckBox_Biface: TCheckBox;
  44. CheckBox_TextureInterp: TCheckBox;
  45. ColorDialog1: TColorDialog;
  46. ComboBox_Normals: TComboBox;
  47. GroupBox_SelectedMaterial: TGroupBox;
  48. GroupBox_SelectedLight: TGroupBox;
  49. Label_Color1: TLabel;
  50. Label_Materials: TLabel;
  51. Label_Lights: TLabel;
  52. Label_Zoom: TLabel;
  53. Label_LightingNormals: TLabel;
  54. Label_SpecularIndex: TLabel;
  55. Label_Color: TLabel;
  56. Label_Opacity: TLabel;
  57. Label_Width: TLabel;
  58. Label_Height: TLabel;
  59. ListBox_Materials: TListBox;
  60. ListBox_Lights: TListBox;
  61. OpenTextureDialog: TOpenDialog;
  62. PageControl1: TPageControl;
  63. PaintBox_LightPos: TPaintBox;
  64. Shape_MaterialColor: TShape;
  65. Shape_LightColor: TShape;
  66. SpinEdit_ColorOpacity: TSpinEdit;
  67. SpinEdit_Height: TSpinEdit;
  68. SpinEdit_SpecularIndex: TSpinEdit;
  69. Rendering: TTabSheet;
  70. Materials: TTabSheet;
  71. SpinEdit_Width: TSpinEdit;
  72. Lights: TTabSheet;
  73. ToolBar1: TToolBar;
  74. ToolAddDirectional: TToolButton;
  75. ToolPointLight: TToolButton;
  76. ToolRemoveSelectedLight: TToolButton;
  77. procedure BGRAKnob_ZoomValueChanged(Sender: TObject; Value: single);
  78. procedure BGRAView3DMouseDown(Sender: TObject; Button: TMouseButton;
  79. {%H-}Shift: TShiftState; X, Y: Integer);
  80. procedure BGRAView3DMouseMove(Sender: TObject; {%H-}Shift: TShiftState; X,
  81. Y: Integer);
  82. procedure BGRAView3DMouseUp(Sender: TObject; Button: TMouseButton;
  83. {%H-}Shift: TShiftState; {%H-}X, {%H-}Y: Integer);
  84. procedure BGRAView3DRedraw(Sender: TObject; Bitmap: TBGRABitmap);
  85. procedure BGRAView3DResize(Sender: TObject);
  86. procedure Button_LoadTexClick(Sender: TObject);
  87. procedure Button_NoTexClick(Sender: TObject);
  88. procedure CheckBox_AntialiasingChange(Sender: TObject);
  89. procedure CheckBox_BifaceChange(Sender: TObject);
  90. procedure CheckBox_TextureInterpChange(Sender: TObject);
  91. procedure ComboBox_NormalsChange(Sender: TObject);
  92. procedure FormCreate(Sender: TObject);
  93. procedure FormDestroy(Sender: TObject);
  94. procedure FormKeyPress(Sender: TObject; var Key: char);
  95. procedure FormResize(Sender: TObject);
  96. procedure FormShow(Sender: TObject);
  97. procedure ListBox_LightsKeyPress(Sender: TObject; var Key: char);
  98. procedure ListBox_LightsSelectionChange(Sender: TObject; User: boolean);
  99. procedure ListBox_MaterialsKeyPress(Sender: TObject; var Key: char);
  100. procedure ListBox_MaterialsSelectionChange(Sender: TObject; {%H-}User: boolean);
  101. procedure PaintBox_LightPosMouseDown(Sender: TObject; {%H-}Button: TMouseButton;
  102. {%H-}Shift: TShiftState; X, Y: Integer);
  103. procedure PaintBox_LightPosMouseMove(Sender: TObject; Shift: TShiftState;
  104. X, Y: Integer);
  105. procedure PaintBox_LightPosMouseUp(Sender: TObject; Button: TMouseButton;
  106. {%H-}Shift: TShiftState; {%H-}X, {%H-}Y: Integer);
  107. procedure PaintBox_LightPosPaint(Sender: TObject);
  108. procedure Shape_LightColorMouseUp(Sender: TObject; {%H-}Button: TMouseButton;
  109. {%H-}Shift: TShiftState; {%H-}X, {%H-}Y: Integer);
  110. procedure Shape_MaterialColorMouseUp(Sender: TObject;
  111. {%H-}Button: TMouseButton; {%H-}Shift: TShiftState; {%H-}X, {%H-}Y: Integer);
  112. procedure SpinEdit_ColorOpacityChange(Sender: TObject);
  113. procedure SpinEdit_ColorOpacityKeyPress(Sender: TObject; var Key: char);
  114. procedure SpinEdit_HeightKeyPress(Sender: TObject; var Key: char);
  115. procedure SpinEdit_SpecularIndexChange(Sender: TObject);
  116. procedure SpinEdit_SpecularIndexKeyPress(Sender: TObject; var Key: char);
  117. procedure SpinEdit_WidthKeyPress(Sender: TObject; var Key: char);
  118. procedure ToolAddDirectionalClick(Sender: TObject);
  119. procedure ToolPointLightClick(Sender: TObject);
  120. procedure ToolRemoveSelectedLightClick(Sender: TObject);
  121. private
  122. { private declarations }
  123. procedure UpdateTabSize;
  124. procedure CheckKey(var Key: char);
  125. procedure DoLoadTexture(AFilename: string);
  126. procedure DoFreeTexture;
  127. function MaterialHasTexture: boolean;
  128. procedure UpdateLightList;
  129. procedure UpdateSelectedLight;
  130. procedure SetLightPos(x,y: single);
  131. public
  132. { public declarations }
  133. scene : TScene;
  134. moving,rotating: boolean;
  135. moveOrigin: TPoint;
  136. previousAngle: single;
  137. previousZoom: single;
  138. materialIndex, lightIndex: integer;
  139. InnerTabBottomPadding: integer;
  140. Config: TLazPaintConfig;
  141. end;
  142. function ShowObject3DDlg({%H-}Instance: TLazPaintCustomInstance; filenameUTF8: string; maxWidth, maxHeight: integer): TBGRABitmap;
  143. implementation
  144. uses LazFileUtils, ugraph, LCScaleDPI, umac, ULoadImage, UFileSystem;
  145. const PointLightDist = 80;
  146. { TScene }
  147. procedure TScene.NoTextures;
  148. var i: integer;
  149. begin
  150. for i := 0 to MaterialCount-1 do
  151. begin
  152. ReleaseTextureReference(Material[i].Texture);
  153. Material[i].Texture := nil;
  154. end;
  155. end;
  156. procedure TScene.ComputeTexCoord(AFace: IBGRAFace3D; AWidth,AHeight: integer);
  157. var
  158. j: integer;
  159. p1,p2,p3,u,v: TPoint3d;
  160. min,max,pt: TPointF;
  161. factor: single;
  162. begin
  163. if AFace.VertexCount < 3 then exit;
  164. j := 0;
  165. p1 := AFace.Vertex[j].GetSceneCoord;
  166. repeat
  167. inc(j);
  168. if j >= AFace.VertexCount then exit;
  169. p2 := AFace.Vertex[j].GetSceneCoord;
  170. u := p2-p1;
  171. until u*u <> 0;
  172. Normalize3D(u);
  173. repeat
  174. inc(j);
  175. if j >= AFace.VertexCount then exit;
  176. p3 := AFace.Vertex[j].GetSceneCoord;
  177. v := p3-p2;
  178. v := v - u*(u*v);
  179. until v*v <> 0;
  180. Normalize3D(v);
  181. with AFace.Vertex[0] do
  182. begin
  183. pt := PointF((GetSceneCoord-p1)*u,(GetSceneCoord-p1)*v);
  184. min := pt;
  185. max := pt
  186. end;
  187. for j := 1 to AFace.VertexCount-1 do
  188. with AFace.Vertex[j] do
  189. begin
  190. pt := PointF((GetSceneCoord-p1)*u,(GetSceneCoord-p1)*v);
  191. if pt.x < min.x then min.x := pt.x else
  192. if pt.x > max.x then max.x := pt.x;
  193. if pt.y < min.y then min.y := pt.y else
  194. if pt.y > max.y then max.y := pt.y;
  195. end;
  196. if min.x = max.x then max.x := min.x+1;
  197. if min.y = max.y then max.y := min.y+1;
  198. factor := AWidth/(max.x-min.x);
  199. if AHeight/(max.y-min.y) < factor then factor := AHeight/(max.y-min.y);
  200. for j := 0 to AFace.VertexCount-1 do
  201. with AFace.Vertex[j] do
  202. begin
  203. pt := PointF((GetSceneCoord-p1)*u,(GetSceneCoord-p1)*v);
  204. pt := PointF((pt.x-min.x)*factor,(pt.y-min.y)*factor);
  205. AFace.TexCoord[j] := pt;
  206. end;
  207. end;
  208. function TScene.FetchTexture(AName: string; out texSize: TPointF): IBGRAScanner;
  209. var bmp: TBGRABitmap;
  210. begin
  211. bmp := FetchTextureAsBitmap(AName, True);
  212. result := bmp;
  213. texSize := PointF(bmp.Width,bmp.Height);
  214. end;
  215. function TScene.FetchTextureAsBitmap(AName: string; ARelativePath: boolean): TBGRABitmap;
  216. function AddTexture(AFilename: string): TBGRABitmap;
  217. var bmp: TBGRABitmap;
  218. begin
  219. bmp := nil;
  220. try
  221. bmp := LoadFlatImageUTF8(AFilename).bmp;
  222. except
  223. on ex:exception do ShowMessage(ex.Message);
  224. end;
  225. setlength(FTextures, length(FTextures)+1);
  226. FTextures[high(FTextures)].Name:= AName;
  227. FTextures[high(FTextures)].Texture:= bmp;
  228. result := bmp;
  229. end;
  230. var i: integer;
  231. begin
  232. for i := 0 to High(FTextures) do
  233. if FTextures[i].Name = AName then
  234. begin
  235. result := FTextures[i].Texture;
  236. exit;
  237. end;
  238. if ARelativePath and FileManager.FileExists(AppendPathDelim(TexturePath) + AName) then
  239. result := AddTexture(AppendPathDelim(TexturePath) + AName)
  240. else if not ARelativePath and FileManager.FileExists(AName) then
  241. result := AddTexture(AName)
  242. else
  243. result := AddTexture('');
  244. end;
  245. procedure TScene.ReleaseTextureReference(ref: IBGRAScanner);
  246. var i: integer;
  247. comp: IBGRAScanner;
  248. begin
  249. for i := 0 to high(FTextures) do
  250. begin
  251. comp := FTextures[i].Texture;
  252. if comp = ref then
  253. begin
  254. dec(FTextures[i].Usage);
  255. exit;
  256. end;
  257. end;
  258. end;
  259. procedure TScene.QueryTextureReference(ref: IBGRAScanner);
  260. var i: integer;
  261. comp: IBGRAScanner;
  262. begin
  263. for i := 0 to high(FTextures) do
  264. begin
  265. comp := FTextures[i].Texture;
  266. if comp = ref then
  267. begin
  268. inc(FTextures[i].Usage);
  269. exit;
  270. end;
  271. end;
  272. end;
  273. procedure TScene.Clear;
  274. begin
  275. NoTextures;
  276. FreeUnusedTextures;
  277. inherited Clear;
  278. end;
  279. procedure TScene.FreeUnusedTextures;
  280. var i,j,usage: integer;
  281. begin
  282. for i:= high(FTextures) downto 0 do
  283. begin
  284. if FTextures[i].Texture <> nil then
  285. begin
  286. usage := FTextures[i].Usage;
  287. if usage = 0 then
  288. begin
  289. FreeAndNil(FTextures[i].Texture);
  290. for j := i to high(FTextures)-1 do
  291. FTextures[j] := FTextures[j+1];
  292. setlength(FTextures,length(FTextures)-1);
  293. end;
  294. end;
  295. end;
  296. end;
  297. { TFObject3D }
  298. procedure TFObject3D.BGRAView3DRedraw(Sender: TObject;
  299. Bitmap: TBGRABitmap);
  300. begin
  301. DrawCheckers(Bitmap,rect(0,0,Bitmap.Width,Bitmap.Height),CanvasScale);
  302. scene.Surface := Bitmap;
  303. scene.Render;
  304. end;
  305. procedure TFObject3D.BGRAView3DResize(Sender: TObject);
  306. begin
  307. BGRAView3D.DiscardBitmap;
  308. end;
  309. procedure TFObject3D.Button_LoadTexClick(Sender: TObject);
  310. begin
  311. if materialIndex <> -1 then
  312. begin
  313. OpenTextureDialog.InitialDir := Config.DefaultTextureDirectory;
  314. if OpenTextureDialog.Execute then
  315. begin
  316. DoLoadTexture(OpenTextureDialog.Filename);
  317. scene.FreeUnusedTextures;
  318. BGRAView3D.DiscardBitmap;
  319. end;
  320. end;
  321. end;
  322. procedure TFObject3D.Button_NoTexClick(Sender: TObject);
  323. begin
  324. if MaterialHasTexture then
  325. begin
  326. DoFreeTexture;
  327. scene.FreeUnusedTextures;
  328. BGRAView3D.DiscardBitmap;
  329. end;
  330. end;
  331. procedure TFObject3D.CheckBox_AntialiasingChange(Sender: TObject);
  332. begin
  333. if CheckBox_Antialiasing.Checked then
  334. scene.RenderingOptions.AntialiasingMode := am3dResample
  335. else
  336. scene.RenderingOptions.AntialiasingMode := am3dNone;
  337. BGRAView3D.RedrawBitmap;
  338. end;
  339. procedure TFObject3D.CheckBox_BifaceChange(Sender: TObject);
  340. begin
  341. if scene.Object3DCount > 0 then
  342. begin
  343. scene.Object3D[0].SetBiface(CheckBox_Biface.Checked);
  344. BGRAView3D.RedrawBitmap;
  345. end;
  346. end;
  347. procedure TFObject3D.BGRAView3DMouseDown(Sender: TObject; Button: TMouseButton;
  348. Shift: TShiftState; X, Y: Integer);
  349. begin
  350. if not rotating and (button = mbLeft) and (scene <> nil) then
  351. begin
  352. moving := true;
  353. moveOrigin := point(x,y);
  354. end else
  355. if not moving and (button = mbRight) and (scene <> nil) then
  356. begin
  357. rotating := true;
  358. previousAngle := ComputeAngle(x-BGRAView3D.Width/2,y-BGRAView3D.Height/2);
  359. end;
  360. end;
  361. procedure TFObject3D.BGRAKnob_ZoomValueChanged(Sender: TObject; Value: single);
  362. begin
  363. if scene.Object3DCount > 0 then
  364. begin
  365. scene.Object3D[0].MainPart.Scale(Value/previousZoom,false);
  366. BGRAView3D.DiscardBitmap;
  367. end;
  368. previousZoom := Value;
  369. end;
  370. procedure TFObject3D.BGRAView3DMouseMove(Sender: TObject; Shift: TShiftState;
  371. X, Y: Integer);
  372. var angle: single;
  373. begin
  374. if moving then
  375. begin
  376. if scene.Object3DCount > 0 then
  377. begin
  378. scene.RenderingOptions.AntialiasingResampleLevel := AntialiasingLevelWhenMoving;
  379. scene.Object3D[0].MainPart.RotateYDeg(-(X-moveOrigin.X),False);
  380. scene.Object3D[0].MainPart.RotateXDeg(Y-moveOrigin.Y,False);
  381. BGRAView3D.RedrawBitmap;
  382. end;
  383. moveOrigin := point(x,y);
  384. end else
  385. if rotating then
  386. begin
  387. angle := ComputeAngle(x-BGRAView3D.Width/2,y-BGRAView3D.Height/2);
  388. if scene.Object3DCount > 0 then
  389. begin
  390. scene.RenderingOptions.AntialiasingResampleLevel := AntialiasingLevelWhenMoving;
  391. scene.Object3D[0].MainPart.RotateZDeg(angle-previousAngle,False);
  392. BGRAView3D.RedrawBitmap;
  393. end;
  394. previousAngle := angle;
  395. end;
  396. end;
  397. procedure TFObject3D.BGRAView3DMouseUp(Sender: TObject; Button: TMouseButton;
  398. Shift: TShiftState; X, Y: Integer);
  399. begin
  400. if (button = mbLeft) and moving then
  401. begin
  402. moving := false;
  403. scene.RenderingOptions.AntialiasingResampleLevel := AntialiasingLevelWhenFixed;
  404. BGRAView3D.RedrawBitmap;
  405. end
  406. else if (button = mbRight) and rotating then
  407. begin
  408. rotating := false;
  409. scene.RenderingOptions.AntialiasingResampleLevel := AntialiasingLevelWhenFixed;
  410. BGRAView3D.RedrawBitmap;
  411. end;
  412. end;
  413. procedure TFObject3D.CheckBox_TextureInterpChange(Sender: TObject);
  414. begin
  415. scene.RenderingOptions.TextureInterpolation := checkbox_textureinterp.Checked;
  416. BGRAView3D.RedrawBitmap;
  417. end;
  418. procedure TFObject3D.ComboBox_NormalsChange(Sender: TObject);
  419. begin
  420. scene.DefaultLightingNormal := TLightingNormal3D(ComboBox_Normals.ItemIndex);
  421. BGRAView3D.RedrawBitmap;
  422. end;
  423. procedure TFObject3D.FormCreate(Sender: TObject);
  424. begin
  425. ScaleControl(Self,OriginalDPI);
  426. BGRAView3D.BitmapAutoScale:= false;
  427. CheckOKCancelBtns(Button_OK,Button_Cancel);
  428. CheckSpinEdit(SpinEdit_ColorOpacity);
  429. CheckSpinEdit(SpinEdit_SpecularIndex);
  430. CheckSpinEdit(SpinEdit_Width);
  431. CheckSpinEdit(SpinEdit_Height);
  432. scene := TScene.Create;
  433. scene.DefaultLightingNormal := lnFaceVertexMix;
  434. with scene.RenderingOptions do
  435. begin
  436. LightingInterpolation := liAlwaysHighQuality;
  437. AntialiasingMode := am3dResample;
  438. AntialiasingResampleLevel := AntialiasingLevelWhenFixed;
  439. PerspectiveMode := pmZBuffer;
  440. TextureInterpolation := True;
  441. end;
  442. previousZoom := BGRAKnob_Zoom.Value;
  443. ComboBox_Normals.Items.Add('None');
  444. ComboBox_Normals.Items.Add('Flat faces');
  445. ComboBox_Normals.Items.Add('Rounded faces');
  446. ComboBox_Normals.Items.Add('Intermediate');
  447. materialIndex:= -1;
  448. InnerTabBottomPadding := PageControl1.Height - (GroupBox_SelectedMaterial.Top+GroupBox_SelectedMaterial.Height);
  449. UpdateTabSize;
  450. end;
  451. procedure TFObject3D.FormDestroy(Sender: TObject);
  452. begin
  453. scene.Free;
  454. end;
  455. procedure TFObject3D.FormKeyPress(Sender: TObject; var Key: char);
  456. begin
  457. CheckKey(Key);
  458. end;
  459. procedure TFObject3D.FormResize(Sender: TObject);
  460. begin
  461. UpdateTabSize;
  462. end;
  463. procedure TFObject3D.FormShow(Sender: TObject);
  464. var i: integer;
  465. begin
  466. ListBox_Materials.Clear;
  467. for i := 0 to scene.MaterialCount-1 do
  468. begin
  469. if scene.Material[i] = scene.DefaultMaterial then
  470. ListBox_Materials.Items.Add('<default>')
  471. else
  472. ListBox_Materials.Items.Add(scene.Material[i].Name);
  473. end;
  474. if ListBox_Materials.Items.Count > 0 then
  475. ListBox_Materials.ItemIndex := 0;
  476. if ComboBox_Normals.Items.Count > ord(scene.DefaultLightingNormal) then
  477. ComboBox_Normals.ItemIndex := ord(scene.DefaultLightingNormal);
  478. if scene.LightCount > 0 then
  479. lightIndex := 0
  480. else
  481. lightIndex := -1;
  482. UpdateLightList;
  483. UpdateSelectedLight;
  484. end;
  485. procedure TFObject3D.ListBox_LightsKeyPress(Sender: TObject; var Key: char);
  486. begin
  487. CheckKey(Key);
  488. end;
  489. procedure TFObject3D.ListBox_LightsSelectionChange(Sender: TObject;
  490. User: boolean);
  491. begin
  492. if User then UpdateSelectedLight;
  493. end;
  494. procedure TFObject3D.ListBox_MaterialsKeyPress(Sender: TObject; var Key: char);
  495. begin
  496. CheckKey(Key);
  497. end;
  498. procedure TFObject3D.ListBox_MaterialsSelectionChange(Sender: TObject;
  499. User: boolean);
  500. begin
  501. if ListBox_Materials.ItemIndex <> -1 then
  502. begin
  503. materialIndex := ListBox_Materials.ItemIndex;
  504. SpinEdit_SpecularIndex.Enabled := false;
  505. if scene.Material[materialIndex].SpecularOn then
  506. SpinEdit_SpecularIndex.Value := scene.Material[materialIndex].SpecularIndex
  507. else
  508. SpinEdit_SpecularIndex.Value := 0;
  509. SpinEdit_SpecularIndex.Enabled := true;
  510. Shape_MaterialColor.Brush.Color := BGRAToColor(scene.Material[materialIndex].SimpleColor);
  511. Shape_MaterialColor.Enabled := true;
  512. SpinEdit_ColorOpacity.Enabled := false;
  513. SpinEdit_ColorOpacity.Value := scene.Material[materialIndex].SimpleAlpha;
  514. SpinEdit_ColorOpacity.Enabled := true;
  515. end;
  516. end;
  517. procedure TFObject3D.PaintBox_LightPosMouseDown(Sender: TObject;
  518. Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
  519. begin
  520. if Button = mbLeft then SetLightPos(X/PaintBox_LightPos.Width,Y/PaintBox_LightPos.Height);
  521. end;
  522. procedure TFObject3D.PaintBox_LightPosMouseMove(Sender: TObject;
  523. Shift: TShiftState; X, Y: Integer);
  524. begin
  525. if ssLeft in Shift then
  526. SetLightPos(X/PaintBox_LightPos.Width,Y/PaintBox_LightPos.Height);
  527. end;
  528. procedure TFObject3D.PaintBox_LightPosMouseUp(Sender: TObject;
  529. Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
  530. begin
  531. if Button = mbLeft then
  532. begin
  533. scene.RenderingOptions.AntialiasingResampleLevel := AntialiasingLevelWhenFixed;
  534. BGRAView3D.RedrawBitmap;
  535. end;
  536. end;
  537. procedure TFObject3D.PaintBox_LightPosPaint(Sender: TObject);
  538. var x,y: integer;
  539. pt: TPointF;
  540. light: IBGRALight3D;
  541. begin
  542. if lightIndex <> -1 then
  543. begin
  544. light := scene.Light[lightIndex];
  545. if light.IsDirectional then
  546. begin
  547. with (light as IBGRADirectionalLight3D).Direction do
  548. pt := PointF(-x/2+0.5,-y/2+0.5);
  549. end else
  550. begin
  551. with (light as IBGRAPointLight3D).Vertex.GetSceneCoord do
  552. pt := PointF(x/2/PointLightDist+0.5,y/2/PointLightDist+0.5);
  553. end;
  554. with PaintBox_LightPos do
  555. begin
  556. x := round(pt.X*Width);
  557. y := round(pt.Y*Height);
  558. Canvas.Brush.Style := bsSolid;
  559. Canvas.Brush.Color:= clForm;
  560. Canvas.Pen.Style := psSolid;
  561. Canvas.Pen.Color := clWindowText;
  562. Canvas.Rectangle(0,0,Width,Height);
  563. Canvas.Pen.Color := clBlack;
  564. Canvas.Brush.Style := bsSolid;
  565. Canvas.Brush.Color := clWhite;
  566. Canvas.Ellipse(x-3,y-3,x+4,y+4);
  567. end;
  568. end;
  569. end;
  570. procedure TFObject3D.Shape_LightColorMouseUp(Sender: TObject;
  571. Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
  572. begin
  573. if lightIndex <> -1 then
  574. begin
  575. ColorDialog1.Color := BGRAToColor(scene.Light[materialIndex].Color);
  576. if ColorDialog1.Execute then
  577. begin
  578. Shape_LightColor.Brush.Color := ColorDialog1.Color;
  579. scene.Light[lightIndex].Color := ColorToBGRA(ColorDialog1.Color);
  580. BGRAView3D.DiscardBitmap;
  581. end;
  582. end;
  583. end;
  584. procedure TFObject3D.Shape_MaterialColorMouseUp(Sender: TObject;
  585. Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
  586. begin
  587. if materialIndex <> -1 then
  588. begin
  589. ColorDialog1.Color := BGRAToColor(scene.Material[materialIndex].SimpleColor);
  590. if ColorDialog1.Execute then
  591. begin
  592. Shape_MaterialColor.Brush.Color := ColorDialog1.Color;
  593. scene.Material[materialIndex].SimpleColor := ColorToBGRA(ColorDialog1.Color,scene.Material[materialIndex].SimpleAlpha);
  594. BGRAView3D.DiscardBitmap;
  595. end;
  596. end;
  597. end;
  598. procedure TFObject3D.SpinEdit_ColorOpacityChange(Sender: TObject);
  599. begin
  600. if SpinEdit_ColorOpacity.Enabled and (materialIndex <> -1) then
  601. begin
  602. scene.Material[materialIndex].SimpleAlpha := SpinEdit_ColorOpacity.Value;
  603. BGRAView3D.DiscardBitmap;
  604. end;
  605. end;
  606. procedure TFObject3D.SpinEdit_ColorOpacityKeyPress(Sender: TObject;
  607. var Key: char);
  608. begin
  609. CheckKey(Key);
  610. end;
  611. procedure TFObject3D.SpinEdit_HeightKeyPress(Sender: TObject; var Key: char);
  612. begin
  613. CheckKey(Key);
  614. end;
  615. procedure TFObject3D.SpinEdit_SpecularIndexChange(Sender: TObject);
  616. begin
  617. if SpinEdit_SpecularIndex.Enabled and (materialIndex <> -1) then
  618. begin
  619. scene.Material[materialIndex].SpecularIndex := SpinEdit_SpecularIndex.Value;
  620. BGRAView3D.DiscardBitmap;
  621. end;
  622. end;
  623. procedure TFObject3D.SpinEdit_SpecularIndexKeyPress(Sender: TObject;
  624. var Key: char);
  625. begin
  626. CheckKey(Key);
  627. end;
  628. procedure TFObject3D.SpinEdit_WidthKeyPress(Sender: TObject; var Key: char);
  629. begin
  630. CheckKey(Key);
  631. end;
  632. procedure TFObject3D.ToolAddDirectionalClick(Sender: TObject);
  633. begin
  634. if scene.LightCount < 10 then
  635. begin
  636. scene.AddDirectionalLight(Point3D(1,1,1),BGRAWhite,-0.5); //add a directional light from top-left
  637. lightIndex:= scene.LightCount-1;
  638. UpdateLightList;
  639. UpdateSelectedLight;
  640. BGRAView3D.DiscardBitmap;
  641. end;
  642. end;
  643. procedure TFObject3D.ToolPointLightClick(Sender: TObject);
  644. const OneOverSqrt3 = 0.57735026918962576450914878050196;
  645. begin
  646. if scene.LightCount < 10 then
  647. begin
  648. scene.AddPointLight(scene.CreateObject.MainPart.Add(-OneOverSqrt3*PointLightDist,-OneOverSqrt3*PointLightDist,-OneOverSqrt3*PointLightDist),PointLightDist,BGRAWhite);
  649. lightIndex:= scene.LightCount-1;
  650. UpdateLightList;
  651. UpdateSelectedLight;
  652. BGRAView3D.DiscardBitmap;
  653. end;
  654. end;
  655. procedure TFObject3D.ToolRemoveSelectedLightClick(Sender: TObject);
  656. begin
  657. if lightIndex <> -1 then
  658. begin
  659. scene.RemoveLight(scene.Light[lightIndex]);
  660. if lightIndex >= scene.LightCount then dec(lightIndex);
  661. UpdateLightList;
  662. UpdateSelectedLight;
  663. BGRAView3D.DiscardBitmap;
  664. end;
  665. end;
  666. procedure TFObject3D.UpdateTabSize;
  667. begin
  668. GroupBox_SelectedMaterial.Top := PageControl1.Height - InnerTabBottomPadding - GroupBox_SelectedMaterial.Height;
  669. ListBox_Materials.Height := GroupBox_SelectedMaterial.Top-2-ListBox_Materials.Top;
  670. GroupBox_SelectedLight.Top := PageControl1.Height - InnerTabBottomPadding - GroupBox_SelectedLight.Height;
  671. ListBox_Lights.Height := GroupBox_SelectedLight.Top-2-ListBox_Lights.Top;
  672. end;
  673. procedure TFObject3D.CheckKey(var Key: char);
  674. begin
  675. if (Key = '+') or (Key = '-') then
  676. begin
  677. if Key = '+' then
  678. scene.Object3D[0].MainPart.Scale(1.1,false) else
  679. scene.Object3D[0].MainPart.Scale(1/1.1,false);
  680. Key := #0;
  681. BGRAView3D.DiscardBitmap;
  682. end;
  683. end;
  684. procedure TFObject3D.DoLoadTexture(AFilename: string);
  685. var
  686. mat: IBGRAMaterial3D;
  687. i,j: integer;
  688. bmp: TBGRABitmap;
  689. begin
  690. if materialIndex = -1 then exit;
  691. Config.SetDefaultTextureDirectory(ExtractFilePath(AFilename));
  692. mat := scene.Material[materialIndex];
  693. bmp := scene.FetchTextureAsBitmap(OpenTextureDialog.FileName,False);
  694. scene.ReleaseTextureReference(mat.Texture);
  695. scene.QueryTextureReference(bmp);
  696. mat.Texture := bmp;
  697. mat.TextureZoom := PointF(1,1);
  698. mat.SimpleColor := BGRAWhite;
  699. Shape_MaterialColor.Brush.Color := BGRAToColor(mat.SimpleColor);
  700. for i := 0 to scene.Object3DCount-1 do
  701. with scene.Object3D[i] do
  702. begin
  703. for j := 0 to FaceCount-1 do
  704. if Face[j].Material = mat then
  705. self.scene.ComputeTexCoord(Face[j],bmp.Width,bmp.Height);
  706. end;
  707. end;
  708. procedure TFObject3D.DoFreeTexture;
  709. var
  710. mat: IBGRAMaterial3D;
  711. begin
  712. if materialIndex = -1 then exit;
  713. mat := scene.Material[materialIndex];
  714. scene.ReleaseTextureReference(mat.Texture);
  715. mat.Texture := nil;
  716. mat.SimpleColor := scene.UnknownColor;
  717. Shape_MaterialColor.Brush.Color := BGRAToColor(mat.SimpleColor);
  718. end;
  719. function TFObject3D.MaterialHasTexture: boolean;
  720. begin
  721. if materialIndex <> -1 then
  722. result := scene.Material[materialIndex].Texture <> nil
  723. else
  724. result := false;
  725. end;
  726. procedure TFObject3D.UpdateLightList;
  727. var
  728. i: Integer;
  729. begin
  730. ListBox_Lights.Clear;
  731. for i := 0 to scene.LightCount-1 do
  732. begin
  733. if scene.Light[i].IsDirectional then
  734. ListBox_Lights.Items.Add('#'+inttostr(i+1)+' Directional')
  735. else
  736. ListBox_Lights.Items.Add('#'+inttostr(i+1)+' Point');
  737. end;
  738. ListBox_Lights.ItemIndex := lightIndex;
  739. end;
  740. procedure TFObject3D.UpdateSelectedLight;
  741. begin
  742. lightIndex:= ListBox_Lights.ItemIndex;
  743. if lightIndex <> -1 then
  744. begin
  745. GroupBox_SelectedLight.Enabled := true;
  746. Shape_LightColor.Brush.Color := BGRAToColor(scene.Light[lightIndex].Color);
  747. Shape_LightColor.Enabled := true;
  748. PaintBox_LightPos.Repaint;
  749. end else
  750. begin
  751. GroupBox_SelectedLight.Enabled := false;
  752. end;
  753. end;
  754. procedure TFObject3D.SetLightPos(x, y: single);
  755. var light: IBGRALight3D;
  756. xy,z: single;
  757. begin
  758. if lightIndex <> -1 then
  759. begin
  760. light := scene.Light[lightIndex];
  761. x := (x-0.5)*2;
  762. y := (y-0.5)*2;
  763. xy := sqrt(sqr(x)+sqr(y));
  764. if xy >= 1 then
  765. begin
  766. x /= xy;
  767. y /= xy;
  768. z := 0;
  769. end else
  770. z := sqrt(1-sqr(xy));
  771. if light.IsDirectional then
  772. (light as IBGRADirectionalLight3D).Direction := Point3D(-x,-y,z)
  773. else
  774. (light as IBGRAPointLight3D).Vertex.SceneCoord := Point3D(x*PointLightDist,y*PointLightDist,-z*PointLightDist);
  775. PaintBox_LightPos.Repaint;
  776. scene.RenderingOptions.AntialiasingResampleLevel := AntialiasingLevelWhenMoving;
  777. BGRAView3D.DiscardBitmap;
  778. end;
  779. end;
  780. function DoLoadObject(scene: TScene; filenameUTF8: string): boolean;
  781. var
  782. obj: IBGRAObject3D;
  783. r: single;
  784. matFile: string;
  785. i: integer;
  786. s: TStream;
  787. begin
  788. scene.TexturePath := ExtractFilePath(filenameUTF8);
  789. result := false;
  790. matFile := ChangeFileExt(filenameUTF8,'.mtl');
  791. if FileManager.FileExists(matFile) then
  792. begin
  793. s := FileManager.CreateFileStream(matFile, fmOpenRead);
  794. try
  795. scene.LoadMaterialsFromStream(s);
  796. finally
  797. s.Free;
  798. end;
  799. for i := 0 to scene.MaterialCount-1 do
  800. scene.QueryTextureReference(scene.Material[i].Texture);
  801. end;
  802. s := FileManager.CreateFileStream(filenameUTF8, fmOpenRead);
  803. try
  804. obj := scene.LoadObjectFromStream(s);
  805. finally
  806. s.Free;
  807. end;
  808. if obj <> nil then
  809. begin
  810. scene.DefaultLightingNormal := obj.LightingNormal;
  811. obj.ParentLighting := true; //set for the whole scene with the dialog box
  812. with obj do
  813. begin
  814. with MainPart.BoundingBox do
  815. MainPart.Translate((min+max)*(-1/2), False);
  816. r := MainPart.Radius;
  817. if r <> 0 then MainPart.Scale(50/r, False);
  818. MainPart.RotateXDeg(180-20, False);
  819. MainPart.RotateYDeg(-20, False);
  820. end;
  821. with scene do
  822. begin
  823. //set ambiant lightness to dark (1 is normal lightness)
  824. AmbiantLightness := 0.5;
  825. AddDirectionalLight(Point3D(1,1,1),BGRAWhite,-0.5); //add a directional light from top-left
  826. end;
  827. result := true;
  828. end;
  829. end;
  830. var
  831. f: TFObject3D;
  832. function ShowObject3DDlg(Instance: TLazPaintCustomInstance; filenameUTF8: string;
  833. maxWidth, maxHeight: integer): TBGRABitmap;
  834. begin
  835. if f = nil then
  836. begin
  837. f:= TFObject3D.Create(nil);
  838. f.Config := Instance.Config;
  839. end;
  840. result := nil;
  841. try
  842. if DoLoadObject(f.scene,filenameUTF8) then
  843. begin
  844. f.BGRAView3D.DiscardBitmap;
  845. f.SpinEdit_Width.MaxValue := maxWidth;
  846. f.SpinEdit_Width.Value := maxWidth;
  847. f.SpinEdit_Height.MaxValue := maxHeight;
  848. f.SpinEdit_Height.Value := maxHeight;
  849. if f.ShowModal = mrOK then
  850. begin
  851. result := TBGRABitmap.Create(f.SpinEdit_Width.Value,f.SpinEdit_Height.Value);
  852. f.scene.Surface := result;
  853. f.scene.RenderingOptions.AntialiasingResampleLevel := 5;
  854. f.scene.Render;
  855. end;
  856. end;
  857. except
  858. on ex:Exception do
  859. Instance.ShowError('ShowObject3DDlg',ex.Message);
  860. end;
  861. f.scene.Clear;
  862. end;
  863. {$R *.lfm}
  864. finalization
  865. FreeAndNil(f);
  866. end.