OpenGL terrain

disclaimer

Sometimes function : z:=f(x,y) has to be displayed in 3D for better viewability.
Here a solution in OpenGL is shown. It is suited for functions where f(x,y) is
a continous graph to be computed at any point within the intervall within
reasonable time. The component requests the function values at equidistant points.
It is not made to display randomly spaced values, or where the calculation takes
ages.

A rectangular area is defined in the constructor. After setting the x/y spacing
and supplying the function for the values, 'getvalues' gets the values. This is
required once, the values are stored. Calling 'display' builds the triangle strips.

the component

{
  TTerrain3D - a 3D terrain from a function

  The constructor defines the area, then the function
  has to be assigned and the stepwidth has to be set.

  given a function z:=f(x,y), the TTerrain3D.getvalues
  gets the values by calling this function and
  stores them in a 2D array.

  the display procedure builds triangle strips for OpenGL

}
unit Terrain3D;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  Varrays,OpenGL;

type
  ZofXY=function(x,y:single):single;	// define callback function

  TTerrain3D = class(TObject)
  private
    { Private declarations }
   fcallback:ZofXY;
   fdata:Single2DArray;
   fxlow,fylow,fxhigh,fyhigh,fdx,fdy:single;
  protected
    { Protected declarations }
  public
    { Public declarations }
   constructor create(xlo,ylo,xhi,yhi:single);
   destructor destroy;
   procedure display;
   procedure getvalues;
   property callback:ZofXY read fcallback write fcallback;
  published
    { Published declarations }
   property xlow:single read fxlow;
   property ylow:single read fylow;
   property xhigh:single read fxhigh;
   property yhigh:single read fyhigh;
   property dx:single read fdx write fdx;
   property dy:single read fdy write fdy;

  end;

procedure Register;

implementation

constructor TTerrain3D.create(xlo,ylo,xhi,yhi:single);
begin
 inherited create;
 fdata:=nil;
 fcallback:=nil;
 fdx:=0; fdy:=0;
 fxlow:=xlo; fylow:=ylo; fxhigh:=xhi; fyhigh:=yhi;
end;

destructor TTerrain3D.destroy;
begin
 if fdata<>nil then fdata.destroy;
 inherited destroy;
end;

procedure TTerrain3D.display;
var h,v,xs,ys:integer;
 x,y1,y2,z:GLFLOAT;
begin
 ys:=fdata.sizey;
 xs:=fdata.sizex;
 y1:=fylow;
 for v:=0 to ys-2 do begin // horizontal strips
  glBegin(GL_TRIANGLE_STRIP);
   y2:=y1+fdy;
   x:=fxlow;
   for h:=0 to xs-1 do begin
    z:=fdata[h,v];
    glcolor(0.2,0.2,0.5+2*z);  // here for testing - replace
    glVertex3f(x,y1,z);
    z:=fdata[h,v+1];
    glVertex3f(x,y2,z);
    x:=x+fdx;
   end;
  glEnd;
  y1:=y1+fdy;
 end;
end;

procedure TTerrain3D.getvalues;		// called only once
var h,v,xs,ys:integer;
  x,y,z:single;
begin
 if (fdx<>0) and (fdy<>0) then begin
  xs:=round((fxhigh-fxlow)/dx);
  ys:=round((fyhigh-fylow)/dy);
  fdata:=Single2DArray.create(xs,ys);
  if assigned(fcallback) then begin
   for h:=0 to xs-1 do begin
    x:=fxlow+h*fdx;
    for v:=0 to ys-1 do begin
     y:=fylow+v*fdy;
     z:=fcallback(x,y);
     fdata[h,v]:=z;
    end;
   end;
  end; //callback exists
 end; // dx,dy<>0
end;

procedure Register;
begin
 // RegisterComponents('Samples', [TTerrain3D]);
end;

end.

and a test application

function terrain(x,y:single):single; // this is the actual function f(x,y)
begin
 result:=0.25*sin(6*x)*cos(6*y);
end;

procedure TForm1.FormClick(Sender: TObject);
begin
 t:=TTerrain3D.create(-3,-3,3,3);
 t.callback:=terrain;
 t.dx:=0.1;
 t.dy:=0.1;
 t.getvalues;
end;

procedure TForm1.Transform1Paint(Sender: TObject);
begin
 Light1.Apply;
 Material1.Apply;
 t.display;
end;

note

the used 'VArray' unit can be found on the delphi page variable arrays,
where the Int2DArray is modified to hold single.

OpenGL
home

last updated: 31.jan.00

Copyright (99,2000) Ing.Büro R.Tschaggelar