1 /* 2 * Room3D.js 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 scene3d.js 22 // Object3DBranch.js 23 // TextureManager.js 24 25 26 /** 27 * Creates the 3D room matching the given home <code>room</code>. 28 * @param {Room} room 29 * @param {Home} home 30 * @param {UserPreferences} [preferences] 31 * @param {boolean} ignoreCeilingPart 32 * @param {boolean} waitTextureLoadingEnd 33 * @constructor 34 * @extends Object3DBranch 35 * @author Emmanuel Puybaret 36 */ 37 function Room3D(room, home, preferences, ignoreCeilingPart, waitTextureLoadingEnd) { 38 if (waitTextureLoadingEnd === undefined) { 39 // 4 parameters 40 waitModelAndTextureLoadingEnd = ignoreCeilingPart; 41 ignoreCeilingPart = preferences; 42 preferences = null; 43 } 44 Object3DBranch.call(this, room, home, preferences); 45 if (ignoreCeilingPart === undefined) { 46 ignoreCeilingPart = false; 47 waitTextureLoadingEnd = false; 48 } 49 50 this.addChild(this.createRoomPartShape()); 51 this.addChild(this.createRoomPartShape()); 52 53 // Add selection node 54 var roomSelectionShape = new Shape3D(); 55 roomSelectionShape.setAppearance(this.getSelectionAppearance()); 56 roomSelectionShape.setCapability(Shape3D.ALLOW_GEOMETRY_WRITE); 57 roomSelectionShape.setPickable(false); 58 this.addChild(roomSelectionShape); 59 60 this.updateRoomGeometry(); 61 this.updateRoomAppearance(waitTextureLoadingEnd); 62 if (ignoreCeilingPart) { 63 this.removeChild(Room3D.CEILING_PART); 64 } 65 } 66 Room3D.prototype = Object.create(Object3DBranch.prototype); 67 Room3D.prototype.constructor = Room3D; 68 69 Room3D.FLOOR_PART = 0; 70 Room3D.CEILING_PART = 1; 71 72 /** 73 * Returns a new room part shape with no geometry 74 * and a default appearance with a white material. 75 * @return {Node3D} 76 * @private 77 */ 78 Room3D.prototype.createRoomPartShape = function() { 79 var roomShape = new Shape3D(); 80 roomShape.setCapability(Shape3D.ALLOW_GEOMETRY_WRITE); 81 var roomAppearance = new Appearance3D(); 82 roomShape.setAppearance(roomAppearance); 83 this.updateAppearanceMaterial(roomAppearance, Object3DBranch.DEFAULT_COLOR, Object3DBranch.DEFAULT_AMBIENT_COLOR, 0); 84 return roomShape; 85 } 86 87 Room3D.prototype.update = function() { 88 this.updateRoomGeometry(); 89 this.updateRoomAppearance(false); 90 } 91 92 /** 93 * Sets the 3D geometry of this room shapes that matches its 2D geometry. 94 * @private 95 */ 96 Room3D.prototype.updateRoomGeometry = function() { 97 this.updateRoomPartGeometry(Room3D.FLOOR_PART, this.getUserData().getFloorTexture()); 98 this.updateRoomPartGeometry(Room3D.CEILING_PART, this.getUserData().getCeilingTexture()); 99 var room = this.getUserData(); 100 this.setPickable(this.getHome().getEnvironment().getWallsAlpha() == 0 101 || room.getLevel() == null 102 || room.getLevel().getElevation() <= 0); 103 } 104 105 Room3D.prototype.updateRoomPartGeometry = function(roomPart, texture) { 106 var roomShape = this.getChild(roomPart); 107 var currentGeometriesCount = roomShape.getGeometries().length; 108 var room = this.getUserData(); 109 if (room.getLevel() == null || room.getLevel().isViewableAndVisible()) { 110 var geometries = this.createRoomGeometries(roomPart, texture); 111 for (var i = 0; i < geometries.length; i++) { 112 roomShape.addGeometry(geometries[i]); 113 } 114 } 115 for (var i = currentGeometriesCount - 1; i >= 0; i--) { 116 roomShape.removeGeometry(i); 117 } 118 119 var roomSelectionShape = this.getChild(2); 120 roomSelectionShape.addGeometry(this.createRoomSelectionGeometry()); 121 if (roomSelectionShape.getGeometries().length > 1) { 122 roomSelectionShape.removeGeometry(0); 123 } 124 } 125 126 /** 127 * Returns room geometry computed from its points. 128 * @param {number} roomPart 129 * @param {HomeTexture} texture 130 * @return {Array} 131 * @private 132 */ 133 Room3D.prototype.createRoomGeometries = function(roomPart, texture) { 134 var room = this.getUserData(); 135 var points = room.getPoints(); 136 if ((roomPart === Room3D.FLOOR_PART && room.isFloorVisible() 137 || roomPart === Room3D.CEILING_PART && room.isCeilingVisible()) 138 && points.length > 2) { 139 var roomLevel = room.getLevel(); 140 var levels = this.getHome().getLevels(); 141 var lastLevel = this.isLastLevel(roomLevel, levels); 142 var floorBottomElevation; 143 var roomElevation; 144 if (roomLevel != null) { 145 roomElevation = roomLevel.getElevation(); 146 floorBottomElevation = roomElevation - roomLevel.getFloorThickness(); 147 } else { 148 roomElevation = 0; 149 floorBottomElevation = 0; 150 } 151 var firstLevelElevation; 152 if (levels.length === 0) { 153 firstLevelElevation = 0; 154 } else { 155 firstLevelElevation = levels[0].getElevation(); 156 } 157 var floorBottomVisible = roomPart === Room3D.FLOOR_PART 158 && roomLevel !== null 159 && roomElevation !== firstLevelElevation; 160 161 var roomsAtSameElevation = []; 162 var ceilingsAtSameFloorBottomElevation = []; 163 var rooms = this.getHome().getRooms(); 164 for (var i = 0; i < rooms.length; i++) { 165 var homeRoom = rooms[i]; 166 var homeRoomLevel = homeRoom.getLevel(); 167 if (homeRoomLevel === null || homeRoomLevel.isViewableAndVisible()) { 168 if (room === homeRoom 169 || roomLevel === homeRoomLevel 170 && (roomPart === Room3D.FLOOR_PART && homeRoom.isFloorVisible() 171 || roomPart === Room3D.CEILING_PART && homeRoom.isCeilingVisible()) 172 || roomLevel != null 173 && homeRoomLevel != null 174 && (roomPart === Room3D.FLOOR_PART 175 && homeRoom.isFloorVisible() 176 && Math.abs(roomElevation - homeRoomLevel.getElevation()) < 1.0E-4 177 || roomPart === Room3D.CEILING_PART 178 && homeRoom.isCeilingVisible() 179 && !lastLevel 180 && !this.isLastLevel(homeRoomLevel, levels) 181 && Math.abs(roomElevation + roomLevel.getHeight() - (homeRoomLevel.getElevation() + homeRoomLevel.getHeight())) < 1.0E-4)) { 182 roomsAtSameElevation.push(homeRoom); 183 } else if (floorBottomVisible 184 && homeRoomLevel != null 185 && homeRoom.isCeilingVisible() 186 && !this.isLastLevel(homeRoomLevel, levels) 187 && Math.abs(floorBottomElevation - (homeRoomLevel.getElevation() + homeRoomLevel.getHeight())) < 1.0E-4) { 188 ceilingsAtSameFloorBottomElevation.push(homeRoom); 189 } 190 } 191 } 192 if (roomLevel != null) { 193 roomsAtSameElevation.sort(function (room1, room2) { 194 var comparison = (room1.getLevel().getElevation() - room2.getLevel().getElevation()); 195 if (comparison !== 0) { 196 return comparison; 197 } else { 198 return room1.getLevel().getElevationIndex() - room2.getLevel().getElevationIndex(); 199 } 200 }); 201 } 202 203 var visibleStaircases; 204 if (roomLevel === null 205 || roomPart === Room3D.CEILING_PART 206 && lastLevel) { 207 visibleStaircases = []; 208 } else { 209 visibleStaircases = this.getVisibleStaircases(this.getHome().getFurniture(), roomPart, roomLevel, 210 roomLevel.getElevation() === firstLevelElevation); 211 } 212 213 var sameElevation = true; 214 if (roomPart === Room3D.CEILING_PART && (roomLevel === null || lastLevel)) { 215 var firstPointElevation = this.getRoomHeightAt(points[0][0], points[0][1]); 216 for (var i = 1; i < points.length && sameElevation; i++) { 217 sameElevation = this.getRoomHeightAt(points[i][0], points[i][1]) === firstPointElevation; 218 } 219 } 220 221 var roomPoints; 222 var roomHoles; 223 var roomPointsWithoutHoles; 224 var roomVisibleArea; 225 if (!room.isSingular() 226 || sameElevation 227 && (roomsAtSameElevation[roomsAtSameElevation.length - 1] !== room 228 || visibleStaircases.length > 0)) { 229 roomVisibleArea = new java.awt.geom.Area(this.getShape(points)); 230 if ((roomsAtSameElevation.indexOf(room) >= 0)) { 231 for (var i = roomsAtSameElevation.length - 1; i > 0 && roomsAtSameElevation[i] !== room; i--) { 232 var otherRoom = roomsAtSameElevation[i]; 233 roomVisibleArea.subtract(new java.awt.geom.Area(this.getShape(otherRoom.getPoints()))); 234 } 235 } 236 this.removeStaircasesFromArea(visibleStaircases, roomVisibleArea); 237 roomPoints = []; 238 roomHoles = []; 239 roomPointsWithoutHoles = this.getAreaPoints(roomVisibleArea, roomPoints, roomHoles, 1, roomPart === Room3D.CEILING_PART); 240 } else { 241 var clockwise = room.isClockwise(); 242 if (clockwise && roomPart === Room3D.FLOOR_PART 243 || !clockwise && roomPart === Room3D.CEILING_PART) { 244 points = this.getReversedArray(points); 245 } 246 roomPointsWithoutHoles = 247 roomPoints = [points]; 248 roomHoles = []; 249 roomVisibleArea = null; 250 } 251 252 var geometries = []; 253 var subpartSize = this.getHome().getEnvironment().getSubpartSizeUnderLight(); 254 255 if (roomPointsWithoutHoles.length !== 0) { 256 var roomPointElevations = []; 257 var roomAtSameElevation = true; 258 for (var i = 0; i < roomPointsWithoutHoles.length; i++) { 259 var roomPartPoints = roomPointsWithoutHoles[i]; 260 var roomPartPointElevations = new Array(roomPartPoints.length); 261 for (var j = 0; j < roomPartPoints.length; j++) { 262 roomPartPointElevations[j] = roomPart === Room3D.FLOOR_PART 263 ? roomElevation 264 : this.getRoomHeightAt(roomPartPoints[j][0], roomPartPoints[j][1]); 265 if (roomAtSameElevation && j > 0) { 266 roomAtSameElevation = roomPartPointElevations[j] === roomPartPointElevations[j - 1]; 267 } 268 } 269 roomPointElevations.push(roomPartPointElevations); 270 } 271 272 if (roomAtSameElevation && subpartSize > 0) { 273 for (var j = 0; j < roomPointsWithoutHoles.length; j++) { 274 var roomPartPoints = roomPointsWithoutHoles[j]; 275 var xMin = Number.MAX_VALUE; 276 var xMax = Number.MIN_VALUE; 277 var zMin = Number.MAX_VALUE; 278 var zMax = Number.MIN_VALUE; 279 for (var i = 0; i < roomPartPoints.length; i++) { 280 var point = roomPartPoints[i]; 281 xMin = Math.min(xMin, point[0]); 282 xMax = Math.max(xMax, point[0]); 283 zMin = Math.min(zMin, point[1]); 284 zMax = Math.max(zMax, point[1]); 285 } 286 287 var roomPartArea = new java.awt.geom.Area(this.getShape(roomPartPoints)); 288 for (var xSquare = xMin; xSquare < xMax; xSquare += subpartSize) { 289 for (var zSquare = zMin; zSquare < zMax; zSquare += subpartSize) { 290 var roomPartSquare = new java.awt.geom.Area(new java.awt.geom.Rectangle2D.Float(xSquare, zSquare, subpartSize, subpartSize)); 291 roomPartSquare.intersect(roomPartArea); 292 if (!roomPartSquare.isEmpty()) { 293 var geometryPartPointsWithoutHoles = this.getAreaPoints(roomPartSquare, 1, roomPart === Room3D.CEILING_PART); 294 if (!(geometryPartPointsWithoutHoles.length == 0)) { 295 geometries.push(this.computeRoomPartGeometry(geometryPartPointsWithoutHoles, 296 null, roomLevel, roomPointElevations[i][0], floorBottomElevation, 297 roomPart === Room3D.FLOOR_PART, false, texture)); 298 } 299 } 300 } 301 } 302 } 303 } else { 304 geometries.push(this.computeRoomPartGeometry(roomPointsWithoutHoles, roomPointElevations, roomLevel, 305 roomElevation, floorBottomElevation, roomPart === Room3D.FLOOR_PART, false, texture)); 306 } 307 if (roomLevel != null 308 && roomPart === Room3D.FLOOR_PART 309 && roomLevel.getElevation() !== firstLevelElevation) { 310 geometries.push(this.computeRoomBorderGeometry(roomPoints, roomHoles, roomLevel, roomElevation, texture)); 311 } 312 } 313 314 if (floorBottomVisible) { 315 var floorBottomPointsWithoutHoles; 316 if (roomVisibleArea != null 317 || ceilingsAtSameFloorBottomElevation.length > 0) { 318 var floorBottomVisibleArea = roomVisibleArea != null ? roomVisibleArea : new java.awt.geom.Area(this.getShape(points)); 319 for (var i = 0; i < ceilingsAtSameFloorBottomElevation.length; i++) { 320 var otherRoom = ceilingsAtSameFloorBottomElevation[i]; 321 floorBottomVisibleArea.subtract(new java.awt.geom.Area(this.getShape(otherRoom.getPoints()))); 322 } 323 floorBottomPointsWithoutHoles = this.getAreaPoints(floorBottomVisibleArea, 1, true); 324 } else { 325 floorBottomPointsWithoutHoles = [this.getReversedArray(points)]; 326 } 327 328 if (floorBottomPointsWithoutHoles.length !== 0) { 329 if (subpartSize > 0) { 330 for (var i = 0; i < floorBottomPointsWithoutHoles.length; i++) { 331 var floorBottomPartPoints = floorBottomPointsWithoutHoles[i]; 332 var xMin = Number.MAX_VALUE; 333 var xMax = Number.MIN_VALUE; 334 var zMin = Number.MAX_VALUE; 335 var zMax = Number.MIN_VALUE; 336 for (var j = 0; j < floorBottomPartPoints.length; j++) { 337 var point = floorBottomPartPoints[j]; 338 xMin = Math.min(xMin, point[0]); 339 xMax = Math.max(xMax, point[0]); 340 zMin = Math.min(zMin, point[1]); 341 zMax = Math.max(zMax, point[1]); 342 } 343 344 var floorBottomPartArea = new java.awt.geom.Area(this.getShape(floorBottomPartPoints)); 345 for (var xSquare = xMin; xSquare < xMax; xSquare += subpartSize) { 346 for (var zSquare = zMin; zSquare < zMax; zSquare += subpartSize) { 347 var floorBottomPartSquare = new java.awt.geom.Area(new java.awt.geom.Rectangle2D.Float(xSquare, zSquare, subpartSize, subpartSize)); 348 floorBottomPartSquare.intersect(floorBottomPartArea); 349 if (!floorBottomPartSquare.isEmpty()) { 350 var geometryPartPointsWithoutHoles = this.getAreaPoints(floorBottomPartSquare, 1, true); 351 if (geometryPartPointsWithoutHoles.length !== 0) { 352 geometries.push(this.computeRoomPartGeometry(geometryPartPointsWithoutHoles, 353 null, roomLevel, roomElevation, floorBottomElevation, 354 true, true, texture)); 355 } 356 } 357 } 358 } 359 } 360 } else { 361 geometries.push(this.computeRoomPartGeometry(floorBottomPointsWithoutHoles, null, roomLevel, 362 roomElevation, floorBottomElevation, true, true, texture)); 363 } 364 } 365 } 366 367 return geometries.slice(0); 368 } else { 369 return []; 370 } 371 } 372 373 /** 374 * Returns the room part geometry matching the given points. 375 * @param {Array} geometryPoints 376 * @param {Array} roomPointElevations 377 * @param {Level} roomLevel 378 * @param {number} roomPartElevation 379 * @param {number} floorBottomElevation 380 * @param {boolean} floorPart 381 * @param {boolean} floorBottomPart 382 * @param {HomeTexture} texture 383 * @return {IndexedGeometryArray3D} 384 * @private 385 */ 386 Room3D.prototype.computeRoomPartGeometry = function(geometryPoints, roomPointElevations, 387 roomLevel, roomPartElevation, floorBottomElevation, 388 floorPart, floorBottomPart, texture) { 389 var stripCounts = new Array(geometryPoints.length); 390 var vertexCount = 0; 391 for (var i = 0; i < geometryPoints.length; i++) { 392 var areaPoints = geometryPoints[i]; 393 stripCounts[i] = areaPoints.length; 394 vertexCount += stripCounts[i]; 395 } 396 var coords = new Array(vertexCount); 397 var i = 0; 398 for (var j = 0; j < geometryPoints.length; j++) { 399 var areaPoints = geometryPoints[j]; 400 var roomPartPointElevations = roomPointElevations != null 401 ? roomPointElevations[j] 402 : null; 403 for (var k = 0; k < areaPoints.length; k++) { 404 var y = floorBottomPart 405 ? floorBottomElevation 406 : (roomPartPointElevations != null 407 ? roomPartPointElevations[k] 408 : roomPartElevation); 409 coords[i++] = vec3.fromValues(areaPoints[k][0], y, areaPoints[k][1]); 410 } 411 } 412 413 var geometryInfo = new GeometryInfo3D(GeometryInfo3D.POLYGON_ARRAY); 414 geometryInfo.setCoordinates(coords); 415 geometryInfo.setStripCounts(stripCounts); 416 417 if (texture != null) { 418 var textureCoords = new Array(vertexCount); 419 i = 0; 420 for (var j = 0; j < geometryPoints.length; j++) { 421 var areaPoints = geometryPoints[j]; 422 for (var k = 0; k < areaPoints.length; k++) { 423 textureCoords[i++] = vec2.fromValues(areaPoints[k][0], 424 floorPart 425 ? -areaPoints[k][1] 426 : areaPoints[k][1]); 427 } 428 } 429 geometryInfo.setTextureCoordinates(textureCoords); 430 } 431 geometryInfo.setGeneratedNormals(true); 432 return geometryInfo.getIndexedGeometryArray(); 433 } 434 435 /** 436 * Returns the room border geometry matching the given points. 437 * @param {Array} geometryRooms 438 * @param {Array} geometryHoles 439 * @param {Level} roomLevel 440 * @param {number} roomElevation 441 * @param {HomeTexture} texture 442 * @return {IndexedGeometryArray3D} 443 * @private 444 */ 445 Room3D.prototype.computeRoomBorderGeometry = function(geometryRooms, geometryHoles, 446 roomLevel, roomElevation, texture) { 447 var vertexCount = 0; 448 for (var i = 0; i < geometryRooms.length; i++) { 449 vertexCount += geometryRooms[i].length; 450 } 451 for (var i = 0; i < geometryHoles.length; i++) { 452 vertexCount += geometryHoles[i].length; 453 } 454 vertexCount = vertexCount * 4; 455 456 var i = 0; 457 var coords = new Array(vertexCount); 458 var floorBottomElevation = roomElevation - roomLevel.getFloorThickness(); 459 for (var index = 0; index < geometryRooms.length; index++) { 460 var geometryPoints = geometryRooms[index]; 461 for (var j = 0; j < geometryPoints.length; j++) { 462 coords[i++] = vec3.fromValues(geometryPoints[j][0], roomElevation, geometryPoints[j][1]); 463 coords[i++] = vec3.fromValues(geometryPoints[j][0], floorBottomElevation, geometryPoints[j][1]); 464 var nextPoint = j < geometryPoints.length - 1 465 ? j + 1 466 : 0; 467 coords[i++] = vec3.fromValues(geometryPoints[nextPoint][0], floorBottomElevation, geometryPoints[nextPoint][1]); 468 coords[i++] = vec3.fromValues(geometryPoints[nextPoint][0], roomElevation, geometryPoints[nextPoint][1]); 469 } 470 } 471 for (var index = 0; index < geometryHoles.length; index++) { 472 var geometryHole = geometryHoles[index]; 473 for (var j = 0; j < geometryHole.length; j++) { 474 coords[i++] = vec3.fromValues(geometryHole[j][0], roomElevation, geometryHole[j][1]); 475 var nextPoint = j < geometryHole.length - 1 476 ? j + 1 477 : 0; 478 coords[i++] = vec3.fromValues(geometryHole[nextPoint][0], roomElevation, geometryHole[nextPoint][1]); 479 coords[i++] = vec3.fromValues(geometryHole[nextPoint][0], floorBottomElevation, geometryHole[nextPoint][1]); 480 coords[i++] = vec3.fromValues(geometryHole[j][0], floorBottomElevation, geometryHole[j][1]); 481 } 482 } 483 484 var geometryInfo = new GeometryInfo3D(GeometryInfo3D.QUAD_ARRAY); 485 geometryInfo.setCoordinates(coords); 486 487 if (texture != null) { 488 var textureCoords = new Array(vertexCount); 489 i = 0; 490 if (texture.isFittingArea()) { 491 for (var index = 0; index < geometryRooms.length; index++) { 492 var geometryPoints = geometryRooms[index]; 493 for (var j = 0; j < geometryPoints.length; j++) { 494 textureCoords [i++] = 495 textureCoords [i++] = vec2.fromValues(geometryPoints [j][0], -geometryPoints [j][1]); 496 var nextPoint = j < geometryPoints.length - 1 497 ? j + 1 498 : 0; 499 textureCoords [i++] = 500 textureCoords [i++] = vec2.fromValues(geometryPoints [nextPoint][0], -geometryPoints [nextPoint][1]); 501 } 502 } 503 for (var index = 0; index < geometryHoles.length; index++) { 504 var geometryHole = geometryHoles[index]; 505 for (var j = 0; j < geometryHole.length; j++) { 506 textureCoords [i] = vec2.fromValues(geometryHole [j][0], -geometryHole [j][1]); 507 var nextPoint = j < geometryHole.length - 1 508 ? j + 1 509 : 0; 510 textureCoords [i + 1] = vec2.fromValues(geometryHole [nextPoint][0], -geometryHole [nextPoint][1]); 511 textureCoords [i + 2] = textureCoords [i + 1]; 512 textureCoords [i + 3] = textureCoords [i]; 513 i += 4; 514 } 515 } 516 } else { 517 for (var index = 0; index < geometryRooms.length; index++) { 518 var geometryPoints = geometryRooms[index]; 519 for (var j = 0; j < geometryPoints.length; j++) { 520 textureCoords[i++] = vec2.fromValues(0, roomLevel.getFloorThickness()); 521 textureCoords[i++] = vec2.fromValues(0, 0); 522 var nextPoint = j < geometryPoints.length - 1 523 ? j + 1 524 : 0; 525 var textureCoord = java.awt.geom.Point2D.distance(geometryPoints[j][0], geometryPoints[j][1], geometryPoints[nextPoint][0], geometryPoints[nextPoint][1]); 526 textureCoords[i++] = vec2.fromValues(textureCoord, 0); 527 textureCoords[i++] = vec2.fromValues(textureCoord, roomLevel.getFloorThickness()); 528 } 529 } 530 for (var index = 0; index < geometryHoles.length; index++) { 531 var geometryHole = geometryHoles[index]; 532 for (var j = 0; j < geometryHole.length; j++) { 533 textureCoords[i++] = vec2.fromValues(0, 0); 534 var nextPoint = j < geometryHole.length - 1 535 ? j + 1 536 : 0; 537 var textureCoord = java.awt.geom.Point2D.distance(geometryHole[j][0], geometryHole[j][1], geometryHole[nextPoint][0], geometryHole[nextPoint][1]); 538 textureCoords[i++] = vec2.fromValues(textureCoord, 0); 539 textureCoords[i++] = vec2.fromValues(textureCoord, roomLevel.getFloorThickness()); 540 textureCoords[i++] = vec2.fromValues(0, roomLevel.getFloorThickness()); 541 } 542 } 543 } 544 geometryInfo.setTextureCoordinates(textureCoords); 545 } 546 547 geometryInfo.setCreaseAngle(Math.PI / 8); 548 geometryInfo.setGeneratedNormals(true); 549 return geometryInfo.getIndexedGeometryArray(); 550 } 551 552 Room3D.prototype.removeStaircasesFromArea = function (visibleStaircases, area) { 553 var modelManager = ModelManager.getInstance(); 554 for (var i = 0; i < visibleStaircases.length; i++) { 555 var staircase = visibleStaircases[i]; 556 area.subtract(modelManager.getAreaOnFloor(staircase)); 557 } 558 } 559 560 /** 561 * Returns the visible staircases among the given <code>furniture</code>. 562 * @param {Array} furniture 563 * @param {number} roomPart 564 * @param {Level} roomLevel 565 * @param {boolean} firstLevel 566 * @return {Array} 567 * @private 568 */ 569 Room3D.prototype.getVisibleStaircases = function(furniture, roomPart, roomLevel, firstLevel) { 570 var visibleStaircases = []; 571 for (var i = 0; i < furniture.length; i++) { 572 var piece = furniture[i]; 573 if (piece.isVisible() 574 && (piece.getLevel() === null 575 || piece.getLevel().isViewableAndVisible())) { 576 if (piece instanceof HomeFurnitureGroup) { 577 visibleStaircases.push.apply(visibleStaircases, this.getVisibleStaircases(piece.getFurniture(), roomPart, roomLevel, firstLevel)); 578 } else if (piece.getStaircaseCutOutShape() != null 579 && "false" != piece.getStaircaseCutOutShape().toLowerCase() 580 && ((roomPart === Room3D.FLOOR_PART 581 && piece.getGroundElevation() < roomLevel.getElevation() 582 && piece.getGroundElevation() + piece.getHeight() >= roomLevel.getElevation() - (firstLevel ? 0 : roomLevel.getFloorThickness()) 583 || roomPart === Room3D.CEILING_PART 584 && piece.getGroundElevation() < roomLevel.getElevation() + roomLevel.getHeight() 585 && piece.getGroundElevation() + piece.getHeight() >= roomLevel.getElevation() + roomLevel.getHeight()))) { 586 visibleStaircases.push(piece); 587 } 588 } 589 } 590 return visibleStaircases; 591 } 592 593 /** 594 * Returns an array that cites <code>points</code> in reverse order. 595 * @param {Array} points 596 * @return {Array} 597 * @private 598 */ 599 Room3D.prototype.getReversedArray = function (points) { 600 points = points.slice(0); 601 return points.reverse(); 602 } 603 604 /** 605 * Returns the room height at the given point. 606 * @param {number} x 607 * @param {number} y 608 * @return {number} 609 * @private 610 */ 611 Room3D.prototype.getRoomHeightAt = function (x, y) { 612 var smallestDistance = Infinity; 613 var room = this.getUserData(); 614 var home = this.getHome(); 615 var roomLevel = room.getLevel(); 616 var roomElevation = roomLevel !== null 617 ? roomLevel.getElevation() 618 : 0; 619 var roomHeight = roomElevation + 620 (roomLevel == null ? home.getWallHeight() : roomLevel.getHeight()); 621 var levels = home.getLevels(); 622 if (roomLevel == null || this.isLastLevel(roomLevel, levels)) { 623 var walls = home.getWalls(); 624 if (room.isCeilingFlat()) { 625 // Search the highest wall at last level 626 var roomHeightSet = false; 627 for (var index = 0; index < walls.length; index++) { 628 var wall = walls[index]; 629 if ((wall.getLevel() === null || wall.getLevel().isViewable()) 630 && wall.isAtLevel(roomLevel)) { 631 if (wall.getHeight() !== null) { 632 var wallHeight = wall.getHeight(); 633 if (wall.getLevel() !== null) { 634 wallHeight += wall.getLevel().getElevation(); 635 } 636 if (roomHeightSet) { 637 roomHeight = Math.max(roomHeight, wallHeight); 638 } else { 639 roomHeight = wallHeight; 640 roomHeightSet = true; 641 } 642 } 643 if (wall.getHeightAtEnd() !== null) { 644 var wallHeightAtEnd = wall.getHeightAtEnd(); 645 if (wall.getLevel() !== null) { 646 wallHeightAtEnd += wall.getLevel().getElevation(); 647 } 648 if (roomHeightSet) { 649 roomHeight = Math.max(roomHeight, wallHeightAtEnd); 650 } else { 651 roomHeight = wallHeightAtEnd; 652 roomHeightSet = true; 653 } 654 } 655 } 656 } 657 } else { 658 var closestWall = null; 659 var closestWallPoints = null; 660 var closestIndex = -1; 661 for (var index = 0; index < walls.length; index++) { 662 var wall = walls[index]; 663 if ((wall.getLevel() === null || wall.getLevel().isViewable()) 664 && wall.isAtLevel(roomLevel)) { 665 var points = wall.getPoints(); 666 for (var i = 0; i < points.length; i++) { 667 var distanceToWallPoint = java.awt.geom.Point2D.distanceSq(points[i][0], points[i][1], x, y); 668 if (distanceToWallPoint < smallestDistance) { 669 closestWall = wall; 670 closestWallPoints = points; 671 closestIndex = i; 672 smallestDistance = distanceToWallPoint; 673 } 674 } 675 } 676 } 677 678 if (closestWall != null) { 679 roomHeight = closestWall.getLevel() == null ? 0 : closestWall.getLevel().getElevation(); 680 var wallHeightAtStart = closestWall.getHeight(); 681 if (closestIndex === 0 || closestIndex === closestWallPoints.length - 1) { 682 roomHeight += wallHeightAtStart != null 683 ? wallHeightAtStart 684 : home.getWallHeight(); 685 } else { 686 if (closestWall.isTrapezoidal()) { 687 var arcExtent = closestWall.getArcExtent(); 688 if (arcExtent == null 689 || arcExtent === 0 690 || closestIndex === Math.floor(closestWallPoints.length / 2) 691 || closestIndex === Math.floor(closestWallPoints.length / 2) - 1) { 692 roomHeight += closestWall.getHeightAtEnd(); 693 } else { 694 var xArcCircleCenter = closestWall.getXArcCircleCenter(); 695 var yArcCircleCenter = closestWall.getYArcCircleCenter(); 696 var xClosestPoint = closestWallPoints[closestIndex][0]; 697 var yClosestPoint = closestWallPoints[closestIndex][1]; 698 var centerToClosestPointDistance = java.awt.geom.Point2D.distance(xArcCircleCenter, yArcCircleCenter, xClosestPoint, yClosestPoint); 699 var xStart = closestWall.getXStart(); 700 var yStart = closestWall.getYStart(); 701 var centerToStartPointDistance = java.awt.geom.Point2D.distance(xArcCircleCenter, yArcCircleCenter, xStart, yStart); 702 var scalarProduct = (xClosestPoint - xArcCircleCenter) * (xStart - xArcCircleCenter) 703 + (yClosestPoint - yArcCircleCenter) * (yStart - yArcCircleCenter); 704 scalarProduct /= (centerToClosestPointDistance * centerToStartPointDistance); 705 var arcExtentToClosestWallPoint = Math.acos(scalarProduct) * (arcExtent > 0 ? 1 : (arcExtent < 0 ? -1 : 0));; 706 roomHeight += wallHeightAtStart 707 + (closestWall.getHeightAtEnd() - wallHeightAtStart) * arcExtentToClosestWallPoint / arcExtent; 708 } 709 } else { 710 roomHeight += (wallHeightAtStart != null ? wallHeightAtStart : home.getWallHeight()); 711 } 712 } 713 } 714 } 715 } 716 return roomHeight; 717 } 718 719 /** 720 * Returns <code>true</code> if the given level is the last level in home. 721 * @param {Level} level 722 * @param {Array} levels 723 * @return {boolean} 724 * @private 725 */ 726 Room3D.prototype.isLastLevel = function(level, levels) { 727 return levels.indexOf(level) === levels.length - 1; 728 } 729 730 /** 731 * Returns the selection geometry of this room. 732 * @return {IndexedGeometryArray3D} 733 * @private 734 */ 735 Room3D.prototype.createRoomSelectionGeometry = function() { 736 var room = this.getUserData(); 737 var roomLevel = room.getLevel(); 738 var levels = this.getHome().getLevels(); 739 var floorBottomElevation; 740 var roomElevation; 741 if (roomLevel != null) { 742 roomElevation = roomLevel.getElevation(); 743 floorBottomElevation = roomElevation - roomLevel.getFloorThickness(); 744 } else { 745 roomElevation = 0; 746 floorBottomElevation = 0; 747 } 748 var firstLevelElevation; 749 if (levels.length == 0) { 750 firstLevelElevation = 0; 751 } else { 752 firstLevelElevation = levels [0].getElevation(); 753 } 754 var floorVisible = room.isFloorVisible(); 755 var floorBottomVisible = floorVisible 756 && roomLevel != null 757 && roomElevation != firstLevelElevation; 758 759 var roomPoints = room.getPoints(); 760 var ceilingVisible = room.isCeilingVisible(); 761 if (!floorVisible && !ceilingVisible) { 762 // If floor and ceiling not visible, draw at least floor contour for feedback 763 floorVisible = true; 764 } 765 var selectionCoordinates = new Array(roomPoints.length * ((floorVisible ? (floorBottomVisible ? 2 : 1) : 0) 766 + (ceilingVisible ? 1 : 0))); 767 var indices = new Array ((floorVisible ? (floorBottomVisible ? roomPoints.length * 6 : roomPoints.length * 2) : 0) 768 + (ceilingVisible ? (roomPoints.length * 2) : 0)); 769 var j = 0, k = 0; 770 if (floorVisible) { 771 // Contour at room elevation 772 for (var i = 0; i < roomPoints.length; i++, j++) { 773 selectionCoordinates [j] = vec3.fromValues(roomPoints [i][0], roomElevation, roomPoints [i][1]); 774 indices [k++] = j; 775 if (i > 0) { 776 indices [k++] = j; 777 } 778 } 779 indices [k++] = 0; 780 781 if (floorBottomVisible) { 782 // Contour at floor bottom 783 for (var i = 0; i < roomPoints.length; i++, j++) { 784 selectionCoordinates [j] = vec3.fromValues(roomPoints [i][0], floorBottomElevation, roomPoints [i][1]); 785 indices [k++] = j; 786 if (i > 0) { 787 indices [k++] = j; 788 } 789 } 790 indices [k++] = roomPoints.length; 791 792 for (var i = 0; i < roomPoints.length; i++) { 793 indices [k++] = i; 794 indices [k++] = i + roomPoints.length; 795 } 796 } 797 } 798 799 if (ceilingVisible) { 800 // Contour at room ceiling 801 for (var i = 0; i < roomPoints.length; i++, j++) { 802 selectionCoordinates [j] = vec3.fromValues(roomPoints [i][0], this.getRoomHeightAt(roomPoints [i][0], roomPoints [i][1]), roomPoints [i][1]); 803 indices [k++] = j; 804 if (i > 0) { 805 indices [k++] = j; 806 } 807 } 808 indices [k++] = selectionCoordinates.length - roomPoints.length; 809 } 810 811 return new IndexedLineArray3D(selectionCoordinates, indices); 812 } 813 814 /** 815 * Sets room appearance with its color, texture. 816 * @param {boolean} waitTextureLoadingEnd 817 * @private 818 */ 819 Room3D.prototype.updateRoomAppearance = function(waitTextureLoadingEnd) { 820 var room = this.getUserData(); 821 var ignoreFloorTransparency = room.getLevel() == null || room.getLevel().getElevation() <= 0; 822 this.updateRoomPartAppearance(this.getChild(Room3D.FLOOR_PART).getAppearance(), 823 room.getFloorTexture(), waitTextureLoadingEnd, room.getFloorColor(), room.getFloorShininess(), room.isFloorVisible(), ignoreFloorTransparency, true); 824 var numChildren = this.getChildren().length; 825 if (numChildren > 2) { 826 var ignoreCeillingTransparency = room.getLevel() == null; 827 this.updateRoomPartAppearance(this.getChild(Room3D.CEILING_PART).getAppearance(), 828 room.getCeilingTexture(), waitTextureLoadingEnd, room.getCeilingColor(), room.getCeilingShininess(), room.isCeilingVisible(), ignoreCeillingTransparency, false); 829 } 830 var selectionShapeAppearance = this.getChild(numChildren > 2 ? 2 : 1).getAppearance(); 831 selectionShapeAppearance.setVisible(this.getUserPreferences() != null 832 && this.getUserPreferences().isEditingIn3DViewEnabled() 833 && this.getHome().isItemSelected(room)); 834 } 835 836 /** 837 * Sets room part appearance with its color, texture and visibility. 838 * @param {Appearance3D} roomPartAppearance 839 * @param {HomeTexture} roomPartTexture 840 * @param {boolean} waitTextureLoadingEnd 841 * @param {number} roomPartColor 842 * @param {number} shininess 843 * @param {boolean} visible 844 * @param {boolean} ignoreTransparency 845 * @param {boolean} floor 846 * @private 847 */ 848 Room3D.prototype.updateRoomPartAppearance = function(roomPartAppearance, roomPartTexture, waitTextureLoadingEnd, roomPartColor, shininess, visible, ignoreTransparency, floor) { 849 if (roomPartTexture == null) { 850 this.updateAppearanceMaterial(roomPartAppearance, roomPartColor, roomPartColor, shininess); 851 roomPartAppearance.setTextureImage(null); 852 } else { 853 this.updateAppearanceMaterial(roomPartAppearance, Object3DBranch.DEFAULT_COLOR, Object3DBranch.DEFAULT_AMBIENT_COLOR, shininess); 854 var room = this.getUserData(); 855 if (roomPartTexture.isFittingArea()) { 856 this.updateTextureTransformFittingArea(roomPartAppearance, roomPartTexture, room.getPoints(), floor); 857 } else { 858 this.updateTextureTransform(roomPartAppearance, roomPartTexture, true); 859 } 860 TextureManager.getInstance().loadTexture(roomPartTexture.getImage(), waitTextureLoadingEnd, { 861 textureUpdated : function(texture) { 862 roomPartAppearance.setTextureImage(texture); 863 }, 864 textureError : function(error) { 865 return this.textureUpdated(TextureManager.getInstance().getErrorImage()); 866 } 867 }); 868 } 869 var upperRoomsAlpha = this.getHome().getEnvironment().getWallsAlpha(); 870 if (ignoreTransparency || upperRoomsAlpha === 0) { 871 roomPartAppearance.setTransparency(0); 872 } else { 873 roomPartAppearance.setTransparency(upperRoomsAlpha); 874 } 875 roomPartAppearance.setVisible(visible); 876 }