@woosh/meep-engine 2.138.1 → 2.138.3

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.
package/package.json CHANGED
@@ -5,7 +5,7 @@
5
5
  "description": "Pure JavaScript game engine. Fully featured and production ready.",
6
6
  "type": "module",
7
7
  "author": "Alexander Goldring",
8
- "version": "2.138.1",
8
+ "version": "2.138.3",
9
9
  "main": "build/meep.module.js",
10
10
  "module": "build/meep.module.js",
11
11
  "exports": {
@@ -1,3 +1,12 @@
1
+ export type WorkerStatus = string;
2
+ /**
3
+ * @enum {string}
4
+ */
5
+ export const WorkerStatus: Readonly<{
6
+ STOPPED: "STOPPED";
7
+ RUNNING: "RUNNING";
8
+ FAILED: "FAILED";
9
+ }>;
1
10
  export default WorkerProxy;
2
11
  declare class WorkerProxy {
3
12
  /**
@@ -12,7 +21,17 @@ declare class WorkerProxy {
12
21
  * @private
13
22
  */
14
23
  private __pending;
15
- __isRunning: boolean;
24
+ /**
25
+ * @type {WorkerStatus}
26
+ * @private
27
+ */
28
+ private __status;
29
+ /**
30
+ * Error captured when the worker transitions to FAILED. Used to reject any subsequent requests.
31
+ * @type {Error|null}
32
+ * @private
33
+ */
34
+ private __failure;
16
35
  /**
17
36
  *
18
37
  * @type {Worker|null}
@@ -60,9 +79,14 @@ declare class WorkerProxy {
60
79
  */
61
80
  private __handleMessage;
62
81
  isRunning(): boolean;
82
+ /**
83
+ * @returns {WorkerStatus}
84
+ */
85
+ getStatus(): WorkerStatus;
63
86
  /**
64
87
  * Stop the worker.
65
88
  * If the worker is not running, this method does nothing.
89
+ * A worker in FAILED state cannot be stopped (it has already been terminated).
66
90
  */
67
91
  stop(): void;
68
92
  /**
@@ -81,5 +105,19 @@ declare class WorkerProxy {
81
105
  *
82
106
  */
83
107
  start(): void;
108
+ /**
109
+ * Worker-level error handler. Fires when the worker script itself fails
110
+ * (e.g. importScripts 404) or when an uncaught error escapes a handler.
111
+ * Treats the worker as permanently failed: rejects all pending and future requests.
112
+ *
113
+ * @param {ErrorEvent} errorEvent
114
+ * @private
115
+ */
116
+ private __handleError;
117
+ /**
118
+ * @param {Error} err
119
+ * @private
120
+ */
121
+ private __failAllPending;
84
122
  }
85
123
  //# sourceMappingURL=WorkerProxy.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"WorkerProxy.d.ts","sourceRoot":"","sources":["../../../../../src/core/process/worker/WorkerProxy.js"],"names":[],"mappings":";AAsCA;IAiCI;;;;OAIG;IACH,iBAHW,MAAM,gBAQhB;IAzCD;;;;OAIG;IACH,kBAAe;IAEf,qBAAoB;IAEpB;;;;OAIG;IACH,iBAAgB;IAEhB;;;;OAIG;IACH,qBAAiB;IAEjB;;;;;OAKG;IACH,eAAiB;IAQb,YAAc;IACd,aAAsB;IAK1B;;;;;;OAMG;IACH,wBAJW,MAAM,2BAkDhB;IAED;;;;OAIG;IACH,qBAgBC;IAED;;;OAGG;IACH,sBAQC;IAED;;;;OAIG;IACH,wBAqCC;IAED,qBAEC;IAED;;;OAGG;IACH,aAOC;IAED;;;;;OAKG;IACH,kBAJW,MAAM,eACN,MAAM,GACJ,OAAO,CAkCnB;IAED,4BAmBC;IAED;;;;;;OAMG;IACH,cAoBC;CACJ"}
1
+ {"version":3,"file":"WorkerProxy.d.ts","sourceRoot":"","sources":["../../../../../src/core/process/worker/WorkerProxy.js"],"names":[],"mappings":"2BAIU,MAAM;AADhB;;GAEG;AACH;;;;GAIG;;AAqCH;IA4CI;;;;OAIG;IACH,iBAHW,MAAM,gBAQhB;IApDD;;;;OAIG;IACH,kBAAe;IAEf;;;OAGG;IACH,iBAAgC;IAEhC;;;;OAIG;IACH,kBAAiB;IAEjB;;;;OAIG;IACH,iBAAgB;IAEhB;;;;OAIG;IACH,qBAAiB;IAEjB;;;;;OAKG;IACH,eAAiB;IAQb,YAAc;IACd,aAAsB;IAK1B;;;;;;OAMG;IACH,wBAJW,MAAM,2BAsDhB;IAED;;;;OAIG;IACH,qBAgBC;IAED;;;OAGG;IACH,sBAQC;IAED;;;;OAIG;IACH,wBAqCC;IAED,qBAEC;IAED;;OAEG;IACH,aAFa,YAAY,CAIxB;IAED;;;;OAIG;IACH,aAMC;IAED;;;;;OAKG;IACH,kBAJW,MAAM,eACN,MAAM,GACJ,OAAO,CAkCnB;IAED,4BAmBC;IAED;;;;;;OAMG;IACH,cAqBC;IAED;;;;;;;OAOG;IACH,sBAmBC;IAED;;;OAGG;IACH,yBAaC;CACJ"}
@@ -1,6 +1,15 @@
1
1
  import { assert } from "../../assert.js";
2
2
  import { array_remove_first } from "../../collection/array/array_remove_first.js";
3
3
 
4
+ /**
5
+ * @enum {string}
6
+ */
7
+ export const WorkerStatus = Object.freeze({
8
+ STOPPED: 'STOPPED',
9
+ RUNNING: 'RUNNING',
10
+ FAILED: 'FAILED',
11
+ });
12
+
4
13
  /**
5
14
  *
6
15
  * @param {Worker} worker
@@ -45,7 +54,18 @@ class WorkerProxy {
45
54
  */
46
55
  __pending = {};
47
56
 
48
- __isRunning = false;
57
+ /**
58
+ * @type {WorkerStatus}
59
+ * @private
60
+ */
61
+ __status = WorkerStatus.STOPPED;
62
+
63
+ /**
64
+ * Error captured when the worker transitions to FAILED. Used to reject any subsequent requests.
65
+ * @type {Error|null}
66
+ * @private
67
+ */
68
+ __failure = null;
49
69
 
50
70
  /**
51
71
  *
@@ -91,6 +111,10 @@ class WorkerProxy {
91
111
  $submitRequest(name, args) {
92
112
  assert.isString(name, "name");
93
113
 
114
+ if (this.__status === WorkerStatus.FAILED) {
115
+ return Promise.reject(this.__failure);
116
+ }
117
+
94
118
  const pending = this.__pending[name];
95
119
 
96
120
  const argumentCount = args.length;
@@ -218,20 +242,27 @@ class WorkerProxy {
218
242
  }
219
243
 
220
244
  isRunning() {
221
- return this.__isRunning;
245
+ return this.__status === WorkerStatus.RUNNING;
246
+ }
247
+
248
+ /**
249
+ * @returns {WorkerStatus}
250
+ */
251
+ getStatus() {
252
+ return this.__status;
222
253
  }
223
254
 
224
255
  /**
225
256
  * Stop the worker.
226
257
  * If the worker is not running, this method does nothing.
258
+ * A worker in FAILED state cannot be stopped (it has already been terminated).
227
259
  */
228
260
  stop() {
229
- if (!this.__isRunning) {
230
- //not running
261
+ if (this.__status !== WorkerStatus.RUNNING) {
231
262
  return;
232
263
  }
233
264
  this.__worker.terminate();
234
- this.__isRunning = false;
265
+ this.__status = WorkerStatus.STOPPED;
235
266
  }
236
267
 
237
268
  /**
@@ -258,7 +289,7 @@ class WorkerProxy {
258
289
  if (request.id === id) {
259
290
 
260
291
 
261
- if (!this.__isRunning) {
292
+ if (this.__status !== WorkerStatus.RUNNING) {
262
293
  // not running, simply cut from the queue
263
294
 
264
295
  requestQueue.splice(i, 1);
@@ -303,26 +334,75 @@ class WorkerProxy {
303
334
  *
304
335
  */
305
336
  start() {
306
- if (this.__isRunning) {
337
+ if (this.__status === WorkerStatus.RUNNING) {
307
338
  //already running
308
339
  return;
309
340
  }
310
341
 
342
+ if (this.__status === WorkerStatus.FAILED) {
343
+ // FAILED is terminal — refuse to restart. A re-spawn on the same URL would just hit the same load error.
344
+ return;
345
+ }
346
+
311
347
  this.__worker = new Worker(this.url,{
312
348
  name: this.__name
313
349
  });
314
350
 
315
351
  this.__worker.onmessage = this.__handleMessage;
352
+ this.__worker.onerror = this.__handleError;
316
353
 
317
- // TODO attach proper error handler
318
- this.__worker.onerror = (errorEvent) => {
319
- console.error('Worker error:', errorEvent);
320
- };
321
-
322
- this.__isRunning = true;
354
+ this.__status = WorkerStatus.RUNNING;
323
355
 
324
356
  this.sendPendingRequests();
325
357
  }
358
+
359
+ /**
360
+ * Worker-level error handler. Fires when the worker script itself fails
361
+ * (e.g. importScripts 404) or when an uncaught error escapes a handler.
362
+ * Treats the worker as permanently failed: rejects all pending and future requests.
363
+ *
364
+ * @param {ErrorEvent} errorEvent
365
+ * @private
366
+ */
367
+ __handleError = (errorEvent) => {
368
+ const message = errorEvent.message || 'unknown error';
369
+ const filename = errorEvent.filename || this.url;
370
+ const lineno = errorEvent.lineno || 0;
371
+
372
+ const err = new Error(`Worker '${this.__name}' failed: ${message} (${filename}:${lineno})`);
373
+
374
+ // Set status BEFORE rejecting requests so any synchronous resubmission in a rejection
375
+ // handler immediately fails fast instead of being queued against a dead worker.
376
+ this.__status = WorkerStatus.FAILED;
377
+ this.__failure = err;
378
+
379
+ if (this.__worker !== null) {
380
+ this.__worker.terminate();
381
+ }
382
+
383
+ this.__failAllPending(err);
384
+
385
+ console.error('Worker error:', errorEvent);
386
+ }
387
+
388
+ /**
389
+ * @param {Error} err
390
+ * @private
391
+ */
392
+ __failAllPending(err) {
393
+ for (const methodName in this.__pending) {
394
+ if (!this.__pending.hasOwnProperty(methodName)) {
395
+ continue;
396
+ }
397
+
398
+ const queue = this.__pending[methodName];
399
+ const requests = queue.splice(0, queue.length);
400
+
401
+ for (const request of requests) {
402
+ request.reject(err);
403
+ }
404
+ }
405
+ }
326
406
  }
327
407
 
328
408
  export default WorkerProxy;
@@ -1,5 +1,4 @@
1
1
  import { initializeEditor } from "../../../../../../../model/game/initializeEditor.js";
2
- import { OrbitingBehavior } from "../../../../../../../model/game/util/behavior/OrbitingBehavior.js";
3
2
  import { enableEditor } from "../../../../../../editor/enableEditor.js";
4
3
  import { AABB3 } from "../../../../../core/geom/3d/aabb/AABB3.js";
5
4
  import Quaternion from "../../../../../core/geom/Quaternion.js";
@@ -15,6 +14,7 @@ import { Transform } from "../../../../ecs/transform/Transform.js";
15
14
  import { EngineHarness } from "../../../../EngineHarness.js";
16
15
  import { BehaviorComponent } from "../../../../intelligence/behavior/ecs/BehaviorComponent.js";
17
16
  import { BehaviorSystem } from "../../../../intelligence/behavior/ecs/BehaviorSystem.js";
17
+ import { OrbitingBehavior } from "../../../../intelligence/behavior/ecs/OrbitingBehavior.js";
18
18
  import Highlight from "../../highlight/Highlight.js";
19
19
  import { ShadedGeometryHighlightSystem } from "../../highlight/system/ShadedGeometryHighlightSystem.js";
20
20
  import { MeshSystem } from "../../mesh/MeshSystem.js";
@@ -1,5 +1,4 @@
1
1
  import { BoxBufferGeometry, MeshStandardMaterial, OctahedronGeometry } from "three";
2
- import { OrbitingBehavior } from "../../../../../../../model/game/util/behavior/OrbitingBehavior.js";
3
2
  import { Color } from "../../../../../core/color/Color.js";
4
3
  import Quaternion from "../../../../../core/geom/Quaternion.js";
5
4
  import Vector2 from "../../../../../core/geom/Vector2.js";
@@ -19,6 +18,7 @@ import { SequenceBehavior } from "../../../../intelligence/behavior/composite/Se
19
18
  import { RepeatBehavior } from "../../../../intelligence/behavior/decorator/RepeatBehavior.js";
20
19
  import { BehaviorComponent } from "../../../../intelligence/behavior/ecs/BehaviorComponent.js";
21
20
  import { BehaviorSystem } from "../../../../intelligence/behavior/ecs/BehaviorSystem.js";
21
+ import { OrbitingBehavior } from "../../../../intelligence/behavior/ecs/OrbitingBehavior.js";
22
22
  import { ActionBehavior } from "../../../../intelligence/behavior/primitive/ActionBehavior.js";
23
23
  import { DelayBehavior } from "../../../../intelligence/behavior/util/DelayBehavior.js";
24
24
  import { ShadedGeometry } from "../ShadedGeometry.js";
@@ -5,7 +5,6 @@ import {
5
5
  OctahedronGeometry,
6
6
  SphereBufferGeometry
7
7
  } from "three";
8
- import { OrbitingBehavior } from "../../../../../../../model/game/util/behavior/OrbitingBehavior.js";
9
8
  import { SurfacePoint3 } from "../../../../../core/geom/3d/SurfacePoint3.js";
10
9
  import Quaternion from "../../../../../core/geom/Quaternion.js";
11
10
  import Vector2 from "../../../../../core/geom/Vector2.js";
@@ -24,6 +23,7 @@ import { Transform } from "../../../../ecs/transform/Transform.js";
24
23
  import { EngineHarness } from "../../../../EngineHarness.js";
25
24
  import { BehaviorComponent } from "../../../../intelligence/behavior/ecs/BehaviorComponent.js";
26
25
  import { BehaviorSystem } from "../../../../intelligence/behavior/ecs/BehaviorSystem.js";
26
+ import { OrbitingBehavior } from "../../../../intelligence/behavior/ecs/OrbitingBehavior.js";
27
27
  import {
28
28
  MicronShadedGeometryRenderAdapter
29
29
  } from "../../../micron/plugin/shaded_geometry/MicronShadedGeometryRenderAdapter.js";
@@ -1,4 +1,3 @@
1
- import { OrbitingBehavior } from "../../../../../../model/game/util/behavior/OrbitingBehavior.js";
2
1
  import { AABB3 } from "../../../../core/geom/3d/aabb/AABB3.js";
3
2
  import { make_justified_point_grid } from "../../../../core/geom/3d/util/make_justified_point_grid.js";
4
3
  import Vector3 from "../../../../core/geom/Vector3.js";
@@ -8,6 +7,7 @@ import { Transform } from "../../../ecs/transform/Transform.js";
8
7
  import { EngineHarness } from "../../../EngineHarness.js";
9
8
  import { BehaviorComponent } from "../../../intelligence/behavior/ecs/BehaviorComponent.js";
10
9
  import { BehaviorSystem } from "../../../intelligence/behavior/ecs/BehaviorSystem.js";
10
+ import { OrbitingBehavior } from "../../../intelligence/behavior/ecs/OrbitingBehavior.js";
11
11
  import Trail2D from "./Trail2D.js";
12
12
  import Trail2DSystem from "./Trail2DSystem.js";
13
13
 
@@ -54,10 +54,10 @@ async function main(engine) {
54
54
  const renderer = engine.graphics.renderer;
55
55
  baker.renderer = renderer;
56
56
 
57
- // const path = 'data/models/LowPolyTownshipSet/Small_house/Small_house.gltf';
57
+ const path = 'data/models/LowPolyTownshipSet/Small_house/Small_house.gltf';
58
58
  // const path = 'data/models/samples/textured_unit_cube.gltf';
59
59
  // const path = 'data/models/samples/teapot.gltf';
60
- const path = 'data/models/samples/just_a_girl/scene.gltf';
60
+ // const path = 'data/models/samples/just_a_girl/scene.gltf';
61
61
  // const path = 'data/models/road_bike/road_bike.gltf'; //large CAD-type model
62
62
  // const path = 'data/models/LowPolyTownshipSet/Barrel/model.gltf';
63
63
  // const path = 'data/models/LowPolyTownshipSet/Town_Hall/model.gltf';
@@ -96,7 +96,7 @@ async function main(engine) {
96
96
  offset: new Vector2(0, 0)
97
97
  }))
98
98
  .add(GUIElement.fromView(ctrl))
99
- .build(ecd);
99
+ // .build(ecd);
100
100
 
101
101
  // build out preview scene with impostor and the original
102
102
  const t0 = Transform.fromJSON({
@@ -1 +1 @@
1
- {"version":3,"file":"ImpostorShaderV0.d.ts","sourceRoot":"","sources":["../../../../../../../src/engine/graphics/impostors/octahedral/shader/ImpostorShaderV0.js"],"names":[],"mappings":"AA+SA;IACI,cA2DC;IAHG,4CAAyB;CAIhC;kCApWM,OAAO"}
1
+ {"version":3,"file":"ImpostorShaderV0.d.ts","sourceRoot":"","sources":["../../../../../../../src/engine/graphics/impostors/octahedral/shader/ImpostorShaderV0.js"],"names":[],"mappings":"AA+TA;IACI,cAmEC;IAHG,4CAAyB;CAIhC;kCA5XM,OAAO"}
@@ -13,291 +13,307 @@ import {
13
13
  * For ray projection using projection matrix : https://encreative.blogspot.com/2019/05/computing-ray-origin-and-direction-from.html
14
14
  */
15
15
  const shader_vx = `
16
-
16
+
17
17
  in vec2 uv;
18
18
  in vec3 position;
19
- in vec3 normal;
20
-
19
+
21
20
  out vec2 vUv;
22
- out vec4 plane0;
23
- out vec4 plane1;
24
- out vec4 plane2;
25
-
26
- out vec3 local_ray_near;
27
- out vec3 local_ray_far;
21
+ // View-space position of this vertex. Interpolated per-fragment for the
22
+ // tangent-space parallax computation in the fragment shader.
23
+ out vec3 vViewPos;
24
+
25
+ // Octahedral atlas frame indices and triangle interpolation weights are
26
+ // chosen once per-impostor and forwarded as flat varyings so every
27
+ // fragment samples the same 3 atlas frames (without this, a perspective
28
+ // camera could flip the chosen frame across the card and produce
29
+ // visible seams).
30
+ flat out vec2 vGridFloor;
31
+ flat out vec4 vWeights;
32
+
33
+ // Card's TBN basis in view space. The card is oriented so its NORMAL
34
+ // points along the weighted blend of the 3 nearest baked view
35
+ // directions, and TANGENT/BINORMAL line up with the bake camera's
36
+ // right/up at bake time — i.e. with the texture's X/Y axes. This is
37
+ // what makes the depth field (which is perpendicular to the bake
38
+ // direction) usable as a tangent-space height map. flat because the
39
+ // basis is the same for every vertex of the card.
40
+ flat out vec3 vTangent;
41
+ flat out vec3 vBinormal;
42
+ flat out vec3 vNormal;
28
43
 
29
44
  uniform mat4 modelViewMatrix;
30
45
  uniform mat4 projectionMatrix;
31
- uniform mat3 normalMatrix;
32
-
46
+
33
47
  uniform vec3 uOffset;
34
48
  uniform float uRadius;
35
49
  uniform float uFrames;
36
-
37
- void main() {
38
- vUv = uv;
39
-
40
- vec2 framesMinusOne = uFrames - vec2(1.0);
41
-
42
- mat4 m4 = modelViewMatrix;
43
-
44
- m4[0][0] = 1.0;
45
- m4[0][1] = 0.0;
46
- m4[0][2] = 0.0;
47
-
48
- m4[1][0] = 0.0;
49
- m4[1][1] = 1.0;
50
- m4[1][2] = 0.0;
51
-
52
- m4[2][0] = 0.0;
53
- m4[2][1] = 0.0;
54
- m4[2][2] = 1.0;
55
-
56
- vec3 object_scale = vec3(
57
- length(modelViewMatrix[0].xyz),
58
- length(modelViewMatrix[1].xyz),
59
- length(modelViewMatrix[2].xyz)
60
- );
61
-
62
- // scale by object's baking bounding sphere's radius
63
- float card_diameter = uRadius*2.0;
64
- object_scale *= card_diameter;
65
-
66
- vec3 transformedNormal = normalize(normalMatrix * normal);
67
-
68
- vec4 mvPosition = m4 * vec4( object_scale*(position+uOffset/card_diameter), 1.0 );
69
-
70
- gl_Position = projectionMatrix * mvPosition;
71
-
72
- mat4 inverse_matrix = inverse(projectionMatrix * modelViewMatrix);
73
-
74
- //
75
- //get 2D projection of this vertex in normalized device coordinates
76
- vec2 pos = gl_Position.xy/gl_Position.w;
77
-
78
- //compute ray's start and end as inversion of this coordinates
79
- //in near and far clip planes
80
- vec4 near_4 = inverse_matrix * (vec4(pos, -1.0, 1.0));
81
- vec4 far_4 = inverse_matrix * (vec4(pos, 1.0, 1.0));
82
-
83
- local_ray_near = near_4.xyz / near_4.w;
84
- local_ray_far = far_4.xyz/ far_4.w;
85
- }
86
- `;
87
- const shader_fg = `
88
- precision highp float;
89
- precision highp int;
90
-
91
- const float depth_scale = 0.5;
92
-
93
- in vec2 vUv;
94
-
95
- in vec4 plane0;
96
- in vec4 plane1;
97
- in vec4 plane2;
98
-
99
- in vec3 vViewPosition;
100
- in vec3 vFacingDirection;
101
-
102
- out vec4 color_out;
103
-
104
- uniform sampler2D tBase;
105
- uniform sampler2D tGeometry;
106
- uniform float uFrames;
107
- uniform float uDepthScale;
108
50
  uniform bool uIsFullSphere;
109
-
110
-
111
- in vec3 local_ray_near;
112
- in vec3 local_ray_far;
113
-
114
- struct Material{
115
- vec3 diffuse;
116
- vec3 normal;
117
- float depth;
118
- float occlusion;
119
- float roughness;
120
- float metalness;
121
- };
122
-
51
+
52
+ // ----- direction -> octahedral grid coord (range -1..+1) -----
53
+
123
54
  vec2 VecToSphereOct(vec3 pivotToCamera)
124
55
  {
125
56
  vec3 octant = sign(pivotToCamera);
126
-
127
- // |x| + |y| + |z| = 1
128
57
  float sum = dot(pivotToCamera, octant);
129
58
  vec3 octahedron = pivotToCamera / sum;
130
-
131
- if (octahedron.y < 0.0){
59
+ if (octahedron.y < 0.0) {
132
60
  vec3 absolute = abs(octahedron);
133
61
  octahedron.xz = octant.xz * vec2(1.0 - absolute.z, 1.0 - absolute.x);
134
62
  }
135
63
  return octahedron.xz;
136
64
  }
137
-
138
- //for hemisphere
139
- vec2 VecToHemiSphereOct(vec3 vec)
140
- {
141
- vec.y = max(vec.y, 0.001);
142
- vec = normalize(vec);
143
- vec3 octant = sign(vec);
144
-
145
- // |x| + |y| + |z| = 1
146
- float sum = dot(vec, octant);
147
- vec3 octahedron = vec / sum;
148
-
65
+
66
+ vec2 VecToHemiSphereOct(vec3 v)
67
+ {
68
+ v.y = max(v.y, 0.001);
69
+ v = normalize(v);
70
+ vec3 octant = sign(v);
71
+ float sum = dot(v, octant);
72
+ vec3 octahedron = v / sum;
149
73
  return vec2(
150
74
  octahedron.x + octahedron.z,
151
75
  octahedron.z - octahedron.x
152
76
  );
153
77
  }
154
-
155
- vec2 VectorToGrid(vec3 vec)
78
+
79
+ vec2 VectorToGrid(vec3 v)
156
80
  {
157
- if (uIsFullSphere)
158
- {
159
- return VecToSphereOct(vec);
160
- }
161
- else
162
- {
163
- return VecToHemiSphereOct(vec);
81
+ if (uIsFullSphere) {
82
+ return VecToSphereOct(v);
83
+ } else {
84
+ return VecToHemiSphereOct(v);
164
85
  }
165
86
  }
166
-
167
- vec4 TriangleInterpolate(vec2 uv){
168
- uv = fract(uv);
169
-
170
- vec2 omuv = vec2(1.0, 1.0) - uv.xy;
171
-
172
- vec4 res = vec4(0, 0, 0, 0);
173
- //frame 0
174
- res.x = min(omuv.x, omuv.y);
175
- //frame 1
176
- res.y = abs(dot(uv, vec2(1.0, -1.0)));
177
- //frame 2
178
- res.z = min(uv.x, uv.y);
179
- //mask
180
- res.w = clamp(ceil(uv.x-uv.y),0.0, 1.0);
181
-
182
- return res;
183
- }
184
-
185
-
186
- vec4 ImposterBlendWeights(sampler2D tex, vec2 frame0, vec2 frame1, vec2 frame2, vec4 weights, vec4 ddxy)
87
+
88
+ // ----- octahedral grid coord (range 0..1) -> direction -----
89
+ //
90
+ // Inverse of VectorToGrid (after the 0.5/2.0 remap). Given the
91
+ // normalised position of a frame in the atlas, return the world-space
92
+ // direction the bake camera was sitting at when it captured that frame.
93
+
94
+ vec3 OctaSphereDec(vec2 coord)
187
95
  {
188
- vec4 samp0 = textureGrad(tex, frame0, ddxy.xy, ddxy.zw);
189
- vec4 samp1 = textureGrad(tex, frame1, ddxy.xy, ddxy.zw);
190
- vec4 samp2 = textureGrad(tex, frame2, ddxy.xy, ddxy.zw);
191
-
192
- vec4 result = samp0*weights.x + samp1*weights.y + samp2*weights.z;
193
-
194
- return result;
96
+ coord = (coord - 0.5) * 2.0;
97
+ vec3 p = vec3(coord.x, 0.0, coord.y);
98
+ vec2 a = abs(p.xz);
99
+ p.y = 1.0 - a.x - a.y;
100
+ if (p.y < 0.0) {
101
+ p.xz = sign(p.xz) * vec2(1.0 - a.y, 1.0 - a.x);
102
+ }
103
+ return p;
195
104
  }
196
-
197
- vec4 ImposterBlendWeightsNearest(sampler2D tex, vec2 frame0, vec2 frame1, vec2 frame2, vec4 weights, vec4 ddxy)
105
+
106
+ vec3 OctaHemiSphereDec(vec2 coord)
198
107
  {
199
- vec4 samp0 = textureGrad(tex, frame0, ddxy.xy, ddxy.zw);
200
- vec4 samp1 = textureGrad(tex, frame1, ddxy.xy, ddxy.zw);
201
- vec4 samp2 = textureGrad(tex, frame2, ddxy.xy, ddxy.zw);
202
-
203
- vec4 result;
204
-
205
- if(weights.x > weights.y && weights.x > weights.z){
206
- result = samp0;
207
- }if(weights.y > weights.z){
208
- result = samp1;
209
- }else{
210
- result = samp2;
211
- }
212
-
213
- return result;
108
+ vec3 p = vec3(coord.x - coord.y, 0.0, -1.0 + coord.x + coord.y);
109
+ vec2 a = abs(p.xz);
110
+ p.y = 1.0 - a.x - a.y;
111
+ return p;
214
112
  }
215
-
216
-
217
- void calcuateXYbasis(vec3 plane_normal, out vec3 plane_x, out vec3 plane_y)
113
+
114
+ vec3 GridToVector(vec2 coord)
218
115
  {
219
- vec3 up = vec3(0,1,0);
220
- //cross product doesnt work if we look directly from bottom
221
- if (abs(plane_normal.y) > 0.999f)
222
- {
223
- up = vec3(0,0,1);
116
+ if (uIsFullSphere) {
117
+ return OctaSphereDec(coord);
118
+ } else {
119
+ return OctaHemiSphereDec(coord);
224
120
  }
225
- plane_x = normalize(cross(plane_normal, up));
226
- plane_y = normalize(cross(plane_x, plane_normal));
227
121
  }
228
122
 
229
- vec3 projectOnPlaneBasis(vec3 ray, vec3 plane_normal, vec3 plane_x, vec3 plane_y)
123
+ // Frame index (gridFloor + corner offset) -> baked view direction.
124
+ // Frame coords at the grid border can sit slightly outside the
125
+ // [0, uFrames-1] range (e.g. gridFloor + (1,1) at the corner); clamp so
126
+ // we don't feed invalid UVs to the octahedral decoder.
127
+ vec3 FrameToRay(vec2 frame, vec2 framesMinusOne)
230
128
  {
231
- //reproject plane normal onto planeXY basos
232
- return normalize(vec3(
233
- dot(plane_x,ray),
234
- dot(plane_y,ray),
235
- dot(plane_normal,ray)
236
- ));
129
+ vec2 f = clamp(frame / framesMinusOne, 0.0, 1.0);
130
+ return normalize(GridToVector(f));
237
131
  }
238
-
239
- vec2 recompute_uv(vec2 frame, vec2 frame_uv, sampler2D gBuffer){
240
- vec2 frame_size = vec2(1.0/ uFrames);
241
-
242
- vec2 source_uv = (frame + frame_uv)*frame_size;
243
-
244
- float n_depth = texture(gBuffer, source_uv).a;
245
-
246
- vec2 offset = clamp(length(frame_uv*2.0 - 1.0) * vec2(0.5-n_depth ) * depth_scale,0.0, 1.0);
247
-
248
- vec2 uv_f = clamp(frame_uv+offset, 0.0, 1.0);
249
-
250
- uv_f = ( frame + uv_f)*frame_size;
251
-
252
- return clamp(uv_f,0.0, 1.0);
253
- // return source_uv;
132
+
133
+ vec4 TriangleInterpolate(vec2 frac_uv)
134
+ {
135
+ vec2 omuv = vec2(1.0) - frac_uv;
136
+ vec4 res;
137
+ // frame 0 weight (corner (0,0) of the quad)
138
+ res.x = min(omuv.x, omuv.y);
139
+ // frame 1 weight (off-diagonal corner — picked by res.w)
140
+ res.y = abs(frac_uv.x - frac_uv.y);
141
+ // frame 2 weight (corner (1,1) of the quad)
142
+ res.z = min(frac_uv.x, frac_uv.y);
143
+ // triangle-half mask: 1 in the lower-right half, 0 in the upper-left
144
+ res.w = clamp(ceil(frac_uv.x - frac_uv.y), 0.0, 1.0);
145
+ return res;
254
146
  }
255
-
256
- void main(){
257
- vec3 view_direction = normalize(local_ray_near-local_ray_far);
258
-
259
- vec2 octahedral_uv = clamp(VectorToGrid(view_direction)*0.5 + 0.5, 0.0, 1.0);
260
- vec2 grid = octahedral_uv * vec2(uFrames - 1.0);
261
-
262
- vec2 gridFrac = fract(grid);
147
+
148
+ void main() {
149
+ vUv = uv;
150
+
151
+ // 1. Octahedral atlas lookup.
152
+ // cameraPos_OS is the camera position in the impostor's local
153
+ // space — the same coordinate frame the meshes were in during
154
+ // the bake. The encoded direction is what picks which atlas
155
+ // frames we'll sample.
156
+ vec3 cameraPos_OS = (inverse(modelViewMatrix) * vec4(0.0, 0.0, 0.0, 1.0)).xyz;
157
+ vec3 pivotToCameraRay = normalize(cameraPos_OS);
158
+
159
+ vec2 framesMinusOne = vec2(uFrames - 1.0);
160
+ vec2 octahedral_uv = clamp(VectorToGrid(pivotToCameraRay) * 0.5 + 0.5, 0.0, 1.0);
161
+ vec2 grid = octahedral_uv * framesMinusOne;
263
162
  vec2 gridFloor = floor(grid);
264
-
265
- vec4 weights = TriangleInterpolate( gridFrac );
266
-
267
- vec2 frame_uv = vUv;
268
-
269
- //3 nearest frames
163
+ vec4 weights = TriangleInterpolate(fract(grid));
164
+
165
+ vGridFloor = gridFloor;
166
+ vWeights = weights;
167
+
168
+ // 2. Decode each of the 3 chosen frame indices back into the
169
+ // bake-time view direction, then blend with the triangle weights.
170
+ // projectedRay is the "effective" baked view direction the card
171
+ // is showing — the direction the depth and colour textures we're
172
+ // blending were captured along.
270
173
  vec2 frame0 = gridFloor;
271
- vec2 frame1 = gridFloor + mix(vec2(0,1),vec2(1,0),weights.w);
272
- vec2 frame2 = gridFloor + vec2(1.0,1.0);
273
-
274
- vec2 uv0 = recompute_uv(frame0, frame_uv, tGeometry);
275
- vec2 uv1 = recompute_uv(frame1, frame_uv, tGeometry);
276
- vec2 uv2 = recompute_uv(frame2, frame_uv, tGeometry);
277
-
278
- vec4 ddxy = vec4( dFdx(vUv.xy), dFdy(vUv.xy) );
279
-
280
- vec2 frame_size = vec2(1.0/ uFrames);
281
-
282
- vec4 texel_color = ImposterBlendWeights(
283
- // vec4 texel_color = ImposterBlendWeightsNearest(
284
- tBase,
285
- uv0,
286
- uv1,
287
- uv2,
288
- weights, ddxy
174
+ vec2 frame1 = gridFloor + mix(vec2(0.0, 1.0), vec2(1.0, 0.0), weights.w);
175
+ vec2 frame2 = gridFloor + vec2(1.0, 1.0);
176
+ vec3 ray0 = FrameToRay(frame0, framesMinusOne);
177
+ vec3 ray1 = FrameToRay(frame1, framesMinusOne);
178
+ vec3 ray2 = FrameToRay(frame2, framesMinusOne);
179
+ vec3 projectedRay = normalize(
180
+ ray0 * weights.x + ray1 * weights.y + ray2 * weights.z
181
+ );
182
+
183
+ // 3. Build the card's TBN in object-local space.
184
+ // NORMAL = projectedRay (the effective bake direction).
185
+ // TANGENT = cross(up, NORMAL) — matches three.js Camera.lookAt
186
+ // (which is what the baker used), so position.x of
187
+ // the cutout shape lines up with the texture's X.
188
+ // BINORMAL = cross(NORMAL, TANGENT) — matches the bake camera's
189
+ // up axis, so position.y lines up with texture Y.
190
+ //
191
+ // The bake used world-up (0,1,0). The bake's world frame IS our
192
+ // object-local frame (the meshes lived there during the bake),
193
+ // so we use object-local (0,1,0) here too. The fallback covers
194
+ // the degenerate pole case where NORMAL is parallel to up.
195
+ vec3 normal_OS = projectedRay;
196
+ vec3 up_OS = abs(normal_OS.y) > 0.999
197
+ ? vec3(0.0, 0.0, -1.0)
198
+ : vec3(0.0, 1.0, 0.0);
199
+ vec3 tangent_OS = normalize(cross(up_OS, normal_OS));
200
+ vec3 binormal_OS = cross(normal_OS, tangent_OS);
201
+
202
+ // 4. Position the card vertex in object-local space.
203
+ // position.xy is in [-0.5, 0.5] (the centred cutout shape), so
204
+ // multiplying by card_diameter places it on a card whose half-
205
+ // width and half-height are uRadius — tangentially spanning the
206
+ // bounding sphere. uOffset is the bounding sphere centre in
207
+ // object-local space (captured at bake time).
208
+ float card_diameter = uRadius * 2.0;
209
+ vec3 pos_OS = uOffset
210
+ + position.x * card_diameter * tangent_OS
211
+ + position.y * card_diameter * binormal_OS;
212
+
213
+ vec4 mvPosition = modelViewMatrix * vec4(pos_OS, 1.0);
214
+ vViewPos = mvPosition.xyz;
215
+ gl_Position = projectionMatrix * mvPosition;
216
+
217
+ // 5. TBN in view space — what the fragment shader needs to convert
218
+ // its view-space view-dir into tangent space. For typical
219
+ // impostor transforms (rigid + uniform scale) mat3(modelView) is
220
+ // orthogonal up to a uniform scalar, so renormalising after the
221
+ // multiply is sufficient; non-uniform scale would require the
222
+ // inverse-transpose.
223
+ mat3 m3 = mat3(modelViewMatrix);
224
+ vTangent = normalize(m3 * tangent_OS);
225
+ vBinormal = normalize(m3 * binormal_OS);
226
+ vNormal = normalize(m3 * normal_OS);
227
+ }
228
+ `;
229
+ const shader_fg = `
230
+ precision highp float;
231
+ precision highp int;
232
+
233
+ in vec2 vUv;
234
+ in vec3 vViewPos;
235
+
236
+ // Frame indices + triangle weights are computed per-impostor in the
237
+ // vertex shader. flat = no interpolation; every fragment sees the same
238
+ // values and samples the same 3 atlas frames.
239
+ flat in vec2 vGridFloor;
240
+ flat in vec4 vWeights;
241
+
242
+ // Card's TBN in view space. The card is no longer view-aligned — it's
243
+ // oriented perpendicular to the effective bake direction — so we have
244
+ // to convert the view-space view direction into this tangent frame
245
+ // before doing parallax.
246
+ flat in vec3 vTangent;
247
+ flat in vec3 vBinormal;
248
+ flat in vec3 vNormal;
249
+
250
+ out vec4 color_out;
251
+
252
+ uniform sampler2D tBase;
253
+ uniform sampler2D tGeometry;
254
+ uniform float uFrames;
255
+ uniform float uDepthScale;
256
+
257
+ // Sample the same within-frame UV from the three chosen atlas frames and
258
+ // blend with the triangle weights from the vertex shader. Used for both
259
+ // depth and colour.
260
+ vec4 blend_3_frames(sampler2D tex, vec2 frame_uv, vec2 f0, vec2 f1, vec2 f2, vec4 w)
261
+ {
262
+ vec2 frame_size = vec2(1.0 / uFrames);
263
+ vec4 s0 = texture(tex, (f0 + frame_uv) * frame_size);
264
+ vec4 s1 = texture(tex, (f1 + frame_uv) * frame_size);
265
+ vec4 s2 = texture(tex, (f2 + frame_uv) * frame_size);
266
+ return s0 * w.x + s1 * w.y + s2 * w.z;
267
+ }
268
+
269
+ void main() {
270
+ // 3 nearest frames — flat from vertex shader, identical for every
271
+ // fragment of this impostor card.
272
+ vec2 frame0 = vGridFloor;
273
+ vec2 frame1 = vGridFloor + mix(vec2(0.0, 1.0), vec2(1.0, 0.0), vWeights.w);
274
+ vec2 frame2 = vGridFloor + vec2(1.0, 1.0);
275
+
276
+ // View direction in the card's tangent space. The card is oriented
277
+ // perpendicular to the effective bake direction, not view-aligned,
278
+ // so we project the view-space view-dir onto the (T, B, N) basis we
279
+ // built in the vertex shader.
280
+ vec3 view_dir_view = normalize(-vViewPos);
281
+ vec3 view_dir = vec3(
282
+ dot(vTangent, view_dir_view),
283
+ dot(vBinormal, view_dir_view),
284
+ dot(vNormal, view_dir_view)
289
285
  );
290
-
291
-
292
- // texel_color = vec4(texel_color.aaa, 1.0);
293
-
294
- if(texel_color.a <= 0.5){
295
- texel_color.r = 1.0;
286
+
287
+ // One-step approximate parallax: sample the blended depth at the
288
+ // un-corrected UV, then shift the UV by the height that depth
289
+ // implies.
290
+ //
291
+ // Our bake writes depth = 1 at the near plane (front of the bounding
292
+ // sphere, closest to the bake camera), depth = 0 at the far plane,
293
+ // depth = 0.5 at the bounding sphere centre — i.e. the card plane.
294
+ // So (depth - 0.5) is the signed height of the surface above the
295
+ // card plane, in units where ±0.5 = ±radius. The card spans 2*radius
296
+ // mapped to UV [0,1], so that height is already in card-UV units.
297
+ //
298
+ // Standard parallax-mapping formula: shift base_uv by V.xy / V.z * h,
299
+ // where V is the view direction in tangent space and h is the
300
+ // surface height above the card plane.
301
+ vec2 base_uv = vUv;
302
+ float depth = blend_3_frames(tGeometry, base_uv, frame0, frame1, frame2, vWeights).a;
303
+ base_uv += (view_dir.xy / view_dir.z) * (depth - 0.5) * uDepthScale;
304
+
305
+ // Keep the parallax-shifted sample inside the current frame's tile.
306
+ // Without this, large shifts at oblique angles bleed colour from the
307
+ // neighbouring atlas frame (a completely different bake direction).
308
+ base_uv = clamp(base_uv, 0.0, 1.0);
309
+
310
+ vec4 texel_color = blend_3_frames(tBase, base_uv, frame0, frame1, frame2, vWeights);
311
+
312
+ if (texel_color.a <= 0.5) {
296
313
  discard;
297
314
  }
298
-
315
+
299
316
  color_out = texel_color;
300
- // color_out = vec4( snapped_oct_uv, 1.0, 1.0);
301
317
  }
302
318
  `;
303
319
 
@@ -347,9 +363,17 @@ export class ImpostorShaderV0 extends RawShaderMaterial {
347
363
  uIsFullSphere: {
348
364
  value: false
349
365
  },
366
+ /**
367
+ * Strength of the tangent-space parallax offset. The
368
+ * geometrically-correct value for a true sphere surface is
369
+ * about 1.0, but in practice 0.3–0.5 produces a more
370
+ * believable result because the bake only contains the
371
+ * silhouette and front-facing geometry — strong parallax
372
+ * starts revealing missing/occluded parts at oblique angles.
373
+ * Range: [0, 1].
374
+ */
350
375
  uDepthScale:{
351
- // value should be in range between 0 and 1
352
- value:1
376
+ value: 0.5
353
377
  }
354
378
  },
355
379
  glslVersion: GLSL3
@@ -9,7 +9,6 @@ import {
9
9
  } from "three";
10
10
  import FacingDirectionSystem from "../../../../../../../model/game/ecs/system/FacingDirectionSystem.js";
11
11
  import { makeEngineOptionsModel } from "../../../../../../../model/game/options/makeEngineOptionsModel.js";
12
- import { OrbitingBehavior } from "../../../../../../../model/game/util/behavior/OrbitingBehavior.js";
13
12
  import { enableEditor } from "../../../../../../editor/enableEditor.js";
14
13
  import { makeHelperSphereGeometry } from "../../../../../../editor/process/symbolic/makeHelperSphereGeometry.js";
15
14
  import { parse_color } from "../../../../../core/color/parse_color.js";
@@ -52,6 +51,7 @@ import InputControllerSystem from "../../../../input/ecs/systems/InputController
52
51
  import { InputSystem } from "../../../../input/ecs/systems/InputSystem.js";
53
52
  import { BehaviorComponent } from "../../../../intelligence/behavior/ecs/BehaviorComponent.js";
54
53
  import { BehaviorSystem } from "../../../../intelligence/behavior/ecs/BehaviorSystem.js";
54
+ import { OrbitingBehavior } from "../../../../intelligence/behavior/ecs/OrbitingBehavior.js";
55
55
  import PathFollowingSystem from "../../../../navigation/ecs/path_following/PathFollowingSystem.js";
56
56
  import { SoundEmitterSystem } from "../../../../sound/ecs/emitter/SoundEmitterSystem.js";
57
57
  import SoundControllerSystem from "../../../../sound/ecs/SoundControllerSystem.js";
@@ -1 +1 @@
1
- {"version":3,"file":"BufferedGeometryBVH.d.ts","sourceRoot":"","sources":["../../../../../../src/engine/graphics/sh3/path_tracer/BufferedGeometryBVH.js"],"names":[],"mappings":"AAwCA;IAkBI;;;OAGG;IACH,eAFW,KAAK,GAAC,MAAM,EAAE,QAIxB;IAIG;;;;OAIG;IACH,mBAAsB;IAEtB;;;;OAIG;IACH,yBAA4B;IAE5B;;;;OAIG;IACH,6BAAgC;IAGhC;;;;OAIG;IACH,yBAAyB;IAG7B;;;OAGG;IACH,WAFW,MAAM,cAAc,QA8C9B;IAED;;;;;OAKG;IACH,qBAFa,OAAO,CAmHnB;IAED;;;;;;;OAOG;IACH,4BANW,aAAa,KACb,MAAM,KACN,MAAM,KACN,MAAM,GACJ,OAAO,CAUnB;IAGD;;;;;;OAMG;IACH,iBAJW,MAAM,EAAE,OACR,MAAM,EAAE,OAAK,GACX,MAAM,CAoIlB;IAED;;;;;OAKG;IACH,gBAJW,MAAM,EAAE,OACR,MAAM,EAAE,OAAK,GACX,MAAM,CA2ElB;;CACJ;sBA9eqB,wCAAwC;8BAGhC,2CAA2C"}
1
+ {"version":3,"file":"BufferedGeometryBVH.d.ts","sourceRoot":"","sources":["../../../../../../src/engine/graphics/sh3/path_tracer/BufferedGeometryBVH.js"],"names":[],"mappings":"AAwCA;IAkBI;;;OAGG;IACH,eAFW,KAAK,GAAC,MAAM,EAAE,QAIxB;IAIG;;;;OAIG;IACH,mBAAsB;IAEtB;;;;OAIG;IACH,yBAA4B;IAE5B;;;;OAIG;IACH,6BAAgC;IAGhC;;;;OAIG;IACH,yBAAyB;IAG7B;;;OAGG;IACH,WAFW,MAAM,cAAc,QAgD9B;IAED;;;;;OAKG;IACH,qBAFa,OAAO,CAmHnB;IAED;;;;;;;OAOG;IACH,4BANW,aAAa,KACb,MAAM,KACN,MAAM,KACN,MAAM,GACJ,OAAO,CAUnB;IAGD;;;;;;OAMG;IACH,iBAJW,MAAM,EAAE,OACR,MAAM,EAAE,OAAK,GACX,MAAM,CAoIlB;IAED;;;;;OAKG;IACH,gBAJW,MAAM,EAAE,OACR,MAAM,EAAE,OAAK,GACX,MAAM,CA2ElB;;CACJ;sBAhfqB,wCAAwC;8BAGhC,2CAA2C"}
@@ -129,10 +129,12 @@ export class BufferedGeometryBVH {
129
129
  array_positions,
130
130
  this.#morton_codes,
131
131
  this.#bounds,
132
- 16
132
+ 2
133
133
  );
134
134
 
135
- ebvh_optimize_treelet(bvh);
135
+ for (let i = 0; i < 3; i++) {
136
+ ebvh_optimize_treelet(bvh);
137
+ }
136
138
 
137
139
  //
138
140
  // ebvh_build_for_geometry_incremental(bvh, index_array, array_positions, 1);
@@ -1 +1 @@
1
- {"version":3,"file":"PathTracedScene.d.ts","sourceRoot":"","sources":["../../../../../../src/engine/graphics/sh3/path_tracer/PathTracedScene.js"],"names":[],"mappings":"AAoDA;IAGI;;;OAGG;IACH,eAFU,GAAG,CAEa;IAG1B;;;OAGG;IACH,QAFU,IAAI,MAAM,EAAE,cAAc,CAAC,CAElB;IAEnB;;;OAGG;IACH,UAFU,aAAa,EAAE,CAEX;IAEd;;;OAGG;IACH,WAFU,IAAI,MAAM,cAAc,EAAE,mBAAmB,CAAC,CAElC;IAmBtB;;;;OAIG;IACH,6BAA6C;IAkF7C,iBAOC;IAED;;;;OAIG;IACH,uBAHW,MAAM,cAAc,GACnB,mBAAmB,CAmB9B;IAED;;;OAGG;IACH,gBAFW,aAAa,QAMvB;IAED;;;;OAIG;IACH,cAHW,cAAc,GACZ,OAAO,CAInB;IAED;;;;OAIG;IACH,cAHW,cAAc,GACZ,OAAO,CAiCnB;IAED;;;;OAIG;IACH,iBAHW,cAAc,GACZ,OAAO,CAgBnB;IAED;;;;OAIG;IACH,yBAHW,MAAM,QAAQ,oBAKxB;IAED;;;;;OAKG;IACH,qBAJW,MAAM,cAAc,YACpB,MAAM,QAAQ,aACd,OAAK,MAAM,EAAE,QAavB;IAED;;;;;OAKG;IACH,qBAJW,MAAM,EAAE,OACR,MAAM,EAAE,gBACR,IAAI,QA0Cd;IAED;;;;OAIG;IACH,cAHW,IAAI,GACF,OAAO,CAsCnB;IAED;;;;;OAKG;IACH,WAJW,MAAM,EAAE,OACR,MAAM,EAAE,GAAC,IAAI,GACZ,MAAM,CA+CjB;IAED;;;;;;OAMG;IACH,uBALW,MAAM,EAAE,cACR,MAAM,aACN,MAAM,EAAE,oBACR,MAAM,QAIhB;IAED;;;;;OAKG;IACH,mBAJW,MAAM,EAAE,cACR,MAAM,OACN,MAAM,EAAE,QAwHlB;;CACJ;oBA1lB8B,mCAAmC;+BAqBnC,qBAAqB;8BAJtB,kDAAkD;oCAC5C,0BAA0B;qBARzC,sCAAsC"}
1
+ {"version":3,"file":"PathTracedScene.d.ts","sourceRoot":"","sources":["../../../../../../src/engine/graphics/sh3/path_tracer/PathTracedScene.js"],"names":[],"mappings":"AAoDA;IAGI;;;OAGG;IACH,eAFU,GAAG,CAEa;IAG1B;;;OAGG;IACH,QAFU,IAAI,MAAM,EAAE,cAAc,CAAC,CAElB;IAEnB;;;OAGG;IACH,UAFU,aAAa,EAAE,CAEX;IAEd;;;OAGG;IACH,WAFU,IAAI,MAAM,cAAc,EAAE,mBAAmB,CAAC,CAElC;IAmBtB;;;;OAIG;IACH,6BAA6C;IAoF7C,iBAOC;IAED;;;;OAIG;IACH,uBAHW,MAAM,cAAc,GACnB,mBAAmB,CAmB9B;IAED;;;OAGG;IACH,gBAFW,aAAa,QAMvB;IAED;;;;OAIG;IACH,cAHW,cAAc,GACZ,OAAO,CAInB;IAED;;;;OAIG;IACH,cAHW,cAAc,GACZ,OAAO,CAiCnB;IAED;;;;OAIG;IACH,iBAHW,cAAc,GACZ,OAAO,CAgBnB;IAED;;;;OAIG;IACH,yBAHW,MAAM,QAAQ,oBAKxB;IAED;;;;;OAKG;IACH,qBAJW,MAAM,cAAc,YACpB,MAAM,QAAQ,aACd,OAAK,MAAM,EAAE,QAavB;IAED;;;;;OAKG;IACH,qBAJW,MAAM,EAAE,OACR,MAAM,EAAE,gBACR,IAAI,QA0Cd;IAED;;;;OAIG;IACH,cAHW,IAAI,GACF,OAAO,CAsCnB;IAED;;;;;OAKG;IACH,WAJW,MAAM,EAAE,OACR,MAAM,EAAE,GAAC,IAAI,GACZ,MAAM,CA+CjB;IAED;;;;;;OAMG;IACH,uBALW,MAAM,EAAE,cACR,MAAM,aACN,MAAM,EAAE,oBACR,MAAM,QAIhB;IAED;;;;;OAKG;IACH,mBAJW,MAAM,EAAE,cACR,MAAM,OACN,MAAM,EAAE,QAwHlB;;CACJ;oBA5lB8B,mCAAmC;+BAqBnC,qBAAqB;8BAJtB,kDAAkD;oCAC5C,0BAA0B;qBARzC,sCAAsC"}
@@ -177,9 +177,11 @@ export class PathTracedScene {
177
177
  array_copy(nodes, 0, unprocessed_nodes, 0, node_leaf_count);
178
178
 
179
179
  // assign root
180
- bvh.__root = ebvh_build_hierarchy(bvh, unprocessed_nodes, node_leaf_count, nodes, node_leaf_count,16);
180
+ bvh.__root = ebvh_build_hierarchy(bvh, unprocessed_nodes, node_leaf_count, nodes, node_leaf_count, 3);
181
181
 
182
- ebvh_optimize_treelet(bvh);
182
+ for (let i = 0; i < 3; i++) {
183
+ ebvh_optimize_treelet(bvh);
184
+ }
183
185
  }
184
186
 
185
187
  optimize() {
@@ -67,8 +67,8 @@ vCanvas.css({
67
67
  * How many rays to use per-pixel
68
68
  * @type {number}
69
69
  */
70
- const PIXEL_SAMPLE_COUNT = 1;
71
- const PIXEL_RENDER_RATIO = 0.5;
70
+ const PIXEL_SAMPLE_COUNT = 16;
71
+ const PIXEL_RENDER_RATIO = 1;
72
72
 
73
73
  const scene = new PathTracedScene();
74
74
  const pt = new PathTracer();
@@ -0,0 +1,63 @@
1
+ export class OrbitingBehavior extends EntityBehavior {
2
+ /**
3
+ *
4
+ * @param {Vector3} [center]
5
+ * @param {number} [radius]
6
+ * @param {number} [rate]
7
+ * @returns {OrbitingBehavior}
8
+ */
9
+ static from({ center, radius, rate }?: Vector3): OrbitingBehavior;
10
+ /**
11
+ * Radians per second.
12
+ * Can be negative for counter-clockwise rotation.
13
+ * @type {number}
14
+ */
15
+ rate: number;
16
+ /**
17
+ *
18
+ * @type {number}
19
+ */
20
+ radius: number;
21
+ /**
22
+ * Initial offset in radians
23
+ * @type {number}
24
+ */
25
+ phase: number;
26
+ /**
27
+ *
28
+ * @type {number}
29
+ */
30
+ angle: number;
31
+ /**
32
+ * Offset from the origin
33
+ * @type {Vector3}
34
+ */
35
+ offset: Vector3;
36
+ /**
37
+ *
38
+ * @type {number}
39
+ */
40
+ parent: number;
41
+ /**
42
+ *
43
+ * @type {number}
44
+ */
45
+ easing: number;
46
+ orientation: Quaternion;
47
+ /**
48
+ *
49
+ * @type {Vector3}
50
+ */
51
+ center: Vector3;
52
+ /**
53
+ *
54
+ * @type {Quaternion}
55
+ */
56
+ lookRotation: Quaternion;
57
+ tick(timeDelta: any): BehaviorStatus.Running | BehaviorStatus.Failed;
58
+ }
59
+ import { EntityBehavior } from "./EntityBehavior.js";
60
+ import Vector3 from "../../../../core/geom/Vector3.js";
61
+ import Quaternion from "../../../../core/geom/Quaternion.js";
62
+ import { BehaviorStatus } from "../BehaviorStatus.js";
63
+ //# sourceMappingURL=OrbitingBehavior.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"OrbitingBehavior.d.ts","sourceRoot":"","sources":["../../../../../../src/engine/intelligence/behavior/ecs/OrbitingBehavior.js"],"names":[],"mappings":"AAQA;IA4DI;;;;;;OAMG;IACH,uCALW,OAAO,GAGL,gBAAgB,CAmB5B;IAlFD;;;;OAIG;IACH,MAFU,MAAM,CAED;IAEf;;;OAGG;IACH,QAFU,MAAM,CAEL;IAEX;;;OAGG;IACH,OAFU,MAAM,CAEN;IAEV;;;OAGG;IACH,OAFU,MAAM,CAEN;IAEV;;;OAGG;IACH,QAFU,OAAO,CAEM;IAEvB;;;OAGG;IACH,QAFU,MAAM,CAEJ;IAEZ;;;OAGG;IACH,QAFU,MAAM,CAEL;IAEX,wBAA+B;IAE/B;;;OAGG;IACH,QAFU,OAAO,CAEM;IAGvB;;;OAGG;IACH,cAFU,UAAU,CAEY;IAwChC,qEAgGC;CACJ;+BArM8B,qBAAqB;oBAJhC,kCAAkC;uBAD/B,qCAAqC;+BAI7B,sBAAsB"}
@@ -0,0 +1,204 @@
1
+ import { assert } from "../../../../core/assert.js";
2
+ import Quaternion from "../../../../core/geom/Quaternion.js";
3
+ import Vector3 from "../../../../core/geom/Vector3.js";
4
+ import { lerp } from "../../../../core/math/lerp.js";
5
+ import { Transform } from "../../../ecs/transform/Transform.js";
6
+ import { BehaviorStatus } from "../BehaviorStatus.js";
7
+ import { EntityBehavior } from "./EntityBehavior.js";
8
+
9
+ export class OrbitingBehavior extends EntityBehavior {
10
+
11
+ /**
12
+ * Radians per second.
13
+ * Can be negative for counter-clockwise rotation.
14
+ * @type {number}
15
+ */
16
+ rate = Math.PI;
17
+
18
+ /**
19
+ *
20
+ * @type {number}
21
+ */
22
+ radius = 1;
23
+
24
+ /**
25
+ * Initial offset in radians
26
+ * @type {number}
27
+ */
28
+ phase = 0;
29
+
30
+ /**
31
+ *
32
+ * @type {number}
33
+ */
34
+ angle = 0;
35
+
36
+ /**
37
+ * Offset from the origin
38
+ * @type {Vector3}
39
+ */
40
+ offset = new Vector3();
41
+
42
+ /**
43
+ *
44
+ * @type {number}
45
+ */
46
+ parent = -1;
47
+
48
+ /**
49
+ *
50
+ * @type {number}
51
+ */
52
+ easing = 1;
53
+
54
+ orientation = new Quaternion();
55
+
56
+ /**
57
+ *
58
+ * @type {Vector3}
59
+ */
60
+ center = new Vector3();
61
+
62
+
63
+ /**
64
+ *
65
+ * @type {Quaternion}
66
+ */
67
+ lookRotation = new Quaternion();
68
+
69
+ /**
70
+ *
71
+ * @param {Vector3} [center]
72
+ * @param {number} [radius]
73
+ * @param {number} [rate]
74
+ * @returns {OrbitingBehavior}
75
+ */
76
+ static from({
77
+ center = Vector3.zero,
78
+ radius = 1,
79
+ rate = 1
80
+ }) {
81
+
82
+ assert.defined(center, 'center')
83
+ assert.isNumber(radius, 'radius')
84
+ assert.isNumber(rate, 'rate')
85
+
86
+ const r = new OrbitingBehavior();
87
+
88
+ r.offset.copy(center);
89
+ r.radius = radius;
90
+ r.rate = rate;
91
+
92
+ return r;
93
+ }
94
+
95
+ initialize(context) {
96
+ super.initialize(context);
97
+
98
+ const easing = this.easing;
99
+
100
+ this.easing = 1;
101
+
102
+ this.tick(0);
103
+
104
+ this.easing = easing;
105
+ }
106
+
107
+ tick(timeDelta) {
108
+
109
+ const angleDelta = timeDelta * this.rate;
110
+
111
+ this.angle += angleDelta;
112
+
113
+ const entity = this.context.entity;
114
+ const ecd = this.context.ecd;
115
+
116
+ /**
117
+ *
118
+ * @type {Transform}
119
+ */
120
+ const transform = ecd.getComponent(entity, Transform);
121
+
122
+ if (transform === undefined) {
123
+ return BehaviorStatus.Failed;
124
+ }
125
+
126
+ const a = this.angle + this.phase;
127
+
128
+ const cos = Math.cos(a);
129
+ const sin = Math.sin(a);
130
+
131
+ let center_x = this.offset.x;
132
+ let center_y = this.offset.y;
133
+ let center_z = this.offset.z;
134
+
135
+ const r = this.radius;
136
+
137
+
138
+ let x = r * cos;
139
+ let y = 0;
140
+ let z = r * sin;
141
+
142
+ const q = this.orientation;
143
+
144
+ //transform point into quaternion
145
+ const qx = q.x, qy = q.y, qz = q.z, qw = q.w;
146
+
147
+ // calculate quat * vector
148
+
149
+ var ix = qw * x + qy * z;
150
+ var iy = qz * x - qx * z;
151
+ var iz = qw * z - qy * x;
152
+ var iw = -qx * x - qz * z;
153
+
154
+ // calculate result * inverse quat
155
+
156
+ x = ix * qw + iw * -qx + iy * -qz - iz * -qy;
157
+ y = iy * qw + iw * -qy + iz * -qx - ix * -qz;
158
+ z = iz * qw + iw * -qz + ix * -qy - iy * -qx;
159
+
160
+
161
+ if (this.parent !== -1 && ecd.entityExists(this.parent)) {
162
+
163
+ const parentTransform = ecd.getComponent(this.parent, Transform);
164
+
165
+ center_x += parentTransform.position.x;
166
+ center_y += parentTransform.position.y;
167
+ center_z += parentTransform.position.z;
168
+
169
+
170
+ const parentScape = parentTransform.scale;
171
+
172
+ x *= parentScape.x;
173
+ y *= parentScape.y;
174
+ z *= parentScape.z;
175
+ }
176
+
177
+ center_x = lerp(this.center.x, center_x, this.easing);
178
+ center_y = lerp(this.center.y, center_y, this.easing);
179
+ center_z = lerp(this.center.z, center_z, this.easing);
180
+
181
+ this.center.set(center_x, center_y, center_z);
182
+
183
+ const _x = center_x + x;
184
+ const _y = center_y + y;
185
+ const _z = center_z + z;
186
+
187
+ const p = transform.position;
188
+
189
+ const old_x = p.x;
190
+ const old_y = p.y;
191
+ const old_z = p.z;
192
+
193
+ if (old_x !== _x || old_y !== _y || old_z !== _z) {
194
+
195
+ transform.rotation._lookRotation(_x - old_x, _y - old_y, _z - old_z, 0, 1, 0);
196
+ transform.rotation.multiplyQuaternions(transform.rotation, this.lookRotation);
197
+
198
+ }
199
+
200
+ p.set(_x, _y, _z);
201
+
202
+ return BehaviorStatus.Running;
203
+ }
204
+ }