474 lines
13 KiB
JavaScript
474 lines
13 KiB
JavaScript
|
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;
|