@woosh/meep-engine 2.43.17 → 2.43.19

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 (68) hide show
  1. package/core/assert.js +3 -1
  2. package/core/bvh2/aabb3/aabb3_intersects_ray.js +14 -9
  3. package/core/bvh2/aabb3/aabb3_intersects_ray_branchless.js +52 -0
  4. package/core/bvh2/bvh3/ExplicitBinaryBoundingVolumeHierarchy.d.ts +2 -0
  5. package/core/bvh2/bvh3/ExplicitBinaryBoundingVolumeHierarchy.js +162 -10
  6. package/core/bvh2/bvh3/ebvh_build_for_geometry_incremental.js +34 -0
  7. package/core/bvh2/bvh3/ebvh_build_for_geometry_morton.js +175 -0
  8. package/core/bvh2/bvh3/ebvh_sort_for_traversal_depth_first.js +122 -0
  9. package/core/bvh2/bvh3/{bvh_collect_user_data.js → query/bvh_collect_user_data.js} +1 -1
  10. package/core/bvh2/bvh3/{bvh_query_leaves_generic.js → query/bvh_query_leaves_generic.js} +1 -1
  11. package/core/bvh2/bvh3/query/bvh_query_leaves_ray.js +97 -0
  12. package/core/bvh2/bvh3/{bvh_query_user_data_generic.js → query/bvh_query_user_data_generic.js} +1 -1
  13. package/core/bvh2/bvh3/{bvh_query_user_data_nearest_to_point.js → query/bvh_query_user_data_nearest_to_point.js} +3 -3
  14. package/core/bvh2/bvh3/{bvh_query_user_data_nearest_to_point.spec.js → query/bvh_query_user_data_nearest_to_point.spec.js} +1 -1
  15. package/core/bvh2/bvh3/{bvh_query_user_data_overlaps_frustum.js → query/bvh_query_user_data_overlaps_frustum.js} +2 -2
  16. package/core/bvh2/bvh3/query/compute_tight_near_far_clipping_planes.js +1 -1
  17. package/core/collection/array/arrayQuickSort.js +1 -1
  18. package/core/collection/array/typed/typed_array_copy.js +2 -2
  19. package/core/geom/3d/aabb/compute_aabb_from_points.js +4 -3
  20. package/core/geom/3d/compute_triangle_normal.js +76 -0
  21. package/core/geom/3d/topology/samples/sampleFloodFill.js +1 -1
  22. package/core/geom/3d/topology/simplify/compute_face_normal_change_dot_product.js +1 -1
  23. package/core/geom/3d/topology/simplify/quadratic/Quadratic3.js +1 -1
  24. package/core/geom/3d/topology/struct/TopoTriangle.js +1 -57
  25. package/core/geom/3d/topology/tm_face_normal.js +1 -1
  26. package/core/geom/3d/topology/tm_vertex_compute_normal.js +1 -1
  27. package/core/geom/3d/triangle/computeTriangleRayIntersection.js +195 -27
  28. package/core/geom/Vector3.js +12 -12
  29. package/core/math/physics/brdf/D_GGX.js +13 -0
  30. package/editor/tools/v2/prototypeTransformControls.js +14 -2
  31. package/engine/ecs/parent/EntityNode.js +80 -7
  32. package/engine/ecs/parent/EntityNodeFlags.js +8 -0
  33. package/engine/graphics/ecs/mesh-v2/ShadedGeometrySystem.js +2 -2
  34. package/engine/graphics/ecs/mesh-v2/aggregate/SGMesh.js +9 -1
  35. package/engine/graphics/ecs/mesh-v2/render/ShadedGeometryRendererContext.js +1 -1
  36. package/engine/graphics/geometry/AttributeSpec.js +18 -3
  37. package/engine/graphics/geometry/VertexDataSpec.js +53 -3
  38. package/engine/graphics/micron/format/VirtualGeometry.js +7 -0
  39. package/engine/graphics/micron/render/VirtualGeometryBuilder.js +1 -1
  40. package/engine/graphics/micron/render/refinement/get_geometry_patch_cut.js +5 -2
  41. package/engine/graphics/particles/particular/engine/parameter/sample/RGBA_LUT_HEATMAP_IR.js +11 -0
  42. package/engine/graphics/particles/particular/engine/utils/volume/prototypeParticleVolume.js +2 -9
  43. package/engine/graphics/render/forward_plus/model/DirectionalLight.js +40 -0
  44. package/engine/graphics/sh3/README.md +1 -0
  45. package/engine/graphics/sh3/path_tracer/GeometryBVHBatched.js +265 -0
  46. package/engine/graphics/sh3/path_tracer/PathTracedMesh.js +85 -0
  47. package/engine/graphics/sh3/path_tracer/PathTracer.js +534 -0
  48. package/engine/graphics/sh3/path_tracer/apply_texture_clamping_to_coordinate.js +22 -0
  49. package/engine/graphics/sh3/path_tracer/compute_triangle_group_aabb3.js +36 -0
  50. package/engine/graphics/sh3/path_tracer/getBiasedNormalSample.js +55 -0
  51. package/engine/graphics/sh3/path_tracer/make_one_vector3.js +7 -0
  52. package/engine/graphics/sh3/path_tracer/make_sky_hosek.js +44 -0
  53. package/engine/graphics/sh3/path_tracer/make_sky_rtiw.js +17 -0
  54. package/engine/graphics/sh3/path_tracer/make_zero_vector3.js +7 -0
  55. package/engine/graphics/sh3/path_tracer/prototypePathTracer.js +631 -0
  56. package/engine/graphics/sh3/path_tracer/random_in_hemisphere.js +39 -0
  57. package/engine/graphics/sh3/path_tracer/ray_hit_apply_transform.js +42 -0
  58. package/engine/graphics/sh3/path_tracer/ray_reflect.js +27 -0
  59. package/engine/graphics/sh3/path_tracer/sample_triangle_attribute.js +35 -0
  60. package/engine/graphics/sh3/path_tracer/vec3_uint8_to_float.js +12 -0
  61. package/engine/graphics/sh3/sky/hosek/README.md +4 -0
  62. package/engine/graphics/sh3/sky/hosek/prototype_hosek.js +71 -0
  63. package/engine/graphics/sh3/sky/hosek/sky_hosek_compute_irradiance_by_direction.js +4171 -0
  64. package/engine/graphics/texture/sampler/convertTexture2Sampler2D.js +2 -0
  65. package/package.json +1 -1
  66. package/view/elements/progress/SmoothProgressBar.js +1 -1
  67. package/view/task/TaskProgressView.js +6 -8
  68. package/core/bvh2/bvh3/bvh_query_leaves_ray.js +0 -95
@@ -0,0 +1,534 @@
1
+ import {
2
+ ExplicitBinaryBoundingVolumeHierarchy
3
+ } from "../../../../core/bvh2/bvh3/ExplicitBinaryBoundingVolumeHierarchy.js";
4
+ import { bvh_query_leaves_ray } from "../../../../core/bvh2/bvh3/query/bvh_query_leaves_ray.js";
5
+ import { aabb3_matrix4_project } from "../../../../core/geom/3d/aabb/aabb3_matrix4_project.js";
6
+ import { array_copy } from "../../../../core/collection/array/copyArray.js";
7
+ import { mat4, vec3 } from "gl-matrix";
8
+ import { ensureGeometryBoundingBox } from "../../util/ensureGeometryBoundingBox.js";
9
+ import { GeometryBVHBatched } from "./GeometryBVHBatched.js";
10
+ import { PathTracedMesh } from "./PathTracedMesh.js";
11
+ import { TextureAttachmentsByMaterialType } from "../../../asset/loaders/material/TextureAttachmensByMaterialType.js";
12
+ import { convertTexture2Sampler2D } from "../../texture/sampler/convertTexture2Sampler2D.js";
13
+ import {
14
+ LinearEncoding,
15
+ LinearFilter,
16
+ LinearMipmapLinearFilter,
17
+ NearestFilter,
18
+ NearestMipMapLinearFilter,
19
+ sRGBEncoding,
20
+ UnsignedByteType
21
+ } from "three";
22
+ import { linear_to_sRGB } from "../../../../core/color/linear_to_sRGB.js";
23
+ import { random_in_hemisphere } from "./random_in_hemisphere.js";
24
+ import { sample_triangle_attribute } from "./sample_triangle_attribute.js";
25
+ import { vec3_uint8_to_float } from "./vec3_uint8_to_float.js";
26
+ import { make_sky_rtiw } from "./make_sky_rtiw.js";
27
+ import { apply_texture_clamping_to_coordinate } from "./apply_texture_clamping_to_coordinate.js";
28
+ import { ebvh_sort_for_traversal_depth_first } from "../../../../core/bvh2/bvh3/ebvh_sort_for_traversal_depth_first.js";
29
+ import { clamp01 } from "../../../../core/math/clamp01.js";
30
+ import { v3_dot } from "../../../../core/geom/v3_dot.js";
31
+ import { LightType } from "../../ecs/light/LightType.js";
32
+ import { make_zero_vector3 } from "./make_zero_vector3.js";
33
+ import { make_one_vector3 } from "./make_one_vector3.js";
34
+
35
+ /*
36
+ Ray hit data layout:
37
+ position: v3
38
+ normal: v3
39
+ t: float
40
+ u: float
41
+ v: float
42
+ primitive_index: uint
43
+ instance_id: uint
44
+ */
45
+
46
+ const temp_ray_results = [];
47
+
48
+ const texture_uv = [0, 0];
49
+ const color = [];
50
+
51
+ const irradiance = [0, 0, 0];
52
+ const trace_result = [];
53
+
54
+ const _ray = [];
55
+
56
+ const tmp_0 = [];
57
+ const tmp_1 = [];
58
+
59
+
60
+ export class PathTracer {
61
+ constructor() {
62
+
63
+ /**
64
+ *
65
+ * @type {ExplicitBinaryBoundingVolumeHierarchy}
66
+ */
67
+ this.bvh_top_level = new ExplicitBinaryBoundingVolumeHierarchy();
68
+
69
+ /**
70
+ *
71
+ * @type {Map<number, PathTracedMesh>}
72
+ */
73
+ this.meshes = new Map();
74
+
75
+ /**
76
+ *
77
+ * @type {AbstractLight[]}
78
+ */
79
+ this.__lights = [];
80
+
81
+ /**
82
+ *
83
+ * @type {Map<THREE.BufferGeometry, GeometryBVHBatched>}
84
+ */
85
+ this.geo_cache = new Map();
86
+
87
+ /**
88
+ *
89
+ * @type {Map<THREE.Texture, Sampler2D>}
90
+ */
91
+ this.textures = new Map();
92
+
93
+ /**
94
+ *
95
+ * @type {function}
96
+ * @private
97
+ */
98
+ this.__background_sampler = make_one_vector3();
99
+ }
100
+
101
+ /**
102
+ *
103
+ * @param {THREE.BufferGeometry} geo
104
+ * @return {GeometryBVHBatched}
105
+ */
106
+ obtainGeometryBVH(geo) {
107
+ const cached = this.geo_cache.get(geo);
108
+
109
+ if (cached !== undefined) {
110
+ return cached;
111
+ }
112
+
113
+ const bvh8 = new GeometryBVHBatched();
114
+
115
+ bvh8.build(geo);
116
+
117
+ this.geo_cache.set(geo, bvh8);
118
+
119
+ return bvh8;
120
+ }
121
+
122
+ optimize() {
123
+ this.bvh_top_level.trim();
124
+ ebvh_sort_for_traversal_depth_first(this.bvh_top_level);
125
+ }
126
+
127
+ async build() {
128
+ for (const [id, mesh] of this.meshes) {
129
+ const material = mesh.material;
130
+
131
+ //patch textures
132
+ const materialType = material.type;
133
+
134
+ /**
135
+ *
136
+ * @type {TextureAttachment[]}
137
+ */
138
+ const attachments = TextureAttachmentsByMaterialType[materialType];
139
+
140
+ if (attachments === undefined) {
141
+ // unsupported
142
+ continue;
143
+ }
144
+
145
+ for (let i = 0; i < attachments.length; i++) {
146
+ const attachment = attachments[i];
147
+
148
+ const texture = attachment.read(material);
149
+
150
+ if (texture === undefined || texture === null) {
151
+ continue;
152
+ }
153
+
154
+ if (this.textures.has(texture.id)) {
155
+ continue;
156
+ }
157
+
158
+ const sampler2D = convertTexture2Sampler2D(texture, undefined, undefined, false);
159
+
160
+ this.textures.set(texture.id, sampler2D);
161
+ }
162
+ }
163
+ }
164
+
165
+ /**
166
+ *
167
+ * @param {THREE.Light} light
168
+ */
169
+ addLight(light) {
170
+ this.__lights.push(light);
171
+ }
172
+
173
+ /**
174
+ *
175
+ * @param {THREE.BufferGeometry} geo
176
+ * @param {THREE.Material} material
177
+ * @param {mat4|number[]} transform
178
+ */
179
+ addMesh(geo, material, transform) {
180
+ //meshopt_optimizeVertexCache(geo.getIndex().array, geo.getIndex().array, geo.getIndex().array.length, geo.getAttribute('position').count);
181
+
182
+ const mesh = new PathTracedMesh();
183
+ mesh.geometry = geo;
184
+ mesh.material = material;
185
+ mesh.transform = transform;
186
+
187
+
188
+ mesh.geo_bvh = this.obtainGeometryBVH(geo);
189
+
190
+ const bvh = this.bvh_top_level;
191
+
192
+ const bvh_node_id = bvh.allocate_node();
193
+
194
+ mesh.__bvh_node_id = bvh_node_id;
195
+
196
+ ensureGeometryBoundingBox(geo);
197
+
198
+ const aabb = [
199
+ geo.boundingBox.min.x,
200
+ geo.boundingBox.min.y,
201
+ geo.boundingBox.min.z,
202
+ geo.boundingBox.max.x,
203
+ geo.boundingBox.max.y,
204
+ geo.boundingBox.max.z,
205
+ ];
206
+
207
+ const aabb_t = [];
208
+
209
+ aabb3_matrix4_project(aabb_t, aabb, transform);
210
+
211
+ bvh.node_set_aabb(
212
+ bvh_node_id,
213
+ aabb_t
214
+ );
215
+ bvh.node_set_user_data(
216
+ bvh_node_id,
217
+ mesh.id
218
+ );
219
+ bvh.insert_leaf(bvh_node_id);
220
+
221
+ this.meshes.set(mesh.id, mesh);
222
+ }
223
+
224
+ /**
225
+ *
226
+ * @param {number[]} out
227
+ * @param {Texture} texture
228
+ * @param {number} u
229
+ * @param {number} v
230
+ */
231
+ sample_texture(out, texture, u, v) {
232
+ if (texture === undefined || texture == null) {
233
+ return;
234
+ }
235
+
236
+ const sampler = this.textures.get(texture.id);
237
+
238
+ if (sampler === undefined) {
239
+ return;
240
+ }
241
+
242
+ // TODO apply texture matrix transform
243
+
244
+ const _u = apply_texture_clamping_to_coordinate(texture.wrapS, u);
245
+ const _v = apply_texture_clamping_to_coordinate(texture.wrapT, v);
246
+
247
+ const magFilter = texture.magFilter;
248
+
249
+ switch (magFilter) {
250
+ default:
251
+ case NearestFilter:
252
+ case NearestMipMapLinearFilter:
253
+ sampler.sampleNearestUV(_u, _v, out);
254
+ break;
255
+
256
+ case LinearFilter:
257
+ case LinearMipmapLinearFilter:
258
+
259
+ sampler.sampleBicubicUV(_u, _v, out);
260
+ break;
261
+ }
262
+
263
+ if (texture.type === UnsignedByteType) {
264
+ vec3_uint8_to_float(out, out);
265
+ }
266
+
267
+ const texture_encoding = texture.encoding;
268
+
269
+ if (texture_encoding === LinearEncoding) {
270
+ // nothing
271
+ } else if (texture_encoding === sRGBEncoding) {
272
+ // convert value to sRGB
273
+ linear_to_sRGB(out, 0, out, 0);
274
+ }
275
+
276
+ }
277
+
278
+ /**
279
+ *
280
+ * @param {number[]} out
281
+ * @param {number[]} hit
282
+ */
283
+ sample_material(out, hit) {
284
+ const primitive_id = hit[9];
285
+ const instance_id = hit[10];
286
+ const u = hit[7];
287
+ const v = hit[8];
288
+
289
+ out[0] = 1;
290
+ out[1] = 1;
291
+ out[2] = 1;
292
+
293
+ /**
294
+ *
295
+ * @type {PathTracedMesh}
296
+ */
297
+ const mesh = this.meshes.get(instance_id);
298
+
299
+ if (mesh === undefined) {
300
+ // instance not found
301
+ return;
302
+ }
303
+
304
+ const geometry = mesh.geometry;
305
+ const index_attribute = geometry.getIndex();
306
+ const index_array = index_attribute.array;
307
+
308
+
309
+ // sample surface normal
310
+ const index_offset = primitive_id * 3;
311
+
312
+ const index_0 = index_array[index_offset];
313
+ const index_1 = index_array[index_offset + 1];
314
+ const index_2 = index_array[index_offset + 2];
315
+
316
+ const normal_attribute = geometry.getAttribute('normal');
317
+
318
+ if (normal_attribute !== undefined) {
319
+ // vertex normals are present, use those
320
+ const normal_array = normal_attribute.array;
321
+
322
+ sample_triangle_attribute(out, 3, index_0, index_1, index_2, normal_array, 3, u, v);
323
+ } else {
324
+ // copy hit normal
325
+ array_copy(hit, 3, out, 3, 3);
326
+ }
327
+
328
+
329
+ const uv_attribute = geometry.getAttribute('uv');
330
+
331
+ if (uv_attribute !== undefined) {
332
+ const uv_array = uv_attribute.array;
333
+ sample_triangle_attribute(texture_uv, 0, index_0, index_1, index_2, uv_array, 2, u, v);
334
+ } else {
335
+ // default texture uv
336
+ texture_uv[0] = 0;
337
+ texture_uv[1] = 0;
338
+ }
339
+
340
+ const material = mesh.material;
341
+
342
+ if (material.isMeshStandardMaterial) {
343
+ out[0] *= material.color.r;
344
+ out[1] *= material.color.g;
345
+ out[2] *= material.color.b;
346
+
347
+ const diffuse_map = material.map;
348
+
349
+ if (diffuse_map !== null) {
350
+ this.sample_texture(color, diffuse_map, texture_uv[0], texture_uv[1]);
351
+
352
+ vec3.multiply(out, out, color);
353
+ }
354
+
355
+ } else {
356
+ // unsupported
357
+ }
358
+
359
+ }
360
+
361
+ /**
362
+ *
363
+ * @param {number[]} out
364
+ * @param {number[]} ray
365
+ * @param {number} min_distance
366
+ * @param {number} max_distance
367
+ * @return {number}
368
+ */
369
+ trace(out, ray, min_distance, max_distance) {
370
+ const bvh = this.bvh_top_level;
371
+ const hit_count = bvh_query_leaves_ray(bvh, temp_ray_results, 0, ray[0], ray[1], ray[2], ray[3], ray[4], ray[5]);
372
+
373
+ let nearest_hit_distance = max_distance;
374
+ let nearest_mesh = null;
375
+
376
+
377
+ for (let i = 0; i < hit_count; i++) {
378
+ const node_id = temp_ray_results[i];
379
+
380
+ const node_user_data = bvh.node_get_user_data(node_id);
381
+
382
+ const mesh = this.meshes.get(node_user_data);
383
+
384
+ const distance_to_hit = mesh.hit(tmp_0, ray, min_distance, nearest_hit_distance);
385
+
386
+ if (distance_to_hit >= 0) {
387
+ // since raycast in leaf nodes is already bound by maximum distance, any hit we get is necessarily a closer hit than before
388
+ nearest_hit_distance = distance_to_hit;
389
+ nearest_mesh = mesh;
390
+
391
+ array_copy(tmp_0, 0, out, 0, 11);
392
+ }
393
+ }
394
+
395
+ if (nearest_hit_distance !== max_distance) {
396
+
397
+ // out[6] = nearest_mesh;
398
+
399
+ return nearest_hit_distance;
400
+ }
401
+
402
+ return -1;
403
+ }
404
+
405
+ /**
406
+ *
407
+ * @param {number[]} out
408
+ * @param {number} out_offset
409
+ * @param {number[]} direction
410
+ * @param {number} direction_offset
411
+ */
412
+ sample_background(out, out_offset, direction, direction_offset) {
413
+ this.__background_sampler(out, out_offset, direction, direction_offset);
414
+ }
415
+
416
+ /**
417
+ *
418
+ * @param {number[]} out
419
+ * @param {number} out_offset
420
+ * @param {number[]} hit_info
421
+ * @param {number} hit_info_address
422
+ */
423
+ sample_lights(out, out_offset, hit_info, hit_info_address) {
424
+ let lights_sampled = false;
425
+
426
+ const lights = this.__lights;
427
+ const light_count = lights.length;
428
+
429
+ for (let i = 0; i < 3; i++) {
430
+ out[out_offset + i] = 0;
431
+ }
432
+
433
+ for (let i = 0; i < light_count; i++) {
434
+ const light = lights[i];
435
+
436
+ if (light.isDirectionalLight === true) {
437
+ const dir = light.direction;
438
+
439
+ // check if there are any obstacles in the direction of the light
440
+ if (this.trace([], [
441
+ hit_info[hit_info_address + 0],
442
+ hit_info[hit_info_address + 1],
443
+ hit_info[hit_info_address + 2],
444
+ -dir.x, -dir.y, -dir.z
445
+ ], 0.0001, Infinity) > 0) {
446
+ // light is occluded
447
+ break;
448
+ }
449
+
450
+ const dotNL = clamp01(v3_dot(hit_info[hit_info_address + 3], hit_info[hit_info_address + 4], hit_info[hit_info_address + 5], -dir.x, -dir.y, -dir.z));
451
+
452
+ for (let j = 0; j < 3; j++) {
453
+ // irradiance
454
+ out[out_offset + j] += dotNL * light.color[j] * light.intensity.getValue();
455
+ }
456
+ }
457
+ }
458
+
459
+ return lights_sampled;
460
+ }
461
+
462
+ path_trace(out, ray, max_distance, max_bounce, random = Math.random) {
463
+ //TODO add importance sampling, see https://raytracing.github.io/books/RayTracingTheRestOfYourLife.html#lightscattering/thescatteringpdf
464
+
465
+ array_copy(ray, 0, _ray, 0, 6);
466
+
467
+ irradiance[0] = 0;
468
+ irradiance[1] = 0;
469
+ irradiance[2] = 0;
470
+
471
+ let got_emission = false;
472
+
473
+ let i;
474
+ for (i = 0; i < max_bounce; i++) {
475
+
476
+ const hit_distance = this.trace(trace_result, _ray, 0.0001, max_distance);
477
+
478
+ if (hit_distance < 0) {
479
+ // ray didn't hit anything
480
+
481
+ // sample "environment" and terminate path as there is nothing to reflect off of
482
+
483
+ this.sample_background(tmp_0, 0, _ray, 3);
484
+
485
+ vec3.multiply(irradiance, irradiance, tmp_0);
486
+
487
+ got_emission = true;
488
+
489
+ break;
490
+ }
491
+
492
+
493
+ // irradiance[0] += m;
494
+ // irradiance[1] += m;
495
+ // irradiance[2] += m;
496
+ this.sample_material(tmp_0, trace_result);
497
+
498
+ // adjust normal on the hit
499
+ // array_copy(tmp_0, 3, trace_result, 3, 3);
500
+
501
+ // sample light
502
+ this.sample_lights(tmp_1, 0, trace_result, 0);
503
+ vec3.add(irradiance, irradiance, tmp_1);
504
+
505
+ // reflect ray
506
+ array_copy(trace_result, 0, _ray, 0, 3);
507
+
508
+ // getBiasedNormalSample(_ray, 3, tmp, 3, 1, random);
509
+ random_in_hemisphere(random, _ray, 3, tmp_0, 3);
510
+
511
+ vec3.multiply(irradiance, irradiance, tmp_0);
512
+ }
513
+
514
+ if (got_emission === false) {
515
+ // no light source was hit, propagate shadow(darkness) along the ray
516
+ irradiance[0] = 0;
517
+ irradiance[1] = 0;
518
+ irradiance[2] = 0;
519
+ }
520
+
521
+ array_copy(irradiance, 0, out, 0, 3);
522
+ }
523
+ }
524
+
525
+ /**
526
+ *
527
+ * @param {number} material
528
+ * @param {number} hit
529
+ */
530
+ function compute_hit_reaction_standard_material(material, hit) {
531
+
532
+ }
533
+
534
+
@@ -0,0 +1,22 @@
1
+ import { ClampToEdgeWrapping, RepeatWrapping } from "three";
2
+ import { clamp01 } from "../../../../core/math/clamp01.js";
3
+
4
+ /**
5
+ *
6
+ * @param {number} clamping
7
+ * @param {number} v
8
+ * @return {number}
9
+ */
10
+ export function apply_texture_clamping_to_coordinate(clamping, v) {
11
+
12
+ switch (clamping) {
13
+ case RepeatWrapping:
14
+ return v % 1;
15
+ case ClampToEdgeWrapping:
16
+ return clamp01(v);
17
+ default:
18
+ // unsupported?
19
+ return v;
20
+ }
21
+
22
+ }
@@ -0,0 +1,36 @@
1
+ import { min2 } from "../../../../core/math/min2.js";
2
+ import { max2 } from "../../../../core/math/max2.js";
3
+
4
+ /**
5
+ * Compute AABB for a group of triangles based on index-buffer
6
+ * @param {number[]|Float32Array} output
7
+ * @param {number} output_offset
8
+ * @param {number[]|ArrayLike<number>} indices
9
+ * @param {number[]|ArrayLike<number>} positions
10
+ * @param {number} index_offset
11
+ * @param {number} index_count
12
+ * @param {number} d
13
+ */
14
+ export function compute_triangle_group_aabb3(output, output_offset, indices, positions, index_offset, index_count, d) {
15
+
16
+ let i = 0;
17
+ for (; i < d; i++) {
18
+ output[output_offset + i] = Infinity;
19
+ output[output_offset + i + d] = -Infinity;
20
+ }
21
+
22
+ for (i = 0; i < index_count; i++) {
23
+ for (let j = 0; j < 3; j++) {
24
+ const index = indices[index_offset + i * 3 + j];
25
+
26
+ const i3 = index * d;
27
+
28
+ for (let k = 0; k < d; k++) {
29
+ const v = positions[i3 + k];
30
+
31
+ output[output_offset + k] = min2(output[output_offset + k], v);
32
+ output[output_offset + k + d] = max2(output[output_offset + k + d], v);
33
+ }
34
+ }
35
+ }
36
+ }
@@ -0,0 +1,55 @@
1
+ /**
2
+ * @see 2003 "Global Illumination Compendium" by Philip Dutré (equation 36)
3
+ * @see http://blog.hvidtfeldts.net/index.php/2015/01/path-tracing-3d-fractals/
4
+ * @param {number[]} out
5
+ * @param {number} out_offset
6
+ * @param {number[]} normal
7
+ * @param {number} normal_offset
8
+ * @param {number} power
9
+ * @param {function():number} random
10
+ */
11
+ function getBiasedNormalSample(out, out_offset, normal, normal_offset, power, random) {
12
+ const dir_x = normal[normal_offset];
13
+ const dir_y = normal[normal_offset + 1];
14
+ const dir_z = normal[normal_offset + 2];
15
+
16
+ // we build orthonormal vectors with respect to the direction vector
17
+ let o1_x, o1_y, o1_z;
18
+
19
+ if (Math.abs(dir_x) > Math.abs(dir_z)) {
20
+ o1_x = -dir_y;
21
+ o1_y = dir_x;
22
+ o1_z = 0;
23
+ } else {
24
+ o1_x = 0;
25
+ o1_y = -dir_z;
26
+ o1_z = dir_y;
27
+ }
28
+
29
+ // normalize orthonormal vector
30
+ const o1_norm = 1 / Math.hypot(o1_x, o1_y, o1_z);
31
+
32
+ o1_x *= o1_norm;
33
+ o1_y *= o1_norm;
34
+ o1_z *= o1_norm;
35
+
36
+ const o2_x = dir_y * o1_z - dir_z * o1_y;
37
+ const o2_y = dir_z * o1_x - dir_x * o1_z;
38
+ const o2_z = dir_x * o1_y - dir_y * o1_x;
39
+
40
+ // we can skip normalizing second orthonormal vector, as it will be guaranteed to be unit length already
41
+ // for explanation, see https://math.stackexchange.com/questions/23259/is-the-cross-product-of-two-unit-vectors-itself-a-unit-vector#:~:text=If%20you%20know%20that%20the,(a%20length%20of%20one).
42
+
43
+ const r_x = (random()) * 2 * Math.PI;
44
+ const r_y = Math.pow((random()), 1 / (power + 1));
45
+
46
+ const oneminus = Math.sqrt(1.0 - r_y * r_y);
47
+
48
+ const k0 = Math.cos(r_x) * oneminus;
49
+ const k1 = Math.sin(r_x) * oneminus;
50
+
51
+
52
+ out[out_offset] = k0 * o1_x + k1 * o2_x + r_y * dir_x;
53
+ out[out_offset + 1] = k0 * o1_y + k1 * o2_y + r_y * dir_y;
54
+ out[out_offset + 2] = k0 * o1_z + k1 * o2_z + r_y * dir_z;
55
+ }
@@ -0,0 +1,7 @@
1
+ export function make_one_vector3() {
2
+ return (out, out_offset, direction, direction_offset) => {
3
+ out[out_offset] = 1;
4
+ out[out_offset + 1] = 1;
5
+ out[out_offset + 2] = 1;
6
+ }
7
+ }
@@ -0,0 +1,44 @@
1
+ import {
2
+ sky_hosek_compute_irradiance_by_direction,
3
+ sky_hosek_precompute
4
+ } from "../sky/hosek/sky_hosek_compute_irradiance_by_direction.js";
5
+ import { vec3 } from "gl-matrix";
6
+ import { array_copy } from "../../../../core/collection/array/copyArray.js";
7
+ import { max2 } from "../../../../core/math/max2.js";
8
+
9
+ export function make_sky_hosek(sun = [0, 1, 0], turbidity = 1, overcast = 0, albedo = [0, 0, 0]) {
10
+ const coeffs = new Float32Array(27);
11
+ const rad = new Float32Array([1, 1, 1]);
12
+ const sun_position = new Float32Array([sun[0], sun[2], sun[1]]);
13
+
14
+ vec3.normalize(sun_position, sun_position);
15
+
16
+ sky_hosek_precompute(coeffs, rad, sun_position, turbidity, albedo, overcast);
17
+
18
+ const v3 = [];
19
+
20
+ const value_scale = 8e-5;
21
+
22
+ return (result, result_offset, direction, direction_offset) => {
23
+ const d_x = direction[direction_offset];
24
+ const d_z = direction[direction_offset + 2];
25
+ const d_y = direction[direction_offset + 1];
26
+
27
+ sky_hosek_compute_irradiance_by_direction(
28
+ v3, coeffs, rad, sun_position,
29
+ d_z ,
30
+ d_x ,
31
+ d_y ,
32
+ );
33
+
34
+ vec3.scale(v3, v3, value_scale);
35
+
36
+ // clamp sky contribution
37
+ v3[0] = max2(0, v3[0]);
38
+ v3[1] = max2(0, v3[1]);
39
+ v3[2] = max2(0, v3[2]);
40
+
41
+
42
+ array_copy(v3, 0, result, result_offset, 3);
43
+ }
44
+ }