UNPKG

@polygonjs/polygonjs

Version:

node-based WebGL 3D engine https://polygonjs.com

219 lines (218 loc) 7.65 kB
"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); } }