This commit is contained in:
2021-01-17 17:17:19 +01:00
commit ceac92cad0
137 changed files with 9900 additions and 0 deletions

474
js/controls.js vendored Executable file
View File

@ -0,0 +1,474 @@
import {
Vector3,
Raycaster,
Object3D,
Mesh,
BackSide,
MeshBasicMaterial,
ShaderMaterial,
AdditiveBlending,
JSONLoader,
AxesHelper,
BoxGeometry
} from "./three_modules";
const overlayInfo = (function () {
"use strict";
let d = document.getElementById("overlay-info");
return {
animIn: function (heading, text) {
d.innerHTML = "<h3>" + heading.charAt(0).toUpperCase() + heading.slice(1) + "</h3><h5>" + text + "</h5>";
d.classList.add("overlayIN");
},
animOut: function () {
d.classList.remove("overlayIN");
}
}
})();
const helpers = {
//Clamp a number to two decimals
twoDecimals: function (value) {
"use strict";
return Math.round((value * 100)) / 100;
},
//Linear Interpolate between two numbers
lerp: function (a, b, f) {
"use strict";
return a + f * (b - a);
},
//Clamp a number to the min and max
clip: function (number, min, max) {
"use strict";
return Math.max(min, Math.min(number, max));
},
//Easing Type
easeInOutCubic: function (t) {
"use strict";
return t < 0.5 ? 4 * t * t * t : (t - 1) * (2 * t - 2) * (2 * t - 2) + 1;
}
};
function initMobileEventListeners(scope) {
import("./controls/initMobileEventListeners").then(mod => {
mod.default(scope);
});
}
function initDesktopEventListeners(scope) {
import("./controls/initDesktopEventListeners").then(mod => {
mod.default(scope);
});
}
var w = window.innerWidth;
var h = window.innerHeight;
window.addEventListener("resize", function () {
"use strict";
w = window.innerWidth;
h = window.innerHeight;
});
class archVizControls {
constructor(scene, camera, renderer) {
"use strict";
this.scene = scene;
this.camera = camera;
this.domElement = (renderer.domElement !== undefined) ? renderer.domElement : document;
this.debug = false;
this.saveCamTransform = true;
//General Stuff
this.type = "fp",
this.lookSpeed = 0.5,
this.camera,
this.target,
this.dddcursor = new Mesh(new BoxGeometry(2, 2, 2), new MeshBasicMaterial());
//Limiting Angles
this.maxAzimuthAngle = -1337,
this.minAzimuthAngle = 1337,
this.maxPolarAngle = -1,
this.minPolarAngle = 1;
//Auto Rotation Stuff
this.autoRotate = (localStorage.autoRotate === "true") || true,
this.autoRotateMaxSpeed = 0.001,
//Drag Stuff
this.drag = (localStorage.drag === "true") || true,
this.dragStrength = 0.05,
this.dragMultiplier = 2,
this.dragVector = {
x: 0,
y: 0
};
//Mouse events
this.mVector = {
x: 0,
y: 0
},
this.mouseMove = {
x: 0,
y: 0
},
this.m1 = {
x: 0,
y: 0
};
this.nm = [0, 0];
this.mouseArray = [...new Array(10)].fill([0, 0]);
//MOBILE STUFF
this.mobile = false;
this.deviceorientation = (localStorage.deviceorientation === "true") || false;
this.deviceorientationSpeed = 0.03;
this.alpha = 0;
this.beta = 0;
this.gamma = 0;
//Raycaster stuff
this.activeObject = {
name: "",
uuid: "",
text: ""
},
this.outlineObject = new Object3D();
this.outlineObject.name = "outlineObject";
this.outlineMaterial = new ShaderMaterial({
uniforms: {},
vertexShader: `
void main()
{
vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );
vec4 displacement = vec4( normalize( normalMatrix * normal ) * 0.02, 0.0 ) + mvPosition;
gl_Position = projectionMatrix * displacement;
}`,
fragmentShader: `
void main()
{
gl_FragColor = vec4( 1.0, 1.0, 1.0, 1.0 );
}`,
blending: AdditiveBlending,
side: BackSide
});
this.anim = (function () {
let anim, time = 0,
percent;
return function (from, to, type) {
let finished = function () {
this.type = type;
time = 0;
}.bind(this);
if (this.type !== "anim") {
this.type = "anim";
anim = function () {
time++;
if (time <= 70) {
window.requestAnimationFrame(anim);
percent = helpers.easeInOutCubic(time / 70);
this.target.position.x = helpers.lerp(from[0], to[0], percent);
this.target.position.y = helpers.lerp(from[1], to[1], percent);
this.target.position.z = helpers.lerp(from[2], to[2], percent);
this.camera.position.x = helpers.lerp(from[3], to[3], percent);
this.camera.position.y = helpers.lerp(from[4], to[4], percent);
this.camera.position.z = helpers.lerp(from[5], to[5], percent);
this.target.rotation.y = helpers.lerp(from[6], to[6], percent);
} else {
this.minPolarAngle = to[7];
this.maxPolarAngle = to[8];
this.minAzimuthAngle = to[9];
this.maxAzimuthAngle = to[10];
finished();
}
}.bind(this)
anim();
};
}
}.bind(this))();
this.update = (function () {
let applyDeviceOrientation = function () {
this.mVector.x += this.alpha * this.deviceorientationSpeed * 0.1;
this.mVector.y += this.beta * this.deviceorientationSpeed * 0.1;
}.bind(this);
let mouseOver = (function () {
let obj, INTERSECTED, raycaster = new Raycaster();
raycaster.far = 50;
return function () {
//GET THE OBJECT UNDER THE CURSOR
raycaster.setFromCamera(this.m1, this.camera);
this.intersects = raycaster.intersectObjects(scene.children)[0];
if (this.intersects !== undefined) {
//ALIAS IT FOR FASTER REFERENCING
obj = this.intersects.object;
//IF THE OBJECT UNDER THE CURSOR IS THE FLOOR -- Most likely
if (obj.name === "floor" || obj.name === "dddcursor") {
this.outlineObject.visible = false;
this.dddcursor.visible = true;
this.dddcursor.position.set(this.intersects.point.x, this.intersects.point.y, this.intersects.point.z);
//IF THE OBJECT UNDER THE CURSOR WAS ALREADY HIGHLIGHTED
} else if (INTERSECTED === obj.uuid) {
this.dddcursor.visible = false;
} else if (obj.userData.clickable === true && this.activeObject.uuid !== obj.uuid) {
INTERSECTED = obj.uuid;
document.body.style.cursor = "pointer";
scene.remove(this.outlineObject);
this.outlineObject = obj.clone();
this.outlineObject.material = this.outlineMaterial;
scene.add(this.outlineObject);
} else {
this.outlineObject.visible = false;
this.dddcursor.visible = false;
document.body.style.cursor = "default";
INTERSECTED = null;
}
}
}.bind(this);
}).bind(this)();
let mouseMove = function () {
if (this.anim !== true) {
this.mouseArray.push([this.nm[0], this.nm[1]]);
this.mouseArray.splice(0, 1);
this.m1.x = this.nm[0];
this.m1.y = this.nm[1];
if (this.pressed === true) {
this.mVector.y += (this.mouseArray[0][0] - this.nm[0]) * -0.75 * this.lookSpeed;
this.mVector.x += (this.mouseArray[0][1] - this.nm[1]) * 0.6 * this.lookSpeed;
} else {
this.mVector.x = 0;
this.mVector.y = 0;
}
}
}.bind(this);
let drag = function () {
if (this.pressed === false && this.dragMultiplier > 0 && this.drag === true) {
this.dragMultiplier -= 0.035;
if (this.type === "fp") {
this.mVector.y += this.dragVector.y * helpers.easeInOutCubic(this.dragMultiplier);
this.mVector.x += this.dragVector.x * helpers.easeInOutCubic(this.dragMultiplier);
} else if (this.type === "orbit") {
this.mVector.y -= this.dragVector.y * helpers.easeInOutCubic(this.dragMultiplier);
this.mVector.x -= this.dragVector.x * helpers.easeInOutCubic(this.dragMultiplier);
}
} else {
this.dragMultiplier = 0;
}
}.bind(this);
let autoRotate = (function () {
let autoRotateTimer;
return function () {
if (autoRotateTimer <= 10) {
//Last Stage
this.target.rotation.y = helpers.clip((this.target.rotation.y - this.autoRotateMaxSpeed), this.minAzimuthAngle, this.maxAzimuthAngle);
//MAKE IT BOUNCE!
//INVERT THE ROTATION IF IT REACHES MIN OR MAX ROTATION
if (this.target.rotation.y === this.minAzimuthAngle) {
this.autoRotateMaxSpeed = -this.autoRotateMaxSpeed;
}
if (this.target.rotation.y === this.maxAzimuthAngle) {
this.autoRotateMaxSpeed = -this.autoRotateMaxSpeed;
}
} else if (autoRotateTimer <= 70) {
autoRotateTimer -= 1;
this.target.rotation.y = helpers.clip((this.target.rotation.y - this.autoRotateMaxSpeed * (10 / autoRotateTimer)), this.minAzimuthAngle, this.maxAzimuthAngle);
} else {
autoRotateTimer -= 1;
}
}.bind(this)
}).bind(this)();
return function () {
if (this.type !== "anim") {
applyDeviceOrientation();
mouseOver();
mouseMove();
drag();
autoRotate();
}
this.rotate();
};
}).bind(this)();
this.initialize();
}
rotate() {
this.target.rotation.y = helpers.clip(this.target.rotation.y + this.mVector.y, this.maxAzimuthAngle, this.minAzimuthAngle)
this.target.rotation.x = helpers.clip(this.target.rotation.x + this.mVector.x, this.maxPolarAngle, this.minPolarAngle);
this.mVector.x = 0;
this.mVector.y = 0;
};
}
archVizControls.prototype.setOrbit = function (target) {
if (this.type === "fp") {
this.savePosition = this.target.position.clone();
}
this.outlineObject.visible = false;
this.activeObject.uuid = this.intersects.object.uuid;
this.activeObject.name = this.intersects.object.name;
this.activeObject.text = this.intersects.object.userData.text;
overlayInfo.animIn(this.activeObject.name, this.activeObject.text);
this.dragMultiplier = 0;
if (this.lookSpeed > 0) {
this.lookSpeed = -this.lookSpeed;
}
if (this.type !== "anim") {
this.target.rotation.y = this.target.rotation.y % (Math.PI * 2);
if (this.target.rotation.y > 0 && ((target.userData.camAngle[2] + target.userData.camAngle[3]) / 2) < 0) {
this.target.rotation.y -= Math.PI * 2;
}
if (this.target.rotation.y < 0 && ((target.userData.camAngle[2] + target.userData.camAngle[3]) / 2) > 0) {
this.target.rotation.y += Math.PI * 2;
}
this.anim(
[
this.target.position.x, this.target.position.y, this.target.position.z,
this.camera.position.x, this.camera.position.y, this.camera.position.z,
this.target.rotation.y
], [
target.position.x, target.position.y + target.userData.fpos[1], target.position.z,
0, 0, target.userData.fpos[0],
(target.userData.camAngle[2] + target.userData.camAngle[3]) / 2,
target.userData.camAngle[1], target.userData.camAngle[0], target.userData.camAngle[3], target.userData.camAngle[2]
],
"orbit"
);
}
};
archVizControls.prototype.setFP = function (target) {
overlayInfo.animOut();
if (this.lookSpeed < 0) {
this.lookSpeed = -this.lookSpeed;
}
this.dragMultiplier = 0;
this.anim(
[
this.target.position.x, this.target.position.y, this.target.position.z,
this.camera.position.x, this.camera.position.y, this.camera.position.z,
this.target.rotation.y
], [
target.point.x, target.point.y + 1.6, target.point.z,
0, 0, 0,
this.target.rotation.y, 0.5, -0.5, 1337, -1337
],
"fp"
);
};
archVizControls.prototype.reset = function () {
let empty = new Object3D();
empty.point = new Vector3(0, 0, 0);
this.setFP(empty);
};
archVizControls.prototype.initialize = function () {
this.camera.position.z = 0.2;
this.target = new AxesHelper();
this.target.name = "ControlsTarget";
this.target.position.set(0, 1.3, 0);
this.target.rotation.order = "YXZ";
if (this.saveCamTransform) {
if (localStorage.camTransform) {
let _trans = localStorage.camTransform.split(",");
this.target.position.x = parseFloat(_trans[0]);
this.target.position.y = parseFloat(_trans[1]);
this.target.position.z = parseFloat(_trans[2]);
this.target.rotation.x = parseFloat(_trans[3]);
this.target.rotation.y = parseFloat(_trans[4]);
this.target.rotation.z = 0;
}
}
this.target.add(this.camera);
this.scene.add(this.target);
this.target.visible = false;
this.scene.add(this.outlineObject);
//ADD EVENTLISTENERS ACCORDING TO PLATTFORM
if (window.isMobile) {
initMobileEventListeners(this);
} else {
initDesktopEventListeners(this);
}
//LOAD THE CURSOR MODEL
new JSONLoader().load("./data/cursor.json", function (geometry) {
this.dddcursor = new Mesh(geometry, new MeshBasicMaterial());
this.scene.add(this.dddcursor);
}.bind(this));
};
export default archVizControls;

7
js/controls/dimensions.js Executable file
View File

@ -0,0 +1,7 @@
let _w = window.innerWidth;
let _h = window.innerHeight;
export default {
"w": _w,
"h": _h
};

View File

@ -0,0 +1,116 @@
import dims from "./dimensions";
export default function (scope) {
"use strict";
let downX = 0;
let downY = 0;
window.addEventListener("mousemove", function (ev) {
if (this.type !== "anim") {
scope.nm[0] = ev.clientX / dims.w * 2 - 1;
scope.nm[1] = ev.clientY / dims.h * -2 + 1;
}
}.bind(scope), false);
scope.domElement.addEventListener("mousedown", function (ev) {
downX = ev.clientX / dims.w * 2 - 1;
downY = ev.clientY / dims.h * -2 + 1;
scope.mouseArray = [
[downX, downY],
[downX, downY],
[downX, downY],
[downX, downY],
[downX, downY],
[downX, downY],
[downX, downY],
[downX, downY],
[downX, downY],
[downX, downY]
]
this.dragMultiplier = 1;
this.pressed = true;
}.bind(scope), false);
scope.domElement.addEventListener("mouseup", function (ev) {
this.pressed = false;
if (Math.abs(downX - scope.nm[0]) + Math.abs(downY - scope.nm[1]) < 0.05) {
//If we have an intersect object
if (this.intersects) {
//If the name of the object under the cursor === floor then set controls to FP, translate to Cursor pos
if (this.intersects.object.name === "floor") {
this.setFP(this.intersects);
this.activeObject.uuid = undefined;
}
//If the object under the cursor is clickable &
//not the activeObject & the camera is not animating translate the Camera to it
else if (
this.intersects.object.userData.clickable === true &&
this.intersects.object.uuid !== this.activeObject.uuid &&
this.type !== "anim") {
this.setOrbit(this.intersects.object);
}
}
} else {
this.dragMultiplier = 1.1;
this.dragVector.x = (scope.mouseArray[9][1] - scope.mouseArray[7][1]) * -1;
this.dragVector.y = scope.mouseArray[9][0] - scope.mouseArray[7][0];
}
if (this.saveCamTransform) {
localStorage.camTransform = [
this.target.position.x,
this.target.position.y,
this.target.position.z,
this.target.rotation.x,
this.target.rotation.y,
this.target.rotation.z
];
}
}.bind(scope), false);
scope.domElement.addEventListener("mouseleave", function () {
this.pressed = false;
}.bind(scope), false);
window.addEventListener("keydown", function (e) {
switch (e.key) {
case "Escape":
if (this.type === "orbit" && this.activeObject.uuid) {
this.setFP({
point: {
x: this.savePosition.x,
y: 0,
z: this.savePosition.z
}
});
}
break;
default:
break;
}
}.bind(scope), false);
}

View File

@ -0,0 +1,94 @@
export default function i(scope) {
"use strict";
let downX = 0;
let downY = 0;
scope.domElement.addEventListener("touchmove", function (ev) {
if (this.type !== "anim") {
nm[0] = ev.touches[0].clientX / w * 2 - 1;
nm[1] = ev.touches[0].clientY / h * -2 + 1;
}
}.bind(scope), false);
scope.domElement.addEventListener("touchstart", function (ev) {
downX = ev.touches[0].clientX / w * 2 - 1;
downY = ev.touches[0].clientY / h * -2 + 1;
scope.mouseArray = [
[downX, downY],
[downX, downY],
[downX, downY],
[downX, downY],
[downX, downY],
[downX, downY],
[downX, downY],
[downX, downY],
[downX, downY],
[downX, downY]
]
this.dragMultiplier = 1;
this.pressed = true;
}.bind(scope), false);
scope.domElement.addEventListener("touchend", function (ev) {
this.pressed = false;
if (Math.abs(downX - nm[0]) + Math.abs(downY - nm[1]) < 0.05) {
//If we have an intersect object
if (this.intersects) {
//If the name of the object under the cursor === floor then set controls to FP, translate to Cursor pos
if (this.intersects.object.name === "floor") {
this.setFP(this.intersects);
this.activeObject.uuid = undefined;
}
//If the object under the cursor is clickable &
//not the activeObject & the camera is not animating translate the Camera to it
else if (
this.intersects.object.userData.clickable === true &&
this.intersects.object.uuid !== this.activeObject.uuid &&
this.type !== "anim") {
this.setOrbit(this.intersects.object);
this.outlineObject.visible = false;
this.activeObject.uuid = this.intersects.object.uuid;
this.activeObject.name = this.intersects.object.name;
this.activeObject.text = this.intersects.object.userData.text;
overlayInfo.animIn(this.activeObject.name, this.activeObject.text);
}
}
} else {
this.dragMultiplier = 1.1;
this.dragVector.x = (mouseArray[9][1] - mouseArray[7][1]) * -1;
this.dragVector.y = mouseArray[9][0] - mouseArray[7][0];
}
if (this.saveCamTransform) {
localStorage.camTransform = [
this.target.position.x,
this.target.position.y,
this.target.position.z,
this.target.rotation.x,
this.target.rotation.y,
this.target.rotation.z
];
}
}.bind(scope), false);
}

22
js/getUrlParams.js Executable file
View File

@ -0,0 +1,22 @@
window.onload = function () {
"use strict";
if (!window.location.href.includes("?scene="))
window.location.href = window.location.href = "?scene=modern";
};
export default function () {
"use strict";
var params = {};
var param_array;
try {
param_array = window.location.href.split("?")[1].split("&");
} catch (e) {
console.error(e);
}
for (var i in param_array) {
let x = param_array[i].split("=");
params[x[0]] = x[1];
}
return params;
};

589
js/interface.js Executable file
View File

@ -0,0 +1,589 @@
const interfaceWrapper = document.getElementById("interface-wrapper");
const moduleWrapper = document.createElement("div");
moduleWrapper.setAttribute("id", "module-wrapper");
interfaceWrapper.appendChild(moduleWrapper);
const isMobile = new RegExp(/Android|webOS|iPhone|iPad|BlackBerry|Windows Phone|Opera Mini|IEMobile|Mobile/).test(navigator.userAgent);
const styles = ["light", "medium", "heavy"];
let currentStyle = styles[0];
if (localStorage.style) {
currentStyle = styles[localStorage.style];
} else {
localStorage.style = "0";
}
const modules = [];
//GENERAL FUNCTIONS
function loadSVG(path, callback) {
let xhr = new XMLHttpRequest();
xhr.open("GET", path, true);
// Following line is just to be on the safe side;
// not needed if your server delivers SVG with correct MIME type
xhr.overrideMimeType("image/svg+xml");
xhr.onreadystatechange = function () {
if (xhr.readyState == 4 && xhr.status === 200) {
// Required use of an anonymous callback as .open will NOT return a value but simply returns undefined in asynchronous mode
callback(xhr.responseXML.documentElement);
}
};
xhr.send();
}
function createModule(titleText) {
let div = document.createElement("div");
div.classList = ["module module-out"];
let title = document.createElement("h2");
title.innerHTML = titleText;
div.appendChild(title);
modules.push(div);
moduleWrapper.appendChild(div);
return div;
}
//STYLE SPECIFICS GENERATION
function createToggleCheckbox(scope, parent, config) {
let wrapper = document.createElement("div");
wrapper.classList = "module-row";
let header = document.createElement("h4");
header.style = "width: fit-content;position: absolute;right: 50%;";
header.innerHTML = config.name;
let container = document.createElement("label");
container.classList = "checkbox-container";
let checkBox = document.createElement("input");
if (config.default) {
checkBox.checked = "checked";
}
checkBox.type = "checkbox";
let span = document.createElement("span");
span.classList = "checkmark";
checkBox.addEventListener("input", function () {
config.callback(checkBox.checked);
}, false);
container.appendChild(checkBox);
container.appendChild(span);
wrapper.appendChild(header);
wrapper.appendChild(container);
parent.appendChild(wrapper);
}
function createToggleSwitch(scope, parent, config) {
let wrapper = document.createElement("div");
wrapper.classList = "module-row";
let header = document.createElement("h4");
header.innerHTML = config.name;
let switchContainer = document.createElement("label");
switchContainer.classList = "switch-container";
let checkbox = document.createElement("input");
checkbox.type = "checkbox";
checkbox.addEventListener("input", function () {
config.callback(checkbox.checked);
});
checkbox.checked = true //_default;
let span = document.createElement("span");
span.classList = "slider";
wrapper.appendChild(header);
switchContainer.appendChild(checkbox);
switchContainer.appendChild(span);
wrapper.appendChild(switchContainer);
parent.appendChild(wrapper);
}
function createToggleTabs(scope, parent, config) {
let wrapper = document.createElement("div");
wrapper.classList.add("module-row");
let buttonWrapper = document.createElement("div");
buttonWrapper.classList.add("toggle-wrapper");
let header = document.createElement("h4");
let left = document.createElement("div");
let pL = document.createElement("p");
pL.innerHTML = "An";
left.appendChild(pL);
left.classList = "toggle-left";
let right = document.createElement("div");
let pR = document.createElement("p");
pR.innerHTML = "Aus";
right.appendChild(pR);
left.classList = "toggle-left";
right.innerHMTL = "Aus";
right.classList = "toggle-right";
left.addEventListener("click", function () {
left.classList.add("toggle-active");
right.classList.remove("toggle-active");
config.callback(true);
});
right.addEventListener("click", function () {
left.classList.remove("toggle-active");
right.classList.add("toggle-active");
config.callback(false);
});
//SET THE DEFAULTS
if (config.default) {
left.classList.add("toggle-active");
} else {
right.classList.add("toggle-active");
}
header.innerHTML = name;
wrapper.appendChild(header);
buttonWrapper.appendChild(left);
buttonWrapper.appendChild(right);
wrapper.appendChild(buttonWrapper);
parent.appendChild(wrapper);
}
//GENERAL INTERFACE GENERATION
function createButton(scope, parent, config) {
let div = document.createElement("div");
let title = document.createElement("h4");
div.classList.add("button");
title.innerHTML = config.name;
div.appendChild(title);
parent.appendChild(div);
div.addEventListener("click", function () {
callback();
}, false);
};
function createSlider(scope, parent, config) {
let div = document.createElement("div");
div.classList.add("module-row");
let title = document.createElement("h4");
title.innerHTML = config.name + " " + config.default * 100 + " %";
let slider = document.createElement("input");
slider.value = config.default * 100;
slider.type = "range";
slider.min = config.min;
slider.max = config.max;
slider.addEventListener("input", function () {
config.callback(slider.value);
title.innerHTML = config.name + " " + slider.value + "%";
});
function handleOut() {
moduleWrapper.style.transition = "all 0s";
title.style.visibility = "visible";
interfaceWrapper.style.visibility = "hidden";
moduleWrapper.style.visibility = "hidden";
slider.style.visibility = "visible";
scope.rendering = true;
}
function handleIn() {
moduleWrapper.style.transition = "";
interfaceWrapper.style.visibility = "";
title.style.visibility = "";
moduleWrapper.style.visibility = "visible";
slider.style.visibility = "";
scope.rendering = false;
}
//HIDE THE BACKGROUND ON CHANGE
if (config.name === "Render Auflösung") {
slider.id = "render-quality-slider";
title.id = "render-quality-title";
if (isMobile) {
slider.addEventListener("touchstart", function () {
handleOut();
}, false);
slider.addEventListener("touchend", function () {
handleIn();
}, false);
slider.addEventListener("touchcancel", function () {
handleIn();
}, false);
} else {
slider.addEventListener("mousedown", function () {
handleOut();
}, false);
slider.addEventListener("mouseup", function () {
handleIn();
}, false);
}
}
div.appendChild(title);
div.appendChild(slider);
parent.appendChild(div);
}
function createToggle(scope, parent, config) {
switch (currentStyle) {
case "light":
createToggleSwitch(scope, parent, config);
break;
case "medium":
createToggleCheckbox(scope, parent, config);
break;
case "heavy":
createToggleTabs(scope, parent, config);
break;
default:
break;
}
}
function createDropdown(scope, parent, config) {
let wrapper = document.createElement("div");
wrapper.classList = "module-row";
let header = document.createElement("h4");
header.innerHTML = name;
let selectWrapper = document.createElement("div");
selectWrapper.classList = "select-wrapper";
let select = document.createElement("select");
let active;
for (let i = 0; i < config.options.length; i++) {
let _option = document.createElement("option");
_option.innerHTML = config.options[i];
_option.setAttribute("value", i);
if (localStorage.style == styles.indexOf(config.options[i])) {
_option.selected = "selected";
active = _option;
}
select.appendChild(_option);
}
select.addEventListener("change", function (ev) {
header.innerHTML = ev.srcElement.innerHTML;
config.callback(this.value);
});
wrapper.appendChild(header);
selectWrapper.append(select);
wrapper.appendChild(selectWrapper);
parent.appendChild(wrapper);
}
//EXPORT CLASS
export default class UserInterface {
constructor(lights, renderer, controls) {
var scope = this;
this.rendering = true;
this.state = false;
this.fullscreenState = false;
//SET CURRENT STYLE
interfaceWrapper.classList = currentStyle;
this.config = [
{
name: "Qualität",
children: [{
type: "slider",
mobileOnly: false,
name: "Render Auflösung",
default: renderer.getPixelRatio(),
min: 20,
max: 200,
callback: function (val) {
renderer.setPixelRatio(val / 100);
}
},
{
type: "toggle",
mobileOnly: false,
name: "High Quality Lights",
default: lights.state,
callback: function (val) {
if (val) {
lights.enable();
} else {
lights.disable();
}
}
}
]
},
{
name: "Steuerung",
children: [{
type: "slider",
mobileOnly: false,
name: "Geschwindigkeit",
default: controls.lookSpeed,
min: 0,
max: 200,
callback: function (val) {
controls.lookSpeed = val / 100;
localStorage.lookSpeed = controls.lookSpeed;
}
},
{
type: "toggle",
mobileOnly: false,
name: "Nachziehen",
default: controls.drag,
callback: function (val) {
controls.drag = val;
localStorage.drag = controls.drag;
}
},
{
type: "toggle",
mobileOnly: false,
name: "Automatisch drehen",
default: controls.autoRotate,
callback: function (val) {
controls.autoRotate = val;
localStorage.autoRotate = controls.autoRotate;
}
},
{
type: "toggle",
mobileOnly: true,
name: "Geräte Drehung nutzen",
default: controls.deviceorientation,
callback: function (val) {
controls.deviceorientation = val;
localStorage.deviceorientation = controls.deviceorientation;
}
},
{
type: "slider",
mobileOnly: true,
name: "Geräte Drehung Geschwindigkeit",
default: controls.deviceorientationSpeed * 100,
min: 10,
max: 150,
callback: function (val) {
controls.deviceorientationSpeed = val / 1000;
localStorage.deviceorientationSpeed = controls.deviceorientationSpeed;
}
},
{
type: "button",
mobileOnly: false,
name: "Kamera zurücksetzen",
callback: function () {
controls.reset();
}
}
]
},
{
name: "Interface",
children: [{
type: "dropdown",
mobileOnly: false,
name: "Stil",
options: [
"light",
"medium",
"heavy"
],
callback: function (val) {
scope.setStyle(val);
console.log("Setting val " + val);
localStorage.style = val;
}
}]
}
];
this.loadConfig();
}
loadConfig(callback) {
var scope = this;
//LOAD THE COGWHEEL OPTIONS
loadSVG("./icons/" + currentStyle + "_options.svg", (svg) => {
this.cogwheel = svg;
this.cogwheel.classList = "cogwheel";
this.cogwheel.addEventListener("click", () => {
this.toggle();
}, false);
interfaceWrapper.insertBefore(this.cogwheel, interfaceWrapper.firstChild);
});
//LOAD THE FULLSCREEN ICON
loadSVG("./icons/" + currentStyle + "_fullscreen.svg", (svg) => {
this.fullscreen = svg;
this.fullscreen.classList = "fullscreen";
this.fullscreen.addEventListener("click", () => {
this.toggleFullscreen();
}, false);
interfaceWrapper.insertBefore(this.fullscreen, interfaceWrapper.firstChild);
});
//CREATE ALL CHILDREN OF THE MODULE
for (let i = 0; i < this.config.length; i++) {
let _module = createModule(this.config[i].name);
for (let j = 0; j < this.config[i].children.length; j++) {
let obj = this.config[i].children[j];
if (isMobile === false && obj.mobileOnly === true) {
//DONT DO ANYTHING
} else {
//CREATE HAIR LINES
if (currentStyle !== "heavy") {
let hr = document.createElement("hr");
_module.appendChild(hr);
}
switch (obj.type) {
case "toggle":
createToggle(this, _module, obj);
break;
case "slider":
createSlider(this, _module, obj);
break;
case "button":
createButton(this, _module, obj);
break;
case "dropdown":
createDropdown(this, _module, obj);
break;
default:
break;
}
}
}
}
if (callback) {
callback();
}
}
toggleFullscreen() {
if (this.fullscreenState) {
this.fullscreenState = false;
if (document.exitFullscreen) {
document.exitFullscreen();
} else if (document.mozCancelFullScreen) {
document.mozCancelFullScreen();
} else if (document.webkitExitFullscreen) {
document.webkitExitFullscreen();
}
} else {
this.fullscreenState = true;
if (document.documentElement.requestFullscreen) {
document.documentElement.requestFullscreen();
} else if (document.documentElement.mozRequestFullScreen) {
document.documentElement.mozRequestFullScreen();
} else if (document.documentElement.webkitRequestFullscreen) {
document.documentElement.webkitRequestFullscreen();
} else if (document.documentElement.msRequestFullscreen) {
document.documentElement.msRequestFullscreen();
}
}
}
setStyle(style) {
var scope = this;
modules.length = 0;
currentStyle = styles[style];
interfaceWrapper.classList = currentStyle;
//REMOVE ALL CHILDREN
while (moduleWrapper.firstChild) {
moduleWrapper.removeChild(moduleWrapper.firstChild);
}
interfaceWrapper.removeChild(this.cogwheel);
interfaceWrapper.removeChild(this.fullscreen);
this.loadConfig(function () {
scope.animIn();
});
}
setRenderQuality(val) {
document.getElementById("render-quality-slider").value = Math.floor(val * 100);
document.getElementById("render-quality-title").innerHTML = "Render Auflösung " + Math.floor(val * 100) + "%";
}
animIn() {
this.state = true;
interfaceWrapper.classList.add("interface-in");
this.cogwheel.classList.add("cogwheel-in");
let i = modules.length - 1;
modules[i].classList.remove("module-out");
let int = window.setInterval(function () {
i--;
if (i >= 0) {
modules[i].classList.remove("module-out");
} else {
moduleWrapper.style.visibility = "visible";
clearInterval(int);
}
}, 100);
}
animOut() {
this.state = false;
interfaceWrapper.classList.remove("interface-in");
this.cogwheel.classList.remove("cogwheel-in");
let i = modules.length - 1;
modules[i].classList.add("module-out");
let int = window.setInterval(function () {
i--;
if (i >= 0) {
modules[i].classList.add("module-out");
} else {
moduleWrapper.style.visibility = "hidden";
clearInterval(int);
}
}, 100);
}
toggle() {
if (this.state) {
this.animOut();
this.rendering = true;
} else {
this.rendering = false;
this.animIn();
}
}
}

9
js/isMobile.js Executable file
View File

@ -0,0 +1,9 @@
let isMobile = (function () {
var check = false;
(function (a) {
if (/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge|maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino|android|ipad|playbook|silk/i.test(a) || /1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(a.substr(0, 4))) check = true;
})(navigator.userAgent || navigator.vendor || window.opera);
return check;
})();
export default isMobile;

81
js/lights.js Executable file
View File

@ -0,0 +1,81 @@
"use strict";
import {
Group,
RectAreaLight
} from "./three_modules";
/*
TODO:
- Loading light configuration from scene file
*/
export default class Lights {
constructor(scene) {
this.state = false;
this.lightgroup = new Group();
let intensity = 1;
let decay = 20;
let rectLight = new RectAreaLight(0xc6ffe5, intensity, 2.15, 2.35);
rectLight.decay = decay;
rectLight.position.set(4.76, 1.35, -4.08);
rectLight.rotation.set(0, -1.57, 0);
this.lightgroup.add(rectLight);
rectLight = new RectAreaLight(0xc6ffe5, intensity, 2.15, 2.35);
rectLight.decay = decay;
rectLight.position.set(4.76, 1.35, -6.49);
rectLight.rotation.set(0, -1.57, 0);
this.lightgroup.add(rectLight);
rectLight = new RectAreaLight(0xc6ffe5, intensity, 2.15, 2.35);
rectLight.decay = decay;
rectLight.position.set(4.76, 1.35, -1.7);
rectLight.rotation.set(0, -1.57, 0);
this.lightgroup.add(rectLight);
rectLight = new RectAreaLight(0xc6ffe5, intensity, 2.24, 2.36);
rectLight.decay = decay;
rectLight.position.set(-2.27, 1.34, -7.9);
rectLight.rotation.set(0, 0, 0);
this.lightgroup.add(rectLight);
rectLight = new RectAreaLight(0xc6ffe5, intensity, 2.67, 2.36);
rectLight.decay = decay;
rectLight.position.set(-3.80, 1.34, -6.15);
rectLight.rotation.set(0, 1.57, 0);
this.lightgroup.add(rectLight);
this.lightgroup.name = "lightgroup",
this.lightgroup.visible = false;
scene.add(this.lightgroup);
}
disable() {
this.state = false;
this.lightgroup.visible = false;
console.log("Lights disabled");
}
enable() {
this.state = true;
this.lightgroup.visible = true;
console.log("Lights enabled");
}
toggle() {
switch (this.state) {
case false:
this.enable();
break;
case true:
this.disable();
break;
}
}
}

383
js/loader.js Executable file
View File

@ -0,0 +1,383 @@
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;

63
js/three_modules.js Executable file
View File

@ -0,0 +1,63 @@
import { WebGLRenderer } from "three/src/renderers/WebGLRenderer"
import { TextureLoader } from "three/src/loaders/TextureLoader"
import { FontLoader } from "three/src/loaders/FontLoader"
import { LoadingManager } from "three/src/loaders/LoadingManager"
import { JSONLoader } from "three/src/loaders/JSONLoader"
import { MeshStandardMaterial } from "three/src/materials/MeshStandardMaterial"
import { MeshBasicMaterial } from "three/src/materials/MeshBasicMaterial"
import { ShaderMaterial } from "three/src/materials/ShaderMaterial"
import { MeshPhysicalMaterial } from "three/src/materials/MeshPhysicalMaterial"
import { PerspectiveCamera } from "three/src/cameras/PerspectiveCamera"
import { Scene } from "three/src/scenes/Scene"
import { Geometry } from "three/src/core/Geometry"
import { BufferGeometry } from "three/src/core/BufferGeometry"
import { AxesHelper } from "three/src/helpers/AxesHelper"
import { BoxGeometry } from "three/src/geometries/BoxGeometry"
import { CylinderGeometry } from "three/src/geometries/CylinderGeometry"
import { SphereGeometry } from "three/src/geometries/SphereGeometry"
import { TextGeometry } from "three/src/geometries/TextGeometry"
import { PlaneGeometry } from "three/src/geometries/PlaneGeometry"
import { Mesh } from "three/src/objects/Mesh"
import { Object3D } from "three/src/core/Object3D"
import { Group } from "three/src/objects/Group"
import { AmbientLight } from "three/src/lights/AmbientLight"
import { RectAreaLight } from "three/src/lights/RectAreaLight"
import { Raycaster } from "three/src/core/Raycaster"
import { Vector2 } from "three/src/math/Vector2";
import { Vector3 } from "three/src/math/Vector3";
import { EquirectangularReflectionMapping, RepeatWrapping, AdditiveBlending, BackSide, CineonToneMapping} from "three/src/constants";
export {
AdditiveBlending,
BackSide,
WebGLRenderer,
TextureLoader,
MeshStandardMaterial,
MeshPhysicalMaterial,
AxesHelper,
Object3D,
ShaderMaterial,
PerspectiveCamera,
CineonToneMapping,
Scene,
AmbientLight,
RectAreaLight,
MeshBasicMaterial,
Raycaster,
Vector2,
Vector3,
FontLoader,
Geometry,
Group,
BoxGeometry,
CylinderGeometry,
SphereGeometry,
JSONLoader,
TextGeometry,
Mesh,
PlaneGeometry,
BufferGeometry,
EquirectangularReflectionMapping,
RepeatWrapping,
LoadingManager
};