1 | import { Vector3 } from "@babylonjs/core/Maths/math";
|
2 | import { Tools } from "@babylonjs/core/Misc/tools";
|
3 | import { AnimationEvent } from "@babylonjs/core/Animations/animationEvent";
|
4 | import { Sound } from "@babylonjs/core/Audio/sound";
|
5 | import { WeightedSound } from "@babylonjs/core/Audio/weightedsound";
|
6 | import { GLTFLoader, ArrayItem } from "../glTFLoader";
|
7 | var NAME = "MSFT_audio_emitter";
|
8 |
|
9 |
|
10 |
|
11 | var MSFT_audio_emitter = (function () {
|
12 |
|
13 | function MSFT_audio_emitter(loader) {
|
14 |
|
15 | this.name = NAME;
|
16 |
|
17 | this.enabled = true;
|
18 | this._loader = loader;
|
19 | }
|
20 |
|
21 | MSFT_audio_emitter.prototype.dispose = function () {
|
22 | delete this._loader;
|
23 | delete this._clips;
|
24 | delete this._emitters;
|
25 | };
|
26 |
|
27 | MSFT_audio_emitter.prototype.onLoading = function () {
|
28 | var extensions = this._loader.gltf.extensions;
|
29 | if (extensions && extensions[this.name]) {
|
30 | var extension = extensions[this.name];
|
31 | this._clips = extension.clips;
|
32 | this._emitters = extension.emitters;
|
33 | ArrayItem.Assign(this._clips);
|
34 | ArrayItem.Assign(this._emitters);
|
35 | }
|
36 | };
|
37 |
|
38 | MSFT_audio_emitter.prototype.loadSceneAsync = function (context, scene) {
|
39 | var _this = this;
|
40 | return GLTFLoader.LoadExtensionAsync(context, scene, this.name, function (extensionContext, extension) {
|
41 | var promises = new Array();
|
42 | promises.push(_this._loader.loadSceneAsync(context, scene));
|
43 | for (var _i = 0, _a = extension.emitters; _i < _a.length; _i++) {
|
44 | var emitterIndex = _a[_i];
|
45 | var emitter = ArrayItem.Get(extensionContext + "/emitters", _this._emitters, emitterIndex);
|
46 | if (emitter.refDistance != undefined || emitter.maxDistance != undefined || emitter.rolloffFactor != undefined ||
|
47 | emitter.distanceModel != undefined || emitter.innerAngle != undefined || emitter.outerAngle != undefined) {
|
48 | throw new Error(extensionContext + ": Direction or Distance properties are not allowed on emitters attached to a scene");
|
49 | }
|
50 | promises.push(_this._loadEmitterAsync(extensionContext + "/emitters/" + emitter.index, emitter));
|
51 | }
|
52 | return Promise.all(promises).then(function () { });
|
53 | });
|
54 | };
|
55 |
|
56 | MSFT_audio_emitter.prototype.loadNodeAsync = function (context, node, assign) {
|
57 | var _this = this;
|
58 | return GLTFLoader.LoadExtensionAsync(context, node, this.name, function (extensionContext, extension) {
|
59 | var promises = new Array();
|
60 | return _this._loader.loadNodeAsync(extensionContext, node, function (babylonMesh) {
|
61 | var _loop_1 = function (emitterIndex) {
|
62 | var emitter = ArrayItem.Get(extensionContext + "/emitters", _this._emitters, emitterIndex);
|
63 | promises.push(_this._loadEmitterAsync(extensionContext + "/emitters/" + emitter.index, emitter).then(function () {
|
64 | for (var _i = 0, _a = emitter._babylonSounds; _i < _a.length; _i++) {
|
65 | var sound = _a[_i];
|
66 | sound.attachToMesh(babylonMesh);
|
67 | if (emitter.innerAngle != undefined || emitter.outerAngle != undefined) {
|
68 | sound.setLocalDirectionToMesh(Vector3.Forward());
|
69 | sound.setDirectionalCone(2 * Tools.ToDegrees(emitter.innerAngle == undefined ? Math.PI : emitter.innerAngle), 2 * Tools.ToDegrees(emitter.outerAngle == undefined ? Math.PI : emitter.outerAngle), 0);
|
70 | }
|
71 | }
|
72 | }));
|
73 | };
|
74 | for (var _i = 0, _a = extension.emitters; _i < _a.length; _i++) {
|
75 | var emitterIndex = _a[_i];
|
76 | _loop_1(emitterIndex);
|
77 | }
|
78 | assign(babylonMesh);
|
79 | }).then(function (babylonMesh) {
|
80 | return Promise.all(promises).then(function () {
|
81 | return babylonMesh;
|
82 | });
|
83 | });
|
84 | });
|
85 | };
|
86 |
|
87 | MSFT_audio_emitter.prototype.loadAnimationAsync = function (context, animation) {
|
88 | var _this = this;
|
89 | return GLTFLoader.LoadExtensionAsync(context, animation, this.name, function (extensionContext, extension) {
|
90 | return _this._loader.loadAnimationAsync(context, animation).then(function (babylonAnimationGroup) {
|
91 | var promises = new Array();
|
92 | ArrayItem.Assign(extension.events);
|
93 | for (var _i = 0, _a = extension.events; _i < _a.length; _i++) {
|
94 | var event_1 = _a[_i];
|
95 | promises.push(_this._loadAnimationEventAsync(extensionContext + "/events/" + event_1.index, context, animation, event_1, babylonAnimationGroup));
|
96 | }
|
97 | return Promise.all(promises).then(function () {
|
98 | return babylonAnimationGroup;
|
99 | });
|
100 | });
|
101 | });
|
102 | };
|
103 | MSFT_audio_emitter.prototype._loadClipAsync = function (context, clip) {
|
104 | if (clip._objectURL) {
|
105 | return clip._objectURL;
|
106 | }
|
107 | var promise;
|
108 | if (clip.uri) {
|
109 | promise = this._loader.loadUriAsync(context, clip, clip.uri);
|
110 | }
|
111 | else {
|
112 | var bufferView = ArrayItem.Get(context + "/bufferView", this._loader.gltf.bufferViews, clip.bufferView);
|
113 | promise = this._loader.loadBufferViewAsync("#/bufferViews/" + bufferView.index, bufferView);
|
114 | }
|
115 | clip._objectURL = promise.then(function (data) {
|
116 | return URL.createObjectURL(new Blob([data], { type: clip.mimeType }));
|
117 | });
|
118 | return clip._objectURL;
|
119 | };
|
120 | MSFT_audio_emitter.prototype._loadEmitterAsync = function (context, emitter) {
|
121 | var _this = this;
|
122 | emitter._babylonSounds = emitter._babylonSounds || [];
|
123 | if (!emitter._babylonData) {
|
124 | var clipPromises = new Array();
|
125 | var name_1 = emitter.name || "emitter" + emitter.index;
|
126 | var options_1 = {
|
127 | loop: false,
|
128 | autoplay: false,
|
129 | volume: emitter.volume == undefined ? 1 : emitter.volume,
|
130 | };
|
131 | var _loop_2 = function (i) {
|
132 | var clipContext = "#/extensions/" + this_1.name + "/clips";
|
133 | var clip = ArrayItem.Get(clipContext, this_1._clips, emitter.clips[i].clip);
|
134 | clipPromises.push(this_1._loadClipAsync(clipContext + "/" + emitter.clips[i].clip, clip).then(function (objectURL) {
|
135 | var sound = emitter._babylonSounds[i] = new Sound(name_1, objectURL, _this._loader.babylonScene, null, options_1);
|
136 | sound.refDistance = emitter.refDistance || 1;
|
137 | sound.maxDistance = emitter.maxDistance || 256;
|
138 | sound.rolloffFactor = emitter.rolloffFactor || 1;
|
139 | sound.distanceModel = emitter.distanceModel || 'exponential';
|
140 | sound._positionInEmitterSpace = true;
|
141 | }));
|
142 | };
|
143 | var this_1 = this;
|
144 | for (var i = 0; i < emitter.clips.length; i++) {
|
145 | _loop_2(i);
|
146 | }
|
147 | var promise = Promise.all(clipPromises).then(function () {
|
148 | var weights = emitter.clips.map(function (clip) { return clip.weight || 1; });
|
149 | var weightedSound = new WeightedSound(emitter.loop || false, emitter._babylonSounds, weights);
|
150 | if (emitter.innerAngle) {
|
151 | weightedSound.directionalConeInnerAngle = 2 * Tools.ToDegrees(emitter.innerAngle);
|
152 | }
|
153 | if (emitter.outerAngle) {
|
154 | weightedSound.directionalConeOuterAngle = 2 * Tools.ToDegrees(emitter.outerAngle);
|
155 | }
|
156 | if (emitter.volume) {
|
157 | weightedSound.volume = emitter.volume;
|
158 | }
|
159 | emitter._babylonData.sound = weightedSound;
|
160 | });
|
161 | emitter._babylonData = {
|
162 | loaded: promise
|
163 | };
|
164 | }
|
165 | return emitter._babylonData.loaded;
|
166 | };
|
167 | MSFT_audio_emitter.prototype._getEventAction = function (context, sound, action, time, startOffset) {
|
168 | switch (action) {
|
169 | case "play" : {
|
170 | return function (currentFrame) {
|
171 | var frameOffset = (startOffset || 0) + (currentFrame - time);
|
172 | sound.play(frameOffset);
|
173 | };
|
174 | }
|
175 | case "stop" : {
|
176 | return function (currentFrame) {
|
177 | sound.stop();
|
178 | };
|
179 | }
|
180 | case "pause" : {
|
181 | return function (currentFrame) {
|
182 | sound.pause();
|
183 | };
|
184 | }
|
185 | default: {
|
186 | throw new Error(context + ": Unsupported action " + action);
|
187 | }
|
188 | }
|
189 | };
|
190 | MSFT_audio_emitter.prototype._loadAnimationEventAsync = function (context, animationContext, animation, event, babylonAnimationGroup) {
|
191 | var _this = this;
|
192 | if (babylonAnimationGroup.targetedAnimations.length == 0) {
|
193 | return Promise.resolve();
|
194 | }
|
195 | var babylonAnimation = babylonAnimationGroup.targetedAnimations[0];
|
196 | var emitterIndex = event.emitter;
|
197 | var emitter = ArrayItem.Get("#/extensions/" + this.name + "/emitters", this._emitters, emitterIndex);
|
198 | return this._loadEmitterAsync(context, emitter).then(function () {
|
199 | var sound = emitter._babylonData.sound;
|
200 | if (sound) {
|
201 | var babylonAnimationEvent = new AnimationEvent(event.time, _this._getEventAction(context, sound, event.action, event.time, event.startOffset));
|
202 | babylonAnimation.animation.addEvent(babylonAnimationEvent);
|
203 |
|
204 | babylonAnimationGroup.onAnimationGroupEndObservable.add(function () {
|
205 | sound.stop();
|
206 | });
|
207 | babylonAnimationGroup.onAnimationGroupPauseObservable.add(function () {
|
208 | sound.pause();
|
209 | });
|
210 | }
|
211 | });
|
212 | };
|
213 | return MSFT_audio_emitter;
|
214 | }());
|
215 | export { MSFT_audio_emitter };
|
216 | GLTFLoader.RegisterExtension(NAME, function (loader) { return new MSFT_audio_emitter(loader); });
|
217 |
|
\ | No newline at end of file |