1import { 2 AmbientLight, 3 AxesHelper, 4 Box3, 5 Cache, 6 Color, 7 DirectionalLight, 8 HemisphereLight, 9 LoadingManager, 10 PMREMGenerator, 11 PerspectiveCamera, 12 Scene, 13 Vector3, 14 WebGLRenderer, 15 LinearToneMapping, 16 } from "three"; 17 import Stats from "three/addons/libs/stats.module.js"; 18 import { GLTFLoader } from "three/addons/loaders/GLTFLoader.js"; 19 import { OrbitControls } from "three/addons/controls/OrbitControls.js"; 20 import { EXRLoader } from "three/addons/loaders/EXRLoader.js"; 21 import { RoomEnvironment } from "three/addons/environments/RoomEnvironment.js"; 22 23 document.addEventListener("DOMContentLoaded", () => { 24 window.VIEWER = {}; 25 26 const DEFAULT_CAMERA = "[default]"; 27 const MANAGER = new LoadingManager(); 28 const Preset = { ASSET_GENERATOR: "assetgenerator" }; 29 30 Cache.enabled = true; 31 32 class Viewer { 33 constructor(el, options) { 34 this.el = el; 35 this.options = {}; 36 37 this.lights = []; 38 this.content = null; 39 this.mixer = null; 40 this.clips = []; 41 42 this.state = { 43 environment: "Neutral", 44 background: false, 45 playbackSpeed: 1.0, 46 actionStates: {}, 47 camera: DEFAULT_CAMERA, 48 wireframe: false, 49 skeleton: false, 50 grid: false, 51 autoRotate: false, 52 53 // Lights 54 punctualLights: true, 55 exposure: 0.0, 56 toneMapping: LinearToneMapping, 57 ambientIntensity: 0.3, 58 ambientColor: "#FFFFFF", 59 directIntensity: 0.8 * Math.PI, 60 directColor: "#FFFFFF", 61 bgColor: "#B8B8B8", 62 pointSize: 1.0, 63 }; 64 65 this.prevTime = 0; 66 this.stats = new Stats(); 67 this.stats.dom.height = "48px"; 68 this.backgroundColor = new Color(this.state.bgColor); 69 this.scene = new Scene(); 70 this.scene.background = this.backgroundColor; 71 const fov = options.preset === Preset.ASSET_GENERATOR ? (0.8 * 180) / Math.PI : 60; 72 const aspect = el.clientWidth / el.clientHeight; 73 this.defaultCamera = new PerspectiveCamera(fov, aspect, 0.01, 1000); 74 this.activeCamera = this.defaultCamera; 75 this.scene.add(this.defaultCamera); 76 this.renderer = window.renderer = new WebGLRenderer({ antialias: true }); 77 this.renderer.setClearColor(0xcccccc); 78 this.renderer.setPixelRatio(window.devicePixelRatio); 79 this.renderer.setSize(el.clientWidth, el.clientHeight); 80 this.pmremGenerator = new PMREMGenerator(this.renderer); 81 this.pmremGenerator.compileEquirectangularShader(); 82 this.neutralEnvironment = this.pmremGenerator.fromScene(new RoomEnvironment()).texture; 83 this.controls = new OrbitControls(this.defaultCamera, this.renderer.domElement); 84 this.controls.screenSpacePanning = true; 85 this.el.appendChild(this.renderer.domElement); 86 this.cameraCtrl = null; 87 this.cameraFolder = null; 88 this.animFolder = null; 89 this.animCtrls = []; 90 this.morphFolder = null; 91 this.morphCtrls = []; 92 this.skeletonHelpers = []; 93 this.gridHelper = null; 94 this.axesHelper = null; 95 this.addAxesHelper(); 96 this.animate = this.animate.bind(this); 97 requestAnimationFrame(this.animate); 98 window.addEventListener("resize", this.resize.bind(this), false); 99 } 100 101 animate(time) { 102 requestAnimationFrame(this.animate); 103 const dt = (time - this.prevTime) / 1000; 104 this.controls.update(); 105 this.stats.update(); 106 this.mixer && this.mixer.update(dt); 107 this.render(); 108 this.prevTime = time; 109 } 110 111 render() { 112 this.renderer.render(this.scene, this.activeCamera); 113 if (this.state.grid) { 114 this.axesCamera.position.copy(this.defaultCamera.position); 115 this.axesCamera.lookAt(this.axesScene.position); 116 this.axesRenderer.render(this.axesScene, this.axesCamera); 117 } 118 } 119 120 resize() { 121 const { clientHeight, clientWidth } = this.el.parentElement; 122 123 this.defaultCamera.aspect = clientWidth / clientHeight; 124 this.defaultCamera.updateProjectionMatrix(); 125 this.renderer.setSize(clientWidth, clientHeight); 126 127 this.axesCamera.aspect = this.axesDiv.clientWidth / this.axesDiv.clientHeight; 128 this.axesCamera.updateProjectionMatrix(); 129 this.axesRenderer.setSize(this.axesDiv.clientWidth, this.axesDiv.clientHeight); 130 } 131 132 load(url) { 133 return new Promise((resolve, reject) => { 134 const loader = new GLTFLoader(MANAGER); 135 136 loader.load( 137 url, 138 (gltf) => { 139 window.VIEWER.json = gltf; 140 const scene = gltf.scene || gltf.scenes[0]; 141 const clips = gltf.animations || []; 142 this.setContent(scene, clips); 143 resolve(gltf); 144 }, 145 undefined, 146 reject 147 ); 148 }); 149 } 150 setContent(object) { 151 object.updateMatrixWorld(); 152 const box = new Box3().setFromObject(object); 153 const size = box.getSize(new Vector3()).length(); 154 const center = box.getCenter(new Vector3()); 155 156 this.controls.reset(); 157 object.position.x -= center.x; 158 object.position.y -= center.y; 159 object.position.z -= center.z; 160 this.controls.maxDistance = size * 10; 161 this.defaultCamera.near = size / 100; 162 this.defaultCamera.far = size * 100; 163 this.defaultCamera.updateProjectionMatrix(); 164 165 if (this.options.cameraPosition) { 166 this.defaultCamera.position.fromArray(this.options.cameraPosition); 167 this.defaultCamera.lookAt(new Vector3()); 168 } else { 169 this.defaultCamera.position.copy(center); 170 this.defaultCamera.position.x += size / 2.0; 171 this.defaultCamera.position.y += size / 15.0; 172 this.defaultCamera.position.z += size / 2.0; 173 this.defaultCamera.lookAt(center); 174 } 175 176 this.axesCamera.position.copy(this.defaultCamera.position); 177 this.axesCamera.lookAt(this.axesScene.position); 178 this.axesCamera.near = size / 100; 179 this.axesCamera.far = size * 100; 180 this.axesCamera.updateProjectionMatrix(); 181 this.axesCorner.scale.set(size, size, size); 182 this.scene.add(object); 183 this.content = object; 184 this.state.punctualLights = true; 185 this.content.traverse((node) => { 186 if (node.isLight) { 187 this.state.punctualLights = false; 188 } 189 }); 190 this.updateLights();
191 this.updateEnvironment(); 192 } 193 194 updateLights() { 195 const state = this.state; 196 const lights = this.lights; 197 198 if (state.punctualLights && !lights.length) { 199 this.addLights(); 200 } 201 202 this.renderer.toneMapping = Number(state.toneMapping); 203 this.renderer.toneMappingExposure = Math.pow(2, state.exposure); 204 205 if (lights.length === 2) { 206 lights[0].intensity = state.ambientIntensity; 207 lights[0].color.set(state.ambientColor); 208 lights[1].intensity = state.directIntensity; 209 lights[1].color.set(state.directColor); 210 } 211 } 212 213 addLights() { 214 const state = this.state; 215 216 if (this.options.preset === Preset.ASSET_GENERATOR) { 217 const hemiLight = new HemisphereLight(); 218 hemiLight.name = "hemi_light"; 219 this.scene.add(hemiLight); 220 this.lights.push(hemiLight); 221 return; 222 } 223 224 const light1 = new AmbientLight(state.ambientColor, state.ambientIntensity); 225 light1.name = "ambient_light"; 226 this.defaultCamera.add(light1); 227 228 const light2 = new DirectionalLight(state.directColor, state.directIntensity); 229 light2.position.set(0.5, 0, 0.866); // ~60º 230 light2.name = "main_light"; 231 this.defaultCamera.add(light2); 232 233 this.lights.push(light1, light2); 234 } 235 236 updateEnvironment() { 237 const environment = { 238 id: "neutral", 239 name: "Neutral", 240 path: null, 241 }; 242 243 this.getCubeMapTexture(environment).then(({ envMap }) => { 244 this.scene.environment = envMap; 245 this.scene.background = this.state.background ? envMap : this.backgroundColor; 246 }); 247 } 248 249 getCubeMapTexture(environment) { 250 const { id, path } = environment; 251 if (id === "neutral") { 252 return Promise.resolve({ envMap: this.neutralEnvironment }); 253 } 254 255 // none 256 if (id === "") { 257 return Promise.resolve({ envMap: null }); 258 } 259 260 return new Promise((resolve, reject) => { 261 new EXRLoader().load( 262 path, 263 (texture) => { 264 const envMap = this.pmremGenerator.fromEquirectangular(texture).texture; 265 this.pmremGenerator.dispose(); 266 267 resolve({ envMap }); 268 }, 269 undefined, 270 reject 271 ); 272 }); 273 } 274 addAxesHelper() { 275 this.axesDiv = document.createElement("div"); 276 this.el.appendChild(this.axesDiv); 277 this.axesDiv.classList.add("axes"); 278 279 const { clientWidth, clientHeight } = this.axesDiv; 280 281 this.axesScene = new Scene(); 282 this.axesCamera = new PerspectiveCamera(50, clientWidth / clientHeight, 0.1, 10); 283 this.axesScene.add(this.axesCamera); 284 285 this.axesRenderer = new WebGLRenderer({ alpha: true }); 286 this.axesRenderer.setPixelRatio(window.devicePixelRatio); 287 this.axesRenderer.setSize(this.axesDiv.clientWidth, this.axesDiv.clientHeight); 288 289 this.axesCamera.up = this.defaultCamera.up; 290 291 this.axesCorner = new AxesHelper(5); 292 this.axesScene.add(this.axesCorner); 293 this.axesDiv.appendChild(this.axesRenderer.domElement); 294 } 295 } 296 297 class App { 298 constructor(el, file) { 299 this.options = {}; 300 this.el = el; 301 this.viewer = null; 302 this.viewerEl = null; 303 this.dropEl = el.querySelector(".dropzone"); 304 this.view(file); 305 } 306 307 createViewer() { 308 this.viewerEl = document.createElement("div"); 309 this.viewerEl.classList.add("viewer"); 310 this.dropEl.appendChild(this.viewerEl); 311 this.viewer = new Viewer(this.viewerEl, this.options); 312 return this.viewer; 313 } 314 315 view(rootFile) { 316 const viewer = this.createViewer(); 317 viewer.load(rootFile); 318 } 319 } 320 321 // const file1 = "/assets/img/closed _type_small.glb"; 322 // const file2 = "/assets/img/container_type_big.glb"; 323 // const file3 = "/assets/img/open_type_small.glb"; 324 325 const file1 = "/assets/img/closed.glb"; 326 const file2 = "/assets/img/container.glb"; 327 const file3 = "/assets/img/open.glb"; 328 329 // new App(document.body, file1); 330 // const app = new App(document.body, file2); 331 const app = new App(document.body, file3); 332 }); 333
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.