land.pp 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402
  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. Clamp := 0
  30. Else
  31. If x > 255 Then
  32. Clamp := 255
  33. Else
  34. Clamp := 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 : TPTCFormat;
  237. console : TPTCConsole;
  238. surface : TPTCSurface;
  239. timer : TPTCTimer;
  240. key : TPTCKeyEvent;
  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. format := Nil;
  250. console := Nil;
  251. surface := Nil;
  252. timer := Nil;
  253. key := Nil;
  254. Try
  255. Try
  256. key := TPTCKeyEvent.Create;
  257. format := TPTCFormat.Create(32, $00FF0000, $0000FF00, $000000FF);
  258. console := TPTCConsole.Create;
  259. console.open('Land demo', SCREENWIDTH, SCREENHEIGHT, format);
  260. surface := TPTCSurface.Create(SCREENWIDTH, SCREENHEIGHT, format);
  261. { Compute the height map }
  262. ComputeMap;
  263. InitTables;
  264. x0 := 0;
  265. y0 := 0;
  266. height := -200;
  267. angle := 0;
  268. deltaAngle := 0;
  269. deltaSpeed := 4096;
  270. CurrentSpeed := deltaSpeed * 10;
  271. { time scaling constant }
  272. scale := 20;
  273. { create timer }
  274. timer := TPTCTimer.Create;
  275. { start timer }
  276. timer.start;
  277. { main loop }
  278. Repeat
  279. { get time delta between frames }
  280. delta := timer.delta;
  281. { clear surface }
  282. surface.clear;
  283. { lock surface pixels }
  284. pixels := surface.lock;
  285. Try
  286. { draw current landscape view }
  287. View(x0, y0, Trunc(angle), height, pixels);
  288. Finally
  289. { unlock surface }
  290. surface.unlock;
  291. End;
  292. { copy surface to console }
  293. surface.copy(console);
  294. { update console }
  295. console.update;
  296. { check key press }
  297. While console.KeyPressed Do
  298. Begin
  299. { read key press }
  300. console.ReadKey(key);
  301. { handle key press }
  302. Case key.code Of
  303. { increase speed }
  304. PTCKEY_UP : CurrentSpeed += deltaSpeed * delta * scale;
  305. { decrease speed }
  306. PTCKEY_DOWN : CurrentSpeed -= deltaSpeed * delta * scale;
  307. { turn to the left }
  308. PTCKEY_LEFT : deltaAngle -= 1;
  309. { turn to the right }
  310. PTCKEY_RIGHT : deltaAngle += 1;
  311. PTCKEY_SPACE : Begin
  312. { stop moving }
  313. CurrentSpeed := 0;
  314. deltaAngle := 0;
  315. End;
  316. { exit }
  317. PTCKEY_ESCAPE : Done := True;
  318. End;
  319. End;
  320. { Update position/angle }
  321. angle += deltaAngle * delta * scale;
  322. index := Trunc(angle) And $7FF;
  323. Inc(x0, Trunc(CurrentSpeed * CosT[index]) Div 256);
  324. Inc(y0, Trunc(CurrentSpeed * SinT[index]) Div 256);
  325. Until Done;
  326. Finally
  327. console.close;
  328. console.Free;
  329. surface.Free;
  330. timer.Free;
  331. format.Free;
  332. key.Free;
  333. End;
  334. Except
  335. On error : TPTCError Do
  336. { report error }
  337. error.report;
  338. End;
  339. End.