1 /** 2 * Handle object3D 3 * 4 * @class include THREE.Object3D 5 * 6 * @param {} object 7 * @param {THREE.Object3D} rootnode 8 * @returns {tQuery.*} the tQuery object created 9 */ 10 tQuery.Object3D = function(object, root) 11 { 12 // handle the case of selector 13 if( typeof object === "string" ){ 14 object = tQuery.Object3D._select(object, root); 15 } 16 17 // call parent ctor 18 tQuery.Object3D.parent.constructor.call(this, object) 19 20 // sanity check - all items MUST be THREE.Object3D 21 this._lists.forEach(function(item){ console.assert(item instanceof THREE.Object3D); }); 22 }; 23 24 /** 25 * inherit from tQuery.Node 26 */ 27 tQuery.inherit(tQuery.Object3D, tQuery.Node); 28 29 /** 30 * Make it pluginable 31 */ 32 tQuery.pluginsInstanceOn(tQuery.Object3D); 33 34 /** 35 * define all acceptable attributes for this class 36 */ 37 tQuery.mixinAttributes(tQuery.Object3D, { 38 eulerOrder : tQuery.convert.toString, 39 40 doubleSided : tQuery.convert.toBoolean, 41 flipSided : tQuery.convert.toBoolean, 42 43 rotationAutoUpdate : tQuery.convert.toBoolean, 44 matrixAutoUpdate : tQuery.convert.toBoolean, 45 matrixWorldNeedsUpdate : tQuery.convert.toBoolean, 46 useQuaternion : tQuery.convert.toBoolean, 47 48 visible : tQuery.convert.toBoolean, 49 50 receiveShadow : tQuery.convert.toBoolean, 51 castShadow : tQuery.convert.toBoolean 52 }); 53 54 /** 55 * Traverse the hierarchy of Object3D. 56 * 57 * @returns {tQuery.Object3D} return the tQuery.Object3D itself 58 */ 59 tQuery.Object3D.prototype.traverseHierarchy = function(callback){ 60 this.each(function(object3d){ 61 THREE.SceneUtils.traverseHierarchy(object3d, function(object3d){ 62 callback(object3d); 63 }); 64 }); 65 return this; // for chained API 66 }; 67 68 69 ////////////////////////////////////////////////////////////////////////////////// 70 // geometry and material // 71 ////////////////////////////////////////////////////////////////////////////////// 72 73 /** 74 * get geometry. 75 * 76 * TODO this should be move in tQuery.Mesh 77 * 78 * @returns {tQuery.Geometry} return the geometries from the tQuery.Object3D 79 */ 80 tQuery.Object3D.prototype.geometry = function(value){ 81 var geometries = []; 82 this.each(function(object3d){ 83 geometries.push(object3d.geometry) 84 }); 85 return new tQuery.Geometry(geometries).back(this); 86 }; 87 88 /** 89 * get material. 90 * 91 * TODO this should be move in tQuery.Mesh 92 * 93 * @returns {tQuery.Material} return the materials from the tQuery.Object3D 94 */ 95 tQuery.Object3D.prototype.material = function(){ 96 var materials = []; 97 this.each(function(object3d){ 98 materials.push(object3d.material) 99 }); 100 return new tQuery.Material(materials); 101 }; 102 103 104 /** 105 * Clone a Object3D 106 */ 107 tQuery.Object3D.prototype.clone = function(){ 108 var clones = []; 109 this._lists.forEach(function(object3d){ 110 var clone = THREE.SceneUtils.cloneObject(object3d) 111 clones.push(clone); 112 }) 113 return tQuery(clones) 114 } 115 116 ////////////////////////////////////////////////////////////////////////////////// 117 // addTo/removeFrom tQuery.World/tQuery.Object3d // 118 ////////////////////////////////////////////////////////////////////////////////// 119 120 /** 121 * add all matched elements to a world 122 * 123 * @param {tQuery.World or tQuery.Object3D} target object to which add it 124 * @returns {tQuery.Object3D} chained API 125 */ 126 tQuery.Object3D.prototype.addTo = function(target) 127 { 128 console.assert( target instanceof tQuery.World || target instanceof tQuery.Object3D || target instanceof THREE.Object3D ) 129 this.each(function(object3d){ 130 target.add(object3d) 131 }.bind(this)); 132 return this; 133 } 134 135 /** 136 * remove all matched elements from a world 137 * 138 * @param {tQuery.World or tQuery.Object3D} target object to which add it 139 * @returns {tQuery.Object3D} chained API 140 */ 141 tQuery.Object3D.prototype.removeFrom = function(target) 142 { 143 console.assert( target instanceof tQuery.World || target instanceof tQuery.Object3D ) 144 this.each(function(tObject3d){ 145 target.remove(tObject3d) 146 }.bind(this)); 147 return this; 148 } 149 150 /** 151 * remove an element from the parent to which it is attached 152 * 153 * @returns {tQuery.Object3D} chained API 154 */ 155 tQuery.Object3D.prototype.detach = function() 156 { 157 this.each(function(object3D){ 158 if( !object3D.parent ) return; 159 object3D.parent.remove(object3D) 160 }.bind(this)); 161 return this; 162 } 163 164 ////////////////////////////////////////////////////////////////////////////////// 165 // addTo/removeFrom tQuery.World/tQuery.Object3d // 166 ////////////////////////////////////////////////////////////////////////////////// 167 168 /** 169 * add all matched elements to a world 170 * 171 * @param {tQuery.Object3D} target object to which add it 172 * @returns {tQuery.Object3D} chained API 173 */ 174 tQuery.Object3D.prototype.add = function(object3D) 175 { 176 if( object3D instanceof tQuery.Object3D ){ 177 this.each(function(object1){ 178 object3D.each(function(object2){ 179 object1.add(object2); 180 }) 181 }.bind(this)); 182 }else if( object3D instanceof THREE.Object3D ){ 183 this.each(function(object1){ 184 object1.add(object3D); 185 }); 186 }else console.assert(false, "invalid parameter"); 187 return this; 188 } 189 190 /** 191 * remove all matched elements from a world 192 * 193 * @param {tQuery.Object3D} object3d the object to add in this object 194 * @returns {tQuery.Object3D} chained API 195 */ 196 tQuery.Object3D.prototype.remove = function(object3D) 197 { 198 if( object3D instanceof tQuery.Object3D ){ 199 this.each(function(object1){ 200 object3D.each(function(object2){ 201 object1.remove(object2); 202 }) 203 }.bind(this)); 204 }else if( object3D instanceof THREE.Object3D ){ 205 this.each(function(object1){ 206 object1.remove(object3D); 207 }); 208 }else console.assert(false, "invalid parameter"); 209 return this; 210 } 211 212 213 ////////////////////////////////////////////////////////////////////////////////// 214 // Handle dom attribute // 215 ////////////////////////////////////////////////////////////////////////////////// 216 217 /** 218 * Getter/Setter for the id of the matched elements 219 */ 220 tQuery.Object3D.prototype.id = function(value) 221 { 222 // sanity check 223 console.assert(this.length <= 1, "tQuery.Object3D.id used on multi-elements" ); 224 if( value !== undefined ){ 225 if( this.length > 0 ){ 226 var object3d = this.get(0); 227 object3d._tqId = value; 228 } 229 return this; 230 }else{ 231 if( this.length > 0 ){ 232 var object3d = this.get(0); 233 return object3d._tqId; 234 } 235 return undefined; 236 } 237 }; 238 239 /** 240 * add a class to all matched elements 241 * 242 * @param {string} className the name of the class to add 243 * @returns {tQuery.Object3D} chained API 244 */ 245 tQuery.Object3D.prototype.addClass = function(className){ 246 this.each(function(tObject3d){ 247 // init ._tqClasses if needed 248 tObject3d._tqClasses = tObject3d._tqClasses || ''; 249 250 if( tQuery.Object3D._hasClassOne(tObject3d, className) ) return; 251 252 tObject3d._tqClasses += ' '+className; 253 }.bind(this)); 254 return this; 255 }; 256 257 /** 258 * remove a class to all matched elements 259 * 260 * @param {string} className the name of the class to remove 261 * @returns {tQuery.Object3D} chained API 262 */ 263 tQuery.Object3D.prototype.removeClass = function(className){ 264 this.each(function(tObject3d){ 265 tQuery.Object3D._removeClassOne(tObject3d, className); 266 }.bind(this)); 267 return this; // for chained api 268 }; 269 270 /** 271 * return true if any of the matched elements has this class 272 * 273 * @param {string} className the name of the class 274 * @returns {tQuery.Object3D} true if any of the matched elements has this class, false overwise 275 */ 276 tQuery.Object3D.prototype.hasClass = function(className){ 277 var completed = this.each(function(object3d){ 278 // init ._tqClasses if needed 279 object3d._tqClasses = object3d._tqClasses || ''; 280 281 var hasClass = tQuery.Object3D._hasClassOne(object3d, className); 282 return hasClass ? false : true; 283 }.bind(this)); 284 return completed ? false : true; 285 }; 286 287 tQuery.Object3D._hasClassOne = function(object3d, className){ 288 if( object3d._tqClasses === undefined ) return false; 289 var classes = object3d._tqClasses; 290 var re = new RegExp('(^| |\t)+('+className+')($| |\t)+'); 291 return classes.match(re) ? true : false; 292 }; 293 294 tQuery.Object3D._removeClassOne = function(object3d, className){ 295 if( object3d._tqClasses === undefined ) return; 296 var re = new RegExp('(^| |\t)('+className+')($| |\t)'); 297 object3d._tqClasses = object3d._tqClasses.replace(re, ' '); 298 }; 299 300 ////////////////////////////////////////////////////////////////////////////////// 301 // handling selection // 302 ////////////////////////////////////////////////////////////////////////////////// 303 304 tQuery.Object3D._select = function(selector, root){ 305 // handle parameter 306 root = root || tQuery.world.tScene(); 307 if( root instanceof tQuery.Object3D ) root = root.get(0) 308 var selectItems = selector.split(' ').filter(function(v){ return v.length > 0;}) 309 310 // sanity check 311 console.assert(root instanceof THREE.Object3D); 312 313 var lists = []; 314 root.children.forEach(function(child){ 315 var nodes = this._crawls(child, selectItems); 316 // FIXME reallocate the array without need 317 lists = lists.concat(nodes); 318 }.bind(this)); 319 return lists; 320 } 321 322 tQuery.Object3D._crawls = function(root, selectItems) 323 { 324 var result = []; 325 //console.log("crawl", root, selectItems) 326 console.assert( selectItems.length >= 1 ); 327 var match = this._selectItemMatch(root, selectItems[0]); 328 //console.log(" match", match) 329 var nextSelect = match ? selectItems.slice(1) : selectItems; 330 //console.log(" nextSelect", nextSelect) 331 332 if( nextSelect.length === 0 ) return [root]; 333 334 root.children.forEach(function(child){ 335 var nodes = this._crawls(child, nextSelect); 336 // FIXME reallocate the array without need 337 result = result.concat(nodes); 338 }.bind(this)); 339 340 return result; 341 } 342 343 // all the geometries keywords 344 tQuery.Object3D._selectableGeometries = Object.keys(THREE).filter(function(value){ 345 return value.match(/.+Geometry$/);}).map(function(value){ return value.replace(/Geometry$/,'').toLowerCase(); 346 }); 347 348 // all the light keywords 349 tQuery.Object3D._selectableLights = Object.keys(THREE).filter(function(value){ 350 return value.match(/.+Light$/);}).map(function(value){ return value.replace(/Light$/,'').toLowerCase(); 351 }); 352 353 tQuery.Object3D._selectableClasses = ['mesh', 'light']; 354 355 tQuery.Object3D._selectItemMatch = function(object3d, selectItem) 356 { 357 // sanity check 358 console.assert( object3d instanceof THREE.Object3D ); 359 console.assert( typeof selectItem === 'string' ); 360 361 // parse selectItem into subItems 362 var subItems = selectItem.match(new RegExp("([^.#]+|\.[^.#]+|\#[^.#]+)", "g"));; 363 364 // go thru each subItem 365 var completed = tQuery.each(subItems, function(subItem){ 366 var meta = subItem.charAt(0); 367 var suffix = subItem.slice(1); 368 //console.log("meta", meta, subItem, suffix, object3d) 369 if( meta === "." ){ 370 var hasClass = tQuery.Object3D._hasClassOne(object3d, suffix); 371 return hasClass ? true : false; 372 }else if( meta === "#" ){ 373 return object3d._tqId === suffix ? true : false; 374 }else if( subItem === "*" ){ 375 return true; 376 }else if( this._selectableGeometries.indexOf(subItem) !== -1 ){ // Handle geometries 377 var geometry = object3d.geometry; 378 var className = subItem.charAt(0).toUpperCase() + subItem.slice(1) + "Geometry"; 379 return geometry instanceof THREE[className]; 380 }else if( this._selectableLights.indexOf(subItem) !== -1 ){ // Handle light 381 var className = subItem.charAt(0).toUpperCase() + subItem.slice(1) + "Light"; 382 return object3d instanceof THREE[className]; 383 }else if( this._selectableClasses.indexOf(subItem) !== -1 ){ // Handle light 384 var className = subItem.charAt(0).toUpperCase() + subItem.slice(1); 385 return object3d instanceof THREE[className]; 386 } 387 // this point should never be reached 388 console.assert(false, "invalid selector: "+subItem); 389 return true; 390 }.bind(this)); 391 392 return completed ? true : false; 393 } 394