1 /* 2 * DAELoader.js 2017 3 * 4 * Sweet Home 3D, Copyright (c) 2024 Space Mushrooms <info@sweethome3d.com> 5 * 6 * This program is free software; you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License as published by 8 * the Free Software Foundation; either version 2 of the License, or 9 * (at your option) any later version. 10 * 11 * This program is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU General Public License for more details. 15 * 16 * You should have received a copy of the GNU General Public License 17 * along with this program; if not, write to the Free Software 18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 19 */ 20 21 // Requires core.js 22 // gl-matrix-min.js 23 // jszip.min.js 24 // scene3d.js 25 // Triangulator.js 26 // URLContent.js 27 // ModelLoader.js 28 // jsXmlSaxParser.min.js 29 30 /** 31 * Creates a loader for DAE Collada 1.4.1 format as specified by 32 * <a href="http://www.khronos.org/files/collada_spec_1_4.pdf">http://www.khronos.org/files/collada_spec_1_4.pdf</a>. 33 * All texture coordinates are considered to belong to the same set (for example UVSET0).<br> 34 * @constructor 35 * @extends ModelLoader 36 * @author Emmanuel Puybaret 37 */ 38 function DAELoader() { 39 ModelLoader.call(this, "dae"); 40 } 41 DAELoader.prototype = Object.create(ModelLoader.prototype); 42 DAELoader.prototype.constructor = DAELoader; 43 44 /** 45 * Parses the given DAE content and calls onmodelloaded asynchronously or 46 * returns the scene it describes if onmodelloaded is null. 47 * @protected 48 */ 49 DAELoader.prototype.parseEntryScene = function(daeContent, daeEntryName, zip, modelContext, onmodelloaded, onprogression) { 50 var sceneRoot = new Group3D(); 51 var handler = new DAEHandler(sceneRoot, daeEntryName); 52 var saxParser = new SAXParser(handler, handler, handler, handler, handler); 53 onprogression(ModelLoader.PARSING_MODEL, daeEntryName, 0); 54 try { 55 saxParser.parseString(daeContent); 56 } catch (ex) { 57 sceneRoot.removeAllChildren(); 58 } 59 if (onmodelloaded === null) { 60 onprogression(ModelLoader.PARSING_MODEL, daeEntryName, 1); 61 return sceneRoot; 62 } else { 63 setTimeout( 64 function() { 65 onprogression(ModelLoader.PARSING_MODEL, daeEntryName, 1); 66 onmodelloaded(sceneRoot); 67 }, 0); 68 } 69 } 70 71 /** 72 * SAX handler for DAE Collada stream. 73 * @constructor 74 * @private 75 */ 76 function DAEHandler(sceneRoot, daeEntryName) { 77 DefaultHandler.call(this); 78 this.sceneRoot = sceneRoot; 79 this.daeEntryName = daeEntryName; 80 81 this.parentGroups = []; // Group3D 82 this.parentElements = []; // string 83 this.buffer = ""; 84 this.postProcessingBinders = []; // function 85 86 this.textures = {}; // string 87 this.effectAppearances = {}; // Appearance3D 88 this.materialEffects = {}; // string 89 this.materialNames = {}; // string 90 this.surface2DIds = {}; // string 91 this.sampler2DIds = {}; // string 92 this.geometries = {}; // IndexedGeometryArray3D [] 93 this.sources = {}; // number [] 94 this.positions = {}; // number [] 95 this.normals = {}; // number [] 96 this.textureCoordinates = {}; // number [] 97 this.floatArrays = {}; // number [] 98 this.sourceAccessorStrides = []; 99 this.appearanceGeometries = {}; // IndexedGeometryArray3D 100 this.facesAndLinesPrimitives = []; // number [][] 101 this.polygonsPrimitives = []; // number [][] 102 this.polygonsHoles = []; // number [][][] 103 this.nodes = {}; // TransformGroup3D 104 this.controllersSkinMeshes = {}; // string 105 this.instantiatedNodes = {}; // SharedGroup3D 106 this.visualScenes = {}; // TransformGroup3D 107 this.visualScene = null; 108 this.floats = null; // number [] 109 this.geometryVertices = []; // number [] 110 this.geometryNormals = []; // number [] 111 this.geometryTextureCoordinates = []; // number [] 112 this.vcount = []; // number [] 113 this.transparentColor = []; // number [] 114 this.transparency = null; 115 116 this.inRootAsset = false; 117 this.reverseTransparency = false; 118 this.imageId = null; 119 this.materialId = null; 120 this.effectId = null; 121 this.newParamSid = null; 122 this.inSurface2D = false; 123 this.inPhongBlinnOrLambert = false; 124 this.techniqueProfile = null; 125 this.inConstant = false; 126 this.geometryId = null; 127 this.meshSourceId = null; 128 this.verticesId = null; 129 this.floatArrayId = null; 130 this.controllerId = null; 131 this.geometryAppearance = null; 132 this.geometryVertexOffset = 0; 133 this.geometryNormalOffset = 0; 134 this.geometryTextureCoordinatesOffset = 0; 135 this.axis = null; 136 this.meterScale = 1.; 137 this.floatValue = 0.; 138 this.opaque = null; 139 this.inputCount = 0; 140 } 141 DAEHandler.prototype = Object.create(DefaultHandler.prototype); 142 DAEHandler.prototype.constructor = DAEHandler; 143 144 DAEHandler.prototype.startElement = function(uri, localName, name, attributes) { 145 this.buffer = ""; 146 var parent = this.parentElements.length === 0 147 ? null 148 : this.parentElements [this.parentElements.length - 1]; 149 150 if (parent === null && !("COLLADA" == name)) { 151 throw new SAXException("Expected COLLADA element"); 152 } else if ("COLLADA" == name) { 153 var version = attributes.getValue("version"); 154 if (version.indexOf("1.4") !== 0) { 155 throw new SAXException("Version " + version + " not supported"); 156 } 157 } else if ("COLLADA" == parent && "asset" == name) { 158 this.inRootAsset = true; 159 } else if ("asset" == parent && "unit" == name) { 160 var value = attributes.getValue("meter"); 161 if (value !== null) { 162 this.meterScale = parseFloat(value); 163 } 164 } else if ("image" == name) { 165 this.imageId = attributes.getValue("id"); 166 } else if ("material" == name) { 167 this.materialId = attributes.getValue("id"); 168 this.materialNames [this.materialId] = attributes.getValue("name"); 169 } else if ("material" == parent && "instance_effect" == name) { 170 var effectInstanceUrl = attributes.getValue("url"); 171 if (effectInstanceUrl.indexOf("#") === 0) { 172 var effectInstanceAnchor = effectInstanceUrl.substring(1); 173 this.materialEffects [this.materialId] = effectInstanceAnchor; 174 } 175 } else if ("effect" == name) { 176 this.effectId = attributes.getValue("id"); 177 this.effectAppearances [this.effectId] = new Appearance3D(); 178 } else if (this.effectId !== null) { 179 if ("profile_COMMON" == parent && "newparam" == name) { 180 this.newParamSid = attributes.getValue("sid"); 181 } else if ("newparam" == parent && "surface" == name 182 && "2D" == attributes.getValue("type")) { 183 this.inSurface2D = true; 184 } else if ("extra" == parent && "technique" == name) { 185 this.techniqueProfile = attributes.getValue("profile"); 186 } else if ("phong" == name || "blinn" == name) { 187 this.inPhongBlinnOrLambert = true; 188 } else if ("lambert" == name) { 189 this.inPhongBlinnOrLambert = true; 190 this.effectAppearances [this.effectId].setSpecularColor(vec3.fromValues(0, 0, 0)); 191 this.effectAppearances [this.effectId].setShininess(1); 192 } else if ("constant" == name) { 193 this.inConstant = true; 194 } else if (this.inConstant || this.inPhongBlinnOrLambert 195 && ("transparent" == name)) { 196 this.opaque = attributes.getValue("opaque"); 197 if (this.opaque === null) { 198 this.opaque = "A_ONE"; 199 } 200 } else if ("texture" == name && "diffuse" == parent) { 201 var textureId = this.surface2DIds [this.sampler2DIds [attributes.getValue("texture")]]; 202 var appearance = this.effectAppearances [this.effectId]; 203 var handler = this; 204 this.postProcessingBinders.push(function() { 205 // Resolve texture at the end of the document 206 appearance.imageEntryName = handler.textures [textureId]; 207 }); 208 } 209 } else if ("geometry" == name) { 210 this.geometryId = attributes.getValue("id"); 211 this.geometries [this.geometryId] = []; 212 } else if (this.geometryId !== null) { 213 if ("mesh" == parent && "source" == name) { 214 this.meshSourceId = attributes.getValue("id"); 215 } else if ("mesh" == parent && "vertices" == name) { 216 this.verticesId = attributes.getValue("id"); 217 } else if (this.meshSourceId !== null) { 218 if ("float_array" == name) { 219 this.floatArrayId = attributes.getValue("id"); 220 } else if ("technique_common" == parent && "accessor" == name) { 221 var floatArrayAnchor = attributes.getValue("source").substring(1); 222 var stride = attributes.getValue("stride"); 223 stride = stride !== null ? parseInt(stride) : 1; 224 this.sourceAccessorStrides.push( 225 {floatArray : this.floatArrays [floatArrayAnchor], stride : stride}); 226 } 227 } else if (this.verticesId !== null && "input" == name) { 228 var sourceAnchor = attributes.getValue("source").substring(1); 229 var source = this.sources [sourceAnchor]; 230 if (source !== undefined) { 231 if ("POSITION" == attributes.getValue("semantic")) { 232 this.positions [this.verticesId] = new Array(source.length / 3); 233 for (var i = 0, k = 0; k < source.length; i++, k += 3) { 234 this.positions [this.verticesId][i] = vec3.fromValues(source [k], source [k + 1], source [k + 2]); 235 } 236 } else if ("NORMAL" == attributes.getValue("semantic")) { 237 this.normals [this.verticesId] = new Array(source.length / 3); 238 for (var i = 0, k = 0; k < source.length; i++, k += 3) { 239 this.normals [this.verticesId][i] = vec3.fromValues(source [k], source [k + 1], source [k + 2]); 240 } 241 } else if ("TEXCOORD" == attributes.getValue("semantic")) { 242 this.textureCoordinates [this.verticesId] = new Array(source.length / 2); 243 for (var i = 0, k = 0; k < source.length; i++, k += 2) { 244 this.textureCoordinates [this.verticesId][i] = vec2.fromValues(source [k], source [k + 1]); 245 } 246 } 247 } 248 } else if (this.verticesId === null && "input" == name) { 249 var sourceAnchor = attributes.getValue("source").substring(1); 250 var offset = parseInt(attributes.getValue("offset")); 251 if (this.inputCount < offset + 1) { 252 this.inputCount = offset + 1; 253 } 254 if ("VERTEX" == attributes.getValue("semantic")) { 255 this.geometryVertices = this.positions [sourceAnchor]; 256 if (this.geometryVertices === undefined) { 257 this.geometryVertices = null; 258 } 259 this.geometryVertexOffset = offset; 260 if (this.geometryNormals === null) { 261 this.geometryNormals = this.normals [sourceAnchor]; 262 this.geometryNormalOffset = offset; 263 } 264 if (this.geometryTextureCoordinates === null) { 265 this.geometryTextureCoordinates = this.textureCoordinates [sourceAnchor]; 266 this.geometryTextureCoordinatesOffset = offset; 267 } 268 } else if ("NORMAL" == attributes.getValue("semantic")) { 269 var source = this.sources [sourceAnchor]; 270 if (source !== undefined) { 271 this.geometryNormals = new Array(source.length / 3); 272 for (var i = 0, k = 0; k < source.length; i++, k += 3) { 273 this.geometryNormals [i] = vec3.fromValues(source [k], source [k + 1], source [k + 2]); 274 } 275 this.geometryNormalOffset = offset; 276 } 277 } else if ("TEXCOORD" == attributes.getValue("semantic")) { 278 var source = this.sources [sourceAnchor]; 279 if (source !== undefined) { 280 this.geometryTextureCoordinates = new Array(source.length / 2); 281 for (var i = 0, k = 0; k < source.length; i++, k += 2) { 282 this.geometryTextureCoordinates [i] = vec2.fromValues(source [k], source [k + 1]); 283 } 284 this.geometryTextureCoordinatesOffset = offset; 285 } 286 } 287 } else if ("triangles" == name 288 || "trifans" == name 289 || "tristrips" == name 290 || "polylist" == name 291 || "polygons" == name 292 || "lines" == name 293 || "linestrips" == name) { 294 this.geometryAppearance = attributes.getValue("material"); 295 this.inputCount = 0; 296 this.facesAndLinesPrimitives = []; 297 this.polygonsPrimitives = []; 298 this.polygonsHoles = []; 299 this.vcount = null; 300 } 301 } else if ("controller" == name) { 302 this.controllerId = attributes.getValue("id"); 303 } else if ("skin" == name) { 304 var skinSource = attributes.getValue("source"); 305 if (skinSource.indexOf("#") === 0) { 306 var skinSourceAnchor = skinSource.substring(1); 307 this.controllersSkinMeshes [this.controllerId] = skinSourceAnchor; 308 } 309 } else if ("visual_scene" == name) { 310 var visualSceneGroup = new TransformGroup3D(); 311 this.parentGroups.push(visualSceneGroup); 312 this.visualScenes [attributes.getValue("id")] = visualSceneGroup; 313 } else if ("node" == name) { 314 var nodeGroup = new TransformGroup3D(); 315 if (this.parentGroups.length > 0) { 316 // Add node to parent node only for children nodes 317 this.parentGroups [this.parentGroups.length - 1].addChild(nodeGroup); 318 } 319 this.parentGroups.push(nodeGroup); 320 this.nodes [attributes.getValue("id")] = nodeGroup; 321 var nodeName = attributes.getValue("name"); 322 if (nodeName !== null) { 323 nodeGroup.setName(nodeName); 324 } 325 } else if ("node" == parent 326 && ("instance_geometry" == name 327 || "instance_controller" == name)) { 328 var instanceUrl = attributes.getValue("url"); 329 if (instanceUrl.indexOf("#") === 0) { 330 var inInstanceController = "instance_controller" == name; 331 var instanceAnchor = instanceUrl.substring(1); 332 var nodeName = attributes.getValue("name"); 333 var parentGroup = new Group3D(); 334 this.parentGroups [this.parentGroups.length - 1].addChild(parentGroup); 335 this.parentGroups.push(parentGroup); 336 var handler = this; 337 this.postProcessingBinders.push(function() { 338 var nameSuffix = 0; 339 // Resolve URL at the end of the document 340 var geometries = inInstanceController 341 ? handler.geometries [handler.controllersSkinMeshes [instanceAnchor]] 342 : handler.geometries [instanceAnchor]; 343 for (var i = 0; i < geometries.length; i++) { 344 var shape = new Shape3D(geometries [i]); 345 parentGroup.addChild(shape); 346 // Give a name to shape 347 if (nodeName !== null) { 348 if (nameSuffix === 0) { 349 shape.setName(nodeName); 350 } else { 351 shape.setName(nodeName + "_" + nameSuffix); 352 } 353 nameSuffix++; 354 } 355 } 356 }); 357 } 358 } else if ("instance_node" == name) { 359 var nodeInstanceUrl = attributes.getValue("url"); 360 if (nodeInstanceUrl.indexOf("#") === 0) { 361 var nodeInstanceAnchor = nodeInstanceUrl.substring(1); 362 var parentTransformGroup = this.parentGroups [this.parentGroups.length - 1]; 363 var handler = this; 364 this.postProcessingBinders.push(function() { 365 // Resolve URL at the end of the document 366 var sharedGroup = handler.instantiatedNodes [nodeInstanceAnchor]; 367 if (sharedGroup === undefined) { 368 sharedGroup = new SharedGroup3D(); 369 sharedGroup.addChild(handler.nodes [nodeInstanceAnchor]); 370 handler.instantiatedNodes [nodeInstanceAnchor] = sharedGroup; 371 } 372 parentTransformGroup.addChild(new Link3D(sharedGroup)); 373 }); 374 } 375 } else if ("instance_material" == name && this.parentGroups.length > 0) { 376 var materialInstanceTarget = attributes.getValue("target"); 377 if (materialInstanceTarget.indexOf("#") == 0) { 378 var materialInstanceAnchor = materialInstanceTarget.substring(1); 379 var materialInstanceSymbol = attributes.getValue("symbol"); 380 var group = this.parentGroups [this.parentGroups.length - 1]; 381 var handler = this; 382 this.postProcessingBinders.push(function() { 383 var appearance = handler.effectAppearances [handler.materialEffects [materialInstanceAnchor]]; 384 var updateShapeAppearance = function(node, appearance, forceUpdate) { 385 if (node instanceof Group3D) { 386 var updated = false; 387 var children = node.getChildren(); 388 for (var i = 0; i < children.length; i++) { 389 updated |= updateShapeAppearance(children[i], appearance, forceUpdate); 390 } 391 return updated; 392 } else if (node instanceof Link3D) { 393 return updateShapeAppearance(node.getSharedGroup(), appearance, forceUpdate); 394 } else if (node instanceof Shape3D) { 395 var geometries = forceUpdate 396 ? node.getGeometries() 397 : handler.appearanceGeometries [materialInstanceSymbol]; 398 if (geometries !== undefined) { 399 for (var i = 0; i < geometries.length; i++) { 400 if (geometries [i] === node.getGeometries() [0]) { 401 node.setAppearance(appearance); 402 return true; 403 } 404 } 405 } 406 } 407 return false; 408 }; 409 410 if (!updateShapeAppearance(group, appearance, false)) { 411 updateShapeAppearance(group, appearance, true); 412 } 413 appearance.setName(handler.materialNames [materialInstanceAnchor]); 414 }); 415 } 416 } else if ("instance_visual_scene" == name) { 417 var visualSceneInstanceUrl = attributes.getValue("url"); 418 if (visualSceneInstanceUrl.indexOf("#") === 0) { 419 var visualSceneInstanceAnchor = visualSceneInstanceUrl.substring(1); 420 var handler = this; 421 this.postProcessingBinders.push(function() { 422 // Resolve URL at the end of the document 423 handler.visualScene = handler.visualScenes [visualSceneInstanceAnchor]; 424 }); 425 } 426 } 427 this.parentElements.push(name); 428 } 429 430 DAEHandler.prototype.characters = function(ch, start, length) { 431 this.buffer += ch.substring(start, start + length); 432 } 433 434 DAEHandler.prototype.endElement = function(uri, localName, name) { 435 this.parentElements.pop(); 436 var parent = this.parentElements.length === 0 437 ? null 438 : this.parentElements [this.parentElements.length - 1]; 439 440 if ("color" == name 441 || "float_array" == name 442 || "matrix" == name 443 || "rotate" == name 444 || "scale" == name 445 || "translate" == name) { 446 var floatValues = this.getCharacters().split(/\s/); 447 this.floats = new Array(floatValues.length); 448 var floatCount = 0; 449 for (var i = 0; i < floatValues.length; i++) { 450 if (floatValues [i].length > 0) { 451 var floatValue = parseFloat(floatValues [i]); 452 if (isNaN(floatValue)) { 453 // This may happen with some bad DAE files 454 floatValue = 0.; 455 } 456 this.floats [floatCount++] = floatValue; 457 } 458 } 459 if (floatCount !== floatValues.length) { 460 this.floats.splice(floatCount, this.floats.length - floatCount); 461 } 462 if (this.floatArrayId !== null) { 463 this.floatArrays [this.floatArrayId] = this.floats; 464 this.floatArrayId = null; 465 } 466 } else if ("float" == name) { 467 this.floatValue = parseFloat(this.getCharacters()); 468 } 469 470 if (this.inRootAsset) { 471 this.handleRootAssetElementsEnd(name); 472 } else if ("image" == name) { 473 this.imageId = null; 474 } else if (this.imageId !== null) { 475 this.handleImageElementsEnd(name); 476 } else if ("material" == name) { 477 this.materialId = null; 478 } else if ("effect" == name) { 479 this.effectId = null; 480 } else if (this.effectId !== null) { 481 this.handleEffectElementsEnd(name, parent); 482 } else if ("geometry" == name) { 483 this.geometryId = null; 484 } else if (this.geometryId !== null) { 485 this.handleGeometryElementsEnd(name, parent); 486 } else if ("controller" == name) { 487 this.controllerId = null; 488 } else if ("visual_scene" == name 489 || "node" == name 490 || "node" == parent && "instance_geometry" == name) { 491 this.parentGroups.pop(); 492 } else if ("matrix" == name) { 493 var matrix = mat4.fromValues( 494 this.floats [0], this.floats [4], this.floats [8], this.floats [12], 495 this.floats [1], this.floats [5], this.floats [9], this.floats [13], 496 this.floats [2], this.floats [6], this.floats [10], this.floats [14], 497 this.floats [3], this.floats [7], this.floats [11], this.floats [15]); 498 this.mulTransformGroup(matrix); 499 } else if ("node" == parent && "rotate" == name) { 500 var rotation = mat4.create(); 501 mat4.fromRotation(rotation, this.floats [3] * Math.PI / 180., 502 vec3.fromValues(this.floats [0], this.floats [1], this.floats [2])); 503 this.mulTransformGroup(rotation); 504 } else if ("scale" == name) { 505 var scale = mat4.create(); 506 mat4.scale(scale, scale, vec3.fromValues(this.floats [0], this.floats [1], this.floats [2])); 507 this.mulTransformGroup(scale); 508 } else if ("node" == parent && "translate" == name) { 509 var translation = mat4.create(); 510 mat4.translate(translation, translation, vec3.fromValues(this.floats [0], this.floats [1], this.floats [2])); 511 this.mulTransformGroup(translation); 512 } 513 } 514 515 /** 516 * Returns the trimmed string of last element value. 517 * @private 518 */ 519 DAEHandler.prototype.getCharacters = function() { 520 return this.buffer.trim(); 521 } 522 523 /** 524 * Handles the end tag of elements children of root "asset". 525 * @private 526 */ 527 DAEHandler.prototype.handleRootAssetElementsEnd = function(name) { 528 if ("asset" == name) { 529 this.inRootAsset = false; 530 } else if ("up_axis" == name) { 531 this.axis = this.getCharacters(); 532 } else if ("subject" == name) { 533 this.scene.setName(this.getCharacters()); 534 } else if ("authoring_tool" == name) { 535 var tool = this.getCharacters(); 536 // Try to detect if DAE file was created by Google SketchUp version < 7.1 537 if (tool.indexOf("Google SketchUp") === 0) { 538 var sketchUpVersion = tool.substring("Google SketchUp".length).trim(); 539 if (sketchUpVersion.length > 0) { 540 var dotIndex = sketchUpVersion.indexOf('.'); 541 var majorVersionString = dotIndex === -1 542 ? sketchUpVersion : sketchUpVersion.substring(0, dotIndex); 543 var majorVersion = parseInt(majorVersionString); 544 if (majorVersion < 7 545 || (majorVersion == 7 546 && (dotIndex >= sketchUpVersion.length - 1 // No subversion 547 || sketchUpVersion [dotIndex + 1] < '1'))) { 548 // From http://www.collada.org/public_forum/viewtopic.php?f=12&t=1667 549 // let's reverse transparency 550 this.reverseTransparency = true; 551 } 552 } 553 } 554 } 555 } 556 557 /** 558 * Handles the end tag of elements children of "image". 559 * @private 560 */ 561 DAEHandler.prototype.handleImageElementsEnd = function(name) { 562 if ("init_from" == name) { 563 var imageName = this.getCharacters(); 564 if (imageName.indexOf("./") === 0) { 565 // Remove leading dot 566 imageName = imageName.substring(2); 567 } 568 var lastSlash = this.daeEntryName.lastIndexOf("/"); 569 if (lastSlash >= 0) { 570 // Build imageName path simplifying .. relative paths if necessary 571 var daeEntryNameParts = this.daeEntryName.split("/"); 572 var imageNameParts = imageName.split("/"); 573 daeEntryNameParts.splice(daeEntryNameParts.length - 1, 1); 574 while (imageNameParts [0] == ".." || imageNameParts [0] == ".") { 575 if (imageNameParts [0] == "..") { 576 daeEntryNameParts.splice(daeEntryNameParts.length - 1, 1); 577 } 578 imageNameParts.splice(0, 1); 579 } 580 imageName = ""; 581 for (var i = 0; i < daeEntryNameParts.length; i++) { 582 imageName += daeEntryNameParts [i] + "/"; 583 } 584 for (var i = 0; i < imageNameParts.length; i++) { 585 imageName += imageNameParts [i] + "/"; 586 } 587 imageName = imageName.substring(0, imageName.length - 1); 588 } 589 this.textures [this.imageId] = imageName; 590 } else if ("data" == name) { 591 throw new SAXException("<data> not supported"); 592 } 593 } 594 595 /** 596 * Handles the end tag of elements children of "effect". 597 * @private 598 */ 599 DAEHandler.prototype.handleEffectElementsEnd = function(name, parent) { 600 if ("profile_COMMON" == parent && "newparam" == name) { 601 this.newParamSid = null; 602 } else if ("newparam" == parent && "surface" == name) { 603 this.inSurface2D = false; 604 } else if (this.newParamSid !== null) { 605 if (this.inSurface2D && "init_from" == name) { 606 this.surface2DIds [this.newParamSid] = this.getCharacters(); 607 } else if ("sampler2D" == parent && "source" == name) { 608 this.sampler2DIds [this.newParamSid] = this.getCharacters(); 609 } 610 } else if ("extra" == parent && "technique" == name) { 611 this.techniqueProfile = null; 612 } else if ("phong" == name || "blinn" == name 613 || "lambert" == name || "constant" == name) { 614 var transparencyValue; 615 if (this.transparentColor !== null) { 616 if ("RGB_ZERO" == this.opaque) { 617 transparencyValue = this.transparentColor [0] * 0.212671 618 + this.transparentColor [1] * 0.715160 619 + this.transparentColor [2] * 0.072169; 620 if (this.transparency !== null) { 621 transparencyValue *= this.transparency; 622 } 623 } else { // A_ONE 624 if (this.transparency !== null) { 625 transparencyValue = 1 - this.transparentColor [3] * this.transparency; 626 } else { 627 transparencyValue = 1 - this.transparentColor [3]; 628 } 629 if (this.reverseTransparency) { 630 transparencyValue = 1 - transparencyValue; 631 } 632 } 633 } else { 634 transparencyValue = 0; 635 } 636 var appearance = this.effectAppearances [this.effectId]; 637 if (transparencyValue > 0) { 638 appearance.setTransparency(transparencyValue); // 0 means opaque 639 } 640 // Set default color if it doesn't exist yet 641 var defaultColor = this.transparentColor != null 642 ? vec3.fromValues(this.transparentColor [0], this.transparentColor [1], this.transparentColor [2]) 643 : vec3.fromValues(0., 0., 0.); 644 if (!appearance.getAmbientColor() 645 && !appearance.getDiffuseColor() 646 && !appearance.getSpecularColor()) { 647 if (!appearance.getAmbientColor()) { 648 appearance.setAmbientColor(defaultColor); 649 } 650 if (!appearance.getDiffuseColor()) { 651 appearance.setDiffuseColor(defaultColor); 652 } 653 if (!appearance.getSpecularColor()) { 654 appearance.setSpecularColor(defaultColor); 655 } 656 if (!appearance.getShininess()) { 657 appearance.setShininess(1); 658 } 659 if ("constant" == name) { 660 // Set illumination to 0 for coloring attributes effect 661 this.effectAppearances [this.effectId].setIllumination(0); 662 } 663 } 664 this.transparentColor = null; 665 this.transparency = null; 666 667 this.inPhongBlinnOrLambert = false; 668 this.inConstant = false; 669 } else if (this.inConstant || this.inPhongBlinnOrLambert) { 670 // Set appearance attributes 671 if ("color" == name) { 672 if ("emission" == parent) { 673 if (this.inPhongBlinnOrLambert) { 674 this.effectAppearances [this.effectId].setEmissiveColor(vec3.fromValues( 675 this.floats [0], this.floats [1], this.floats [2])); 676 } else { // inConstant 677 this.effectAppearances [this.effectId].setDiffuseColor(vec3.fromValues( 678 this.floats [0], this.floats [1], this.floats [2])); 679 // Set illumination to 0 for coloring attributes effect 680 this.effectAppearances [this.effectId].setIllumination(0); 681 } 682 } else if ("ambient" == parent) { 683 this.effectAppearances [this.effectId].setAmbientColor(vec3.fromValues( 684 this.floats [0], this.floats [1], this.floats [2])); 685 } else if ("diffuse" == parent) { 686 this.effectAppearances [this.effectId].setDiffuseColor(vec3.fromValues( 687 this.floats [0], this.floats [1], this.floats [2])); // this.floats [3] 688 } else if ("specular" == parent) { 689 this.effectAppearances [this.effectId].setSpecularColor(vec3.fromValues( 690 this.floats [0], this.floats [1], this.floats [2])); 691 } else if ("transparent" == parent) { 692 this.transparentColor = this.floats; 693 } 694 } else if ("float" == name) { 695 if ("shininess" == parent) { 696 this.effectAppearances [this.effectId].setShininess(this.floatValue); 697 } else if ("transparency" == parent) { 698 this.transparency = this.floatValue; 699 } 700 } 701 } else if ("double_sided" == name 702 && "1" == this.getCharacters() 703 && ("GOOGLEEARTH" == this.techniqueProfile 704 || "MAX3D" == this.techniqueProfile 705 || "MAYA" == this.techniqueProfile)) { 706 this.effectAppearances [this.effectId].setCullFace(Appearance3D.CULL_NONE); 707 } 708 } 709 710 /** 711 * Handles the end tag of elements children of "geometry". 712 * @private 713 */ 714 DAEHandler.prototype.handleGeometryElementsEnd = function(name, parent) { 715 if ("mesh" == parent && "source" == name) { 716 if (this.floats !== null) { 717 this.sources [this.meshSourceId] = this.floats; 718 this.floats = null; 719 } 720 this.meshSourceId = null; 721 } else if ("mesh" == parent && "vertices" == name) { 722 this.verticesId = null; 723 } else if ("p" == name 724 || "h" == name 725 || "vcount" == name) { 726 // Get integers 727 var intValues = this.getCharacters().split(/\s/); 728 var integers = new Array(intValues.length); 729 var intCount = 0; 730 for (var i = 0; i < intValues.length; i++) { 731 if (intValues [i].length > 0) { 732 integers [intCount++] = parseInt(intValues [i]); 733 } 734 } 735 if (intCount !== intValues.length) { 736 integers.splice(intCount, integers.length - intCount); 737 } 738 739 if ("ph" != parent && "p" == name) { 740 this.facesAndLinesPrimitives.push(integers); 741 } else if ("vcount" == name) { 742 this.vcount = integers; 743 } else if ("ph" == parent) { 744 if ("p" == name) { 745 this.polygonsPrimitives.push(integers); 746 } else if ("h" == name) { 747 if (this.polygonsPrimitives.length > this.polygonsHoles.length) { 748 this.polygonsHoles.push([]); 749 } 750 this.polygonsHoles [this.polygonsPrimitives.length - 1].push(integers); 751 } 752 } 753 } else if (("triangles" == name 754 || "trifans" == name 755 || "tristrips" == name 756 || "polylist" == name 757 || "polygons" == name 758 || "lines" == name 759 || "linestrips" == name) 760 // Ignore geometries with missing vertices 761 && this.geometryVertices !== null) { 762 var geometry; 763 if ("lines" == name 764 || "linestrips" == name) { 765 geometry = this.getLinesGeometry(name); 766 } else { 767 geometry = this.getFacesGeometry(name); 768 } 769 if (geometry !== null) { 770 this.geometries [this.geometryId].push(geometry); 771 if (this.geometryAppearance !== null) { 772 var geometries = this.appearanceGeometries [this.geometryAppearance]; 773 if (geometries === undefined) { 774 geometries = []; 775 this.appearanceGeometries [this.geometryAppearance] = geometries; 776 } 777 geometries.push(geometry); 778 } 779 } 780 this.geometryAppearance = null; 781 this.geometryVertices = null; 782 this.geometryNormals = null; 783 this.geometryTextureCoordinates = null; 784 this.facesAndLinesPrimitives = []; 785 this.polygonsPrimitives = []; 786 this.polygonsHoles = []; 787 this.vcount = null; 788 } 789 } 790 791 /** 792 * Returns the triangles or polygons geometry matching the read values. 793 * @private 794 */ 795 DAEHandler.prototype.getFacesGeometry = function(name) { 796 var primitiveType; 797 if ("triangles" == name) { 798 primitiveType = GeometryInfo3D.TRIANGLE_ARRAY; 799 } else if ("trifans" == name) { 800 primitiveType = GeometryInfo3D.TRIANGLE_FAN_ARRAY; 801 } else if ("tristrips" == name) { 802 primitiveType = GeometryInfo3D.TRIANGLE_STRIP_ARRAY; 803 } else { 804 primitiveType = GeometryInfo3D.POLYGON_ARRAY; 805 } 806 807 var geometryInfo = new GeometryInfo3D(primitiveType); 808 geometryInfo.setCoordinates(this.geometryVertices); 809 geometryInfo.setCoordinateIndices(this.getIndices(this.geometryVertexOffset)); 810 if (this.geometryNormals) { 811 geometryInfo.setNormals(this.geometryNormals); 812 geometryInfo.setNormalIndices(this.getIndices(this.geometryNormalOffset)); 813 } 814 if (this.geometryTextureCoordinates) { 815 var stride; 816 for (var i = 0; i < this.sourceAccessorStrides.length; i++) { 817 var sourceAccessorStride = this.sourceAccessorStrides [i]; 818 if (sourceAccessorStride.floatArray === this.geometryTextureCoordinates) { 819 stride = sourceAccessorStride.stride; 820 break; 821 } 822 } 823 // Support only UV texture coordinates 824 var textureCoordinates; 825 if (stride > 2) { 826 textureCoordinates = new Array(this.geometryTextureCoordinates.length / stride * 2); 827 for (var i = 0, j = 0; j < this.geometryTextureCoordinates.length; j += stride) { 828 textureCoordinates [i++] = this.geometryTextureCoordinates [j]; 829 textureCoordinates [i++] = this.geometryTextureCoordinates [j + 1]; 830 } 831 } else { 832 textureCoordinates = this.geometryTextureCoordinates; 833 } 834 geometryInfo.setTextureCoordinates(textureCoordinates); 835 geometryInfo.setTextureCoordinateIndices(this.getIndices(this.geometryTextureCoordinatesOffset)); 836 } 837 838 839 if ("tristrips" == name 840 || "trifans" == name) { 841 var stripCounts = new Array(this.facesAndLinesPrimitives.length); 842 for (var i = 0; i < stripCounts.length; i++) { 843 stripCounts [i] = this.facesAndLinesPrimitives [i].length / this.inputCount; 844 } 845 geometryInfo.setStripCounts(stripCounts); 846 } else if ("polylist" == name) { 847 geometryInfo.setStripCounts(this.vcount); 848 } else if ("polygons" == name) { 849 var polygonHolesCount = 0; 850 for (var i = 0; i < this.polygonsHoles.length; i++) { 851 polygonHolesCount += this.polygonsHoles [i].length; 852 } 853 var stripCounts = new Array(this.facesAndLinesPrimitives.length + this.polygonsPrimitives.length 854 + polygonHolesCount); 855 var contourCounts = new Array(this.facesAndLinesPrimitives.length + this.polygonsPrimitives.length); 856 var stripIndex = 0; 857 var countourIndex = 0; 858 for (var i = 0; i < this.facesAndLinesPrimitives.length; i++) { 859 stripCounts [stripIndex++] = this.facesAndLinesPrimitives [i].length / this.inputCount; 860 contourCounts [countourIndex++] = 1; // One polygon 861 } 862 for (var i = 0; i < this.polygonsPrimitives.length; i++) { 863 stripCounts [stripIndex++] = this.polygonsPrimitives [i].length / this.inputCount; 864 var polygonHoles = this.polygonsHoles [i]; 865 for (var j = 0; j < polygonHoles.length; j++) { 866 stripCounts [stripIndex++] = polygonHoles [j].length / this.inputCount; 867 } 868 contourCounts [countourIndex++] = 1 + polygonHoles.length; // One polygon + its holes count 869 } 870 geometryInfo.setStripCounts(stripCounts); 871 geometryInfo.setContourCounts(contourCounts); 872 } 873 874 if (!this.geometryNormals) { 875 geometryInfo.setCreaseAngle(Math.PI / 2); 876 geometryInfo.setGeneratedNormals(true); 877 } 878 return geometryInfo.getIndexedGeometryArray(); 879 } 880 881 /** 882 * Returns the lines geometry matching the read values. 883 * @private 884 */ 885 DAEHandler.prototype.getLinesGeometry = function(name) { 886 var coordinatesIndices = this.getIndices(this.geometryVertexOffset); 887 if (coordinatesIndices.length !== 0) { 888 var textureCoordinatesIndices = this.geometryTextureCoordinates 889 ? this.getIndices(this.geometryTextureCoordinatesOffset) 890 : []; 891 if ("linestrips" == name) { 892 var noStripCoordinatesIndices = []; 893 var noStripTextureCoordinatesIndices = []; 894 for (var i = 0, index = 0; i < this.facesAndLinesPrimitives.length; i++) { 895 var stripCount = this.facesAndLinesPrimitives [i].length / this.inputCount; 896 for (var k = 0; k < stripCount - 1; k++) { 897 noStripCoordinatesIndices.push(coordinatesIndices [index + k]); 898 noStripCoordinatesIndices.push(coordinatesIndices [index + k + 1]); 899 if (textureCoordinateIndices.length > 0) { 900 noStripTextureCoordinatesIndices.push(textureCoordinateIndices [index + k]); 901 noStripTextureCoordinatesIndices.push(textureCoordinateIndices [index + k + 1]); 902 } 903 } 904 index += stripCount; 905 } 906 coordinatesIndices = noStripCoordinatesIndices; 907 textureCoordinatesIndices = noStripTextureCoordinatesIndices; 908 } 909 return new IndexedLineArray3D(this.geometryVertices, coordinatesIndices, 910 this.geometryTextureCoordinates, textureCoordinatesIndices); 911 } else { 912 // Ignore lines with an empty index set 913 return null; 914 } 915 } 916 917 /** 918 * Returns the indices at the given <code>indexOffset</code>. 919 * @private 920 */ 921 DAEHandler.prototype.getIndices = function(indexOffset) { 922 if (this.facesAndLinesPrimitives.length === 1 && this.polygonsPrimitives.length === 1 && this.inputCount === 1) { 923 return facesAndLinesPrimitives [0]; 924 } else { 925 var indexCount = this.getIndexCount(this.facesAndLinesPrimitives); 926 indexCount += this.getIndexCount(this.polygonsPrimitives); 927 for (var i = 0; i < this.polygonsHoles.length; i++) { 928 indexCount += this.getIndexCount(this.polygonsHoles [i]); 929 } 930 931 var indices = new Array(indexCount / this.inputCount); 932 var i = 0; 933 for (var j = 0; j < this.facesAndLinesPrimitives.length; j++) { 934 var primitives = this.facesAndLinesPrimitives [j]; 935 for (var k = indexOffset; k < primitives.length; k += this.inputCount) { 936 indices [i++] = primitives [k]; 937 } 938 } 939 for (var j = 0; j < this.polygonsPrimitives.length; j++) { 940 var polygonPrimitives = this.polygonsPrimitives [j]; 941 for (var k = indexOffset; k < polygonPrimitives.length; k += this.inputCount) { 942 indices [i++] = polygonPrimitives [k]; 943 } 944 var polygonHoles = this.polygonsHoles [j]; 945 for (var k = 0; k < polygonHoles.length; k++) { 946 var polygonHole = polygonHoles [k]; 947 for (var l = indexOffset; l < polygonHole.length; l += this.inputCount) { 948 indices [i++] = polygonHole [l]; 949 } 950 } 951 } 952 return indices; 953 } 954 } 955 956 /** 957 * Returns the total count of indices among the given <code>faceIndices</code>. 958 * @private 959 */ 960 DAEHandler.prototype.getIndexCount = function(faceIndices) { 961 var indexCount = 0; 962 for (var i = 0; i < faceIndices.length; i++) { 963 indexCount += faceIndices [i].length; 964 } 965 return indexCount; 966 } 967 968 /** 969 * Multiplies the transform at top of the transform groups stack by the 970 * given <code>transformMultiplier</code>. 971 * @private 972 */ 973 DAEHandler.prototype.mulTransformGroup = function(transformMultiplier) { 974 var transformGroup = this.parentGroups [this.parentGroups.length - 1]; 975 var transform = mat4.create(); 976 transformGroup.getTransform(transform); 977 mat4.mul(transform, transform, transformMultiplier); 978 transformGroup.setTransform(transform); 979 } 980 981 DAEHandler.prototype.endDocument = function() { 982 for (var i = 0; i < this.postProcessingBinders.length; i++) { 983 this.postProcessingBinders [i] (); 984 } 985 986 if (this.visualScene !== null) { 987 var rootTransform = mat4.create(); 988 this.visualScene.getTransform(rootTransform); 989 990 var bounds = new BoundingBox3D( 991 vec3.fromValues(Infinity, Infinity, Infinity), 992 vec3.fromValues(-Infinity, -Infinity, -Infinity)); 993 this.computeBounds(this.visualScene, bounds, mat4.create()); 994 995 // Translate model to its center 996 var lower = vec3.create(); 997 bounds.getLower(lower); 998 if (lower [0] !== Infinity) { 999 var upper = vec3.create(); 1000 bounds.getUpper(upper); 1001 var translation = mat4.create(); 1002 mat4.translate(translation, translation, 1003 vec3.fromValues(-lower [0] - (upper [0] - lower [0]) / 2, 1004 -lower [1] - (upper [1] - lower [1]) / 2, 1005 -lower [2] - (upper [2] - lower [2]) / 2)); 1006 mat4.mul(translation, translation, rootTransform); 1007 rootTransform = translation; 1008 } 1009 1010 // Scale model to cm 1011 var scaleTransform = mat4.create(); 1012 mat4.scale(scaleTransform, scaleTransform, 1013 vec3.fromValues(this.meterScale * 100, this.meterScale * 100, this.meterScale * 100)); 1014 mat4.mul(scaleTransform, scaleTransform, rootTransform); 1015 1016 // Set orientation to Y_UP 1017 var axisTransform = mat4.create(); 1018 if ("Z_UP" == this.axis) { 1019 mat4.fromXRotation(axisTransform, -Math.PI / 2); 1020 } else if ("X_UP" == this.axis) { 1021 mat4.fromZRotation(axisTransform, Math.PI / 2); 1022 } 1023 mat4.mul(axisTransform, axisTransform, scaleTransform); 1024 1025 this.visualScene.setTransform(axisTransform); 1026 1027 this.sceneRoot.addChild(this.visualScene); 1028 } 1029 } 1030 1031 /** 1032 * Combines the given <code>bounds</code> with the bounds of the given <code>node</code> 1033 * and its children. 1034 */ 1035 DAEHandler.prototype.computeBounds = function(node, bounds, parentTransformations) { 1036 if (node instanceof Group3D) { 1037 if (node instanceof TransformGroup3D) { 1038 parentTransformations = mat4.clone(parentTransformations); 1039 var transform = mat4.create(); 1040 node.getTransform(transform); 1041 mat4.mul(parentTransformations, parentTransformations, transform); 1042 } 1043 // Compute the bounds of all the node children 1044 var children = node.getChildren(); 1045 for (var i = 0; i < children.length; i++) { 1046 this.computeBounds(children [i], bounds, parentTransformations); 1047 } 1048 } else if (node instanceof Link3D) { 1049 this.computeBounds(node.getSharedGroup(), bounds, parentTransformations); 1050 } else if (node instanceof Shape3D) { 1051 var shapeBounds = node.getBounds(); 1052 shapeBounds.transform(parentTransformations); 1053 bounds.combine(shapeBounds); 1054 } 1055 } 1056 1057