threex
Version:
Game Extensions for three.js http://www.threejsgames.com/extensions/
379 lines (341 loc) • 11.9 kB
HTML
<script src='../../../vendor/three.js/build/three.min.js'></script>
<script src="../../../vendor/require.js/require.js"></script>
<script src='js/threex.textureutils.js'></script>
<script src='js/fartscroll.js'></script>
<script src='bower_components/threex.nyancat/threex.nyancat.js'></script>
<script src='bower_components/threex.nyancat/threex.nyancatsound.js'></script>
<script src='bower_components/threex.nyancat/threex.nyancatstars.js'></script>
<script src='bower_components/threex.nyancat/threex.nyancatrainbow.js'></script>
<body style='margin: 0px; background-color: #003366; overflow: hidden;'><script>
require([ '../../threex.effectcomposer/package.require.js'
], function(){
var renderer = new THREE.WebGLRenderer();
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );
renderer.setClearColor('#003366')
var updateFcts = [];
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.01, 1000 );
camera.position.z = 3;
scene.fog = new THREE.FogExp2( 0x003366, 0.0095 );
var pointLight = new THREE.PointLight( 0xFFFFFF );
pointLight.position.z = 10;
scene.add(pointLight);
//////////////////////////////////////////////////////////////////////////////////
// comment //
//////////////////////////////////////////////////////////////////////////////////
var paused = false;
var nyanCat = new THREEx.NyanCat()
nyanCat.container.scale.multiplyScalar(1/20)
scene.add( nyanCat.container )
updateFcts.push(function(delta, now){
if( paused ) return
nyanCat.update(delta, now)
})
// var sound = new THREEx.NyanCatSound()
// document.body.addEventListener('mousedown', function(){
// paused = paused ? false : true
// sound.toggle()
// })
var nyanStars = new THREEx.NyanCatStars()
nyanStars.container.scale.multiplyScalar(1/20)
scene.add( nyanStars.container )
updateFcts.push(function(delta, now){
if( paused ) return
nyanStars.update(delta, now)
})
var nyanRainbow = new THREEx.NyanCatRainbow()
nyanRainbow.container.scale.multiplyScalar(1/20)
window.nyanRainbow = nyanRainbow
scene.add( nyanRainbow.container )
updateFcts.push(function(delta, now){
if( paused ) return
nyanRainbow.update(delta, now)
})
//////////////////////////////////////////////////////////////////////////////////
// Camera Controls //
//////////////////////////////////////////////////////////////////////////////////
var mouse = {x : 0, y : 0}
document.addEventListener('mousemove', function(event){
mouse.x = (event.clientX / window.innerWidth ) - 0.5
mouse.y = (event.clientY / window.innerHeight) - 0.5
}, false)
updateFcts.push(function(delta, now){
camera.position.x += (mouse.x*15 - camera.position.x) * 0.01
camera.position.y += (mouse.y*15 - camera.position.y) * 0.01
camera.lookAt( scene.position )
})
//////////////////////////////////////////////////////////////////////////////////
// comment //
//////////////////////////////////////////////////////////////////////////////////
var urls = [
// 'images/flame/flame00.png',
// 'images/flame/flame01.png',
// 'images/flame/flame02.png',
// 'images/flame/flame03.png',
// 'images/flame/flame04.png',
// 'images/flame/flame05.png',
// 'images/flame/flame06.png',
// 'images/flame/flame07.png',
// 'images/flame/flame08.png',
// 'images/flame/flame09.png',
// 'images/flame/flame10.png',
// 'images/flame/flame11.png',
'images/flame/flame12.png',
'images/flame/flame13.png',
'images/flame/flame14.png',
'images/flame/flame15.png',
'images/flame/flame16.png',
'images/flame/flame17.png',
'images/flame/flame18.png',
'images/flame/flame19.png',
'images/flame/flame20.png',
'images/flame/flame21.png',
'images/flame/flame22.png',
'images/flame/flame23.png',
'images/flame/flame24.png'
];
//////////////////////////////////////////////////////////////////////////////////
// handle trigger //
//////////////////////////////////////////////////////////////////////////////////
// TODO put that in the particle itself ?
// handle emit rate with a MidiTrigger
var emitTrigger = new MidiTrigger(0.2, 0.2)
/**
* 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
}
}
var fartScroll = new FartScroll();
document.addEventListener('mousedown' , function(){
emitTrigger.start()
var soundIdx = fartScroll.play()
var durations = [
0.5,
0.2,
0.4,
0.1,
0.4,
0.8,
0.6,
0.1,
0.1,
0.5,
];
var duration = durations[soundIdx]
scene.remove( nyanRainbow.container )
setTimeout(function(){
console.log('stop emit', duration)
emitTrigger.stop()
scene.add( nyanRainbow.container )
}, duration*1000)
})
//////////////////////////////////////////////////////////////////////////////////
// comment //
//////////////////////////////////////////////////////////////////////////////////
// TODO put that in the particle itself ?
loadTremulousFlameParticules(urls, function(texture, urls){
// create emitter
var emitter = new FlameThrower(texture, urls.length, scene)
// emit based on rate
var lastEmit = 0;
updateFcts.push(function(delta, now){
// rate limiter emition
var rate = 15 * emitTrigger.intensity();
if( rate === 0 || now - lastEmit < 1/rate ) return;
lastEmit = now;
var position = new THREE.Vector3(0,0,0)
position.copy( nyanCat.container.position )
position.add(new THREE.Vector3(-0.2,0,0.05))
var velocity = new THREE.Vector3(-7, 0, 0)
// var velocity = new THREE.Vector3(0, 1, -6)
emitter.emit(position, velocity);
});
// update emitter
updateFcts.push(function(delta, now){
emitter.update(delta, now);
})
})
function loadTremulousFlameParticules(urls, onReady){
// load all the images from urls
THREEx.TextureUtils.loadImages(urls, function(images, urls){
// build a tiled spreadsheet canvas with images
var canvas = THREEx.TextureUtils.buildTiledSpriteSheet({
images : images,
spriteW : images[0].width,
spriteH : images[0].height,
nSpriteX: 1
});
// create the texture
var texture = new THREE.Texture( canvas );
texture.needsUpdate = true;
// generate Alpha as it got no alpha
THREEx.TextureUtils.generateAlphaFromLuminance(texture, 16, 1);
// notify caller
onReady(texture, urls)
})
}
//////////////////////////////////////////////////////////////////////////////////
// render the scene //
//////////////////////////////////////////////////////////////////////////////////
// updateFcts.push(function(){
// renderer.render( scene, camera );
// })
var composer = new THREEx.EffectComposer(renderer)
.renderPass(scene, camera)
// .film()
.motionBlur(0.7)
.vignette()
.copy()
.finish()
updateFcts.push(function(delta, now){
composer.update(delta);
})
//////////////////////////////////////////////////////////////////////////////////
// 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 FlameThrower(texture, nTiles, container){
// handle local loop
var updateFcts = [];
this.update = function(delta, now){
updateFcts.forEach(function(updateFct){
updateFct(delta, now)
})
}
// 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(position, velocity){
// randomize the initial position
position = position.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 = 0.8 + (Math.random()-0.5)*0
// set velocity
var speed = velocity.length()
velocity = velocity.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))
})
}
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>