diff --git a/lib/nodejs/serve.js b/lib/nodejs/serve.js index 5413ac069..9a1b56767 100755 --- a/lib/nodejs/serve.js +++ b/lib/nodejs/serve.js @@ -98,7 +98,8 @@ function ServeYAML( filename, response, URL ) { try { var deYAML = JSON.stringify( YAML.load( file ) ); } catch ( e ) { - global.log( "error parsing YAML " + filename ); + global.log( "error parsing YAML " + filename); + global.log("" + e); _404( response ); return; } diff --git a/support/client/lib/vwf.js b/support/client/lib/vwf.js index 36e3e758a..b74d5f56b 100644 --- a/support/client/lib/vwf.js +++ b/support/client/lib/vwf.js @@ -697,7 +697,7 @@ var model = require( modelName ).create( this.models.kernel, // model's kernel access - [ require( "vwf/model/stage/log" ) ], // stages between the kernel and model + [], // stages between the kernel and model {}, // state shared with a paired view [].concat( modelArguments || [] ) // arguments for initialize() ); @@ -716,11 +716,16 @@ while ( this.models.object.model ) this.models.object = this.models.object.model; } - if(model.model.compatibilityStatus) { + if(model.model && model.model.compatibilityStatus) { if(!model.model.compatibilityStatus.compatible) { compatibilityStatus.compatible = false; jQuery.extend(compatibilityStatus.errors, model.model.compatibilityStatus.errors); } + } else if(model.compatibilityStatus) { + if(!model.compatibilityStatus.compatible) { + compatibilityStatus.compatible = false; + jQuery.extend(compatibilityStatus.errors, model.compatibilityStatus.errors); + } } } @@ -3177,73 +3182,87 @@ if ( ! childComponent.source ) { var delegated = false, assigned = false; - // Call settingProperty() on each model. The first model to return a non-undefined value - // has performed the set and dictates the return value. The property is considered set - // after all models have run. + if ( propertyName[ propertyName.length - 1 ] !== "$" ) { - this.models.some( function( model, index ) { + // Call settingProperty() on each model. The first model to return a non-undefined value + // has performed the set and dictates the return value. The property is considered set + // after all models have run. - // Skip initial models that an outer call has already invoked for this node and - // property (if any). If an inner call completed for this node and property, skip - // the remaining models. + this.models.some( function( model, index ) { - if ( ( ! reentered || index > entry.index ) && ! reentry.completed ) { + // Skip initial models that an outer call has already invoked for this node and + // property (if any). If an inner call completed for this node and property, skip + // the remaining models. - // Record the active model number. - - reentry.index = index; + if ( ( ! reentered || index > entry.index ) && ! reentry.completed ) { - // Record the number of assignments made since the outermost call. When - // `entrants.assignments` increases, a driver has called `setProperty` to make - // an assignment elsewhere. + // Record the active model number. - var assignments = entrants.assignments; + reentry.index = index; - // Make the call. + // Record the number of assignments made since the outermost call. When + // `entrants.assignments` increases, a driver has called `setProperty` to make + // an assignment elsewhere. - if ( ! delegated && ! assigned ) { - var value = model[settingPropertyEtc] && model[settingPropertyEtc]( nodeID, propertyName, propertyValue ); - } else { - model[settingPropertyEtc] && model[settingPropertyEtc]( nodeID, propertyName, undefined ); - } + var assignments = entrants.assignments; - // Ignore the result if reentry is disabled and the driver attempted to call - // back into the kernel. Kernel reentry is disabled during replication to - // prevent coloring from accessor scripts. + // Make the call. - if ( this.models.kernel.blocked() ) { // TODO: this might be better handled wholly in vwf/kernel/model by converting to a stage and clearing blocked results on the return - value = undefined; - } + if ( ! delegated && ! assigned ) { + var value = model[settingPropertyEtc] && model[settingPropertyEtc]( nodeID, propertyName, propertyValue ); + } else { + model[settingPropertyEtc] && model[settingPropertyEtc]( nodeID, propertyName, undefined ); + } - // The property was delegated if the call made any assignments. + // Ignore the result if reentry is disabled and the driver attempted to call + // back into the kernel. Kernel reentry is disabled during replication to + // prevent coloring from accessor scripts. - if ( entrants.assignments !== assignments ) { - delegated = true; - } + if ( this.models.kernel.blocked() ) { // TODO: this might be better handled wholly in vwf/kernel/model by converting to a stage and clearing blocked results on the return + value = undefined; + } - // Otherwise if the call returned a value, the property was assigned here. + // The property was delegated if the call made any assignments. - else if ( value !== undefined ) { - entrants.assignments++; - assigned = true; - } + if ( entrants.assignments !== assignments ) { + delegated = true; + } - // Record the value actually assigned. This may differ from the incoming value - // if it was range limited, quantized, etc. by the model. This is the value - // passed to the views. + // Otherwise if the call returned a value, the property was assigned here. - if ( value !== undefined ) { - propertyValue = value; + else if ( value !== undefined ) { + entrants.assignments++; + assigned = true; + } + + // Record the value actually assigned. This may differ from the incoming value + // if it was range limited, quantized, etc. by the model. This is the value + // passed to the views. + + if ( value !== undefined ) { + propertyValue = value; + } + + // If we are setting, exit from the this.models.some() iterator once the value + // has been set. Don't exit early if we are creating or initializing since every + // model needs the opportunity to register the property. + + return settingPropertyEtc == "settingProperty" && ( delegated || assigned ); } - // If we are setting, exit from the this.models.some() iterator once the value - // has been set. Don't exit early if we are creating or initializing since every - // model needs the opportunity to register the property. + }, this ); - return settingPropertyEtc == "settingProperty" && ( delegated || assigned ); + } else { + + var value = this.models.object[settingPropertyEtc]( nodeID, propertyName, propertyValue ); + + if ( value !== undefined ) { + entrants.assignments++; + assigned = true; + propertyValue = value; } - }, this ); + } // Record the change if the property was assigned here. @@ -3258,7 +3277,7 @@ if ( ! childComponent.source ) { // ephemeral, and views on late-joining clients would never see it, it's best to never // send those notifications. - if ( assigned ) { + if ( assigned && propertyName[ propertyName.length - 1 ] !== "$" ) { this.views.forEach( function( view ) { view[satPropertyEtc] && view[satPropertyEtc]( nodeID, propertyName, propertyValue ); } ); @@ -3350,65 +3369,78 @@ if ( ! childComponent.source ) { var delegated = false, retrieved = false; - // Call gettingProperty() on each model. The first model to return a non-undefined value - // dictates the return value. + if ( propertyName[ propertyName.length - 1 ] !== "$" ) { - this.models.some( function( model, index ) { + // Call gettingProperty() on each model. The first model to return a non-undefined value + // dictates the return value. - // Skip initial models that an outer call has already invoked for this node and - // property (if any). If an inner call completed for this node and property, skip - // the remaining models. + this.models.some( function( model, index ) { - if ( ( ! reentered || index > entry.index ) && ! reentry.completed ) { + // Skip initial models that an outer call has already invoked for this node and + // property (if any). If an inner call completed for this node and property, skip + // the remaining models. - // Record the active model number. - - reentry.index = index; + if ( ( ! reentered || index > entry.index ) && ! reentry.completed ) { - // Record the number of retrievals made since the outermost call. When - // `entrants.retrievals` increases, a driver has called `getProperty` to make - // a retrieval elsewhere. + // Record the active model number. - var retrievals = entrants.retrievals; + reentry.index = index; - // Make the call. + // Record the number of retrievals made since the outermost call. When + // `entrants.retrievals` increases, a driver has called `getProperty` to make + // a retrieval elsewhere. - var value = model.gettingProperty && - model.gettingProperty( nodeID, propertyName, propertyValue ); // TODO: probably don't need propertyValue here + var retrievals = entrants.retrievals; - // Ignore the result if reentry is disabled and the driver attempted to call - // back into the kernel. Kernel reentry is disabled during replication to - // prevent coloring from accessor scripts. + // Make the call. - if ( this.models.kernel.blocked() ) { // TODO: this might be better handled wholly in vwf/kernel/model by converting to a stage and clearing blocked results on the return - value = undefined; - } + var value = model.gettingProperty && + model.gettingProperty( nodeID, propertyName, propertyValue ); // TODO: probably don't need propertyValue here - // The property was delegated if the call made any retrievals. + // Ignore the result if reentry is disabled and the driver attempted to call + // back into the kernel. Kernel reentry is disabled during replication to + // prevent coloring from accessor scripts. - if ( entrants.retrievals !== retrievals ) { - delegated = true; - } + if ( this.models.kernel.blocked() ) { // TODO: this might be better handled wholly in vwf/kernel/model by converting to a stage and clearing blocked results on the return + value = undefined; + } - // Otherwise if the call returned a value, the property was retrieved here. + // The property was delegated if the call made any retrievals. - else if ( value !== undefined ) { - entrants.retrievals++; - retrieved = true; - } + if ( entrants.retrievals !== retrievals ) { + delegated = true; + } + + // Otherwise if the call returned a value, the property was retrieved here. + + else if ( value !== undefined ) { + entrants.retrievals++; + retrieved = true; + } + + // Record the value retrieved. + + if ( value !== undefined ) { + propertyValue = value; + } - // Record the value retrieved. + // Exit from the this.models.some() iterator once we have a return value. - if ( value !== undefined ) { - propertyValue = value; + return delegated || retrieved; } - // Exit from the this.models.some() iterator once we have a return value. + }, this ); + + } else { + + var value = this.models.object.gettingProperty( nodeID, propertyName, propertyValue ); // TODO: probably don't need propertyValue here - return delegated || retrieved; + if ( value !== undefined ) { + entrants.retrievals++; + propertyValue = value; } - }, this ); + } if ( reentered ) { @@ -3447,9 +3479,11 @@ if ( ! childComponent.source ) { // Call gotProperty() on each view. - this.views.forEach( function( view ) { - view.gotProperty && view.gotProperty( nodeID, propertyName, propertyValue ); // TODO: be sure this is the value actually gotten and not an intermediate value from above - } ); + if ( propertyName[ propertyName.length - 1 ] !== "$" ) { + this.views.forEach( function( view ) { + view.gotProperty && view.gotProperty( nodeID, propertyName, propertyValue ); // TODO: be sure this is the value actually gotten and not an intermediate value from above + } ); + } } @@ -4319,8 +4353,12 @@ if ( ! childComponent.source ) { /// /// @see {@link module:vwf/api/kernel.application} +this.applicationID = undefined; + this.application = function( initializedOnly ) { +if ( this.applicationID ) return this.applicationID; + var applicationID; Object.keys( nodes.globals ).forEach( function( globalID ) { @@ -4330,6 +4368,8 @@ if ( ! childComponent.source ) { } }, this ); +this.applicationID = applicationID; + return applicationID; }; diff --git a/support/client/lib/vwf/model/blockly.js b/support/client/lib/vwf/model/blockly.js index 5bdea9eac..ac4c45f74 100644 --- a/support/client/lib/vwf/model/blockly.js +++ b/support/client/lib/vwf/model/blockly.js @@ -398,18 +398,6 @@ define( [ "module", "vwf/model", "vwf/utility", } ); - function getPrototypes( extendsID ) { - var prototypes = []; - var id = extendsID; - - while ( id !== undefined ) { - prototypes.push( id ); - id = self.kernel.prototype( id ); - } - - return prototypes; - } - function isBlockly3Node( nodeID ) { return self.kernel.test( nodeID, "self::element(*,'http://vwf.example.com/blockly/controller.vwf')", diff --git a/support/client/lib/vwf/model/buzz.js b/support/client/lib/vwf/model/buzz.js index c0c010feb..b5559311f 100644 --- a/support/client/lib/vwf/model/buzz.js +++ b/support/client/lib/vwf/model/buzz.js @@ -112,7 +112,7 @@ define( [ return; } - var protos = getPrototypes( this.kernel, childExtendsID ); + var protos = this.kernel.prototypes( childID ); var node; if ( this.state.isSoundComponent( protos ) ) { @@ -415,18 +415,6 @@ define( [ } ); - function getPrototypes( kernel, extendsID ) { - var prototypes = []; - var id = extendsID; - - while ( id !== undefined ) { - prototypes.push( id ); - id = kernel.prototype( id ); - } - - return prototypes; - } - function createSound( node, url ) { var soundProps; diff --git a/support/client/lib/vwf/model/cesium.js b/support/client/lib/vwf/model/cesium.js index d47a142dd..72ede50c9 100644 --- a/support/client/lib/vwf/model/cesium.js +++ b/support/client/lib/vwf/model/cesium.js @@ -306,7 +306,7 @@ define( [ "module", } var node = undefined, parentNode, sceneNode; - var protos = getPrototypes.call( this, childExtendsID ); + var protos = this.kernel.prototypes( childID ); var createNode = function() { return { @@ -626,7 +626,7 @@ define( [ "module", // there, too function notifyDriverOfPrototypeAndBehaviorProps() { var ptPropValue; - var protos = getPrototypes.call( self, childExtendsID ); + var protos = self.kernel.prototypes( childID ); protos.forEach( function( prototypeID ) { for ( var propertyName in kernel.getProperties( prototypeID ) ) { //console.info( " 1 getting "+propertyName+" of: " + childExtendsID ); @@ -2011,18 +2011,6 @@ define( [ "module", } - function getPrototypes( extendsID ) { - var prototypes = []; - var id = extendsID; - - while ( id !== undefined ) { - prototypes.push( id ); - id = this.kernel.prototype( id ); - } - - return prototypes; - } - function findParent( ID ) { var retNode = this.state.nodes[ ID ]; if ( retNode === undefined ) { @@ -2037,7 +2025,7 @@ define( [ "module", var protos = undefined; var parent = findParent.call( this, parentID ); while ( parent && sceneNode === undefined ) { - protos = getPrototypes.call( this, parent.extendsID ); + protos = this.kernel.prototypes( parent.ID ); if ( protos && isCesium.call( this, protos ) ) { sceneNode = parent; } else { diff --git a/support/client/lib/vwf/model/glge.js b/support/client/lib/vwf/model/glge.js index 137175fa0..1966cdd14 100644 --- a/support/client/lib/vwf/model/glge.js +++ b/support/client/lib/vwf/model/glge.js @@ -75,7 +75,7 @@ define( [ "module", "vwf/model", "vwf/utility" ], function( module, model, utili var node, parentNode, glgeChild, glgeParent; var kernel = this.kernel; - var prototypes = getPrototypes.call( this, kernel, childExtendsID ); + var prototypes = this.kernel.prototypes( childID ); // this.logger.enabled = true; // this.logger.infox( "creatingNode", nodeID, childID, childExtendsID, childImplementsIDs, @@ -328,7 +328,7 @@ define( [ "module", "vwf/model", "vwf/utility" ], function( module, model, utili // there, too function notifyDriverOfPrototypeAndBehaviorProps() { var ptPropValue; - var protos = getPrototypes.call( this, kernel, childExtendsID ); + var protos = self.kernel.prototypes( childID ); protos.forEach( function( prototypeID ) { for ( var propertyName in kernel.getProperties( prototypeID ) ) { //console.info( " 1 getting "+propertyName+" of: " + childExtendsID ); @@ -620,7 +620,7 @@ define( [ "module", "vwf/model", "vwf/utility" ], function( module, model, utili break; default: - prototypes = getPrototypes.call( this, this.kernel.kernel.kernel, node["type"] ); + prototypes = this.kernel.prototypes( node.ID ); if ( isGlgeMaterialDefinition.call( this, prototypes ) ){ value = setMaterialProperty.call( this, nodeID, propertyName, propertyValue ); } else if ( isGlgeCameraDefinition.call( this, prototypes ) ) { @@ -773,7 +773,7 @@ define( [ "module", "vwf/model", "vwf/utility" ], function( module, model, utili default: // handle all of the other types - prototypes = getPrototypes.call( this, this.kernel.kernel.kernel, node["type"] ); + prototypes = this.kernel.prototypes( node.ID ); if ( isGlgeMaterialDefinition.call( this, prototypes ) ){ value = getMaterialProperty.call( this, nodeID, propertyName, propertyValue ); } else if ( isGlgeCameraDefinition.call( this, prototypes ) ) { @@ -2222,20 +2222,6 @@ define( [ "module", "vwf/model", "vwf/utility" ], function( module, model, utili return vertexIndices; } - // get the list of types this ID extends - - function getPrototypes( kernel, extendsID ) { - var prototypes = []; - var id = extendsID; - - while ( id !== undefined ) { - prototypes.push( id ); - id = kernel.prototype( id ); - } - - return prototypes; - } - function isPrototype( nodeID, childID ) { var ptID; if ( ( nodeID == 0 && childID != this.kernel.application() ) || this.state.prototypes[ nodeID ] !== undefined ) { diff --git a/support/client/lib/vwf/model/graphtool.js b/support/client/lib/vwf/model/graphtool.js index 791c6348d..6fca7b781 100644 --- a/support/client/lib/vwf/model/graphtool.js +++ b/support/client/lib/vwf/model/graphtool.js @@ -20,7 +20,6 @@ define( [ "module", "vwf/model", "vwf/utility" ], function( module, model, utili self = this; this.state.graphs = {}; this.state.objects = {}; - this.state.kernel = this.kernel.kernel.kernel; }, // == Model API ============================================================================ @@ -31,8 +30,7 @@ define( [ "module", "vwf/model", "vwf/utility" ], function( module, model, utili childSource, childType, childIndex, childName, callback /* ( ready ) */ ) { var node = undefined; - var kernel = this.state.kernel; - var protos = getPrototypes.call( this, kernel, childExtendsID ); + var protos = this.kernel.prototypes( childID ); if ( protos && isGraph( protos ) ) { @@ -318,18 +316,6 @@ define( [ "module", "vwf/model", "vwf/utility" ], function( module, model, utili return threejs; } - - function getPrototypes( kernel, extendsID ) { - var prototypes = []; - var id = extendsID; - - while ( id !== undefined ) { - prototypes.push( id ); - id = kernel.prototype( id ); - } - - return prototypes; - } function isGraph( prototypes ) { diff --git a/support/client/lib/vwf/model/heightmap.js b/support/client/lib/vwf/model/heightmap.js index 106620309..c8e162ad5 100644 --- a/support/client/lib/vwf/model/heightmap.js +++ b/support/client/lib/vwf/model/heightmap.js @@ -18,14 +18,14 @@ define( [ "module", "vwf/model", "vwf/utility" ], function( module, model, utili creatingNode: function( nodeID, childID, childExtendsID, childImplementsIDs, childSource, childType, childIndex, childName, callback ) { - var protos = getPrototypes( this.kernel, childExtendsID ); + var protos = this.kernel.prototypes( childID ); if ( isHeightmap( protos ) ) { var node = this.nodes[ childID ]; // Create the local copy of the node properties - if ( this.nodes[ childID ] === undefined ){ + if ( this.nodes[ childID ] === undefined && childSource !== undefined ){ // Suspend the queue until the load is complete callback( false ); @@ -86,18 +86,6 @@ define( [ "module", "vwf/model", "vwf/utility" ], function( module, model, utili } ); - function getPrototypes( kernel, extendsID ) { - var prototypes = []; - var id = extendsID; - - while ( id !== undefined ) { - prototypes.push( id ); - id = kernel.prototype( id ); - } - - return prototypes; - } - function isHeightmap( prototypes ) { var found = false; if ( prototypes ) { diff --git a/support/client/lib/vwf/model/jPlayer.js b/support/client/lib/vwf/model/jPlayer.js index 6debc886e..15abb073f 100644 --- a/support/client/lib/vwf/model/jPlayer.js +++ b/support/client/lib/vwf/model/jPlayer.js @@ -120,7 +120,7 @@ define( [ return; } - var protos = getPrototypes( this.kernel, childExtendsID ); + var protos = this.kernel.prototypes( childID ); var isAudioManager = this.state.isAudioManager( protos ); var isVideoManager = this.state.isVideoManager( protos ); @@ -342,18 +342,6 @@ define( [ } ); - function getPrototypes( kernel, extendsID ) { - var prototypes = []; - var id = extendsID; - - while ( id !== undefined ) { - prototypes.push( id ); - id = kernel.prototype( id ); - } - - return prototypes; - } - function setWithPrototypeProperties( proto ) { if ( proto.url !== null ) { vwf.setProperty( node.ID, "url", proto.url ); diff --git a/support/client/lib/vwf/model/kineticjs.js b/support/client/lib/vwf/model/kineticjs.js index 8088f51bd..978a10114 100644 --- a/support/client/lib/vwf/model/kineticjs.js +++ b/support/client/lib/vwf/model/kineticjs.js @@ -118,7 +118,7 @@ define( [ "module", return; } - var protos = getPrototypes( this.kernel, childExtendsID ); + var protos = this.kernel.prototypes( childID ); var node; @@ -2097,18 +2097,6 @@ define( [ "module", } ); // == PRIVATE ======================================================================================== - function getPrototypes( kernel, extendsID ) { - var prototypes = []; - var id = extendsID; - - while ( id !== undefined ) { - prototypes.push( id ); - id = kernel.prototype( id ); - } - - return prototypes; - } - function createKineticObject( node, config ) { var protos = node.prototypes; var kineticObj = undefined; diff --git a/support/client/lib/vwf/model/mil-sym.js b/support/client/lib/vwf/model/mil-sym.js index 9e139303f..2d7278f38 100644 --- a/support/client/lib/vwf/model/mil-sym.js +++ b/support/client/lib/vwf/model/mil-sym.js @@ -106,7 +106,7 @@ define( [ "module", return; } - var protos = getPrototypes( childExtendsID ); + var protos = this.kernel.prototypes( childID ); var node = this.state.nodes[ childID ]; if ( node === undefined ) { @@ -424,18 +424,6 @@ define( [ "module", } ); - function getPrototypes( extendsID ) { - var prototypes = []; - var id = extendsID; - - while ( id !== undefined ) { - prototypes.push( id ); - id = modelDriver.kernel.prototype( id ); - } - - return prototypes; - } - function isUnitNode( prototypes ) { var found = false; if ( prototypes ) { diff --git a/support/client/lib/vwf/model/sound.js b/support/client/lib/vwf/model/sound.js index f824ad00a..46582f4db 100644 --- a/support/client/lib/vwf/model/sound.js +++ b/support/client/lib/vwf/model/sound.js @@ -356,8 +356,9 @@ define( [ "module", "vwf/model" ], function( module, model ) { } if ( !this.allowMultiplay && this.isPlaying() ) { + var instance = Object.keys( this.playingInstances )[ 0 ]; return { soundName: this.name, - instanceID: this.playingInstances[ 0 ] }; + instanceID: this.playingInstances[ instance ].id }; } var id = this.instanceIDCounter; diff --git a/support/client/lib/vwf/model/threejs.js b/support/client/lib/vwf/model/threejs.js index 2c16019b8..f39c119d6 100644 --- a/support/client/lib/vwf/model/threejs.js +++ b/support/client/lib/vwf/model/threejs.js @@ -50,11 +50,12 @@ define( [ "module", "vwf/model", "vwf/utility", + "vwf/kernel/utility", "vwf/utility/color", "jquery" ], - function( module, model, utility, Color, $ ) { + function( module, model, utility, kutility, Color, $ ) { var self; @@ -76,7 +77,6 @@ define( [ "module", this.state.scenes = {}; // id => { glgeDocument: new GLGE.Document(), glgeRenderer: new GLGE.Renderer(), glgeScene: new GLGE.Scene() } this.state.nodes = {}; // id => { name: string, glgeObject: GLGE.Object, GLGE.Collada, GLGE.Light, or other...? } this.state.prototypes = {}; - this.state.kernel = this.kernel.kernel.kernel; this.state.lights = {}; this.state.setMeshPropertyRecursively = function( threeObject, propertyName, value ) { @@ -177,9 +177,8 @@ define( [ "module", } } } - var kernel = this.kernel.kernel.kernel; - var protos = getPrototypes.call( this, kernel, childExtendsID ); + var protos = this.kernel.prototypes( childID ); if ( isSceneDefinition.call(this, protos) && childID == this.kernel.application() ) { var sceneNode = CreateThreeJSSceneNode( nodeID, childID, childExtendsID ); @@ -499,7 +498,7 @@ define( [ "module", // If we do not have a load a model for this node, then we are almost done, so we can update all // the driver properties w/ the stop-gap function below. // Else, it will be called at the end of the assetLoaded callback - if ( ! supportedFileType( childType ) ) { + if ( node && node.threeObject && !supportedFileType( childType ) ) { notifyDriverOfPrototypeAndBehaviorProps(); } @@ -511,18 +510,18 @@ define( [ "module", // there, too function notifyDriverOfPrototypeAndBehaviorProps() { var ptPropValue; - var protos = getPrototypes.call( this, kernel, childExtendsID ); + var protos = self.kernel.prototypes( childID ); protos.forEach( function( prototypeID ) { - for ( var propertyName in kernel.getProperties( prototypeID ) ) { - ptPropValue = kernel.getProperty( childExtendsID, propertyName ); + for ( var propertyName in self.kernel.getProperties( prototypeID ) ) { + ptPropValue = self.kernel.getProperty( childExtendsID, propertyName ); if ( ptPropValue !== undefined && ptPropValue !== null && childID !== undefined && childID !== null) { self.settingProperty( childID, propertyName, ptPropValue ); } } } ); childImplementsIDs.forEach( function( behaviorID ) { - for ( var propertyName in kernel.getProperties( behaviorID ) ) { - ptPropValue = kernel.getProperty( behaviorID, propertyName ); + for ( var propertyName in self.kernel.getProperties( behaviorID ) ) { + ptPropValue = self.kernel.getProperty( behaviorID, propertyName ); if ( ptPropValue !== undefined && ptPropValue !== null && childID !== undefined && childID !== null) { self.settingProperty( childID, propertyName, ptPropValue ); } @@ -1282,12 +1281,12 @@ define( [ "module", // Skeletal Animations (takes precedence over Morph Target) if ( node.threeObject.bones && node.threeObject.bones.length > 0 ) { - var animRate = this.state.kernel.getProperty( nodeID, "animationRate" ) || 1; + var animRate = this.kernel.getProperty( nodeID, "animationRate" ) || 1; THREE.AnimationHandler.update(animRate); } // Morph Target Animations else if ( node.threeObject.animatedMesh && node.threeObject.animatedMesh.length && propertyValue !== undefined ) { - var fps = this.state.kernel.getProperty( nodeID, "animationFPS" ) || 30; + var fps = this.kernel.getProperty( nodeID, "animationFPS" ) || 30; for( var i = 0; i < node.threeObject.animatedMesh.length; i++ ) { if ( node.threeObject.animatedMesh[i].morphTargetInfluences ) { for( var j = 0; j < node.threeObject.animatedMesh[i].morphTargetInfluences.length; j++ ) { @@ -1724,7 +1723,7 @@ define( [ "module", } // Need to reset the viewport or you just get a blank screen - this.state.kernel.dispatchEvent( nodeID, "resetViewport" ); + this.kernel.dispatchEvent( nodeID, "resetViewport" ); } if ( propertyName == 'shadowMapCullFace') { var shadowMapCullFace; @@ -2012,7 +2011,8 @@ define( [ "module", if ( propertyValue instanceof Array ) { value = propertyValue; if ( threeObject.target ) { - threeObject.target.position.set( value[ 0 ], value[ 1 ], value[ 2 ] ); + threeObject.target.position.set( value[ 0 ], value[ 1 ], value[ 2 ] ); + threeObject.target.updateMatrixWorld(); } } else if ( this.state.nodes[ propertyValue ] ) { value = propertyValue; @@ -2101,7 +2101,7 @@ define( [ "module", value = animationDuration; } else if ( node.threeObject.animatedMesh && node.threeObject.animatedMesh.length ) { - var fps = this.state.kernel.getProperty( nodeID, "animationFPS") || 30; + var fps = this.kernel.getProperty( nodeID, "animationFPS") || 30; for(var i=0, il = node.threeObject.animatedMesh.length; i < il; i++) { if (node.threeObject.animatedMesh[i].bones) { @@ -2699,7 +2699,30 @@ define( [ "module", var raycaster = new THREE.Raycaster( origin, direction, near, far ); var intersects = raycaster.intersectObjects( objects, recursive ); - return intersects; + + // clean up results before passing back to applications + var results = []; + var result, intersectedNode; + for ( var i = 0; i < intersects.length; i++ ) { + result = {}; + result[ "distance" ] = intersects[ i ].distance; + result[ "point" ] = [ + intersects[ i ].point.x, + intersects[ i ].point.y, + intersects[ i ].point.z ]; + intersectedNode = intersects[ i ].object; + while ( !intersectedNode.vwfID ) { + intersectedNode = intersectedNode.parent; + } + result[ "node" ] = kutility.nodeReference( intersectedNode.vwfID ); + result[ "normal" ] = [ + intersects[ i ].face.normal.x, + intersects[ i ].face.normal.y, + intersects[ i ].face.normal.z ]; + results[ i ] = result; + } + + return results; } @@ -2750,18 +2773,6 @@ define( [ "module", return false; } - function getPrototypes( kernel, extendsID ) { - var prototypes = []; - var id = extendsID; - - while ( id !== undefined ) { - prototypes.push( id ); - id = kernel.prototype( id ); - } - - return prototypes; - } - function getThreeScene( id ) { if ( id === undefined ) { id = this.kernel.application(); @@ -3492,51 +3503,51 @@ define( [ "module", //walk the graph of an object, and set all materials to new material clones function cloneMaterials( nodein ) { - - //sort the materials in the model, and when cloneing, make the new model share the same material setup as the old. - var materialMap = {}; - + + //sort the materials in the model, and when cloneing, make the new model share the same material setup as the old. + var materialMap = {}; + walkGraph( nodein, function( node ) { if(node.material) { if ( node.material instanceof THREE.Material ) { if(!materialMap[node.material.uuid]) { - materialMap[node.material.uuid] = []; + materialMap[node.material.uuid] = []; } - materialMap[node.material.uuid].push( [ node, -1 ] ); + materialMap[node.material.uuid].push( [ node, -1 ] ); } else if ( node.material instanceof THREE.MeshFaceMaterial ) { if ( node.material.materials ) { for ( var index = 0; index < node.material.materials.length; index++ ) { if ( node.material.materials[ index ] instanceof THREE.Material ) { if(!materialMap[node.material.materials[ index ].uuid]) { - materialMap[node.material.materials[ index ].uuid] = []; + materialMap[node.material.materials[ index ].uuid] = []; } - materialMap[node.material.materials[ index ].uuid].push( [ node, index ] ); + materialMap[node.material.materials[ index ].uuid].push( [ node, index ] ); } } } } } }); - - for(var i in materialMap) - { - var newmat; + + for(var i in materialMap) + { + var newmat; if ( materialMap[ i ][ 0 ][ 1 ] < 0 ) { newmat = materialMap[ i ][ 0 ][ 0 ].material.clone( ); } else { newmat = materialMap[ i ][ 0 ][ 0 ].material.materials[ materialMap[ i ][ 0 ][ 1 ] ].clone( ); } - for ( var j =0; j < materialMap[i].length; j++ ) { + for ( var j =0; j < materialMap[i].length; j++ ) { if ( materialMap[ i ][ j ][ 1 ] < 0 ) { - materialMap[ i ][ j ][ 0 ].material = newmat; + materialMap[ i ][ j ][ 0 ].material = newmat; } else { materialMap[ i ][ j ][ 0 ].material.materials[ materialMap[ i ][ j ][ 1 ] ] = newmat; } } - } + } } function loadAsset( parentNode, node, childType, propertyNotifyCallback ) { @@ -3642,7 +3653,7 @@ define( [ "module", //find and bind the animations //NOTE: this would probably be better handled by walking and finding the animations and skins only on the //property setter when needed. - + animatedMesh = []; walkGraph(nodeCopy.threeObject,function( node ){ if( node instanceof THREE.SkinnedMesh || node instanceof THREE.MorphAnimMesh ) { @@ -3857,7 +3868,7 @@ define( [ "module", //no download or parse necessary else if( reg.loaded == true && reg.pending == false ) { var asset = (reg.node.clone()); - + // make sure the materails are unique cloneMaterials( asset ); @@ -4049,60 +4060,60 @@ define( [ "module", //default material expects all computation done cpu side, just renders - // note that since the color, size, spin and orientation are just linear - // interpolations, they can be done in the shader + // note that since the color, size, spin and orientation are just linear + // interpolations, they can be done in the shader var vertShader_default = "attribute float size; \n"+ "attribute vec4 vertexColor;\n"+ "varying vec4 vColor;\n"+ - "attribute vec4 random;\n"+ - "varying vec4 vRandom;\n"+ - "uniform float sizeRange;\n"+ - "uniform vec4 colorRange;\n"+ + "attribute vec4 random;\n"+ + "varying vec4 vRandom;\n"+ + "uniform float sizeRange;\n"+ + "uniform vec4 colorRange;\n"+ "void main() {\n"+ " vColor = vertexColor + (random -0.5) * colorRange;\n"+ " vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );\n"+ - " float psize = size + (random.y -0.5) * sizeRange;\n"+ + " float psize = size + (random.y -0.5) * sizeRange;\n"+ " gl_PointSize = psize * ( 1000.0/ length( mvPosition.xyz ) );\n"+ " gl_Position = projectionMatrix * mvPosition;\n"+ - " vRandom = random;"+ + " vRandom = random;"+ "} \n"; var fragShader_default = "uniform float useTexture;\n"+ "uniform sampler2D texture;\n"+ "varying vec4 vColor;\n"+ - "varying vec4 vRandom;\n"+ - "uniform float time;\n"+ + "varying vec4 vRandom;\n"+ + "uniform float time;\n"+ "uniform float maxSpin;\n"+ "uniform float minSpin;\n"+ - "uniform float maxOrientation;\n"+ + "uniform float maxOrientation;\n"+ "uniform float minOrientation;\n"+ - "uniform float textureTiles;\n"+ + "uniform float textureTiles;\n"+ "void main() {\n"+ - " vec2 coord = vec2(0.0,0.0);"+ - " vec2 orig_coord = vec2(gl_PointCoord.s,1.0-gl_PointCoord.t);"+ + " vec2 coord = vec2(0.0,0.0);"+ + " vec2 orig_coord = vec2(gl_PointCoord.s,1.0-gl_PointCoord.t);"+ " float spin = mix(maxSpin,minSpin,vRandom.x);"+ " float orientation = mix(maxOrientation,minOrientation,vRandom.y);"+ " coord.s = (orig_coord.s-.5)*cos(time*spin+orientation)-(orig_coord.t-.5)*sin(time*spin+orientation);"+ " coord.t = (orig_coord.t-.5)*cos(time*spin+orientation)+(orig_coord.s-.5)*sin(time*spin+orientation);"+ - " coord = coord + vec2(.5,.5);\n"+ - " coord = coord/textureTiles;\n"+ - " coord.x = clamp(coord.x,0.0,1.0/textureTiles);\n"+ - " coord.y = clamp(coord.y,0.0,1.0/textureTiles);\n"+ - " coord += vec2(floor(vRandom.x*textureTiles)/textureTiles,floor(vRandom.y*textureTiles)/textureTiles);\n"+ + " coord = coord + vec2(.5,.5);\n"+ + " coord = coord/textureTiles;\n"+ + " coord.x = clamp(coord.x,0.0,1.0/textureTiles);\n"+ + " coord.y = clamp(coord.y,0.0,1.0/textureTiles);\n"+ + " coord += vec2(floor(vRandom.x*textureTiles)/textureTiles,floor(vRandom.y*textureTiles)/textureTiles);\n"+ " vec4 outColor = (vColor * texture2D( texture, coord )) *useTexture + vColor * (1.0-useTexture);\n"+ " gl_FragColor = outColor;\n"+ "}\n"; - - //the default shader - the one used by the analytic solver, just has some simple stuff - //note that this could be changed to do just life and lifespan, and calculate the - //size and color from to uniforms. Im not going to bother + + //the default shader - the one used by the analytic solver, just has some simple stuff + //note that this could be changed to do just life and lifespan, and calculate the + //size and color from to uniforms. Im not going to bother var attributes_default = { size: { type: 'f', value: [] }, vertexColor: { type: 'v4', value: [] }, - random: { type: 'v4', value: [] }, - + random: { type: 'v4', value: [] }, + }; var uniforms_default = { amplitude: { type: "f", value: 1.0 }, @@ -4110,14 +4121,14 @@ define( [ "module", useTexture: { type: "f", value: 0.0 }, maxSpin: { type: "f", value: 0.0 }, minSpin: { type: "f", value: 0.0 }, - maxOrientation: { type: "f", value: 0.0 }, + maxOrientation: { type: "f", value: 0.0 }, minOrientation: { type: "f", value: 0.0 }, - time: { type: "f", value: 0.0 }, - fractime: { type: "f", value: 0.0 }, - sizeRange: { type: "f", value: 0.0 }, - textureTiles: { type: "f", value: 1.0 }, - colorRange: { type: 'v4', value: new THREE.Vector4(0,0,0,0) }, - startColor:{type: "v4", value:new THREE.Vector4()}, + time: { type: "f", value: 0.0 }, + fractime: { type: "f", value: 0.0 }, + sizeRange: { type: "f", value: 0.0 }, + textureTiles: { type: "f", value: 1.0 }, + colorRange: { type: 'v4', value: new THREE.Vector4(0,0,0,0) }, + startColor:{type: "v4", value:new THREE.Vector4()}, endColor:{type: "v4", value:new THREE.Vector4()}, startSize:{type:"f", value:1}, endSize:{type:"f", value:1}, @@ -4131,40 +4142,40 @@ define( [ "module", }); - //the interpolate shader blends from one simulation step to the next on the shader - //this allows for a complex sim to run at a low framerate, but still have smooth motion - //this is very efficient, as it only requires sending data up to the gpu on each sim tick - //reuse the frag shader from the normal material + //the interpolate shader blends from one simulation step to the next on the shader + //this allows for a complex sim to run at a low framerate, but still have smooth motion + //this is very efficient, as it only requires sending data up to the gpu on each sim tick + //reuse the frag shader from the normal material var vertShader_interpolate = - "attribute float age; \n"+ - "attribute float lifespan; \n"+ - "attribute vec3 previousPosition;\n"+ + "attribute float age; \n"+ + "attribute float lifespan; \n"+ + "attribute vec3 previousPosition;\n"+ "varying vec4 vColor;\n"+ - "attribute vec4 random;\n"+ - "varying vec4 vRandom;\n"+ - "uniform float sizeRange;\n"+ - "uniform vec4 colorRange;\n"+ - "uniform float fractime;\n"+ - "uniform float startSize;\n"+ + "attribute vec4 random;\n"+ + "varying vec4 vRandom;\n"+ + "uniform float sizeRange;\n"+ + "uniform vec4 colorRange;\n"+ + "uniform float fractime;\n"+ + "uniform float startSize;\n"+ "uniform float endSize;\n"+ "uniform vec4 startColor;\n"+ "uniform vec4 endColor;\n"+ "void main() {\n"+ " vColor = mix(startColor,endColor,(age+fractime*3.33)/lifespan) + (random -0.5) * colorRange;\n"+ " vec4 mvPosition = modelViewMatrix * vec4(mix(previousPosition,position,fractime), 1.0 );\n"+ - " float psize = mix(startSize,endSize,(age+fractime*3.33)/lifespan) + (random.y -0.5) * sizeRange;\n"+ + " float psize = mix(startSize,endSize,(age+fractime*3.33)/lifespan) + (random.y -0.5) * sizeRange;\n"+ " gl_PointSize = psize * ( 1000.0/ length( mvPosition.xyz ) );\n"+ " gl_Position = projectionMatrix * mvPosition;\n"+ - " vRandom = random;"+ + " vRandom = random;"+ "} \n"; - //the interpolation does need to remember the previous position - var attributes_interpolate = { - random: attributes_default.random, - previousPosition: { type: 'v3', value: [] }, - age: { type: 'f', value: [] }, - lifespan: { type: 'f', value: [] } + //the interpolation does need to remember the previous position + var attributes_interpolate = { + random: attributes_default.random, + previousPosition: { type: 'v3', value: [] }, + age: { type: 'f', value: [] }, + lifespan: { type: 'f', value: [] } }; var shaderMaterial_interpolate = new THREE.ShaderMaterial( { uniforms: uniforms_default, @@ -4176,12 +4187,12 @@ define( [ "module", //analytic shader does entire simulation on GPU - //it cannot account for drag, gravity. nor can it generate new randomness. Each particle has it's randomness assigned and it - //just repeats the same motion over and over. Also, the other solvers can hold a particle until - //it can be reused based on the emitRate. This cannot, as the entire life of the particle must be - //computed from an equation given just time t. It does offset them in time to avoid all the particles - //being generated at once. Also, it does not account for emitter motion. - //upside : very very efficient. No CPU intervention required + //it cannot account for drag, gravity. nor can it generate new randomness. Each particle has it's randomness assigned and it + //just repeats the same motion over and over. Also, the other solvers can hold a particle until + //it can be reused based on the emitRate. This cannot, as the entire life of the particle must be + //computed from an equation given just time t. It does offset them in time to avoid all the particles + //being generated at once. Also, it does not account for emitter motion. + //upside : very very efficient. No CPU intervention required var vertShader_analytic = "attribute float size; \n"+ "attribute vec4 vertexColor;\n"+ @@ -4196,20 +4207,20 @@ define( [ "module", "uniform vec4 endColor;\n"+ "varying vec4 vColor;\n"+ "varying vec4 vRandom;\n"+ - "uniform float sizeRange;\n"+ - "uniform vec4 colorRange;\n"+ + "uniform float sizeRange;\n"+ + "uniform vec4 colorRange;\n"+ "void main() {\n"+ - //randomly offset in time + //randomly offset in time " float lifetime = mod( random.x * lifespan + time, lifespan );"+ - //solve for position + //solve for position " vec3 pos2 = position.xyz + velocity*lifetime + (acceleration*lifetime*lifetime)/2.0;"+ // ; " vec4 mvPosition = modelViewMatrix * vec4( pos2.xyz, 1.0 );\n"+ - //find random size based on randomness, start and end size, and size range - " float psize = mix(startSize,endSize,lifetime/lifespan) + (random.y -0.5) * sizeRange;\n"+ + //find random size based on randomness, start and end size, and size range + " float psize = mix(startSize,endSize,lifetime/lifespan) + (random.y -0.5) * sizeRange;\n"+ " gl_PointSize = psize * ( 1000.0/ length( mvPosition.xyz ) );\n"+ " gl_Position = projectionMatrix * mvPosition;\n"+ - " vec4 nR = (random -0.5);\n"+ - //find random color based on start and endcolor, time and colorRange + " vec4 nR = (random -0.5);\n"+ + //find random color based on start and endcolor, time and colorRange " vColor = mix(startColor,endColor,lifetime/lifespan) + nR * colorRange;\n"+ " vRandom = random;"+ "} \n"; @@ -4221,27 +4232,27 @@ define( [ "module", "uniform float minSpin;\n"+ "varying vec4 vColor;\n"+ "varying vec4 vRandom;\n"+ - "uniform float maxOrientation;\n"+ + "uniform float maxOrientation;\n"+ "uniform float minOrientation;\n"+ - "uniform float textureTiles;\n"+ + "uniform float textureTiles;\n"+ "void main() {\n"+ - //bit of drama for dividing into 4 or 9 'virtual' textures - //nice to be able to have different images on particles - " vec2 coord = vec2(0.0,0.0);"+ - " vec2 orig_coord = vec2(gl_PointCoord.s,1.0-gl_PointCoord.t);"+ + //bit of drama for dividing into 4 or 9 'virtual' textures + //nice to be able to have different images on particles + " vec2 coord = vec2(0.0,0.0);"+ + " vec2 orig_coord = vec2(gl_PointCoord.s,1.0-gl_PointCoord.t);"+ " float spin = mix(maxSpin,minSpin,vRandom.x);"+ " float orientation = mix(maxOrientation,minOrientation,vRandom.y);"+ " coord.s = (orig_coord.s-.5)*cos(time*spin+orientation)-(orig_coord.t-.5)*sin(time*spin+orientation);"+ " coord.t = (orig_coord.t-.5)*cos(time*spin+orientation)+(orig_coord.s-.5)*sin(time*spin+orientation);"+ - " coord = coord + vec2(.5,.5);\n"+ - " coord = coord/textureTiles;\n"+ - " coord.x = clamp(coord.x,0.0,1.0/textureTiles);\n"+ - " coord.y = clamp(coord.y,0.0,1.0/textureTiles);\n"+ - " coord += vec2(floor(vRandom.x*textureTiles)/textureTiles,floor(vRandom.y*textureTiles)/textureTiles);\n"+ + " coord = coord + vec2(.5,.5);\n"+ + " coord = coord/textureTiles;\n"+ + " coord.x = clamp(coord.x,0.0,1.0/textureTiles);\n"+ + " coord.y = clamp(coord.y,0.0,1.0/textureTiles);\n"+ + " coord += vec2(floor(vRandom.x*textureTiles)/textureTiles,floor(vRandom.y*textureTiles)/textureTiles);\n"+ - //get the color from the texture and blend with the vertexColor. - " vec4 outColor = (vColor * texture2D( texture, coord )) *useTexture + vColor * (1.0-useTexture);\n"+ + //get the color from the texture and blend with the vertexColor. + " vec4 outColor = (vColor * texture2D( texture, coord )) *useTexture + vColor * (1.0-useTexture);\n"+ " gl_FragColor = outColor;\n"+ "}\n"; @@ -4263,12 +4274,12 @@ define( [ "module", // create the particle system var particleSystem = new THREE.PointCloud( particles, shaderMaterial_default ); - //keep track of the shaders + //keep track of the shaders particleSystem.shaderMaterial_analytic = shaderMaterial_analytic; particleSystem.shaderMaterial_default = shaderMaterial_default; - particleSystem.shaderMaterial_interpolate = shaderMaterial_interpolate; + particleSystem.shaderMaterial_interpolate = shaderMaterial_interpolate; - //setup all the default values + //setup all the default values particleSystem.minVelocity = [0,0,0]; particleSystem.maxVelocity = [0,0,0]; particleSystem.maxAcceleration = [0,0,0]; @@ -4285,54 +4296,54 @@ define( [ "module", particleSystem.damping = 0; particleSystem.startSize = 3; particleSystem.endSize = 3; - particleSystem.gravity = 0; - particleSystem.gravityCenter = [0,0,0]; + particleSystem.gravity = 0; + particleSystem.gravityCenter = [0,0,0]; particleSystem.velocityMode = 'cartesian'; - particleSystem.temp = new THREE.Vector3(); + particleSystem.temp = new THREE.Vector3(); - //create a new particle. create and store all the values for vertex attributes in each shader - particleSystem.createParticle = function(i) + //create a new particle. create and store all the values for vertex attributes in each shader + particleSystem.createParticle = function(i) { var particle = new THREE.Vector3(0,0,0); this.geometry.vertices.push(particle); particle.i = i; - //the world space position + //the world space position particle.world = new THREE.Vector3(); - //the previous !tick! (not frame) position + //the previous !tick! (not frame) position particle.prevworld = new THREE.Vector3(); - this.shaderMaterial_interpolate.attributes.previousPosition.value.push(particle.prevworld); + this.shaderMaterial_interpolate.attributes.previousPosition.value.push(particle.prevworld); //the color - var color = new THREE.Vector4(1,1,1,1); + var color = new THREE.Vector4(1,1,1,1); this.shaderMaterial_default.attributes.vertexColor.value.push(color); - //age - this.shaderMaterial_interpolate.attributes.age.value.push(1); + //age + this.shaderMaterial_interpolate.attributes.age.value.push(1); particle.color = color; - - //the sise + + //the sise this.shaderMaterial_default.attributes.size.value.push(1); var self = this; - //set the size - stored per vertex + //set the size - stored per vertex particle.setSize = function(s) { self.material.attributes.size.value[this.i] = s; } - //set the age - stored per vertex - particle.setAge = function(a) - { - this.age = a; - self.shaderMaterial_interpolate.attributes.age.value[this.i] = this.age; - } - //the lifespan - stored per vertex - particle.setLifespan = function(a) - { - this.lifespan = a; - self.shaderMaterial_interpolate.attributes.lifespan.value[this.i] = this.a; - } + //set the age - stored per vertex + particle.setAge = function(a) + { + this.age = a; + self.shaderMaterial_interpolate.attributes.age.value[this.i] = this.age; + } + //the lifespan - stored per vertex + particle.setLifespan = function(a) + { + this.lifespan = a; + self.shaderMaterial_interpolate.attributes.lifespan.value[this.i] = this.a; + } - //This looks like it could be computed from the start and end plus random on the shader - //doing this saves computetime on the shader at expense of gpu mem + //This looks like it could be computed from the start and end plus random on the shader + //doing this saves computetime on the shader at expense of gpu mem shaderMaterial_analytic.attributes.acceleration.value.push(new THREE.Vector3()); shaderMaterial_analytic.attributes.velocity.value.push(new THREE.Vector3()); shaderMaterial_analytic.attributes.lifespan.value.push(1); @@ -4340,17 +4351,17 @@ define( [ "module", return particle; } - //Generate a new point in space based on the emitter type and size + //Generate a new point in space based on the emitter type and size particleSystem.generatePoint = function() { - //generate from a point - //TODO: specify point? + //generate from a point + //TODO: specify point? if(this.emitterType.toLowerCase() == 'point') { return new THREE.Vector3(0,0,0); } - //Generate in a box - //assumes centered at 0,0,0 + //Generate in a box + //assumes centered at 0,0,0 if(this.emitterType.toLowerCase() == 'box') { var x = this.emitterSize[0] * Math.random() - this.emitterSize[0]/2; @@ -4359,8 +4370,8 @@ define( [ "module", return new THREE.Vector3(x,y,z); } - //Generate in a sphere - //assumes centered at 0,0,0 + //Generate in a sphere + //assumes centered at 0,0,0 if(this.emitterType.toLowerCase() == 'sphere') { var u2 = Math.random(); @@ -4377,31 +4388,31 @@ define( [ "module", } } - //setup the particles with new values + //setup the particles with new values particleSystem.rebuildParticles = function() { - for(var i = 0; i < this.geometry.vertices.length; i++) + for ( var i = 0; i < this.geometry.vertices.length; i++ ) { - this.setupParticle(this.geometry.vertices[i],this.matrix); + this.setupParticle( this.geometry.vertices[ i ], this.matrix ); } } - //set the particles initial values. Used when creating and resuing particles - particleSystem.setupParticle = function(particle,mat,inv) + //set the particles initial values. Used when creating and resuing particles + particleSystem.setupParticle = function( particle, mat, inv ) { particle.x = 0; particle.y = 0; particle.z = 0; - //generate a point in objects space, the move to world space + //generate a point in objects space, the move to world space particle.world = this.generatePoint().applyMatrix4( mat ); - //back up initial (needed by the analyticShader) + //back up initial (needed by the analyticShader) particle.initialx = particle.world.x; particle.initialy = particle.world.y; particle.initialz = particle.world.z; - //start at initial pos + //start at initial pos particle.x = particle.initialx; particle.y = particle.initialy; particle.z = particle.initialz; @@ -4413,16 +4424,16 @@ define( [ "module", particle.acceleration = new THREE.Vector3( 0,0,0); particle.lifespan = 1; - //Generate the initial velocity - //In this mode, you specify a min and max x,y,z + //Generate the initial velocity + //In this mode, you specify a min and max x,y,z if(this.velocityMode == 'cartesian') { particle.velocity.x = this.minVelocity[0] + (this.maxVelocity[0] - this.minVelocity[0]) * Math.random(); particle.velocity.y = this.minVelocity[1] + (this.maxVelocity[1] - this.minVelocity[1]) * Math.random(); particle.velocity.z = this.minVelocity[2] + (this.maxVelocity[2] - this.minVelocity[2]) * Math.random(); } - //In this mode, you give a pitch and yaw from 0,1, and a min and max length. - //This is easier to emit into a circle, or a cone section + //In this mode, you give a pitch and yaw from 0,1, and a min and max length. + //This is easier to emit into a circle, or a cone section if(this.velocityMode == 'spherical') { @@ -4434,7 +4445,7 @@ define( [ "module", particle.velocity.y = r * Math.sin(t)*Math.sin(w); particle.velocity.z = r * Math.cos(t); */ - //better distribution + //better distribution var o = this.minVelocity[0] + (this.maxVelocity[0] - this.minVelocity[0]) * Math.random() * Math.PI*2; var u = this.minVelocity[1] + (this.maxVelocity[1] - this.minVelocity[1]) * Math.random() * 2 - 1; var u2 = Math.random(); @@ -4446,8 +4457,8 @@ define( [ "module", particle.velocity.setLength(r); } - //The velocity should be in world space, but is generated in local space for - //ease of use + //The velocity should be in world space, but is generated in local space for + //ease of use mat = mat.clone(); mat.elements[12] = 0; mat.elements[13] = 0; @@ -4460,13 +4471,13 @@ define( [ "module", particle.acceleration.z = this.minAcceleration[2] + (this.maxAcceleration[2] - this.minAcceleration[2]) * Math.random(); particle.setLifespan(this.minLifeTime + (this.maxLifeTime - this.minLifeTime) * Math.random()); - //color is start color - particle.color.x = this.startColor[0]; + //color is start color + particle.color.x = this.startColor[0]; particle.color.y = this.startColor[1]; particle.color.z = this.startColor[2]; particle.color.w = this.startColor[3]; - //save the values into the attributes + //save the values into the attributes shaderMaterial_analytic.attributes.acceleration.value[particle.i] = (particle.acceleration); shaderMaterial_analytic.attributes.velocity.value[particle.i] = (particle.velocity); shaderMaterial_analytic.attributes.lifespan.value[particle.i] = (particle.lifespan); @@ -4483,53 +4494,58 @@ define( [ "module", } - //when updating in AnalyticShader mode, is very simple, just inform the shader of new time. - particleSystem.updateAnalyticShader = function(time) + //when updating in AnalyticShader mode, is very simple, just inform the shader of new time. + particleSystem.updateAnalyticShader = function( time ) { - particleSystem.material.uniforms.time.value += time/1000; + particleSystem.material.uniforms.time.value += time / 1000; } - //In Analytic mode, run the equation for the position - particleSystem.updateAnalytic =function(time) + //In Analytic mode, run the equation for the position + particleSystem.updateAnalytic = function( time ) { - particleSystem.material.uniforms.time.value += time/3333.0; - - var time_in_ticks = time/33.333; + var timeInSeconds = time / 1000; + particleSystem.material.uniforms.time.value += timeInSeconds; var inv = this.matrix.clone(); - inv = inv.getInverse(inv); + inv = inv.getInverse( inv ); var particles = this.geometry; - - //update each particle + + //update each particle var pCount = this.geometry.vertices.length; - while(pCount--) + while ( pCount-- ) { - var particle =particles.vertices[pCount]; - this.updateParticleAnalytic(particle,this.matrix,inv,time_in_ticks); + var particle = particles.vertices[ pCount ]; + this.updateParticleAnalytic( particle, this.matrix, inv, timeInSeconds ); } //examples developed with faster tick - maxrate *33 is scale to make work //with new timing - //Reuse up to maxRate particles, sliced for delta_time - //Once a particle reaches it's end of life, its available to be regenerated. - //We hold extras in limbo with alpha 0 until they can be regenerated - //Note the maxRate never creates or destroys particles, just manages when they will restart - //after dying - var len = Math.min(this.regenParticles.length,this.maxRate*15*time_in_ticks); - for(var i =0; i < len; i++) + //Reuse up to maxRate particles, sliced for delta_time + //Once a particle reaches it's end of life, its available to be regenerated. + //We hold extras in limbo with alpha 0 until they can be regenerated + //Note the maxRate never creates or destroys particles, just manages when they will restart + //after dying + if ( this.timeSinceLastSpawn === undefined ) { + this.timeSinceLastSpawn = 0; + } else { + this.timeSinceLastSpawn += timeInSeconds; + } + var maxParticlesToSpawn = Math.floor( this.timeSinceLastSpawn * this.maxRate ); + var len = Math.min( this.regenParticles.length, maxParticlesToSpawn ); + for ( var i = 0; i < len; i++ ) { - - //setup with new random values, and move randomly forward in time one step + //setup with new random values, and move randomly forward in time one step var particle = this.regenParticles.shift(); - this.setupParticle(particle,this.matrix,inv); - this.updateParticleAnalytic(particle,this.matrix,inv,Math.random()*3.33); + this.setupParticle( particle, this.matrix, inv ); + this.updateParticleAnalytic( particle, this.matrix, inv, Math.random() * timeInSeconds ); particle.waitForRegen = false; } - - - //only these things change, other properties are in the shader as they are linear WRT time + + this.timeSinceLastSpawn = Boolean( len ) ? 0 : this.timeSinceLastSpawn; + + //only these things change, other properties are in the shader as they are linear WRT time this.geometry.verticesNeedUpdate = true; this.geometry.colorsNeedUpdate = true; this.material.attributes.vertexColor.needsUpdate = true; @@ -4541,156 +4557,156 @@ define( [ "module", particleSystem.totaltime = 0; //timesliced Euler integrator //todo: switch to RK4 - //This can do more complex sim, maybe even a cloth sim or such. It ticks 10 times a second, and blends tick with previous via a shader + //This can do more complex sim, maybe even a cloth sim or such. It ticks 10 times a second, and blends tick with previous via a shader particleSystem.updateEuler = function(time) { - particleSystem.material.uniforms.time.value += time/3333.0; + particleSystem.material.uniforms.time.value += time/3333.0; var time_in_ticks = time/100.0; if(this.lastTime === undefined) this.lastTime = 0; this.lastTime += time_in_ticks;//ticks - Math.floor(ticks); - var inv = this.matrix.clone(); - inv = inv.getInverse(inv); - - var particles = this.geometry; - - //timesliced tick give up after 5 steps - just cant go fast enough - if(Math.floor(this.lastTime) > 5) - this.lastTime = 1; - for(var i=0; i < Math.floor(this.lastTime) ; i++) - { - this.lastTime--; - - var pCount = this.geometry.vertices.length; - while(pCount--) - { - var particle =particles.vertices[pCount]; - this.updateParticleEuler(particle,this.matrix,inv,3.333); - } - - //examples developed with faster tick - maxrate *33 is scale to make work - //with new timing - - //Reuse up to maxRate particles, sliced for delta_time - //Once a particle reaches it's end of life, its available to be regenerated. - //We hold extras in limbo with alpha 0 until they can be regenerated - //Note the maxRate never creates or destroys particles, just manages when they will restart - //after dying - var len = Math.min(this.regenParticles.length,this.maxRate*333); - for(var i =0; i < len; i++) - { - - particle.waitForRegen = false; - var particle = this.regenParticles.shift(); - this.setupParticle(particle,this.matrix,inv); - this.updateParticleEuler(particle,this.matrix,inv,Math.random()*3.33); - this.material.attributes.lifespan.needsUpdate = true; - } - - //only need to send up the age, position, and previous position. other props handled in the shader - this.geometry.verticesNeedUpdate = true; - this.material.attributes.previousPosition.needsUpdate = true; - - this.material.attributes.age.needsUpdate = true; - - } - - //even if this is not a sim tick, we need to send the fractional time up to the shader for the interpolation - this.material.uniforms.fractime.value = this.lastTime; - - } - - //Update a particle from the Analytic solver - particleSystem.updateParticleAnalytic = function(particle,mat,inv,delta_time) - { - particle.age += delta_time; - - //Make the particle dead. Hide it until it can be reused - if(particle.age >= particle.lifespan && !particle.waitForRegen) - { - this.regenParticles.push(particle); - particle.waitForRegen = true; - particle.x = 0; - particle.y = 0; - particle.z = 0; - particle.color.w = 0.0; - }else - { - //Run the formula to get position. - var percent = particle.age/particle.lifespan; - particle.world.x = particle.initialx + (particle.velocity.x * particle.age) + 0.5*(particle.acceleration.x * particle.age * particle.age) - particle.world.y = particle.initialy + (particle.velocity.y * particle.age) + 0.5*(particle.acceleration.y * particle.age * particle.age) - particle.world.z = particle.initialz + (particle.velocity.z * particle.age) + 0.5*(particle.acceleration.z * particle.age * particle.age) - - this.temp.x = particle.world.x; - this.temp.y = particle.world.y; - this.temp.z = particle.world.z; - - //need to specify in object space, event though comptued in local - this.temp.applyMatrix4( inv ); - particle.x = this.temp.x; - particle.y = this.temp.y; - particle.z = this.temp.z; - - //Should probably move this to the shader. Linear with time, no point in doing on CPU - particle.color.x = this.startColor[0] + (this.endColor[0] - this.startColor[0]) * percent; - particle.color.y = this.startColor[1] + (this.endColor[1] - this.startColor[1]) * percent; - particle.color.z = this.startColor[2] + (this.endColor[2] - this.startColor[2]) * percent; - particle.color.w = this.startColor[3] + (this.endColor[3] - this.startColor[3]) * percent; - - particle.setSize(this.startSize + (this.endSize - this.startSize) * percent); - } - } - - //updtae a partilce with the Euler solver - particleSystem.updateParticleEuler = function(particle,mat,inv,step_dist) - { - particle.prevage = particle.age; - particle.age += step_dist; - particle.setAge(particle.age + step_dist); - - //If the particle is dead ,hide it unitl it can be reused - if(particle.age >= particle.lifespan && !particle.waitForRegen) - { - - this.regenParticles.push(particle); - particle.waitForRegen = true; - particle.x = 0; - particle.y = 0; - particle.z = 0; - particle.world.x = 0; - particle.world.y = 0; - particle.world.z = 0; - particle.prevworld.x = 0; - particle.prevworld.y = 0; - particle.prevworld.z = 0; - particle.color.w = 1.0; - particle.size = 100; - }else - { - - - // and the position - particle.prevworld.x = particle.world.x; - particle.prevworld.y = particle.world.y; - particle.prevworld.z = particle.world.z; - - //find direction to center for gravity - var gravityAccel = new THREE.Vector3(particle.world.x,particle.world.y,particle.world.z); - gravityAccel.x -= this.gravityCenter[0]; - gravityAccel.y -= this.gravityCenter[1]; - gravityAccel.z -= this.gravityCenter[2]; - var len = gravityAccel.length()+.1; - gravityAccel.normalize(); - gravityAccel.multiplyScalar(-Math.min(1/(len*len),100)); - gravityAccel.multiplyScalar(this.gravity); - - //update position - particle.world.x += particle.velocity.x * step_dist + (particle.acceleration.x + gravityAccel.x)* step_dist * step_dist; - particle.world.y += particle.velocity.y * step_dist + (particle.acceleration.y + gravityAccel.y )* step_dist * step_dist;; - particle.world.z += particle.velocity.z * step_dist + (particle.acceleration.z + gravityAccel.z )* step_dist * step_dist;; + var inv = this.matrix.clone(); + inv = inv.getInverse(inv); + + var particles = this.geometry; + + //timesliced tick give up after 5 steps - just cant go fast enough + if(Math.floor(this.lastTime) > 5) + this.lastTime = 1; + for(var i=0; i < Math.floor(this.lastTime) ; i++) + { + this.lastTime--; + + var pCount = this.geometry.vertices.length; + while(pCount--) + { + var particle =particles.vertices[pCount]; + this.updateParticleEuler(particle,this.matrix,inv,3.333); + } + + //examples developed with faster tick - maxrate *33 is scale to make work + //with new timing + + //Reuse up to maxRate particles, sliced for delta_time + //Once a particle reaches it's end of life, its available to be regenerated. + //We hold extras in limbo with alpha 0 until they can be regenerated + //Note the maxRate never creates or destroys particles, just manages when they will restart + //after dying + var len = Math.min(this.regenParticles.length,this.maxRate*333); + for(var i =0; i < len; i++) + { + + particle.waitForRegen = false; + var particle = this.regenParticles.shift(); + this.setupParticle(particle,this.matrix,inv); + this.updateParticleEuler(particle,this.matrix,inv,Math.random()*3.33); + this.material.attributes.lifespan.needsUpdate = true; + } + + //only need to send up the age, position, and previous position. other props handled in the shader + this.geometry.verticesNeedUpdate = true; + this.material.attributes.previousPosition.needsUpdate = true; + + this.material.attributes.age.needsUpdate = true; + + } + + //even if this is not a sim tick, we need to send the fractional time up to the shader for the interpolation + this.material.uniforms.fractime.value = this.lastTime; + + } + + //Update a particle from the Analytic solver + particleSystem.updateParticleAnalytic = function( particle, mat, inv, delta_time ) + { + particle.age += delta_time; + + //Make the particle dead. Hide it until it can be reused + if ( particle.age >= particle.lifespan ) // && !particle.waitForRegen ) + { + this.regenParticles.push( particle ); + particle.waitForRegen = true; + particle.x = 0; + particle.y = 0; + particle.z = 0; + particle.color.w = 0.0; + }else + { + //Run the formula to get position. + var percent = particle.age / particle.lifespan; + particle.world.x = particle.initialx + ( particle.velocity.x * particle.age ) + 0.5 * ( particle.acceleration.x * particle.age * particle.age ) + particle.world.y = particle.initialy + ( particle.velocity.y * particle.age ) + 0.5 * ( particle.acceleration.y * particle.age * particle.age ) + particle.world.z = particle.initialz + ( particle.velocity.z * particle.age ) + 0.5 * ( particle.acceleration.z * particle.age * particle.age ) + + this.temp.x = particle.world.x; + this.temp.y = particle.world.y; + this.temp.z = particle.world.z; + + //need to specify in object space, event though comptued in local + this.temp.applyMatrix4( inv ); + particle.x = this.temp.x; + particle.y = this.temp.y; + particle.z = this.temp.z; + + //Should probably move this to the shader. Linear with time, no point in doing on CPU + particle.color.x = this.startColor[ 0 ] + ( this.endColor[ 0 ] - this.startColor[ 0 ] ) * percent; + particle.color.y = this.startColor[ 1 ] + ( this.endColor[ 1 ] - this.startColor[ 1 ] ) * percent; + particle.color.z = this.startColor[ 2 ] + ( this.endColor[ 2 ] - this.startColor[ 2 ] ) * percent; + particle.color.w = this.startColor[ 3 ] + ( this.endColor[ 3 ] - this.startColor[ 3 ] ) * percent; + + particle.setSize( this.startSize + ( this.endSize - this.startSize ) * percent ); + } + } + + //updtae a partilce with the Euler solver + particleSystem.updateParticleEuler = function(particle,mat,inv,step_dist) + { + particle.prevage = particle.age; + particle.age += step_dist; + particle.setAge(particle.age + step_dist); + + //If the particle is dead ,hide it unitl it can be reused + if(particle.age >= particle.lifespan && !particle.waitForRegen) + { + + this.regenParticles.push(particle); + particle.waitForRegen = true; + particle.x = 0; + particle.y = 0; + particle.z = 0; + particle.world.x = 0; + particle.world.y = 0; + particle.world.z = 0; + particle.prevworld.x = 0; + particle.prevworld.y = 0; + particle.prevworld.z = 0; + particle.color.w = 1.0; + particle.size = 100; + }else + { + + + // and the position + particle.prevworld.x = particle.world.x; + particle.prevworld.y = particle.world.y; + particle.prevworld.z = particle.world.z; + + //find direction to center for gravity + var gravityAccel = new THREE.Vector3(particle.world.x,particle.world.y,particle.world.z); + gravityAccel.x -= this.gravityCenter[0]; + gravityAccel.y -= this.gravityCenter[1]; + gravityAccel.z -= this.gravityCenter[2]; + var len = gravityAccel.length()+.1; + gravityAccel.normalize(); + gravityAccel.multiplyScalar(-Math.min(1/(len*len),100)); + gravityAccel.multiplyScalar(this.gravity); + + //update position + particle.world.x += particle.velocity.x * step_dist + (particle.acceleration.x + gravityAccel.x)* step_dist * step_dist; + particle.world.y += particle.velocity.y * step_dist + (particle.acceleration.y + gravityAccel.y )* step_dist * step_dist;; + particle.world.z += particle.velocity.z * step_dist + (particle.acceleration.z + gravityAccel.z )* step_dist * step_dist;; //update velocity particle.velocity.x += (particle.acceleration.x + gravityAccel.x) * step_dist * step_dist; @@ -4698,30 +4714,30 @@ define( [ "module", particle.velocity.z += (particle.acceleration.z + gravityAccel.z) * step_dist * step_dist var damping = 1-(this.damping * step_dist); - - //drag + + //drag particle.velocity.x *= damping; particle.velocity.y *= damping; particle.velocity.z *= damping; - - //move from world to local space - this.temp.x = particle.world.x ; - this.temp.y = particle.world.y ; - this.temp.z = particle.world.z; - this.temp.applyMatrix4( inv ); - particle.x = this.temp.x; - particle.y = this.temp.y; - particle.z = this.temp.z; - //careful to have prev and current pos in same space!!!! - particle.prevworld.applyMatrix4( inv ); + + //move from world to local space + this.temp.x = particle.world.x ; + this.temp.y = particle.world.y ; + this.temp.z = particle.world.z; + this.temp.applyMatrix4( inv ); + particle.x = this.temp.x; + particle.y = this.temp.y; + particle.z = this.temp.z; + //careful to have prev and current pos in same space!!!! + particle.prevworld.applyMatrix4( inv ); } } //Change the solver type for the system particleSystem.setSolverType =function(type) { - this.solver = type; + this.solver = type; if(type == 'Euler') { particleSystem.update = particleSystem.updateEuler; @@ -4743,63 +4759,65 @@ define( [ "module", } - //If you move a system, all the particles need to be recomputed to look like they stick in world space - //not that we pointedly dont do this for the AnalyticShader. We could, but that solver is ment to be very high performance, do we dont + //If you move a system, all the particles need to be recomputed to look like they stick in world space + //not that we pointedly dont do this for the AnalyticShader. We could, but that solver is ment to be very high performance, do we dont particleSystem.updateTransform = function(newtransform) { - - //Get he current transform, and invert new one - var inv = new THREE.Matrix4(); - var newt = new THREE.Matrix4(); - inv.elements = matCpy(newtransform); - newt = newt.copy(this.matrix); - inv = inv.getInverse(inv); - - - //don't adjust for the high performance shader - if(particleSystem.solver == 'AnalyticShader') - { - return; - } - - //Move all particles out of old space to world, then back into new space. - //this will make it seem like they stay at the correct position in the world, though - //acutally they change position - //note that it would actually be more efficient to leave the matrix as identity, and change the position of the - //emitters for this...... Could probably handle it in the model setter actually... would be much more efficient, but linking - //a system to a moving object would break. - for(var i =0; i < this.geometry.vertices.length; i++) - { - this.geometry.vertices[ i ].applyMatrix4( inv ); - this.shaderMaterial_interpolate.attributes.previousPosition.value[ i ].applyMatrix4( inv ); - this.geometry.vertices[ i ].applyMatrix4( newt ); - this.shaderMaterial_interpolate.attributes.previousPosition.value[ i ].applyMatrix4( newt ); - } - this.geometry.verticesNeedUpdate = true; - this.shaderMaterial_interpolate.attributes.previousPosition.needsUpdate = true; - + + //Get he current transform, and invert new one + var inv = new THREE.Matrix4(); + var newt = new THREE.Matrix4(); + inv.elements = matCpy(newtransform); + newt = newt.copy(this.matrix); + inv = inv.getInverse(inv); + + + //don't adjust for the high performance shader + if(particleSystem.solver == 'AnalyticShader') + { + return; + } + + //Move all particles out of old space to world, then back into new space. + //this will make it seem like they stay at the correct position in the world, though + //acutally they change position + //note that it would actually be more efficient to leave the matrix as identity, and change the position of the + //emitters for this...... Could probably handle it in the model setter actually... would be much more efficient, but linking + //a system to a moving object would break. + for(var i =0; i < this.geometry.vertices.length; i++) + { + this.geometry.vertices[ i ].applyMatrix4( inv ); + this.shaderMaterial_interpolate.attributes.previousPosition.value[ i ].applyMatrix4( inv ); + this.geometry.vertices[ i ].applyMatrix4( newt ); + this.shaderMaterial_interpolate.attributes.previousPosition.value[ i ].applyMatrix4( newt ); + } + this.geometry.verticesNeedUpdate = true; + this.shaderMaterial_interpolate.attributes.previousPosition.needsUpdate = true; + } - //Change the system count. Note that this must be set before the first frame renders, cant be changed at runtime. - particleSystem.setParticleCount = function(newcount) + //Change the system count. Note that this must be set before the first frame renders, cant be changed at runtime. + particleSystem.setParticleCount = function( newcount ) { var inv = this.matrix.clone(); - inv = inv.getInverse(inv); + inv = inv.getInverse( inv ); var particles = this.geometry; - while(this.geometry.vertices.length > newcount) + particles.vertices.length = 0; + // while(this.geometry.vertices.length > newcount) + // { + // this.geometry.vertices.pop(); + // } + this.regenParticles.length = 0; + while ( this.geometry.vertices.length < newcount ) { - this.geometry.vertices.pop(); - } - while(this.geometry.vertices.length < newcount) - { - var particle = particleSystem.createParticle(this.geometry.vertices.length); - particleSystem.setupParticle(particle,particleSystem.matrix,inv); + var particle = particleSystem.createParticle( this.geometry.vertices.length ); + particleSystem.setupParticle( particle, particleSystem.matrix, inv ); particle.age = Infinity; - this.regenParticles.push(particle); + this.regenParticles.push( particle ); particle.waitForRegen = true; } - this.geometry.verticesNeedUpdate = true; - this.geometry.colorsNeedUpdate = true; + this.geometry.verticesNeedUpdate = true; + this.geometry.colorsNeedUpdate = true; this.shaderMaterial_default.attributes.vertexColor.needsUpdate = true; this.particleCount = newcount; } @@ -5382,9 +5400,9 @@ define( [ "module", if(node.name && newnode) newnode.name = node.name; - if(newnode && newnode.children && newnode.children.length == 1 && isIdentityMatrix(newnode.matrix.elements)) - return newnode.children[0]; - + if(newnode && newnode.children && newnode.children.length == 1 && isIdentityMatrix(newnode.matrix.elements)) + return newnode.children[0]; + return newnode; } var blobsfound = 0; diff --git a/support/client/lib/vwf/model/threejs/js/loaders/ColladaLoader.js b/support/client/lib/vwf/model/threejs/js/loaders/ColladaLoader.js index 33f0b5745..f57dcca25 100644 --- a/support/client/lib/vwf/model/threejs/js/loaders/ColladaLoader.js +++ b/support/client/lib/vwf/model/threejs/js/loaders/ColladaLoader.js @@ -1153,7 +1153,7 @@ THREE.ColladaLoader = function () { } - material3js.opacity = !material3js.opacity ? 1 : material3js.opacity; + material3js.opacity = material3js.opacity === undefined ? 1 : material3js.opacity; used_materials[ instance_material.symbol ] = num_materials; used_materials_array.push( material3js ); first_material = material3js; @@ -3577,6 +3577,7 @@ THREE.ColladaLoader = function () { case 'emission': case 'diffuse': case 'specular': + case 'specularLevel': case 'transparent': this[ child.nodeName ] = ( new ColorOrTexture() ).parse( child ); @@ -3633,26 +3634,40 @@ THREE.ColladaLoader = function () { var props = {}; - var transparent = false; - - if (this['transparency'] !== undefined && this['transparent'] !== undefined) { + if ( this[ 'transparency' ] !== undefined && this[ 'transparent' ] !== undefined ) { // convert transparent color RBG to average value - var transparentColor = this['transparent']; - var transparencyLevel = (this.transparent.color.r + this.transparent.color.g + this.transparent.color.b) / 3 * this.transparency; - - if (transparencyLevel > 0) { - transparent = true; + var transparentColor = this[ 'transparent' ]; + var transparencyLevel = 0; + // Determine transparency level based on opaque mode + if ( transparentColor.opaque == "RGB_ONE" ) { + transparencyLevel = ( 3 - this.transparent.color.r - + this.transparent.color.g - + this.transparent.color.b ) / + 3 * this.transparency; + } else if ( transparentColor.opaque == "RGB_ZERO" ) { + transparencyLevel = ( this.transparent.color.r + + this.transparent.color.g + + this.transparent.color.b ) / + 3 * this.transparency; + } else if ( transparentColor.opaque == "A_ONE" ) { + transparencyLevel = ( 1 - this.transparent.color.a ) * this.transparency; + } else { // A_ZERO (default in collada 1.5.0) - http://www.khronos.org/files/collada_1_5_release_notes.pdf (pg 16) + transparencyLevel = this.transparent.color.a * this.transparency; + } + // Assumes all texures in the 'transparent' field will have an alpha channel + if ( transparentColor.isTexture() || transparencyLevel > 0 ) { props[ 'transparent' ] = true; - props[ 'opacity' ] = 1 - transparencyLevel; - + } else { + props[ 'transparent' ] = false; } - + props[ 'opacity' ] = 1 - transparencyLevel; } var keys = { 'diffuse':'map', 'ambient':'lightMap' , 'specular':'specularMap', + 'specularLevel':'specularMap', 'emission':'emissionMap', 'bump':'bumpMap', 'normal':'normalMap' @@ -3666,6 +3681,7 @@ THREE.ColladaLoader = function () { case 'emission': case 'diffuse': case 'specular': + case 'specularLevel': case 'bump': case 'normal': @@ -3722,7 +3738,7 @@ THREE.ColladaLoader = function () { } - } else if ( prop === 'diffuse' || !transparent ) { + } else { if ( prop === 'emission' ) { diff --git a/support/client/lib/vwf/view/cesium.js b/support/client/lib/vwf/view/cesium.js index f139c0a09..252f24cf8 100644 --- a/support/client/lib/vwf/view/cesium.js +++ b/support/client/lib/vwf/view/cesium.js @@ -121,7 +121,7 @@ define( [ "module", "vwf/view", "vwf/utility", "vwf/model/cesium/Cesium", "jquer }; var kernel = this.kernel; - var protos = getPrototypes.call( this, childExtendsID ) + var protos = this.kernel.prototypes( childID ); var node = undefined; if ( isCesiumDefinition.call( this, protos ) ) { @@ -362,18 +362,6 @@ define( [ "module", "vwf/view", "vwf/utility", "vwf/model/cesium/Cesium", "jquer } ); - function getPrototypes( extendsID ) { - var prototypes = []; - var id = extendsID; - - while ( id !== undefined ) { - prototypes.push( id ); - id = this.kernel.prototype( id ); - } - - return prototypes; - } - function isCesiumDefinition( prototypes ) { var foundCesium = false; if ( prototypes ) { diff --git a/support/client/lib/vwf/view/threejs.js b/support/client/lib/vwf/view/threejs.js index ff60f2c51..ed56f7880 100644 --- a/support/client/lib/vwf/view/threejs.js +++ b/support/client/lib/vwf/view/threejs.js @@ -49,6 +49,7 @@ define( [ "module", var boundingBox = undefined; var userObjectRequested = false; var usersShareView = true; + var enableRenderer = true; var degreesToRadians = Math.PI / 180; var movingForward = false; var movingBack = false; @@ -86,6 +87,8 @@ define( [ "module", var enableStereo = false; + var sceneRootID = undefined; + return view.load( module, { initialize: function( options ) { @@ -95,7 +98,7 @@ define( [ "module", checkCompatibility.call(this); this.state.appInitialized = false; - + sceneRootID = this.kernel.application(); this.pickInterval = 10; this.enableInputs = true; this.applicationWantsPointerEvents = false; @@ -159,17 +162,18 @@ define( [ "module", createdNode: function( nodeID, childID, childExtendsID, childImplementsIDs, childSource, childType, childIndex, childName, callback /* ( ready ) */) { + sceneRootID = this.kernel.application(); //the created node is a scene, and has already been added to the state by the model. //how/when does the model set the state object? if ( this.state.scenes[ childID ] ) { - this.canvasQuery = $(this.rootSelector).append("" + this.canvasQuery = $(this.rootSelector).append("" ).children(":last"); initScene.call(this,this.state.scenes[childID]); } - else if ( this.state.scenes[ this.kernel.application() ] ) { - var sceneNode = this.state.scenes[ this.kernel.application() ]; + else if ( this.state.scenes[ sceneRootID ] ) { + var sceneNode = this.state.scenes[ sceneRootID ]; if ( sceneNode.camera.ID == childID ) { setActiveCamera.call( this, sceneNode.camera.ID ); } @@ -183,7 +187,8 @@ define( [ "module", initializedNode: function( nodeID, childID ) { // If the node that was initialized is the application node, find the user's navigation object - var appID = this.kernel.application(); + var appID = sceneRootID; + if ( childID == appID ) { if ( enableStereo ) { @@ -207,6 +212,7 @@ define( [ "module", } this.state.appInitialized = true; + } else { //TODO: This is a temporary workaround until the callback functionality is implemented for @@ -289,9 +295,11 @@ define( [ "module", } else if ( propertyName == "boundingBox" ) { boundingBox = propertyValue; } else if ( propertyName == "activeCamera" ) { - setActiveCamera.call( this, this.state.scenes[ this.kernel.application() ].camera.ID ); + setActiveCamera.call( this, this.state.scenes[ sceneRootID ].camera.ID ); } else if ( propertyName == "usersShareView" ) { usersShareView = propertyValue; + } else if ( propertyName == "enableRenderer" ) { + enableRenderer = propertyValue; } } @@ -315,7 +323,7 @@ define( [ "module", gotProperty: function ( nodeID, propertyName, propertyValue ) { var clientThatGotProperty = this.kernel.client(); var me = this.kernel.moniker(); - var sceneRootID = this.kernel.application(); + if ( clientThatGotProperty == me ) { if ( propertyName == "owner") { @@ -358,7 +366,7 @@ define( [ "module", // Retrieve the userObject property so we may create a navigation object from // it for this user (the rest of the logic is in the gotProperty call for // userObject) - this.kernel.getProperty( this.kernel.application(), "userObject" ); + this.kernel.getProperty( sceneRootID, "userObject" ); userObjectRequested = true; } } @@ -388,7 +396,7 @@ define( [ "module", // TODO: The callback function is commented out because callbacks have not yet been // implemented for createChild - see workaround in initializedNode - this.kernel.createChild( this.kernel.application(), navObjectName, userObject, undefined, undefined /*, + this.kernel.createChild( sceneRootID, navObjectName, userObject, undefined, undefined /*, function( nodeID ) { controlNavObject( this.state.nodes[ nodeID ] ); } */ ); @@ -1117,6 +1125,11 @@ define( [ "module", return; } + // Return if rendering is suppressed in the application scene. + if ( ! enableRenderer ) { + return; + } + // Verify that there is a camera to render from before going any farther var camera = self.state.cameraInUse; if ( !camera ) { diff --git a/support/client/lib/vwf/view/webrtc.js b/support/client/lib/vwf/view/webrtc.js index 4b6188682..068a04ae7 100644 --- a/support/client/lib/vwf/view/webrtc.js +++ b/support/client/lib/vwf/view/webrtc.js @@ -75,7 +75,7 @@ define( [ "module", "vwf/view", "vwf/utility", "vwf/utility/color", "jquery" ], var self = this, node; - var protos = getPrototypes.call( self, childExtendsID ) + var protos = this.kernel.prototypes( childID ); if ( isClientInstanceDef.call( this, protos ) && childName ) { @@ -338,18 +338,6 @@ define( [ "module", "vwf/view", "vwf/utility", "vwf/utility/color", "jquery" ], } ); - function getPrototypes( extendsID ) { - var prototypes = []; - var id = extendsID; - - while ( id !== undefined ) { - prototypes.push( id ); - id = this.kernel.prototype( id ); - } - - return prototypes; - } - function getPeer( moniker ) { var clientNode; for ( var id in this.state.clients ) { diff --git a/support/proxy/vwf.example.com/scene.vwf.yaml b/support/proxy/vwf.example.com/scene.vwf.yaml index 76ec85ddf..4ff3f58cf 100644 --- a/support/proxy/vwf.example.com/scene.vwf.yaml +++ b/support/proxy/vwf.example.com/scene.vwf.yaml @@ -155,6 +155,13 @@ properties: } value: "PCFSoft" + ## Set `enableRenderer` to `false` to disable rendering for this scene. Defaults to `true`. + ## + ## @name scene.vwf#enableRenderer + ## @property + + enableRenderer: true + methods: initializeActiveCamera: getActiveCameraComp: