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