import { createPerspectiveCamera } from './components/camera.js';
import { createScene } from './components/scene.js';
import { createSpotLight } from './components/light.js';
import { createGround, createOcean, createSky, createSun, createRock } from './components/terrain.js';
import { Controller } from './components/Controller.js';
import { ColissionBoxBuilder } from './components/ColissionBoxBuilder.js';
import { BeachHouseBuilder } from './components/BeachHouseBuilder.js';
import { PierBuilder } from './components/PierBuilder.js';
import { BoatBuilder } from './components/BoatBuilder.js';
import { BallBuilder } from './components/BallBuilder.js';
import { VegetationBuilder } from './components/VegetationBuilder.js';
import { UmbrellaBuilder } from './components/UmbrellaBuilder.js';
import { ChairBuilder } from './components/ChairBuilder.js';
import { WolfBuilder } from './components/WolfBuilder.js';
import { SharkBuilder } from './components/SharkBuilder.js';
import { CrateBuilder } from './components/CrateBuilder.js';
import {
PMREMGenerator,
Vector3
} from 'three';
import Stats from './../../lib/three/examples/jsm/libs/stats.module.js';
import { Resizer } from './systems/Resizer.js';
import { createRenderer } from './systems/renderer.js';
import { Loop } from './systems/Loop.js';
import { InteractionHelper } from './systems/InteractionHelper.js';
const GRAVITY = new Vector3(0, -0.05, 0);
/**
* This represents the world. The whole scene is contained and managed in this class.
*/
class World {
#camera;
#renderer;
#scene;
#light;
#loop;
#controller;
#ocean;
#ground;
#interactionHelper;
/**
* @param {HTMLDivElement} container Append the Scene to this container
*/
constructor(container) {
this.stats = new Stats();
this.stats.dom.style.display = "none";
document.body.appendChild(this.stats.dom);
this.#camera = createPerspectiveCamera(new Vector3(10, 20, 105));
this.#scene = createScene();
this.#renderer = createRenderer();
this.#controller = new Controller(this.#camera);
this.#interactionHelper = new InteractionHelper(this.#camera);
container.append(this.#renderer.domElement);
container.addEventListener("click", (ev) => {
if (!this.#controller.isLocked) {
this.#controller.lock();
}
});
const resizer = new Resizer(container, this.#camera, this.#renderer);
}
/**
* You need to call this method to initialize the world. It will create all the objects and add them to the scene.
* @param {HTMLElement} spinner The spinner will be hidden after the world is initialized
*/
async init(spinner) {
//#region Start spinner for loading process
this.setText("");
spinner.style.display = "block";
//#endregion
//#region Create World
this.#ground = createGround();
this.#ocean = createOcean();
this.#ocean.fog = this - this.#scene.fog !== undefined;
const sky = createSky();
const sun = createSun(sky);
this.#light = createSpotLight(sun);
this.#scene.add(this.#light.target);
//#endregion
//#region Create Builder
const beachHouseBuilder = new BeachHouseBuilder();
const pierBuilder = new PierBuilder();
const boatBuilder = new BoatBuilder();
const ballBuilder = new BallBuilder();
const vegetationBuilder = new VegetationBuilder();
const umbrellaBuilder = new UmbrellaBuilder();
const colissionBoxBuilder = new ColissionBoxBuilder();
const chairBuilder = new ChairBuilder();
const wolfBuilder = new WolfBuilder();
const sharkBuilder = new SharkBuilder();
const crateBuilder = new CrateBuilder();
//#endregion
//#region Create Objects for the World
this.beachHouse = await beachHouseBuilder.load(0, 10, -50, 0);
this.pier = await pierBuilder.load(10, -10, 158, 0, 15, 15, 40);
this.bridge = await pierBuilder.load(18, -10, 25, 0, 15, 15, 30);
this.rock = createRock(50, 5, -20);
this.boat = await boatBuilder.load(30, -26, 105, Math.PI);
this.crate1 = await crateBuilder.load(30, 11, 105, 0);
this.crate2 = await crateBuilder.load(20, 11, 102, Math.PI / 2);
this.crate3 = await crateBuilder.load(-55, 11.5, 28, 0);
this.crate4 = await crateBuilder.load(-55, 11.5, 30, 0);
this.crate5 = await crateBuilder.load(-55, 11.5, 32, 0);
this.crate6 = await crateBuilder.load(-55, 13.5, 30.5, 0);
this.crate7 = await crateBuilder.load(-57, 11.5, 28.5, 0);
this.soccerBall = await ballBuilder.loadSoccerBall(20, -6, -30, 0);
this.umbrella = await umbrellaBuilder.load(110, -11, 60, 0);
this.chairWithTowel = await chairBuilder.loadChairWithTowel(125, -11, 25, Math.PI / 2);
this.shark = await sharkBuilder.load(170, 1, 25, Math.PI / 2);
this.wolf = await wolfBuilder.load(-2, 12, -54, -Math.PI / 2);
this.tree0 = await vegetationBuilder.loadTree(50, 11, -30, 0);
this.tree1 = await vegetationBuilder.loadTree(-20, 11, -50, 0);
this.palm = await vegetationBuilder.loadPalmWithCoconut(90, 10.6, 25, 0);
this.acaiPalm1 = await vegetationBuilder.loadAcaiPalm(0, 8, 60, 0);
this.acaiPalm2 = await vegetationBuilder.loadAcaiPalm(30, 8, 30, 0);
this.datePalm1 = await vegetationBuilder.loadDatePalm(60, 8, 70, 0);
this.datePalm2 = await vegetationBuilder.loadDatePalm(-20, 8, 40, 0);
this.seaPlant = await vegetationBuilder.loadSeaPlant(10, 2, -5, 0);
//#endregion
//#region Create extra colission boxes for some objects
this.beachHouseBox1 = await colissionBoxBuilder.loadBox(4, 15, -57, 10, 25, 20);
this.beachHouseBox2 = await colissionBoxBuilder.loadBox(-5, 15, -64, 12, 25, 10);
this.tree0Box0 = await colissionBoxBuilder.loadBox(50, 25, -30, 7, 15, 7);
this.tree0Box1 = await colissionBoxBuilder.loadBox(50, 13, -30, 3, 10, 5);
this.tree1Box0 = await colissionBoxBuilder.loadBox(-20, 25, -50, 7, 15, 7);
this.tree1Box1 = await colissionBoxBuilder.loadBox(-20, 13, -50, 3, 10, 5);
//#endregion
//#region Add all Objects to the scene
this.#scene.add(sky);
this.#scene.add(this.#ocean);
this.#scene.add(this.#ground);
this.#scene.add(this.#light);
this.#scene.add(this.palm);
this.#scene.add(this.beachHouse);
this.#scene.add(this.pier);
this.#scene.add(this.bridge);
this.#scene.add(this.boat);
this.#scene.add(this.soccerBall);
this.#scene.add(this.tree0);
this.#scene.add(this.tree1);
this.#scene.add(this.umbrella);
this.#scene.add(this.chairWithTowel);
this.#scene.add(this.wolf);
this.#scene.add(this.wolf.box);
this.#scene.add(this.acaiPalm1);
this.#scene.add(this.acaiPalm2);
this.#scene.add(this.datePalm1);
this.#scene.add(this.datePalm2);
this.#scene.add(this.seaPlant);
this.#scene.add(this.shark);
this.#scene.add(this.crate1);
this.#scene.add(this.crate2);
this.#scene.add(this.crate3);
this.#scene.add(this.crate4);
this.#scene.add(this.crate5);
this.#scene.add(this.crate6);
this.#scene.add(this.crate7);
this.#scene.add(this.rock);
//#endregion
//#region Add objects for colission
this.#controller.addObjectForCollision(this.palm.children[0]);
this.#controller.addObjectForCollision(this.palm.children[1]);
this.#controller.addObjectForCollision(this.tree0Box0);
this.#controller.addObjectForCollision(this.tree0Box1);
this.#controller.addObjectForCollision(this.tree1Box0);
this.#controller.addObjectForCollision(this.tree1Box1);
this.#controller.addObjectForCollision(this.rock.children[0]);
this.#controller.addObjectForCollision(this.crate1.children);
this.#controller.addObjectForCollision(this.crate2.children);
this.#controller.addObjectForCollision(this.crate3.children);
this.#controller.addObjectForCollision(this.crate4.children);
this.#controller.addObjectForCollision(this.crate5.children);
this.#controller.addObjectForCollision(this.crate6.children);
this.#controller.addObjectForCollision(this.crate7.children);
this.#controller.addObjectForCollision(this.beachHouseBox1);
this.#controller.addObjectForCollision(this.beachHouseBox2);
this.#controller.addObjectForCollision(this.chairWithTowel.children[0].children);
this.#controller.addObjectForCollision(this.pier.children);
this.#controller.addObjectForCollision(this.bridge.children);
this.#controller.addObjectForCollision(this.boat.children);
this.#controller.addObjectForCollision(this.#ground);
//#endregion Add objects for colission
//#region Add object interactions
// Palm
let palmInteraction = (ev) => {
switch (ev.code) {
case 'KeyE':
this.#interactionHelper.removeInteraction(this.palm.children[2]);
this.palm.startAnimation();
this.setText("⌖");
}
};
this.#interactionHelper.addInteraction(this.palm.children[2], this.palm.setInteractionText, "keydown", palmInteraction);
// Soccer Ball
const soccerBallInteraction = (ev) => {
switch (ev.code) {
case 'KeyE':
this.#interactionHelper.removeInteraction(this.soccerBall.children[0]);
this.#loop.updatables.push(this.soccerBall);
this.setText("⌖");
let direction = this.#camera.getWorldDirection(new Vector3());
direction.y = 1;
this.soccerBall.kick(direction);
}
};
this.#interactionHelper.addInteraction(this.soccerBall.children[0], this.soccerBall.setInteractionText, "keydown", soccerBallInteraction);
// Wolf
this.#interactionHelper.addInteraction(this.wolf.box, this.wolf.sit);
// Chair with towel
this.#interactionHelper.addInteraction(this.chairWithTowel.children[0].children[0], this.chairWithTowel.setInteractionText, "keydown", (ev) => {
switch (ev.code) {
case 'KeyE':
this.setText("⌖");
this.chairWithTowel.sitOnChair((newPosition, quaternion) => { this.#controller.setFixedPosition(newPosition, quaternion) });
}
});
//#endregion Add object interactions
//#region Some other init configs
const pmremGenerator = new PMREMGenerator(this.#renderer);
this.#scene.environment = pmremGenerator.fromScene(sky).texture;
this.#loop = new Loop(this.#camera, this.#scene, this.#renderer, this);
await this.wolf.startAnimation();
await this.shark.startAnimation();
this.#loop.updatables.push(this.palm);
this.#loop.updatables.push(this.wolf);
this.#loop.updatables.push(this.shark);
this.#camera.lookAt(this.beachHouse.position);
//#endregion
// End loading spinner
spinner.style.display = "none";
}
/**
* Sets the text of the interaction text
* @param {String} text Anzuzeigender Text
*/
setText(text) {
let div = document.getElementById("info");
div.innerHTML = text;
}
/**
* This method is called every frame from the loop. It is used to update the camera position and interactions with other objects.
* It also update the animation of the water.
*/
render() {
this.#controller.update();
this.#interactionHelper.checkInteractions();
this.#ocean.material.uniforms['time'].value += 0.20 / 60.0;
this.stats.update();
}
/**
* Starts the loop for animating the scene
*/
start() {
this.#loop.start();
}
/**
* Stops the loop for animating the scene
*/
stop() {
this.#loop.stop();
}
/**
* Toggle the stats for the threejs canvas
*/
toggleStats() {
this.stats.dom.style.display = this.stats.dom.style.display == "none" ? "block" : "none";
}
}
export { World, GRAVITY };