@polygonjs/polygonjs
Version:
node-based WebGL 3D engine https://polygonjs.com
219 lines (218 loc) • 7.65 kB
JavaScript
"use strict";
import { TypedObjNode } from "./_Base";
import { Group } from "three";
import { TransformedParamConfig, TransformController } from "./utils/TransformController";
import { FlagsControllerD } from "../utils/FlagsController";
import { NodeParamsConfig, ParamConfig } from "../utils/params/ParamsConfig";
import { HierarchyController } from "./utils/HierarchyController";
import { ObjType } from "../../poly/registers/nodes/types/Obj";
import { NodeContext } from "../../poly/NodeContext";
import { isBooleanTrue } from "../../../core/BooleanValue";
import { CorePositionalAudio, DistanceModel, DISTANCE_MODELS } from "../../../core/audio/PositionalAudio";
import { CorePositionalAudioHelper } from "../../../core/audio/PositionalAudioHelper";
const paramCallback = () => {
return {
cook: false,
callback: (node) => {
PositionalAudioObjNode.PARAM_CALLBACK_updatePositionalAudio(node);
}
};
};
class PositionalAudioParamConfig extends TransformedParamConfig(NodeParamsConfig) {
constructor() {
super(...arguments);
this.audio = ParamConfig.FOLDER();
/** @param audio node */
this.audioNode = ParamConfig.NODE_PATH("", {
nodeSelection: {
context: NodeContext.AUDIO
}
// dependentOnFoundNode: false,
});
/** @param refDistance. See https://developer.mozilla.org/en-US/docs/Web/API/PannerNode/refDistance*/
this.refDistance = ParamConfig.FLOAT(10, {
range: [0, 10],
rangeLocked: [true, false],
...paramCallback()
});
/** @param rolloffFactor. See https://developer.mozilla.org/en-US/docs/Web/API/PannerNode/rolloffFactor */
this.rolloffFactor = ParamConfig.FLOAT(10, {
range: [0, 10],
rangeLocked: [true, false],
...paramCallback()
});
/** @param maxDistance. See https://developer.mozilla.org/en-US/docs/Web/API/PannerNode/maxDistance */
this.maxDistance = ParamConfig.FLOAT(100, {
range: [1e-3, 100],
rangeLocked: [true, false],
...paramCallback()
});
/** @param distanceModel. See https://developer.mozilla.org/en-US/docs/Web/API/PannerNode/distanceModel */
this.distanceModel = ParamConfig.INTEGER(DISTANCE_MODELS.indexOf(DistanceModel.LINEAR), {
menu: {
entries: DISTANCE_MODELS.map((name, value) => {
return { name, value };
})
},
...paramCallback()
});
/** @param coneInnerAngle. See https://developer.mozilla.org/en-US/docs/Web/API/PannerNode */
this.coneInnerAngle = ParamConfig.FLOAT(360, {
range: [0, 360],
rangeLocked: [true, true],
...paramCallback()
});
/** @param coneOuterAngle. See https://developer.mozilla.org/en-US/docs/Web/API/PannerNode */
this.coneOuterAngle = ParamConfig.FLOAT(230, {
range: [0, 360],
rangeLocked: [true, true],
...paramCallback()
});
/** @param coneOuterGain. See https://developer.mozilla.org/en-US/docs/Web/API/PannerNode */
this.coneOuterGain = ParamConfig.FLOAT(0.1, {
range: [0, 1],
rangeLocked: [true, true],
...paramCallback()
});
/** @param ensures the transform of the audio listener is updated on very frame */
this.listenerTransformAutoUpdate = ParamConfig.BOOLEAN(1);
/** @param show helper */
this.showHelper = ParamConfig.BOOLEAN(1);
/** @param helper size */
this.helperSize = ParamConfig.FLOAT(1, {
range: [0, 10],
rangeLocked: [true, false],
visibleIf: { showHelper: true },
...paramCallback()
});
}
}
const ParamsConfig = new PositionalAudioParamConfig();
function createPositionalAudio() {
const positionalAudio = new CorePositionalAudio();
positionalAudio.matrixAutoUpdate = false;
return positionalAudio;
}
export class PositionalAudioObjNode extends TypedObjNode {
constructor() {
super(...arguments);
this.paramsConfig = ParamsConfig;
this.hierarchyController = new HierarchyController(this);
this.transformController = new TransformController(this);
this.flags = new FlagsControllerD(this);
this._positionalAudio = createPositionalAudio();
/*
*
* CHILDREN
*
*/
this._childrenControllerContext = NodeContext.AUDIO;
}
static type() {
return ObjType.POSITIONAL_AUDIO;
}
createObject() {
const group = new Group();
group.matrixAutoUpdate = false;
group.add(this._positionalAudio);
return group;
}
initializeNode() {
this.hierarchyController.initializeNode();
this.transformController.initializeNode();
this._updateHelperHierarchy();
this.flags.display.onUpdate(async () => {
await this._updateToDestination();
await this._updateHelperHierarchy();
});
}
async _updateToDestination() {
if (this.flags.display.active()) {
const listener = this.root().audioController.audioListeners()[0];
if (!listener) {
this.states.error.set("a listener is required in the scene");
return;
}
listener.object.addInput(this._positionalAudio);
} else {
this._positionalAudio.disconnect();
}
}
_updateHelperHierarchy() {
if (!this._helper) {
return;
}
if (this.flags.display.active()) {
this.object.add(this._helper);
} else {
this.object.remove(this._helper);
}
}
async cook() {
this.transformController.update();
await this._updateToDestination();
this._updatePositionalAudio();
this._connectAudioNode();
this.cookController.endCook();
}
async _updatePositionalAudio() {
this._positionalAudio.setRefDistance(this.pv.refDistance);
this._positionalAudio.setRolloffFactor(this.pv.rolloffFactor);
this._positionalAudio.setMaxDistance(this.pv.maxDistance);
this._positionalAudio.setDistanceModel(DISTANCE_MODELS[this.pv.distanceModel]);
this._positionalAudio.setDirectionalCone(this.pv.coneInnerAngle, this.pv.coneOuterAngle, this.pv.coneOuterGain);
this._positionalAudio.listenerTransformAutoUpdate = isBooleanTrue(this.pv.listenerTransformAutoUpdate);
if (isBooleanTrue(this.pv.showHelper)) {
this._helper = this._helper || this._createHelper(this._positionalAudio);
this.object.add(this._helper);
}
if (this._helper) {
this._helper.visible = isBooleanTrue(this.pv.showHelper);
this._helper.range = this.pv.helperSize;
this._helper.update();
}
}
_createHelper(positionalAudio) {
const helper = new CorePositionalAudioHelper(positionalAudio);
helper.matrixAutoUpdate = false;
return helper;
}
_resetAudioNode() {
this._positionalAudio.setInput(void 0);
}
async _connectAudioNode() {
this._resetAudioNode();
const baseAudioNode = this.pv.audioNode.nodeWithContext(NodeContext.AUDIO);
if (!baseAudioNode) {
this.states.error.set("no audio node found");
this._resetAudioNode();
return;
}
const audioContainer = await baseAudioNode.compute();
const audioBuilder = audioContainer.coreContent();
if (!audioBuilder) {
this.states.error.set("invalid audio node");
this._resetAudioNode();
return;
}
const toneAudioNode = audioBuilder.audioNode();
if (!toneAudioNode) {
this.states.error.set("no valid audio node given");
this._resetAudioNode();
return;
}
this._positionalAudio.setInput(toneAudioNode);
}
static PARAM_CALLBACK_updatePositionalAudio(node) {
node._updatePositionalAudio();
}
createNode(node_class, options) {
return super.createNode(node_class, options);
}
children() {
return super.children();
}
nodesByType(type) {
return super.nodesByType(type);
}
}