Переглянути джерело

Merge pull request #15663 from sunag/dev-mask

NodeMaterial - Intro to boolean, mask and dissolve example
Mr.doob 6 роки тому
батько
коміт
e25b50fea4

+ 1 - 0
examples/js/nodes/Nodes.js

@@ -20,6 +20,7 @@ export { NodeBuilder } from './core/NodeBuilder.js';
 
 // inputs
 
+export { BoolNode } from './inputs/BoolNode.js';
 export { IntNode } from './inputs/IntNode.js';
 export { FloatNode } from './inputs/FloatNode.js';
 export { Vector2Node } from './inputs/Vector2Node.js';

+ 2 - 0
examples/js/nodes/THREE.Nodes.js

@@ -20,6 +20,7 @@ import {
 
 	// inputs
 
+	BoolNode,
 	IntNode,
 	FloatNode,
 	Vector2Node,
@@ -132,6 +133,7 @@ THREE.NodeBuilder = NodeBuilder;
 
 // inputs
 
+THREE.BoolNode = BoolNode;
 THREE.IntNode = IntNode;
 THREE.FloatNode = FloatNode;
 THREE.Vector2Node = Vector2Node;

+ 18 - 6
examples/js/nodes/core/NodeBuilder.js

@@ -22,7 +22,8 @@ var elements = NodeUtils.elements,
 		vec3: 'v3',
 		vec4: 'v4',
 		mat4: 'v4',
-		int: 'i'
+		int: 'i',
+		bool: 'b'
 	},
 	convertTypeToFormat = {
 		t: 'sampler2D',
@@ -821,28 +822,39 @@ NodeBuilder.prototype = {
 			case 'f <- v2' : return code + '.x';
 			case 'f <- v3' : return code + '.x';
 			case 'f <- v4' : return code + '.x';
-			case 'f <- i' : return 'float( ' + code + ' )';
+			case 'f <- i' :
+			case 'f <- b' :	return 'float( ' + code + ' )';
 
 			case 'v2 <- f' : return 'vec2( ' + code + ' )';
 			case 'v2 <- v3': return code + '.xy';
 			case 'v2 <- v4': return code + '.xy';
-			case 'v2 <- i' : return 'vec2( float( ' + code + ' ) )';
+			case 'v2 <- i' :
+			case 'v2 <- b' : return 'vec2( float( ' + code + ' ) )';
 
 			case 'v3 <- f' : return 'vec3( ' + code + ' )';
 			case 'v3 <- v2': return 'vec3( ' + code + ', 0.0 )';
 			case 'v3 <- v4': return code + '.xyz';
-			case 'v3 <- i' : return 'vec2( float( ' + code + ' ) )';
+			case 'v3 <- i' :
+			case 'v3 <- b' : return 'vec2( float( ' + code + ' ) )';
 
 			case 'v4 <- f' : return 'vec4( ' + code + ' )';
 			case 'v4 <- v2': return 'vec4( ' + code + ', 0.0, 1.0 )';
 			case 'v4 <- v3': return 'vec4( ' + code + ', 1.0 )';
-			case 'v4 <- i' : return 'vec4( float( ' + code + ' ) )';
+			case 'v4 <- i' :
+			case 'v4 <- b' : return 'vec4( float( ' + code + ' ) )';
 
-			case 'i <- f' : return 'int( ' + code + ' )';
+			case 'i <- f' :
+			case 'i <- b' : return 'int( ' + code + ' )';
 			case 'i <- v2' : return 'int( ' + code + '.x )';
 			case 'i <- v3' : return 'int( ' + code + '.x )';
 			case 'i <- v4' : return 'int( ' + code + '.x )';
 
+			case 'b <- f' : return '( ' + code + ' != 0.0 )';
+			case 'b <- v2' : return '( ' + code + ' != vec2( 0.0 ) )';
+			case 'b <- v3' : return '( ' + code + ' != vec3( 0.0 ) )';
+			case 'b <- v4' : return '( ' + code + ' != vec4( 0.0 ) )';
+			case 'b <- i' : return '( ' + code + ' != 0 )';
+
 		}
 
 		return code;

+ 51 - 0
examples/js/nodes/inputs/BoolNode.js

@@ -0,0 +1,51 @@
+/**
+ * @author sunag / http://www.sunag.com.br/
+ */
+
+import { InputNode } from '../core/InputNode.js';
+
+function BoolNode( value ) {
+
+	InputNode.call( this, 'b' );
+
+	this.value = Boolean( value );
+
+}
+
+BoolNode.prototype = Object.create( InputNode.prototype );
+BoolNode.prototype.constructor = BoolNode;
+BoolNode.prototype.nodeType = "Bool";
+
+BoolNode.prototype.generateReadonly = function ( builder, output, uuid, type, ns, needsUpdate ) {
+
+	return builder.format( this.value, type, output );
+
+};
+
+BoolNode.prototype.copy = function ( source ) {
+
+	InputNode.prototype.copy.call( this, source );
+
+	this.value = source.value;
+
+};
+
+BoolNode.prototype.toJSON = function ( meta ) {
+
+	var data = this.getJSONNode( meta );
+
+	if ( ! data ) {
+
+		data = this.createJSONNode( meta );
+
+		data.value = this.value;
+
+		if ( this.readonly === true ) data.readonly = true;
+
+	}
+
+	return data;
+
+};
+
+export { BoolNode };

+ 1 - 0
examples/js/nodes/materials/PhongNodeMaterial.js

@@ -32,6 +32,7 @@ NodeUtils.addShortcuts( PhongNodeMaterial.prototype, 'fragment', [
 	'ao',
 	'environment',
 	'environmentAlpha',
+	'mask',
 	'position'
 ] );
 

+ 1 - 0
examples/js/nodes/materials/SpriteNodeMaterial.js

@@ -22,6 +22,7 @@ SpriteNodeMaterial.prototype.constructor = SpriteNodeMaterial;
 NodeUtils.addShortcuts( SpriteNodeMaterial.prototype, 'fragment', [
 	'color',
 	'alpha',
+	'mask',
 	'position',
 	'spherical'
 ] );

+ 1 - 0
examples/js/nodes/materials/StandardNodeMaterial.js

@@ -34,6 +34,7 @@ NodeUtils.addShortcuts( StandardNodeMaterial.prototype, 'fragment', [
 	'shadow',
 	'ao',
 	'environment',
+	'mask',
 	'position'
 ] );
 

+ 21 - 2
examples/js/nodes/materials/nodes/PhongNode.js

@@ -103,6 +103,8 @@ PhongNode.prototype.build = function ( builder ) {
 
 		// parse all nodes to reuse generate codes
 
+		if ( this.mask ) this.mask.parse( builder );
+
 		this.color.parse( builder, { slot: 'color' } );
 		this.specular.parse( builder );
 		this.shininess.parse( builder );
@@ -123,6 +125,8 @@ PhongNode.prototype.build = function ( builder ) {
 
 		// build code
 
+		var mask = this.mask ? this.mask.buildCode( builder, 'b' ) : undefined;
+
 		var color = this.color.buildCode( builder, 'c', { slot: 'color' } );
 		var specular = this.specular.buildCode( builder, 'c' );
 		var shininess = this.shininess.buildCode( builder, 'f' );
@@ -157,8 +161,19 @@ PhongNode.prototype.build = function ( builder ) {
 			"#include <normal_fragment_begin>",
 
 			// prevent undeclared material
-			"	BlinnPhongMaterial material;",
+			"	BlinnPhongMaterial material;"
+		];
+
+		if ( mask ) {
+
+			output.push(
+				mask.code,
+				'if ( ! ' + mask.result + ' ) discard;'
+			);
 
+		}
+
+		output.push(
 			color.code,
 			"	vec3 diffuseColor = " + color.result + ";",
 			"	ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );",
@@ -172,7 +187,7 @@ PhongNode.prototype.build = function ( builder ) {
 			"	float shininess = max( 0.0001, " + shininess.result + " );",
 
 			"	float specularStrength = 1.0;" // Ignored in MaterialNode ( replace to specular )
-		];
+		);
 
 		if ( alpha ) {
 
@@ -335,6 +350,8 @@ PhongNode.prototype.copy = function ( source ) {
 	this.specular = source.specular;
 	this.shininess = source.shininess;
 
+	if ( source.mask ) this.mask = source.mask;
+
 	if ( source.alpha ) this.alpha = source.alpha;
 
 	if ( source.normal ) this.normal = source.normal;
@@ -370,6 +387,8 @@ PhongNode.prototype.toJSON = function ( meta ) {
 		data.specular = this.specular.toJSON( meta ).uuid;
 		data.shininess = this.shininess.toJSON( meta ).uuid;
 
+		if ( this.mask ) data.mask = this.mask.toJSON( meta ).uuid;
+
 		if ( this.alpha ) data.alpha = this.alpha.toJSON( meta ).uuid;
 
 		if ( this.normal ) data.normal = this.normal.toJSON( meta ).uuid;

+ 23 - 6
examples/js/nodes/materials/nodes/SpriteNode.js

@@ -123,18 +123,31 @@ SpriteNode.prototype.build = function ( builder ) {
 
 		// parse all nodes to reuse generate codes
 
+		if ( this.mask ) this.mask.parse( builder );
+
 		if ( this.alpha ) this.alpha.parse( builder );
 
 		this.color.parse( builder, { slot: 'color' } );
 
 		// build code
 
-		var alpha = this.alpha ? this.alpha.buildCode( builder, 'f' ) : undefined,
-			color = this.color.buildCode( builder, 'c', { slot: 'color' } );
+		var mask = this.mask ? this.mask.buildCode( builder, 'b' ) : undefined,
+			alpha = this.alpha ? this.alpha.buildCode( builder, 'f' ) : undefined,
+			color = this.color.buildCode( builder, 'c', { slot: 'color' } ),
+			output = [];
+
+		if ( mask ) {
+
+			output.push(
+				mask.code,
+				'if ( ! ' + mask.result + ' ) discard;'
+			);
+
+		}
 
 		if ( alpha ) {
 
-			output = [
+			output.push(
 				alpha.code,
 				'#ifdef ALPHATEST',
 
@@ -143,14 +156,14 @@ SpriteNode.prototype.build = function ( builder ) {
 				'#endif',
 				color.code,
 				"gl_FragColor = vec4( " + color.result + ", " + alpha.result + " );"
-			];
+			);
 
 		} else {
 
-			output = [
+			output.push(
 				color.code,
 				"gl_FragColor = vec4( " + color.result + ", 1.0 );"
-			];
+			);
 
 		}
 
@@ -180,6 +193,8 @@ SpriteNode.prototype.copy = function ( source ) {
 
 	if ( source.spherical !== undefined ) this.spherical = source.spherical;
 
+	if ( source.mask ) this.mask = source.mask;
+
 	if ( source.alpha ) this.alpha = source.alpha;
 
 };
@@ -202,6 +217,8 @@ SpriteNode.prototype.toJSON = function ( meta ) {
 
 		if ( this.spherical === false ) data.spherical = false;
 
+		if ( this.mask ) data.mask = this.mask.toJSON( meta ).uuid;
+
 		if ( this.alpha ) data.alpha = this.alpha.toJSON( meta ).uuid;
 
 	}

+ 22 - 3
examples/js/nodes/materials/nodes/StandardNode.js

@@ -117,6 +117,8 @@ StandardNode.prototype.build = function ( builder ) {
 
 		// parse all nodes to reuse generate codes
 
+		if ( this.mask ) this.mask.parse( builder );
+
 		this.color.parse( builder, { slot: 'color', context: contextGammaOnly } );
 		this.roughness.parse( builder );
 		this.metalness.parse( builder );
@@ -141,6 +143,8 @@ StandardNode.prototype.build = function ( builder ) {
 
 		// build code
 
+		var mask = this.mask ? this.mask.buildCode( builder, 'b' ) : undefined;
+
 		var color = this.color.buildCode( builder, 'c', { slot: 'color', context: contextGammaOnly } );
 		var roughness = this.roughness.buildCode( builder, 'f' );
 		var metalness = this.metalness.buildCode( builder, 'f' );
@@ -194,8 +198,19 @@ StandardNode.prototype.build = function ( builder ) {
 
 			// add before: prevent undeclared material
 			"	PhysicalMaterial material;",
-			"	material.diffuseColor = vec3( 1.0 );",
+			"	material.diffuseColor = vec3( 1.0 );"
+		];
+
+		if ( mask ) {
+
+			output.push(
+				mask.code,
+				'if ( ! ' + mask.result + ' ) discard;'
+			);
 
+		}
+
+		output.push(
 			color.code,
 			"	vec3 diffuseColor = " + color.result + ";",
 			"	ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );",
@@ -207,7 +222,7 @@ StandardNode.prototype.build = function ( builder ) {
 
 			metalness.code,
 			"	float metalnessFactor = " + metalness.result + ";"
-		];
+		);
 
 		if ( alpha ) {
 
@@ -215,7 +230,7 @@ StandardNode.prototype.build = function ( builder ) {
 				alpha.code,
 				'#ifdef ALPHATEST',
 
-				'if ( ' + alpha.result + ' <= ALPHATEST ) discard;',
+				'	if ( ' + alpha.result + ' <= ALPHATEST ) discard;',
 
 				'#endif'
 			);
@@ -403,6 +418,8 @@ StandardNode.prototype.copy = function ( source ) {
 	this.roughness = source.roughness;
 	this.metalness = source.metalness;
 
+	if ( source.mask ) this.mask = source.mask;
+
 	if ( source.alpha ) this.alpha = source.alpha;
 
 	if ( source.normal ) this.normal = source.normal;
@@ -442,6 +459,8 @@ StandardNode.prototype.toJSON = function ( meta ) {
 		data.roughness = this.roughness.toJSON( meta ).uuid;
 		data.metalness = this.metalness.toJSON( meta ).uuid;
 
+		if ( this.mask ) data.mask = this.mask.toJSON( meta ).uuid;
+
 		if ( this.alpha ) data.alpha = this.alpha.toJSON( meta ).uuid;
 
 		if ( this.normal ) data.normal = this.normal.toJSON( meta ).uuid;

+ 35 - 16
examples/js/nodes/math/CondNode.js

@@ -4,18 +4,18 @@
 
 import { TempNode } from '../core/TempNode.js';
 
-function CondNode( a, b, ifNode, elseNode, op ) {
+function CondNode( a, b, op, ifNode, elseNode ) {
 
 	TempNode.call( this );
 
 	this.a = a;
 	this.b = b;
 
+	this.op = op;
+	
 	this.ifNode = ifNode;
 	this.elseNode = elseNode;
 
-	this.op = op;
-
 }
 
 CondNode.EQUAL = '==';
@@ -31,13 +31,22 @@ CondNode.prototype.nodeType = "Cond";
 
 CondNode.prototype.getType = function ( builder ) {
 
-	if ( builder.getTypeLength( this.elseNode.getType( builder ) ) > builder.getTypeLength( this.ifNode.getType( builder ) ) ) {
+	if (this.ifNode) {
+		
+		var ifType = this.ifNode.getType( builder );
+		var elseType = this.elseNode.getType( builder );
+		
+		if ( builder.getTypeLength( elseType ) > builder.getTypeLength( ifType ) ) {
 
-		return this.elseNode.getType( builder );
+			return elseType;
 
-	}
+		}
 
-	return this.ifNode.getType( builder );
+		return ifType;
+		
+	}
+	
+	return 'b';
 
 };
 
@@ -59,10 +68,20 @@ CondNode.prototype.generate = function ( builder, output ) {
 		condType = this.getCondType( builder ),
 		a = this.a.build( builder, condType ),
 		b = this.b.build( builder, condType ),
-		ifNode = this.ifNode.build( builder, type ),
-		elseNode = this.elseNode.build( builder, type );
-
-	var code = '( ' + [ a, this.op, b, '?', ifNode, ':', elseNode ].join( ' ' ) + ' )';
+		code;
+		
+	if (this.ifNode) {
+		
+		var ifCode = this.ifNode.build( builder, type ),
+			elseCode = this.elseNode.build( builder, type );
+		
+		code = '( ' + [ a, this.op, b, '?', ifCode, ':', elseCode ].join( ' ' ) + ' )';
+		
+	} else {
+
+		code = '( ' + a + ' ' + this.op + ' ' +  b  + ' )';
+		
+	}
 
 	return builder.format( code, this.getType( builder ), output );
 
@@ -75,11 +94,11 @@ CondNode.prototype.copy = function ( source ) {
 	this.a = source.a;
 	this.b = source.b;
 
+	this.op = source.op;
+
 	this.ifNode = source.ifNode;
 	this.elseNode = source.elseNode;
 
-	this.op = source.op;
-
 };
 
 CondNode.prototype.toJSON = function ( meta ) {
@@ -93,11 +112,11 @@ CondNode.prototype.toJSON = function ( meta ) {
 		data.a = this.a.toJSON( meta ).uuid;
 		data.b = this.b.toJSON( meta ).uuid;
 
-		data.ifNode = this.ifNode.toJSON( meta ).uuid;
-		data.elseNode = this.elseNode.toJSON( meta ).uuid;
-
 		data.op = this.op;
 
+		if ( data.ifNode ) data.ifNode = this.ifNode.toJSON( meta ).uuid;
+		if ( data.elseNode ) data.elseNode = this.elseNode.toJSON( meta ).uuid;
+
 	}
 
 	return data;

+ 1 - 1
examples/js/nodes/utils/TimerNode.js

@@ -12,7 +12,7 @@ function TimerNode( scale, scope, timeScale ) {
 	this.scale = scale !== undefined ? scale : 1;
 	this.scope = scope || TimerNode.GLOBAL;
 
-	this.timeScale = timeScale !== undefined ? timeScale : this.scale !== 1;
+	this.timeScale = timeScale !== undefined ? timeScale : scale !== undefined;
 
 }
 

+ 211 - 1
examples/webgl_materials_nodes.html

@@ -180,6 +180,8 @@
 					'adv / skin-phong': 'skin-phong',
 					'adv / caustic': 'caustic',
 					'adv / displace': 'displace',
+					'adv / dissolve': 'dissolve',
+					'adv / dissolve-fire': 'dissolve-fire',
 					'adv / plush': 'plush',
 					'adv / toon': 'toon',
 					'adv / camera-depth': 'camera-depth',
@@ -1321,6 +1323,214 @@
 
 						break;
 
+						
+					case 'dissolve':
+
+						// MATERIAL
+
+						mtl = new THREE.StandardNodeMaterial();
+
+						var color = new THREE.ColorNode( 0xEEEEEE );
+						var borderColor = new THREE.ColorNode( 0x0054df );
+						var threshold = new THREE.FloatNode( .1 );
+						var borderSize = new THREE.FloatNode( .2 );
+
+						var tex = new THREE.TextureNode( getTexture( "cloud" ) );
+						var texArea = new THREE.SwitchNode( tex, 'w' );
+
+						var thresholdBorder = new THREE.Math3Node(
+							new THREE.OperatorNode( threshold, borderSize, THREE.OperatorNode.ADD ),
+							threshold,
+							texArea,
+							THREE.Math3Node.SMOOTHSTEP
+						);
+
+						var thresholdEmissive = new THREE.OperatorNode(
+							borderColor,
+							thresholdBorder,
+							THREE.OperatorNode.MUL
+						);
+
+						// APPLY
+
+						mtl.color = color;
+						mtl.emissive = thresholdEmissive;
+						mtl.mask = new THREE.CondNode(
+							texArea, // a: value
+							threshold, // b: value
+							THREE.CondNode.GREATER // condition
+						);
+
+						// GUI
+
+						addGui( 'threshold', threshold.value, function ( val ) {
+
+							threshold.value = val;
+
+						}, false, -.3, 1.3 );
+
+						addGui( 'borderSize', borderSize.value, function ( val ) {
+
+							borderSize.value = val;
+
+						}, false, 0, .5 );
+
+						addGui( 'color', color.value.getHex(), function ( val ) {
+
+							color.value.setHex( val );
+
+						}, true );
+
+						addGui( 'borderColor', borderColor.value.getHex(), function ( val ) {
+
+							borderColor.value.setHex( val );
+
+						}, true );
+
+						break;
+
+					case 'dissolve-fire':
+
+						// MATERIAL
+
+						mtl = new THREE.StandardNodeMaterial();
+
+						var color = new THREE.ColorNode( 0xEEEEEE );
+						var fireStartColor = new THREE.ColorNode( 0xF7CA78 );
+						var fireEndColor = new THREE.ColorNode( 0xFF0000 );
+						var burnedColor = new THREE.ColorNode( 0x000000 );
+						var burnedFinalColor = new THREE.ColorNode( 0x000000 );
+						var threshold = new THREE.FloatNode( .1 );
+						var fireSize = new THREE.FloatNode( .16 );
+						var burnedSize = new THREE.FloatNode( .5 );
+						var timer = new THREE.TimerNode( 0.8 );
+
+						var sinCycleInSecs = new THREE.OperatorNode(
+							timer,
+							new THREE.ConstNode( THREE.ConstNode.PI2 ),
+							THREE.OperatorNode.MUL
+						);
+
+						var cycle = new THREE.Math1Node( sinCycleInSecs, THREE.Math1Node.SIN );
+
+						// round sin to 0 at 1
+						cycle = new THREE.OperatorNode( cycle, new THREE.FloatNode( 1 ), THREE.OperatorNode.ADD );
+						cycle = new THREE.OperatorNode( cycle, new THREE.FloatNode( 2 ), THREE.OperatorNode.DIV );
+
+						// offset to +.9
+						cycle = new THREE.OperatorNode( cycle, new THREE.FloatNode( .9 ), THREE.OperatorNode.ADD );
+
+						var tex = new THREE.TextureNode( getTexture( "cloud" ) );
+						var texArea = new THREE.SwitchNode( tex, 'w' );
+
+						var thresholdBorder = new THREE.Math3Node(
+							new THREE.OperatorNode( threshold, fireSize, THREE.OperatorNode.ADD ),
+							threshold,
+							texArea,
+							THREE.Math3Node.SMOOTHSTEP
+						);
+
+						var fireStartAnimatedColor = new THREE.ColorAdjustmentNode( 
+							fireStartColor, 
+							cycle, 
+							THREE.ColorAdjustmentNode.SATURATION 
+						);
+
+						var fireEndAnimatedColor = new THREE.ColorAdjustmentNode( 
+							fireEndColor, 
+							cycle, 
+							THREE.ColorAdjustmentNode.SATURATION 
+						);
+
+						var fireColor = new THREE.Math3Node(
+							fireEndAnimatedColor,
+							fireStartAnimatedColor,
+							thresholdBorder,
+							THREE.Math3Node.MIX
+						);
+
+						var thresholdBurnedBorder = new THREE.Math3Node(
+							new THREE.OperatorNode( threshold, burnedSize, THREE.OperatorNode.ADD ),
+							threshold,
+							texArea,
+							THREE.Math3Node.SMOOTHSTEP
+						);
+
+						var fireEmissive = new THREE.OperatorNode(
+							fireColor,
+							thresholdBorder,
+							THREE.OperatorNode.MUL
+						);
+
+						var burnedResultColor = new THREE.Math3Node(
+							color,
+							burnedColor,
+							thresholdBurnedBorder,
+							THREE.Math3Node.MIX
+						);
+
+						// APPLY
+
+						mtl.color = burnedResultColor;
+						mtl.emissive = fireEmissive;
+						mtl.mask = new THREE.CondNode(
+							texArea, // a: value
+							threshold, // b: value
+							THREE.CondNode.GREATER // condition
+						);
+
+						// GUI
+
+						addGui( 'threshold', threshold.value, function ( val ) {
+
+							threshold.value = val;
+
+						}, false, -.5, 1.5 );
+
+						addGui( 'fireSize', fireSize.value, function ( val ) {
+
+							fireSize.value = val;
+
+						}, false, 0, .5 );
+
+						addGui( 'burnedSize', burnedSize.value, function ( val ) {
+
+							burnedSize.value = val;
+
+						}, false, 0, 1 );
+
+						addGui( 'color', color.value.getHex(), function ( val ) {
+
+							color.value.setHex( val );
+
+						}, true );
+
+						addGui( 'fireStartColor', fireStartColor.value.getHex(), function ( val ) {
+
+							fireStartColor.value.setHex( val );
+
+						}, true );
+
+						addGui( 'fireEndColor', fireEndColor.value.getHex(), function ( val ) {
+
+							fireEndColor.value.setHex( val );
+
+						}, true );
+
+						addGui( 'burnedColor', burnedColor.value.getHex(), function ( val ) {
+
+							burnedColor.value.setHex( val );
+
+						}, true );
+
+						addGui( 'timeScale', timer.scale, function ( val ) {
+
+							timer.scale = val;
+
+						}, false, 0, 2 );
+
+						break;
+
 					case 'smoke':
 
 						// MATERIAL
@@ -2172,7 +2382,7 @@
 							ifNode = new THREE.ColorNode( 0x0000FF ),
 							elseNode = new THREE.ColorNode( 0xFF0000 );
 
-						var cond = new THREE.CondNode( a, b, ifNode, elseNode, THREE.CondNode.EQUAL );
+						var cond = new THREE.CondNode( a, b, THREE.CondNode.EQUAL, ifNode, elseNode );
 
 						mtl.color = cond;