1 /** 2 * @fileoverview 3 * 4 * TODO add _ prefix to private properties 5 * TODO much cleanup needed 6 * TODO no change of property from outside. use getter/setter 7 * TODO only chained API 8 */ 9 10 /** 11 * widely inspired from MD2Character.js from alteredq / http://alteredqualia.com/ 12 * 13 * @name tQuery.MD2Character 14 * @class 15 */ 16 tQuery.registerStatic('MD2Character', function(){ 17 this._scale = 1; 18 this.animationFPS = 6; 19 20 this._root = new THREE.Object3D(); 21 this._meshBody = null; 22 this._meshWeapon = null; 23 24 this._skinsBody = []; 25 this._skinsWeapon = []; 26 27 this._weapons = []; 28 29 this._curAnimation = null; 30 this._nLoadInProgress = 0; 31 }); 32 //tQuery.MD2Character = function(){ 33 34 // make it eventable 35 tQuery.MicroeventMixin(tQuery.MD2Character.prototype); 36 37 // Make the class pluginable 38 tQuery.pluginsStaticOn(tQuery.MD2Character); 39 40 /** 41 * Destructor 42 */ 43 tQuery.MD2Character.prototype.destroy = function() 44 { 45 console.log("tQuery.MD2Character destoy") 46 } 47 48 ////////////////////////////////////////////////////////////////////////////////// 49 // // 50 ////////////////////////////////////////////////////////////////////////////////// 51 52 /** 53 * Update the animation 54 * 55 * @param {Number} deltaSeconds nb seconds since the last update 56 */ 57 tQuery.MD2Character.prototype.update = function( deltaSeconds ) 58 { 59 if ( this._meshBody ) { 60 var direction = this._meshBody.direction; 61 var timeBefore = this._meshBody.time; 62 // update the animation 63 this._meshBody.updateAnimation( 1000 * deltaSeconds ); 64 // ugly kludge to get an event 'animationCompleted' 65 var timeAfter = this._meshBody.time; 66 if( (direction === 1 && timeBefore > timeAfter) || (direction === -1 && timeAfter < timeBefore) ){ 67 this.trigger("animationCompleted", this, this._curAnimation) 68 //console.log("endofanim", this._curAnimation) 69 } 70 } 71 if ( this._meshWeapon ) { 72 this._meshWeapon.updateAnimation( 1000 * deltaSeconds ); 73 } 74 return this; // for chained API 75 }; 76 77 /** 78 * @returns {THREE.Object3D} the object3D containing the object 79 */ 80 tQuery.MD2Character.prototype.container = function(){ 81 return this._root; 82 } 83 84 /** 85 * @return {Boolean} true if the character is loaded, false otherwise 86 */ 87 tQuery.MD2Character.prototype.isLoaded = function(){ 88 return this._nLoadInProgress === 0 ? true : false; 89 } 90 91 /** 92 * Getter/setter for the scale of the object 93 */ 94 tQuery.MD2Character.prototype.scale = function(value){ 95 if( value === undefined ) return this._scale; 96 this._scale = value; 97 return this; 98 } 99 100 101 ////////////////////////////////////////////////////////////////////////////////// 102 // Setter // 103 ////////////////////////////////////////////////////////////////////////////////// 104 105 /** 106 * @param {Boolean} enable true to enable wireframe, false otherwise 107 */ 108 tQuery.MD2Character.prototype.setWireframe = function ( enable ) 109 { 110 // TODO remove the added property on THREE.Mesh 111 if( enable ){ 112 if ( this._meshBody ) this._meshBody.material = this._meshBody.materialWireframe; 113 if ( this._meshWeapon ) this._meshWeapon.material= this._meshWeapon.materialWireframe; 114 } else { 115 if ( this._meshBody ) this._meshBody.material = this._meshBody.materialTexture; 116 if ( this._meshWeapon ) this._meshWeapon.material= this._meshWeapon.materialTexture; 117 } 118 return this; // for chained API 119 }; 120 121 /** 122 * Set the weapons 123 * 124 * @param {Number} index the index of the animations 125 */ 126 tQuery.MD2Character.prototype.setWeapon = function ( index ) 127 { 128 // make all weapons invisible 129 for ( var i = 0; i < this._weapons.length; i ++ ){ 130 this._weapons[ i ].visible = false; 131 } 132 // set the active weapon 133 var activeWeapon = this._weapons[ index ]; 134 135 if( activeWeapon ){ 136 activeWeapon.visible = true; 137 this._meshWeapon = activeWeapon; 138 139 activeWeapon.playAnimation( this._curAnimation, this.animationFPS ); 140 141 this._meshWeapon.baseDuration = this._meshWeapon.duration; 142 143 this._meshWeapon.time = this._meshBody.time; 144 this._meshWeapon.duration = this._meshBody.duration; 145 } 146 return this; // for chained API 147 }; 148 149 /** 150 * set the animation. TODO a setter/getter 151 * 152 * @param {string} animationName the animation name to set 153 */ 154 tQuery.MD2Character.prototype.animation = function( animationName ) 155 { 156 // for getter 157 if( animationName === undefined ){ 158 return this._curAnimation; 159 } 160 // for setter when the animation is already the same, do nothign 161 if( animationName === this._curAnimation ){ 162 return this; // for chained API 163 } 164 // sanity check 165 console.assert( Object.keys(this._meshBody.geometry.animations).indexOf(animationName) !== -1 ); 166 // setter on this._meshBody 167 if ( this._meshBody ) { 168 this._meshBody.playAnimation( animationName, this.animationFPS ); 169 this._meshBody.baseDuration = this._meshBody.duration; 170 } 171 // setter on this._meshWeapon 172 if ( this._meshWeapon ) { 173 this._meshWeapon.playAnimation( animationName, this.animationFPS ); 174 this._meshWeapon.baseDuration = this._meshWeapon.duration; 175 this._meshWeapon.time = this._meshBody.time; 176 } 177 // set the animation itself 178 this._curAnimation = animationName; 179 return this; // for chained API 180 }; 181 182 /** 183 * @param {number} rate the rate to play the object 184 */ 185 tQuery.MD2Character.prototype.setPlaybackRate = function( rate ) 186 { 187 if ( this._meshBody ){ 188 this._meshBody.duration = this._meshBody.baseDuration / rate; 189 } 190 if ( this._meshWeapon ){ 191 this._meshWeapon.duration = this._meshWeapon.baseDuration / rate; 192 } 193 return this; // for chained API 194 }; 195 196 /** 197 * @param {Number} index set the index of the skin 198 */ 199 tQuery.MD2Character.prototype.setSkin = function( index ) 200 { 201 if ( this._meshBody && this._meshBody.material.wireframe === false ) { 202 console.assert( index < this._skinsBody.length ); 203 this._meshBody.material.map = this._skinsBody[ index ]; 204 } 205 return this; // for chained API 206 }; 207 208 ////////////////////////////////////////////////////////////////////////////////// 209 // Loader // 210 ////////////////////////////////////////////////////////////////////////////////// 211 212 /** 213 * Load the part of your characters 214 */ 215 tQuery.MD2Character.prototype.load = function ( config ) 216 { 217 var _this = this; 218 this._nLoadInProgress = config.weapons.length * 2 + config.skins.length + 1; 219 220 var weaponsTextures = [] 221 for ( var i = 0; i < config.weapons.length; i ++ ){ 222 weaponsTextures[ i ] = config.weapons[ i ][ 1 ]; 223 } 224 225 // SKINS 226 this._skinsBody = this._loadTextures( config.baseUrl + "skins/", config.skins ); 227 this._skinsWeapon = this._loadTextures( config.baseUrl + "skins/", weaponsTextures ); 228 229 // BODY 230 var loader = new THREE.JSONLoader(); 231 232 loader.load( config.baseUrl + config.body, function( geometry ) { 233 geometry.computeBoundingBox(); 234 _this._root.position.y = - _this._scale * geometry.boundingBox.min.y; 235 236 var mesh = createPart( geometry, _this._skinsBody[ 0 ] ); 237 mesh.scale.set( _this._scale, _this._scale, _this._scale ); 238 239 _this._root.add( mesh ); 240 241 _this._meshBody = mesh; 242 _this._curAnimation = geometry.firstAnimation; 243 244 _this._checkLoadingComplete(); 245 } ); 246 247 // WEAPONS 248 var generateCallback = function( index, name ){ 249 return function( geometry ) { 250 var mesh = createPart( geometry, _this._skinsWeapon[ index ] ); 251 mesh.scale.set( _this._scale, _this._scale, _this._scale ); 252 mesh.visible = false; 253 254 mesh.name = name; 255 256 _this._root.add( mesh ); 257 258 _this._weapons[ index ] = mesh; 259 _this._meshWeapon = mesh; 260 261 _this._checkLoadingComplete(); 262 }.bind(this); 263 }.bind(this); 264 265 for ( var i = 0; i < config.weapons.length; i ++ ) { 266 var url = config.baseUrl + config.weapons[ i ][ 0 ]; 267 var callback = generateCallback( i, config.weapons[ i ][ 0 ] ); 268 loader.load( url, callback ); 269 } 270 271 function createPart( geometry, skinMap ) { 272 geometry.computeMorphNormals(); 273 274 var whiteMap = THREE.ImageUtils.generateDataTexture( 1, 1, new THREE.Color( 0xffffff ) ); 275 var materialWireframe = new THREE.MeshPhongMaterial({ 276 color : 0xffaa00, 277 specular : 0x111111, 278 shininess : 50, 279 wireframe : true, 280 shading : THREE.SmoothShading, 281 map : whiteMap, 282 morphTargets : true, 283 morphNormals : true, 284 perPixel : true, 285 metal : false 286 }); 287 288 var materialTexture = new THREE.MeshPhongMaterial({ 289 color : 0xffffff, 290 specular : 0x111111, 291 shininess : 50, 292 wireframe : false, 293 shading : THREE.SmoothShading, 294 map : skinMap, 295 morphTargets : true, 296 morphNormals : true, 297 perPixel : true, 298 metal : false 299 }); 300 materialTexture.wrapAround = true; 301 302 // 303 304 var mesh = new THREE.MorphAnimMesh( geometry, materialTexture ); 305 mesh.rotation.y = -Math.PI/2; 306 307 mesh.castShadow = true; 308 mesh.receiveShadow = true; 309 310 // 311 312 mesh.materialTexture = materialTexture; 313 mesh.materialWireframe = materialWireframe; 314 315 // 316 317 mesh.parseAnimations(); 318 319 mesh.playAnimation( geometry.firstAnimation, _this.animationFPS ); 320 mesh.baseDuration = mesh.duration; 321 322 return mesh; 323 }; 324 return this; // for chained API 325 }; 326 327 tQuery.MD2Character.prototype._checkLoadingComplete = function() 328 { 329 this._nLoadInProgress--; 330 if( this._nLoadInProgress === 0 ){ 331 this.trigger('loaded'); 332 } 333 } 334 335 /** 336 * Load a texture and return it 337 */ 338 tQuery.MD2Character.prototype._loadTextures = function( baseUrl, textureUrls ) 339 { 340 var mapping = new THREE.UVMapping(); 341 var textures = []; 342 var callback = function(){ 343 this._checkLoadingComplete() 344 }.bind(this); 345 // load all textureUrls 346 for( var i = 0; i < textureUrls.length; i ++ ){ 347 var url = baseUrl + textureUrls[ i ]; 348 var texture = THREE.ImageUtils.loadTexture( url, mapping, callback); 349 textures[ i ] = texture; 350 textures[ i ].name = textureUrls[ i ]; 351 } 352 // return them 353 return textures; 354 }; 355