init
This commit is contained in:
474
js/controls.js
vendored
Executable file
474
js/controls.js
vendored
Executable 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
7
js/controls/dimensions.js
Executable file
@ -0,0 +1,7 @@
|
||||
let _w = window.innerWidth;
|
||||
let _h = window.innerHeight;
|
||||
|
||||
export default {
|
||||
"w": _w,
|
||||
"h": _h
|
||||
};
|
116
js/controls/initDesktopEventListeners.js
Executable file
116
js/controls/initDesktopEventListeners.js
Executable 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);
|
||||
}
|
94
js/controls/initMobileEventListeners.js
Executable file
94
js/controls/initMobileEventListeners.js
Executable 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
22
js/getUrlParams.js
Executable 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
589
js/interface.js
Executable 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
9
js/isMobile.js
Executable 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
81
js/lights.js
Executable 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
383
js/loader.js
Executable 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
63
js/three_modules.js
Executable 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
|
||||
};
|
Reference in New Issue
Block a user