GLS.BumpmapHDS.pas 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860
  1. //
  2. // The graphics engine GLScene
  3. //
  4. unit GLS.BumpmapHDS;
  5. (*
  6. Implements a HDS that automatically generates an elevation bumpmap.
  7. The object-space elevation bumpmap can be used for dynamic terrain lighting.
  8. A bumpmap texture is generated for each terrain tile, and placed into a TGLMaterialLibrary.
  9. *)
  10. interface
  11. {$I Stage.Defines.inc}
  12. uses
  13. System.Classes,
  14. System.SysUtils,
  15. System.SyncObjs,
  16. System.UITypes,
  17. Vcl.Graphics,
  18. Stage.OpenGLTokens,
  19. Stage.VectorTypes,
  20. Stage.VectorGeometry,
  21. GLS.Coordinates,
  22. GLS.HeightData,
  23. GLS.Graphics,
  24. GLS.Color,
  25. GLS.VectorLists,
  26. GLS.Texture,
  27. GLS.Material,
  28. Stage.Utils;
  29. type
  30. TGLNormalMapSpace = (nmsObject, nmsTangent);
  31. TGLBumpmapHDS = class;
  32. TNewTilePreparedEvent = procedure(Sender: TGLBumpmapHDS;
  33. heightData: TGLHeightData; normalMapMaterial: TGLLibMaterial) of object;
  34. (* An Height Data Source that generates elevation bumpmaps automatically.
  35. The HDS must be connected to another HDS, which will provide the elevation
  36. data, and to a MaterialLibrary where bumpmaps will be placed. *)
  37. TGLBumpmapHDS = class(TGLHeightDataSourceFilter)
  38. private
  39. // FElevationHDS : TGLHeightDataSource;
  40. FBumpmapLibrary: TGLMaterialLibrary;
  41. FOnNewTilePrepared: TNewTilePreparedEvent;
  42. FBumpScale: Single;
  43. FSubSampling: Integer;
  44. FMaxTextures: Integer;
  45. Uno: TCriticalSection;
  46. protected
  47. procedure SetBumpmapLibrary(const val: TGLMaterialLibrary);
  48. procedure SetBumpScale(const val: Single);
  49. function StoreBumpScale: Boolean;
  50. procedure SetSubSampling(const val: Integer);
  51. procedure Trim(MaxTextureCount: Integer);
  52. public
  53. constructor Create(AOwner: TComponent); override;
  54. destructor Destroy; override;
  55. procedure Release(aHeightData: TGLHeightData); override;
  56. procedure Notification(AComponent: TComponent;
  57. Operation: TOperation); override;
  58. procedure GenerateNormalMap(heightData: TGLHeightData; normalMap: TGLImage;
  59. scale: Single);
  60. (* This will repeatedly delete the oldest unused texture from the TGLMaterialLibrary,
  61. until the texture count drops to MaxTextureCount.
  62. DONT use this if you used TGLHeightData.MaterialName to link your terrain textures.
  63. Either use with TGLHeightData.LibMaterial, or manually delete unused Normal-Map textures.*)
  64. procedure TrimTextureCache(MaxTextureCount: Integer);
  65. // procedure TileTextureCoordinates(heightData : TGLHeightData; TextureScale:TTexPoint; TextureOffset:TTexPoint);
  66. procedure PreparingData(heightData: TGLHeightData); override;
  67. published
  68. property BumpmapLibrary: TGLMaterialLibrary read FBumpmapLibrary
  69. write SetBumpmapLibrary;
  70. property OnNewTilePrepared: TNewTilePreparedEvent read FOnNewTilePrepared
  71. write FOnNewTilePrepared;
  72. property BumpScale: Single read FBumpScale write SetBumpScale
  73. stored StoreBumpScale;
  74. (* Specifies the amount of subsampling for the bump texture.
  75. This value must be a power of 2, and is used to divide the height
  76. tile resolution to determine the bump texture resolution (f.i.
  77. a tile size of 128 with a subsampling of 4 will result in textures
  78. of a resolution of 32x32. SubSampling won't allow texture resolution
  79. to get below 16x16 (minimal bumpmap resolution). *)
  80. property SubSampling: Integer read FSubSampling write SetSubSampling
  81. default 1;
  82. property MaxPoolSize;
  83. (* If MaxTextures>0 then the Bumpmap library is trimmed down to size whenever
  84. the texture count is larger than MaxTextures. The oldest, unused texture is trimmed first.
  85. However, if you used TGLHeightData.MaterialName, instead of TGLHeightData.LibMaterial,
  86. then the TGLHeightData component does not register the texture as being used.
  87. So, if you use TGLHeightData.MaterialName then make sure MaxTextures=0.
  88. If MaxTextures=0 or if treads(GLS.AsyncHDS) are used, then the texture cache
  89. is NOT trimmed automatically.
  90. You will have to manually trim the cache from the main thread, by
  91. calling 'TrimTextureCache'. (GLS.AsyncHDS.OnIdle is a good place.) *)
  92. property MaxTextures: Integer read FMaxTextures write FMaxTextures;
  93. property OnSourceDataFetched;
  94. end;
  95. //------------------- Bumpmapping routines -----------------------
  96. (* Some useful methods for setting up bump maps. *)
  97. procedure CalcObjectSpaceLightVectors(Light : TAffineVector;
  98. Vertices: TGLAffineVectorList;
  99. Colors: TGLVectorList);
  100. procedure SetupTangentSpace(Vertices, Normals, TexCoords,
  101. Tangents, BiNormals : TGLAffineVectorList);
  102. procedure CalcTangentSpaceLightVectors(Light : TAffineVector;
  103. Vertices, Normals,
  104. Tangents, BiNormals : TGLAffineVectorList;
  105. Colors: TGLVectorList);
  106. function CreateObjectSpaceNormalMap(Width, Height : Integer;
  107. HiNormals,HiTexCoords : TGLAffineVectorList) : TBitmap;
  108. function CreateTangentSpaceNormalMap(Width, Height : Integer;
  109. HiNormals, HiTexCoords,
  110. LoNormals, LoTexCoords,
  111. Tangents, BiNormals : TGLAffineVectorList) : TBitmap;
  112. // ------------------------------------------------------------------
  113. implementation
  114. // ------------------------------------------------------------------
  115. // ------------------
  116. // ------------------ TGLBumpmapHDS ------------------
  117. // ------------------
  118. const
  119. cDefaultBumpScale = 0.01;
  120. constructor TGLBumpmapHDS.Create(AOwner: TComponent);
  121. begin
  122. inherited Create(AOwner);
  123. FBumpScale := cDefaultBumpScale;
  124. FSubSampling := 1;
  125. Uno := TCriticalSection.Create;
  126. end;
  127. destructor TGLBumpmapHDS.Destroy;
  128. begin
  129. BumpmapLibrary := nil;
  130. Uno.Free;
  131. inherited Destroy;
  132. end;
  133. procedure TGLBumpmapHDS.Notification(AComponent: TComponent;
  134. Operation: TOperation);
  135. begin
  136. if Operation = opRemove then
  137. begin
  138. if AComponent = FBumpmapLibrary then
  139. BumpmapLibrary := nil;
  140. end;
  141. inherited;
  142. end;
  143. procedure TGLBumpmapHDS.Release(aHeightData: TGLHeightData);
  144. var
  145. libMat: TGLLibMaterial;
  146. begin
  147. libMat := aHeightData.LibMaterial;
  148. aHeightData.MaterialName := '';
  149. if (FMaxTextures > 0) and (assigned(libMat)) and (libMat.IsUsed = false) then
  150. libMat.Free;
  151. inherited;
  152. end;
  153. procedure TGLBumpmapHDS.TrimTextureCache(MaxTextureCount: Integer);
  154. // Thread-safe Version
  155. begin
  156. if assigned(self) then
  157. begin
  158. Uno.Acquire;
  159. Trim(MaxTextureCount);
  160. Uno.Release;
  161. end;
  162. end;
  163. procedure TGLBumpmapHDS.Trim(MaxTextureCount: Integer); // internal use only
  164. var
  165. matLib: TGLMaterialLibrary;
  166. libMat: TGLLibMaterial;
  167. i: Integer;
  168. cnt: Integer;
  169. begin
  170. matLib := FBumpmapLibrary;
  171. if matLib <> nil then
  172. begin
  173. cnt := matLib.Materials.Count;
  174. i := 0;
  175. while (i < cnt) and (cnt >= MaxTextureCount) do
  176. begin
  177. libMat := matLib.Materials[i];
  178. if libMat.IsUsed then
  179. i := i + 1
  180. else
  181. libMat.Free;
  182. cnt := matLib.Materials.Count;
  183. end;
  184. end;
  185. end;
  186. procedure TGLBumpmapHDS.PreparingData(heightData: TGLHeightData);
  187. var
  188. TmpHD: TGLHeightData;
  189. libMat: TGLLibMaterial;
  190. bmp32: TGLImage;
  191. MatName: string;
  192. begin
  193. if not assigned(FBumpmapLibrary) then
  194. exit;
  195. // --Generate Normal Map for tile--
  196. heightData.TextureCoordinatesMode := tcmLocal;
  197. heightData.TextureCoordinatesOffset := NullTexPoint;
  198. heightData.TextureCoordinatesScale := XYTexPoint;
  199. MatName := 'BumpHDS_x' + IntToStr(heightData.XLeft) + 'y' +
  200. IntToStr(heightData.YTop) + '.';
  201. // name contains xy coordinates of the current tile
  202. Uno.Acquire;
  203. libMat := FBumpmapLibrary.Materials.GetLibMaterialByName(MatName);
  204. // Check if Tile Texture already exists
  205. if libMat = nil then
  206. begin
  207. if (FMaxTextures > 0) then
  208. begin
  209. if heightData.Thread = nil { //Dont trim the cache from a sub-thread; }
  210. then
  211. TrimTextureCache(FMaxTextures)
  212. // Trim unused textures from the material library
  213. end;
  214. // Generate new NormalMap texture for this tile
  215. libMat := FBumpmapLibrary.Materials.Add;
  216. libMat.Name := MatName;
  217. // Transfer tile texture coordinates to generated texture
  218. libMat.TextureScale.X := heightData.TextureCoordinatesScale.S;
  219. libMat.TextureScale.Y := heightData.TextureCoordinatesScale.T;
  220. libMat.TextureOffset.X := heightData.TextureCoordinatesOffset.S;
  221. libMat.TextureOffset.Y := heightData.TextureCoordinatesOffset.T;
  222. // ------------------------------------------------------
  223. // --Set up new Normalmap texture for the current tile--
  224. libMat.Material.MaterialOptions := [moNoLighting];
  225. with libMat.Material.Texture do
  226. begin
  227. ImageClassName := TGLBlankImage.ClassName;
  228. Enabled := True;
  229. MinFilter := miNearestMipmapNearest;
  230. MagFilter := maLinear; // MagFilter:=maNearest;
  231. TextureMode := tmReplace;
  232. TextureWrap := twNone;
  233. TextureFormat := tfRGB16;
  234. // TextureFormat:=tfRGBA16;
  235. bmp32 := (Image as TGLBlankImage).GetBitmap32;
  236. TmpHD := HeightDataSource.GetData(heightData.XLeft - 1,
  237. heightData.YTop - 1, heightData.Size + 1, heightData.DataType);
  238. GenerateNormalMap(TmpHD, bmp32, FBumpScale);
  239. TmpHD.Release;
  240. end;
  241. // ----------------------------------------------------
  242. end;
  243. // HD.MaterialName:=LibMat.Name;
  244. heightData.LibMaterial := libMat; // attach texture to current tile
  245. if assigned(FOnNewTilePrepared) then
  246. FOnNewTilePrepared(self, heightData, libMat);
  247. Uno.Release;
  248. end;
  249. procedure TGLBumpmapHDS.GenerateNormalMap(heightData: TGLHeightData;
  250. normalMap: TGLImage; scale: Single);
  251. var
  252. MapSize: Integer;
  253. HD: TGLHeightData;
  254. X, Y: Integer;
  255. scaleVec: TAffineVector;
  256. vec: TAffineVector;
  257. nmRow: PGLPixel32Array;
  258. px, py: Integer;
  259. begin
  260. HD := heightData;
  261. MapSize := (HD.Size - 1);
  262. MapSize := MapSize div SubSampling;
  263. normalMap.Height := MapSize;
  264. normalMap.Width := MapSize;
  265. normalMap.Blank := false;
  266. SetVector(scaleVec, 1, 1, FBumpScale);
  267. for Y := 0 to MapSize - 1 do
  268. begin
  269. nmRow := normalMap.ScanLine[MapSize - 1 - Y];
  270. for X := 0 to MapSize - 1 do
  271. begin
  272. px := X * SubSampling;
  273. py := Y * SubSampling;
  274. vec := HD.NormalAtNode(px, py, scaleVec);
  275. nmRow[X].r := round(128 + 127 * vec.X); // nmRow[x].r:=0; //Red
  276. nmRow[X].g := round(128 + 127 * vec.Y);
  277. // nmRow[x].g:=0; //Green
  278. nmRow[X].b := round(128 + 127 * vec.Z);
  279. // nmRow[x].b:=0; //Blue
  280. nmRow[X].a := 255;
  281. end;
  282. end;
  283. end;
  284. procedure TGLBumpmapHDS.SetBumpmapLibrary(const val: TGLMaterialLibrary);
  285. begin
  286. if val <> FBumpmapLibrary then
  287. begin
  288. if assigned(FBumpmapLibrary) then
  289. FBumpmapLibrary.RemoveFreeNotification(self);
  290. FBumpmapLibrary := val;
  291. if assigned(FBumpmapLibrary) then
  292. FBumpmapLibrary.FreeNotification(self);
  293. MarkDirty;
  294. end;
  295. end;
  296. procedure TGLBumpmapHDS.SetBumpScale(const val: Single);
  297. begin
  298. if FBumpScale <> val then
  299. begin
  300. FBumpScale := val;
  301. MarkDirty;
  302. end;
  303. end;
  304. function TGLBumpmapHDS.StoreBumpScale: Boolean;
  305. begin
  306. Result := (FBumpScale <> cDefaultBumpScale);
  307. end;
  308. procedure TGLBumpmapHDS.SetSubSampling(const val: Integer);
  309. begin
  310. if val <> FSubSampling then
  311. begin
  312. FSubSampling := RoundDownToPowerOf2(val);
  313. if FSubSampling < 1 then
  314. FSubSampling := 1;
  315. MarkDirty;
  316. end;
  317. end;
  318. //----------------- BumpMapping routines ---------------------
  319. procedure CalcObjectSpaceLightVectors(Light: TAffineVector; Vertices: TGLAffineVectorList; Colors: TGLVectorList);
  320. var
  321. i: Integer;
  322. vec: TAffineVector;
  323. begin
  324. Colors.Count := Vertices.Count;
  325. for i := 0 to Vertices.Count - 1 do
  326. begin
  327. vec := VectorNormalize(VectorSubtract(Light, Vertices[i]));
  328. Colors[i] := VectorMake(VectorAdd(VectorScale(vec, 0.5), 0.5), 1);
  329. end;
  330. end;
  331. procedure SetupTangentSpace(Vertices, Normals, TexCoords, Tangents, BiNormals: TGLAffineVectorList);
  332. var
  333. i, j: Integer;
  334. v, n, t: TAffineMatrix;
  335. vt, tt: TAffineVector;
  336. interp, dot: Single;
  337. procedure SortVertexData(sortidx: Integer);
  338. begin
  339. if t.v[0].V[sortidx] < t.v[1].V[sortidx] then
  340. begin
  341. vt := v.v[0];
  342. tt := t.v[0];
  343. v.v[0] := v.v[1];
  344. t.v[0] := t.v[1];
  345. v.v[1] := vt;
  346. t.v[1] := tt;
  347. end;
  348. if t.v[0].V[sortidx] < t.v[2].V[sortidx] then
  349. begin
  350. vt := v.v[0];
  351. tt := t.v[0];
  352. v.v[0] := v.v[2];
  353. t.v[0] := t.v[2];
  354. v.v[2] := vt;
  355. t.v[2] := tt;
  356. end;
  357. if t.v[1].V[sortidx] < t.v[2].V[sortidx] then
  358. begin
  359. vt := v.v[1];
  360. tt := t.v[1];
  361. v.v[1] := v.v[2];
  362. t.v[1] := t.v[2];
  363. v.v[2] := vt;
  364. t.v[2] := tt;
  365. end;
  366. end;
  367. begin
  368. for i := 0 to (Vertices.Count div 3) - 1 do
  369. begin
  370. // Get triangle data
  371. for j := 0 to 2 do
  372. begin
  373. v.v[j] := Vertices[3 * i + j];
  374. n.v[j] := Normals[3 * i + j];
  375. t.v[j] := TexCoords[3 * i + j];
  376. end;
  377. for j := 0 to 2 do
  378. begin
  379. // Compute tangent
  380. SortVertexData(1);
  381. if (t.v[2].Y - t.v[0].Y) = 0 then
  382. interp := 1
  383. else
  384. interp := (t.v[1].Y - t.v[0].Y) / (t.v[2].Y - t.v[0].Y);
  385. vt := VectorLerp(v.v[0], v.v[2], interp);
  386. interp := t.v[0].X + (t.v[2].X - t.v[0].X) * interp;
  387. vt := VectorSubtract(vt, v.v[1]);
  388. if t.v[1].X < interp then
  389. vt := VectorNegate(vt);
  390. dot := VectorDotProduct(vt, n.v[j]);
  391. vt.X := vt.X - n.v[j].X * dot;
  392. vt.Y := vt.Y - n.v[j].Y * dot;
  393. vt.Z := vt.Z - n.v[j].Z * dot;
  394. Tangents.Add(VectorNormalize(vt));
  395. // Compute Bi-Normal
  396. SortVertexData(0);
  397. if (t.v[2].X - t.v[0].X) = 0 then
  398. interp := 1
  399. else
  400. interp := (t.v[1].X - t.v[0].X) / (t.v[2].X - t.v[0].X);
  401. vt := VectorLerp(v.v[0], v.v[2], interp);
  402. interp := t.v[0].Y + (t.v[2].Y - t.v[0].Y) * interp;
  403. vt := VectorSubtract(vt, v.v[1]);
  404. if t.v[1].Y < interp then
  405. vt := VectorNegate(vt);
  406. dot := VectorDotProduct(vt, n.v[j]);
  407. vt.X := vt.X - n.v[j].X * dot;
  408. vt.Y := vt.Y - n.v[j].Y * dot;
  409. vt.Z := vt.Z - n.v[j].Z * dot;
  410. BiNormals.Add(VectorNormalize(vt));
  411. end;
  412. end;
  413. end;
  414. procedure CalcTangentSpaceLightVectors(Light: TAffineVector;
  415. Vertices, Normals, Tangents, BiNormals: TGLAffineVectorList;
  416. Colors: TGLVectorList);
  417. var
  418. i: Integer;
  419. mat: TAffineMatrix;
  420. vec: TAffineVector;
  421. begin
  422. Colors.Count := Vertices.Count;
  423. for i := 0 to Vertices.Count - 1 do
  424. begin
  425. mat.v[0] := Tangents[i];
  426. mat.v[1] := BiNormals[i];
  427. mat.v[2] := Normals[i];
  428. TransposeMatrix(mat);
  429. vec := VectorNormalize(VectorTransform(VectorSubtract(Light, Vertices[i]), mat));
  430. vec.X := -vec.X;
  431. Colors[i] := VectorMake(VectorAdd(VectorScale(vec, 0.5), 0.5), 1);
  432. end;
  433. end;
  434. // ------------------------------------------------------------------------
  435. // Local functions used for creating normal maps
  436. // ------------------------------------------------------------------------
  437. function ConvertNormalToColor(normal: TAffineVector): TColor;
  438. var
  439. r, g, b: Byte;
  440. begin
  441. r := Round(255 * (normal.X * 0.5 + 0.5));
  442. g := Round(255 * (normal.Y * 0.5 + 0.5));
  443. b := Round(255 * (normal.Z * 0.5 + 0.5));
  444. Result := RGB2Color(r, g, b);
  445. end;
  446. procedure GetBlendCoeffs(X, Y, x1, y1, x2, y2, x3, y3: Integer; var f1, f2, f3: Single);
  447. var
  448. m1, m2, d1, d2, px, py: Single;
  449. begin
  450. if (x1 = X) and (x2 = x3) then
  451. f1 := 0
  452. else
  453. begin
  454. if x1 = X then
  455. begin
  456. m2 := (y3 - y2) / (x3 - x2);
  457. d2 := y2 - m2 * x2;
  458. px := X;
  459. py := m2 * px + d2;
  460. end
  461. else if x2 = x3 then
  462. begin
  463. m1 := (y1 - Y) / (x1 - X);
  464. d1 := y1 - m1 * x1;
  465. px := x2;
  466. py := m1 * px + d1;
  467. end
  468. else
  469. begin
  470. m1 := (y1 - Y) / (x1 - X);
  471. d1 := y1 - m1 * x1;
  472. m2 := (y3 - y2) / (x3 - x2);
  473. d2 := y2 - m2 * x2;
  474. px := (d1 - d2) / (m2 - m1);
  475. py := m2 * px + d2;
  476. end;
  477. f1 := sqrt((X - x1) * (X - x1) + (Y - y1) * (Y - y1)) /
  478. sqrt((px - x1) * (px - x1) + (py - y1) * (py - y1));
  479. end;
  480. if (x2 = X) and (x1 = x3) then
  481. f2 := 0
  482. else
  483. begin
  484. if x2 = X then
  485. begin
  486. m2 := (y3 - y1) / (x3 - x1);
  487. d2 := y1 - m2 * x1;
  488. px := X;
  489. py := m2 * px + d2;
  490. end
  491. else if x3 = x1 then
  492. begin
  493. m1 := (y2 - Y) / (x2 - X);
  494. d1 := y2 - m1 * x2;
  495. px := x1;
  496. py := m1 * px + d1;
  497. end
  498. else
  499. begin
  500. m1 := (y2 - Y) / (x2 - X);
  501. d1 := y2 - m1 * x2;
  502. m2 := (y3 - y1) / (x3 - x1);
  503. d2 := y1 - m2 * x1;
  504. px := (d1 - d2) / (m2 - m1);
  505. py := m2 * px + d2;
  506. end;
  507. f2 := sqrt((X - x2) * (X - x2) + (Y - y2) * (Y - y2)) /
  508. sqrt((px - x2) * (px - x2) + (py - y2) * (py - y2));
  509. end;
  510. if (x3 = X) and (x1 = x2) then
  511. f3 := 0
  512. else
  513. begin
  514. if X = x3 then
  515. begin
  516. m2 := (y2 - y1) / (x2 - x1);
  517. d2 := y1 - m2 * x1;
  518. px := X;
  519. py := m2 * px + d2;
  520. end
  521. else if x2 = x1 then
  522. begin
  523. m1 := (y3 - Y) / (x3 - X);
  524. d1 := y3 - m1 * x3;
  525. px := x1;
  526. py := m1 * px + d1;
  527. end
  528. else
  529. begin
  530. m1 := (y3 - Y) / (x3 - X);
  531. d1 := y3 - m1 * x3;
  532. m2 := (y2 - y1) / (x2 - x1);
  533. d2 := y1 - m2 * x1;
  534. px := (d1 - d2) / (m2 - m1);
  535. py := m2 * px + d2;
  536. end;
  537. f3 := sqrt((X - x3) * (X - x3) + (Y - y3) * (Y - y3)) /
  538. sqrt((px - x3) * (px - x3) + (py - y3) * (py - y3));
  539. end;
  540. end;
  541. function BlendNormals(X, Y, x1, y1, x2, y2, x3, y3: Integer;
  542. n1, n2, n3: TAffineVector): TAffineVector;
  543. var
  544. f1, f2, f3: Single;
  545. begin
  546. GetBlendCoeffs(X, Y, x1, y1, x2, y2, x3, y3, f1, f2, f3);
  547. Result := VectorScale(n1, 1 - f1);
  548. AddVector(Result, VectorScale(n2, 1 - f2));
  549. AddVector(Result, VectorScale(n3, 1 - f3));
  550. end;
  551. procedure CalcObjectSpaceNormalMap(Width, Height: Integer;
  552. NormalMap, Normals, TexCoords: TGLAffineVectorList);
  553. var
  554. i, X, Y, xs, xe, x1, y1, x2, y2, x3, y3: Integer;
  555. n, n1, n2, n3: TAffineVector;
  556. begin
  557. for i := 0 to (TexCoords.Count div 3) - 1 do
  558. begin
  559. x1 := Round(TexCoords[3 * i].X * (Width - 1));
  560. y1 := Round((1 - TexCoords[3 * i].Y) * (Height - 1));
  561. x2 := Round(TexCoords[3 * i + 1].X * (Width - 1));
  562. y2 := Round((1 - TexCoords[3 * i + 1].Y) * (Height - 1));
  563. x3 := Round(TexCoords[3 * i + 2].X * (Width - 1));
  564. y3 := Round((1 - TexCoords[3 * i + 2].Y) * (Height - 1));
  565. n1 := Normals[3 * i];
  566. n2 := Normals[3 * i + 1];
  567. n3 := Normals[3 * i + 2];
  568. if y2 < y1 then
  569. begin
  570. X := x1;
  571. Y := y1;
  572. n := n1;
  573. x1 := x2;
  574. y1 := y2;
  575. n1 := n2;
  576. x2 := X;
  577. y2 := Y;
  578. n2 := n;
  579. end;
  580. if y3 < y1 then
  581. begin
  582. X := x1;
  583. Y := y1;
  584. n := n1;
  585. x1 := x3;
  586. y1 := y3;
  587. n1 := n3;
  588. x3 := X;
  589. y3 := Y;
  590. n3 := n;
  591. end;
  592. if y3 < y2 then
  593. begin
  594. X := x2;
  595. Y := y2;
  596. n := n2;
  597. x2 := x3;
  598. y2 := y3;
  599. n2 := n3;
  600. x3 := X;
  601. y3 := Y;
  602. n3 := n;
  603. end;
  604. if y1 < y2 then
  605. for Y := y1 to y2 do
  606. begin
  607. xs := Round(x1 + (x2 - x1) * ((Y - y1) / (y2 - y1)));
  608. xe := Round(x1 + (x3 - x1) * ((Y - y1) / (y3 - y1)));
  609. if xe < xs then
  610. begin
  611. X := xs;
  612. xs := xe;
  613. xe := X;
  614. end;
  615. for X := xs to xe do
  616. NormalMap[X + Y * Width] := BlendNormals(X, Y, x1, y1, x2, y2, x3, y3, n1, n2, n3);
  617. end;
  618. if y2 < y3 then
  619. for Y := y2 to y3 do
  620. begin
  621. xs := Round(x2 + (x3 - x2) * ((Y - y2) / (y3 - y2)));
  622. xe := Round(x1 + (x3 - x1) * ((Y - y1) / (y3 - y1)));
  623. if xe < xs then
  624. begin
  625. X := xs;
  626. xs := xe;
  627. xe := X;
  628. end;
  629. for X := xs to xe do
  630. NormalMap[X + Y * Width] := BlendNormals(X, Y, x1, y1, x2, y2, x3, y3, n1, n2, n3);
  631. end;
  632. end;
  633. end;
  634. function CreateObjectSpaceNormalMap(Width, Height: Integer;
  635. HiNormals, HiTexCoords: TGLAffineVectorList): TBitmap;
  636. var
  637. i: Integer;
  638. NormalMap: TGLAffineVectorList;
  639. begin
  640. NormalMap := TGLAffineVectorList.Create;
  641. NormalMap.AddNulls(Width * Height);
  642. CalcObjectSpaceNormalMap(Width, Height, NormalMap, HiNormals, HiTexCoords);
  643. // Creates the bitmap
  644. Result := TBitmap.Create;
  645. Result.Width := Width;
  646. Result.Height := Height;
  647. Result.PixelFormat := pf24bit;
  648. // Paint bitmap with normal map normals (X,Y,Z) -> (R,G,B)
  649. for i := 0 to NormalMap.Count - 1 do
  650. Result.Canvas.Pixels[i mod Width, i div Height] := ConvertNormalToColor(NormalMap[i]);
  651. NormalMap.Free;
  652. end;
  653. function CreateTangentSpaceNormalMap(Width, Height: Integer; HiNormals, HiTexCoords, LoNormals, LoTexCoords, Tangents,
  654. BiNormals: TGLAffineVectorList): TBitmap;
  655. function NormalToTangentSpace(normal: TAffineVector; X, Y, x1, y1, x2, y2, x3, y3: Integer; m1, m2, m3: TAffineMatrix)
  656. : TAffineVector;
  657. var
  658. n1, n2, n3: TAffineVector;
  659. begin
  660. n1 := VectorTransform(normal, m1);
  661. n2 := VectorTransform(normal, m2);
  662. n3 := VectorTransform(normal, m3);
  663. Result := BlendNormals(X, Y, x1, y1, x2, y2, x3, y3, n1, n2, n3);
  664. NormalizeVector(Result);
  665. end;
  666. var
  667. i, X, Y, xs, xe, x1, y1, x2, y2, x3, y3: Integer;
  668. NormalMap: TGLAffineVectorList;
  669. n: TAffineVector;
  670. m, m1, m2, m3: TAffineMatrix;
  671. begin
  672. NormalMap := TGLAffineVectorList.Create;
  673. NormalMap.AddNulls(Width * Height);
  674. CalcObjectSpaceNormalMap(Width, Height, NormalMap, HiNormals, HiTexCoords);
  675. // Transform the object space normals into tangent space
  676. for i := 0 to (LoTexCoords.Count div 3) - 1 do
  677. begin
  678. x1 := Round(LoTexCoords[3 * i].X * (Width - 1));
  679. y1 := Round((1 - LoTexCoords[3 * i].Y) * (Height - 1));
  680. x2 := Round(LoTexCoords[3 * i + 1].X * (Width - 1));
  681. y2 := Round((1 - LoTexCoords[3 * i + 1].Y) * (Height - 1));
  682. x3 := Round(LoTexCoords[3 * i + 2].X * (Width - 1));
  683. y3 := Round((1 - LoTexCoords[3 * i + 2].Y) * (Height - 1));
  684. m1.X := Tangents[3 * i];
  685. m1.Y := BiNormals[3 * i];
  686. m1.Z := LoNormals[3 * i];
  687. m2.X := Tangents[3 * i + 1];
  688. m2.Y := BiNormals[3 * i + 1];
  689. m2.Z := LoNormals[3 * i + 1];
  690. m3.X := Tangents[3 * i + 2];
  691. m3.Y := BiNormals[3 * i + 2];
  692. m3.Z := LoNormals[3 * i + 2];
  693. TransposeMatrix(m1);
  694. TransposeMatrix(m2);
  695. TransposeMatrix(m3);
  696. InvertMatrix(m1);
  697. InvertMatrix(m2);
  698. InvertMatrix(m3);
  699. if y2 < y1 then
  700. begin
  701. X := x1;
  702. Y := y1;
  703. m := m1;
  704. x1 := x2;
  705. y1 := y2;
  706. m1 := m2;
  707. x2 := X;
  708. y2 := Y;
  709. m2 := m;
  710. end;
  711. if y3 < y1 then
  712. begin
  713. X := x1;
  714. Y := y1;
  715. m := m1;
  716. x1 := x3;
  717. y1 := y3;
  718. m1 := m3;
  719. x3 := X;
  720. y3 := Y;
  721. m3 := m;
  722. end;
  723. if y3 < y2 then
  724. begin
  725. X := x2;
  726. Y := y2;
  727. m := m2;
  728. x2 := x3;
  729. y2 := y3;
  730. m2 := m3;
  731. x3 := X;
  732. y3 := Y;
  733. m3 := m;
  734. end;
  735. if y1 < y2 then
  736. for Y := y1 to y2 do
  737. begin
  738. xs := Round(x1 + (x2 - x1) * ((Y - y1) / (y2 - y1)));
  739. xe := Round(x1 + (x3 - x1) * ((Y - y1) / (y3 - y1)));
  740. if xe < xs then
  741. begin
  742. X := xs;
  743. xs := xe;
  744. xe := X;
  745. end;
  746. for X := xs to xe - 1 do
  747. begin
  748. n := NormalToTangentSpace(NormalMap[X + Y * Width], X, Y, x1, y1, x2, y2, x3, y3, m1, m2, m3);
  749. NormalizeVector(n);
  750. n.X := -n.X;
  751. NormalMap[X + Y * Width] := n;
  752. end;
  753. end;
  754. if y2 < y3 then
  755. for Y := y2 + 1 to y3 do
  756. begin
  757. xs := Round(x2 + (x3 - x2) * ((Y - y2) / (y3 - y2)));
  758. xe := Round(x1 + (x3 - x1) * ((Y - y1) / (y3 - y1)));
  759. if xe < xs then
  760. begin
  761. X := xs;
  762. xs := xe;
  763. xe := X;
  764. end;
  765. for X := xs to xe - 1 do
  766. begin
  767. n := NormalToTangentSpace(NormalMap[X + Y * Width], X, Y, x1, y1, x2, y2, x3, y3, m1, m2, m3);
  768. NormalizeVector(n);
  769. n.X := -n.X;
  770. NormalMap[X + Y * Width] := n;
  771. end;
  772. end;
  773. end;
  774. // Creates the bitmap
  775. Result := TBitmap.Create;
  776. Result.Width := Width;
  777. Result.Height := Height;
  778. Result.PixelFormat := pf24bit;
  779. // Paint bitmap with normal map normals (X,Y,Z) -> (R,G,B)
  780. for i := 0 to NormalMap.Count - 1 do
  781. Result.Canvas.Pixels[i mod Width, i div Height] := ConvertNormalToColor(NormalMap[i]);
  782. NormalMap.Free;
  783. end;
  784. // ------------------------------------------------------------------
  785. initialization
  786. // ------------------------------------------------------------------
  787. RegisterClass(TGLBumpmapHDS);
  788. end.