unit fSynthTerrain; interface uses Winapi.Windows, Winapi.OpenGL, System.SysUtils, System.Classes, System.UITypes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.ExtCtrls, Vcl.StdCtrls, Vcl.Imaging.Jpeg, GLS.Scene, GLS.Objects, GLS.TerrainRenderer, GLS.HeightData, GLS.Cadencer, GLS.VectorTypes, GLS.Texture, GLS.SceneViewer, GLS.VectorGeometry, GLS.Material, GLS.Coordinates, GLS.BaseClasses, GLS.Keyboard, GLS.ShadowHDS; type TFormSynthTerrain = class(TForm) GLSceneViewer1: TGLSceneViewer; GLScene1: TGLScene; GLCamera1: TGLCamera; DummyCube1: TGLDummyCube; TerrainRenderer1: TGLTerrainRenderer; Timer1: TTimer; GLCadencer1: TGLCadencer; GLMaterialLibrary1: TGLMaterialLibrary; GLCustomHDS: TGLCustomHDS; GLShadowHDS: TGLShadowHDS; procedure GLSceneViewer1MouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer); procedure GLSceneViewer1MouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer); procedure Timer1Timer(Sender: TObject); procedure GLCadencer1Progress(Sender: TObject; const deltaTime, newTime: Double); procedure FormCreate(Sender: TObject); procedure FormKeyPress(Sender: TObject; var Key: Char); procedure GLCustomHDSStartPreparingData(HeightData: TGLHeightData); public mx, my: Integer; fullScreen: Boolean; FCamHeight: Single; end; var FormSynthTerrain: TFormSynthTerrain; implementation {$R *.DFM} procedure TFormSynthTerrain.FormCreate(Sender: TObject); var i: Integer; bmp: TBitmap; begin // 8 MB height data cache // Note this is the data size in terms of elevation samples, it does not // take into account all the data required/allocated by the renderer GLCustomHDS.MaxPoolSize := 8 * 1024 * 1024; // Move camera starting point to an interesting hand-picked location DummyCube1.Position.X := 50; DummyCube1.Position.Z := 150; // Initial camera height offset (controled with pageUp/pageDown) FCamHeight := 20; // We build several basic 1D textures which are just color ramps // all use automatic texture mapping corodinates, in ObjectLinear method // (ie. texture coordinates for a vertex depend on that vertex coordinates) bmp := TBitmap.Create; bmp.PixelFormat := pf24bit; bmp.Width := 256; bmp.Height := 1; // Black-White ramp, autotexture maps to Z coordinate // This one changes with altitude, this is a quick way to obtain // altitude-dependant coloring for i := 0 to 255 do bmp.Canvas.Pixels[i, 0] := RGB(i, i, i); with GLMaterialLibrary1.AddTextureMaterial('BW', bmp) do begin Material.Texture.MappingMode := tmmObjectLinear; Material.Texture.MappingSCoordinates.AsVector := VectorMake(0, 0, 0.0001, 0); end; // Red, Blue map linearly to X and Y axis respectively for i := 0 to 255 do bmp.Canvas.Pixels[i, 0] := RGB(i, 0, 0); with GLMaterialLibrary1.AddTextureMaterial('Red', bmp) do begin Material.Texture.MappingMode := tmmObjectLinear; Material.Texture.MappingSCoordinates.AsVector := VectorMake(0.1, 0, 0, 0); end; for i := 0 to 255 do bmp.Canvas.Pixels[i, 0] := RGB(0, 0, i); with GLMaterialLibrary1.AddTextureMaterial('Blue', bmp) do begin Material.Texture.MappingMode := tmmObjectLinear; Material.Texture.MappingSCoordinates.AsVector := VectorMake(0, 0.1, 0, 0); end; bmp.Free; TerrainRenderer1.MaterialLibrary := GLMaterialLibrary1; end; // // The beef : this event does all the interesting elevation data stuff // procedure TFormSynthTerrain.GLCustomHDSStartPreparingData(HeightData: TGLHeightData); var Y, X: Integer; rasterLine: PByteArray; oldType: TGLHeightDataType; b: Byte; d, dy: Single; begin HeightData.DataState := hdsPreparing; // retrieve data with HeightData do begin oldType := DataType; Allocate(hdtByte); // Cheap texture changed (32 is our tileSize = 2^5) // This basicly picks a texture for each tile depending on the tile's position case (((XLeft xor YTop) shr 5) and 3) of 0, 3: HeightData.MaterialName := 'BW'; 1: HeightData.MaterialName := 'Blue'; 2: HeightData.MaterialName := 'Red'; end; // 'Cheap' elevation data : this is just a formula z=f(x, y) for Y := YTop to YTop + Size - 1 do begin rasterLine := ByteRaster[Y - YTop]; dy := Sqr(Y); for X := XLeft to XLeft + Size - 1 do begin d := Sqrt(Sqr(X) + dy); b := Round(128 + 128 * Sin(d * 0.2) / (d * 0.1 + 1)); rasterLine[X - XLeft] := b; end; end; if oldType <> hdtByte then DataType := oldType; end; inherited; end; // Movement, mouse handling etc. procedure TFormSynthTerrain.GLSceneViewer1MouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer); begin mx := X; my := Y; end; procedure TFormSynthTerrain.GLSceneViewer1MouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer); begin if ssLeft in Shift then begin GLCamera1.MoveAroundTarget(my - Y, mx - X); mx := X; my := Y; end; end; procedure TFormSynthTerrain.Timer1Timer(Sender: TObject); begin Caption := Format('%.1f FPS - %d', [GLSceneViewer1.FramesPerSecond, TerrainRenderer1.LastTriangleCount]); GLSceneViewer1.ResetPerformanceMonitor; end; procedure TFormSynthTerrain.FormKeyPress(Sender: TObject; var Key: Char); begin case Key of '+': if GLCamera1.DepthOfView < 4000 then begin GLCamera1.DepthOfView := GLCamera1.DepthOfView * 1.2; with GLSceneViewer1.Buffer.FogEnvironment do begin FogEnd := FogEnd * 1.2; FogStart := FogStart * 1.2; end; end; '-': if GLCamera1.DepthOfView > 300 then begin GLCamera1.DepthOfView := GLCamera1.DepthOfView / 1.2; with GLSceneViewer1.Buffer.FogEnvironment do begin FogEnd := FogEnd / 1.2; FogStart := FogStart / 1.2; end; end; '*': with TerrainRenderer1 do if CLODPrecision > 5 then CLODPrecision := Round(CLODPrecision * 0.8); '/': with TerrainRenderer1 do if CLODPrecision < 500 then CLODPrecision := Round(CLODPrecision * 1.2); '8': with TerrainRenderer1 do if QualityDistance > 40 then QualityDistance := Round(QualityDistance * 0.8); '9': with TerrainRenderer1 do if QualityDistance < 1000 then QualityDistance := Round(QualityDistance * 1.2); end; Key := #0; end; procedure TFormSynthTerrain.GLCadencer1Progress(Sender: TObject; const deltaTime, newTime: Double); var speed: Single; begin // handle keypresses if IsKeyDown(VK_SHIFT) then speed := 5 * deltaTime else speed := deltaTime; with GLCamera1.Position do begin if IsKeyDown(VK_RIGHT) then DummyCube1.Translate(Z * speed, 0, -X * speed); if IsKeyDown(VK_LEFT) then DummyCube1.Translate(-Z * speed, 0, X * speed); if IsKeyDown(VK_UP) then DummyCube1.Translate(-X * speed, 0, -Z * speed); if IsKeyDown(VK_DOWN) then DummyCube1.Translate(X * speed, 0, Z * speed); if IsKeyDown(VK_PRIOR) then FCamHeight := FCamHeight + 10 * speed; if IsKeyDown(VK_NEXT) then FCamHeight := FCamHeight - 10 * speed; if IsKeyDown(VK_ESCAPE) then Close; end; // don't drop through terrain! with DummyCube1.Position do Y := TerrainRenderer1.InterpolatedHeight(AsVector) + FCamHeight; end; end.