123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370 |
- unit GBE.Heightmap;
- (*
- The TGBEHeightmap allows you to generate a heightmap.
- Based on code by Gregory Bersegeay
- *)
- interface
- uses
- System.Types,
- System.SysUtils,
- System.Classes,
- System.RTLConsts,
- System.Math,
- System.UITypes,
- System.UIConsts,
- FMX.Types,
- FMX.Controls3D,
- FMX.Objects3D,
- FMX.Graphics,
- System.Math.Vectors,
- FMX.types3D,
- FMX.Effects,
- FMX.MaterialSources,
- uGBEUtils3D;
- type
- TGBEHeightmap = class(TMesh)
- private
- fSubdivisionsX, fSubdivisionsZ, fFlou, fHalfSubdivisionsX,
- fHalfSubdivisionsZ: integer;
- fHeightmap: TBitmap;
- fTracerLignes, FUseRamp: boolean;
- fMaterialLignes: TColorMaterialSource;
- fMiseAEchelle, fMaxHauteur, fMinHauteur: single;
- function GetFlou: integer;
- procedure SetFlou(const Value: integer);
- function GetTracerLignes: boolean;
- procedure SetTracerLignes(const Value: boolean);
- procedure SetUseRamp(const Value: boolean);
- procedure GenerateHeightmap(Const aData: TMeshData);
- protected
- procedure Render; override;
- public
- constructor Create(AOwner: TComponent); override;
- destructor Destroy; override;
- procedure RebuildMesh;
- procedure LoadHeightmapFromFile(FileName: string);
- procedure LoadHeightmapFromStream(Stream: TStream);
- procedure LoadHeightmapFromResource(ResourceName: string);
- function GetHeight(P: TPoint3d): single;
- published
- property Flou: integer read GetFlou write SetFlou;
- property ShowLines: boolean read GetTracerLignes write SetTracerLignes;
- property MaterialLines: TColorMaterialSource read fMaterialLignes
- write fMaterialLignes;
- property MinHeight: single read fMinHauteur;
- property MaxHeight: single read fMaxHauteur;
- property Locked default True;
- property HitTest default False;
- property UseRamp: boolean read FUseRamp write SetUseRamp;
- property TwoSide default True;
- property Visible default True;
- property ZWrite default True;
- property MiseAEchelle: single read fMiseAEchelle;
- end;
- procedure Register;
- implementation //--------------------------------------------------------------
- // TGBEHeightmap
- constructor TGBEHeightmap.Create(AOwner: TComponent);
- begin
- inherited;
- fSubdivisionsX := 0;
- fHalfSubdivisionsX := 0;
- fSubdivisionsZ := 0;
- fHalfSubdivisionsZ := 0;
- fHeightmap := TBitmap.Create;
- ShowLines := False;
- UseRamp := False;
- HitTest := False;
- rotationAngle.X := 180;
- fMiseAEchelle := 1;
- fMaxHauteur := 0;
- fMinHauteur := 0;
- end;
- destructor TGBEHeightmap.Destroy;
- begin
- FreeAndNil(fHeightmap);
- inherited;
- end;
- function TGBEHeightmap.GetFlou: integer;
- begin
- result := fFlou;
- end;
- function TGBEHeightmap.GetHeight(P: TPoint3d): single;
- begin
- result := CalculateHeight(self, P, self.fMiseAEchelle, fSubdivisionsX,
- fSubdivisionsZ);
- end;
- function TGBEHeightmap.GetTracerLignes: boolean;
- begin
- result := fTracerLignes;
- end;
- procedure TGBEHeightmap.LoadHeightmapFromFile(FileName: string);
- begin
- if FileExists(FileName) then
- begin
- self.Data.Clear;
- fHeightmap.LoadFromFile(FileName);
- fSubdivisionsX := fHeightmap.Width;
- fHalfSubdivisionsX := Floor(fSubdivisionsX / 2);
- fSubdivisionsZ := fHeightmap.Height;
- fHalfSubdivisionsZ := Floor(fSubdivisionsZ / 2);
- GenerateHeightmap(self.Data);
- end;
- end;
- procedure TGBEHeightmap.LoadHeightmapFromStream(Stream: TStream);
- begin
- self.Data.Clear;
- fHeightmap.LoadFromStream(Stream);
- fSubdivisionsX := fHeightmap.Width;
- fHalfSubdivisionsX := Floor(fSubdivisionsX / 2);
- fSubdivisionsZ := fHeightmap.Height;
- fHalfSubdivisionsZ := Floor(fSubdivisionsZ / 2);
- GenerateHeightmap(self.Data);
- end;
- procedure TGBEHeightmap.LoadHeightmapFromResource(ResourceName: string);
- var
- Stream: TResourceStream;
- begin
- Stream := TResourceStream.Create(HInstance, ResourceName, RT_RCDATA);
- LoadHeightmapFromStream(Stream);
- Stream.Free;
- end;
- procedure TGBEHeightmap.RebuildMesh;
- begin
- GenerateHeightmap(self.Data);
- end;
- procedure TGBEHeightmap.Render;
- begin
- inherited;
- if ShowLines then
- begin
- Context.DrawLines(self.Data.VertexBuffer, self.Data.IndexBuffer,
- TMaterialSource.ValidMaterial(fMaterialLignes), 1);
- end;
- end;
- procedure TGBEHeightmap.SetFlou(const Value: integer);
- begin
- if Value <> fFlou then
- begin
- fFlou := Value;
- GenerateHeightmap(self.Data);
- end;
- end;
- procedure TGBEHeightmap.SetTracerLignes(const Value: boolean);
- begin
- if Value <> fTracerLignes then
- fTracerLignes := Value;
- end;
- procedure TGBEHeightmap.SetUseRamp(const Value: boolean);
- begin
- if Value <> FUseRamp then
- begin
- FUseRamp := Value;
- fSubdivisionsX := fHeightmap.Width;
- fHalfSubdivisionsX := Floor(fSubdivisionsX / 2);
- fSubdivisionsZ := fHeightmap.Height;
- fHalfSubdivisionsZ := Floor(fSubdivisionsZ / 2);
- GenerateHeightmap(self.Data);
- end;
- end;
- procedure TGBEHeightmap.GenerateHeightmap(Const aData: TMeshData);
- var
- SubMap: TBitmap;
- // Bitmap which will be used to generate the relief from the heightmap
- zMap: single;
- C: TAlphaColorRec;
- // Color read from the heightmap and used to determine the height of a vertex
- bitmapData: TBitmapData;
- D: TMeshData;
- u, v: Double;
- P: array [0 .. 3] of TPoint3d;
- decallage: Double;
- NP, NI: integer;
- MaxX, MaxZ, MaxX_1, MaxZ_1: Double;
- begin
- if fSubdivisionsX < 1 then
- exit; // at least one subdivision is required
- if fSubdivisionsZ < 1 then
- exit; // the same
- decallage := 1;
- NP := 0;
- NI := 0;
- fMaxHauteur := 0;
- fMinHauteur := 0;
- MaxX := fHeightmap.Width * 0.5;
- MaxZ := fHeightmap.Height * 0.5;
- MaxX_1 := MaxX - 1;
- MaxZ_1 := MaxZ - 1;
- try
- D := TMeshData.Create;
- D.VertexBuffer.Length := Round(2 * MaxX * 2 * MaxZ) * 4;
- D.IndexBuffer.Length := Round(2 * MaxX * 2 * MaxZ) * 6;
- SubMap := TBitmap.Create(fHeightmap.Width, fHeightmap.Height);
- // Creating the bitmap
- SubMap.Assign(fHeightmap); // We load the heightmap
- Blur(SubMap.canvas, SubMap, Flou);
- if (SubMap.Map(TMapAccess.Read, bitmapData)) then
- // needed to access the pixel of the Bitmap in order to retrieve the color
- begin
- v := -MaxZ;
- while v < MaxZ do
- begin
- u := -MaxX;
- while u < MaxX do
- begin
- P[0].X := u;
- P[0].Z := v;
- // Retrieves the color of the corresponding pixel in the heightmap
- C := TAlphaColorRec
- (CorrectColor(bitmapData.GetPixel(Trunc(P[0].X + MaxX_1),
- Trunc(P[0].Z + MaxZ_1))));
- zMap := C.R;
- // (C.R + C.G + C.B ); // Determination of peak height based on color
- P[0].Y := zMap;
- if zMap > fMaxHauteur then
- fMaxHauteur := zMap;
- if zMap < fMinHauteur then
- fMinHauteur := zMap;
- P[1].X := u + decallage;
- P[1].Z := v;
- // Retrieves the color of the corresponding pixel in the heightmap
- C := TAlphaColorRec
- (CorrectColor(bitmapData.GetPixel(Trunc(P[1].X + MaxX_1),
- Trunc(P[1].Z + MaxZ_1))));
- zMap := C.R;
- // (C.R + C.G + C.B ); // détermination de la hauteur du sommet en fonction de la couleur
- P[1].Y := zMap;
- if zMap > fMaxHauteur then
- fMaxHauteur := zMap;
- if zMap < fMinHauteur then
- fMinHauteur := zMap;
- P[2].X := u + decallage;
- P[2].Z := v + decallage;
- // Retrieves the color of the corresponding pixel in the heightmap
- C := TAlphaColorRec
- (CorrectColor(bitmapData.GetPixel(Trunc(P[2].X + MaxX_1),
- Trunc(P[2].Z + MaxZ_1))));
- zMap := C.R;
- // (C.R + C.G + C.B ); // détermination de la hauteur du sommet en fonction de la couleur
- P[2].Y := zMap;
- if zMap > fMaxHauteur then
- fMaxHauteur := zMap;
- if zMap < fMinHauteur then
- fMinHauteur := zMap;
- P[3].X := u;
- P[3].Z := v + decallage;
- // Retrieves the color of the corresponding pixel in the heightmap
- C := TAlphaColorRec
- (CorrectColor(bitmapData.GetPixel(Trunc(P[3].X + MaxX_1),
- Trunc(P[3].Z + MaxZ_1))));
- zMap := C.R;
- // (C.R + C.G + C.B ); // détermination de la hauteur du sommet en fonction de la couleur
- P[3].Y := zMap;
- if zMap > fMaxHauteur then
- fMaxHauteur := zMap;
- if zMap < fMinHauteur then
- fMinHauteur := zMap;
- with D do
- begin
- with VertexBuffer do
- begin
- Vertices[NP + 0] := P[0];
- Vertices[NP + 1] := P[1];
- Vertices[NP + 2] := P[2];
- Vertices[NP + 3] := P[3];
- end;
- with VertexBuffer do
- begin
- if FUseRamp then
- begin
- TexCoord0[NP + 0] := PointF((abs(P[0].Y)) / 255, 0);
- TexCoord0[NP + 1] := PointF((abs(P[1].Y)) / 255, 0);
- TexCoord0[NP + 2] := PointF((abs(P[2].Y)) / 255, 0);
- TexCoord0[NP + 3] := PointF((abs(P[3].Y)) / 255, 0);
- end
- else
- begin
- begin
- TexCoord0[NP + 0] := PointF((P[0].X + MaxX) / fSubdivisionsX,
- (P[0].Z + MaxZ) / fSubdivisionsZ);
- TexCoord0[NP + 1] := PointF((P[1].X + MaxX) / fSubdivisionsX,
- (P[1].Z + MaxZ) / fSubdivisionsZ);
- TexCoord0[NP + 2] := PointF((P[2].X + MaxX) / fSubdivisionsX,
- (P[2].Z + MaxZ) / fSubdivisionsZ);
- TexCoord0[NP + 3] := PointF((P[3].X + MaxX) / fSubdivisionsX,
- (P[3].Z + MaxZ) / fSubdivisionsZ);
- end;
- end;
- end;
- IndexBuffer[NI + 0] := NP + 0;
- IndexBuffer[NI + 1] := NP + 1;
- IndexBuffer[NI + 2] := NP + 3;
- IndexBuffer[NI + 3] := NP + 3;
- IndexBuffer[NI + 4] := NP + 1;
- IndexBuffer[NI + 5] := NP + 2;
- end;
- NP := NP + 4;
- NI := NI + 6;
- u := u + decallage;
- end;
- v := v + decallage;
- end;
- end;
- D.CalcTangentBinormals;
- // Calculation of binormal vectors and tangent for all faces (for example, allows for better reaction to light)
- /// CalcFaceNormals;
- aData.Clear;
- aData.Assign(D);
- if fMaxHauteur <> fMinHauteur then
- fMiseAEchelle := self.Height / (fMaxHauteur - fMinHauteur)
- else
- fMiseAEchelle := 0;
- finally
- FreeAndNil(SubMap);
- FreeAndNil(D);
- end;
- end;
- // ----------------------------------------------------------------------
- procedure Register;
- begin
- RegisterComponents('GXScene GBE', [TGBEHeightmap]);
- end;
- end.
|