diff --git a/examples/sound-3d/index.html b/examples/sound-3d/index.html new file mode 100644 index 000000000..c910e2e01 --- /dev/null +++ b/examples/sound-3d/index.html @@ -0,0 +1,76 @@ + + + + + Sound3D + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+ + + + OSG.JS + + +
+ 3D spatial sounds example. Data are provided by soundcloud
+ Use FPS controls to navigate in the scene +
+ +
+ + + + diff --git a/examples/sound-3d/main.js b/examples/sound-3d/main.js new file mode 100644 index 000000000..b7986bbac --- /dev/null +++ b/examples/sound-3d/main.js @@ -0,0 +1,161 @@ +( function () { + 'use strict'; + + var SoundCloudID = '237d195ad90846f5e6294ade2e8cf87b'; + + var P = window.P; + var OSG = window.OSG; + var osgText = OSG.osgText; + var osgGA = OSG.osgGA; + var osg = OSG.osg; + var ExampleOSGJS = window.ExampleOSGJS; + var SC = window.SC; + var SoundManager = window.SoundManager; + + var Example = function () { + + this._soundList = [ { + url: 'https://soundcloud.com/wearecc/mdg-banana-man', + pos: [ 15, 0, 0 ], + audio: undefined + }, { + url: 'https://soundcloud.com/kodak-black/kodak-black-22-no-flocking', + pos: [ -15, 0, 0 ], + audio: undefined + }, { + url: 'https://soundcloud.com/championdnb/true-survivor-champion', + pos: [ 0, -20, 0 ], + audio: undefined + } ]; + + ExampleOSGJS.call( this ); + this.initializeSoundCloud(); + + }; + + + Example.prototype = osg.objectInherit( ExampleOSGJS.prototype, { + + initializeSoundCloud: function () { + SC.initialize( { + client_id: SoundCloudID + } ); + }, + + // helpers + createSound: function ( sound ) { + + // create the audio element in the dom + var audio = document.createElement( 'audio' ); + sound.audio = audio; + audio.preload = 'auto'; + audio.crossOrigin = 'anonymous'; + + var url = sound.url; + var self = this; + var defer = P.defer(); + + SC.get( '/resolve', { + 'url': url + }, function ( soundCloudResult ) { + + if ( soundCloudResult.errors ) { + + var errors = ''; + for ( var i = 0; i < soundCloudResult.errors.length; i++ ) { + errors += soundCloudResult.errors[ i ].error_message + '\n'; + } + + defer.reject( errors ); + + } else { + + var soundCloudURL = soundCloudResult.stream_url + '?client_id=' + SoundCloudID; + osg.log( 'stream ' + soundCloudURL + ' ready' ); + + var soundUpdateCallback = self._soundManager.create3DSound( audio, soundCloudURL ); + sound.sound = soundUpdateCallback; + + defer.resolve( sound ); + } + } ); + + return defer.promise; + }, + + run: function () { + ExampleOSGJS.prototype.run.call( this ); + + this._viewer.getManipulator().strafeVertical( 5 ); + }, + + createScene: function () { + this._soundManager = new SoundManager(); + this._soundManager._camera = this._viewer.getCamera(); + + // the root node + var scene = new osg.Node(); + scene.getOrCreateStateSet().setAttributeAndModes( new osg.CullFace( 0 ) ); + scene.addUpdateCallback( this._soundManager ); + + var addSoundInScene = function ( sound ) { + + var node = new osg.MatrixTransform(); + var sphere = osg.createTexturedSphere( 1, 15, 15 ); + node.addChild( sphere ); + osg.mat4.fromTranslation( node.getMatrix(), sound.pos ); + scene.addChild( node ); + + node.addUpdateCallback( sound.sound ); + var strArray = sound.url.split( '/' ); + var text = strArray.slice( strArray.length - 2 ).join( '/' ); + var textNode = new osgText.Text( text ); + textNode.setAutoRotateToScreen( true ); + textNode.setPosition( osg.vec3.fromValues( 0, 0, 1.1 ) ); + textNode.setCharacterSize( 0.5 ); + node.addChild( textNode ); + + sound.sound.play(); + // if ( !window.soundsList ) window.soundsList = []; + // window.soundsList.push( sound ); + }; + + // window.soundManager = this._soundManager; + + var mt = new osg.MatrixTransform(); + var grid = osg.createGridGeometry( -30, -40, -3, + 60, 0, 0, + 0, 60, 0, + 15, 15 + ); + mt.addChild( grid ); + scene.addChild( mt ); + + this.getRootNode().addChild( scene ); + + this._manipulator = new osgGA.FirstPersonManipulator(); + this._viewer.setManipulator( this._manipulator ); + this._viewer.getManipulator().setNode( scene ); + this._viewer.getManipulator().computeHomePosition(); + + for ( var i = 0, l = this._soundList.length; i < l; i++ ) { + + var sound = this._soundList[ i ]; + this.createSound( sound ).then( addSoundInScene ).catch( osg.error ); + + } + + + } + + } ); + + window.addEventListener( 'load', function () { + + var example = new Example(); + example.run(); + window.example = example; + + }, true ); + +} )(); diff --git a/examples/sound-3d/sound.js b/examples/sound-3d/sound.js new file mode 100644 index 000000000..a3adf3d16 --- /dev/null +++ b/examples/sound-3d/sound.js @@ -0,0 +1,116 @@ + var Sound = function ( domElement, url, panner ) { + this._player = domElement; + this._mediaElement = undefined; + this._panner = panner; + this._player.setAttribute( 'src', url ); + }; + + Sound.prototype = { + + play: function () { + if ( this._player ) this._player.play(); + }, + + pause: function () { + if ( this._player ) this._player.pause(); + }, + + update: ( function () { + var matrixWorldSpace = osg.mat4.create(); + var position = osg.vec3.create(); + + return function ( node, nv ) { + + if ( !this._panner ) return true; + + osg.computeLocalToWorld( nv.nodePath, true, osg.mat4.identity( matrixWorldSpace ) ); + + osg.mat4.getTranslation( position, matrixWorldSpace ); + var soundPosition = this._panner; + soundPosition.setPosition( position[ 0 ], position[ 1 ], position[ 2 ] ); + return true; + + }; + + } )() + + }; + + + var SoundManager = function () { + this._context = new( window.AudioContext || window.webkitAudioContext ); + }; + + SoundManager.prototype = { + + create3DSound: function ( player, url ) { + var panner = this._context.createPanner(); + + panner.panningModel = 'HRTF'; + panner.distanceModel = 'inverse'; + panner.refDistance = 1; + panner.maxDistance = 1000; + panner.rolloffFactor = 1; + panner.coneInnerAngle = 360; + panner.coneOuterAngle = 0; + panner.coneOuterGain = 0; + panner.setPosition( 0, 0, 0 ); + + var sound = new Sound( player, url, panner ); + + // panner.orientationX.value = 1; + // panner.orientationY.value = 0; + // panner.orientationZ.value = 0; + // panner.positionX.value = 0.0; + // panner.positionY.value = 0.0; + // panner.positionZ.value = 0.0; + sound._panner = panner; + + var source = this._context.createMediaElementSource( player ); + sound._mediaElement = source; + + source.connect( panner ); + panner.connect( this._context.destination ); + return sound; + }, + + createAmbientSound: function ( player, url ) { + var sound = new Sound( player, url ); + sound._mediaElement = this._context.createMediaElementSource( player ); + return sound; + }, + + releaseSound: function ( sound ) { + if ( !sound ) return; + + sound.pause(); + if ( sound._mediaElement ) sound._mediaElement.disconnect( sound._panner ); + if ( sound._panner ) sound._panner.disconnect( this._context ); + sound._panner = undefined; + sound._mediaElement = undefined; + }, + + update: ( function () { + var eye = osg.vec3.create(); + var center = osg.vec3.create(); + var up = osg.vec3.create(); + + return function () { + + var camera = this._camera; + + if ( camera ) { + + osg.mat4.getLookAt( eye, center, up, camera.getViewMatrix() ); + osg.vec3.sub( center, center, eye ); + var listener = this._context.listener; + listener.setPosition( eye[ 0 ], eye[ 1 ], eye[ 2 ] ); + listener.setOrientation( center[ 0 ], center[ 1 ], center[ 2 ], up[ 0 ], up[ 1 ], up[ 2 ] ); + + } + + return true; + }; + } )() + + }; diff --git a/sources/osg/ComputeMatrixFromNodePath.js b/sources/osg/ComputeMatrixFromNodePath.js index a4aa8418f..6e9823925 100644 --- a/sources/osg/ComputeMatrixFromNodePath.js +++ b/sources/osg/ComputeMatrixFromNodePath.js @@ -3,7 +3,6 @@ var mat4 = require( 'osg/glMatrix' ).mat4; var TransformEnums = require( 'osg/TransformEnums' ); -// TODO: GC PERF: add a result Matrix Parameter. var computeLocalToWorld = function ( nodePath, ignoreCameras, userMatrix ) { var ignoreCamera = ignoreCameras; diff --git a/website/contents/assets/img/sound3d.jpg b/website/contents/assets/img/sound3d.jpg new file mode 100644 index 000000000..a25d13972 Binary files /dev/null and b/website/contents/assets/img/sound3d.jpg differ diff --git a/website/contents/examples.md b/website/contents/examples.md index f2f4bcd82..161b66a65 100644 --- a/website/contents/examples.md +++ b/website/contents/examples.md @@ -2,6 +2,10 @@ title: SDK Examples samples: + - + title: 3D sounds + image: assets/img/sound3d.jpg + link: examples/sound-3d - title: PBR image: assets/img/pbr.jpg