@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 +1 -1
- package/src/core/process/worker/WorkerProxy.d.ts +39 -1
- package/src/core/process/worker/WorkerProxy.d.ts.map +1 -1
- package/src/core/process/worker/WorkerProxy.js +93 -13
- package/src/engine/graphics/ecs/mesh-v2/aggregate/prototypeSGMesh.js +1 -1
- package/src/engine/graphics/ecs/mesh-v2/sample/prototypeShadedGeometry.js +1 -1
- package/src/engine/graphics/ecs/mesh-v2/sample/prototype_sg_raycast.js +1 -1
- package/src/engine/graphics/ecs/trail2d/prototypeTrail2D.js +1 -1
- package/src/engine/graphics/impostors/octahedral/prototypeBaker.js +3 -3
- package/src/engine/graphics/impostors/octahedral/shader/ImpostorShaderV0.d.ts.map +1 -1
- package/src/engine/graphics/impostors/octahedral/shader/ImpostorShaderV0.js +266 -242
- package/src/engine/graphics/render/forward_plus/plugin/ptototypeFPPlugin.js +1 -1
- package/src/engine/graphics/sh3/path_tracer/BufferedGeometryBVH.d.ts.map +1 -1
- package/src/engine/graphics/sh3/path_tracer/BufferedGeometryBVH.js +4 -2
- package/src/engine/graphics/sh3/path_tracer/PathTracedScene.d.ts.map +1 -1
- package/src/engine/graphics/sh3/path_tracer/PathTracedScene.js +4 -2
- package/src/engine/graphics/sh3/path_tracer/prototypePathTracer.js +2 -2
- package/src/engine/intelligence/behavior/ecs/OrbitingBehavior.d.ts +63 -0
- package/src/engine/intelligence/behavior/ecs/OrbitingBehavior.d.ts.map +1 -0
- package/src/engine/intelligence/behavior/ecs/OrbitingBehavior.js +204 -0
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.
|
|
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
|
-
|
|
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":";
|
|
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
|
-
|
|
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.
|
|
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 (
|
|
230
|
-
//not running
|
|
261
|
+
if (this.__status !== WorkerStatus.RUNNING) {
|
|
231
262
|
return;
|
|
232
263
|
}
|
|
233
264
|
this.__worker.terminate();
|
|
234
|
-
this.
|
|
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 (
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
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+
|
|
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
|
-
|
|
20
|
-
|
|
19
|
+
|
|
21
20
|
out vec2 vUv;
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
out
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
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
|
|
78
|
+
|
|
79
|
+
vec2 VectorToGrid(vec3 v)
|
|
156
80
|
{
|
|
157
|
-
if (uIsFullSphere)
|
|
158
|
-
|
|
159
|
-
|
|
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
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
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
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
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
|
-
|
|
105
|
+
|
|
106
|
+
vec3 OctaHemiSphereDec(vec2 coord)
|
|
198
107
|
{
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
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
|
-
|
|
220
|
-
|
|
221
|
-
|
|
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
|
-
|
|
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
|
-
|
|
232
|
-
return normalize(
|
|
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
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
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
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
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
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
//3
|
|
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
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
//
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
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
|
-
//
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
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
|
-
|
|
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,
|
|
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
|
-
|
|
132
|
+
2
|
|
133
133
|
);
|
|
134
134
|
|
|
135
|
-
|
|
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;
|
|
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,
|
|
180
|
+
bvh.__root = ebvh_build_hierarchy(bvh, unprocessed_nodes, node_leaf_count, nodes, node_leaf_count, 3);
|
|
181
181
|
|
|
182
|
-
|
|
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 =
|
|
71
|
-
const PIXEL_RENDER_RATIO =
|
|
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
|
+
}
|