Pas2JS_WebGL_Terrain.pas 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223
  1. program Pas2JS_WebGL_Terrain;
  2. uses
  3. Terrain, Noise, Types, Mat4, GLUtils, GLTypes,
  4. SysUtils,
  5. BrowserConsole, Web, WebGL, JS, Math;
  6. var
  7. gl: TJSWebGLRenderingContext;
  8. shader: TShader;
  9. projTransform: TMat4;
  10. viewTransform: TMat4;
  11. modelTransform: TMat4;
  12. var
  13. debugConsole: TJSElement;
  14. canvasAnimationHandler: integer = 0;
  15. var
  16. maps: TJSArray;
  17. camera: TVec3;
  18. lightPosition: TVec3;
  19. terrainNoise: TNoise;
  20. terrainSize: integer = 64 * 3;
  21. terrainResolution: integer = 128;
  22. flySpeed: TJSFloat32 = 1.3;
  23. visiblity: integer = 4;
  24. textureLoaded: boolean = false;
  25. type
  26. TTilingTerrain = class (TTerrain)
  27. public
  28. neighbor: TTilingTerrain;
  29. protected
  30. function GetHeightForVertex (localX, localY, x, y: integer): TNoiseFloat; override;
  31. end;
  32. function TTilingTerrain.GetHeightForVertex (localX, localY, x, y: integer): TNoiseFloat;
  33. begin
  34. if (localY = 0) and (neighbor <> nil) then
  35. result := neighbor.GetHeightAtPoint(localX, localY + neighbor.GetWidth - 1)
  36. else
  37. begin
  38. result := noise.GetNoise(x, y, GetWidth, GetHeight, 6, 3);
  39. result := Power(result, 9) * 60;
  40. end;
  41. end;
  42. procedure DrawCanvas;
  43. var
  44. terrainCoord: TVec3;
  45. startIndex, endIndex: integer;
  46. i: integer;
  47. map: TTilingTerrain;
  48. begin
  49. gl.clear(gl.COLOR_BUFFER_BIT + gl.DEPTH_BUFFER_BIT);
  50. // apply camera to view transform
  51. viewTransform := TMat4.Identity;
  52. viewTransform := viewTransform.Multiply(TMat4.Translate(camera.x, camera.y, camera.z));
  53. shader.SetUniformMat4('viewTransform', viewTransform);
  54. shader.SetUniformMat4('inverseViewTransform', viewTransform.Inverse);
  55. // move light with camera
  56. lightPosition.z += flySpeed;
  57. shader.SetUniformVec3('lightPosition', lightPosition);
  58. // animate camera
  59. camera.z -= flySpeed;
  60. camera.y := -(terrainSize/4) + Sin(camera.z / terrainSize) * 14;
  61. camera.x := -(terrainSize/2) + Cos(camera.z / terrainSize) * 20;
  62. terrainCoord := Divide(camera, terrainSize);
  63. endIndex := Trunc(Abs(terrainCoord.z));
  64. startIndex := endIndex - visiblity;
  65. if startIndex < 0 then
  66. startIndex := 0;
  67. //debugConsole.innerHTML := IntToStr(startIndex)+'/'+IntToStr(endIndex) + VecStr(terrainCoord);
  68. for i := startIndex to endIndex do
  69. begin
  70. if (maps.length = 0) or ((maps.length = i) and (maps[i] = nil)) then
  71. begin
  72. map := TTilingTerrain.Create(gl, terrainNoise, terrainSize, terrainResolution, V2(0, terrainSize * i));
  73. if (i - 1) >= 0 then
  74. map.neighbor := TTilingTerrain(maps[i - 1]);
  75. map.Generate;
  76. maps.push(map);
  77. // NOTE: does this free memory in JS?
  78. if startIndex - 1 >= 0 then
  79. maps[startIndex - 1] := nil;
  80. end;
  81. map := TTilingTerrain(maps[i]);
  82. modelTransform := TMat4.Identity;
  83. modelTransform := modelTransform.Multiply(TMat4.Translate(0, 0, terrainSize * i));
  84. shader.SetUniformMat4('modelTransform', modelTransform);
  85. map.Draw;
  86. end;
  87. end;
  88. procedure AnimateCanvas(time: TJSDOMHighResTimeStamp);
  89. begin
  90. if textureLoaded then
  91. DrawCanvas;
  92. if canvasAnimationHandler <> 0 then
  93. canvasAnimationHandler := window.requestAnimationFrame(@AnimateCanvas);
  94. end;
  95. procedure StartAnimatingCanvas;
  96. begin
  97. canvasAnimationHandler := window.requestAnimationFrame(@AnimateCanvas);
  98. end;
  99. function LoadedTexture (event: TEventListenerEvent): boolean;
  100. var
  101. texture: TJSWebGLTexture;
  102. begin
  103. texture := gl.createTexture;
  104. gl.bindTexture(gl.TEXTURE_2D, texture);
  105. gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
  106. gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
  107. gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
  108. gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
  109. gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, TexImageSource(event.target));
  110. textureLoaded := true;
  111. result := true;
  112. end;
  113. var
  114. canvas: TJSHTMLCanvasElement;
  115. vertexShaderSource: string;
  116. fragmentShaderSource: string;
  117. img: TJSHTMLElement;
  118. begin
  119. // add debug status
  120. debugConsole := document.getElementById('debug-console');
  121. // make webgl context
  122. canvas := TJSHTMLCanvasElement(document.createElement('canvas'));
  123. canvas.width := 800;
  124. canvas.height := 600;
  125. document.body.appendChild(canvas);
  126. gl := TJSWebGLRenderingContext(canvas.getContext('webgl'));
  127. if gl = nil then
  128. begin
  129. writeln('failed to load webgl!');
  130. exit;
  131. end;
  132. // create shaders from source in html
  133. // TODO: move these to .glsl files so error messages make more sense
  134. // and give valid line numbers
  135. vertexShaderSource := document.getElementById('vertex.glsl').textContent;
  136. fragmentShaderSource := document.getElementById('fragment.glsl').textContent;
  137. shader := TShader.Create(gl, vertexShaderSource, fragmentShaderSource);
  138. shader.Compile;
  139. shader.BindAttribLocation(0, 'in_position');
  140. shader.BindAttribLocation(1, 'in_texCoord');
  141. shader.BindAttribLocation(2, 'in_normal');
  142. shader.Link;
  143. shader.Use;
  144. // prepare context
  145. gl.clearColor(0.9, 0.9, 0.9, 1);
  146. gl.viewport(0, 0, canvas.width, canvas.height);
  147. gl.enable(gl.DEPTH_TEST);
  148. gl.enable(gl.BLEND);
  149. gl.Enable(gl.CULL_FACE);
  150. gl.CullFace(gl.BACK);
  151. // set projection transform
  152. projTransform := TMat4.Perspective(60.0, canvas.width / canvas.height, 0.1, 2000);
  153. shader.SetUniformMat4('projTransform', projTransform);
  154. // lighting
  155. lightPosition := V3(0, terrainSize / 2, -(terrainSize/2));
  156. shader.SetUniformVec3('lightPosition', lightPosition);
  157. shader.SetUniformVec3('lightColor', V3(1, 1, 1));
  158. // model material
  159. shader.SetUniformFloat('shineDamper', 1000);
  160. shader.SetUniformFloat('reflectivity', 1);
  161. gl.clear(gl.COLOR_BUFFER_BIT + gl.DEPTH_BUFFER_BIT);
  162. camera.x := -(terrainSize/2);
  163. camera.y := -(terrainSize/4);
  164. camera.z := -(terrainSize/2);
  165. // load terrain texture from image tag
  166. //img := TJSHTMLElement(document.getElementById('terrain-texture'));
  167. // <image id="terrain-texture" crossOrigin="anonymous" src="res/ground.jpg" hidden/>
  168. img := TJSHTMLElement(document.createElement('IMG'));
  169. img.setAttribute('height', '512');
  170. img.setAttribute('width', '512');
  171. img.setAttribute('crossOrigin', 'anonymous');
  172. img.setAttribute('src', 'res/ground.jpg');
  173. img.onload := @LoadedTexture;
  174. //texture := gl.createTexture;
  175. //gl.bindTexture(gl.TEXTURE_2D, texture);
  176. //gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
  177. //gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
  178. //gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
  179. //gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
  180. //gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, TJSTexImageSource(img));
  181. //textureLoaded := true;
  182. // TODO: RandSeed doesn't seem to work so we get a random seed each time
  183. terrainNoise := TNoise.Create(RandomNoiseSeed(1));
  184. maps := TJSArray.new;
  185. StartAnimatingCanvas;
  186. end.