From da8afc4e6df4499e527137fe67de7670a0aeba29 Mon Sep 17 00:00:00 2001 From: alteredq Date: Sat, 26 Feb 2011 00:24:14 +0100 Subject: [PATCH] Refactored CameraControl into THREE.QuakeCamera. Now you can add keyboard + mouse camera control with a single line, just change camera type and add parameters: camera = new THREE.QuakeCamera( { fov: 50, aspect: window.innerWidth / window.innerHeight, near: 1, far: 10000, movement_speed: 1, look_speed: 0.002, nofly: true, look_vertical: false } ); Thanks to mrdoob for suggestions ;) --- build/Three.js | 28 ++++--- examples/misc_sound.html | 13 ++- src/cameras/Camera.js | 2 +- src/cameras/QuakeCamera.js | 165 +++++++++++++++++++++++++++++++++++++ utils/build.py | 1 + 5 files changed, 189 insertions(+), 20 deletions(-) create mode 100644 src/cameras/QuakeCamera.js diff --git a/build/Three.js b/build/Three.js index 0916c82e..449b01a4 100755 --- a/build/Three.js +++ b/build/Three.js @@ -54,7 +54,7 @@ THREE.Quaternion.prototype.set=function(a,b,c,d){this.x=a;this.y=b;this.z=c;this THREE.Quaternion.prototype.calculateW=function(){this.w=-Math.sqrt(Math.abs(1-this.x*this.x-this.y*this.y-this.z*this.z));return this};THREE.Quaternion.prototype.inverse=function(){this.x*=-1;this.y*=-1;this.z*=-1;return this};THREE.Quaternion.prototype.length=function(){return Math.sqrt(this.x*this.x+this.y*this.y+this.z*this.z+this.w*this.w)}; THREE.Quaternion.prototype.normalize=function(){var a=Math.sqrt(this.x*this.x+this.y*this.y+this.z*this.z+this.w*this.w);if(a==0)this.w=this.z=this.y=this.x=0;else{a=1/a;this.x*=a;this.y*=a;this.z*=a;this.w*=a}return this};THREE.Quaternion.prototype.multiplySelf=function(a){var b=this.x,c=this.y,d=this.z,f=this.w,g=a.x,h=a.y,k=a.z;a=a.w;this.x=b*a+f*g+c*k-d*h;this.y=c*a+f*h+d*g-b*k;this.z=d*a+f*k+b*h-c*g;this.w=f*a-b*g-c*h-d*k;return this}; THREE.Quaternion.prototype.multiplyVector3=function(a,b){b||(b=a);var c=a.x,d=a.y,f=a.z,g=this.x,h=this.y,k=this.z,j=this.w,m=j*c+h*f-k*d,n=j*d+k*c-g*f,w=j*f+g*d-h*c;c=-g*c-h*d-k*f;b.x=m*j+c*-g+n*-k-w*-h;b.y=n*j+c*-h+w*-g-m*-k;b.z=w*j+c*-k+m*-h-n*-g;return b};THREE.Quaternion.prototype.toMatrix3=function(){};THREE.Quaternion.prototype.toMatrix4=function(){}; -THREE.Quaternion.slerp=function(a,b,c,d){var f=a.w*b.w+a.x*b.x+a.y*b.y+a.z*b.z;if(Math.abs(f)>=1){c.w=a.w;c.x=a.x;c.y=a.y;c.z=a.z;return c}var g=Math.acos(f),h=Math.sqrt(1-f*f);if(Math.abs(h)<0.001){c.w=0.5*(a.w+b.w);c.x=0.5*(a.x+b.x);c.y=0.5*(a.y+b.y);c.z=0.5*(a.z+b.z);return c}f=Math.sin((1-d)*g)/h;d=Math.sin(d*g)/h;c.w=a.w*f+b.w*d;c.x=a.x*f+b.x*d;c.y=a.y*f+b.y*d;c.z=a.z*f+b.z*d;return c}; +THREE.Quaternion.slerp=function(a,b,c,d){var f=a.w*b.w+a.x*b.x+a.y*b.y+a.z*b.z;if(Math.abs(f)>=1){c.w=a.w;c.x=a.x;c.y=a.y;c.z=a.z;return c}var g=Math.acos(f),h=Math.sqrt(1-f*f);if(Math.abs(h)<0.0010){c.w=0.5*(a.w+b.w);c.x=0.5*(a.x+b.x);c.y=0.5*(a.y+b.y);c.z=0.5*(a.z+b.z);return c}f=Math.sin((1-d)*g)/h;d=Math.sin(d*g)/h;c.w=a.w*f+b.w*d;c.x=a.x*f+b.x*d;c.y=a.y*f+b.y*d;c.z=a.z*f+b.z*d;return c}; THREE.Vertex=function(a,b){this.position=a||new THREE.Vector3;this.positionWorld=new THREE.Vector3;this.positionScreen=new THREE.Vector4;this.normal=b||new THREE.Vector3;this.normalWorld=new THREE.Vector3;this.normalScreen=new THREE.Vector3;this.tangent=new THREE.Vector4;this.__visible=!0};THREE.Vertex.prototype={toString:function(){return"THREE.Vertex ( position: "+this.position+", normal: "+this.normal+" )"}}; THREE.Face3=function(a,b,c,d,f){this.a=a;this.b=b;this.c=c;this.centroid=new THREE.Vector3;this.normal=d instanceof THREE.Vector3?d:new THREE.Vector3;this.vertexNormals=d instanceof Array?d:[];this.materials=f instanceof Array?f:[f]};THREE.Face3.prototype={toString:function(){return"THREE.Face3 ( "+this.a+", "+this.b+", "+this.c+" )"}}; THREE.Face4=function(a,b,c,d,f,g){this.a=a;this.b=b;this.c=c;this.d=d;this.centroid=new THREE.Vector3;this.normal=f instanceof THREE.Vector3?f:new THREE.Vector3;this.vertexNormals=f instanceof Array?f:[];this.materials=g instanceof Array?g:[g]};THREE.Face4.prototype={toString:function(){return"THREE.Face4 ( "+this.a+", "+this.b+", "+this.c+" "+this.d+" )"}};THREE.UV=function(a,b){this.u=a||0;this.v=b||0}; @@ -74,21 +74,27 @@ this.vertices+", faces: "+this.faces+", uvs: "+this.uvs+" )"}};THREE.GeometryIdC THREE.AnimationHandler=function(){var a=[],b={};b.update=function(c){for(var d=0;dthis.data.length){for(;j>this.data.length;)j-=this.data.length;this.startTime=(new Date).getTime()*0.001-j;j=(new Date).getTime()*0.001-this.startTime}h=Math.min(parseInt(j*this.data.fps),parseInt(this.data.length*this.data.fps));for(var n=0,w=this.hierarchy.length;n1){console.log("Scale out of bounds:"+b);b=b<0?0:1}THREE.Quaternion.slerp(d,f,g.quaternion,b)}else{c=c==="pos"?g.position:g.scale;c.x=d[0]+(f[0]-d[0])*b;c.y=d[1]+(f[1]-d[1])*b;c.z=d[2]+(f[2]-d[2])*b}}}if(k[0][h]===undefined){this.hierarchy[0].update(undefined,!0);for(n=0;nthis.data.length){for(;j>this.data.length;)j-=this.data.length;this.startTime=(new Date).getTime()*0.0010-j;j=(new Date).getTime()*0.0010-this.startTime}h=Math.min(parseInt(j*this.data.fps),parseInt(this.data.length*this.data.fps));for(var n=0,w=this.hierarchy.length;n1){console.log("Scale out of bounds:"+b);b=b<0?0:1}THREE.Quaternion.slerp(d,f,g.quaternion,b)}else{c=c==="pos"?g.position:g.scale;c.x=d[0]+(f[0]-d[0])*b;c.y=d[1]+(f[1]-d[1])*b;c.z=d[2]+(f[2]-d[2])*b}}}if(k[0][h]===undefined){this.hierarchy[0].update(undefined,!0);for(n=0;n-this.zNear)return!1;if(h+g<-this.zFar)return!1;h-=g;var k=this.projectionMatrix,j=1/(k.n43*h),m=j*this.screenCenterX,n=(f.n11*b+f.n12*c+f.n13*d+f.n14)*k.n11*m;g=k.n11*g*m;if(n+g<-this.screenCenterX)return!1;if(n-g>this.screenCenterX)return!1;b=(f.n21*b+f.n22*c+f.n23*d+f.n24)*k.n22*j*this.screenCenterY; -if(b+g<-this.screenCenterY)return!1;if(b-g>this.screenCenterY)return!1;a.screenPosition.set(n,b,h,g);return!0};THREE.Light=function(a){THREE.Object3D.call(this);this.color=new THREE.Color(a)};THREE.Light.prototype=new THREE.Object3D;THREE.Light.prototype.constructor=THREE.Light;THREE.Light.prototype.supr=THREE.Object3D.prototype;THREE.AmbientLight=function(a){THREE.Light.call(this,a)};THREE.AmbientLight.prototype=new THREE.Light;THREE.AmbientLight.prototype.constructor=THREE.AmbientLight; -THREE.DirectionalLight=function(a,b){THREE.Light.call(this,a);this.position=new THREE.Vector3(0,1,0);this.intensity=b||1};THREE.DirectionalLight.prototype=new THREE.Light;THREE.DirectionalLight.prototype.constructor=THREE.DirectionalLight;THREE.PointLight=function(a,b){THREE.Light.call(this,a);this.position=new THREE.Vector3;this.intensity=b||1};THREE.PointLight.prototype=new THREE.Light;THREE.PointLight.prototype.constructor=THREE.PointLight;THREE.FlatShading=0;THREE.SmoothShading=1; -THREE.NormalBlending=0;THREE.AdditiveBlending=1;THREE.SubtractiveBlending=2;THREE.BillboardBlending=3;THREE.ReverseSubtractiveBlending=4;THREE.MaterialCounter={value:0}; +if(b+g<-this.screenCenterY)return!1;if(b-g>this.screenCenterY)return!1;a.screenPosition.set(n,b,h,g);return!0};function bind(a,b){return function(){b.apply(a,arguments)}} +THREE.QuakeCamera=function(a){THREE.Camera.call(this,a.fov,a.aspect,a.near,a.far,a.target);this.movement_speed=1;this.look_speed=0.0050;this.nofly=!1;this.look_vertical=!0;this.domElement=document;if(a){if(a.movement_speed!==undefined)this.movement_speed=a.movement_speed;if(a.look_speed!==undefined)this.look_speed=a.look_speed;if(a.nofly!==undefined)this.nofly=a.nofly;if(a.look_vertical!==undefined)this.look_vertical=a.look_vertical;if(a.domElement!==undefined)this.domElement=a.domElement}this.theta= +this.phy=this.lon=this.lat=this.mouseY=this.mouseX=0;this.moveForward=!1;this.moveBackward=!1;this.moveLeft=!1;this.moveRight=!1;this.windowHalfX=window.innerWidth/2;this.windowHalfY=window.innerHeight/2;this.onMouseDown=function(b){b.preventDefault();b.stopPropagation();switch(b.button){case 0:this.moveForward=!0;break;case 2:this.moveBackward=!0}};this.onMouseUp=function(b){b.preventDefault();b.stopPropagation();switch(b.button){case 0:this.moveForward=!1;break;case 2:this.moveBackward=!1}};this.onMouseMove= +function(b){this.mouseX=b.clientX-this.windowHalfX;this.mouseY=b.clientY-this.windowHalfY};this.onKeyDown=function(b){switch(b.keyCode){case 38:case 87:this.moveForward=!0;break;case 37:case 65:this.moveLeft=!0;break;case 40:case 83:this.moveBackward=!0;break;case 39:case 68:this.moveRight=!0}};this.onKeyUp=function(b){switch(b.keyCode){case 38:case 87:this.moveForward=!1;break;case 37:case 65:this.moveLeft=!1;break;case 40:case 83:this.moveBackward=!1;break;case 39:case 68:this.moveRight=!1}};this.update= +function(){this.moveForward&&this.translateZ(-this.movement_speed,this.nofly);this.moveBackward&&this.translateZ(this.movement_speed,this.nofly);this.moveLeft&&this.translateX(-this.movement_speed,this.nofly);this.moveRight&&this.translateX(this.movement_speed,this.nofly);this.lon+=this.mouseX*this.look_speed;this.look_vertical&&(this.lat-=this.mouseY*this.look_speed);this.lat=Math.max(-85,Math.min(85,this.lat));this.phi=(90-this.lat)*Math.PI/180;this.theta=this.lon*Math.PI/180;var b=this.target.position, +c=this.position;b.x=c.x+100*Math.sin(this.phi)*Math.cos(this.theta);b.y=c.y+100*Math.cos(this.phi);b.z=c.z+100*Math.sin(this.phi)*Math.sin(this.theta);this.supr.update.call(this)};this.domElement.addEventListener("contextmenu",function(b){b.preventDefault()},!1);this.domElement.addEventListener("mousemove",bind(this,this.onMouseMove),!1);this.domElement.addEventListener("mousedown",bind(this,this.onMouseDown),!1);this.domElement.addEventListener("mouseup",bind(this,this.onMouseUp),!1);this.domElement.addEventListener("keydown", +bind(this,this.onKeyDown),!1);this.domElement.addEventListener("keyup",bind(this,this.onKeyUp),!1)};THREE.QuakeCamera.prototype=new THREE.Camera;THREE.QuakeCamera.prototype.constructor=THREE.QuakeCamera;THREE.QuakeCamera.prototype.supr=THREE.Camera.prototype;THREE.Light=function(a){THREE.Object3D.call(this);this.color=new THREE.Color(a)};THREE.Light.prototype=new THREE.Object3D;THREE.Light.prototype.constructor=THREE.Light;THREE.Light.prototype.supr=THREE.Object3D.prototype; +THREE.AmbientLight=function(a){THREE.Light.call(this,a)};THREE.AmbientLight.prototype=new THREE.Light;THREE.AmbientLight.prototype.constructor=THREE.AmbientLight;THREE.DirectionalLight=function(a,b){THREE.Light.call(this,a);this.position=new THREE.Vector3(0,1,0);this.intensity=b||1};THREE.DirectionalLight.prototype=new THREE.Light;THREE.DirectionalLight.prototype.constructor=THREE.DirectionalLight; +THREE.PointLight=function(a,b){THREE.Light.call(this,a);this.position=new THREE.Vector3;this.intensity=b||1};THREE.PointLight.prototype=new THREE.Light;THREE.PointLight.prototype.constructor=THREE.PointLight;THREE.FlatShading=0;THREE.SmoothShading=1;THREE.NormalBlending=0;THREE.AdditiveBlending=1;THREE.SubtractiveBlending=2;THREE.BillboardBlending=3;THREE.ReverseSubtractiveBlending=4;THREE.MaterialCounter={value:0}; THREE.LineBasicMaterial=function(a){this.id=THREE.MaterialCounter.value++;this.color=new THREE.Color(16777215);this.opacity=1;this.blending=THREE.NormalBlending;this.depth_test=!0;this.linewidth=1;this.linejoin=this.linecap="round";this.vertex_colors=!1;if(a){a.color!==undefined&&this.color.setHex(a.color);if(a.opacity!==undefined)this.opacity=a.opacity;if(a.blending!==undefined)this.blending=a.blending;if(a.depth_test!==undefined)this.depth_test=a.depth_test;if(a.linewidth!==undefined)this.linewidth= a.linewidth;if(a.linecap!==undefined)this.linecap=a.linecap;if(a.linejoin!==undefined)this.linejoin=a.linejoin;if(a.vertex_colors!==undefined)this.vertex_colors=a.vertex_colors}}; THREE.LineBasicMaterial.prototype={toString:function(){return"THREE.LineBasicMaterial (
id: "+this.id+"
color: "+this.color+"
opacity: "+this.opacity+"
blending: "+this.blending+"
depth_test: "+this.depth_test+"
linewidth: "+this.linewidth+"
linecap: "+this.linecap+"
linejoin: "+this.linejoin+"
vertex_colors: "+this.vertex_colors+"
)"}}; @@ -322,7 +328,7 @@ var Torus=function(a,b,c,d){this.radius=a||100;this.tube=b||40;this.segmentsR=c| var Icosahedron=function(a){function b(w,q,p){var x=Math.sqrt(w*w+q*q+p*p);return f.vertices.push(new THREE.Vertex(new THREE.Vector3(w/x,q/x,p/x)))-1}function c(w,q,p,x){x.faces.push(new THREE.Face3(w,q,p))}function d(w,q){var p=f.vertices[w].position,x=f.vertices[q].position;return b((p.x+x.x)/2,(p.y+x.y)/2,(p.z+x.z)/2)}var f=this,g=new THREE.Geometry,h;this.subdivisions=a||0;THREE.Geometry.call(this);a=(1+Math.sqrt(5))/2;b(-1,a,0);b(1,a,0);b(-1,-a,0);b(1,-a,0);b(0,-1,a);b(0,1,a);b(0,-1,-a);b(0, 1,-a);b(a,0,-1);b(a,0,1);b(-a,0,-1);b(-a,0,1);c(0,11,5,g);c(0,5,1,g);c(0,1,7,g);c(0,7,10,g);c(0,10,11,g);c(1,5,9,g);c(5,11,4,g);c(11,10,2,g);c(10,7,6,g);c(7,1,8,g);c(3,9,4,g);c(3,4,2,g);c(3,2,6,g);c(3,6,8,g);c(3,8,9,g);c(4,9,5,g);c(2,4,11,g);c(6,2,10,g);c(8,6,7,g);c(9,8,1,g);for(a=0;a - @@ -72,7 +71,9 @@ scene = new THREE.Scene(); scene.fog = new THREE.FogExp2( 0x000000, 0.0035 ); - camera = new THREE.Camera( 50, window.innerWidth / window.innerHeight, 1, 10000 ); + camera = new THREE.QuakeCamera( { fov: 50, aspect: window.innerWidth / window.innerHeight, near: 1, far: 10000, + movement_speed: 1, look_speed: 0.002, nofly: true, look_vertical: false } ); + camera.position.set( 0, 25, 0 ); light = new THREE.DirectionalLight( 0xffffff ); @@ -149,9 +150,7 @@ stats = new Stats(); stats.domElement.style.position = 'absolute'; stats.domElement.style.top = '0px'; - //container.appendChild( stats.domElement ); - - cameraControl = new CameraControlWASD( camera, 1, 0.002, true, false ); + //container.appendChild( stats.domElement ); initPostprocessing(); renderer.autoClear = false; @@ -226,9 +225,7 @@ var time = new Date().getTime() * 0.005; material_sphere1.color.setHSV( 0.0, 0.3 + 0.7 * ( 1 + Math.cos(time) ) / 2, 1 ); - material_sphere2.color.setHSV( 0.1, 0.3 + 0.7 * ( 1 + Math.sin(time) ) / 2, 1 ); - - cameraControl.update(); + material_sphere2.color.setHSV( 0.1, 0.3 + 0.7 * ( 1 + Math.sin(time) ) / 2, 1 ); if ( postprocessing.enabled ) { diff --git a/src/cameras/Camera.js b/src/cameras/Camera.js index 8a353096..9873e05e 100644 --- a/src/cameras/Camera.js +++ b/src/cameras/Camera.js @@ -3,7 +3,7 @@ * @author mikael emtinger / http://gomo.se/ */ -THREE.Camera = function( FOV, aspect, zNear, zFar, renderer, target ) { +THREE.Camera = function( FOV, aspect, zNear, zFar, target ) { THREE.Object3D.call( this ); diff --git a/src/cameras/QuakeCamera.js b/src/cameras/QuakeCamera.js new file mode 100644 index 00000000..1daaa48d --- /dev/null +++ b/src/cameras/QuakeCamera.js @@ -0,0 +1,165 @@ +/** + * @author mrdoob / http://mrdoob.com/ + * @author alteredq / http://alteredqualia.com/ + * @author paulirish / http://paulirish.com/ + */ + +function bind( scope, fn ) { + return function () { + fn.apply( scope, arguments ); + }; +} + +THREE.QuakeCamera = function ( parameters ) { + + THREE.Camera.call( this, parameters.fov, parameters.aspect, parameters.near, parameters.far, parameters.target ); + + this.movement_speed = 1.0; + this.look_speed = 0.005; + + this.nofly = false; + this.look_vertical = true; + + this.domElement = document; + + if ( parameters ) { + + if ( parameters.movement_speed !== undefined ) this.movement_speed = parameters.movement_speed; + if ( parameters.look_speed !== undefined ) this.look_speed = parameters.look_speed; + if ( parameters.nofly !== undefined ) this.nofly = parameters.nofly; + if ( parameters.look_vertical !== undefined ) this.look_vertical = parameters.look_vertical; + + if ( parameters.domElement !== undefined ) this.domElement = parameters.domElement; + + } + + this.mouseX = 0; + this.mouseY = 0; + + this.lat = 0; + this.lon = 0; + this.phy = 0; + this.theta = 0; + + this.moveForward = false; + this.moveBackward = false; + this.moveLeft = false; + this.moveRight = false; + + this.windowHalfX = window.innerWidth / 2; + this.windowHalfY = window.innerHeight / 2; + + this.onMouseDown = function ( event ) { + + event.preventDefault(); + event.stopPropagation(); + + switch ( event.button ) { + + case 0: this.moveForward = true; break; + case 2: this.moveBackward = true; break; + + } + + }; + + this.onMouseUp = function ( event ) { + + event.preventDefault(); + event.stopPropagation(); + + switch ( event.button ) { + + case 0: this.moveForward = false; break; + case 2: this.moveBackward = false; break; + + } + + }; + + this.onMouseMove = function (event) { + + this.mouseX = event.clientX - this.windowHalfX; + this.mouseY = event.clientY - this.windowHalfY; + + }; + + this.onKeyDown = function ( event ) { + + switch( event.keyCode ) { + + case 38: /*up*/ + case 87: /*W*/ this.moveForward = true; break; + + case 37: /*left*/ + case 65: /*A*/ this.moveLeft = true; break; + + case 40: /*down*/ + case 83: /*S*/ this.moveBackward = true; break; + + case 39: /*right*/ + case 68: /*D*/ this.moveRight = true; break; + + } + + }; + + this.onKeyUp = function ( event ) { + + switch( event.keyCode ) { + + case 38: /*up*/ + case 87: /*W*/ this.moveForward = false; break; + + case 37: /*left*/ + case 65: /*A*/ this.moveLeft = false; break; + + case 40: /*down*/ + case 83: /*S*/ this.moveBackward = false; break; + + case 39: /*right*/ + case 68: /*D*/ this.moveRight = false; break; + + } + + }; + + this.update = function() { + + if ( this.moveForward ) this.translateZ( - this.movement_speed, this.nofly ); + if ( this.moveBackward ) this.translateZ( this.movement_speed, this.nofly ); + if ( this.moveLeft ) this.translateX( - this.movement_speed, this.nofly ); + if ( this.moveRight ) this.translateX( this.movement_speed, this.nofly ); + + this.lon += this.mouseX * this.look_speed; + if( this.look_vertical ) this.lat -= this.mouseY * this.look_speed; + + this.lat = Math.max( - 85, Math.min( 85, this.lat ) ); + this.phi = ( 90 - this.lat ) * Math.PI / 180; + this.theta = this.lon * Math.PI / 180; + + var target_position = this.target.position, + position = this.position; + + target_position.x = position.x + 100 * Math.sin( this.phi ) * Math.cos( this.theta ); + target_position.y = position.y + 100 * Math.cos( this.phi ); + target_position.z = position.z + 100 * Math.sin( this.phi ) * Math.sin( this.theta ); + + this.supr.update.call( this ); + + }; + + + this.domElement.addEventListener( 'contextmenu', function ( event ) { event.preventDefault(); }, false ); + + this.domElement.addEventListener( 'mousemove', bind( this, this.onMouseMove ), false ); + this.domElement.addEventListener( 'mousedown', bind( this, this.onMouseDown ), false ); + this.domElement.addEventListener( 'mouseup', bind( this, this.onMouseUp ), false ); + this.domElement.addEventListener( 'keydown', bind( this, this.onKeyDown ), false ); + this.domElement.addEventListener( 'keyup', bind( this, this.onKeyUp ), false ); + +}; + +THREE.QuakeCamera.prototype = new THREE.Camera(); +THREE.QuakeCamera.prototype.constructor = THREE.QuakeCamera; +THREE.QuakeCamera.prototype.supr = THREE.Camera.prototype; diff --git a/utils/build.py b/utils/build.py index 6285e76e..349437b7 100644 --- a/utils/build.py +++ b/utils/build.py @@ -31,6 +31,7 @@ COMMON_FILES = [ 'animation/AnimationHandler.js', 'animation/Animation.js', 'cameras/Camera.js', +'cameras/QuakeCamera.js', 'lights/Light.js', 'lights/AmbientLight.js', 'lights/DirectionalLight.js',