@woosh/meep-engine 2.138.1 → 2.138.2

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.2",
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;
@@ -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":"AAgNA;IACI,cA2DC;IAHG,4CAAyB;CAIhC;kCArQM,OAAO"}
@@ -13,34 +13,87 @@ 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
+
22
+ // Octahedral atlas frame indices and triangle interpolation weights are
23
+ // chosen once per-impostor — the pivot-to-camera direction is constant
24
+ // for all vertices of the card, so every vertex computes the same answer.
25
+ // Forwarded as flat varyings so every fragment samples the same 3 atlas
26
+ // frames; otherwise a perspective camera would flip the chosen frame
27
+ // across the card and produce visible seams.
28
+ flat out vec2 vGridFloor;
29
+ flat out vec4 vWeights;
28
30
 
29
31
  uniform mat4 modelViewMatrix;
30
32
  uniform mat4 projectionMatrix;
31
- uniform mat3 normalMatrix;
32
-
33
+
33
34
  uniform vec3 uOffset;
34
35
  uniform float uRadius;
35
36
  uniform float uFrames;
36
-
37
+ uniform bool uIsFullSphere;
38
+
39
+ vec2 VecToSphereOct(vec3 pivotToCamera)
40
+ {
41
+ vec3 octant = sign(pivotToCamera);
42
+ float sum = dot(pivotToCamera, octant);
43
+ vec3 octahedron = pivotToCamera / sum;
44
+ if (octahedron.y < 0.0) {
45
+ vec3 absolute = abs(octahedron);
46
+ octahedron.xz = octant.xz * vec2(1.0 - absolute.z, 1.0 - absolute.x);
47
+ }
48
+ return octahedron.xz;
49
+ }
50
+
51
+ vec2 VecToHemiSphereOct(vec3 v)
52
+ {
53
+ v.y = max(v.y, 0.001);
54
+ v = normalize(v);
55
+ vec3 octant = sign(v);
56
+ float sum = dot(v, octant);
57
+ vec3 octahedron = v / sum;
58
+ return vec2(
59
+ octahedron.x + octahedron.z,
60
+ octahedron.z - octahedron.x
61
+ );
62
+ }
63
+
64
+ vec2 VectorToGrid(vec3 v)
65
+ {
66
+ if (uIsFullSphere) {
67
+ return VecToSphereOct(v);
68
+ } else {
69
+ return VecToHemiSphereOct(v);
70
+ }
71
+ }
72
+
73
+ vec4 TriangleInterpolate(vec2 frac_uv)
74
+ {
75
+ vec2 omuv = vec2(1.0) - frac_uv;
76
+ vec4 res;
77
+ // frame 0 weight (corner (0,0) of the quad)
78
+ res.x = min(omuv.x, omuv.y);
79
+ // frame 1 weight (off-diagonal corner — picked by res.w)
80
+ res.y = abs(frac_uv.x - frac_uv.y);
81
+ // frame 2 weight (corner (1,1) of the quad)
82
+ res.z = min(frac_uv.x, frac_uv.y);
83
+ // triangle-half mask: 1 in the lower-right half, 0 in the upper-left
84
+ res.w = clamp(ceil(frac_uv.x - frac_uv.y), 0.0, 1.0);
85
+ return res;
86
+ }
87
+
37
88
  void main() {
38
89
  vUv = uv;
39
-
40
- vec2 framesMinusOne = uFrames - vec2(1.0);
41
-
90
+
91
+ // View-aligned billboard: strip the rotation out of modelViewMatrix,
92
+ // keeping only its translation column. The card always faces the
93
+ // camera in screen space. (Step 3 will replace this with a card
94
+ // oriented to the bake direction.)
42
95
  mat4 m4 = modelViewMatrix;
43
-
96
+
44
97
  m4[0][0] = 1.0;
45
98
  m4[0][1] = 0.0;
46
99
  m4[0][2] = 0.0;
@@ -52,252 +105,104 @@ const shader_vx = `
52
105
  m4[2][0] = 0.0;
53
106
  m4[2][1] = 0.0;
54
107
  m4[2][2] = 1.0;
55
-
108
+
56
109
  vec3 object_scale = vec3(
57
110
  length(modelViewMatrix[0].xyz),
58
111
  length(modelViewMatrix[1].xyz),
59
112
  length(modelViewMatrix[2].xyz)
60
- );
61
-
113
+ );
114
+
62
115
  // scale by object's baking bounding sphere's radius
63
- float card_diameter = uRadius*2.0;
116
+ float card_diameter = uRadius * 2.0;
64
117
  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
-
118
+
119
+ vec4 mvPosition = m4 * vec4(object_scale * (position + uOffset / card_diameter), 1.0);
70
120
  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;
121
+
122
+ // Pivot-to-camera direction in the impostor's object-local space —
123
+ // the same frame the atlas was baked in. The camera sits at the
124
+ // origin in view space; inverting modelViewMatrix takes that back
125
+ // into object-local space. Constant across all vertices of the card.
126
+ vec3 cameraPos_OS = (inverse(modelViewMatrix) * vec4(0.0, 0.0, 0.0, 1.0)).xyz;
127
+ vec3 pivotToCameraRay = normalize(cameraPos_OS);
128
+
129
+ vec2 octahedral_uv = clamp(VectorToGrid(pivotToCameraRay) * 0.5 + 0.5, 0.0, 1.0);
130
+ vec2 grid = octahedral_uv * (uFrames - 1.0);
131
+ vGridFloor = floor(grid);
132
+ vWeights = TriangleInterpolate(fract(grid));
85
133
  }
86
134
  `;
87
135
  const shader_fg = `
88
136
  precision highp float;
89
137
  precision highp int;
90
-
138
+
91
139
  const float depth_scale = 0.5;
92
140
 
93
141
  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
-
142
+
143
+ // Frame indices + triangle weights are computed per-impostor in the
144
+ // vertex shader. flat = no interpolation; every fragment sees the same
145
+ // values and samples the same 3 atlas frames.
146
+ flat in vec2 vGridFloor;
147
+ flat in vec4 vWeights;
148
+
102
149
  out vec4 color_out;
103
-
150
+
104
151
  uniform sampler2D tBase;
105
152
  uniform sampler2D tGeometry;
106
153
  uniform float uFrames;
107
154
  uniform float uDepthScale;
108
- 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
-
123
- vec2 VecToSphereOct(vec3 pivotToCamera)
124
- {
125
- vec3 octant = sign(pivotToCamera);
126
-
127
- // |x| + |y| + |z| = 1
128
- float sum = dot(pivotToCamera, octant);
129
- vec3 octahedron = pivotToCamera / sum;
130
-
131
- if (octahedron.y < 0.0){
132
- vec3 absolute = abs(octahedron);
133
- octahedron.xz = octant.xz * vec2(1.0 - absolute.z, 1.0 - absolute.x);
134
- }
135
- return octahedron.xz;
136
- }
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
-
149
- return vec2(
150
- octahedron.x + octahedron.z,
151
- octahedron.z - octahedron.x
152
- );
153
- }
154
-
155
- vec2 VectorToGrid(vec3 vec)
156
- {
157
- if (uIsFullSphere)
158
- {
159
- return VecToSphereOct(vec);
160
- }
161
- else
162
- {
163
- return VecToHemiSphereOct(vec);
164
- }
165
- }
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)
187
- {
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;
195
- }
196
-
197
- vec4 ImposterBlendWeightsNearest(sampler2D tex, vec2 frame0, vec2 frame1, vec2 frame2, vec4 weights, vec4 ddxy)
198
- {
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;
214
- }
215
-
216
-
217
- void calcuateXYbasis(vec3 plane_normal, out vec3 plane_x, out vec3 plane_y)
155
+
156
+ vec4 ImposterBlendWeights(sampler2D tex, vec2 uv0, vec2 uv1, vec2 uv2, vec4 weights, vec4 ddxy)
218
157
  {
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);
224
- }
225
- plane_x = normalize(cross(plane_normal, up));
226
- plane_y = normalize(cross(plane_x, plane_normal));
158
+ vec4 samp0 = textureGrad(tex, uv0, ddxy.xy, ddxy.zw);
159
+ vec4 samp1 = textureGrad(tex, uv1, ddxy.xy, ddxy.zw);
160
+ vec4 samp2 = textureGrad(tex, uv2, ddxy.xy, ddxy.zw);
161
+
162
+ return samp0 * weights.x + samp1 * weights.y + samp2 * weights.z;
227
163
  }
228
164
 
229
- vec3 projectOnPlaneBasis(vec3 ray, vec3 plane_normal, vec3 plane_x, vec3 plane_y)
165
+ vec2 recompute_uv(vec2 frame, vec2 frame_uv, sampler2D gBuffer)
230
166
  {
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
- ));
237
- }
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
-
167
+ vec2 frame_size = vec2(1.0 / uFrames);
168
+ vec2 source_uv = (frame + frame_uv) * frame_size;
244
169
  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;
170
+
171
+ vec2 offset = (frame_uv * 2.0 - 1.0) * (0.5 - n_depth) * depth_scale;
172
+ vec2 uv_f = clamp(frame_uv + offset, 0.0, 1.0);
173
+ uv_f = (frame + uv_f) * frame_size;
174
+ return clamp(uv_f, 0.0, 1.0);
254
175
  }
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);
263
- vec2 gridFloor = floor(grid);
264
-
265
- vec4 weights = TriangleInterpolate( gridFrac );
266
-
176
+
177
+ void main() {
267
178
  vec2 frame_uv = vUv;
268
-
269
- //3 nearest frames
270
- 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
-
179
+
180
+ // 3 nearest frames — chosen in the vertex shader (vGridFloor +
181
+ // vWeights are flat, so frame0/1/2 are identical for every fragment
182
+ // of this impostor card).
183
+ vec2 frame0 = vGridFloor;
184
+ vec2 frame1 = vGridFloor + mix(vec2(0.0, 1.0), vec2(1.0, 0.0), vWeights.w);
185
+ vec2 frame2 = vGridFloor + vec2(1.0, 1.0);
186
+
274
187
  vec2 uv0 = recompute_uv(frame0, frame_uv, tGeometry);
275
188
  vec2 uv1 = recompute_uv(frame1, frame_uv, tGeometry);
276
189
  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
-
190
+
191
+ vec4 ddxy = vec4(dFdx(vUv.xy), dFdy(vUv.xy));
192
+
282
193
  vec4 texel_color = ImposterBlendWeights(
283
- // vec4 texel_color = ImposterBlendWeightsNearest(
284
194
  tBase,
285
195
  uv0,
286
196
  uv1,
287
197
  uv2,
288
- weights, ddxy
198
+ vWeights, ddxy
289
199
  );
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;
200
+
201
+ if (texel_color.a <= 0.5) {
296
202
  discard;
297
203
  }
298
-
204
+
299
205
  color_out = texel_color;
300
- // color_out = vec4( snapped_oct_uv, 1.0, 1.0);
301
206
  }
302
207
  `;
303
208
 
@@ -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();