uobject3d.pas 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950
  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_MaterialColorMouseDown(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));
  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. CheckOKCancelBtns(Button_OK,Button_Cancel);
  427. CheckSpinEdit(SpinEdit_ColorOpacity);
  428. CheckSpinEdit(SpinEdit_SpecularIndex);
  429. CheckSpinEdit(SpinEdit_Width);
  430. CheckSpinEdit(SpinEdit_Height);
  431. scene := TScene.Create;
  432. scene.DefaultLightingNormal := lnFaceVertexMix;
  433. with scene.RenderingOptions do
  434. begin
  435. LightingInterpolation := liAlwaysHighQuality;
  436. AntialiasingMode := am3dResample;
  437. AntialiasingResampleLevel := AntialiasingLevelWhenFixed;
  438. PerspectiveMode := pmZBuffer;
  439. TextureInterpolation := True;
  440. end;
  441. previousZoom := BGRAKnob_Zoom.Value;
  442. ComboBox_Normals.Items.Add('None');
  443. ComboBox_Normals.Items.Add('Flat faces');
  444. ComboBox_Normals.Items.Add('Rounded faces');
  445. ComboBox_Normals.Items.Add('Intermediate');
  446. materialIndex:= -1;
  447. InnerTabBottomPadding := PageControl1.Height - (GroupBox_SelectedMaterial.Top+GroupBox_SelectedMaterial.Height);
  448. UpdateTabSize;
  449. end;
  450. procedure TFObject3D.FormDestroy(Sender: TObject);
  451. begin
  452. scene.Free;
  453. end;
  454. procedure TFObject3D.FormKeyPress(Sender: TObject; var Key: char);
  455. begin
  456. CheckKey(Key);
  457. end;
  458. procedure TFObject3D.FormResize(Sender: TObject);
  459. begin
  460. UpdateTabSize;
  461. end;
  462. procedure TFObject3D.FormShow(Sender: TObject);
  463. var i: integer;
  464. begin
  465. ListBox_Materials.Clear;
  466. for i := 0 to scene.MaterialCount-1 do
  467. begin
  468. if scene.Material[i] = scene.DefaultMaterial then
  469. ListBox_Materials.Items.Add('<default>')
  470. else
  471. ListBox_Materials.Items.Add(scene.Material[i].Name);
  472. end;
  473. if ListBox_Materials.Items.Count > 0 then
  474. ListBox_Materials.ItemIndex := 0;
  475. if ComboBox_Normals.Items.Count > ord(scene.DefaultLightingNormal) then
  476. ComboBox_Normals.ItemIndex := ord(scene.DefaultLightingNormal);
  477. if scene.LightCount > 0 then
  478. lightIndex := 0
  479. else
  480. lightIndex := -1;
  481. UpdateLightList;
  482. UpdateSelectedLight;
  483. end;
  484. procedure TFObject3D.ListBox_LightsKeyPress(Sender: TObject; var Key: char);
  485. begin
  486. CheckKey(Key);
  487. end;
  488. procedure TFObject3D.ListBox_LightsSelectionChange(Sender: TObject;
  489. User: boolean);
  490. begin
  491. if User then UpdateSelectedLight;
  492. end;
  493. procedure TFObject3D.ListBox_MaterialsKeyPress(Sender: TObject; var Key: char);
  494. begin
  495. CheckKey(Key);
  496. end;
  497. procedure TFObject3D.ListBox_MaterialsSelectionChange(Sender: TObject;
  498. User: boolean);
  499. begin
  500. if ListBox_Materials.ItemIndex <> -1 then
  501. begin
  502. materialIndex := ListBox_Materials.ItemIndex;
  503. SpinEdit_SpecularIndex.Enabled := false;
  504. if scene.Material[materialIndex].SpecularOn then
  505. SpinEdit_SpecularIndex.Value := scene.Material[materialIndex].SpecularIndex
  506. else
  507. SpinEdit_SpecularIndex.Value := 0;
  508. SpinEdit_SpecularIndex.Enabled := true;
  509. Shape_MaterialColor.Brush.Color := BGRAToColor(scene.Material[materialIndex].SimpleColor);
  510. Shape_MaterialColor.Enabled := true;
  511. SpinEdit_ColorOpacity.Enabled := false;
  512. SpinEdit_ColorOpacity.Value := scene.Material[materialIndex].SimpleAlpha;
  513. SpinEdit_ColorOpacity.Enabled := true;
  514. end;
  515. end;
  516. procedure TFObject3D.PaintBox_LightPosMouseDown(Sender: TObject;
  517. Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
  518. begin
  519. if Button = mbLeft then SetLightPos(X/PaintBox_LightPos.Width,Y/PaintBox_LightPos.Height);
  520. end;
  521. procedure TFObject3D.PaintBox_LightPosMouseMove(Sender: TObject;
  522. Shift: TShiftState; X, Y: Integer);
  523. begin
  524. if ssLeft in Shift then
  525. SetLightPos(X/PaintBox_LightPos.Width,Y/PaintBox_LightPos.Height);
  526. end;
  527. procedure TFObject3D.PaintBox_LightPosMouseUp(Sender: TObject;
  528. Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
  529. begin
  530. if Button = mbLeft then
  531. begin
  532. scene.RenderingOptions.AntialiasingResampleLevel := AntialiasingLevelWhenFixed;
  533. BGRAView3D.RedrawBitmap;
  534. end;
  535. end;
  536. procedure TFObject3D.PaintBox_LightPosPaint(Sender: TObject);
  537. var x,y: integer;
  538. pt: TPointF;
  539. light: IBGRALight3D;
  540. begin
  541. if lightIndex <> -1 then
  542. begin
  543. light := scene.Light[lightIndex];
  544. if light.IsDirectional then
  545. begin
  546. with (light as IBGRADirectionalLight3D).Direction do
  547. pt := PointF(-x/2+0.5,-y/2+0.5);
  548. end else
  549. begin
  550. with (light as IBGRAPointLight3D).Vertex.GetSceneCoord do
  551. pt := PointF(x/2/PointLightDist+0.5,y/2/PointLightDist+0.5);
  552. end;
  553. with PaintBox_LightPos do
  554. begin
  555. x := round(pt.X*Width);
  556. y := round(pt.Y*Height);
  557. Canvas.Brush.Style := bsSolid;
  558. Canvas.Brush.Color:= clBtnFace;
  559. Canvas.Pen.Style := psSolid;
  560. Canvas.Pen.Color := clWindowText;
  561. Canvas.Rectangle(0,0,Width,Height);
  562. Canvas.Pen.Color := clBlack;
  563. Canvas.Brush.Style := bsSolid;
  564. Canvas.Brush.Color := clWhite;
  565. Canvas.Ellipse(x-3,y-3,x+4,y+4);
  566. end;
  567. end;
  568. end;
  569. procedure TFObject3D.Shape_LightColorMouseUp(Sender: TObject;
  570. Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
  571. begin
  572. if lightIndex <> -1 then
  573. begin
  574. ColorDialog1.Color := BGRAToColor(scene.Light[materialIndex].Color);
  575. if ColorDialog1.Execute then
  576. begin
  577. Shape_LightColor.Brush.Color := ColorDialog1.Color;
  578. scene.Light[lightIndex].Color := ColorToBGRA(ColorDialog1.Color);
  579. BGRAView3D.DiscardBitmap;
  580. end;
  581. end;
  582. end;
  583. procedure TFObject3D.Shape_MaterialColorMouseDown(Sender: TObject;
  584. Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
  585. begin
  586. if materialIndex <> -1 then
  587. begin
  588. ColorDialog1.Color := BGRAToColor(scene.Material[materialIndex].SimpleColor);
  589. if ColorDialog1.Execute then
  590. begin
  591. Shape_MaterialColor.Brush.Color := ColorDialog1.Color;
  592. scene.Material[materialIndex].SimpleColor := ColorToBGRA(ColorDialog1.Color,scene.Material[materialIndex].SimpleAlpha);
  593. BGRAView3D.DiscardBitmap;
  594. end;
  595. end;
  596. end;
  597. procedure TFObject3D.SpinEdit_ColorOpacityChange(Sender: TObject);
  598. begin
  599. if SpinEdit_ColorOpacity.Enabled and (materialIndex <> -1) then
  600. begin
  601. scene.Material[materialIndex].SimpleAlpha := SpinEdit_ColorOpacity.Value;
  602. BGRAView3D.DiscardBitmap;
  603. end;
  604. end;
  605. procedure TFObject3D.SpinEdit_ColorOpacityKeyPress(Sender: TObject;
  606. var Key: char);
  607. begin
  608. CheckKey(Key);
  609. end;
  610. procedure TFObject3D.SpinEdit_HeightKeyPress(Sender: TObject; var Key: char);
  611. begin
  612. CheckKey(Key);
  613. end;
  614. procedure TFObject3D.SpinEdit_SpecularIndexChange(Sender: TObject);
  615. begin
  616. if SpinEdit_SpecularIndex.Enabled and (materialIndex <> -1) then
  617. begin
  618. scene.Material[materialIndex].SpecularIndex := SpinEdit_SpecularIndex.Value;
  619. BGRAView3D.DiscardBitmap;
  620. end;
  621. end;
  622. procedure TFObject3D.SpinEdit_SpecularIndexKeyPress(Sender: TObject;
  623. var Key: char);
  624. begin
  625. CheckKey(Key);
  626. end;
  627. procedure TFObject3D.SpinEdit_WidthKeyPress(Sender: TObject; var Key: char);
  628. begin
  629. CheckKey(Key);
  630. end;
  631. procedure TFObject3D.ToolAddDirectionalClick(Sender: TObject);
  632. begin
  633. if scene.LightCount < 10 then
  634. begin
  635. scene.AddDirectionalLight(Point3D(1,1,1),BGRAWhite,-0.5); //add a directional light from top-left
  636. lightIndex:= scene.LightCount-1;
  637. UpdateLightList;
  638. UpdateSelectedLight;
  639. BGRAView3D.DiscardBitmap;
  640. end;
  641. end;
  642. procedure TFObject3D.ToolPointLightClick(Sender: TObject);
  643. const OneOverSqrt3 = 0.57735026918962576450914878050196;
  644. begin
  645. if scene.LightCount < 10 then
  646. begin
  647. scene.AddPointLight(scene.CreateObject.MainPart.Add(-OneOverSqrt3*PointLightDist,-OneOverSqrt3*PointLightDist,-OneOverSqrt3*PointLightDist),PointLightDist,BGRAWhite);
  648. lightIndex:= scene.LightCount-1;
  649. UpdateLightList;
  650. UpdateSelectedLight;
  651. BGRAView3D.DiscardBitmap;
  652. end;
  653. end;
  654. procedure TFObject3D.ToolRemoveSelectedLightClick(Sender: TObject);
  655. begin
  656. if lightIndex <> -1 then
  657. begin
  658. scene.RemoveLight(scene.Light[lightIndex]);
  659. if lightIndex >= scene.LightCount then dec(lightIndex);
  660. UpdateLightList;
  661. UpdateSelectedLight;
  662. BGRAView3D.DiscardBitmap;
  663. end;
  664. end;
  665. procedure TFObject3D.UpdateTabSize;
  666. begin
  667. GroupBox_SelectedMaterial.Top := PageControl1.Height - InnerTabBottomPadding - GroupBox_SelectedMaterial.Height;
  668. ListBox_Materials.Height := GroupBox_SelectedMaterial.Top-2-ListBox_Materials.Top;
  669. GroupBox_SelectedLight.Top := PageControl1.Height - InnerTabBottomPadding - GroupBox_SelectedLight.Height;
  670. ListBox_Lights.Height := GroupBox_SelectedLight.Top-2-ListBox_Lights.Top;
  671. end;
  672. procedure TFObject3D.CheckKey(var Key: char);
  673. begin
  674. if (Key = '+') or (Key = '-') then
  675. begin
  676. if Key = '+' then
  677. scene.Object3D[0].MainPart.Scale(1.1,false) else
  678. scene.Object3D[0].MainPart.Scale(1/1.1,false);
  679. Key := #0;
  680. BGRAView3D.DiscardBitmap;
  681. end;
  682. end;
  683. procedure TFObject3D.DoLoadTexture(AFilename: string);
  684. var
  685. mat: IBGRAMaterial3D;
  686. i,j: integer;
  687. bmp: TBGRABitmap;
  688. begin
  689. if materialIndex = -1 then exit;
  690. Config.SetDefaultTextureDirectory(ExtractFilePath(AFilename));
  691. mat := scene.Material[materialIndex];
  692. bmp := scene.FetchTextureAsBitmap(OpenTextureDialog.FileName,False);
  693. scene.ReleaseTextureReference(mat.Texture);
  694. scene.QueryTextureReference(bmp);
  695. mat.Texture := bmp;
  696. mat.TextureZoom := PointF(1,1);
  697. mat.SimpleColor := BGRAWhite;
  698. Shape_MaterialColor.Brush.Color := BGRAToColor(mat.SimpleColor);
  699. for i := 0 to scene.Object3DCount-1 do
  700. with scene.Object3D[i] do
  701. begin
  702. for j := 0 to FaceCount-1 do
  703. if Face[j].Material = mat then
  704. self.scene.ComputeTexCoord(Face[j],bmp.Width,bmp.Height);
  705. end;
  706. end;
  707. procedure TFObject3D.DoFreeTexture;
  708. var
  709. mat: IBGRAMaterial3D;
  710. begin
  711. if materialIndex = -1 then exit;
  712. mat := scene.Material[materialIndex];
  713. scene.ReleaseTextureReference(mat.Texture);
  714. mat.Texture := nil;
  715. mat.SimpleColor := scene.UnknownColor;
  716. Shape_MaterialColor.Brush.Color := BGRAToColor(mat.SimpleColor);
  717. end;
  718. function TFObject3D.MaterialHasTexture: boolean;
  719. begin
  720. if materialIndex <> -1 then
  721. result := scene.Material[materialIndex].Texture <> nil
  722. else
  723. result := false;
  724. end;
  725. procedure TFObject3D.UpdateLightList;
  726. var
  727. i: Integer;
  728. begin
  729. ListBox_Lights.Clear;
  730. for i := 0 to scene.LightCount-1 do
  731. begin
  732. if scene.Light[i].IsDirectional then
  733. ListBox_Lights.Items.Add('#'+inttostr(i+1)+' Directional')
  734. else
  735. ListBox_Lights.Items.Add('#'+inttostr(i+1)+' Point');
  736. end;
  737. ListBox_Lights.ItemIndex := lightIndex;
  738. end;
  739. procedure TFObject3D.UpdateSelectedLight;
  740. begin
  741. lightIndex:= ListBox_Lights.ItemIndex;
  742. if lightIndex <> -1 then
  743. begin
  744. GroupBox_SelectedLight.Enabled := true;
  745. Shape_LightColor.Brush.Color := BGRAToColor(scene.Light[lightIndex].Color);
  746. Shape_LightColor.Enabled := true;
  747. PaintBox_LightPos.Repaint;
  748. end else
  749. begin
  750. GroupBox_SelectedLight.Enabled := false;
  751. end;
  752. end;
  753. procedure TFObject3D.SetLightPos(x, y: single);
  754. var light: IBGRALight3D;
  755. xy,z: single;
  756. begin
  757. if lightIndex <> -1 then
  758. begin
  759. light := scene.Light[lightIndex];
  760. x := (x-0.5)*2;
  761. y := (y-0.5)*2;
  762. xy := sqrt(sqr(x)+sqr(y));
  763. if xy >= 1 then
  764. begin
  765. x /= xy;
  766. y /= xy;
  767. z := 0;
  768. end else
  769. z := sqrt(1-sqr(xy));
  770. if light.IsDirectional then
  771. (light as IBGRADirectionalLight3D).Direction := Point3D(-x,-y,z)
  772. else
  773. (light as IBGRAPointLight3D).Vertex.SceneCoord := Point3D(x*PointLightDist,y*PointLightDist,-z*PointLightDist);
  774. PaintBox_LightPos.Repaint;
  775. scene.RenderingOptions.AntialiasingResampleLevel := AntialiasingLevelWhenMoving;
  776. BGRAView3D.DiscardBitmap;
  777. end;
  778. end;
  779. function DoLoadObject(scene: TScene; filenameUTF8: string): boolean;
  780. var
  781. obj: IBGRAObject3D;
  782. r: single;
  783. matFile: string;
  784. i: integer;
  785. s: TStream;
  786. begin
  787. scene.TexturePath := ExtractFilePath(filenameUTF8);
  788. result := false;
  789. matFile := ChangeFileExt(filenameUTF8,'.mtl');
  790. if FileManager.FileExists(matFile) then
  791. begin
  792. s := FileManager.CreateFileStream(matFile, fmOpenRead);
  793. try
  794. scene.LoadMaterialsFromStream(s);
  795. finally
  796. s.Free;
  797. end;
  798. for i := 0 to scene.MaterialCount-1 do
  799. scene.QueryTextureReference(scene.Material[i].Texture);
  800. end;
  801. s := FileManager.CreateFileStream(filenameUTF8, fmOpenRead);
  802. try
  803. obj := scene.LoadObjectFromStream(s);
  804. finally
  805. s.Free;
  806. end;
  807. if obj <> nil then
  808. begin
  809. scene.DefaultLightingNormal := obj.LightingNormal;
  810. obj.ParentLighting := true; //set for the whole scene with the dialog box
  811. with obj do
  812. begin
  813. with MainPart.BoundingBox do
  814. MainPart.Translate((min+max)*(-1/2), False);
  815. r := MainPart.Radius;
  816. if r <> 0 then MainPart.Scale(50/r, False);
  817. MainPart.RotateXDeg(180-20, False);
  818. MainPart.RotateYDeg(-20, False);
  819. end;
  820. with scene do
  821. begin
  822. //set ambiant lightness to dark (1 is normal lightness)
  823. AmbiantLightness := 0.5;
  824. AddDirectionalLight(Point3D(1,1,1),BGRAWhite,-0.5); //add a directional light from top-left
  825. end;
  826. result := true;
  827. end;
  828. end;
  829. var
  830. f: TFObject3D;
  831. function ShowObject3DDlg(Instance: TLazPaintCustomInstance; filenameUTF8: string;
  832. maxWidth, maxHeight: integer): TBGRABitmap;
  833. begin
  834. if f = nil then
  835. begin
  836. f:= TFObject3D.Create(nil);
  837. f.Config := Instance.Config;
  838. end;
  839. result := nil;
  840. try
  841. if DoLoadObject(f.scene,filenameUTF8) then
  842. begin
  843. f.BGRAView3D.DiscardBitmap;
  844. f.SpinEdit_Width.MaxValue := maxWidth;
  845. f.SpinEdit_Width.Value := maxWidth;
  846. f.SpinEdit_Height.MaxValue := maxHeight;
  847. f.SpinEdit_Height.Value := maxHeight;
  848. if f.ShowModal = mrOK then
  849. begin
  850. result := TBGRABitmap.Create(f.SpinEdit_Width.Value,f.SpinEdit_Height.Value);
  851. f.scene.Surface := result;
  852. f.scene.RenderingOptions.AntialiasingResampleLevel := 5;
  853. f.scene.Render;
  854. end;
  855. end;
  856. except
  857. on ex:Exception do
  858. Instance.ShowError('ShowObject3DDlg',ex.Message);
  859. end;
  860. f.scene.Clear;
  861. end;
  862. {$R *.lfm}
  863. finalization
  864. FreeAndNil(f);
  865. end.