123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382 |
- {
- Ported to FPC by Nikolay Nikolov ([email protected])
- }
- {
- Land demo for OpenPTC 1.0 C++ API
- Based on Heightmap example from Hornet (RIP)
- PTC version Copyright (c) 1998 Marcus Fletcher ([email protected])
- Updated to OpenPTC 1.0 by Glenn Fiedler ([email protected])
- Cursor keys to move, <Pause> to brake and <Esc> to quit
- }
- program Land;
- {$MODE objfpc}
- uses
- ptc;
- const
- SCREENWIDTH = 320;
- SCREENHEIGHT = 200;
- FOV: Integer = 256; { half of the xy field of view (This is based on the 0-2048 convention) }
- var
- HMap: array [0..256*256 - 1] of Uint8; { Height field }
- CMap: array [0..256*256 - 1] of Uint8; { Color map }
- lasty, { Last pixel drawn on a given column }
- lastc: array [0..SCREENWIDTH - 1] of Integer; { Color of last pixel on a column }
- CosT, SinT: array [0..2047] of Integer; { Cosine and Sine tables }
- { Reduces a value to 0..255 (used in height field computation) }
- function Clamp(x: Integer): Integer;
- begin
- if x < 0 then
- Result := 0
- else
- if x > 255 then
- Result := 255
- else
- Result := x;
- end;
- { Heightfield and colormap computation }
- procedure ComputeMap;
- var
- p, i, j, k, k2, p2, a, b, c, d: Integer;
- begin
- { Start from a plasma clouds fractal }
- HMap[0] := 128;
- p := 256;
- while p > 1 do
- begin
- p2 := p shr 1;
- k := p * 8 + 20;
- k2 := k shr 1;
- i := 0;
- while i < 256 do
- begin
- j := 0;
- while j < 256 do
- begin
- a := HMap[(i shl 8) + j];
- b := HMap[(((i + p) and 255) shl 8) + j];
- c := HMap[(i shl 8) + ((j + p) and 255)];
- d := HMap[(((i + p) and 255) shl 8) + ((j + p) and 255)];
- HMap[(i shl 8) + ((j + p2) and 255)] :=
- Clamp(((a + c) shr 1) + (Random(k) - k2));
- HMap[(((i + p2) and 255) shl 8) + ((j + p2) and 255)] :=
- Clamp(((a + b + c + d) shr 2) + (Random(k) - k2));
- HMap[(((i + p2) and 255) shl 8) + j] :=
- Clamp(((a + b) shr 1) + (Random(k) - k2));
- Inc(j, p);
- end;
- Inc(i, p);
- end;
- p := p2;
- end;
- { Smoothing }
- for k := 0 to 2 do
- begin
- i := 0;
- while i < 256*256 do
- begin
- for j := 0 to 255 do
- HMap[i + j] := (HMap[((i + 256) and $FF00) + j] +
- HMap[i + ((j + 1) and $FF)] +
- HMap[((i - 256) and $FF00) + j] +
- HMap[i + ((j - 1) and $FF)]) shr 2;
- Inc(i, 256);
- end;
- end;
- { Color computation (derivative of the height field) }
- i := 0;
- while i < 256*256 do
- begin
- for j := 0 to 255 do
- begin
- k := 128 + (HMap[((i + 256) and $FF00) + ((j + 1) and 255)] - HMap[i + j])*4;
- if k < 0 then
- k := 0;
- if k > 255 then
- k := 255;
- CMap[i + j] := k;
- end;
- Inc(i, 256);
- end;
- end;
- { Calculate the lookup tables }
- procedure InitTables;
- var
- a: Integer;
- result: Single;
- begin
- for a := 0 to 2047 do
- begin
- { Precalculate cosine }
- result := cos(a * PI / 1024) * 256;
- CosT[a] := Trunc(result);
- { and sine }
- result := sin(a * PI / 1024) * 256;
- SinT[a] := Trunc(result);
- end;
- end;
- {
- Draw a "section" of the landscape; x0,y0 and x1,y1 and the xy coordinates
- on the height field, hy is the viewpoint height, s is the scaling factor
- for the distance. x0,y0,x1,y1 are 16.16 fixed point numbers and the
- scaling factor is a 16.8 fixed point value.
- }
- procedure Line(x0, y0, x1, y1, hy, s: Integer; surface_buffer: PUint32; fadeout: Integer);
- var
- sx, sy, i, a, b, u0, u1, v0, v1, h0, h1, h2, h3, h, c, y: Integer;
- coord_x, coord_y, sc, cc, currentColor: Integer;
- pixel: PUint32;
- begin
- { Compute xy speed }
- sx := (x1 - x0) div SCREENWIDTH;
- sy := (y1 - y0) div SCREENWIDTH;
- for i := 0 to SCREENWIDTH - 1 do
- begin
- { Compute the xy coordinates; a and b will be the position inside the }
- { single map cell (0..255). }
- a := (x0 shr 8) and $FF;
- b := (y0 shr 8) and $FF;
- u0 := (x0 shr 16) and $FF;
- u1 := (u0 + 1) and $FF;
- v0 := (y0 shr 8) and $FF00;
- v1 := (v0 + 256) and $FF00;
- { Fetch the height at the four corners of the square the point is in }
- h0 := HMap[u0 + v0];
- h1 := HMap[u1 + v0];
- h2 := HMap[u0 + v1];
- h3 := HMap[u1 + v1];
- { Compute the height using bilinear interpolation }
- h0 := (h0 shl 8) + a * (h1 - h0);
- h2 := (h2 shl 8) + a * (h3 - h2);
- h := ((h0 shl 8) + b * (h2 - h0)) shr 16;
- { Fetch the color at the centre of the square the point is in }
- h0 := CMap[u0 + v0];
- h1 := CMap[u1 + v0];
- h2 := CMap[u0 + v1];
- h3 := CMap[u1 + v1];
- { Compute the color using bilinear interpolation (in 16.16) }
- h0 := (h0 shl 8) + a * (h1 - h0);
- h2 := (h2 shl 8) + a * (h3 - h2);
- c := ((h0 shl 8) + b * (h2 - h0));
- { Compute screen height using the scaling factor }
- y := (((h - hy) * s) shr 11) + (SCREENHEIGHT shr 1);
- { Draw the column }
- a := lasty[i];
- if y < a then
- begin
- coord_x := i;
- coord_y := a;
- if lastc[i] = -1 then
- lastc[i] := c;
- sc := (c - lastc[i]) div (a - y);
- cc := lastc[i];
- if a > (SCREENHEIGHT - 1) then
- begin
- Dec(coord_y, a - (SCREENHEIGHT - 1));
- a := SCREENHEIGHT - 1;
- end;
- if y < 0 then
- y := 0;
- while y < a do
- begin
- currentColor := cc shr 18;
- pixel := surface_buffer + (coord_y * SCREENWIDTH) + coord_x;
- pixel^ := ((currentColor shl 2) * (150 - fadeout) div 150) shl 8;
- Inc(cc, sc);
- Dec(coord_y);
- Dec(a);
- end;
- lasty[i] := y;
- end;
- lastc[i] := c;
- { Advance to next xy position }
- Inc(x0, sx); Inc(y0, sy);
- end;
- end;
- { Draw the view from the point x0,y0 (16.16) looking at angle a }
- procedure View(x0, y0, angle, height: Integer; surface_buffer: PUint32);
- var
- d, u0, a, v0, u1, v1, h0, h1, h2, h3: Integer;
- begin
- { Initialize last-y and last-color arrays }
- for d := 0 to SCREENWIDTH - 1 do
- begin
- lasty[d] := SCREENHEIGHT;
- lastc[d] := -1;
- end;
- { Compute the xy coordinates; a and b will be the position inside the }
- { single map cell (0..255). }
- u0 := (x0 shr 16) and $FF;
- a := (x0 shr 8) and $FF;
- v0 := (y0 shr 8) and $FF00;
- u1 := (u0 + 1) and $FF;
- v1 := (v0 + 256) and $FF00;
- { Fetch the height at the four corners of the square the point is in }
- h0 := HMap[u0 + v0];
- h1 := HMap[u1 + v0];
- h2 := HMap[u0 + v1];
- h3 := HMap[u1 + v1];
- { Compute the height using bilinear interpolation }
- h0 := (h0 shl 8) + a * (h1 - h0);
- h2 := (h2 shl 8) + a * (h3 - h2);
- { Draw the landscape from near to far without overdraw }
- d := 0;
- while d < 150 do
- begin
- Line(x0 + (d shl 8)*CosT[(angle - FOV) and $7FF],
- y0 + (d shl 8)*SinT[(angle - FOV) and $7FF],
- x0 + (d shl 8)*CosT[(angle + FOV) and $7FF],
- y0 + (d shl 8)*SinT[(angle + FOV) and $7FF],
- height, (100 shl 8) div (d + 1),
- surface_buffer,
- d);
- Inc(d, 1 + (d shr 6));
- end;
- end;
- var
- format: IPTCFormat;
- console: IPTCConsole;
- surface: IPTCSurface;
- timer: IPTCTimer;
- key: IPTCKeyEvent;
- pixels: PUint32;
- Done: Boolean;
- x0, y0: Integer;
- height: Integer;
- angle, deltaAngle, deltaSpeed, CurrentSpeed, scale, delta: Double;
- index: Integer;
- begin
- Done := False;
- try
- try
- format := TPTCFormatFactory.CreateNew(32, $00FF0000, $0000FF00, $000000FF);
- console := TPTCConsoleFactory.CreateNew;
- console.open('Land demo', SCREENWIDTH, SCREENHEIGHT, format);
- surface := TPTCSurfaceFactory.CreateNew(SCREENWIDTH, SCREENHEIGHT, format);
- { Compute the height map }
- ComputeMap;
- InitTables;
- x0 := 0;
- y0 := 0;
- height := -200;
- angle := 0;
- deltaAngle := 0;
- deltaSpeed := 4096;
- CurrentSpeed := deltaSpeed * 10;
- { time scaling constant }
- scale := 20;
- { create timer }
- timer := TPTCTimerFactory.CreateNew;
- { start timer }
- timer.start;
- { main loop }
- repeat
- { get time delta between frames }
- delta := timer.delta;
- { clear surface }
- surface.clear;
- { lock surface pixels }
- pixels := surface.lock;
- try
- { draw current landscape view }
- View(x0, y0, Trunc(angle), height, pixels);
- finally
- { unlock surface }
- surface.unlock;
- end;
- { copy surface to console }
- surface.copy(console);
- { update console }
- console.update;
- { check key press }
- while console.KeyPressed do
- begin
- { read key press }
- console.ReadKey(key);
- { handle key press }
- case key.code of
- { increase speed }
- PTCKEY_UP: CurrentSpeed := CurrentSpeed + deltaSpeed * delta * scale;
- { decrease speed }
- PTCKEY_DOWN: CurrentSpeed := CurrentSpeed - deltaSpeed * delta * scale;
- { turn to the left }
- PTCKEY_LEFT: deltaAngle := deltaAngle - 1;
- { turn to the right }
- PTCKEY_RIGHT: deltaAngle := deltaAngle + 1;
- PTCKEY_SPACE: begin
- { stop moving }
- CurrentSpeed := 0;
- deltaAngle := 0;
- end;
- { exit }
- PTCKEY_ESCAPE: Done := True;
- end;
- end;
- { Update position/angle }
- angle := angle + deltaAngle * delta * scale;
- index := Trunc(angle) and $7FF;
- Inc(x0, Trunc(CurrentSpeed * CosT[index]) div 256);
- Inc(y0, Trunc(CurrentSpeed * SinT[index]) div 256);
- until Done;
- finally
- if Assigned(console) then
- console.close;
- end;
- except
- on error: TPTCError do
- { report error }
- error.report;
- end;
- end.
|