GPUParticleSystem.js 15.4 KB
Newer Older
M
r72  
Mr.doob 已提交
1 2 3 4
/*
 * GPU Particle System
 * @author flimshaw - Charlie Hoey - http://charliehoey.com
 *
M
r78  
Mr.doob 已提交
5
 * A simple to use, general purpose GPU system. Particles are spawn-and-forget with
M
r72  
Mr.doob 已提交
6 7 8 9 10 11 12 13 14 15 16 17 18 19
 * several options available, and do not require monitoring or cleanup after spawning.
 * Because the paths of all particles are completely deterministic once spawned, the scale
 * and direction of time is also variable.
 *
 * Currently uses a static wrapping perlin noise texture for turbulence, and a small png texture for
 * particles, but adding support for a particle texture atlas or changing to a different type of turbulence
 * would be a fairly light day's work.
 *
 * Shader and javascript packing code derrived from several Stack Overflow examples.
 *
 */

THREE.GPUParticleSystem = function(options) {

M
r78  
Mr.doob 已提交
20 21
	var self = this;
	var options = options || {};
M
r72  
Mr.doob 已提交
22

M
r78  
Mr.doob 已提交
23 24 25 26 27 28
	// parse options and use defaults
	self.PARTICLE_COUNT = options.maxParticles || 1000000;
	self.PARTICLE_CONTAINERS = options.containerCount || 1;
	self.PARTICLES_PER_CONTAINER = Math.ceil(self.PARTICLE_COUNT / self.PARTICLE_CONTAINERS);
	self.PARTICLE_CURSOR = 0;
	self.time = 0;
M
r72  
Mr.doob 已提交
29 30


M
r78  
Mr.doob 已提交
31 32
	// Custom vertex and fragement shader
	var GPUParticleShader = {
M
r72  
Mr.doob 已提交
33

M
r78  
Mr.doob 已提交
34
		vertexShader: [
M
r72  
Mr.doob 已提交
35

M
r78  
Mr.doob 已提交
36 37 38 39
			'precision highp float;',
			'const vec4 bitSh = vec4(256. * 256. * 256., 256. * 256., 256., 1.);',
			'const vec4 bitMsk = vec4(0.,vec3(1./256.0));',
			'const vec4 bitShifts = vec4(1.) / bitSh;',
M
r72  
Mr.doob 已提交
40

M
r78  
Mr.doob 已提交
41 42
			'#define FLOAT_MAX	1.70141184e38',
			'#define FLOAT_MIN	1.17549435e-38',
M
r72  
Mr.doob 已提交
43

M
r78  
Mr.doob 已提交
44 45
			'lowp vec4 encode_float(highp float v) {',
			'highp float av = abs(v);',
M
r72  
Mr.doob 已提交
46

M
r78  
Mr.doob 已提交
47 48 49 50 51 52 53 54
			'//Handle special cases',
			'if(av < FLOAT_MIN) {',
			'return vec4(0.0, 0.0, 0.0, 0.0);',
			'} else if(v > FLOAT_MAX) {',
			'return vec4(127.0, 128.0, 0.0, 0.0) / 255.0;',
			'} else if(v < -FLOAT_MAX) {',
			'return vec4(255.0, 128.0, 0.0, 0.0) / 255.0;',
			'}',
M
r72  
Mr.doob 已提交
55

M
r78  
Mr.doob 已提交
56
			'highp vec4 c = vec4(0,0,0,0);',
M
r72  
Mr.doob 已提交
57

M
r78  
Mr.doob 已提交
58 59 60
			'//Compute exponent and mantissa',
			'highp float e = floor(log2(av));',
			'highp float m = av * pow(2.0, -e) - 1.0;',
M
r72  
Mr.doob 已提交
61

M
r78  
Mr.doob 已提交
62 63 64 65 66 67
			//Unpack mantissa
			'c[1] = floor(128.0 * m);',
			'm -= c[1] / 128.0;',
			'c[2] = floor(32768.0 * m);',
			'm -= c[2] / 32768.0;',
			'c[3] = floor(8388608.0 * m);',
M
r72  
Mr.doob 已提交
68

M
r78  
Mr.doob 已提交
69 70 71 72 73
			'//Unpack exponent',
			'highp float ebias = e + 127.0;',
			'c[0] = floor(ebias / 2.0);',
			'ebias -= c[0] * 2.0;',
			'c[1] += floor(ebias) * 128.0;',
M
r72  
Mr.doob 已提交
74

M
r78  
Mr.doob 已提交
75 76
			'//Unpack sign bit',
			'c[0] += 128.0 * step(0.0, -v);',
M
r72  
Mr.doob 已提交
77

M
r78  
Mr.doob 已提交
78 79 80
			'//Scale back to range',
			'return c / 255.0;',
			'}',
M
r72  
Mr.doob 已提交
81

M
r78  
Mr.doob 已提交
82 83 84 85 86 87 88 89
			'vec4 pack(const in float depth)',
			'{',
			'const vec4 bit_shift = vec4(256.0*256.0*256.0, 256.0*256.0, 256.0, 1.0);',
			'const vec4 bit_mask	= vec4(0.0, 1.0/256.0, 1.0/256.0, 1.0/256.0);',
			'vec4 res = mod(depth*bit_shift*vec4(255), vec4(256))/vec4(255);',
			'res -= res.xxyz * bit_mask;',
			'return res;',
			'}',
M
r72  
Mr.doob 已提交
90

M
r78  
Mr.doob 已提交
91 92 93 94 95 96
			'float unpack(const in vec4 rgba_depth)',
			'{',
			'const vec4 bit_shift = vec4(1.0/(256.0*256.0*256.0), 1.0/(256.0*256.0), 1.0/256.0, 1.0);',
			'float depth = dot(rgba_depth, bit_shift);',
			'return depth;',
			'}',
M
r72  
Mr.doob 已提交
97

M
r78  
Mr.doob 已提交
98 99 100
			'uniform float uTime;',
			'uniform float uScale;',
			'uniform sampler2D tNoise;',
M
r72  
Mr.doob 已提交
101

M
r78  
Mr.doob 已提交
102 103
			'attribute vec4 particlePositionsStartTime;',
			'attribute vec4 particleVelColSizeLife;',
M
r72  
Mr.doob 已提交
104

M
r78  
Mr.doob 已提交
105 106
			'varying vec4 vColor;',
			'varying float lifeLeft;',
M
r72  
Mr.doob 已提交
107

M
r78  
Mr.doob 已提交
108
			'void main() {',
M
r72  
Mr.doob 已提交
109

M
r78  
Mr.doob 已提交
110 111
			'// unpack things from our attributes',
			'vColor = encode_float( particleVelColSizeLife.y );',
M
r72  
Mr.doob 已提交
112

M
r78  
Mr.doob 已提交
113 114 115 116
			'// convert our velocity back into a value we can use',
			'vec4 velTurb = encode_float( particleVelColSizeLife.x );',
			'vec3 velocity = vec3( velTurb.xyz );',
			'float turbulence = velTurb.w;',
M
r72  
Mr.doob 已提交
117

M
r78  
Mr.doob 已提交
118
			'vec3 newPosition;',
M
r72  
Mr.doob 已提交
119

M
r78  
Mr.doob 已提交
120
			'float timeElapsed = uTime - particlePositionsStartTime.a;',
M
r72  
Mr.doob 已提交
121

M
r78  
Mr.doob 已提交
122
			'lifeLeft = 1. - (timeElapsed / particleVelColSizeLife.w);',
M
r72  
Mr.doob 已提交
123

M
r78  
Mr.doob 已提交
124
			'gl_PointSize = ( uScale * particleVelColSizeLife.z ) * lifeLeft;',
M
r72  
Mr.doob 已提交
125

M
r78  
Mr.doob 已提交
126 127 128
			'velocity.x = ( velocity.x - .5 ) * 3.;',
			'velocity.y = ( velocity.y - .5 ) * 3.;',
			'velocity.z = ( velocity.z - .5 ) * 3.;',
M
r72  
Mr.doob 已提交
129

M
r78  
Mr.doob 已提交
130
			'newPosition = particlePositionsStartTime.xyz + ( velocity * 10. ) * ( uTime - particlePositionsStartTime.a );',
M
r72  
Mr.doob 已提交
131

M
r78  
Mr.doob 已提交
132 133
			'vec3 noise = texture2D( tNoise, vec2( newPosition.x * .015 + (uTime * .05), newPosition.y * .02 + (uTime * .015) )).rgb;',
			'vec3 noiseVel = ( noise.rgb - .5 ) * 30.;',
M
r72  
Mr.doob 已提交
134

M
r78  
Mr.doob 已提交
135
			'newPosition = mix(newPosition, newPosition + vec3(noiseVel * ( turbulence * 5. ) ), (timeElapsed / particleVelColSizeLife.a) );',
M
r72  
Mr.doob 已提交
136

M
r78  
Mr.doob 已提交
137 138 139
			'if( velocity.y > 0. && velocity.y < .05 ) {',
			'lifeLeft = 0.;',
			'}',
M
r72  
Mr.doob 已提交
140

M
r78  
Mr.doob 已提交
141 142 143
			'if( velocity.x < -1.45 ) {',
			'lifeLeft = 0.;',
			'}',
M
r72  
Mr.doob 已提交
144

M
r78  
Mr.doob 已提交
145 146 147 148 149 150 151 152
			'if( timeElapsed > 0. ) {',
			'gl_Position = projectionMatrix * modelViewMatrix * vec4( newPosition, 1.0 );',
			'} else {',
			'gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );',
			'lifeLeft = 0.;',
			'gl_PointSize = 0.;',
			'}',
			'}'
M
r72  
Mr.doob 已提交
153

M
r78  
Mr.doob 已提交
154
		].join("\n"),
M
r72  
Mr.doob 已提交
155

M
r78  
Mr.doob 已提交
156
		fragmentShader: [
M
r72  
Mr.doob 已提交
157

M
r78  
Mr.doob 已提交
158 159 160
			'float scaleLinear(float value, vec2 valueDomain) {',
			'return (value - valueDomain.x) / (valueDomain.y - valueDomain.x);',
			'}',
M
r72  
Mr.doob 已提交
161

M
r78  
Mr.doob 已提交
162 163 164
			'float scaleLinear(float value, vec2 valueDomain, vec2 valueRange) {',
			'return mix(valueRange.x, valueRange.y, scaleLinear(value, valueDomain));',
			'}',
M
r72  
Mr.doob 已提交
165

M
r78  
Mr.doob 已提交
166 167
			'varying vec4 vColor;',
			'varying float lifeLeft;',
M
r72  
Mr.doob 已提交
168

M
r78  
Mr.doob 已提交
169
			'uniform sampler2D tSprite;',
M
r72  
Mr.doob 已提交
170

M
r78  
Mr.doob 已提交
171
			'void main() {',
M
r72  
Mr.doob 已提交
172

M
r78  
Mr.doob 已提交
173
			'float alpha = 0.;',
M
r72  
Mr.doob 已提交
174

M
r78  
Mr.doob 已提交
175 176 177 178 179
			'if( lifeLeft > .995 ) {',
			'alpha = scaleLinear( lifeLeft, vec2(1., .995), vec2(0., 1.));//mix( 0., 1., ( lifeLeft - .95 ) * 100. ) * .75;',
			'} else {',
			'alpha = lifeLeft * .75;',
			'}',
M
r72  
Mr.doob 已提交
180

M
r78  
Mr.doob 已提交
181
			'vec4 tex = texture2D( tSprite, gl_PointCoord );',
M
r72  
Mr.doob 已提交
182

M
r78  
Mr.doob 已提交
183 184
			'gl_FragColor = vec4( vColor.rgb * tex.a, alpha * tex.a );',
			'}'
M
r72  
Mr.doob 已提交
185

M
r78  
Mr.doob 已提交
186
		].join("\n")
M
r72  
Mr.doob 已提交
187

M
r78  
Mr.doob 已提交
188
	};
M
r72  
Mr.doob 已提交
189

M
r78  
Mr.doob 已提交
190 191
	// preload a million random numbers
	self.rand = [];
M
r72  
Mr.doob 已提交
192

M
r78  
Mr.doob 已提交
193 194 195
	for (var i = 1e5; i > 0; i--) {
		self.rand.push(Math.random() - .5);
	}
M
r72  
Mr.doob 已提交
196

M
r78  
Mr.doob 已提交
197 198 199
	self.random = function() {
		return ++i >= self.rand.length ? self.rand[i = 1] : self.rand[i];
	}
M
r74  
Mr.doob 已提交
200

M
r78  
Mr.doob 已提交
201
	var textureLoader = new THREE.TextureLoader();
M
r72  
Mr.doob 已提交
202

M
r78  
Mr.doob 已提交
203 204
	self.particleNoiseTex = textureLoader.load("textures/perlin-512.png");
	self.particleNoiseTex.wrapS = self.particleNoiseTex.wrapT = THREE.RepeatWrapping;
M
r72  
Mr.doob 已提交
205

M
r78  
Mr.doob 已提交
206 207
	self.particleSpriteTex = textureLoader.load("textures/particle2.png");
	self.particleSpriteTex.wrapS = self.particleSpriteTex.wrapT = THREE.RepeatWrapping;
M
r72  
Mr.doob 已提交
208

M
r78  
Mr.doob 已提交
209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229
	self.particleShaderMat = new THREE.ShaderMaterial({
		transparent: true,
		depthWrite: false,
		uniforms: {
			"uTime": {
				value: 0.0
			},
			"uScale": {
				value: 1.0
			},
			"tNoise": {
				value: self.particleNoiseTex
			},
			"tSprite": {
				value: self.particleSpriteTex
			}
		},
		blending: THREE.AdditiveBlending,
		vertexShader: GPUParticleShader.vertexShader,
		fragmentShader: GPUParticleShader.fragmentShader
	});
M
r72  
Mr.doob 已提交
230

M
r78  
Mr.doob 已提交
231 232 233
	// define defaults for all values
	self.particleShaderMat.defaultAttributeValues.particlePositionsStartTime = [0, 0, 0, 0];
	self.particleShaderMat.defaultAttributeValues.particleVelColSizeLife = [0, 0, 0, 0];
M
r72  
Mr.doob 已提交
234

M
r78  
Mr.doob 已提交
235
	self.particleContainers = [];
M
r72  
Mr.doob 已提交
236 237


M
r78  
Mr.doob 已提交
238 239
	// extend Object3D
	THREE.Object3D.apply(this, arguments);
M
r72  
Mr.doob 已提交
240

M
r78  
Mr.doob 已提交
241
	this.init = function() {
M
r72  
Mr.doob 已提交
242

M
r78  
Mr.doob 已提交
243
		for (var i = 0; i < self.PARTICLE_CONTAINERS; i++) {
M
r72  
Mr.doob 已提交
244

M
r78  
Mr.doob 已提交
245 246 247
			var c = new THREE.GPUParticleContainer(self.PARTICLES_PER_CONTAINER, self);
			self.particleContainers.push(c);
			self.add(c);
M
r72  
Mr.doob 已提交
248

M
r78  
Mr.doob 已提交
249
		}
M
r72  
Mr.doob 已提交
250

M
r78  
Mr.doob 已提交
251
	}
M
r72  
Mr.doob 已提交
252

M
r78  
Mr.doob 已提交
253
	this.spawnParticle = function(options) {
M
r72  
Mr.doob 已提交
254

M
r78  
Mr.doob 已提交
255 256 257 258
		self.PARTICLE_CURSOR++;
		if (self.PARTICLE_CURSOR >= self.PARTICLE_COUNT) {
			self.PARTICLE_CURSOR = 1;
		}
M
r72  
Mr.doob 已提交
259

M
r78  
Mr.doob 已提交
260
		var currentContainer = self.particleContainers[Math.floor(self.PARTICLE_CURSOR / self.PARTICLES_PER_CONTAINER)];
M
r72  
Mr.doob 已提交
261

M
r78  
Mr.doob 已提交
262
		currentContainer.spawnParticle(options);
M
r72  
Mr.doob 已提交
263

M
r78  
Mr.doob 已提交
264
	}
M
r72  
Mr.doob 已提交
265

M
r78  
Mr.doob 已提交
266 267
	this.update = function(time) {
		for (var i = 0; i < self.PARTICLE_CONTAINERS; i++) {
M
r72  
Mr.doob 已提交
268

M
r78  
Mr.doob 已提交
269
			self.particleContainers[i].update(time);
M
r72  
Mr.doob 已提交
270

M
r78  
Mr.doob 已提交
271 272 273 274
		}
	};

	this.init();
M
r72  
Mr.doob 已提交
275 276 277 278 279 280 281 282 283 284

}

THREE.GPUParticleSystem.prototype = Object.create(THREE.Object3D.prototype);
THREE.GPUParticleSystem.prototype.constructor = THREE.GPUParticleSystem;


// Subclass for particle containers, allows for very large arrays to be spread out
THREE.GPUParticleContainer = function(maxParticles, particleSystem) {

M
r78  
Mr.doob 已提交
285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411
	var self = this;
	self.PARTICLE_COUNT = maxParticles || 100000;
	self.PARTICLE_CURSOR = 0;
	self.time = 0;
	self.DPR = window.devicePixelRatio;
	self.GPUParticleSystem = particleSystem;

	var particlesPerArray = Math.floor(self.PARTICLE_COUNT / self.MAX_ATTRIBUTES);

	// extend Object3D
	THREE.Object3D.apply(this, arguments);

	// construct a couple small arrays used for packing variables into floats etc
	var UINT8_VIEW = new Uint8Array(4)
	var FLOAT_VIEW = new Float32Array(UINT8_VIEW.buffer)

	function decodeFloat(x, y, z, w) {
		UINT8_VIEW[0] = Math.floor(w)
		UINT8_VIEW[1] = Math.floor(z)
		UINT8_VIEW[2] = Math.floor(y)
		UINT8_VIEW[3] = Math.floor(x)
		return FLOAT_VIEW[0]
	}

	function componentToHex(c) {
		var hex = c.toString(16);
		return hex.length == 1 ? "0" + hex : hex;
	}

	function rgbToHex(r, g, b) {
		return "#" + componentToHex(r) + componentToHex(g) + componentToHex(b);
	}

	function hexToRgb(hex) {
		var r = hex >> 16;
		var g = (hex & 0x00FF00) >> 8;
		var b = hex & 0x0000FF;

		if (r > 0) r--;
		if (g > 0) g--;
		if (b > 0) b--;

		return [r, g, b];
	};

	self.particles = [];
	self.deadParticles = [];
	self.particlesAvailableSlot = [];

	// create a container for particles
	self.particleUpdate = false;

	// Shader Based Particle System
	self.particleShaderGeo = new THREE.BufferGeometry();

	// new hyper compressed attributes
	self.particleVertices = new Float32Array(self.PARTICLE_COUNT * 3); // position
	self.particlePositionsStartTime = new Float32Array(self.PARTICLE_COUNT * 4); // position
	self.particleVelColSizeLife = new Float32Array(self.PARTICLE_COUNT * 4);

	for (var i = 0; i < self.PARTICLE_COUNT; i++) {
		self.particlePositionsStartTime[i * 4 + 0] = 100; //x
		self.particlePositionsStartTime[i * 4 + 1] = 0; //y
		self.particlePositionsStartTime[i * 4 + 2] = 0.0; //z
		self.particlePositionsStartTime[i * 4 + 3] = 0.0; //startTime

		self.particleVertices[i * 3 + 0] = 0; //x
		self.particleVertices[i * 3 + 1] = 0; //y
		self.particleVertices[i * 3 + 2] = 0.0; //z

		self.particleVelColSizeLife[i * 4 + 0] = decodeFloat(128, 128, 0, 0); //vel
		self.particleVelColSizeLife[i * 4 + 1] = decodeFloat(0, 254, 0, 254); //color
		self.particleVelColSizeLife[i * 4 + 2] = 1.0; //size
		self.particleVelColSizeLife[i * 4 + 3] = 0.0; //lifespan
	}

	self.particleShaderGeo.addAttribute('position', new THREE.BufferAttribute(self.particleVertices, 3));
	self.particleShaderGeo.addAttribute('particlePositionsStartTime', new THREE.BufferAttribute(self.particlePositionsStartTime, 4).setDynamic(true));
	self.particleShaderGeo.addAttribute('particleVelColSizeLife', new THREE.BufferAttribute(self.particleVelColSizeLife, 4).setDynamic(true));

	self.posStart = self.particleShaderGeo.getAttribute('particlePositionsStartTime')
	self.velCol = self.particleShaderGeo.getAttribute('particleVelColSizeLife');

	self.particleShaderMat = self.GPUParticleSystem.particleShaderMat;

	this.init = function() {
		self.particleSystem = new THREE.Points(self.particleShaderGeo, self.particleShaderMat);
		self.particleSystem.frustumCulled = false;
		this.add(self.particleSystem);
	};

	var options = {},
		position = new THREE.Vector3(),
		velocity = new THREE.Vector3(),
		positionRandomness = 0.,
		velocityRandomness = 0.,
		color = 0xffffff,
		colorRandomness = 0.,
		turbulence = 0.,
		lifetime = 0.,
		size = 0.,
		sizeRandomness = 0.,
		i;

	var maxVel = 2;
	var maxSource = 250;
	this.offset = 0;
	this.count = 0;

	this.spawnParticle = function(options) {

		options = options || {};

		// setup reasonable default values for all arguments
		position = options.position !== undefined ? position.copy(options.position) : position.set(0., 0., 0.);
		velocity = options.velocity !== undefined ? velocity.copy(options.velocity) : velocity.set(0., 0., 0.);
		positionRandomness = options.positionRandomness !== undefined ? options.positionRandomness : 0.0;
		velocityRandomness = options.velocityRandomness !== undefined ? options.velocityRandomness : 0.0;
		color = options.color !== undefined ? options.color : 0xffffff;
		colorRandomness = options.colorRandomness !== undefined ? options.colorRandomness : 1.0;
		turbulence = options.turbulence !== undefined ? options.turbulence : 1.0;
		lifetime = options.lifetime !== undefined ? options.lifetime : 5.0;
		size = options.size !== undefined ? options.size : 10;
		sizeRandomness = options.sizeRandomness !== undefined ? options.sizeRandomness : 0.0,
			smoothPosition = options.smoothPosition !== undefined ? options.smoothPosition : false;

		if (self.DPR !== undefined) size *= self.DPR;
M
r72  
Mr.doob 已提交
412

M
r78  
Mr.doob 已提交
413 414 415 416 417 418 419 420 421 422 423 424
		i = self.PARTICLE_CURSOR;

		self.posStart.array[i * 4 + 0] = position.x + ((particleSystem.random()) * positionRandomness); // - ( velocity.x * particleSystem.random() ); //x
		self.posStart.array[i * 4 + 1] = position.y + ((particleSystem.random()) * positionRandomness); // - ( velocity.y * particleSystem.random() ); //y
		self.posStart.array[i * 4 + 2] = position.z + ((particleSystem.random()) * positionRandomness); // - ( velocity.z * particleSystem.random() ); //z
		self.posStart.array[i * 4 + 3] = self.time + (particleSystem.random() * 2e-2); //startTime

		if (smoothPosition === true) {
			self.posStart.array[i * 4 + 0] += -(velocity.x * particleSystem.random()); //x
			self.posStart.array[i * 4 + 1] += -(velocity.y * particleSystem.random()); //y
			self.posStart.array[i * 4 + 2] += -(velocity.z * particleSystem.random()); //z
		}
M
r72  
Mr.doob 已提交
425

M
r78  
Mr.doob 已提交
426 427 428
		var velX = velocity.x + (particleSystem.random()) * velocityRandomness;
		var velY = velocity.y + (particleSystem.random()) * velocityRandomness;
		var velZ = velocity.z + (particleSystem.random()) * velocityRandomness;
M
r72  
Mr.doob 已提交
429

M
r78  
Mr.doob 已提交
430 431
		// convert turbulence rating to something we can pack into a vec4
		var turbulence = Math.floor(turbulence * 254);
M
r72  
Mr.doob 已提交
432

M
r78  
Mr.doob 已提交
433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456
		// clamp our value to between 0. and 1.
		velX = Math.floor(maxSource * ((velX - -maxVel) / (maxVel - -maxVel)));
		velY = Math.floor(maxSource * ((velY - -maxVel) / (maxVel - -maxVel)));
		velZ = Math.floor(maxSource * ((velZ - -maxVel) / (maxVel - -maxVel)));

		self.velCol.array[i * 4 + 0] = decodeFloat(velX, velY, velZ, turbulence); //vel

		var rgb = hexToRgb(color);

		for (var c = 0; c < rgb.length; c++) {
			rgb[c] = Math.floor(rgb[c] + ((particleSystem.random()) * colorRandomness) * 254);
			if (rgb[c] > 254) rgb[c] = 254;
			if (rgb[c] < 0) rgb[c] = 0;
		}

		self.velCol.array[i * 4 + 1] = decodeFloat(rgb[0], rgb[1], rgb[2], 254); //color
		self.velCol.array[i * 4 + 2] = size + (particleSystem.random()) * sizeRandomness; //size
		self.velCol.array[i * 4 + 3] = lifetime; //lifespan

		if (this.offset == 0) {
			this.offset = self.PARTICLE_CURSOR;
		}

		self.count++;
M
r72  
Mr.doob 已提交
457

M
r78  
Mr.doob 已提交
458
		self.PARTICLE_CURSOR++;
M
r72  
Mr.doob 已提交
459

M
r78  
Mr.doob 已提交
460 461 462
		if (self.PARTICLE_CURSOR >= self.PARTICLE_COUNT) {
			self.PARTICLE_CURSOR = 0;
		}
M
r72  
Mr.doob 已提交
463

M
r78  
Mr.doob 已提交
464
		self.particleUpdate = true;
M
r72  
Mr.doob 已提交
465

M
r78  
Mr.doob 已提交
466
	}
M
r72  
Mr.doob 已提交
467

M
r78  
Mr.doob 已提交
468
	this.update = function(time) {
M
r72  
Mr.doob 已提交
469

M
r78  
Mr.doob 已提交
470 471
		self.time = time;
		self.particleShaderMat.uniforms['uTime'].value = time;
M
r72  
Mr.doob 已提交
472

M
r78  
Mr.doob 已提交
473
		this.geometryUpdate();
M
r72  
Mr.doob 已提交
474

M
r78  
Mr.doob 已提交
475
	};
M
r72  
Mr.doob 已提交
476

M
r78  
Mr.doob 已提交
477 478 479
	this.geometryUpdate = function() {
		if (self.particleUpdate == true) {
			self.particleUpdate = false;
M
r72  
Mr.doob 已提交
480

M
r78  
Mr.doob 已提交
481 482 483 484 485 486 487 488
			// if we can get away with a partial buffer update, do so
			if (self.offset + self.count < self.PARTICLE_COUNT) {
				self.posStart.updateRange.offset = self.velCol.updateRange.offset = self.offset * 4;
				self.posStart.updateRange.count = self.velCol.updateRange.count = self.count * 4;
			} else {
				self.posStart.updateRange.offset = 0;
				self.posStart.updateRange.count = self.velCol.updateRange.count = (self.PARTICLE_COUNT * 4);
			}
M
r72  
Mr.doob 已提交
489

M
r78  
Mr.doob 已提交
490 491
			self.posStart.needsUpdate = true;
			self.velCol.needsUpdate = true;
M
r72  
Mr.doob 已提交
492

M
r78  
Mr.doob 已提交
493 494 495 496
			self.offset = 0;
			self.count = 0;
		}
	}
M
r72  
Mr.doob 已提交
497

M
r78  
Mr.doob 已提交
498
	this.init();
M
r72  
Mr.doob 已提交
499 500 501 502 503

}

THREE.GPUParticleContainer.prototype = Object.create(THREE.Object3D.prototype);
THREE.GPUParticleContainer.prototype.constructor = THREE.GPUParticleContainer;