@woosh/meep-engine 2.48.1 → 2.48.5
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/binary/32BitEncoder.js +11 -3
- package/src/core/binary/BinaryBuffer.js +80 -59
- package/src/core/binary/int32_to_binary_string.js +32 -7
- package/src/core/binary/to_half_float_uint16.js +6 -4
- package/src/core/bvh2/visual/convert_bvh_to_dot_format_string.js +1 -1
- package/src/core/collection/list/List.js +3 -0
- package/src/core/geom/3d/CircleMath.js +13 -2
- package/src/core/geom/3d/matrix/m4_make_translation.js +15 -0
- package/src/core/geom/3d/triangle/computeTrianglePlaneSide.js +4 -13
- package/src/core/geom/Vector3.spec.js +10 -0
- package/src/core/graph/convertGraphToDotString.js +13 -3
- package/src/core/graph/v2/Graph.js +17 -1
- package/src/core/graph/v2/NodeContainer.js +58 -0
- package/src/core/model/ModuleRegistry.js +44 -10
- package/src/core/model/ResourceAccessKind.js +11 -0
- package/src/core/model/ResourceAccessSpecification.js +41 -0
- package/src/engine/Engine.js +1 -1
- package/src/engine/ecs/EntityEventBinding.js +74 -0
- package/src/engine/ecs/EntityManager.js +143 -0
- package/src/engine/ecs/System.js +53 -5
- package/src/engine/ecs/animation/InverseKinematicsSystem.js +6 -0
- package/src/engine/ecs/attachment/AttachmentSystem.js +8 -0
- package/src/engine/ecs/dynamic_actions/DynamicActorSystem.js +5 -0
- package/src/engine/ecs/fow/FogOfWarRevealerSystem.js +7 -3
- package/src/engine/ecs/fow/FogOfWarSystem.js +6 -0
- package/src/engine/ecs/gui/GUIElementSystem.js +1 -1
- package/src/engine/ecs/gui/hud/HeadsUpDisplaySystem.js +8 -0
- package/src/engine/ecs/gui/position/ViewportPositionSystem.js +6 -0
- package/src/engine/ecs/speaker/VoiceSystem.js +13 -0
- package/src/engine/ecs/storage/binary/object/BinaryObjectSerializationAdapter2.js +451 -12
- package/src/engine/ecs/storage/binary/object/BinaryObjectSerializationAdapter2.spec.js +123 -0
- package/src/engine/ecs/system/computeSystemComponentDependencyGraph.js +71 -0
- package/src/engine/ecs/systems/RenderSystem.js +6 -0
- package/src/engine/ecs/terrain/ecs/Terrain.js +26 -44
- package/src/engine/ecs/terrain/ecs/cling/ClingToTerrainSystem.js +9 -0
- package/src/engine/ecs/terrain/tiles/TerrainTile.js +5 -0
- package/src/engine/ecs/terrain/tiles/TerrainTileManager.js +26 -6
- package/src/engine/ecs/tooltip/TooltipComponentSystem.js +7 -0
- package/src/engine/graphics/ecs/animation/animator/AnimationGraphSystem.js +6 -0
- package/src/engine/graphics/ecs/camera/CameraSystem.js +6 -0
- package/src/engine/graphics/ecs/camera/pp/PerfectPanner.js +4 -0
- package/src/engine/graphics/ecs/camera/topdown/TopDownCameraControllerSystem.js +5 -0
- package/src/engine/graphics/ecs/camera/topdown/TopDownCameraLanderSystem.js +6 -0
- package/src/engine/graphics/ecs/light/LightSystem.js +8 -0
- package/src/engine/graphics/ecs/mesh/MeshSystem.js +8 -1
- package/src/engine/graphics/ecs/path/PathDisplaySystem.js +7 -1
- package/src/engine/graphics/ecs/trail2d/Trail2DSystem.js +7 -0
- package/src/engine/graphics/geometry/instancing/InstancedMeshGroup.js +34 -7
- package/src/engine/graphics/particles/ecs/ParticleEmitterSystem.js +6 -0
- package/src/engine/grid/systems/GridPosition2TransformSystem.js +6 -0
- package/src/engine/grid/transform2grid/Transform2GridPositionSystem.js +6 -0
- package/src/engine/intelligence/behavior/Behavior.js +11 -0
- package/src/engine/intelligence/behavior/composite/CompositeBehavior.js +10 -4
- package/src/engine/intelligence/behavior/decorator/AbstractDecoratorBehavior.js +1 -0
- package/src/engine/intelligence/behavior/util/behavior_traverse_tree.js +8 -0
- package/src/engine/navigation/ecs/path_following/PathFollowingSystem.js +8 -0
- package/src/engine/network/RemoteController.js +60 -84
- package/src/engine/network/remoteEditor.js +108 -2
- package/src/engine/sound/SoundEngine.js +81 -79
- package/src/engine/sound/ecs/SoundControllerSystem.js +8 -0
- package/src/engine/sound/ecs/SoundListenerSystem.js +7 -0
- package/src/engine/sound/ecs/emitter/SoundEmitterComponentContext.js +42 -46
- package/src/engine/sound/ecs/emitter/SoundEmitterSystem.js +15 -2
- package/src/engine/sound/ecs/emitter/SoundTrack.js +137 -35
- package/src/engine/sound/ecs/emitter/SoundTrackFlags.js +17 -1
- package/src/engine/sound/ecs/emitter/SoundTrackNodes.js +2 -4
- package/src/engine/sound/ecs/emitter/loadSoundTrackAsset.js +1 -2
- package/src/generation/theme/ThemeEngine.js +20 -3
- package/src/view/{elements/image → common}/HTMLElementCacheKey.js +5 -5
- package/src/view/elements/button/ButtonView.js +10 -2
- package/src/view/elements/image/ImageView.js +1 -1
- package/src/view/elements/video/VideoView.js +1 -1
- package/src/view/interaction/CommandButtonView.js +16 -153
- package/src/view/interaction/createInterfaceCommandButton.js +124 -0
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { assert } from "../../../../core/assert.js";
|
|
2
|
+
|
|
1
3
|
/**
|
|
2
4
|
*
|
|
3
5
|
* @param {Behavior|CompositeBehavior|AbstractDecoratorBehavior} root
|
|
@@ -6,6 +8,12 @@
|
|
|
6
8
|
*/
|
|
7
9
|
export function behavior_traverse_tree(root, visitor, thisArg) {
|
|
8
10
|
|
|
11
|
+
assert.defined(root, 'root');
|
|
12
|
+
assert.notNull(root, 'root');
|
|
13
|
+
assert.equal(root.isBehavior, true, 'root.isBehavior !== true');
|
|
14
|
+
|
|
15
|
+
assert.isFunction(visitor, 'visitor');
|
|
16
|
+
|
|
9
17
|
const should_continue = visitor.call(thisArg, root);
|
|
10
18
|
|
|
11
19
|
if (should_continue === false) {
|
|
@@ -13,6 +13,8 @@ import { Transform } from '../../../ecs/transform/Transform.js';
|
|
|
13
13
|
import Vector3 from "../../../../core/geom/Vector3.js";
|
|
14
14
|
import { PathFollowerEventType } from "./PathFollowerEventType.js";
|
|
15
15
|
import { PathFollowerFlags } from "./PathFollowerFlags.js";
|
|
16
|
+
import { ResourceAccessSpecification } from "../../../../core/model/ResourceAccessSpecification.js";
|
|
17
|
+
import { ResourceAccessKind } from "../../../../core/model/ResourceAccessKind.js";
|
|
16
18
|
|
|
17
19
|
const v3_forward = new Vector3();
|
|
18
20
|
const v3_temp1 = new Vector3();
|
|
@@ -127,6 +129,12 @@ class PathFollowingSystem extends System {
|
|
|
127
129
|
|
|
128
130
|
this.dependencies = [PathFollower];
|
|
129
131
|
|
|
132
|
+
this.components_used = [
|
|
133
|
+
ResourceAccessSpecification.from(PathFollower, ResourceAccessKind.Read | ResourceAccessKind.Write),
|
|
134
|
+
ResourceAccessSpecification.from(Transform, ResourceAccessKind.Read | ResourceAccessKind.Write),
|
|
135
|
+
ResourceAccessSpecification.from(Path, ResourceAccessKind.Read),
|
|
136
|
+
];
|
|
137
|
+
|
|
130
138
|
/**
|
|
131
139
|
*
|
|
132
140
|
* @type {number}
|
|
@@ -1,138 +1,114 @@
|
|
|
1
1
|
import { BinaryBuffer } from "../../core/binary/BinaryBuffer.js";
|
|
2
|
-
import
|
|
3
|
-
import { BinaryObjectSerializationAdapter } from "../ecs/storage/binary/object/BinaryObjectSerializationAdapter.js";
|
|
2
|
+
import { assert } from "../../core/assert.js";
|
|
4
3
|
|
|
5
|
-
|
|
4
|
+
/*
|
|
5
|
+
packet header structure
|
|
6
|
+
- TAG : 1 x utf8 string
|
|
7
|
+
- ID : 1 x uint32
|
|
8
|
+
- PAYLOAD : N x bytes
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
export class RemoteController {
|
|
6
12
|
/**
|
|
7
13
|
*
|
|
8
|
-
* @type {
|
|
14
|
+
* @type {WebSocket|null}
|
|
9
15
|
*/
|
|
10
|
-
|
|
16
|
+
#socket = null;
|
|
11
17
|
|
|
12
|
-
|
|
18
|
+
#handlers = {};
|
|
13
19
|
|
|
14
|
-
|
|
15
|
-
return this.engine.binarySerializationRegistry;
|
|
16
|
-
}
|
|
20
|
+
#bound_event_handler = this.#handle_message_event.bind(this)
|
|
17
21
|
|
|
18
22
|
/**
|
|
19
23
|
*
|
|
20
|
-
* @
|
|
21
|
-
* @
|
|
24
|
+
* @param {string} tag
|
|
25
|
+
* @param {function(BinaryBuffer):Promise} handler
|
|
26
|
+
* @param {*} thisArg
|
|
22
27
|
*/
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* Server sent us a new world state
|
|
29
|
-
* @param {BinaryBuffer} buffer
|
|
30
|
-
*/
|
|
31
|
-
async writeCurrentSceneDataset(buffer) {
|
|
32
|
-
const deSerializer = new BinaryBufferDeSerializer();
|
|
33
|
-
|
|
34
|
-
const engine = this.engine;
|
|
35
|
-
|
|
36
|
-
deSerializer.registry = this._registry;
|
|
28
|
+
addHandler(tag, handler, thisArg) {
|
|
29
|
+
const existing = this.#handlers[tag];
|
|
37
30
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
const task = deSerializer.process(buffer, engine, ecd);
|
|
43
|
-
|
|
44
|
-
const p = task.promise();
|
|
45
|
-
|
|
46
|
-
engine.executor.run(task);
|
|
31
|
+
if (existing !== undefined) {
|
|
32
|
+
throw new Error(`Handler for tag '${tag}' already exists`);
|
|
33
|
+
}
|
|
47
34
|
|
|
48
|
-
|
|
35
|
+
this.#handlers[tag] = {
|
|
36
|
+
method: handler,
|
|
37
|
+
scope: thisArg
|
|
38
|
+
};
|
|
49
39
|
}
|
|
50
40
|
|
|
51
41
|
/**
|
|
52
|
-
*
|
|
53
|
-
* @param {
|
|
54
|
-
* @
|
|
42
|
+
* NOTE: entire buffer contents will be sent up to current position
|
|
43
|
+
* @param {string} tag
|
|
44
|
+
* @param {BinaryBuffer} data
|
|
55
45
|
*/
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
const entity_id = buffer.readUintVar();
|
|
46
|
+
send(tag, data) {
|
|
47
|
+
assert.isString(tag, 'tag');
|
|
60
48
|
|
|
61
|
-
|
|
62
|
-
}
|
|
49
|
+
const buffer = new BinaryBuffer();
|
|
63
50
|
|
|
64
|
-
|
|
65
|
-
const entity_id = buffer.readUintVar();
|
|
51
|
+
buffer.writeUTF8String(tag);
|
|
66
52
|
|
|
67
|
-
|
|
68
|
-
const component_instance = this._object_serde.deserialize(buffer);
|
|
53
|
+
buffer.writeBytes(data.raw_bytes, 0, data.position);
|
|
69
54
|
|
|
70
|
-
|
|
55
|
+
buffer.trim();
|
|
71
56
|
|
|
57
|
+
this.#socket.send(buffer.data);
|
|
72
58
|
}
|
|
73
59
|
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
export class RemoteController {
|
|
77
60
|
/**
|
|
78
61
|
*
|
|
79
|
-
* @
|
|
80
|
-
|
|
81
|
-
socket = null;
|
|
82
|
-
|
|
83
|
-
localAPI = new LocalAPI();
|
|
84
|
-
|
|
85
|
-
/**
|
|
86
|
-
*
|
|
87
|
-
* @param {Engine} engine
|
|
62
|
+
* @param {MessageEvent} event
|
|
63
|
+
* @private
|
|
88
64
|
*/
|
|
89
|
-
|
|
90
|
-
this.localAPI.engine = engine;
|
|
65
|
+
async #handle_message_event(event) {
|
|
91
66
|
|
|
92
|
-
|
|
67
|
+
/**
|
|
68
|
+
* @type {ArrayBuffer}
|
|
69
|
+
*/
|
|
70
|
+
const data = event.data;
|
|
93
71
|
|
|
94
|
-
/**
|
|
95
|
-
*
|
|
96
|
-
* @param {ArrayBuffer} data
|
|
97
|
-
* @private
|
|
98
|
-
*/
|
|
99
|
-
async __handle_message(data) {
|
|
100
72
|
// console.log(`received message `, data);
|
|
101
73
|
|
|
102
|
-
const buffer =
|
|
74
|
+
const buffer = BinaryBuffer.fromArrayBuffer(data);
|
|
103
75
|
|
|
104
76
|
// read packet ID
|
|
105
77
|
const packet_id = buffer.readUTF8String();
|
|
106
78
|
|
|
107
|
-
|
|
79
|
+
const handler = this.#handlers[packet_id];
|
|
108
80
|
|
|
109
|
-
if (
|
|
110
|
-
console.error(`
|
|
81
|
+
if (handler === undefined) {
|
|
82
|
+
console.error(`No handler for packet '${packet_id}'`);
|
|
111
83
|
return;
|
|
112
84
|
}
|
|
113
85
|
|
|
114
86
|
try {
|
|
115
|
-
await
|
|
87
|
+
await handler.method.call(handler.scope, buffer);
|
|
116
88
|
} catch (e) {
|
|
117
89
|
//
|
|
118
90
|
console.error(`Failed to process remote call to '${packet_id}':`, e);
|
|
119
91
|
}
|
|
120
92
|
}
|
|
121
93
|
|
|
94
|
+
/**
|
|
95
|
+
*
|
|
96
|
+
* @param {WebSocket} v
|
|
97
|
+
*/
|
|
98
|
+
set socket(v) {
|
|
99
|
+
if (this.#socket !== null) {
|
|
100
|
+
// TODO unbind socket
|
|
101
|
+
}
|
|
122
102
|
|
|
123
|
-
|
|
124
|
-
this.socket = new WebSocket('ws://localhost:9020');
|
|
103
|
+
this.#socket = v;
|
|
125
104
|
|
|
126
|
-
this
|
|
105
|
+
this.#socket.binaryType = "arraybuffer";
|
|
127
106
|
|
|
128
|
-
this
|
|
107
|
+
this.#socket.addEventListener('open', () => {
|
|
129
108
|
console.log('socket connection open');
|
|
130
109
|
});
|
|
131
110
|
|
|
132
|
-
this
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
this.__handle_message(event.data);
|
|
136
|
-
});
|
|
111
|
+
this.#socket.addEventListener('message', this.#bound_event_handler);
|
|
137
112
|
}
|
|
113
|
+
|
|
138
114
|
}
|
|
@@ -2,9 +2,96 @@ import { EngineHarness } from "../EngineHarness.js";
|
|
|
2
2
|
import { RemoteController } from "./RemoteController.js";
|
|
3
3
|
import { ShadedGeometrySystem } from "../graphics/ecs/mesh-v2/ShadedGeometrySystem.js";
|
|
4
4
|
import { initializeGameBinarySerializationRegistry } from "../../../../model/game/GameBinarySerializationRegistry.js";
|
|
5
|
+
import { BinaryObjectSerializationAdapter } from "../ecs/storage/binary/object/BinaryObjectSerializationAdapter.js";
|
|
6
|
+
import BinaryBufferDeSerializer from "../ecs/storage/BinaryBufferDeSerializer.js";
|
|
5
7
|
|
|
6
8
|
const harness = new EngineHarness();
|
|
7
9
|
|
|
10
|
+
class LocalAPI {
|
|
11
|
+
/**
|
|
12
|
+
*
|
|
13
|
+
* @type {Engine|null}
|
|
14
|
+
*/
|
|
15
|
+
engine = null;
|
|
16
|
+
|
|
17
|
+
_object_serde = new BinaryObjectSerializationAdapter();
|
|
18
|
+
|
|
19
|
+
get _registry() {
|
|
20
|
+
return this.engine.binarySerializationRegistry;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
*
|
|
25
|
+
* @return {EntityComponentDataset}
|
|
26
|
+
* @private
|
|
27
|
+
*/
|
|
28
|
+
get _current_dataset() {
|
|
29
|
+
return this.engine.entityManager.dataset;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Server sent us a new world state
|
|
34
|
+
* @param {BinaryBuffer} buffer
|
|
35
|
+
*/
|
|
36
|
+
async writeCurrentSceneDataset(buffer) {
|
|
37
|
+
const deSerializer = new BinaryBufferDeSerializer();
|
|
38
|
+
|
|
39
|
+
const engine = this.engine;
|
|
40
|
+
|
|
41
|
+
deSerializer.registry = this._registry;
|
|
42
|
+
|
|
43
|
+
const ecd = this._current_dataset;
|
|
44
|
+
|
|
45
|
+
ecd.clear();
|
|
46
|
+
|
|
47
|
+
const task = deSerializer.process(buffer, engine, ecd);
|
|
48
|
+
|
|
49
|
+
const p = task.promise();
|
|
50
|
+
|
|
51
|
+
engine.executor.run(task);
|
|
52
|
+
|
|
53
|
+
await p;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
*
|
|
58
|
+
* @param {BinaryBuffer} buffer
|
|
59
|
+
* @return {Promise<void>}
|
|
60
|
+
*/
|
|
61
|
+
async createEntity(buffer) {
|
|
62
|
+
// create entity on the current scene with a given ID
|
|
63
|
+
|
|
64
|
+
const entity_id = buffer.readUintVar();
|
|
65
|
+
|
|
66
|
+
this._current_dataset.createEntitySpecific(entity_id);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
async addComponentToEntity(buffer) {
|
|
70
|
+
const entity_id = buffer.readUintVar();
|
|
71
|
+
|
|
72
|
+
this._object_serde.registry = this._registry;
|
|
73
|
+
const component_instance = this._object_serde.deserialize(buffer);
|
|
74
|
+
|
|
75
|
+
this._current_dataset.addComponentToEntity(entity_id, component_instance);
|
|
76
|
+
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
*
|
|
83
|
+
* @param {RemoteController} controller
|
|
84
|
+
* @param {Object} api
|
|
85
|
+
*/
|
|
86
|
+
function attachLocalAPI(controller, api) {
|
|
87
|
+
for (const apiKey in api) {
|
|
88
|
+
|
|
89
|
+
controller.addHandler(apiKey, api[apiKey], api);
|
|
90
|
+
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
}
|
|
94
|
+
|
|
8
95
|
/**
|
|
9
96
|
*
|
|
10
97
|
* @param {Engine} engine
|
|
@@ -20,9 +107,28 @@ async function main(engine) {
|
|
|
20
107
|
|
|
21
108
|
const remote = new RemoteController();
|
|
22
109
|
|
|
23
|
-
remote.attach(engine);
|
|
24
|
-
remote.startup();
|
|
25
110
|
|
|
111
|
+
const api = new LocalAPI();
|
|
112
|
+
api.engine = engine;
|
|
113
|
+
|
|
114
|
+
Object.getOwnPropertyNames(Object.getPrototypeOf(api))
|
|
115
|
+
.forEach(name => {
|
|
116
|
+
if (name.startsWith("_")) {
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
if (name === "constructor") {
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
if (typeof api[name] !== "function") {
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
remote.addHandler(name, api[name], api);
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
remote.socket = new WebSocket('ws://localhost:9020');
|
|
26
132
|
}
|
|
27
133
|
|
|
28
134
|
harness.initialize({
|
|
@@ -4,93 +4,95 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
7
|
+
class SoundEngine {
|
|
8
|
+
/**
|
|
9
|
+
*
|
|
10
|
+
* @constructor
|
|
11
|
+
*/
|
|
12
|
+
constructor() {
|
|
13
|
+
|
|
14
|
+
// Fix up prefixing
|
|
15
|
+
let AudioContext = (
|
|
16
|
+
window.AudioContext ||
|
|
17
|
+
null
|
|
18
|
+
);
|
|
19
|
+
|
|
20
|
+
if (!AudioContext) {
|
|
21
|
+
throw new Error("AudioContext not supported!");
|
|
22
|
+
}
|
|
12
23
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
24
|
+
// Create a new audio context.
|
|
25
|
+
const context = this.context = new AudioContext({
|
|
26
|
+
/*
|
|
27
|
+
* The type of playback that the context will be used for, as a value from the AudioContextLatencyCategory enum or a double-precision floating-point value indicating the preferred maximum latency of the context in seconds. The user agent may or may not choose to meet this request; check the value of AudioContext.baseLatency to determine the true latency after creating the context.
|
|
28
|
+
*
|
|
29
|
+
* Values:
|
|
30
|
+
*
|
|
31
|
+
* "balanced" The user agent should balance audio output latency and power consumption when selecting a latency value.
|
|
32
|
+
* "interactive" The audio is involved in interactive elements, such as responding to user actions or needing to coincide with visual cues such as a video or game action. The user agent should select the lowest possible latency that doesn't cause glitches in the audio. This is likely to require increased power consumption. This is the default value.
|
|
33
|
+
* "playback" The user agent should select a latency that will maximize playback time by minimizing power consumption at the expense of latency. Useful for non-interactive playback, such as playing music.
|
|
34
|
+
*/
|
|
35
|
+
latencyHint: 'interactive'
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
//start suspended to keep chrome satisfied, otherwise it prints a warning
|
|
39
|
+
context.suspend();
|
|
40
|
+
|
|
41
|
+
// master volume
|
|
42
|
+
const gainNode = context.createGain();
|
|
43
|
+
|
|
44
|
+
// Dynamic compressor makes quiet sounds louder and loud quieter
|
|
45
|
+
const compressor = context.createDynamicsCompressor();
|
|
46
|
+
this.destination = gainNode;
|
|
47
|
+
gainNode.connect(compressor);
|
|
48
|
+
compressor.connect(context.destination);
|
|
49
|
+
|
|
50
|
+
// master channel
|
|
51
|
+
Object.defineProperties(this, {
|
|
52
|
+
volume: {
|
|
53
|
+
get: function () {
|
|
54
|
+
return gainNode.gain.value;
|
|
55
|
+
},
|
|
56
|
+
set: function (val) {
|
|
57
|
+
gainNode.gain.setValueAtTime(val, context.currentTime, 0);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
}
|
|
19
62
|
|
|
20
|
-
|
|
21
|
-
|
|
63
|
+
/**
|
|
64
|
+
*
|
|
65
|
+
* @return {number}
|
|
66
|
+
*/
|
|
67
|
+
getCurrentTime() {
|
|
68
|
+
return this.context.currentTime;
|
|
22
69
|
}
|
|
23
70
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
* "balanced" The user agent should balance audio output latency and power consumption when selecting a latency value.
|
|
32
|
-
* "interactive" The audio is involved in interactive elements, such as responding to user actions or needing to coincide with visual cues such as a video or game action. The user agent should select the lowest possible latency that doesn't cause glitches in the audio. This is likely to require increased power consumption. This is the default value.
|
|
33
|
-
* "playback" The user agent should select a latency that will maximize playback time by minimizing power consumption at the expense of latency. Useful for non-interactive playback, such as playing music.
|
|
34
|
-
*/
|
|
35
|
-
latencyHint: 'interactive'
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
//start suspended to keep chrome satisfied, otherwise it prints a warning
|
|
39
|
-
context.suspend();
|
|
40
|
-
|
|
41
|
-
// master volume
|
|
42
|
-
const gainNode = context.createGain();
|
|
43
|
-
|
|
44
|
-
// Dynamic compressor makes quiet sounds louder and loud quieter
|
|
45
|
-
const compressor = context.createDynamicsCompressor();
|
|
46
|
-
this.destination = gainNode;
|
|
47
|
-
gainNode.connect(compressor);
|
|
48
|
-
compressor.connect(context.destination);
|
|
49
|
-
|
|
50
|
-
// master channel
|
|
51
|
-
Object.defineProperties(this, {
|
|
52
|
-
volume: {
|
|
53
|
-
get: function () {
|
|
54
|
-
return gainNode.gain.value;
|
|
55
|
-
},
|
|
56
|
-
set: function (val) {
|
|
57
|
-
gainNode.gain.setValueAtTime(val, context.currentTime, 0);
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
});
|
|
61
|
-
}
|
|
71
|
+
/**
|
|
72
|
+
* Audio context needs to be resumed as it is created in suspended state by default
|
|
73
|
+
* @see https://developers.google.com/web/updates/2017/09/autoplay-policy-changes#webaudio
|
|
74
|
+
* @returns {Promise<void>}
|
|
75
|
+
*/
|
|
76
|
+
resumeContext() {
|
|
77
|
+
const pResumed = this.context.resume();
|
|
62
78
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
* @return {number}
|
|
66
|
-
*/
|
|
67
|
-
SoundEngine.prototype.getCurrentTime = function () {
|
|
68
|
-
return this.context.currentTime;
|
|
69
|
-
}
|
|
79
|
+
return pResumed;
|
|
80
|
+
}
|
|
70
81
|
|
|
71
|
-
/**
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
SoundEngine.prototype.resumeContext = function () {
|
|
77
|
-
const pResumed = this.context.resume();
|
|
82
|
+
/**
|
|
83
|
+
*
|
|
84
|
+
* @returns {Promise<any>}
|
|
85
|
+
*/
|
|
86
|
+
start() {
|
|
78
87
|
|
|
79
|
-
|
|
80
|
-
|
|
88
|
+
return new Promise((resolve, reject) => {
|
|
89
|
+
//don't want for context to be resumed
|
|
90
|
+
const promise = this.resumeContext();
|
|
81
91
|
|
|
82
|
-
/**
|
|
83
|
-
*
|
|
84
|
-
* @returns {Promise<any>}
|
|
85
|
-
*/
|
|
86
|
-
SoundEngine.prototype.start = function () {
|
|
87
|
-
|
|
88
|
-
return new Promise((resolve, reject) => {
|
|
89
|
-
//don't want for context to be resumed
|
|
90
|
-
this.resumeContext();
|
|
91
92
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
}
|
|
93
|
+
resolve();
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
}
|
|
95
97
|
|
|
96
98
|
export default SoundEngine;
|
|
@@ -7,6 +7,8 @@ import { System } from '../../ecs/System.js';
|
|
|
7
7
|
import { SoundEmitter } from './emitter/SoundEmitter.js';
|
|
8
8
|
import SoundController from './SoundController.js';
|
|
9
9
|
import { SoundTrack } from "./emitter/SoundTrack.js";
|
|
10
|
+
import { ResourceAccessSpecification } from "../../../core/model/ResourceAccessSpecification.js";
|
|
11
|
+
import { ResourceAccessKind } from "../../../core/model/ResourceAccessKind.js";
|
|
10
12
|
|
|
11
13
|
/**
|
|
12
14
|
*
|
|
@@ -30,6 +32,12 @@ class SoundControllerSystem extends System {
|
|
|
30
32
|
constructor() {
|
|
31
33
|
super();
|
|
32
34
|
this.dependencies = [SoundController];
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
this.components_used = [
|
|
38
|
+
ResourceAccessSpecification.from(SoundEmitter, ResourceAccessKind.Write)
|
|
39
|
+
];
|
|
40
|
+
|
|
33
41
|
this.removers = [];
|
|
34
42
|
}
|
|
35
43
|
|
|
@@ -10,6 +10,8 @@ import { browserInfo } from "../../Platform.js";
|
|
|
10
10
|
import Vector3 from "../../../core/geom/Vector3.js";
|
|
11
11
|
import { noop } from "../../../core/function/Functions.js";
|
|
12
12
|
import { assert } from "../../../core/assert.js";
|
|
13
|
+
import { ResourceAccessSpecification } from "../../../core/model/ResourceAccessSpecification.js";
|
|
14
|
+
import { ResourceAccessKind } from "../../../core/model/ResourceAccessKind.js";
|
|
13
15
|
|
|
14
16
|
const v3 = new Vector3();
|
|
15
17
|
const v3_up = new Vector3();
|
|
@@ -92,6 +94,11 @@ class SoundListenerSystem extends System {
|
|
|
92
94
|
assert.isInstanceOf(context, AudioContext, 'context', 'AudioContext');
|
|
93
95
|
|
|
94
96
|
this.dependencies = [SoundListener, Transform];
|
|
97
|
+
|
|
98
|
+
this.components_used = [
|
|
99
|
+
ResourceAccessSpecification.from(SoundListener, ResourceAccessKind.Read|ResourceAccessKind.Write)
|
|
100
|
+
];
|
|
101
|
+
|
|
95
102
|
//
|
|
96
103
|
this.webAudioContext = context;
|
|
97
104
|
|