3D mesh from a height-field

Top  Previous  Next

{: Generates a 3D mesh from a height-field function.<p>

 

 The left viewer shows a triangle-based mesh, the right viewer uses a

 triangle-strip-based mesh, both mesh are based on the same height-field and

 have the same resolution.<p>

 

 This sample wants to demonstrate a few things :<ul>

 <li>how to define a mesh (triangle & triangle strip)

 <li>what the default normal calculations looks like

 <li>TriangleStrips are faster, but slightly more complex to use

 </ul>

 

 In triangle mode, the normals are computed on a triangle basis, hence the

 facetted look, for triangle-strip, they are computed for each vertex based

 on the triangle it completes (smooth along strip-direction).<br>

 

 The reader may make the "good" looking version (ie. smooth aspect in all

 direction) by calculating the proper normal from the formula instead of

 using standard normal calculations.<br>

 

 Sample framerates (K6-400 + Sofware OpenGL), 5000 triangles (cResolution=25) :<br>

 -  mmTriangles : 9.6 FPS<br>

 -  mmTriangleStrip : 17.2 FPS<p>

 Sample framerates (K7-500 + GeForce 256), 20000 triangles (cResolution=50) :<br>

 -  mmTriangles : 53 FPS<br>

 -  mmTriangleStrip : 202 FPS

}

unit Unit1;

 

{$MODE Delphi}

 

interface

 

uses

LCLIntf, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,

GLScene, GLObjects, GLMisc, VectorGeometry, GLTexture, ExtCtrls, GLCadencer,

StdCtrls, GLMesh, GLState, LResources, GLWin32Viewer;

 

type

 

{ TForm1 }

 

TForm1 = class(TForm)

  GLSceneViewer1: TGLSceneViewer;

  GLScene1: TGLScene;

  Mesh1: TGLMesh;

  DummyCube1: TGLDummyCube;

  GLCamera1: TGLCamera;

  GLLightSource1: TGLLightSource;

  Timer1: TTimer;

  GLSceneViewer2: TGLSceneViewer;

  Panel1: TPanel;

  Label1: TLabel;

  GLScene2: TGLScene;

  DummyCube2: TGLDummyCube;

  Mesh2: TGLMesh;

  GLLightSource2: TGLLightSource;

  GLCamera2: TGLCamera;

  Label2: TLabel;

  procedure FormCreate(Sender: TObject);

  procedure Timer1Timer(Sender: TObject);

  procedure GLSceneViewer1MouseDown(Sender: TObject;

    Button: TMouseButton; Shift: TShiftState; X, Y: Integer);

  procedure GLSceneViewer1MouseMove(Sender: TObject; Shift: TShiftState;

    X, Y: Integer);

private

  { D�clarations priv�es }

  mx, my : Integer;

public

  { D�clarations publiques }

end;

 

var

Form1: TForm1;

 

implementation

 

 

const

 // half-grid resolution, grid width is actually cResolution*2 of "quads"

 cResolution = 50;

 

procedure TForm1.FormCreate(Sender: TObject);

var

 x, y : Integer;

 pTopLeft, pTopRight, pBottomRight, pBottomLeft : TAffineVector;

 invRes, invRes2 : Single;

 

 function MakeVect(const x, y : Single) : TAffineVector;

 begin

    SetVector(Result, x*invRes, sin((x*x+y*y)*invRes2), y*invRes);

 end;

 

 procedure AddTriangle(const p1, p2, p3 : TAffineVector;

                       const color : TColorVector);

 begin

    with Mesh1.Vertices do begin

       AddVertex(p1, NullVector, color);

       AddVertex(p2, NullVector, color);

       AddVertex(p3, NullVector, color);

    end;

 end;

 

begin

 // scaling precalcs for our math func

 invRes:=10/cResolution;

 invRes2:=0.1*Sqr(invRes);

 //

 // Triangles

 //

 // this one is basic : we calculate the corner points for each grid quad and

 // add the two triangles that make it

 with Mesh1 do begin

    Mode:=mmTriangles;

    Vertices.Clear;

    for y:=-cResolution to cResolution do begin

       for x:=-cResolution to cResolution do begin

          pTopLeft:=MakeVect(x, y+1);

          pTopRight:=MakeVect(x+1, y+1);

          pBottomRight:=MakeVect(x+1, y);

          pBottomLeft:=MakeVect(x, y);

          // top left triangle

          AddTriangle(pBottomLeft, pTopLeft, pTopRight, clrBlue);

          // bottom right triangle

          AddTriangle(pTopRight, pBottomRight, pBottomLeft, clrBlue);

       end;

    end;

    CalcNormals(fwCounterClockWise);

//      Vertices.Locked:=True;

 end;

 //

 // TriangleStrip

 //

 // Same as triangle, however trianglestrips are continuous, and to cover

 // the grid, "null" segments are used at both ends of a strip (to avoid a

 // visible triangle that would stretch for the full width of the grid).

 // Note : this can be avoided by reversing grid traversing direction (one line

 // from left to right, one from right to left, etc.)

 with Mesh2 do begin

    Mode:=mmTriangleStrip;

    Vertices.Clear;

    for y:=-cResolution to cResolution do begin

       pTopLeft:=MakeVect(-cResolution, y+1);

       Vertices.AddVertex(pTopLeft, NullVector, clrBlue);

       Vertices.AddVertex(pTopLeft, NullVector, clrBlue);

       for x:=-cResolution to cResolution do begin

          pTopRight:=MakeVect(x+1, y+1);

          pBottomLeft:=MakeVect(x, y);

          with Vertices do begin

             AddVertex(pBottomLeft, NullVector, clrBlue);

             AddVertex(pTopRight, NullVector, clrBlue);

          end;

       end;

       pBottomRight:=MakeVect(cResolution+1, y);

       Vertices.AddVertex(pBottomRight, NullVector, clrBlue);

       Vertices.AddVertex(pBottomRight, NullVector, clrBlue);

    end;

    CalcNormals(fwClockWise);

//      Vertices.Locked:=True;

 end;

end;

 

procedure TForm1.Timer1Timer(Sender: TObject);

begin

 // nb of triangles in scene

 Caption:=Format('%d Triangles', [2*(cResolution*2)*(cResolution*2)]);

 // calculate & display triangles framerate

 with GLSceneViewer1 do begin

    // we render twice to get a fair FPS rating

    ResetPerformanceMonitor;

    Buffer.Render;

    Buffer.Render;

    Label1.Caption:=Format('%.2f FPS (mmTriangles)', [FramesPerSecond]);

 end;

 // calculate & display trianglestrip framerate

 with GLSceneViewer2 do begin

    // we render twice to get a fair FPS rating

    ResetPerformanceMonitor;

    Buffer.Render;

    Buffer.Render;

    Label2.Caption:=Format('%.2f FPS (mmTriangleStrip)', [FramesPerSecond]);

 end;

end;

 

procedure TForm1.GLSceneViewer1MouseDown(Sender: TObject;

Button: TMouseButton; Shift: TShiftState; X, Y: Integer);

begin

 mx:=x; my:=y;

end;

 

procedure TForm1.GLSceneViewer1MouseMove(Sender: TObject;

Shift: TShiftState; X, Y: Integer);

begin

 if ssLeft in Shift then begin

    TGLSceneViewer(Sender).Camera.MoveAroundTarget(my-y, mx-x);

    my:=y; mx:=x;

 end;

end;

 

initialization

{$i Unit1.lrs}

 

end.