diff --git a/protocols/schemas/scene.prs b/protocols/schemas/scene.prs index 46a6f5d..e220e0b 100644 --- a/protocols/schemas/scene.prs +++ b/protocols/schemas/scene.prs @@ -8,5 +8,12 @@ PortalDestination = Gravity = . +AmbientSound = . + +SoundSpec = +/ @stream +/ @loop +. + ; Message Touch = . diff --git a/protocols/schemas/shapes.prs b/protocols/schemas/shapes.prs index f848a68..a03f444 100644 --- a/protocols/schemas/shapes.prs +++ b/protocols/schemas/shapes.prs @@ -34,7 +34,7 @@ Color = / @transparent . -Sound = . +Sound = . Name = . diff --git a/scene/lobby.pr b/scene/lobby.pr index eec7181..a26c334 100644 --- a/scene/lobby.pr +++ b/scene/lobby.pr @@ -76,14 +76,13 @@ ] - >>> >>> ]> - >> - >>> + >>>>> ; ; > > +> + ; diff --git a/sounds/Space-atmosphere-sound/Read.txt b/sounds/Space-atmosphere-sound/Read.txt new file mode 100644 index 0000000..0347b89 --- /dev/null +++ b/sounds/Space-atmosphere-sound/Read.txt @@ -0,0 +1,15 @@ + +Hello from Orange Free Sounds, + + +Stock audio - Free sound effects, loops and music. + + +There are no hidden costs or need to sign-up. + + + +License: The sound effect is permitted for non-commercial use under license “Attribution-NonCommercial 4.0 International (CC BY-NC 4.0) + + +https://orangefreesounds.com/ \ No newline at end of file diff --git a/sounds/Space-atmosphere-sound/Space-atmosphere-sound.mp3 b/sounds/Space-atmosphere-sound/Space-atmosphere-sound.mp3 new file mode 100644 index 0000000..ed46e4d Binary files /dev/null and b/sounds/Space-atmosphere-sound/Space-atmosphere-sound.mp3 differ diff --git a/src/index.ts b/src/index.ts index 3a3c529..1d39e49 100644 --- a/src/index.ts +++ b/src/index.ts @@ -15,7 +15,7 @@ import { WebXRCamera, } from '@babylonjs/core/Legacy/legacy'; -import { activeFloorMeshes, activeTouchableMeshes, ShapeTree, scale3, builder as B } from './shapes.js'; +import { activeFloorMeshes, activeTouchableMeshes, ShapeTree, scale3, buildSound, builder as B } from './shapes.js'; import { RunningEngine } from './engine.js'; import { uuid } from './uuid.js'; @@ -32,6 +32,14 @@ function interpretScene(myId: string, runningEngine: RunningEngine, rootMesh: Me spriteMain(name, runningEngine, rootMesh, sceneDs); } } + + during SceneProtocol.AmbientSound({ + "name": $name: string, + "spec": $spec: SceneProtocol.SoundSpec + }) => spawn named `sound:${name}` { + const sound = buildSound(name, runningEngine.scene, spec, false); + on stop sound.dispose(); + } } } diff --git a/src/shapes.ts b/src/shapes.ts index ea361c8..d362229 100644 --- a/src/shapes.ts +++ b/src/shapes.ts @@ -4,6 +4,7 @@ import { CubeTexture, CSG, HemisphericLight, + ISoundOptions, Material, Matrix, Mesh, @@ -22,6 +23,7 @@ import { import { KeyedDictionary, Value, is } from "@syndicate-lang/core"; import * as Shapes from './gen/shapes.js'; +import * as SceneProtocol from './gen/scene.js'; export const activeFloorMeshes: Array = []; export const activeTouchableMeshes: Array = []; @@ -30,6 +32,7 @@ export class ShapeTree { shapePreserve: Value; cleanups: Array<() => void> = []; subnodes: Promise; + subtrees: ShapeTree[] = []; constructor ( public scene: Scene, @@ -60,8 +63,15 @@ export class ShapeTree { } } + set parent(t: ShapeTree) { + t.subtrees.push(this); + this.rootnode.parent = t.rootnode; + } + remove() { + this.subtrees.forEach(t => t.remove()); this.allnodes.then(ns => ns.forEach(n => n.dispose())); + this.cleanups.forEach(c => c()); } static empty(name: string, scene: Scene): ShapeTree { @@ -181,6 +191,26 @@ export function buildMesh( } } +export function buildSound(name: string, scene: Scene, spec: SceneProtocol.SoundSpec, spatial: boolean): Sound { + const options: ISoundOptions = { + loop: true, + autoplay: true, + skipCodecCheck: true, + }; + if (spatial) { + options.distanceModel = "inverse"; + options.rolloffFactor = 0.25; + } + switch (spec._variant) { + case "stream": + options.streaming = true; + break; + case "loop": + break; + } + return new Sound(name, spec.url, scene, null, options); +} + export function build(name: string, scene: Scene, shape: Shapes.Shape, customize: MeshCustomizer): ShapeTree { switch (shape._variant) { case "Mesh": { @@ -199,14 +229,14 @@ export function build(name: string, scene: Scene, shape: Shapes.Shape, customize case "Scale": { const t = ShapeTree.transform(name, scene, shape); t.rootnode.scaling = v3(shape.value.v); - build(name + '.inner', scene, shape.value.shape, customize).rootnode.parent = t.rootnode; + build(name + '.inner', scene, shape.value.shape, customize).parent = t; return t; } case "Move": { const t = ShapeTree.transform(name, scene, shape); t.rootnode.position = v3(shape.value.v); - build(name + '.inner', scene, shape.value.shape, customize).rootnode.parent = t.rootnode; + build(name + '.inner', scene, shape.value.shape, customize).parent = t; return t; } @@ -221,14 +251,14 @@ export function build(name: string, scene: Scene, shape: Shapes.Shape, customize t.rootnode.rotationQuaternion = q(shape.value.q); break; } - build(name + '.inner', scene, shape.value.shape, customize).rootnode.parent = t.rootnode; + build(name + '.inner', scene, shape.value.shape, customize).parent = t; return t; } case "many": { const t = ShapeTree.transform(name, scene, shape); shape.value.forEach((s, i) => { - build(name + '[' + i + ']', scene, s, customize).rootnode.parent = t.rootnode; + build(name + '[' + i + ']', scene, s, customize).parent = t; }); return t; } @@ -254,24 +284,13 @@ export function build(name: string, scene: Scene, shape: Shapes.Shape, customize } case "Sound": { - const sound = new Sound( - name + ".sound", - shape.value.url, - scene, - null, - { - distanceModel: "inverse", - loop: true, - rolloffFactor: 0.25, - autoplay: true, - streaming: true, - skipCodecCheck: true, - }); - console.log(sound); - return build(name + ".inner", scene, shape.value.shape, { + const sound = buildSound(name + ".sound", scene, shape.value.spec, true); + const t = build(name + ".inner", scene, shape.value.shape, { ... customize, sound: async m => sound.attachToMesh(m.rootnode), }); + t.cleanups.push(() => sound.dispose()); + return t; } case "Name":