threex
Version:
Game Extensions for three.js http://www.threejsgames.com/extensions/
244 lines (227 loc) • 8.03 kB
HTML
<!DOCTYPE html>
<script src='../../../vendor/three.js/build/three.min.js'></script>
<script src='../threex.textureutils.js'></script>
<script src='../webaudiox.flamethrower.js'></script>
<script src='../threex.flamethrowertexture.js'></script>
<script src='../threex.flamethrowersprite.js'></script>
<body style='margin: 0px; background-color: #bbbbbb; overflow: hidden;'><script>
var renderer = new THREE.WebGLRenderer();
renderer.setSize( window.innerWidth, window.innerHeight );
renderer.setClearColor('black')
document.body.appendChild( renderer.domElement );
var updateFcts = [];
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.01, 1000 );
camera.position.z = 3;
//////////////////////////////////////////////////////////////////////////////////
// comment //
//////////////////////////////////////////////////////////////////////////////////
// TODO put that in the particle itself ?
new THREEx.FlameThrowerTexture(function(texture){
var flameSprite = new THREEx.FlameThrowerSprite(texture)
scene.add(flameSprite.object3d)
// create sound
var sound = new WebAudiox.FlameThrower()
// update sound
updateFcts.push(function(delta, now){ sound.update(delta, now) })
// create emitter
var emitter = new FlameThrowerEmitter(flameSprite, scene)
// update emitter
updateFcts.push(function(delta, now){ emitter.update(delta, now) })
var position = new THREE.Vector3(-3,0,0)
//var position = new THREE.Vector3(0,-1,-2)
var position = new THREE.Vector3(-4,0,-3)
var velocity = new THREE.Vector3(7, 0, 0)
emitter.start(position, velocity)
// start the trigger and
emitter.trigger.start();
sound.trigger.start();
document.addEventListener('mousedown' , function(){
emitter.trigger.start()
sound.trigger.start();
})
document.addEventListener('mouseup' , function(){
emitter.trigger.stop()
sound.trigger.stop()
})
})
//////////////////////////////////////////////////////////////////////////////////
// render the scene //
//////////////////////////////////////////////////////////////////////////////////
updateFcts.push(function(){
renderer.render( scene, camera );
})
//////////////////////////////////////////////////////////////////////////////////
// loop runner //
//////////////////////////////////////////////////////////////////////////////////
var lastTimeMsec= null
requestAnimationFrame(function animate(nowMsec){
// keep looping
requestAnimationFrame( animate );
// measure time
lastTimeMsec = lastTimeMsec || nowMsec-1000/60
var deltaMsec = Math.min(200, nowMsec - lastTimeMsec)
lastTimeMsec = nowMsec
// call each update function
updateFcts.forEach(function(updateFn){
updateFn(deltaMsec/1000, nowMsec/1000)
})
})
//////////////////////////////////////////////////////////////////////////////////
// comment //
//////////////////////////////////////////////////////////////////////////////////
function FlameThrowerEmitter(flameSprite, container){
var texture = flameSprite.texture
var nTiles = flameSprite.nTiles
// handle local loop
var updateFcts = [];
// init trigger
var trigger = new MidiTrigger(0.2, 0.2)
this.trigger = trigger;
// handle tweening
var age2Friction= (function(){
var gradient = createLinearGradient([
{x : 0.00, y: 1.00}, {x : 0.50, y: 1.00},
{x : 0.70, y: 0.95}, {x : 1.00, y: 0.95}
]);
return function(age, maxAge){ return gradient(age/maxAge) }
})();
var age2Opacity = (function(){
var tween = createTweenMidi(1, 0.1, 0.5)
return function(age, maxAge){ return tween(age/maxAge) }
})();
var age2uvOffset= function(age, maxAge){
var imageIdx = Math.floor(age/maxAge * nTiles);
var uvOffsetY = 1 - imageIdx * 1/nTiles;
return uvOffsetY
}
// put the emmiter
this.emit = function(startPosition, initialVelocity){
// randomize the initial position
var position = startPosition.clone()
// init sprite material
var material = new THREE.SpriteMaterial({
map : texture,
useScreenCoordinates : false,
transparent : true,
blending : THREE.AdditiveBlending
})
material.uvScale.set(1, 1/nTiles)
// init sprite
var sprite = new THREE.Sprite(material)
sprite.position.copy(position)
sprite.rotation = Math.random()*Math.PI*2
sprite.scale.set(1,1,1).multiplyScalar(2)
container.add( sprite )
// init age2OffsetY
var uvOffset = age2uvOffset(0, maxAge)
material.uvOffset.set(0, uvOffset)
var maxAge = 1.2 + (Math.random()-0.5)*0
// set velocity
var speed = initialVelocity.length()
var velocity = initialVelocity.clone().normalize()
velocity.x += (Math.random()-0.5)*0.0
velocity.y += (Math.random()-0.5)*0.2
velocity.z += (Math.random()-0.5)*0.0
velocity.setLength(speed)
// set acceleration
var acceleration= new THREE.Vector3(0, 2, 0)
// init opacity
material.opacity= age2Opacity(0, maxAge)
var birthDate = Date.now()/1000
updateFcts.push(function callback(delta, now){
var age = Date.now()/1000 - birthDate
if( age >= maxAge ){
sprite.parent.remove(sprite)
updateFcts.splice(updateFcts.indexOf(callback),1)
return;
}
// handle acceleration
velocity.add(acceleration.clone().multiplyScalar(delta))
// handle friction
velocity.multiplyScalar( age2Friction(age, maxAge) )
// move by velocity
sprite.position.add( velocity.clone().multiplyScalar(delta) )
// make it grow
sprite.scale.multiplyScalar( 1.015 )
// handle opacity
material.opacity= age2Opacity(age, maxAge)
// init uvOffset
material.uvOffset.set(0, age2uvOffset(age, maxAge))
})
}
var _loopCb = null;
this.start = function(startPosition, initialVelocity, maxRate){
maxRate = maxRate !== undefined ? maxRate : 15
var lastEmit = 0;
updateFcts.push(_loopCb = function(delta, now){
// rate limiter emition
var rate = maxRate * trigger.intensity();
if( rate === 0 || now - lastEmit < 1/rate ) return;
lastEmit = now;
// emit one
this.emit(startPosition, initialVelocity)
}.bind(this))
return this;
}
this.stop = function(){
updateFcts.splice(updateFcts.indexOf(_loopCb),1)
return this;
}
this.update = function(delta, now){
updateFcts.forEach(function(updateFct){
updateFct(delta, now)
})
}
/**
* trigger with a attack, a sustain and a release. like midi notes
*
* @param {Number} attackDelay nb second for the attack phase
* @param {Number} releaseDelay nb second for the release phase
* @param {Number} maxValue the maximum value of the intensity
*/
function MidiTrigger(attackDelay, releaseDelay){
var lastStart = 0, lastStop = 0;
this.start = function(){ lastStart = Date.now()/1000 }
this.stop = function(){ lastStop = Date.now()/1000 }
this.intensity = function(){
var present = Date.now()/1000
if( lastStop >= lastStart ){ // release in-progress or overs
if(present - lastStop >= releaseDelay) return 0 // release over
return 1 - (present - lastStop) / releaseDelay // release inprogress
}else if( present - lastStart <= attackDelay ){ // attack in-progress
return (present - lastStart) / attackDelay
}else return 1; // sustain in-progress
}
}
function createTweenMidi(maxAge, attackTime, releaseTime){
return function(age){
if( age < attackTime ){
return age / attackTime
}else if( age < maxAge - releaseTime ){
return 1;
}else{
return (maxAge - age) / releaseTime
}
}
}
function createLinearGradient(keyPoints){
return function(x){
// find the keyPoints
for( var i = 0; i < keyPoints.length; i++ ){
if( x <= keyPoints[i].x ) break;
}
if( i === 0 ) return keyPoints[0].y;
// sanity check
console.assert(i < keyPoints.length );
// compute the y
var previous = keyPoints[i-1];
var next = keyPoints[i];
var ratio = (x - previous.x) / (next.x - previous.x)
var y = previous.y + ratio * (next.y - previous.y)
// return y
return y;
}
}
}
</script></body>