land.pp 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382
  1. {
  2. Ported to FPC by Nikolay Nikolov ([email protected])
  3. }
  4. {
  5. Land demo for OpenPTC 1.0 C++ API
  6. Based on Heightmap example from Hornet (RIP)
  7. PTC version Copyright (c) 1998 Marcus Fletcher ([email protected])
  8. Updated to OpenPTC 1.0 by Glenn Fiedler ([email protected])
  9. Cursor keys to move, <Pause> to brake and <Esc> to quit
  10. }
  11. program Land;
  12. {$MODE objfpc}
  13. uses
  14. ptc;
  15. const
  16. SCREENWIDTH = 320;
  17. SCREENHEIGHT = 200;
  18. FOV: Integer = 256; { half of the xy field of view (This is based on the 0-2048 convention) }
  19. var
  20. HMap: array [0..256*256 - 1] of Uint8; { Height field }
  21. CMap: array [0..256*256 - 1] of Uint8; { Color map }
  22. lasty, { Last pixel drawn on a given column }
  23. lastc: array [0..SCREENWIDTH - 1] of Integer; { Color of last pixel on a column }
  24. CosT, SinT: array [0..2047] of Integer; { Cosine and Sine tables }
  25. { Reduces a value to 0..255 (used in height field computation) }
  26. function Clamp(x: Integer): Integer;
  27. begin
  28. if x < 0 then
  29. Result := 0
  30. else
  31. if x > 255 then
  32. Result := 255
  33. else
  34. Result := x;
  35. end;
  36. { Heightfield and colormap computation }
  37. procedure ComputeMap;
  38. var
  39. p, i, j, k, k2, p2, a, b, c, d: Integer;
  40. begin
  41. { Start from a plasma clouds fractal }
  42. HMap[0] := 128;
  43. p := 256;
  44. while p > 1 do
  45. begin
  46. p2 := p shr 1;
  47. k := p * 8 + 20;
  48. k2 := k shr 1;
  49. i := 0;
  50. while i < 256 do
  51. begin
  52. j := 0;
  53. while j < 256 do
  54. begin
  55. a := HMap[(i shl 8) + j];
  56. b := HMap[(((i + p) and 255) shl 8) + j];
  57. c := HMap[(i shl 8) + ((j + p) and 255)];
  58. d := HMap[(((i + p) and 255) shl 8) + ((j + p) and 255)];
  59. HMap[(i shl 8) + ((j + p2) and 255)] :=
  60. Clamp(((a + c) shr 1) + (Random(k) - k2));
  61. HMap[(((i + p2) and 255) shl 8) + ((j + p2) and 255)] :=
  62. Clamp(((a + b + c + d) shr 2) + (Random(k) - k2));
  63. HMap[(((i + p2) and 255) shl 8) + j] :=
  64. Clamp(((a + b) shr 1) + (Random(k) - k2));
  65. Inc(j, p);
  66. end;
  67. Inc(i, p);
  68. end;
  69. p := p2;
  70. end;
  71. { Smoothing }
  72. for k := 0 to 2 do
  73. begin
  74. i := 0;
  75. while i < 256*256 do
  76. begin
  77. for j := 0 to 255 do
  78. HMap[i + j] := (HMap[((i + 256) and $FF00) + j] +
  79. HMap[i + ((j + 1) and $FF)] +
  80. HMap[((i - 256) and $FF00) + j] +
  81. HMap[i + ((j - 1) and $FF)]) shr 2;
  82. Inc(i, 256);
  83. end;
  84. end;
  85. { Color computation (derivative of the height field) }
  86. i := 0;
  87. while i < 256*256 do
  88. begin
  89. for j := 0 to 255 do
  90. begin
  91. k := 128 + (HMap[((i + 256) and $FF00) + ((j + 1) and 255)] - HMap[i + j])*4;
  92. if k < 0 then
  93. k := 0;
  94. if k > 255 then
  95. k := 255;
  96. CMap[i + j] := k;
  97. end;
  98. Inc(i, 256);
  99. end;
  100. end;
  101. { Calculate the lookup tables }
  102. procedure InitTables;
  103. var
  104. a: Integer;
  105. result: Single;
  106. begin
  107. for a := 0 to 2047 do
  108. begin
  109. { Precalculate cosine }
  110. result := cos(a * PI / 1024) * 256;
  111. CosT[a] := Trunc(result);
  112. { and sine }
  113. result := sin(a * PI / 1024) * 256;
  114. SinT[a] := Trunc(result);
  115. end;
  116. end;
  117. {
  118. Draw a "section" of the landscape; x0,y0 and x1,y1 and the xy coordinates
  119. on the height field, hy is the viewpoint height, s is the scaling factor
  120. for the distance. x0,y0,x1,y1 are 16.16 fixed point numbers and the
  121. scaling factor is a 16.8 fixed point value.
  122. }
  123. procedure Line(x0, y0, x1, y1, hy, s: Integer; surface_buffer: PUint32; fadeout: Integer);
  124. var
  125. sx, sy, i, a, b, u0, u1, v0, v1, h0, h1, h2, h3, h, c, y: Integer;
  126. coord_x, coord_y, sc, cc, currentColor: Integer;
  127. pixel: PUint32;
  128. begin
  129. { Compute xy speed }
  130. sx := (x1 - x0) div SCREENWIDTH;
  131. sy := (y1 - y0) div SCREENWIDTH;
  132. for i := 0 to SCREENWIDTH - 1 do
  133. begin
  134. { Compute the xy coordinates; a and b will be the position inside the }
  135. { single map cell (0..255). }
  136. a := (x0 shr 8) and $FF;
  137. b := (y0 shr 8) and $FF;
  138. u0 := (x0 shr 16) and $FF;
  139. u1 := (u0 + 1) and $FF;
  140. v0 := (y0 shr 8) and $FF00;
  141. v1 := (v0 + 256) and $FF00;
  142. { Fetch the height at the four corners of the square the point is in }
  143. h0 := HMap[u0 + v0];
  144. h1 := HMap[u1 + v0];
  145. h2 := HMap[u0 + v1];
  146. h3 := HMap[u1 + v1];
  147. { Compute the height using bilinear interpolation }
  148. h0 := (h0 shl 8) + a * (h1 - h0);
  149. h2 := (h2 shl 8) + a * (h3 - h2);
  150. h := ((h0 shl 8) + b * (h2 - h0)) shr 16;
  151. { Fetch the color at the centre of the square the point is in }
  152. h0 := CMap[u0 + v0];
  153. h1 := CMap[u1 + v0];
  154. h2 := CMap[u0 + v1];
  155. h3 := CMap[u1 + v1];
  156. { Compute the color using bilinear interpolation (in 16.16) }
  157. h0 := (h0 shl 8) + a * (h1 - h0);
  158. h2 := (h2 shl 8) + a * (h3 - h2);
  159. c := ((h0 shl 8) + b * (h2 - h0));
  160. { Compute screen height using the scaling factor }
  161. y := (((h - hy) * s) shr 11) + (SCREENHEIGHT shr 1);
  162. { Draw the column }
  163. a := lasty[i];
  164. if y < a then
  165. begin
  166. coord_x := i;
  167. coord_y := a;
  168. if lastc[i] = -1 then
  169. lastc[i] := c;
  170. sc := (c - lastc[i]) div (a - y);
  171. cc := lastc[i];
  172. if a > (SCREENHEIGHT - 1) then
  173. begin
  174. Dec(coord_y, a - (SCREENHEIGHT - 1));
  175. a := SCREENHEIGHT - 1;
  176. end;
  177. if y < 0 then
  178. y := 0;
  179. while y < a do
  180. begin
  181. currentColor := cc shr 18;
  182. pixel := surface_buffer + (coord_y * SCREENWIDTH) + coord_x;
  183. pixel^ := ((currentColor shl 2) * (150 - fadeout) div 150) shl 8;
  184. Inc(cc, sc);
  185. Dec(coord_y);
  186. Dec(a);
  187. end;
  188. lasty[i] := y;
  189. end;
  190. lastc[i] := c;
  191. { Advance to next xy position }
  192. Inc(x0, sx); Inc(y0, sy);
  193. end;
  194. end;
  195. { Draw the view from the point x0,y0 (16.16) looking at angle a }
  196. procedure View(x0, y0, angle, height: Integer; surface_buffer: PUint32);
  197. var
  198. d, u0, a, v0, u1, v1, h0, h1, h2, h3: Integer;
  199. begin
  200. { Initialize last-y and last-color arrays }
  201. for d := 0 to SCREENWIDTH - 1 do
  202. begin
  203. lasty[d] := SCREENHEIGHT;
  204. lastc[d] := -1;
  205. end;
  206. { Compute the xy coordinates; a and b will be the position inside the }
  207. { single map cell (0..255). }
  208. u0 := (x0 shr 16) and $FF;
  209. a := (x0 shr 8) and $FF;
  210. v0 := (y0 shr 8) and $FF00;
  211. u1 := (u0 + 1) and $FF;
  212. v1 := (v0 + 256) and $FF00;
  213. { Fetch the height at the four corners of the square the point is in }
  214. h0 := HMap[u0 + v0];
  215. h1 := HMap[u1 + v0];
  216. h2 := HMap[u0 + v1];
  217. h3 := HMap[u1 + v1];
  218. { Compute the height using bilinear interpolation }
  219. h0 := (h0 shl 8) + a * (h1 - h0);
  220. h2 := (h2 shl 8) + a * (h3 - h2);
  221. { Draw the landscape from near to far without overdraw }
  222. d := 0;
  223. while d < 150 do
  224. begin
  225. Line(x0 + (d shl 8)*CosT[(angle - FOV) and $7FF],
  226. y0 + (d shl 8)*SinT[(angle - FOV) and $7FF],
  227. x0 + (d shl 8)*CosT[(angle + FOV) and $7FF],
  228. y0 + (d shl 8)*SinT[(angle + FOV) and $7FF],
  229. height, (100 shl 8) div (d + 1),
  230. surface_buffer,
  231. d);
  232. Inc(d, 1 + (d shr 6));
  233. end;
  234. end;
  235. var
  236. format: IPTCFormat;
  237. console: IPTCConsole;
  238. surface: IPTCSurface;
  239. timer: IPTCTimer;
  240. key: IPTCKeyEvent;
  241. pixels: PUint32;
  242. Done: Boolean;
  243. x0, y0: Integer;
  244. height: Integer;
  245. angle, deltaAngle, deltaSpeed, CurrentSpeed, scale, delta: Double;
  246. index: Integer;
  247. begin
  248. Done := False;
  249. try
  250. try
  251. format := TPTCFormatFactory.CreateNew(32, $00FF0000, $0000FF00, $000000FF);
  252. console := TPTCConsoleFactory.CreateNew;
  253. console.open('Land demo', SCREENWIDTH, SCREENHEIGHT, format);
  254. surface := TPTCSurfaceFactory.CreateNew(SCREENWIDTH, SCREENHEIGHT, format);
  255. { Compute the height map }
  256. ComputeMap;
  257. InitTables;
  258. x0 := 0;
  259. y0 := 0;
  260. height := -200;
  261. angle := 0;
  262. deltaAngle := 0;
  263. deltaSpeed := 4096;
  264. CurrentSpeed := deltaSpeed * 10;
  265. { time scaling constant }
  266. scale := 20;
  267. { create timer }
  268. timer := TPTCTimerFactory.CreateNew;
  269. { start timer }
  270. timer.start;
  271. { main loop }
  272. repeat
  273. { get time delta between frames }
  274. delta := timer.delta;
  275. { clear surface }
  276. surface.clear;
  277. { lock surface pixels }
  278. pixels := surface.lock;
  279. try
  280. { draw current landscape view }
  281. View(x0, y0, Trunc(angle), height, pixels);
  282. finally
  283. { unlock surface }
  284. surface.unlock;
  285. end;
  286. { copy surface to console }
  287. surface.copy(console);
  288. { update console }
  289. console.update;
  290. { check key press }
  291. while console.KeyPressed do
  292. begin
  293. { read key press }
  294. console.ReadKey(key);
  295. { handle key press }
  296. case key.code of
  297. { increase speed }
  298. PTCKEY_UP: CurrentSpeed := CurrentSpeed + deltaSpeed * delta * scale;
  299. { decrease speed }
  300. PTCKEY_DOWN: CurrentSpeed := CurrentSpeed - deltaSpeed * delta * scale;
  301. { turn to the left }
  302. PTCKEY_LEFT: deltaAngle := deltaAngle - 1;
  303. { turn to the right }
  304. PTCKEY_RIGHT: deltaAngle := deltaAngle + 1;
  305. PTCKEY_SPACE: begin
  306. { stop moving }
  307. CurrentSpeed := 0;
  308. deltaAngle := 0;
  309. end;
  310. { exit }
  311. PTCKEY_ESCAPE: Done := True;
  312. end;
  313. end;
  314. { Update position/angle }
  315. angle := angle + deltaAngle * delta * scale;
  316. index := Trunc(angle) and $7FF;
  317. Inc(x0, Trunc(CurrentSpeed * CosT[index]) div 256);
  318. Inc(y0, Trunc(CurrentSpeed * SinT[index]) div 256);
  319. until Done;
  320. finally
  321. if Assigned(console) then
  322. console.close;
  323. end;
  324. except
  325. on error: TPTCError do
  326. { report error }
  327. error.report;
  328. end;
  329. end.