@zephyr3d/scene 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (236) hide show
  1. package/dist/animation/animation.js +173 -0
  2. package/dist/animation/animation.js.map +1 -0
  3. package/dist/animation/animationset.js +95 -0
  4. package/dist/animation/animationset.js.map +1 -0
  5. package/dist/animation/animationtrack.js +38 -0
  6. package/dist/animation/animationtrack.js.map +1 -0
  7. package/dist/animation/eulerrotationtrack.js +33 -0
  8. package/dist/animation/eulerrotationtrack.js.map +1 -0
  9. package/dist/animation/rotationtrack.js +37 -0
  10. package/dist/animation/rotationtrack.js.map +1 -0
  11. package/dist/animation/scaletrack.js +36 -0
  12. package/dist/animation/scaletrack.js.map +1 -0
  13. package/dist/animation/skeleton.js +97 -0
  14. package/dist/animation/skeleton.js.map +1 -0
  15. package/dist/animation/translationtrack.js +36 -0
  16. package/dist/animation/translationtrack.js.map +1 -0
  17. package/dist/animation/usertrack.js +47 -0
  18. package/dist/animation/usertrack.js.map +1 -0
  19. package/dist/app.js +173 -0
  20. package/dist/app.js.map +1 -0
  21. package/dist/asset/assetmanager.js +476 -0
  22. package/dist/asset/assetmanager.js.map +1 -0
  23. package/dist/asset/builtin.js +373 -0
  24. package/dist/asset/builtin.js.map +1 -0
  25. package/dist/asset/loaders/dds/dds.js +472 -0
  26. package/dist/asset/loaders/dds/dds.js.map +1 -0
  27. package/dist/asset/loaders/dds/dds_loader.js +38 -0
  28. package/dist/asset/loaders/dds/dds_loader.js.map +1 -0
  29. package/dist/asset/loaders/gltf/gltf_loader.js +981 -0
  30. package/dist/asset/loaders/gltf/gltf_loader.js.map +1 -0
  31. package/dist/asset/loaders/gltf/helpers.js +314 -0
  32. package/dist/asset/loaders/gltf/helpers.js.map +1 -0
  33. package/dist/asset/loaders/hdr/hdr.js +175 -0
  34. package/dist/asset/loaders/hdr/hdr.js.map +1 -0
  35. package/dist/asset/loaders/image/tga_Loader.js +117 -0
  36. package/dist/asset/loaders/image/tga_Loader.js.map +1 -0
  37. package/dist/asset/loaders/image/webimage_loader.js +50 -0
  38. package/dist/asset/loaders/image/webimage_loader.js.map +1 -0
  39. package/dist/asset/loaders/loader.js +45 -0
  40. package/dist/asset/loaders/loader.js.map +1 -0
  41. package/dist/asset/model.js +264 -0
  42. package/dist/asset/model.js.map +1 -0
  43. package/dist/blitter/blitter.js +389 -0
  44. package/dist/blitter/blitter.js.map +1 -0
  45. package/dist/blitter/box.js +118 -0
  46. package/dist/blitter/box.js.map +1 -0
  47. package/dist/blitter/copy.js +22 -0
  48. package/dist/blitter/copy.js.map +1 -0
  49. package/dist/blitter/depthlimitedgaussion.js +166 -0
  50. package/dist/blitter/depthlimitedgaussion.js.map +1 -0
  51. package/dist/blitter/gaussianblur.js +229 -0
  52. package/dist/blitter/gaussianblur.js.map +1 -0
  53. package/dist/camera/base.js +90 -0
  54. package/dist/camera/base.js.map +1 -0
  55. package/dist/camera/camera.js +358 -0
  56. package/dist/camera/camera.js.map +1 -0
  57. package/dist/camera/fps.js +246 -0
  58. package/dist/camera/fps.js.map +1 -0
  59. package/dist/camera/orbit.js +157 -0
  60. package/dist/camera/orbit.js.map +1 -0
  61. package/dist/camera/orthocamera.js +126 -0
  62. package/dist/camera/orthocamera.js.map +1 -0
  63. package/dist/camera/perspectivecamera.js +133 -0
  64. package/dist/camera/perspectivecamera.js.map +1 -0
  65. package/dist/index.d.ts +8402 -0
  66. package/dist/index.js +87 -0
  67. package/dist/index.js.map +1 -0
  68. package/dist/input/inputmgr.js +242 -0
  69. package/dist/input/inputmgr.js.map +1 -0
  70. package/dist/material/blinn.js +75 -0
  71. package/dist/material/blinn.js.map +1 -0
  72. package/dist/material/grassmaterial.js +221 -0
  73. package/dist/material/grassmaterial.js.map +1 -0
  74. package/dist/material/lambert.js +52 -0
  75. package/dist/material/lambert.js.map +1 -0
  76. package/dist/material/lightmodel.js +2074 -0
  77. package/dist/material/lightmodel.js.map +1 -0
  78. package/dist/material/lit.js +578 -0
  79. package/dist/material/lit.js.map +1 -0
  80. package/dist/material/material.js +458 -0
  81. package/dist/material/material.js.map +1 -0
  82. package/dist/material/meshmaterial.js +311 -0
  83. package/dist/material/meshmaterial.js.map +1 -0
  84. package/dist/material/mixins/albedocolor.js +130 -0
  85. package/dist/material/mixins/albedocolor.js.map +1 -0
  86. package/dist/material/mixins/texture.js +110 -0
  87. package/dist/material/mixins/texture.js.map +1 -0
  88. package/dist/material/mixins/vertexcolor.js +45 -0
  89. package/dist/material/mixins/vertexcolor.js.map +1 -0
  90. package/dist/material/pbr.js +27 -0
  91. package/dist/material/pbr.js.map +1 -0
  92. package/dist/material/standard.js +282 -0
  93. package/dist/material/standard.js.map +1 -0
  94. package/dist/material/terrainlightmodel.js +259 -0
  95. package/dist/material/terrainlightmodel.js.map +1 -0
  96. package/dist/material/terrainmaterial.js +139 -0
  97. package/dist/material/terrainmaterial.js.map +1 -0
  98. package/dist/material/unlit.js +29 -0
  99. package/dist/material/unlit.js.map +1 -0
  100. package/dist/posteffect/bloom.js +398 -0
  101. package/dist/posteffect/bloom.js.map +1 -0
  102. package/dist/posteffect/compositor.js +264 -0
  103. package/dist/posteffect/compositor.js.map +1 -0
  104. package/dist/posteffect/fxaa.js +291 -0
  105. package/dist/posteffect/fxaa.js.map +1 -0
  106. package/dist/posteffect/grayscale.js +87 -0
  107. package/dist/posteffect/grayscale.js.map +1 -0
  108. package/dist/posteffect/posteffect.js +165 -0
  109. package/dist/posteffect/posteffect.js.map +1 -0
  110. package/dist/posteffect/sao.js +327 -0
  111. package/dist/posteffect/sao.js.map +1 -0
  112. package/dist/posteffect/tonemap.js +112 -0
  113. package/dist/posteffect/tonemap.js.map +1 -0
  114. package/dist/posteffect/water.js +535 -0
  115. package/dist/posteffect/water.js.map +1 -0
  116. package/dist/render/clipmap.js +462 -0
  117. package/dist/render/clipmap.js.map +1 -0
  118. package/dist/render/cluster_light.js +329 -0
  119. package/dist/render/cluster_light.js.map +1 -0
  120. package/dist/render/cull_visitor.js +124 -0
  121. package/dist/render/cull_visitor.js.map +1 -0
  122. package/dist/render/depth_pass.js +47 -0
  123. package/dist/render/depth_pass.js.map +1 -0
  124. package/dist/render/envlight.js +282 -0
  125. package/dist/render/envlight.js.map +1 -0
  126. package/dist/render/forward.js +186 -0
  127. package/dist/render/forward.js.map +1 -0
  128. package/dist/render/forward_pass.js +137 -0
  129. package/dist/render/forward_pass.js.map +1 -0
  130. package/dist/render/helper.js +38 -0
  131. package/dist/render/helper.js.map +1 -0
  132. package/dist/render/primitive.js +246 -0
  133. package/dist/render/primitive.js.map +1 -0
  134. package/dist/render/render_queue.js +163 -0
  135. package/dist/render/render_queue.js.map +1 -0
  136. package/dist/render/renderpass.js +151 -0
  137. package/dist/render/renderpass.js.map +1 -0
  138. package/dist/render/renderscheme.js +61 -0
  139. package/dist/render/renderscheme.js.map +1 -0
  140. package/dist/render/scatteringlut.js +634 -0
  141. package/dist/render/scatteringlut.js.map +1 -0
  142. package/dist/render/shadowmap_pass.js +70 -0
  143. package/dist/render/shadowmap_pass.js.map +1 -0
  144. package/dist/render/sky.js +881 -0
  145. package/dist/render/sky.js.map +1 -0
  146. package/dist/render/temporalcache.js +222 -0
  147. package/dist/render/temporalcache.js.map +1 -0
  148. package/dist/render/watermesh.js +835 -0
  149. package/dist/render/watermesh.js.map +1 -0
  150. package/dist/scene/environment.js +146 -0
  151. package/dist/scene/environment.js.map +1 -0
  152. package/dist/scene/graph_node.js +69 -0
  153. package/dist/scene/graph_node.js.map +1 -0
  154. package/dist/scene/light.js +436 -0
  155. package/dist/scene/light.js.map +1 -0
  156. package/dist/scene/mesh.js +215 -0
  157. package/dist/scene/mesh.js.map +1 -0
  158. package/dist/scene/model.js +111 -0
  159. package/dist/scene/model.js.map +1 -0
  160. package/dist/scene/octree.js +651 -0
  161. package/dist/scene/octree.js.map +1 -0
  162. package/dist/scene/octree_update_visitor.js +16 -0
  163. package/dist/scene/octree_update_visitor.js.map +1 -0
  164. package/dist/scene/raycast_visitor.js +72 -0
  165. package/dist/scene/raycast_visitor.js.map +1 -0
  166. package/dist/scene/scene.js +225 -0
  167. package/dist/scene/scene.js.map +1 -0
  168. package/dist/scene/scene_node.js +299 -0
  169. package/dist/scene/scene_node.js.map +1 -0
  170. package/dist/scene/terrain/grass.js +277 -0
  171. package/dist/scene/terrain/grass.js.map +1 -0
  172. package/dist/scene/terrain/heightfield.js +391 -0
  173. package/dist/scene/terrain/heightfield.js.map +1 -0
  174. package/dist/scene/terrain/patch.js +530 -0
  175. package/dist/scene/terrain/patch.js.map +1 -0
  176. package/dist/scene/terrain/quadtree.js +430 -0
  177. package/dist/scene/terrain/quadtree.js.map +1 -0
  178. package/dist/scene/terrain/terrain.js +258 -0
  179. package/dist/scene/terrain/terrain.js.map +1 -0
  180. package/dist/scene/xform.js +224 -0
  181. package/dist/scene/xform.js.map +1 -0
  182. package/dist/shaders/builtins.js +110 -0
  183. package/dist/shaders/builtins.js.map +1 -0
  184. package/dist/shaders/framework.js +709 -0
  185. package/dist/shaders/framework.js.map +1 -0
  186. package/dist/shaders/lighting.js +335 -0
  187. package/dist/shaders/lighting.js.map +1 -0
  188. package/dist/shaders/misc.js +405 -0
  189. package/dist/shaders/misc.js.map +1 -0
  190. package/dist/shaders/noise.js +157 -0
  191. package/dist/shaders/noise.js.map +1 -0
  192. package/dist/shaders/pbr.js +132 -0
  193. package/dist/shaders/pbr.js.map +1 -0
  194. package/dist/shaders/shadow.js +642 -0
  195. package/dist/shaders/shadow.js.map +1 -0
  196. package/dist/shaders/water.js +630 -0
  197. package/dist/shaders/water.js.map +1 -0
  198. package/dist/shadow/esm.js +235 -0
  199. package/dist/shadow/esm.js.map +1 -0
  200. package/dist/shadow/pcf_opt.js +182 -0
  201. package/dist/shadow/pcf_opt.js.map +1 -0
  202. package/dist/shadow/pcf_pd.js +190 -0
  203. package/dist/shadow/pcf_pd.js.map +1 -0
  204. package/dist/shadow/shadow_impl.js +15 -0
  205. package/dist/shadow/shadow_impl.js.map +1 -0
  206. package/dist/shadow/shadowmapper.js +709 -0
  207. package/dist/shadow/shadowmapper.js.map +1 -0
  208. package/dist/shadow/ssm.js +194 -0
  209. package/dist/shadow/ssm.js.map +1 -0
  210. package/dist/shadow/vsm.js +298 -0
  211. package/dist/shadow/vsm.js.map +1 -0
  212. package/dist/shapes/box.js +313 -0
  213. package/dist/shapes/box.js.map +1 -0
  214. package/dist/shapes/cylinder.js +74 -0
  215. package/dist/shapes/cylinder.js.map +1 -0
  216. package/dist/shapes/plane.js +48 -0
  217. package/dist/shapes/plane.js.map +1 -0
  218. package/dist/shapes/shape.js +33 -0
  219. package/dist/shapes/shape.js.map +1 -0
  220. package/dist/shapes/sphere.js +91 -0
  221. package/dist/shapes/sphere.js.map +1 -0
  222. package/dist/shapes/torus.js +100 -0
  223. package/dist/shapes/torus.js.map +1 -0
  224. package/dist/utility/aabbtree.js +390 -0
  225. package/dist/utility/aabbtree.js.map +1 -0
  226. package/dist/utility/bounding_volume.js +78 -0
  227. package/dist/utility/bounding_volume.js.map +1 -0
  228. package/dist/utility/panorama.js +163 -0
  229. package/dist/utility/panorama.js.map +1 -0
  230. package/dist/utility/pmrem.js +345 -0
  231. package/dist/utility/pmrem.js.map +1 -0
  232. package/dist/utility/shprojection.js +448 -0
  233. package/dist/utility/shprojection.js.map +1 -0
  234. package/dist/values.js +48 -0
  235. package/dist/values.js.map +1 -0
  236. package/package.json +70 -0
@@ -0,0 +1,430 @@
1
+ import { isPowerOf2, nextPowerOf2, Vector3, Frustum, Matrix4x4, ClipState } from '@zephyr3d/base';
2
+ import { BoundingBox } from '../../utility/bounding_volume.js';
3
+ import { TerrainPatch } from './patch.js';
4
+ import { HeightField } from './heightfield.js';
5
+ import { Application } from '../../app.js';
6
+ import { RENDER_PASS_TYPE_SHADOWMAP } from '../../values.js';
7
+
8
+ /** @internal */ class QuadtreeNode {
9
+ _patch;
10
+ _grassClusters;
11
+ _parent;
12
+ _children;
13
+ constructor(){
14
+ this._patch = null;
15
+ this._grassClusters = [];
16
+ this._parent = null;
17
+ this._children = null;
18
+ }
19
+ get grassClusters() {
20
+ return this._grassClusters;
21
+ }
22
+ addGrassCluster(grassCluster) {
23
+ this._grassClusters.push(grassCluster);
24
+ }
25
+ initialize(quadtree, parent, rowIndex, colIndex, baseVertices, normals, heightScale, elevations) {
26
+ this._parent = parent;
27
+ this._children = [];
28
+ this._patch = new TerrainPatch(quadtree.terrain);
29
+ //const rowIndex = position === PatchPosition.LeftBottom || position === PatchPosition.LeftTop ? 0 : 1;
30
+ //const colIndex = position === PatchPosition.LeftTop || position === PatchPosition.RightTop ? 0 : 1;
31
+ if (!this._patch.initialize(quadtree, this._parent?._patch || null, rowIndex, colIndex, baseVertices, normals, heightScale, elevations)) {
32
+ return false;
33
+ }
34
+ if (this._patch.getStep() > 1) {
35
+ let bbox = null;
36
+ const size = (quadtree.getPatchSize() - 1) * (this._patch.getStep() >> 1);
37
+ const offsetX = this._patch.getOffsetX();
38
+ const offsetZ = this._patch.getOffsetZ();
39
+ const offsets = [
40
+ [
41
+ offsetX,
42
+ offsetZ
43
+ ],
44
+ [
45
+ offsetX + size,
46
+ offsetZ
47
+ ],
48
+ [
49
+ offsetX,
50
+ offsetZ + size
51
+ ],
52
+ [
53
+ offsetX + size,
54
+ offsetZ + size
55
+ ]
56
+ ];
57
+ const rootSizeX = quadtree.getRootSizeX() - 1;
58
+ const rootSizeZ = quadtree.getRootSizeZ() - 1;
59
+ for(let i = 0; i < 4; ++i){
60
+ if (offsets[i][0] >= rootSizeX || offsets[i][1] >= rootSizeZ) {
61
+ this._children[i] = null;
62
+ } else {
63
+ this._children[i] = new QuadtreeNode();
64
+ if (!this._children[i].initialize(quadtree, this, i & 1, i >> 1, baseVertices, normals, heightScale, elevations)) {
65
+ return false;
66
+ }
67
+ const childBBox = this._children[i]._patch.getBoundingBox();
68
+ if (childBBox) {
69
+ if (!bbox) {
70
+ bbox = new BoundingBox();
71
+ bbox.beginExtend();
72
+ }
73
+ bbox.extend(childBBox.minPoint);
74
+ bbox.extend(childBBox.maxPoint);
75
+ }
76
+ }
77
+ }
78
+ this._patch.setBoundingBox(bbox);
79
+ }
80
+ return true;
81
+ }
82
+ setupCamera(viewportH, tanHalfFovy, maxPixelError) {
83
+ if (this._patch && !this._patch.isDummy()) {
84
+ this._patch.setupCamera(viewportH, tanHalfFovy, maxPixelError);
85
+ }
86
+ for(let i = 0; i < 4; ++i){
87
+ if (this._children[i]) {
88
+ this._children[i].setupCamera(viewportH, tanHalfFovy, maxPixelError);
89
+ }
90
+ }
91
+ }
92
+ getBoundingbox() {
93
+ return this._patch.getBoundingBox();
94
+ }
95
+ getPatch() {
96
+ return this._patch;
97
+ }
98
+ getParent() {
99
+ return this._parent;
100
+ }
101
+ getChild(index) {
102
+ return this._children[index];
103
+ }
104
+ }
105
+ /** @internal */ class Quadtree {
106
+ _baseVertices;
107
+ _indices;
108
+ _indicesWireframe;
109
+ _normalMap;
110
+ _scaleX;
111
+ _scaleZ;
112
+ _patchSize;
113
+ _rootSizeX;
114
+ _rootSizeZ;
115
+ _rootSize;
116
+ _primitiveCount;
117
+ _primitiveType;
118
+ _rootNode;
119
+ _terrain;
120
+ _heightField;
121
+ constructor(terrain){
122
+ this._terrain = terrain;
123
+ this._baseVertices = null;
124
+ this._indices = null;
125
+ this._indicesWireframe = null;
126
+ this._normalMap = null;
127
+ this._scaleX = 1;
128
+ this._scaleZ = 1;
129
+ this._patchSize = 0;
130
+ this._rootSizeX = 0;
131
+ this._rootSizeZ = 0;
132
+ this._rootSize = 0;
133
+ this._heightField = null;
134
+ this._rootNode = null;
135
+ this._primitiveCount = 0;
136
+ this._primitiveType = 'triangle-strip';
137
+ }
138
+ get normalMap() {
139
+ return this._normalMap;
140
+ }
141
+ get rootNode() {
142
+ return this._rootNode;
143
+ }
144
+ get terrain() {
145
+ return this._terrain;
146
+ }
147
+ build(patchSize, rootSizeX, rootSizeZ, elevations, scaleX, scaleY, scaleZ, vertexCacheSize) {
148
+ if (!isPowerOf2(patchSize - 1) || !!((rootSizeX - 1) % (patchSize - 1)) || !!((rootSizeZ - 1) % (patchSize - 1)) || !elevations) {
149
+ return false;
150
+ }
151
+ this._heightField = new HeightField();
152
+ if (!this._heightField.init(rootSizeX, rootSizeZ, 0, 0, scaleX, scaleY, scaleZ, elevations)) {
153
+ this._heightField = null;
154
+ return false;
155
+ }
156
+ const device = Application.instance.device;
157
+ this._patchSize = patchSize;
158
+ this._rootSizeX = rootSizeX;
159
+ this._rootSizeZ = rootSizeZ;
160
+ this._rootSize = nextPowerOf2(Math.max(rootSizeX - 1, rootSizeZ - 1)) + 1;
161
+ this._scaleX = scaleX;
162
+ this._scaleZ = scaleZ;
163
+ // Create base vertex buffer
164
+ const dimension = patchSize + 2; // with "skirts"
165
+ const vertices = new Float32Array(dimension * dimension * 3);
166
+ let offset = 0;
167
+ // top skirt
168
+ vertices[0] = 0;
169
+ vertices[1] = 0;
170
+ vertices[2] = 0;
171
+ for(let i = 1; i < dimension - 1; ++i){
172
+ vertices[3 * i + 0] = i - 1;
173
+ vertices[3 * i + 1] = 0;
174
+ vertices[3 * i + 2] = 0;
175
+ }
176
+ vertices[3 * (dimension - 1) + 0] = dimension - 3;
177
+ vertices[3 * (dimension - 1) + 1] = 0;
178
+ vertices[3 * (dimension - 1) + 2] = 0;
179
+ offset += dimension * 3;
180
+ for(let i = 1; i < dimension - 1; ++i, offset += dimension * 3){
181
+ // left skirt
182
+ vertices[offset + 0] = 0;
183
+ vertices[offset + 1] = 0;
184
+ vertices[offset + 2] = i - 1;
185
+ // height
186
+ for(let j = 1; j < dimension - 1; ++j){
187
+ vertices[offset + 3 * j + 0] = j - 1;
188
+ vertices[offset + 3 * j + 1] = 0;
189
+ vertices[offset + 3 * j + 2] = i - 1;
190
+ }
191
+ // right skirt
192
+ vertices[offset + (dimension - 1) * 3 + 0] = dimension - 3;
193
+ vertices[offset + (dimension - 1) * 3 + 1] = 0;
194
+ vertices[offset + (dimension - 1) * 3 + 2] = i - 1;
195
+ }
196
+ // bottom skirt
197
+ vertices[offset + 0] = 0;
198
+ vertices[offset + 1] = 0;
199
+ vertices[offset + 2] = dimension - 3;
200
+ for(let i = 1; i < dimension - 1; ++i){
201
+ vertices[offset + 3 * i + 0] = i - 1;
202
+ vertices[offset + 3 * i + 1] = 0;
203
+ vertices[offset + 3 * i + 2] = dimension - 3;
204
+ }
205
+ vertices[offset + (dimension - 1) * 3 + 0] = dimension - 3;
206
+ vertices[offset + (dimension - 1) * 3 + 1] = 0;
207
+ vertices[offset + (dimension - 1) * 3 + 2] = dimension - 3;
208
+ this._baseVertices = vertices;
209
+ // Create base index buffer
210
+ const indices = this.strip(vertexCacheSize);
211
+ this._indices = device.createIndexBuffer(indices, {
212
+ managed: true
213
+ });
214
+ const lineIndices = this.line(indices);
215
+ this._indicesWireframe = device.createIndexBuffer(lineIndices, {
216
+ managed: true
217
+ });
218
+ this._primitiveCount = indices.length - 2;
219
+ this._primitiveType = 'triangle-strip';
220
+ this._rootNode = new QuadtreeNode();
221
+ const normals = this._heightField.normals;
222
+ const normalMapBytes = new Uint8Array(normals.length * 4);
223
+ for(let i = 0; i < normals.length; i++){
224
+ const normal = normals[i];
225
+ normalMapBytes[i * 4 + 0] = Math.floor((normal.x * 0.5 + 0.5) * 255);
226
+ normalMapBytes[i * 4 + 1] = Math.floor((normal.y * 0.5 + 0.5) * 255);
227
+ normalMapBytes[i * 4 + 2] = Math.floor((normal.z * 0.5 + 0.5) * 255);
228
+ normalMapBytes[i * 4 + 3] = 255;
229
+ }
230
+ this._normalMap = device.createTexture2D('rgba8unorm', rootSizeX, rootSizeZ, {
231
+ samplerOptions: {
232
+ mipFilter: 'none'
233
+ }
234
+ });
235
+ this._normalMap.name = `TerrainNormalMap-${this._normalMap.uid}`;
236
+ this._normalMap.update(normalMapBytes, 0, 0, this._normalMap.width, this._normalMap.height);
237
+ return this._rootNode.initialize(this, null, 0, 0, this._baseVertices, normals, scaleY, elevations);
238
+ }
239
+ strip(vertexCacheSize) {
240
+ const dimension = this._patchSize + 2;
241
+ const step = (vertexCacheSize >> 1) - 1;
242
+ const indices = [];
243
+ for(let i = 0; i < dimension - 1; i += step){
244
+ const start = i;
245
+ const end = i + step > dimension - 1 ? dimension - 1 : i + step;
246
+ for(let j = 0; j < dimension - 1; ++j){
247
+ for(let k = start; k <= end; ++k){
248
+ indices.push((dimension - 1 - k) * dimension + j);
249
+ indices.push((dimension - 1 - k) * dimension + j + 1);
250
+ }
251
+ indices.push((dimension - 1 - end) * dimension + j + 1);
252
+ indices.push(j == dimension - 2 ? (dimension - 1 - end) * dimension : (dimension - 1 - start) * dimension + j + 1);
253
+ }
254
+ }
255
+ indices.length = indices.length - 2;
256
+ return new Uint16Array(indices);
257
+ }
258
+ line(strip) {
259
+ const numTris = strip.length - 2;
260
+ const lineIndices = [];
261
+ let lastSkipped = true;
262
+ let a, b, c;
263
+ for(let i = 0; i < numTris; i++){
264
+ if (i % 2 === 0) {
265
+ a = strip[i];
266
+ b = strip[i + 1];
267
+ c = strip[i + 2];
268
+ } else {
269
+ a = strip[i + 1];
270
+ b = strip[i];
271
+ c = strip[i + 2];
272
+ }
273
+ const thisSkipped = a === b || a === c || b === c;
274
+ if (!thisSkipped) {
275
+ if (lastSkipped) {
276
+ lineIndices.push(a, b);
277
+ }
278
+ lineIndices.push(b, c, c, a);
279
+ }
280
+ lastSkipped = thisSkipped;
281
+ }
282
+ return new Uint16Array(lineIndices);
283
+ }
284
+ setupCamera(viewportH, tanHalfFovy, maxPixelError) {
285
+ this._rootNode?.setupCamera(viewportH, tanHalfFovy, maxPixelError);
286
+ }
287
+ getBoundingBox(bbox) {
288
+ if (this._heightField) {
289
+ bbox.minPoint = this._heightField.getBoundingbox().minPoint;
290
+ bbox.maxPoint = this._heightField.getBoundingbox().maxPoint;
291
+ } else {
292
+ bbox.minPoint = Vector3.zero();
293
+ bbox.maxPoint = Vector3.zero();
294
+ }
295
+ }
296
+ getPatchSize() {
297
+ return this._patchSize;
298
+ }
299
+ getRootSize() {
300
+ return this._rootSize;
301
+ }
302
+ getRootSizeX() {
303
+ return this._rootSizeX;
304
+ }
305
+ getRootSizeZ() {
306
+ return this._rootSizeZ;
307
+ }
308
+ getTerrain() {
309
+ return this._terrain;
310
+ }
311
+ getElevations() {
312
+ return this._heightField?.getHeights() || null;
313
+ }
314
+ getScaleX() {
315
+ return this._scaleX;
316
+ }
317
+ getScaleZ() {
318
+ return this._scaleZ;
319
+ }
320
+ getIndices() {
321
+ return this._indices;
322
+ }
323
+ getIndicesWireframe() {
324
+ return this._indicesWireframe;
325
+ }
326
+ getPrimitiveCount() {
327
+ return this._primitiveCount;
328
+ }
329
+ getPrimitiveType() {
330
+ return this._primitiveType;
331
+ }
332
+ getHeightField() {
333
+ return this._heightField;
334
+ }
335
+ /** @internal */ cull(visitor, viewPoint, worldMatrix) {
336
+ if (this._rootNode && this._terrain) {
337
+ const frustum = new Frustum(Matrix4x4.multiply(visitor.camera.viewProjectionMatrix, worldMatrix));
338
+ return this.cull_r(visitor, this._rootNode, viewPoint, worldMatrix, frustum, true, false);
339
+ }
340
+ return 0;
341
+ }
342
+ /** @internal */ cull_r(visitor, node, viewPoint, worldMatrix, frustum, cliptest, ignorePatch) {
343
+ const camera = visitor.camera;
344
+ const bbox = node.getBoundingbox();
345
+ let ret = 0;
346
+ let clipState;
347
+ if (cliptest) {
348
+ clipState = camera.clipMask ? bbox.getClipStateWithFrustumMask(frustum, camera.clipMask) : bbox.getClipStateWithFrustum(frustum);
349
+ if (clipState === ClipState.NOT_CLIPPED) {
350
+ return ret;
351
+ } else if (clipState === ClipState.A_INSIDE_B) {
352
+ cliptest = false;
353
+ }
354
+ } else {
355
+ clipState = ClipState.A_INSIDE_B;
356
+ }
357
+ if (!ignorePatch) {
358
+ const ld = node.getPatch().isDummy() ? -1 : node.getPatch().getLODDistance();
359
+ const lodDistance = ld >= 0 ? ld * ld : Number.MAX_VALUE;
360
+ const eyeDistSq = ld >= 0 ? node.getPatch().sqrDistanceToPoint(viewPoint) : 0;
361
+ if (eyeDistSq >= lodDistance || !node.getChild(0)) {
362
+ if (!node.getPatch().isDummy()) {
363
+ visitor.push(camera, node.getPatch(), this._terrain.renderOrder);
364
+ ignorePatch = true;
365
+ ret = 1;
366
+ }
367
+ }
368
+ }
369
+ if (node.grassClusters.length > 0 && visitor.renderPass.type !== RENDER_PASS_TYPE_SHADOWMAP) {
370
+ for (const grass of node.grassClusters){
371
+ visitor.push(camera, grass, this._terrain.renderOrder);
372
+ }
373
+ }
374
+ for(let i = 0; i < 4; i++){
375
+ const child = node.getChild(i);
376
+ if (child) {
377
+ ret += this.cull_r(visitor, child, viewPoint, worldMatrix, frustum, cliptest, ignorePatch);
378
+ }
379
+ }
380
+ /*
381
+ if (eyeDistSq < lodDistance && node.getChild(0)) {
382
+ for (let i = 0; i < 4; i++) {
383
+ const child = node.getChild(i);
384
+ if (child) {
385
+ ret += this.cull_r(visitor, child, viewPoint, worldMatrix, frustum, cliptest, ignorePatch);
386
+ }
387
+ }
388
+ } else if (!node.getPatch().isDummy()) {
389
+ visitor.push(camera, node.getPatch(), this._terrain.renderOrder, this._terrain.castShadow, clipState, bbox);
390
+ return 1;
391
+ }
392
+ */ /*
393
+ if ((!node.getChild(0) || eyeDistSq >= lodDistance) && !node.getPatch().isDummy()) {
394
+ visitor.push(camera, node.getPatch(), this._terrain.renderOrder, this._terrain.castShadow, clipState, bbox);
395
+ ret = 1;
396
+ ignorePatch = true;
397
+ }
398
+ if (node.grassCluster) {
399
+ visitor.push(camera, node.grassCluster, this._terrain.renderOrder, this._terrain.castShadow, clipState, bbox);
400
+ } else if (!ignorePatch) {
401
+ for (let i = 0; i < 4; i++) {
402
+ const child = node.getChild(i);
403
+ if (child) {
404
+ ret += this.cull_r(visitor, child, viewPoint, worldMatrix, frustum, cliptest, ignorePatch);
405
+ }
406
+ }
407
+ }
408
+ */ /*
409
+ const drawPatch = !(eyeDistSq < lodDistance && node.getChild(0))
410
+ if (
411
+ eyeDistSq < lodDistance
412
+ && node.getChild(0)
413
+ && (lodLevel === 0 || node.getPatch().getMipLevel() < lodLevel)
414
+ ) {
415
+ for (let i = 0; i < 4; i++) {
416
+ const child = node.getChild(i);
417
+ if (child) {
418
+ ret += this.cull_r(visitor, child, viewPoint, worldMatrix, frustum, cliptest);
419
+ }
420
+ }
421
+ } else if (!node.getPatch().isDummy()) {
422
+ visitor.push(camera, node.getPatch(), this._terrain.renderOrder, this._terrain.castShadow, clipState, bbox);
423
+ return 1;
424
+ }
425
+ */ return ret;
426
+ }
427
+ }
428
+
429
+ export { Quadtree, QuadtreeNode };
430
+ //# sourceMappingURL=quadtree.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"quadtree.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -0,0 +1,258 @@
1
+ import { Vector3, Vector4, Vector2 } from '@zephyr3d/base';
2
+ import { Quadtree } from './quadtree.js';
3
+ import { MAX_DETAIL_TEXTURE_LEVELS, TerrainMaterial } from '../../material/terrainmaterial.js';
4
+ import { GraphNode } from '../graph_node.js';
5
+ import { Application } from '../../app.js';
6
+ import { GrassManager } from './grass.js';
7
+ import { GrassMaterial } from '../../material/grassmaterial.js';
8
+
9
+ /**
10
+ * Terrain node
11
+ * @public
12
+ */ class Terrain extends GraphNode {
13
+ /** @internal */ _quadtree;
14
+ /** @internal */ _grassManager;
15
+ /** @internal */ _maxPixelError;
16
+ /** @internal */ _maxPixelErrorDirty;
17
+ /** @internal */ _lodCamera;
18
+ /** @internal */ _heightFieldScale;
19
+ /** @internal */ _patchSize;
20
+ /** @internal */ _lastTanHalfFOVY;
21
+ /** @internal */ _width;
22
+ /** @internal */ _height;
23
+ /** @internal */ _material;
24
+ /** @internal */ _grassMaterial;
25
+ /** @internal */ _maxDetailTextureLevels;
26
+ /** @internal */ _wireframe;
27
+ /** @internal */ _viewPoint;
28
+ /** @internal */ _castShadow;
29
+ /** @internal */ _instanceColor;
30
+ /** @internal */ _overridenStateSet;
31
+ /**
32
+ * Creates an instance of Terrain
33
+ * @param scene - The scene to which the terrain belongs
34
+ */ constructor(scene){
35
+ super(scene);
36
+ this._quadtree = null;
37
+ this._grassManager = null;
38
+ this._maxPixelError = 10;
39
+ this._maxPixelErrorDirty = true;
40
+ this._lodCamera = null;
41
+ this._heightFieldScale = Vector3.one();
42
+ this._patchSize = 33;
43
+ this._lastTanHalfFOVY = 0;
44
+ this._width = 0;
45
+ this._height = 0;
46
+ this._material = null;
47
+ this._grassMaterial = null;
48
+ this._maxDetailTextureLevels = MAX_DETAIL_TEXTURE_LEVELS;
49
+ this._wireframe = false;
50
+ this._viewPoint = null;
51
+ this._castShadow = true;
52
+ this._instanceColor = Vector4.zero();
53
+ this._overridenStateSet = null;
54
+ }
55
+ /** @internal */ get quadtree() {
56
+ return this._quadtree;
57
+ }
58
+ /**
59
+ * {@inheritDoc Drawable.getName}
60
+ */ getName() {
61
+ return this._name;
62
+ }
63
+ /**
64
+ * {@inheritDoc Drawable.getInstanceColor}
65
+ */ getInstanceColor() {
66
+ return this._instanceColor;
67
+ }
68
+ /** Wether the mesh node casts shadows */ get castShadow() {
69
+ return this._castShadow;
70
+ }
71
+ set castShadow(val) {
72
+ this._castShadow = !!val;
73
+ }
74
+ /** The maximum value of detail texture levels */ get maxDetailTextureLevels() {
75
+ return this._maxDetailTextureLevels;
76
+ }
77
+ /** The maximum pixel error for terrain LOD */ get maxPixelError() {
78
+ return this._maxPixelError;
79
+ }
80
+ set maxPixelError(val) {
81
+ if (val !== this._maxPixelError) {
82
+ this._maxPixelError = val;
83
+ this._maxPixelErrorDirty = true;
84
+ }
85
+ }
86
+ /** Camera that will be used to compute LOD level of terrain patches */ get LODCamera() {
87
+ return this._lodCamera;
88
+ }
89
+ set LODCamera(camera) {
90
+ this._lodCamera = camera;
91
+ }
92
+ /** Scaled terrain width */ get scaledWidth() {
93
+ return this._width * this._heightFieldScale.x;
94
+ }
95
+ /** Scaled terrain height */ get scaledHeight() {
96
+ return this._height * this._heightFieldScale.z;
97
+ }
98
+ /** Scale value of the height field */ get heightFieldScale() {
99
+ return this._heightFieldScale;
100
+ }
101
+ /** @internal */ get patchSize() {
102
+ return this._patchSize;
103
+ }
104
+ /** Width of the terrain */ get width() {
105
+ return this._width;
106
+ }
107
+ /** Height of the terrain */ get height() {
108
+ return this._height;
109
+ }
110
+ /** Material of the terrain */ get material() {
111
+ return this._material;
112
+ }
113
+ /** Grass material */ get grassMaterial() {
114
+ return this._grassMaterial;
115
+ }
116
+ /** Whether the terrain should be rendered in wireframe mode */ get wireframe() {
117
+ return this._wireframe;
118
+ }
119
+ set wireframe(b) {
120
+ this._wireframe = !!b;
121
+ }
122
+ /** Normal map of the terrain */ get normalMap() {
123
+ return this._quadtree.normalMap;
124
+ }
125
+ /** @internal */ get overridenStateSet() {
126
+ return this._overridenStateSet;
127
+ }
128
+ /**
129
+ * Creates the terrain
130
+ *
131
+ * @param sizeX - Terrain size in X axis
132
+ * @param sizeZ - Terrain size in Z axis
133
+ * @param elevations - Elevation data of the terrain
134
+ * @param scale - Scale of the terrain
135
+ * @param patchSize - Patch size of the terrain
136
+ * @returns true if succeeded
137
+ */ create(sizeX, sizeZ, elevations, scale, patchSize, options) {
138
+ this._quadtree = new Quadtree(this);
139
+ if (options?.splatMap && options.splatMap.format !== 'rgba8unorm') {
140
+ throw new Error('SplatMap must be rgba8unorm format');
141
+ }
142
+ this._material = new TerrainMaterial(options);
143
+ if (!this._quadtree.build(patchSize, sizeX, sizeZ, elevations, scale.x, scale.y, scale.z, 24)) {
144
+ this._quadtree = null;
145
+ return false;
146
+ }
147
+ this._patchSize = patchSize;
148
+ this._heightFieldScale.set(scale);
149
+ this._width = sizeX;
150
+ this._height = sizeZ;
151
+ this._material.lightModel.setNormalMap(this._quadtree.normalMap, null, -1);
152
+ this._material.terrainInfo = new Vector4(this.scaledWidth, this.scaledHeight, 0, 0);
153
+ this._overridenStateSet = Application.instance.device.createRenderStateSet();
154
+ this._overridenStateSet.useRasterizerState().setCullMode('front');
155
+ this.invalidateBoundingVolume();
156
+ // create grass layers
157
+ if (options?.splatMap && options?.detailMaps?.grass) {
158
+ if (options.detailMaps.grass.findIndex((a)=>a && a.findIndex((b)=>!!b) >= 0) >= 0) {
159
+ const splatMap = options.splatMap;
160
+ const data = new Uint8Array(splatMap.width * splatMap.height * 4);
161
+ splatMap.readPixels(0, 0, splatMap.width, splatMap.height, 0, 0, data).then(()=>{
162
+ for(let detail = 0; detail < 4; detail++){
163
+ if (options.detailMaps.grass[detail]) {
164
+ for (const grass of options.detailMaps.grass[detail]){
165
+ if (grass) {
166
+ const d = grass.density ?? 1;
167
+ const bladeWidth = grass.bladeWidth ?? 4;
168
+ const bladeHeight = grass.bladeHeigh ?? 2;
169
+ const offset = grass.offset ?? 0;
170
+ const grassTexture = grass.texture ?? null;
171
+ const density = [];
172
+ for(let i = 0; i < splatMap.height; i++){
173
+ const row = [];
174
+ for(let j = 0; j < splatMap.width; j++){
175
+ const val = data[i * 4 * splatMap.width + j * 4 + detail] / 255;
176
+ row.push(val * d);
177
+ }
178
+ density.push(row);
179
+ }
180
+ this.createGrass(density, bladeWidth, bladeHeight, offset, grassTexture);
181
+ }
182
+ }
183
+ }
184
+ }
185
+ });
186
+ }
187
+ }
188
+ return true;
189
+ }
190
+ /**
191
+ * Create grass fields
192
+ * @param maxGrassPerCell - Maximum number of grasses in a cell (world space 1x1)
193
+ * @param density - The density map
194
+ */ createGrass(density, bladeWidth, bladeHeight, offset, grassTexture) {
195
+ if (!this._grassManager) {
196
+ this._grassManager = new GrassManager(64, density);
197
+ }
198
+ if (!this._grassMaterial) {
199
+ this._grassMaterial = new GrassMaterial(new Vector2(this.scaledWidth, this.scaledHeight), this._quadtree.normalMap);
200
+ }
201
+ this._grassMaterial = new GrassMaterial(new Vector2(this.scaledWidth, this.scaledHeight), this._quadtree.normalMap, grassTexture);
202
+ this._grassMaterial.stateSet.useRasterizerState().setCullMode('none');
203
+ this._grassManager.addGrassLayer(Application.instance.device, this, density, bladeWidth, bladeHeight, offset, grassTexture);
204
+ }
205
+ /** Get elevation at specified position in terrain coordinate space */ getElevation(x, z) {
206
+ return this._quadtree.getHeightField().getRealHeight(x, z);
207
+ }
208
+ /** Get normal at specified position in terrain coordinate space */ getNormal(x, z, normal) {
209
+ return this._quadtree.getHeightField().getRealNormal(x, z, normal);
210
+ }
211
+ /** Get intersection distance by a ray in terrain coordinate space */ rayIntersect(ray) {
212
+ return this._quadtree.getHeightField().rayIntersect(ray);
213
+ }
214
+ /**
215
+ * {@inheritDoc SceneNode.computeBoundingVolume}
216
+ * @override
217
+ */ computeBoundingVolume(bv) {
218
+ return this._quadtree ? this._quadtree.getHeightField().getBBoxTree().getRootNode().bbox : null;
219
+ }
220
+ /**
221
+ * Traverse quadtree node top down
222
+ * @param callback - the callback function
223
+ */ traverseQuadtree(callback) {
224
+ function visitQuadtreeNode_r(node) {
225
+ callback(node);
226
+ for(let i = 0; i < 4; i++){
227
+ const child = node.getChild(i);
228
+ if (child) {
229
+ visitQuadtreeNode_r(child);
230
+ }
231
+ }
232
+ }
233
+ const rootNode = this._quadtree.rootNode;
234
+ if (rootNode) {
235
+ visitQuadtreeNode_r(this._quadtree.rootNode);
236
+ }
237
+ }
238
+ /** @internal */ cull(cullVisitor) {
239
+ const tanHalfFovy = cullVisitor.primaryCamera.getTanHalfFovy();
240
+ if (tanHalfFovy !== this._lastTanHalfFOVY || this._maxPixelErrorDirty) {
241
+ this._maxPixelErrorDirty = false;
242
+ this._lastTanHalfFOVY = tanHalfFovy;
243
+ this._quadtree.setupCamera(1024, tanHalfFovy, this._maxPixelError);
244
+ }
245
+ const worldEyePos = cullVisitor.primaryCamera.getWorldPosition();
246
+ this._viewPoint = this.invWorldMatrix.transformPointAffine(worldEyePos);
247
+ return this._quadtree.cull(cullVisitor, this._viewPoint, this.worldMatrix);
248
+ }
249
+ /**
250
+ * {@inheritDoc SceneNode.isTerrain}
251
+ * @override
252
+ */ isTerrain() {
253
+ return true;
254
+ }
255
+ }
256
+
257
+ export { Terrain };
258
+ //# sourceMappingURL=terrain.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"terrain.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}