284 lines
8.6 KiB
JavaScript
284 lines
8.6 KiB
JavaScript
|
class Controls {
|
||
|
constructor(object) {
|
||
|
|
||
|
// object is the object controlled by the controls, in our case, the camera.
|
||
|
this.object = object;
|
||
|
|
||
|
// 3D coordinates where the camera is looking at
|
||
|
this.target = new THREE.Vector3(0, 0, 0);
|
||
|
|
||
|
// Unit vectors of the local camera frame
|
||
|
// forward is in the z-direction
|
||
|
// left point towards the x-direction
|
||
|
// (a vector towards y is not necessary)
|
||
|
// in the direction of the angles
|
||
|
this.forward = new THREE.Vector3(0,0,0);
|
||
|
this.left = new THREE.Vector3(0,0,0);
|
||
|
|
||
|
// speed at which the camera will translate
|
||
|
this.speed = 50;
|
||
|
|
||
|
// speed at which the camera will rotate
|
||
|
this.sensitivity = 0.004;
|
||
|
|
||
|
// angles of the camera
|
||
|
this.angles = {
|
||
|
delta: 0,
|
||
|
theta: 0,
|
||
|
};
|
||
|
|
||
|
// the position of the camera is this.object.position
|
||
|
|
||
|
this.vectorsFromAngles();
|
||
|
|
||
|
// currently pressed keys
|
||
|
this.keys = {
|
||
|
up: false,
|
||
|
down: false,
|
||
|
left: false,
|
||
|
right: false,
|
||
|
};
|
||
|
|
||
|
// whether the mouse is being pressed or not
|
||
|
this.mouseClicked = false;
|
||
|
|
||
|
// whether there is a transition running or not
|
||
|
// if not, this.t is NaN
|
||
|
// otherwise, this.t is in [0, 1]
|
||
|
this.t = NaN;
|
||
|
|
||
|
// calls this.keyChange(true) when a key is pressed
|
||
|
document.addEventListener('keydown', this.keyChange(true));
|
||
|
|
||
|
// calls this.keyChange(false) when a key is released
|
||
|
document.addEventListener('keyup', this.keyChange(false));
|
||
|
|
||
|
// calls this.mouseDown(e) when a mouse button is pressed
|
||
|
document.addEventListener('mousedown', (e) => this.mouseDown(e));
|
||
|
|
||
|
// sets this.mouseClicked to false when a mouse button is released
|
||
|
document.addEventListener('mouseup', () => this.mouseClicked = false);
|
||
|
|
||
|
// calls this.mouseMove(e) when the mouse is moved
|
||
|
document.addEventListener('mousemove', (e) => this.mouseMove(e));
|
||
|
}
|
||
|
|
||
|
// rotates the camera to make it look to its target.
|
||
|
look() {
|
||
|
this.object.lookAt(this.target);
|
||
|
}
|
||
|
|
||
|
// updates this.forward, this.left and this.target
|
||
|
// from this.angles.delta and this.angles.theta
|
||
|
|
||
|
// this.forward is the vector moving direction
|
||
|
// this.left is the vector moving sideways to the left
|
||
|
// this.target is the point that the camera is looking
|
||
|
vectorsFromAngles() {
|
||
|
// TODO Part 1
|
||
|
}
|
||
|
|
||
|
|
||
|
// computes this.angles.delta and this.angles.theta from this.forward
|
||
|
// (may be useless)
|
||
|
anglesFromVectors() {
|
||
|
// TODO Part 2
|
||
|
}
|
||
|
|
||
|
// updates this.keys when a key is pressed or released
|
||
|
keyChange(setting) {
|
||
|
return (event) => {
|
||
|
switch (event.keyCode) {
|
||
|
case 37: case 81: this.keys.left = setting; break;
|
||
|
case 38: case 90: this.keys.up = setting; break;
|
||
|
case 39: case 68: this.keys.right = setting; break;
|
||
|
case 40: case 83: this.keys.down = setting; break;
|
||
|
case 13:
|
||
|
console.log("Position:", this.object.position);
|
||
|
console.log("Target:", this.target);
|
||
|
break;
|
||
|
}
|
||
|
};
|
||
|
}
|
||
|
|
||
|
// sets mouseClicked to true, and searches for a bookmark being clicked
|
||
|
mouseDown(event) {
|
||
|
this.mouseClicked = true;
|
||
|
|
||
|
let mouse = {
|
||
|
x: ( event.clientX / window.innerWidth ) * 2 - 1,
|
||
|
y: - ( event.clientY / window.innerHeight ) * 2 + 1,
|
||
|
};
|
||
|
|
||
|
// update the picking ray with the camera and mouse position
|
||
|
raycaster.setFromCamera(mouse, camera);
|
||
|
|
||
|
// calculate objects intersecting the picking ray
|
||
|
var intersects = raycaster.intersectObjects(scene.children, true);
|
||
|
|
||
|
if (this.testIntersection(intersects)) {
|
||
|
this.bookmarkClicked(intersects[0].object.parent);
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
// tests that a bookmark is clicked or not
|
||
|
//
|
||
|
// this function filters out other objects of the scene.
|
||
|
testIntersection(intersects) {
|
||
|
if (intersects.length === 0) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (intersects[0].object === undefined) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (! (intersects[0].object.parent instanceof Bookmark)) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (this.object.position.distanceTo(intersects[0].object.parent.position) < 10) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
this.bookmarkClicked(intersects[0].object.parent);
|
||
|
}
|
||
|
|
||
|
// initializes the paramters for the transition to a bookmark.
|
||
|
bookmarkClicked(bookmark) {
|
||
|
// TODO Part 2
|
||
|
}
|
||
|
|
||
|
// changes this.angles depending of event.movementX and event.movementY
|
||
|
//
|
||
|
// event.movementX indicates how many pixels the mouse has moved to the right
|
||
|
// event.movementY indicates how many piels the mouse has moved to the bottom
|
||
|
// both those values can be negative to indicate a motion in the opposite direction
|
||
|
mouseMove(event) {
|
||
|
// TODO Part 1
|
||
|
}
|
||
|
|
||
|
// called at every frame
|
||
|
update() {
|
||
|
|
||
|
// No transition : use keyboard and mouse to update the camera
|
||
|
if (isNaN(this.t)) {
|
||
|
|
||
|
// TODO Part 1
|
||
|
|
||
|
if (this.keys.up) {
|
||
|
// ...
|
||
|
}
|
||
|
|
||
|
} else {
|
||
|
// Transition to bookmark
|
||
|
// TODO Part 2
|
||
|
|
||
|
}
|
||
|
|
||
|
// update the camera parameters
|
||
|
this.look();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
let container, controls;
|
||
|
let camera, scene, renderer;
|
||
|
let raycaster = new THREE.Raycaster();
|
||
|
|
||
|
init();
|
||
|
animate();
|
||
|
|
||
|
function init() {
|
||
|
|
||
|
container = document.createElement('div');
|
||
|
document.body.appendChild(container);
|
||
|
camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 100, 50000);
|
||
|
camera.position.y = 1000;
|
||
|
camera.position.z = 1000;
|
||
|
|
||
|
scene = new THREE.Scene();
|
||
|
let ambientLight = new THREE.AmbientLight(0xcccccc, 0.4);
|
||
|
scene.add(ambientLight);
|
||
|
let pointLight = new THREE.PointLight(0xffffff, 0.8);
|
||
|
camera.add(pointLight);
|
||
|
scene.add(camera);
|
||
|
|
||
|
let geometry = new THREE.PlaneGeometry(20000, 20000);
|
||
|
for (let index = 0; index < geometry.faceVertexUvs[0].length; index++) {
|
||
|
geometry.faceVertexUvs[0][index][0].multiplyScalar(10);
|
||
|
geometry.faceVertexUvs[0][index][1].multiplyScalar(10);
|
||
|
geometry.faceVertexUvs[0][index][2].multiplyScalar(10);
|
||
|
}
|
||
|
let texture = new THREE.TextureLoader().load('assets/windfall/water.png');
|
||
|
texture.wrapS = THREE.RepeatWrapping;
|
||
|
texture.wrapT = THREE.RepeatWrapping;
|
||
|
let material = new THREE.MeshBasicMaterial({map: texture});
|
||
|
let sea = new THREE.Mesh(geometry, material);
|
||
|
sea.rotation.x = -Math.PI / 2;
|
||
|
scene.add(sea);
|
||
|
|
||
|
// controls
|
||
|
controls = new Controls(camera);
|
||
|
|
||
|
// model
|
||
|
let directory = "assets/windfall/";
|
||
|
let modelName = "Windfall";
|
||
|
|
||
|
let onProgress = function (xhr) {
|
||
|
if (xhr.lengthComputable) {
|
||
|
let percentComplete = xhr.loaded / xhr.total * 100;
|
||
|
console.log(Math.round(percentComplete, 2) + '% downloaded');
|
||
|
}
|
||
|
};
|
||
|
|
||
|
let onError = function () { };
|
||
|
let manager = new THREE.LoadingManager();
|
||
|
|
||
|
new THREE.MTLLoader(manager)
|
||
|
.setPath(directory)
|
||
|
.load(modelName + ".mtl", function (materials) {
|
||
|
materials.preload();
|
||
|
new THREE.OBJLoader(manager)
|
||
|
.setMaterials(materials)
|
||
|
.setPath(directory)
|
||
|
.load(modelName + ".obj", function (object) {
|
||
|
scene.add(object);
|
||
|
}, onProgress, onError);
|
||
|
});
|
||
|
|
||
|
renderer = new THREE.WebGLRenderer();
|
||
|
renderer.setPixelRatio(window.devicePixelRatio);
|
||
|
renderer.setSize(window.innerWidth, window.innerHeight);
|
||
|
renderer.setClearColor(new THREE.Color(0x87ceeb));
|
||
|
container.appendChild(renderer.domElement);
|
||
|
|
||
|
window.addEventListener('resize', onWindowResize, false);
|
||
|
|
||
|
// Add a bookmark to the scene
|
||
|
// let position = { x: -3089.6612461510463, y: 3944.8226721499614, z: -2409.0350261969947 };
|
||
|
// let target = { x: -3088.665295409939, y: 3944.824672148628, z: -2409.124904566436 };
|
||
|
// let bookmark = new Bookmark(position, target);
|
||
|
// scene.add(bookmark);
|
||
|
}
|
||
|
|
||
|
function animate() {
|
||
|
requestAnimationFrame(animate);
|
||
|
controls.update();
|
||
|
|
||
|
for (let child of scene.children) {
|
||
|
if (child instanceof Bookmark) {
|
||
|
child.update(camera);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
renderer.render(scene, camera);
|
||
|
}
|
||
|
|
||
|
function onWindowResize() {
|
||
|
camera.aspect = window.innerWidth / window.innerHeight;
|
||
|
camera.updateProjectionMatrix();
|
||
|
renderer.setSize(window.innerWidth, window.innerHeight);
|
||
|
}
|
||
|
|