GBE.Heightmap.pas 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370
  1. unit GBE.Heightmap;
  2. (*
  3. The TGBEHeightmap allows you to generate a heightmap.
  4. Based on code by Gregory Bersegeay
  5. *)
  6. interface
  7. uses
  8. System.Types,
  9. System.SysUtils,
  10. System.Classes,
  11. System.RTLConsts,
  12. System.Math,
  13. System.UITypes,
  14. System.UIConsts,
  15. FMX.Types,
  16. FMX.Controls3D,
  17. FMX.Objects3D,
  18. FMX.Graphics,
  19. System.Math.Vectors,
  20. FMX.types3D,
  21. FMX.Effects,
  22. FMX.MaterialSources,
  23. uGBEUtils3D;
  24. type
  25. TGBEHeightmap = class(TMesh)
  26. private
  27. fSubdivisionsX, fSubdivisionsZ, fFlou, fHalfSubdivisionsX,
  28. fHalfSubdivisionsZ: integer;
  29. fHeightmap: TBitmap;
  30. fTracerLignes, FUseRamp: boolean;
  31. fMaterialLignes: TColorMaterialSource;
  32. fMiseAEchelle, fMaxHauteur, fMinHauteur: single;
  33. function GetFlou: integer;
  34. procedure SetFlou(const Value: integer);
  35. function GetTracerLignes: boolean;
  36. procedure SetTracerLignes(const Value: boolean);
  37. procedure SetUseRamp(const Value: boolean);
  38. procedure GenerateHeightmap(Const aData: TMeshData);
  39. protected
  40. procedure Render; override;
  41. public
  42. constructor Create(AOwner: TComponent); override;
  43. destructor Destroy; override;
  44. procedure RebuildMesh;
  45. procedure LoadHeightmapFromFile(FileName: string);
  46. procedure LoadHeightmapFromStream(Stream: TStream);
  47. procedure LoadHeightmapFromResource(ResourceName: string);
  48. function GetHeight(P: TPoint3d): single;
  49. published
  50. property Flou: integer read GetFlou write SetFlou;
  51. property ShowLines: boolean read GetTracerLignes write SetTracerLignes;
  52. property MaterialLines: TColorMaterialSource read fMaterialLignes
  53. write fMaterialLignes;
  54. property MinHeight: single read fMinHauteur;
  55. property MaxHeight: single read fMaxHauteur;
  56. property Locked default True;
  57. property HitTest default False;
  58. property UseRamp: boolean read FUseRamp write SetUseRamp;
  59. property TwoSide default True;
  60. property Visible default True;
  61. property ZWrite default True;
  62. property MiseAEchelle: single read fMiseAEchelle;
  63. end;
  64. procedure Register;
  65. implementation //--------------------------------------------------------------
  66. // TGBEHeightmap
  67. constructor TGBEHeightmap.Create(AOwner: TComponent);
  68. begin
  69. inherited;
  70. fSubdivisionsX := 0;
  71. fHalfSubdivisionsX := 0;
  72. fSubdivisionsZ := 0;
  73. fHalfSubdivisionsZ := 0;
  74. fHeightmap := TBitmap.Create;
  75. ShowLines := False;
  76. UseRamp := False;
  77. HitTest := False;
  78. rotationAngle.X := 180;
  79. fMiseAEchelle := 1;
  80. fMaxHauteur := 0;
  81. fMinHauteur := 0;
  82. end;
  83. destructor TGBEHeightmap.Destroy;
  84. begin
  85. FreeAndNil(fHeightmap);
  86. inherited;
  87. end;
  88. function TGBEHeightmap.GetFlou: integer;
  89. begin
  90. result := fFlou;
  91. end;
  92. function TGBEHeightmap.GetHeight(P: TPoint3d): single;
  93. begin
  94. result := CalculateHeight(self, P, self.fMiseAEchelle, fSubdivisionsX,
  95. fSubdivisionsZ);
  96. end;
  97. function TGBEHeightmap.GetTracerLignes: boolean;
  98. begin
  99. result := fTracerLignes;
  100. end;
  101. procedure TGBEHeightmap.LoadHeightmapFromFile(FileName: string);
  102. begin
  103. if FileExists(FileName) then
  104. begin
  105. self.Data.Clear;
  106. fHeightmap.LoadFromFile(FileName);
  107. fSubdivisionsX := fHeightmap.Width;
  108. fHalfSubdivisionsX := Floor(fSubdivisionsX / 2);
  109. fSubdivisionsZ := fHeightmap.Height;
  110. fHalfSubdivisionsZ := Floor(fSubdivisionsZ / 2);
  111. GenerateHeightmap(self.Data);
  112. end;
  113. end;
  114. procedure TGBEHeightmap.LoadHeightmapFromStream(Stream: TStream);
  115. begin
  116. self.Data.Clear;
  117. fHeightmap.LoadFromStream(Stream);
  118. fSubdivisionsX := fHeightmap.Width;
  119. fHalfSubdivisionsX := Floor(fSubdivisionsX / 2);
  120. fSubdivisionsZ := fHeightmap.Height;
  121. fHalfSubdivisionsZ := Floor(fSubdivisionsZ / 2);
  122. GenerateHeightmap(self.Data);
  123. end;
  124. procedure TGBEHeightmap.LoadHeightmapFromResource(ResourceName: string);
  125. var
  126. Stream: TResourceStream;
  127. begin
  128. Stream := TResourceStream.Create(HInstance, ResourceName, RT_RCDATA);
  129. LoadHeightmapFromStream(Stream);
  130. Stream.Free;
  131. end;
  132. procedure TGBEHeightmap.RebuildMesh;
  133. begin
  134. GenerateHeightmap(self.Data);
  135. end;
  136. procedure TGBEHeightmap.Render;
  137. begin
  138. inherited;
  139. if ShowLines then
  140. begin
  141. Context.DrawLines(self.Data.VertexBuffer, self.Data.IndexBuffer,
  142. TMaterialSource.ValidMaterial(fMaterialLignes), 1);
  143. end;
  144. end;
  145. procedure TGBEHeightmap.SetFlou(const Value: integer);
  146. begin
  147. if Value <> fFlou then
  148. begin
  149. fFlou := Value;
  150. GenerateHeightmap(self.Data);
  151. end;
  152. end;
  153. procedure TGBEHeightmap.SetTracerLignes(const Value: boolean);
  154. begin
  155. if Value <> fTracerLignes then
  156. fTracerLignes := Value;
  157. end;
  158. procedure TGBEHeightmap.SetUseRamp(const Value: boolean);
  159. begin
  160. if Value <> FUseRamp then
  161. begin
  162. FUseRamp := Value;
  163. fSubdivisionsX := fHeightmap.Width;
  164. fHalfSubdivisionsX := Floor(fSubdivisionsX / 2);
  165. fSubdivisionsZ := fHeightmap.Height;
  166. fHalfSubdivisionsZ := Floor(fSubdivisionsZ / 2);
  167. GenerateHeightmap(self.Data);
  168. end;
  169. end;
  170. procedure TGBEHeightmap.GenerateHeightmap(Const aData: TMeshData);
  171. var
  172. SubMap: TBitmap;
  173. // Bitmap which will be used to generate the relief from the heightmap
  174. zMap: single;
  175. C: TAlphaColorRec;
  176. // Color read from the heightmap and used to determine the height of a vertex
  177. bitmapData: TBitmapData;
  178. D: TMeshData;
  179. u, v: Double;
  180. P: array [0 .. 3] of TPoint3d;
  181. decallage: Double;
  182. NP, NI: integer;
  183. MaxX, MaxZ, MaxX_1, MaxZ_1: Double;
  184. begin
  185. if fSubdivisionsX < 1 then
  186. exit; // at least one subdivision is required
  187. if fSubdivisionsZ < 1 then
  188. exit; // the same
  189. decallage := 1;
  190. NP := 0;
  191. NI := 0;
  192. fMaxHauteur := 0;
  193. fMinHauteur := 0;
  194. MaxX := fHeightmap.Width * 0.5;
  195. MaxZ := fHeightmap.Height * 0.5;
  196. MaxX_1 := MaxX - 1;
  197. MaxZ_1 := MaxZ - 1;
  198. try
  199. D := TMeshData.Create;
  200. D.VertexBuffer.Length := Round(2 * MaxX * 2 * MaxZ) * 4;
  201. D.IndexBuffer.Length := Round(2 * MaxX * 2 * MaxZ) * 6;
  202. SubMap := TBitmap.Create(fHeightmap.Width, fHeightmap.Height);
  203. // Creating the bitmap
  204. SubMap.Assign(fHeightmap); // We load the heightmap
  205. Blur(SubMap.canvas, SubMap, Flou);
  206. if (SubMap.Map(TMapAccess.Read, bitmapData)) then
  207. // needed to access the pixel of the Bitmap in order to retrieve the color
  208. begin
  209. v := -MaxZ;
  210. while v < MaxZ do
  211. begin
  212. u := -MaxX;
  213. while u < MaxX do
  214. begin
  215. P[0].X := u;
  216. P[0].Z := v;
  217. // Retrieves the color of the corresponding pixel in the heightmap
  218. C := TAlphaColorRec
  219. (CorrectColor(bitmapData.GetPixel(Trunc(P[0].X + MaxX_1),
  220. Trunc(P[0].Z + MaxZ_1))));
  221. zMap := C.R;
  222. // (C.R + C.G + C.B ); // Determination of peak height based on color
  223. P[0].Y := zMap;
  224. if zMap > fMaxHauteur then
  225. fMaxHauteur := zMap;
  226. if zMap < fMinHauteur then
  227. fMinHauteur := zMap;
  228. P[1].X := u + decallage;
  229. P[1].Z := v;
  230. // Retrieves the color of the corresponding pixel in the heightmap
  231. C := TAlphaColorRec
  232. (CorrectColor(bitmapData.GetPixel(Trunc(P[1].X + MaxX_1),
  233. Trunc(P[1].Z + MaxZ_1))));
  234. zMap := C.R;
  235. // (C.R + C.G + C.B ); // détermination de la hauteur du sommet en fonction de la couleur
  236. P[1].Y := zMap;
  237. if zMap > fMaxHauteur then
  238. fMaxHauteur := zMap;
  239. if zMap < fMinHauteur then
  240. fMinHauteur := zMap;
  241. P[2].X := u + decallage;
  242. P[2].Z := v + decallage;
  243. // Retrieves the color of the corresponding pixel in the heightmap
  244. C := TAlphaColorRec
  245. (CorrectColor(bitmapData.GetPixel(Trunc(P[2].X + MaxX_1),
  246. Trunc(P[2].Z + MaxZ_1))));
  247. zMap := C.R;
  248. // (C.R + C.G + C.B ); // détermination de la hauteur du sommet en fonction de la couleur
  249. P[2].Y := zMap;
  250. if zMap > fMaxHauteur then
  251. fMaxHauteur := zMap;
  252. if zMap < fMinHauteur then
  253. fMinHauteur := zMap;
  254. P[3].X := u;
  255. P[3].Z := v + decallage;
  256. // Retrieves the color of the corresponding pixel in the heightmap
  257. C := TAlphaColorRec
  258. (CorrectColor(bitmapData.GetPixel(Trunc(P[3].X + MaxX_1),
  259. Trunc(P[3].Z + MaxZ_1))));
  260. zMap := C.R;
  261. // (C.R + C.G + C.B ); // détermination de la hauteur du sommet en fonction de la couleur
  262. P[3].Y := zMap;
  263. if zMap > fMaxHauteur then
  264. fMaxHauteur := zMap;
  265. if zMap < fMinHauteur then
  266. fMinHauteur := zMap;
  267. with D do
  268. begin
  269. with VertexBuffer do
  270. begin
  271. Vertices[NP + 0] := P[0];
  272. Vertices[NP + 1] := P[1];
  273. Vertices[NP + 2] := P[2];
  274. Vertices[NP + 3] := P[3];
  275. end;
  276. with VertexBuffer do
  277. begin
  278. if FUseRamp then
  279. begin
  280. TexCoord0[NP + 0] := PointF((abs(P[0].Y)) / 255, 0);
  281. TexCoord0[NP + 1] := PointF((abs(P[1].Y)) / 255, 0);
  282. TexCoord0[NP + 2] := PointF((abs(P[2].Y)) / 255, 0);
  283. TexCoord0[NP + 3] := PointF((abs(P[3].Y)) / 255, 0);
  284. end
  285. else
  286. begin
  287. begin
  288. TexCoord0[NP + 0] := PointF((P[0].X + MaxX) / fSubdivisionsX,
  289. (P[0].Z + MaxZ) / fSubdivisionsZ);
  290. TexCoord0[NP + 1] := PointF((P[1].X + MaxX) / fSubdivisionsX,
  291. (P[1].Z + MaxZ) / fSubdivisionsZ);
  292. TexCoord0[NP + 2] := PointF((P[2].X + MaxX) / fSubdivisionsX,
  293. (P[2].Z + MaxZ) / fSubdivisionsZ);
  294. TexCoord0[NP + 3] := PointF((P[3].X + MaxX) / fSubdivisionsX,
  295. (P[3].Z + MaxZ) / fSubdivisionsZ);
  296. end;
  297. end;
  298. end;
  299. IndexBuffer[NI + 0] := NP + 0;
  300. IndexBuffer[NI + 1] := NP + 1;
  301. IndexBuffer[NI + 2] := NP + 3;
  302. IndexBuffer[NI + 3] := NP + 3;
  303. IndexBuffer[NI + 4] := NP + 1;
  304. IndexBuffer[NI + 5] := NP + 2;
  305. end;
  306. NP := NP + 4;
  307. NI := NI + 6;
  308. u := u + decallage;
  309. end;
  310. v := v + decallage;
  311. end;
  312. end;
  313. D.CalcTangentBinormals;
  314. // Calculation of binormal vectors and tangent for all faces (for example, allows for better reaction to light)
  315. /// CalcFaceNormals;
  316. aData.Clear;
  317. aData.Assign(D);
  318. if fMaxHauteur <> fMinHauteur then
  319. fMiseAEchelle := self.Height / (fMaxHauteur - fMinHauteur)
  320. else
  321. fMiseAEchelle := 0;
  322. finally
  323. FreeAndNil(SubMap);
  324. FreeAndNil(D);
  325. end;
  326. end;
  327. // ----------------------------------------------------------------------
  328. procedure Register;
  329. begin
  330. RegisterComponents('GXScene GBE', [TGBEHeightmap]);
  331. end;
  332. end.