1 /* 2 * viewModel.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 /** 22 * Loads the model at given URL and displays it in a 3D canvas. 23 * @param {string} modelUrl the URL of the model to load 24 * @param {string} modelRotation the 9 values of a 3x3 matrix 25 */ 26 function viewModelInOverlay(modelUrl, modelRotation) { 27 // Place canvas in the middle of the screen 28 var windowWidth = window.innerWidth; 29 var windowHeight = window.innerHeight; 30 var pageWidth = document.documentElement.clientWidth; 31 var pageHeight = document.documentElement.clientHeight; 32 var bodyElement = document.getElementsByTagName("body").item(0); 33 if (bodyElement && bodyElement.scrollWidth) { 34 if (bodyElement.scrollWidth > pageWidth) { 35 pageWidth = bodyElement.scrollWidth; 36 } 37 if (bodyElement.scrollHeight > pageHeight) { 38 pageHeight = bodyElement.scrollHeight; 39 } 40 } 41 var pageXOffset = window.pageXOffset ? window.pageXOffset : 0; 42 var pageYOffset = window.pageYOffset ? window.pageYOffset : 0; 43 44 var canvas = document.getElementById("canvas3D"); 45 if (!canvas) { 46 createModel3DOverlay(); 47 canvas = document.getElementById("canvas3D"); 48 } 49 50 var overlayDiv = document.getElementById("modelViewerOverlay"); 51 overlayDiv.style.height = Math.max(pageHeight, windowHeight) + "px"; 52 overlayDiv.style.width = pageWidth <= windowWidth 53 ? "100%" 54 : pageWidth + "px"; 55 overlayDiv.style.display = "block"; 56 57 var canvasSize = Math.min(800, windowWidth, windowHeight); 58 canvasSize *= 0.90; 59 canvas.width = canvasSize; 60 canvas.style.width = canvas.width + "px"; 61 canvas.height = canvasSize; 62 canvas.style.height = canvas.height + "px"; 63 var canvasLeft = pageXOffset + (windowWidth - canvasSize - 10) / 2; 64 canvas.style.left = canvasLeft + "px"; 65 var canvasTop = pageYOffset + (windowHeight - canvasSize - 10) / 2; 66 canvas.style.top = canvasTop + "px"; 67 68 var closeButtonImage = document.getElementById("modelViewerCloseButton"); 69 closeButtonImage.style.left = (canvasLeft + canvasSize - 5) + "px"; 70 closeButtonImage.style.top = (canvasTop - 10) + "px"; 71 72 var progressDiv = document.getElementById("modelViewerProgressDiv"); 73 progressDiv.style.left = (canvasLeft + (canvasSize - 300) / 2) + "px"; 74 progressDiv.style.top = (canvasTop + (canvasSize - 50) / 2) + "px"; 75 progressDiv.style.visibility = "visible"; 76 document.getElementById("modelViewerProgress").value = 0; 77 78 // Show model in canvas 79 try { 80 if (canvas.modelPreviewComponent === undefined) { 81 canvas.modelPreviewComponent = new ModelPreviewComponent("canvas3D", true); 82 } 83 var modelPreviewComponent = canvas.modelPreviewComponent; 84 var modelRotationMatrix = null; 85 if (modelRotation) { 86 var values = modelRotation.split(/\s+/); 87 modelRotationMatrix = [[parseFloat(values [0]), parseFloat(values [1]), parseFloat(values [2])], 88 [parseFloat(values [3]), parseFloat(values [4]), parseFloat(values [5])], 89 [parseFloat(values [6]), parseFloat(values [7]), parseFloat(values [8])]]; 90 } 91 modelPreviewComponent.setModel(new URLContent(modelUrl), modelRotationMatrix, 92 function(err) { 93 console.log(err); 94 alert(err); 95 }, 96 function(part, info, percentage) { 97 var progress = document.getElementById("modelViewerProgress"); 98 if (part === ModelLoader.READING_MODEL) { 99 progress.value = percentage * 100; 100 info = info.substring(info.lastIndexOf('/') + 1); 101 } else if (part === ModelLoader.PARSING_MODEL) { 102 progress.value = 210 + percentage * 100; 103 } else if (part === ModelLoader.BUILDING_MODEL) { 104 progress.value = 310 + percentage * 100; 105 } else if (part === ModelLoader.BINDING_MODEL) { 106 progress.value = 410 + percentage * 50; 107 } 108 109 var progressLabel = document.getElementById("modelViewerProgressLabel"); 110 if (part === ModelLoader.BUILDING_MODEL && percentage === 1) { 111 progressLabel.innerHTML = "Preparing display..."; 112 } else { 113 progressLabel.innerHTML = (percentage ? Math.floor(percentage * 100) + "% " : "") 114 + part + " " + info; 115 } 116 117 if (part === ModelLoader.BINDING_MODEL && percentage === 1) { 118 document.getElementById("modelViewerProgressDiv").style.visibility = "hidden"; 119 modelPreviewComponent.startRotationAnimation(); 120 } 121 }); 122 } catch (ex) { 123 hideModel3DOverlay(); 124 if (ex == "No WebGL") { 125 alert("Sorry, your browser doesn't support WebGL."); 126 } else { 127 alert("Error: " + ex); 128 } 129 } 130 } 131 132 /** 133 * Creates an overlay containing a canvas with <code>modelViewerOverlay</code> as id. 134 * @private 135 */ 136 function createModel3DOverlay() { 137 var overlayDiv = document.createElement("div"); 138 overlayDiv.setAttribute("id", "modelViewerOverlay"); 139 overlayDiv.style.display = "none"; 140 overlayDiv.style.position = "absolute"; 141 overlayDiv.style.left = "0"; 142 overlayDiv.style.top = "0"; 143 overlayDiv.style.zIndex = "100"; 144 overlayDiv.style.background = "rgba(127, 127, 127, .5)"; 145 146 var bodyElement = document.getElementsByTagName("body").item(0); 147 bodyElement.insertBefore(overlayDiv, bodyElement.firstChild); 148 149 var modelViewDiv = document.createElement("div"); 150 modelViewDiv.innerHTML = 151 '<canvas id="canvas3D" style="background-color: #CCCCCC; border: 1px solid gray; position: absolute; touch-action: none"></canvas>' 152 + '<div id="modelViewerProgressDiv" style="position:absolute; width: 300px;">' 153 + ' <progress id="modelViewerProgress" value="0" max="460" style="width: 300px;"></progress>' 154 + ' <label id="modelViewerProgressLabel" style="margin-top: 2px; font-family: Sans-serif; margin-left: 10px; margin-right: 0px; display: block;"></label>' 155 + '</div>'; 156 157 // Create close button image 158 var closeButtonImage = new Image(); 159 closeButtonImage.src = ZIPTools.getScriptFolder() + "close.png"; 160 closeButtonImage.id = "modelViewerCloseButton"; 161 closeButtonImage.style.position = "absolute"; 162 163 overlayDiv.appendChild(modelViewDiv); 164 overlayDiv.appendChild(closeButtonImage); 165 166 var hide = function(ev) { 167 hideModel3DOverlay("canvas3D"); 168 }; 169 document.addEventListener("keydown", 170 function(ev) { 171 if (ev.keyCode === 27) { 172 hide(); 173 } 174 }); 175 closeButtonImage.addEventListener("click", hide); 176 var mouseActionsListener = { 177 mousePressed : function(ev) { 178 mouseActionsListener.mousePressedInCanvas = true; 179 }, 180 mouseClicked : function(ev) { 181 if (mouseActionsListener.mousePressedInCanvas) { 182 delete mouseActionsListener.mousePressedInCanvas; 183 hide(); 184 } 185 } 186 } 187 overlayDiv.addEventListener("mousedown", mouseActionsListener.mousePressed); 188 overlayDiv.addEventListener("click", mouseActionsListener.mouseClicked); 189 overlayDiv.addEventListener("touchmove", 190 function(ev) { 191 ev.preventDefault(); 192 }); 193 } 194 195 /** 196 * Hides the overlay and clears resources. 197 * @private 198 */ 199 function hideModel3DOverlay() { 200 document.getElementById("modelViewerOverlay").style.display = "none"; 201 var modelPreviewComponent = document.getElementById("canvas3D").modelPreviewComponent; 202 if (modelPreviewComponent) { 203 modelPreviewComponent.clear(); 204 ModelManager.getInstance().clear(); 205 ZIPTools.clear(); 206 } 207 } 208 209 /** 210 * Replaces the href attribute of the element matching the given regular expression 211 * by a call to <code>viewModel3D</code> with the link in parameter. 212 * @param linkRegex 213 * @ignore 214 */ 215 function bindAnchorsToModel3DViewer(linkRegex) { 216 var anchors = document.getElementsByTagName("a"); 217 for (var i = 0; i < anchors.length; i++) { 218 var anchor = anchors[i]; 219 var url = anchor.getAttribute("href"); 220 if (url !== null && url.match(linkRegex)) { 221 anchor.onclick = function () { 222 viewModelInOverlay(this.getAttribute("href"), this.getAttribute("data-model-rotation")); 223 return false; 224 }; 225 } 226 } 227 } 228 229