import { Vector2, Geometry, Mesh, BufferGeometry, LoadingManager, JSONLoader, TextureLoader, MeshBasicMaterial, MeshPhysicalMaterial, MeshStandardMaterial, AmbientLight, RepeatWrapping, EquirectangularReflectionMapping } from "./three_modules"; var loadingtextFrame = document.getElementById("loadingtext-frame"); function outputLog(log) { loadingtextFrame.innerHTML = log; } function finishedLoading() { loadingtextFrame.classList.add("loadingtextframe-out"); window.setTimeout(function () { loadingtextFrame.remove(); }, 500); } const Lazyloader = (function () { "use strict"; let stack = []; let loader = new TextureLoader(); let started = false; const loadAll = () => { if (!started && stack.length > 0) { let obj = stack[0]; stack.splice(0, 1); loader.load(obj.url, (tex) => { if (obj.scale !== 1) { tex.repeat.set(obj.scale, obj.scale); tex.wrapT = tex.wrapS = RepeatWrapping; } obj.material[obj.texture] = tex; requestAnimationFrame(loadAll); }); } }; return { add: function (url, texture, material, scale) { stack.push({ url: url, material: material, texture: texture, scale: scale || 1 }); }, start: loadAll }; })(); class cLoader { constructor(scene, callback) { "use strict"; this.callback = callback; this.scene = scene; this.manager = new LoadingManager(); this.texloader = new TextureLoader(this.manager); this.jsonloader = new JSONLoader(this.manager); this.manager.onProgress = function (item) { outputLog("Loading: " + item.replace("./data/", "")); }; this.manager.onLoad = function () { scene.updateMatrixWorld(); finishedLoading(); var ambientLight = new AmbientLight(0xffffff, 1.1); scene.add(ambientLight); }; } } cLoader.prototype.final = function () { "use strict"; setTimeout(Lazyloader.start, 500); this.callback(); }; cLoader.prototype.load = function (jsonpath, callback) { "use strict"; outputLog("Loading Scene Config"); //Load the entire JSON File this.callback = callback; fetch(jsonpath) .then(response => { return response.json(); }) .then(json => { this.json = json; }) .then(() => { fetch("./data/" + this.json.name + "/" + this.json.name + "_objects.json") .then(response => { return response.json(); }) .then(json => { this.objects = json; this.createScene(); }); }); }; cLoader.prototype.looper = (function () { "use strict"; let looperCount = 0; return function (array, callback, finished) { array.forEach((item, index, array) => { callback(item, () => { looperCount++; if (looperCount === array.length) { looperCount = 0; finished(); } }); }); } })(); cLoader.prototype.createScene = function () { "use strict"; this.name = this.json.name; this.scene.name = this.name; let hdris = {}; let materialArray = []; let materials = {}; let staticmeshes = []; let staticMesh = new Geometry(); const mergingMeshes = (item, callback) => { staticMesh.merge(item.mesh, item.matrix, item.materialIndex); callback(); }; const buildMaterial = (material, callback) => { let _material; //Creating the basic material switch (material.type) { case "basic": _material = new MeshBasicMaterial(); break; case "physical": _material = new MeshPhysicalMaterial(); break; default: _material = new MeshStandardMaterial(); } //Go through all properties of the material //and assign it to the new _material created above for (var key in material) { //Filter out Properties that arent values or textures if (key === "type" || key === "scale") { //Do nothing, type is set above, scale is set below } else if (key === "shading") { _material.flatShading = true; } //Filter out envMap property else if (key === "envMap") { _material.envMap = hdris[material.envMap]; } //Filter out the color attribute else if (key === "color") { _material.color.setHex(material[key]); } else if (key === "normalScale") { _material.normalScale = new Vector2(material[key][0], material[key][1]); } //If the property is a texture else if (isNaN(material[key])) { _material[key] = this.texloader.load("./data/" + this.name + "/textures/small/" + material[key]); if (material.scale && key !== "lightMap") { _material[key].repeat.set(material.scale, material.scale); _material[key].wrapT = _material[key].wrapS = RepeatWrapping; Lazyloader.add("./data/" + this.name + "/textures/" + material[key], key, _material, material.scale); } else { Lazyloader.add("./data/" + this.name + "/textures/" + material[key], key, _material); } } //If the property is a value else { _material[key] = material[key]; } } callback(_material); }; const buildGeometry = (item, callback) => { if (!this.json.materials[item.material]) { console.error("Material for " + item.name.toUpperCase() + " is missing"); } let geometry = this.jsonloader.parse(this.objects[item.name]).geometry; geometry.computeBoundingBox(); if (item.type === "static") { if (item.size) { geometry.scale(item.size); } if (item.rot) { geometry.rotateX(item.rot[0] * Math.PI / 180); geometry.rotateY(item.rot[1] * Math.PI / 180); geometry.rotateZ(item.rot[2] * Math.PI / 180); } if (item.pos) { geometry.translate(item.pos[0], item.pos[1], item.pos[2]); } } if (materials[item.material] === undefined) { buildMaterial(this.json.materials[item.material], function (material) { material.name = item.material; buildMesh(item, geometry, material, callback) }); } else { buildMesh(item, geometry, materials[item.material], callback) } }; const buildMesh = (item, geometry, material, callback) => { switch (item.type) { case "static": if (!materials[item.material]) { materialArray.push(material); materials[item.material] = materialArray.length - 1; staticmeshes.push({ "name": item.name, "mesh": geometry, "materialIndex": materialArray.length - 1 }); } else { staticmeshes.push({ "name": item.name, "mesh": geometry, "materialIndex": materials[item.material] }); } break; case "instanced": for (let i = 0; i < item.duplicates.length; i++) { let mesh = new Mesh(geometry, material); mesh.name = item.name; if (item.duplicates[i].pos) { mesh.position.set(item.duplicates[i].pos[0], item.duplicates[i].pos[1], item.duplicates[i].pos[2]); } if (item.duplicates[i].size) { mesh.scale.set(item.duplicates[i].size, item.duplicates[i].size, item.duplicates[i].size); } if (item.duplicates[i].rot) { mesh.rotation.set(item.duplicates[i].rot[0] * Math.PI / 180, item.duplicates[i].rot[1] * Math.PI / 180, item.duplicates[i].rot[2] * Math.PI / 180); } this.scene.add(mesh); } break; default: let mesh = new Mesh(geometry, material); mesh.name = item.name; if (item.pos) { mesh.position.set(item.pos[0], item.pos[1], item.pos[2]); } if (item.size) { mesh.scale.set(item.size, item.size, item.size); } if (item.rot) { mesh.rotation.set(item.rot[0] * Math.PI / 180, item.rot[1] * Math.PI / 180, item.rot[2] * Math.PI / 180); } if (item.fpos) { mesh.userData.fpos = item.fpos; } if (item.type === "clickable") { mesh.userData.clickable = true; } if (item.camAngle) { mesh.userData.camAngle = item.camAngle; } if (item.text) { mesh.userData.text = item.text; } this.scene.add(mesh); break; } callback(); }; const loadHdri = (item, callback) => { this.texloader.load( "./data/" + this.name + "/hdris/" + item.path, function (texture) { texture.mapping = EquirectangularReflectionMapping; hdris[item.name] = texture; callback(); } ); }; //Loops through the hdris, loading every single one //and setting it as a new child of the hdris variable //when finished runs the load meshes function this.looper(this.json.hdris, loadHdri, () => { //Loop through the objects, create a new material if panorama_nature //exist for the mesh. If the mesh is static put it in the staticmeshes array //else just add it to the scene this.looper(this.json.objects, buildGeometry, () => { //Merge the meshes in the static meshes array this.looper(staticmeshes, mergingMeshes, () => { //Convert to Buffergeometry staticMesh.computeVertexNormals(); let tempGeometry = new BufferGeometry().fromGeometry(staticMesh); //Create mesh from the geometry let tempMesh = new Mesh(tempGeometry, materialArray); tempMesh.name = "staticMesh"; tempMesh.userData.clickable = false; this.scene.add(tempMesh); this.final(); }); }); }); }; export default cLoader;