Skip to content

Commit 470f6ee

Browse files
committed
Moved float/vector creation/randomization functions to seperate utils file to be more DRY.
Updated dynamic property setting.
1 parent 2d81d8b commit 470f6ee

File tree

4 files changed

+416
-348
lines changed

4 files changed

+416
-348
lines changed

examples/dynamicAttributes.html

Lines changed: 49 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
<script type="text/javascript" src="./js/dat.gui.min.js"></script>
1313

1414
<!-- ShaderParticles -->
15+
<script type="text/javascript" src="../src/ShaderParticleUtils.js"></script>
1516
<script type="text/javascript" src="../src/ShaderParticleGroup.js"></script>
1617
<script type="text/javascript" src="../src/ShaderParticleEmitter.js"></script>
1718

@@ -47,11 +48,11 @@
4748
}
4849

4950

50-
function makeAttributeGUI( folder, attributeName ) {
51+
function makeAttributeGUI( folder, attributeName, min, max, step ) {
5152
for( var i in emitter[ attributeName ] ) {
5253
if( i !== 'x' && i !== 'y' && i !== 'z' ) continue;
5354

54-
folder.add( emitter[ attributeName ], i, -100, 100 ).step(1).onChange( (function( prop ) {
55+
folder.add( emitter[ attributeName ], i, min, max ).step( step ).onChange( (function( prop ) {
5556
return function( value ) {
5657
if( isNaN( value ) ) return;
5758
emitter[ attributeName ][ prop ] = value;
@@ -62,46 +63,85 @@
6263
}
6364

6465

66+
function makeStandardGUI( folder, name, min, max, step ) {
67+
folder.add( emitter, name ).min( min ).max( max ).step( step ).onChange( function( value ) {
68+
emitter[ name ] = value;
69+
emitter.setOption( name, emitter[ name ] );
70+
} );
71+
}
72+
73+
74+
function makeColorGUI( folder, name ) {
75+
var obj = {};
76+
77+
obj[name] = {
78+
r: emitter[ name ].r * 255,
79+
g: emitter[ name ].g * 255,
80+
b: emitter[ name ].b * 255
81+
};
82+
83+
folder.addColor( obj, name ).onChange( function( value ) {
84+
emitter[ name ].setRGB( value.r / 255, value.g / 255, value.b / 255 );
85+
emitter.setOption( name, emitter[ name ] );
86+
} );
87+
}
88+
89+
6590
function initGUI() {
6691
gui = new dat.GUI();
6792

68-
var standard = gui.addFolder( 'Standard' );
93+
var standard = gui.addFolder( 'Standard' ),
94+
size = gui.addFolder( 'Size' ),
95+
opacity = gui.addFolder( 'Opacity' ),
96+
color = gui.addFolder( 'Color' );
97+
6998
standard.add( emitter, 'particlesPerSecond' ).min(1).max( emitter.particlesPerSecond ).step(1);
7099

71-
var velocityObj = {};
100+
makeStandardGUI( size, 'sizeStart', 0, 200, 1 );
101+
makeStandardGUI( size, 'sizeStartSpread', 0, 200, 1 );
102+
makeStandardGUI( size, 'sizeEnd', 0, 200, 1 );
103+
104+
makeStandardGUI( opacity, 'opacityStart', 0, 1, 0.1 );
105+
makeStandardGUI( opacity, 'opacityMiddle', 0, 1, 0.1 );
106+
makeStandardGUI( opacity, 'opacityEnd', 0, 1, 0.1 );
72107

73108

109+
makeColorGUI( color, 'colorStart' );
110+
makeColorGUI( color, 'colorMiddle' );
111+
makeColorGUI( color, 'colorEnd' );
112+
makeAttributeGUI( gui.addFolder( 'Color Start Spread' ), 'colorStartSpread', 0, 255, 1 );
113+
74114

75115
makeAttributeGUI( gui.addFolder( 'Position' ), 'position' );
76116
makeAttributeGUI( gui.addFolder( 'Position Spread' ), 'positionSpread' );
77117

78-
makeAttributeGUI( gui.addFolder( 'sizeStart' ), 'size' );
79-
80118
makeAttributeGUI( gui.addFolder( 'Velocity' ), 'velocity' );
81119
makeAttributeGUI( gui.addFolder( 'Velocity Spread' ), 'velocitySpread' );
82120
makeAttributeGUI( gui.addFolder( 'Acceleration' ), 'acceleration' );
83121
makeAttributeGUI( gui.addFolder( 'Acceleration Spread' ), 'accelerationSpread' );
84-
85122
}
86123

87124
// Create particle group and emitter
88125
function initParticles() {
89126
particleGroup = new ShaderParticleGroup({
90127
texture: THREE.ImageUtils.loadTexture('./img/smokeparticle.png'),
91-
maxAge: 2
128+
maxAge: 5
92129
});
93130

94131
emitter = new ShaderParticleEmitter({
95132
position: new THREE.Vector3(0, 0, 0),
133+
positionSpread: new THREE.Vector3(30, 30, 30),
96134

97135
colorStart: new THREE.Color('white'),
98136
colorEnd: new THREE.Color('blue'),
99-
size: 1,
137+
sizeStart: 1,
100138
sizeEnd: 2,
101139

102140
particlesPerSecond: 1000
103141
});
104142

143+
console.log( emitter.colorStart );
144+
105145
particleGroup.addEmitter( emitter );
106146
scene.add( particleGroup.mesh );
107147

@@ -116,7 +156,6 @@
116156

117157
// Use a fixed time-step here to avoid gaps
118158
render( deltaTime );
119-
120159
stats.update();
121160
}
122161

src/ShaderParticleEmitter.js

Lines changed: 74 additions & 145 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ function ShaderParticleEmitter( options ) {
3535
that.speedSpread = parseFloat( typeof options.speedSpread === 'number' ? options.speedSpread : 0, 10 );
3636

3737
that.sizeStart = parseFloat( typeof options.sizeStart === 'number' ? options.sizeStart : 10.0, 10 );
38-
that.sizeStartSpread = parseFloat( typeof options.sizeStartSpread === 'number' ? options.sizeStartSpread : 0, 10 );
38+
that.sizeStartSpread = parseFloat( typeof options.sizeStartSpread === 'number' ? options.sizeStartSpread : 0.0, 10 );
3939
that.sizeEnd = parseFloat( typeof options.sizeEnd === 'number' ? options.sizeEnd : that.sizeStart, 10 );
4040

4141
that.colorStart = options.colorStart instanceof THREE.Color ? options.colorStart : new THREE.Color( 'white' );
@@ -62,7 +62,6 @@ function ShaderParticleEmitter( options ) {
6262
10 );
6363

6464
that.particleMass = parseFloat( typeof options.particleMass === 'number' ? options.particleMass : 10 );
65-
that.planetMass = parseFloat( typeof options.planetMass === 'number' ? options.planetMass : 10 );
6665
that.planetPosition = options.planetPosition instanceof THREE.Vector3 ? options.planetPosition : new THREE.Vector3();
6766

6867
that.emitterDuration = typeof options.emitterDuration === 'number' ? options.emitterDuration : null;
@@ -82,31 +81,6 @@ function ShaderParticleEmitter( options ) {
8281
that.particleIndex = 0.0;
8382

8483
that.userData = {};
85-
86-
// A map of option names and their types that require attribute updates if changed.
87-
// Used by ShaderParticleEmitter.prototype.setOption()
88-
that.attributeUpdateNames = {
89-
'acceleration': THREE.Vector3,
90-
'velocity': THREE.Vector3,
91-
'alive': Number,
92-
'age': Number,
93-
'size': Number,
94-
'sizeEnd': Number,
95-
'customColor': THREE.Color,
96-
'customColorEnd': THREE.Color,
97-
'opacity': Number,
98-
'opacityMiddle': Number,
99-
'opacityEnd': Number
100-
};
101-
102-
that.nonAttributeUpdateNames = {
103-
'particlesPerSecond': Number,
104-
'type': String,
105-
'position': THREE.Vector3,
106-
'positionSpread': THREE.Vector3,
107-
'velocitySpread': THREE.Vector3,
108-
'accelerationSpread': THREE.Vector3
109-
};
11084
}
11185

11286

@@ -150,105 +124,8 @@ ShaderParticleEmitter.prototype = {
150124
if (that.angleAlignVelocity) {
151125
that.attributes.angle.value[i] = -Math.atan2(particleVelocity.y, particleVelocity.x);
152126
}
153-
154-
},
155-
156-
/**
157-
* Create a random Number value based on an initial value and
158-
* a spread range
159-
*
160-
* @private
161-
*
162-
* @param {Number} base
163-
* @param {Number} spread
164-
* @return {Number}
165-
*/
166-
_randomFloat: function( base, spread ) {
167-
return base + spread * (Math.random() - 0.5);
168-
},
169-
170-
/**
171-
* Given an existing particle vector, randomise it based on base and spread vectors
172-
*
173-
* @private
174-
*
175-
* @param {THREE.Vector3} v
176-
* @param {THREE.Vector3} base
177-
* @param {THREE.Vector3} spread
178-
*/
179-
_randomizeExistingVector3: function( v, base, spread ) {
180-
var r = Math.random;
181-
182-
v.copy( base );
183-
184-
v.x += r() * spread.x - (spread.x/2);
185-
v.y += r() * spread.y - (spread.y/2);
186-
v.z += r() * spread.z - (spread.z/2);
187-
},
188-
189-
190-
/**
191-
* Given an existing particle vector, project it onto a random point on a
192-
* sphere with radius `radius` and position `base`.
193-
*
194-
* @private
195-
*
196-
* @param {THREE.Vector3} v
197-
* @param {THREE.Vector3} base
198-
* @param {Number} radius
199-
*/
200-
_randomizeExistingVector3OnSphere: function( v, base, radius, radiusSpread, radiusScale ) {
201-
var rand = Math.random,
202-
z = 2 * rand() - 1,
203-
t = 6.2832 * rand(),
204-
r = Math.sqrt( 1 - z*z ),
205-
randomRadius = this._randomFloat( radius, radiusSpread );
206-
207-
v.set(
208-
(r * Math.cos(t)) * randomRadius,
209-
(r * Math.sin(t)) * randomRadius,
210-
z * randomRadius
211-
).multiply( radiusScale );
212-
213-
v.add( base );
214-
},
215-
216-
/**
217-
* Given an existing particle vector, project it onto a random point
218-
* on a disk (in the XY-plane) centered at `base` and with radius `radius`.
219-
*
220-
* @private
221-
*
222-
* @param {THREE.Vector3} v
223-
* @param {THREE.Vector3} base
224-
* @param {Number} radius
225-
*/
226-
_randomizeExistingVector3OnDisk: function( v, base, radius, radiusSpread, radiusScale ) {
227-
var rand = Math.random,
228-
t = 6.2832 * rand(),
229-
randomRadius = this._randomFloat( radius, radiusSpread );
230-
231-
v.set(
232-
Math.cos( t ),
233-
Math.sin( t ),
234-
0
235-
).multiplyScalar( randomRadius );
236-
237-
if ( radiusScale ) {
238-
v.multiply( radiusScale );
239-
}
240-
241-
v.add( base );
242127
},
243128

244-
_randomizeExistingVelocityVector3OnSphere: function( v, base, position, speed, speedSpread ) {
245-
v.copy(position)
246-
.sub(base)
247-
.normalize()
248-
.multiplyScalar( this._randomFloat( speed, speedSpread ) );
249-
},
250-
251-
252129
/**
253130
* Update this emitter's particle's positions. Called by the ShaderParticleGroup
254131
* that this emitter belongs to.
@@ -381,40 +258,92 @@ ShaderParticleEmitter.prototype = {
381258
},
382259

383260

261+
262+
_setRandomVector3Attribute: function( attr, base, spread ) {
263+
var that = this,
264+
start = that.verticesIndex,
265+
end = start + that.numParticles;
266+
267+
for( i = start; i < end; ++i ) {
268+
that._randomizeExistingVector3( attr.value[ i ], base, spread );
269+
}
270+
},
271+
272+
_setRandomColorAttribute: function( attr, base, spread ) {
273+
var that = this,
274+
start = that.verticesIndex,
275+
end = start + that.numParticles;
276+
277+
spread = spread || new THREE.Vector3();
278+
279+
for( i = start; i < end; ++i ) {
280+
that._randomizeExistingColor( attr.value[ i ], base, spread );
281+
}
282+
},
283+
284+
_setRandomFloatAttribute: function( attr, base, spread ) {
285+
var that = this,
286+
start = that.verticesIndex,
287+
end = start + that.numParticles;
288+
289+
spread = spread || 0;
290+
291+
for( i = start; i < end; ++i ) {
292+
attr.value[ i ] = that._randomFloat( base, spread );
293+
}
294+
},
295+
296+
384297
setOption: function( optionName, value ) {
385298
var that = this,
386-
attributeUpdateNames = that.attributeUpdateNames,
387-
nonAttributeUpdateNames = that.nonAttributeUpdateNames,
388299
attr, start, end, i, originalName;
389300

390-
if( attributeUpdateNames[ optionName ] && (value instanceof attributeUpdateNames[ optionName ]) ) {
391-
attr = that.attributes[ optionName ];
392-
start = that.verticesIndex;
393-
end = start + that.numParticles;
394301

395-
for( i = start; i < end; ++i ) {
396-
that._randomizeExistingVector3( attr.value[ i ], that[ optionName ], that[ optionName + 'Spread' ] );
302+
if( typeof that.attributes[ optionName ] === 'undefined' && typeof that[ optionName ] === 'undefined' ) {
303+
console.log( "Won't set", optionName + ".", "Invalid property." );
304+
return;
305+
}
306+
307+
if( that.attributes[ optionName ] ) {
308+
if( typeof that[ optionName ] === 'number' ) {
309+
that._setRandomFloatAttribute(
310+
that.attributes[ optionName ],
311+
that[ optionName ],
312+
that[ optionName + 'Spread' ]
313+
);
314+
}
315+
else if( that[ optionName ] instanceof THREE.Vector3 ) {
316+
that._setRandomVector3Attribute(
317+
that.attributes[ optionName ],
318+
that[ optionName ],
319+
that[ optionName + 'Spread' ]
320+
);
321+
}
322+
else if( that[ optionName ] instanceof THREE.Color ) {
323+
that._setRandomColorAttribute(
324+
that.attributes[ optionName ],
325+
that[ optionName ],
326+
that[ optionName + 'Spread' ]
327+
);
397328
}
398329

330+
that[ optionName ] = value;
399331
that.attributes[ optionName ].needsUpdate = true;
400332
}
401333

402-
else if( nonAttributeUpdateNames[ optionName ] && ( value instanceof nonAttributeUpdateNames[ optionName ] ) ) {
403-
334+
else if( that[ optionName ] ) {
404335
that[ optionName ] = value;
405336

406-
if( optionName.indexOf('Spread') > -1 && optionName !== 'positionSpread' ) {
407-
originalName = optionName.replace('Spread', '');
408-
attr = that.attributes[ originalName ];
409-
start = that.verticesIndex;
410-
end = start + that.numParticles;
411-
412-
for( i = start; i < end; ++i ) {
413-
that._randomizeExistingVector3( attr.value[ i ], that[ originalName ], that[ optionName ] );
414-
}
415-
416-
that.attributes[ originalName ].needsUpdate = true;
337+
if( optionName.indexOf( 'Spread' ) > -1 ) {
338+
var baseName = optionName.replace( 'Spread', '' );
339+
that.setOption( baseName, that[ baseName ] );
417340
}
418341
}
342+
419343
}
420344
};
345+
346+
// Extend ShaderParticleEmitter's prototype with functions from utils object.
347+
for( var i in shaderParticleUtils ) {
348+
ShaderParticleEmitter.prototype[ '_' + i ] = shaderParticleUtils[i];
349+
}

0 commit comments

Comments
 (0)