@woosh/meep-engine 2.43.16 → 2.43.18

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 (53) 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 +214 -5
  6. package/core/bvh2/bvh3/bvh_query_leaves_ray.js +32 -29
  7. package/core/collection/array/typed/typed_array_copy.js +2 -2
  8. package/core/geom/3d/aabb/compute_aabb_from_points.js +4 -3
  9. package/core/geom/3d/compute_triangle_normal.js +76 -0
  10. package/core/geom/3d/topology/samples/sampleFloodFill.js +1 -1
  11. package/core/geom/3d/topology/simplify/compute_face_normal_change_dot_product.js +1 -1
  12. package/core/geom/3d/topology/simplify/quadratic/Quadratic3.js +1 -1
  13. package/core/geom/3d/topology/struct/TopoTriangle.js +1 -57
  14. package/core/geom/3d/topology/tm_face_normal.js +1 -1
  15. package/core/geom/3d/topology/tm_vertex_compute_normal.js +1 -1
  16. package/core/geom/3d/triangle/computeTriangleRayIntersection.js +195 -27
  17. package/core/geom/Vector3.js +12 -12
  18. package/core/math/physics/brdf/D_GGX.js +13 -0
  19. package/editor/tools/v2/prototypeTransformControls.js +14 -2
  20. package/engine/ecs/parent/EntityNode.js +80 -7
  21. package/engine/ecs/parent/EntityNodeFlags.js +8 -0
  22. package/engine/ecs/terrain/tiles/TerrainTile.js +7 -9
  23. package/engine/graphics/ecs/path/PathDisplaySystem.d.ts +3 -0
  24. package/engine/graphics/ecs/path/PathDisplaySystem.js +10 -0
  25. package/engine/graphics/geometry/AttributeSpec.js +18 -3
  26. package/engine/graphics/geometry/VertexDataSpec.js +53 -3
  27. package/engine/graphics/micron/format/VirtualGeometry.js +7 -0
  28. package/engine/graphics/micron/render/VirtualGeometryBuilder.js +1 -1
  29. package/engine/graphics/micron/render/refinement/get_geometry_patch_cut.js +5 -2
  30. package/engine/graphics/particles/particular/engine/parameter/sample/RGBA_LUT_HEATMAP_IR.js +11 -0
  31. package/engine/graphics/particles/particular/engine/utils/volume/prototypeParticleVolume.js +2 -9
  32. package/engine/graphics/sh3/README.md +1 -0
  33. package/engine/graphics/sh3/path_tracer/GeometryBVHBatched.js +324 -0
  34. package/engine/graphics/sh3/path_tracer/PathTracedMesh.js +85 -0
  35. package/engine/graphics/sh3/path_tracer/PathTracer.js +469 -0
  36. package/engine/graphics/sh3/path_tracer/apply_texture_clamping_to_coordinate.js +22 -0
  37. package/engine/graphics/sh3/path_tracer/compute_triangle_group_aabb3.js +36 -0
  38. package/engine/graphics/sh3/path_tracer/getBiasedNormalSample.js +55 -0
  39. package/engine/graphics/sh3/path_tracer/make_sky_hosek.js +44 -0
  40. package/engine/graphics/sh3/path_tracer/make_sky_rtiw.js +15 -0
  41. package/engine/graphics/sh3/path_tracer/prototypePathTracer.js +619 -0
  42. package/engine/graphics/sh3/path_tracer/random_in_hemisphere.js +39 -0
  43. package/engine/graphics/sh3/path_tracer/ray_hit_apply_transform.js +42 -0
  44. package/engine/graphics/sh3/path_tracer/ray_reflect.js +27 -0
  45. package/engine/graphics/sh3/path_tracer/sample_triangle_attribute.js +35 -0
  46. package/engine/graphics/sh3/path_tracer/vec3_uint8_to_float.js +12 -0
  47. package/engine/graphics/sh3/sky/hosek/README.md +4 -0
  48. package/engine/graphics/sh3/sky/hosek/prototype_hosek.js +71 -0
  49. package/engine/graphics/sh3/sky/hosek/sky_hosek_compute_irradiance_by_direction.js +4171 -0
  50. package/engine/graphics/texture/sampler/convertTexture2Sampler2D.js +2 -0
  51. package/package.json +1 -1
  52. package/view/elements/progress/SmoothProgressBar.js +1 -1
  53. package/view/task/TaskProgressView.js +6 -8
@@ -0,0 +1,619 @@
1
+ import EmptyView from "../../../../view/elements/EmptyView.js";
2
+ import { CanvasView } from "../../../../view/elements/CanvasView.js";
3
+ import { PathTracer } from "./PathTracer.js";
4
+ import {
5
+ Box3,
6
+ MeshStandardMaterial,
7
+ OctahedronBufferGeometry,
8
+ PerspectiveCamera,
9
+ PlaneBufferGeometry,
10
+ Sphere,
11
+ SphereBufferGeometry
12
+ } from "three";
13
+ import { Sampler2D } from "../../texture/sampler/Sampler2D.js";
14
+ import sampler2D2Canvas from "../../texture/sampler/Sampler2D2Canvas.js";
15
+ import { ray3_array_compose } from "../../../../core/geom/3d/ray/ray3_array_compose.js";
16
+ import { Transform } from "../../../ecs/transform/Transform.js";
17
+ import { prettyPrint } from "../../../../core/NumberFormat.js";
18
+ import { float2uint8 } from "../../../../core/binary/float2uint8.js";
19
+ import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader.js";
20
+ import { Camera } from "../../ecs/camera/Camera.js";
21
+ import Vector3 from "../../../../core/geom/Vector3.js";
22
+ import { scaleSampler2D } from "../../texture/sampler/scaleSampler2D.js";
23
+ import { seededRandom } from "../../../../core/math/random/seededRandom.js";
24
+ import { mat4 } from "gl-matrix";
25
+ import { v3_distance } from "../../../../core/geom/v3_distance.js";
26
+ import ConcurrentExecutor from "../../../../core/process/executor/ConcurrentExecutor.js";
27
+ import Task from "../../../../core/process/task/Task.js";
28
+ import { TaskSignal } from "../../../../core/process/task/TaskSignal.js";
29
+ import Quaternion from "../../../../core/geom/Quaternion.js";
30
+ import TaskProgressView from "../../../../view/task/TaskProgressView.js";
31
+ import { Localization } from "../../../../core/Localization.js";
32
+
33
+ import '../../../../../../../css/game.scss';
34
+ import { MouseEvents } from "../../../input/devices/events/MouseEvents.js";
35
+ import { PLYLoader } from "three/examples/jsm/loaders/PLYLoader.js";
36
+ import { noop } from "../../../../core/function/Functions.js";
37
+ import { Color } from "../../../../core/color/Color.js";
38
+ import { min2 } from "../../../../core/math/min2.js";
39
+ import { makeGeometryIndexed } from "../../geometry/buffered/makeGeometryIndexed.js";
40
+
41
+ document.body.style.margin = 0;
42
+ document.body.style.overflow = "hidden";
43
+
44
+ const vContainer = new EmptyView();
45
+
46
+
47
+ const vCanvas = new CanvasView();
48
+ vContainer.addChild(vCanvas);
49
+
50
+
51
+ vContainer.size.set(window.innerWidth, window.innerHeight);
52
+ // vCanvas.size.set(512, 512);
53
+ // vCanvas.size.set(1080, 1080);
54
+ vCanvas.size.copy(vContainer.size);
55
+
56
+ vContainer.css({
57
+ position: 'absolute',
58
+ top: 0,
59
+ left: 0,
60
+ overflowY: "visible",
61
+ overflowX: "visible"
62
+
63
+ });
64
+ vCanvas.css({
65
+ overflow: "visible"
66
+ });
67
+
68
+ const pt = new PathTracer();
69
+
70
+ const gltfLoader = new GLTFLoader();
71
+ // const gltf_url = 'data/models/LowPolyTownshipSet/Town_Hall/model.gltf';
72
+ // const gltf_url = 'data/models/road_bike/road_bike.gltf';
73
+ // const gltf_url = 'data/models/sponza-pbr/gltf/sponza.glb';
74
+ // const gltf_url = 'data/models/sibenik/model.gltf';
75
+ // gltfLoader.load(gltf_url, (gltf) => {
76
+ //
77
+ // gltf.scene.updateMatrixWorld();
78
+ //
79
+ // gltf.scene.traverse(m => {
80
+ //
81
+ // if (m.isMesh) {
82
+ //
83
+ // pt.addMesh(
84
+ // m.geometry,
85
+ // m.material,
86
+ // m.matrixWorld.elements
87
+ // );
88
+ //
89
+ // }
90
+ // });
91
+ //
92
+ // trace_image(vCanvas);
93
+ // });
94
+
95
+ //
96
+ // pt.addMesh(
97
+ // new TorusKnotBufferGeometry(1, 0.3, 128, 128),
98
+ // new MeshStandardMaterial({ color: '#FF0000' }),
99
+ // Transform.fromJSON({
100
+ // scale: 0.5
101
+ // }).matrix
102
+ // );
103
+ //
104
+
105
+
106
+ function prepare_scene_sphere_01(pt, camera) {
107
+ camera.position.set(0, 0, -2.1);
108
+ camera.lookAt(0, 0, 0);
109
+ pt.addMesh(
110
+ new SphereBufferGeometry(1, 16, 16),
111
+ new MeshStandardMaterial({ color: '#FF0000' }),
112
+ Transform.fromJSON({
113
+ scale: 0.5
114
+ }).matrix
115
+ );
116
+
117
+ const ground_material = new MeshStandardMaterial({
118
+ color: '#00FF00'
119
+ });
120
+ pt.addMesh(new PlaneBufferGeometry(), ground_material, Transform.fromJSON({
121
+ position: new Vector3(0, -0.5, 0),
122
+ scale: 50,
123
+ rotation: Quaternion.fromEulerAngles(-Math.PI / 2, 0, 0)
124
+ }).matrix);
125
+ }
126
+
127
+ function promise_ply(url) {
128
+
129
+ return new Promise((resolve, reject) => {
130
+ new PLYLoader().load(url, resolve, noop, reject);
131
+ });
132
+ }
133
+
134
+ /**
135
+ *
136
+ * @param {string} url
137
+ * @return {Promise<GLTF>}
138
+ */
139
+ function promise_gltf(url) {
140
+ return new Promise((resolve, reject) => {
141
+ new GLTFLoader().load(url, resolve, noop, reject);
142
+ });
143
+ }
144
+
145
+ /**
146
+ *
147
+ * @param {PathTracer} pt
148
+ * @param {THREE.Camera} camera
149
+ * @param {string} url
150
+ * @return {Promise<void>}
151
+ */
152
+ async function prepare_scene_gltf(pt, camera, url) {
153
+ const gltf = await promise_gltf(url);
154
+
155
+
156
+ const ground_material = new MeshStandardMaterial({
157
+ color: '#ffceae'
158
+ });
159
+ pt.addMesh(new PlaneBufferGeometry(), ground_material, Transform.fromJSON({
160
+ position: new Vector3(0, 0, 0),
161
+ scale: 5000,
162
+ rotation: Quaternion.fromEulerAngles(-Math.PI / 2, 0, 0)
163
+ }).matrix);
164
+
165
+
166
+ gltf.scene.updateMatrixWorld();
167
+
168
+ gltf.scene.traverse((m) => {
169
+ if (m.isMesh) {
170
+ pt.addMesh(m.geometry, m.material, m.matrixWorld.elements);
171
+ }
172
+ });
173
+
174
+ const box3 = new Box3();
175
+ box3.setFromObject(gltf.scene);
176
+
177
+ const sphere = new Sphere();
178
+
179
+ box3.getBoundingSphere(sphere);
180
+
181
+ camera.position.set(1, 1.3, 1)
182
+ .normalize()
183
+ .multiplyScalar(sphere.radius * 2.6);
184
+
185
+ camera.lookAt(sphere.center);
186
+ }
187
+
188
+ /**
189
+ *
190
+ * @param {PathTracer} pt
191
+ * @param {THREE.Camera} camera
192
+ * @return {Promise<void>}
193
+ */
194
+ async function prepare_scene_lucy(pt, camera) {
195
+
196
+
197
+ const lucy_geom = await promise_ply("data/models/stanford/Lucy100k.ply")
198
+
199
+ const lucy_material = new MeshStandardMaterial({
200
+ color: '#95c9ff'
201
+ });
202
+ pt.addMesh(lucy_geom, lucy_material, Transform.fromJSON({
203
+ position: new Vector3(0, 0, 0),
204
+ scale: 1,
205
+ // rotation: Quaternion.fromEulerAngles(-Math.PI / 2, 0, 0)
206
+ }).matrix);
207
+
208
+ const ground_material = new MeshStandardMaterial({
209
+ color: '#ffceae'
210
+ });
211
+ pt.addMesh(new PlaneBufferGeometry(), ground_material, Transform.fromJSON({
212
+ position: new Vector3(0, -800, 0),
213
+ scale: 5000,
214
+ rotation: Quaternion.fromEulerAngles(-Math.PI / 2, 0, 0)
215
+ }).matrix);
216
+
217
+
218
+ camera.position.set(0, 0, -2300);
219
+ camera.lookAt(0, 0, 0);
220
+ }
221
+
222
+ /**
223
+ * Ray-Tracing In One Weekend test scene
224
+ * @param {PathTracer} pt
225
+ * @param {THREE.Camera} camera
226
+ */
227
+ async function prepare_scene_rtiow(pt, camera) {
228
+ const random = seededRandom();
229
+
230
+ const sphere_geo = new OctahedronBufferGeometry(1, 8);
231
+ makeGeometryIndexed(sphere_geo);
232
+
233
+ const ground_material = new MeshStandardMaterial({
234
+ color: '#ffffff'
235
+ });
236
+ pt.addMesh(new PlaneBufferGeometry(), ground_material, Transform.fromJSON({
237
+ position: new Vector3(0, 0, 0),
238
+ scale: 50,
239
+ rotation: Quaternion.fromEulerAngles(-Math.PI / 2, 0, 0)
240
+ }).matrix);
241
+
242
+ const spheres = [];
243
+
244
+ function overlaps_existing(x, y, z, r) {
245
+ for (let i = 0; i < spheres.length; i++) {
246
+ const sphere = spheres[i];
247
+
248
+ const r_1 = sphere[3];
249
+
250
+ if (v3_distance(sphere[0], sphere[1], sphere[2], x, y, z) < r_1 + r) {
251
+ return true;
252
+ }
253
+ }
254
+
255
+ return false;
256
+ }
257
+
258
+ function add_sphere(center, radius, material) {
259
+
260
+ const mat = new Float32Array(16);
261
+ mat4.fromRotationTranslationScale(mat, [0, 0, 0, 1], center, [radius, radius, radius])
262
+
263
+ pt.addMesh(sphere_geo, material, mat);
264
+
265
+ spheres.push([
266
+ center[0],
267
+ center[1],
268
+ center[2],
269
+ radius
270
+ ]);
271
+ }
272
+
273
+ const material1 = new MeshStandardMaterial({
274
+ color: '#FFFFFF'
275
+ });
276
+ add_sphere([0, 1, 0], 1, material1);
277
+
278
+ const material2 = new MeshStandardMaterial({
279
+ color: '#FFFFFF'
280
+ });
281
+ add_sphere([-4, 1, 0], 1, material2);
282
+
283
+ const material3 = new MeshStandardMaterial({
284
+ color: '#FFFFFF'
285
+ });
286
+ add_sphere([4, 1, 0], 1, material3);
287
+
288
+
289
+ for (let a = -11; a < 11; a++) {
290
+ for (let b = -11; b < 11; b++) {
291
+
292
+ const choose_mat = random();
293
+
294
+ const radius = 0.2;
295
+
296
+ const center = [0, radius, 0];
297
+
298
+ do {
299
+ center[0] = a + 0.9 * random();
300
+ center[2] = b + 0.9 * random();
301
+ } while (overlaps_existing(center[0], center[1], center[2], radius));
302
+
303
+
304
+ const mat = new Float32Array(16);
305
+ mat4.fromRotationTranslationScale(mat, [0, 0, 0, 1], center, [radius, radius, radius])
306
+
307
+ const material = new MeshStandardMaterial({
308
+ color: Color.fromHSV(random(), 0.9, 0.9).toHex()
309
+ });
310
+
311
+ if (choose_mat < 0.8) {
312
+
313
+ // diffuse
314
+ add_sphere(center, radius, material);
315
+ } else if (choose_mat < 0.95) {
316
+ // metal
317
+ add_sphere(center, radius, material);
318
+ } else {
319
+ // glass
320
+ add_sphere(center, radius, material);
321
+ }
322
+
323
+
324
+ }
325
+ }
326
+
327
+
328
+ camera.position.set(13, 2, 3);
329
+ camera.lookAt(0, 0, 0);
330
+
331
+ }
332
+
333
+ /**
334
+ *
335
+ * @param {Sampler2D} target
336
+ * @param {PathTracer} pt
337
+ * @param {Camera} camera
338
+ * @param {{current:number, total:number}} progress
339
+ */
340
+ function* render(target, pt, camera, progress = { current: 0, total: 0 }) {
341
+ const random = seededRandom(0);
342
+
343
+ // update camera
344
+ camera.updateProjectionMatrix();
345
+ camera.updateMatrixWorld(true);
346
+
347
+ //update world inverse matrix
348
+ camera.matrixWorldInverse.copy(camera.matrixWorld);
349
+ camera.matrixWorldInverse.invert();
350
+
351
+ const out = [];
352
+ const out2 = [];
353
+ const ray = [];
354
+
355
+ const ray_origin = new Vector3();
356
+ const ray_direction = new Vector3();
357
+
358
+ const width = target.width;
359
+ const height = target.height;
360
+
361
+
362
+ const t0 = performance.now();
363
+ let sample_count = 0;
364
+
365
+ const pixel_scale_x = 1 / (width - 1);
366
+ const pixel_scale_y = 1 / (height - 1);
367
+
368
+ const pixel_sample_count = 500;
369
+
370
+ progress.total = width * height;
371
+
372
+ const max_y = height - 1;
373
+
374
+ const RENDER_TILE_SIZE = 8;
375
+
376
+ const tile_count_x = width / RENDER_TILE_SIZE;
377
+ const tile_count_y = height / RENDER_TILE_SIZE;
378
+
379
+ // render in square tiles to get better memory exploitation, as rays are more likely to hit same things in smaller region
380
+ for (let tile_y = 0; tile_y < tile_count_y; tile_y++) {
381
+
382
+ const y0 = tile_y * RENDER_TILE_SIZE;
383
+ const y1 = min2(height, y0 + RENDER_TILE_SIZE);
384
+
385
+ for (let tile_x = 0; tile_x < tile_count_x; tile_x++) {
386
+
387
+ const x0 = tile_x * RENDER_TILE_SIZE;
388
+ const x1 = min2(width, x0 + RENDER_TILE_SIZE);
389
+
390
+ for (let y = y0; y < y1; y++) {
391
+
392
+ const v = y * pixel_scale_y;
393
+ const _y = v * 2 - 1;
394
+
395
+ for (let x = x0; x < x1; x++) {
396
+
397
+ const u = x * pixel_scale_x;
398
+ const _x = u * 2 - 1;
399
+
400
+ out2[0] = 0;
401
+ out2[1] = 0;
402
+ out2[2] = 0;
403
+
404
+ for (let i = 0; i < pixel_sample_count; i++) {
405
+
406
+ Camera.projectRay(camera,
407
+ _x + random() * pixel_scale_x * 2,
408
+ _y + random() * pixel_scale_y * 2,
409
+ ray_origin, ray_direction
410
+ );
411
+
412
+ ray3_array_compose(
413
+ ray,
414
+ ray_origin.x, ray_origin.y, ray_origin.z,
415
+ ray_direction.x, ray_direction.y, ray_direction.z
416
+ );
417
+
418
+ pt.path_trace(out, ray, Infinity, 4, random);
419
+
420
+ out2[0] += out[0];
421
+ out2[1] += out[1];
422
+ out2[2] += out[2];
423
+
424
+
425
+ sample_count++;
426
+ }
427
+
428
+ const pixel_address = ((max_y - y) * width + x) * 4;
429
+
430
+ target.data[pixel_address] = float2uint8(out2[0] / pixel_sample_count);
431
+ target.data[pixel_address + 1] = float2uint8(out2[1] / pixel_sample_count);
432
+ target.data[pixel_address + 2] = float2uint8(out2[2] / pixel_sample_count);
433
+ target.data[pixel_address + 3] = 255;
434
+
435
+ progress.current++;
436
+
437
+ yield;
438
+ }
439
+ }
440
+ }
441
+
442
+ }
443
+
444
+
445
+ // console.profile('trace');
446
+
447
+
448
+ // console.profileEnd('trace');
449
+
450
+ const total_time = performance.now() - t0;
451
+
452
+ console.log(`Trace finished in ${prettyPrint(total_time)}ms. Total of ${prettyPrint(sample_count)} samples, ${prettyPrint(sample_count * 1000 / total_time)} samples per second`);
453
+
454
+ }
455
+
456
+ const executor = new ConcurrentExecutor(1, 15);
457
+
458
+ const camera = new PerspectiveCamera();
459
+
460
+ /**
461
+ *
462
+ * @param {THREE.Camera} camera
463
+ * @return {Promise<void>}
464
+ */
465
+ async function start_renderer(camera) {
466
+ camera.aspect = vCanvas.size.x / vCanvas.size.y;
467
+
468
+ const path = 'data/models/LowPolyTownshipSet/Small_house/Small_house.gltf';
469
+
470
+ // await prepare_scene_lucy(pt, camera);
471
+ await prepare_scene_rtiow(pt, camera);
472
+ // await prepare_scene_sphere_01(pt, camera);
473
+ // await prepare_scene_gltf(pt, camera, path);
474
+
475
+ pt.build();
476
+ pt.optimize();
477
+
478
+ const pixelRatio = 1;
479
+
480
+ const rt = Sampler2D.uint8(4, vCanvas.size.x, vCanvas.size.y);
481
+
482
+ let scaled_rt = rt;
483
+ if (pixelRatio !== 1) {
484
+ scaled_rt = Sampler2D.uint8clamped(4, Math.ceil(rt.width * pixelRatio), Math.ceil(rt.height * pixelRatio));
485
+ }
486
+
487
+ function make_render_task() {
488
+ const progress_v = { current: 0, total: 0 };
489
+
490
+ const it = render(scaled_rt, pt, camera, progress_v);
491
+
492
+ return new Task({
493
+ name: 'render',
494
+ cycleFunction() {
495
+ const next = it.next();
496
+
497
+ if (!next.done) {
498
+ return TaskSignal.Continue;
499
+ } else {
500
+ return TaskSignal.EndSuccess;
501
+ }
502
+
503
+ },
504
+ computeProgress() {
505
+ return progress_v.total > 0 ? progress_v.current / progress_v.total : 0;
506
+ }
507
+ })
508
+ }
509
+
510
+
511
+ const loc = new Localization();
512
+
513
+ async function update() {
514
+
515
+
516
+ const t = make_render_task();
517
+
518
+
519
+ const vProgress = new TaskProgressView({
520
+ task: t,
521
+ localization: loc
522
+ });
523
+
524
+ vProgress.size.set(vContainer.size.x, vContainer.size.y);
525
+ vProgress.css({
526
+ position: 'absolute',
527
+ top: 0,
528
+ left: 0,
529
+ pointerEvents: "none"
530
+ });
531
+ vProgress.el.querySelector('.fill').style.background = 'rgb(255,220,94)';
532
+ vProgress.el.querySelector('.progress-bar').style.height = '8px';
533
+
534
+ vContainer.addChild(vProgress);
535
+
536
+ const p_done = t.promise();
537
+
538
+ executor.run(t);
539
+
540
+ await p_done;
541
+
542
+ vContainer.removeChild(vProgress);
543
+
544
+ if (scaled_rt !== rt) {
545
+ scaleSampler2D(scaled_rt, rt);
546
+ }
547
+
548
+ sampler2D2Canvas(rt, 1, 0, vCanvas.el);
549
+
550
+ requestAnimationFrame(update);
551
+ }
552
+
553
+ update();
554
+ }
555
+
556
+ start_renderer(camera);
557
+
558
+ vCanvas.el.addEventListener(MouseEvents.Click, (evt) => {
559
+ const ray = [];
560
+
561
+ const ray_origin = new Vector3();
562
+ const ray_direction = new Vector3();
563
+
564
+ const pointer = getPointer(vCanvas.el, evt);
565
+
566
+ const _x = pointer.x;
567
+ const _y = pointer.y;
568
+
569
+ Camera.projectRay(camera,
570
+ _x,
571
+ _y,
572
+ ray_origin, ray_direction
573
+ );
574
+
575
+ ray3_array_compose(
576
+ ray,
577
+ ray_origin.x, ray_origin.y, ray_origin.z,
578
+ ray_direction.x, ray_direction.y, ray_direction.z
579
+ );
580
+
581
+ const out = [];
582
+
583
+ pt.trace(out, ray, 0, Infinity);
584
+
585
+ console.log(pointer, out, ray, pt);
586
+
587
+ });
588
+
589
+ vCanvas.css({
590
+ pointerEvents: 'auto'
591
+ });
592
+
593
+ function getPointer(domElement, event) {
594
+
595
+ if (domElement.ownerDocument.pointerLockElement) {
596
+
597
+ return {
598
+ x: 0,
599
+ y: 0,
600
+ button: event.button
601
+ };
602
+
603
+ } else {
604
+
605
+ const rect = domElement.getBoundingClientRect();
606
+
607
+ return {
608
+ x: (event.clientX - rect.left) / rect.width * 2 - 1,
609
+ y: -(event.clientY - rect.top) / rect.height * 2 + 1,
610
+ button: event.button
611
+ };
612
+
613
+ }
614
+
615
+ }
616
+
617
+ // link
618
+ document.body.appendChild(vContainer.el);
619
+ vContainer.link();
@@ -0,0 +1,39 @@
1
+ import { v3_dot } from "../../../../core/geom/v3_dot.js";
2
+
3
+ /**
4
+ *
5
+ * @param {function} random
6
+ * @param {number[]} result
7
+ * @param {number} result_offset
8
+ * @param {number[]} normal
9
+ * @param {number} normal_offset
10
+ */
11
+ export function random_in_hemisphere(random, result, result_offset, normal, normal_offset) {
12
+ const nx = normal[normal_offset + 0];
13
+ const ny = normal[normal_offset + 1];
14
+ const nz = normal[normal_offset + 2];
15
+
16
+ const phi = Math.PI * 2 * random();
17
+
18
+ const cosTheta = random() * 2 - 1;
19
+
20
+ const theta = Math.acos(cosTheta);
21
+
22
+ const sinTheta = Math.sin(theta);
23
+
24
+ //compute coordinates
25
+ const x = sinTheta * Math.cos(phi);
26
+ const y = sinTheta * Math.sin(phi);
27
+ const z = cosTheta;
28
+
29
+ if (v3_dot(x, y, z, nx, ny, nz) < 0) {
30
+ result[result_offset] = -x;
31
+ result[result_offset + 1] = -y;
32
+ result[result_offset + 2] = -z;
33
+ } else {
34
+ //write the result
35
+ result[result_offset] = x;
36
+ result[result_offset + 1] = y;
37
+ result[result_offset + 2] = z;
38
+ }
39
+ }
@@ -0,0 +1,42 @@
1
+ import { v3_length } from "../../../../core/geom/v3_length.js";
2
+
3
+ /**
4
+ *
5
+ * @param {number[]} output
6
+ * @param {number[]} hit
7
+ * @param {number[]} m 4x4 matrix
8
+ */
9
+ export function ray_hit_apply_transform(output, hit, m) {
10
+
11
+ // transform position
12
+ const p_x = hit[0];
13
+ const p_y = hit[1];
14
+ const p_z = hit[2];
15
+
16
+ // compute perspective projection
17
+ const w = 1 / (m[3] * p_x + m[7] * p_y + m[11] * p_z + m[15]);
18
+
19
+ const result_p_x = (m[0] * p_x + m[4] * p_y + m[8] * p_z + m[12]) * w;
20
+ const result_p_y = (m[1] * p_x + m[5] * p_y + m[9] * p_z + m[13]) * w;
21
+ const result_p_z = (m[2] * p_x + m[6] * p_y + m[10] * p_z + m[14]) * w;
22
+
23
+ output[0] = result_p_x;
24
+ output[1] = result_p_y;
25
+ output[2] = result_p_z;
26
+
27
+ // transform normal
28
+ const n_x = hit[3];
29
+ const n_y = hit[4];
30
+ const n_z = hit[5];
31
+
32
+ const result_n_x = m[0] * n_x + m[4] * n_y + m[8] * n_z;
33
+ const result_n_y = m[1] * n_x + m[5] * n_y + m[9] * n_z;
34
+ const result_n_z = m[2] * n_x + m[6] * n_y + m[10] * n_z;
35
+
36
+ const normal_multiplier = 1 / v3_length(result_n_x, result_n_y, result_n_z);
37
+
38
+ output[3] = result_n_x * normal_multiplier;
39
+ output[4] = result_n_y * normal_multiplier;
40
+ output[5] = result_n_z * normal_multiplier;
41
+
42
+ }