- new loading screen
- huge loading time reducing by simplifiying ocean
This commit is contained in:
parent
4f20dce37f
commit
dd8361cf33
10
index.html
10
index.html
@ -143,15 +143,17 @@
|
|||||||
</script>
|
</script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="container"></div>
|
<div id="loading">
|
||||||
<span id="message" class="loading">
|
<table id="labyTable"></table>
|
||||||
<div id="progressCircle" style="--progress: 0deg;">0%</div>
|
<div id="loadingMessage">Construction du labyrinthe : <span id="progress">0</span>%</div>
|
||||||
<div>
|
<div>
|
||||||
Se déplacer : ↑←↓→, ZQSD ou clic<br/>
|
Se déplacer : ↑←↓→, ZQSD ou clic<br/>
|
||||||
Sauter : ESPACE<br/>
|
Sauter : ESPACE<br/>
|
||||||
Regarder : Souris
|
Regarder : Souris
|
||||||
</div>
|
</div>
|
||||||
</span>
|
</div>
|
||||||
|
<div id="container"></div>
|
||||||
|
<span id="message"></span>
|
||||||
|
|
||||||
<script type="module" src="./main.js"></script>
|
<script type="module" src="./main.js"></script>
|
||||||
</body>
|
</body>
|
||||||
|
579
main.js
579
main.js
@ -7,48 +7,90 @@ import { GUI } from 'three/addons/libs/lil-gui.module.min.js'
|
|||||||
import { OctreeHelper } from 'three/addons/helpers/OctreeHelper.js'
|
import { OctreeHelper } from 'three/addons/helpers/OctreeHelper.js'
|
||||||
import Stats from 'three/addons/libs/stats.module.js'
|
import Stats from 'three/addons/libs/stats.module.js'
|
||||||
|
|
||||||
//import 'three-hex-tiling'
|
|
||||||
|
|
||||||
import MazeMesh from './MazeMesh.js'
|
import MazeMesh from './MazeMesh.js'
|
||||||
|
|
||||||
|
//import 'three-hex-tiling'
|
||||||
|
|
||||||
const playerHeight = 0.5;
|
|
||||||
const mazeWidth = 23
|
|
||||||
|
|
||||||
const parameters = {
|
// LOADING
|
||||||
elevation: 48,
|
|
||||||
azimuth : 53,
|
|
||||||
};
|
|
||||||
|
|
||||||
const waves = {
|
const labyWidth =23
|
||||||
A: { direction: 0, steepness: 0.05, wavelength: 3 },
|
const labyHeight = 23
|
||||||
B: { direction: 30, steepness: 0.10, wavelength: 6 },
|
|
||||||
C: { direction: 60, steepness: 0.05, wavelength: 1.5 },
|
|
||||||
};
|
|
||||||
|
|
||||||
const dev = window.location.search.includes("dev")
|
for(let y=0; y < labyHeight; y++) {
|
||||||
|
let tr = document.createElement("tr")
|
||||||
|
labyTable.appendChild(tr)
|
||||||
|
for(let x=0; x < labyHeight; x++) {
|
||||||
|
let td = document.createElement("td")
|
||||||
|
tr.appendChild(td)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const ambiance = new Audio("snd/ambiance.mp3")
|
let walls
|
||||||
ambiance.loop = true
|
|
||||||
const piano = new Audio("snd/waves-and-tears.mp3")
|
|
||||||
piano.loop = false
|
|
||||||
|
|
||||||
const loadMngr = new THREE.LoadingManager();
|
function dig(x, y) {
|
||||||
const loader = new THREE.TextureLoader(loadMngr);
|
walls[y][x] = false
|
||||||
|
window.requestAnimationFrame(() => labyTable.children[y].children[x].className = "ground")
|
||||||
|
}
|
||||||
|
|
||||||
|
const directions = [[0, 1], [0, -1], [1, 0], [-1, 0]]
|
||||||
|
function* build(x, y) {
|
||||||
|
for (let direction of Array.from(directions).sort(x => .5 - Math.random())) {
|
||||||
|
let [dx, dy] = direction
|
||||||
|
let x1 = x + dx
|
||||||
|
let y1 = y + dy
|
||||||
|
let x2 = x1 + dx
|
||||||
|
let y2 = y1 + dy
|
||||||
|
if (0 <= x2 && x2 < labyWidth && 0 <= y2 && y2 < labyHeight && walls[y2][x2]) {
|
||||||
|
dig(x1, y1)
|
||||||
|
yield x1, y1
|
||||||
|
dig(x2, y2)
|
||||||
|
yield x2, y2
|
||||||
|
yield* build(x2, y2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function* endlessLaby() {
|
||||||
|
while (true) {
|
||||||
|
for (const tr of labyTable.children) {
|
||||||
|
for (const td of tr.children) {
|
||||||
|
td.className = "wall"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
walls = Array(labyHeight).fill(true).map(row => Array(labyWidth).fill(true))
|
||||||
|
|
||||||
|
//let x0 = 2 * Math.floor(labyWidth * Math.random() / 2) + 1
|
||||||
|
//let y0 = 2 * Math.floor(labyHeight * Math.random() / 2) + 1
|
||||||
|
let x0 = Math.floor(labyWidth / 2)
|
||||||
|
let y0 = Math.floor(labyHeight / 2)
|
||||||
|
|
||||||
|
dig(x0, y0)
|
||||||
|
yield* build(x0, y0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let labyIterator = endlessLaby()
|
||||||
|
|
||||||
|
let interval = window.setInterval(() => labyIterator.next(), 200)
|
||||||
|
|
||||||
|
|
||||||
|
const loadMngr = new THREE.LoadingManager()
|
||||||
|
const loader = new THREE.TextureLoader(loadMngr)
|
||||||
|
loader.setPath("textures/")
|
||||||
loadMngr.onStart = function (url, itemsLoaded, itemsTotal) {
|
loadMngr.onStart = function (url, itemsLoaded, itemsTotal) {
|
||||||
progressCircle.innerText = "0%"
|
progress.innerText = "0"
|
||||||
progressCircle.style.setProperty("--progress", "0deg")
|
|
||||||
}
|
}
|
||||||
loadMngr.onProgress = function (url, itemsLoaded, itemsTotal) {
|
loadMngr.onProgress = function (url, itemsLoaded, itemsTotal) {
|
||||||
progressCircle.innerText = Math.floor(100 * itemsLoaded / itemsTotal) + "%"
|
progress.innerText = Math.floor(100 * itemsLoaded / itemsTotal)
|
||||||
progressCircle.style.setProperty("--progress", Math.floor(360 * itemsLoaded / itemsTotal)+"deg")
|
|
||||||
}
|
}
|
||||||
loadMngr.onError = function (url) {
|
loadMngr.onError = function (url) {
|
||||||
message.innerHTML = `Erreur de chargement :<br/>${url}`
|
loadingMessage.innerHTML = `Erreur de chargement :<br/>${url}`
|
||||||
}
|
}
|
||||||
loadMngr.onLoad = function (url, itemsLoaded, itemsTotal) {
|
loadMngr.onLoad = function (url, itemsLoaded, itemsTotal) {
|
||||||
message.innerHTML = ""
|
loading.style.display = "none"
|
||||||
message.className = ""
|
window.clearInterval(interval)
|
||||||
|
|
||||||
renderer.setAnimationLoop(animate)
|
renderer.setAnimationLoop(animate)
|
||||||
|
|
||||||
@ -56,26 +98,45 @@ loadMngr.onLoad = function (url, itemsLoaded, itemsTotal) {
|
|||||||
let x = Math.floor(8 + camera.position.x * 16 / mazeWidth)
|
let x = Math.floor(8 + camera.position.x * 16 / mazeWidth)
|
||||||
let y = Math.floor(8 + camera.position.z * 16 / mazeWidth)
|
let y = Math.floor(8 + camera.position.z * 16 / mazeWidth)
|
||||||
favicon.href = `favicon.php?x=${x}&y=${y}`
|
favicon.href = `favicon.php?x=${x}&y=${y}`
|
||||||
}, 1000);
|
}, 1000)
|
||||||
};
|
}
|
||||||
|
|
||||||
//
|
|
||||||
|
|
||||||
const container = document.getElementById('container');
|
// GAME
|
||||||
|
|
||||||
|
const playerHeight = 0.5
|
||||||
|
const mazeWidth = 23
|
||||||
|
|
||||||
|
const parameters = {
|
||||||
|
elevation: 48,
|
||||||
|
azimuth : 53,
|
||||||
|
}
|
||||||
|
|
||||||
|
const waves = {
|
||||||
|
A: { direction: 0, steepness: 0.06, wavelength: 4 },
|
||||||
|
B: { direction: 30, steepness: 0.10, wavelength: 6 },
|
||||||
|
C: { direction: 60, steepness: 0.05, wavelength: 1.5 },
|
||||||
|
}
|
||||||
|
|
||||||
|
const ambiance = new Audio("snd/ambiance.mp3")
|
||||||
|
ambiance.loop = true
|
||||||
|
const piano = new Audio("snd/waves-and-tears.mp3")
|
||||||
|
piano.loop = false
|
||||||
|
|
||||||
|
const container = document.getElementById('container')
|
||||||
|
|
||||||
const renderer = new THREE.WebGLRenderer({
|
const renderer = new THREE.WebGLRenderer({
|
||||||
powerPreference: "high-performance",
|
powerPreference: "high-performance",
|
||||||
antialias: true,
|
})
|
||||||
});
|
renderer.setPixelRatio(window.devicePixelRatio)
|
||||||
renderer.setPixelRatio(window.devicePixelRatio);
|
renderer.setSize(window.innerWidth, window.innerHeight)
|
||||||
renderer.setSize(window.innerWidth, window.innerHeight);
|
renderer.toneMapping = THREE.ACESFilmicToneMapping
|
||||||
renderer.toneMapping = THREE.ACESFilmicToneMapping;
|
renderer.shadowMap.enabled = true
|
||||||
renderer.shadowMap.enabled = true;
|
renderer.shadowMap.type = THREE.PCFSoftShadowMap
|
||||||
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
|
|
||||||
|
|
||||||
container.appendChild(renderer.domElement);
|
container.appendChild(renderer.domElement)
|
||||||
|
|
||||||
const scene = new THREE.Scene();
|
const scene = new THREE.Scene()
|
||||||
|
|
||||||
scene.background = new THREE.CubeTextureLoader(loadMngr)
|
scene.background = new THREE.CubeTextureLoader(loadMngr)
|
||||||
.setPath( 'textures/calm-sea-skybox/' )
|
.setPath( 'textures/calm-sea-skybox/' )
|
||||||
@ -86,48 +147,50 @@ scene.background = new THREE.CubeTextureLoader(loadMngr)
|
|||||||
'dn.webp',
|
'dn.webp',
|
||||||
'rt.webp',
|
'rt.webp',
|
||||||
'lf.webp',
|
'lf.webp',
|
||||||
] );
|
] )
|
||||||
scene.backgroundBlurriness = 0.03;
|
scene.backgroundBlurriness = 0.03
|
||||||
scene.backgroundIntensity = 1.4;
|
scene.backgroundIntensity = 1.4
|
||||||
scene.environment = scene.background;
|
scene.environment = scene.background
|
||||||
|
|
||||||
window.scene = scene;
|
window.scene = scene
|
||||||
|
|
||||||
const camera = new THREE.PerspectiveCamera(80, window.innerWidth / window.innerHeight, 0.1, 1000);
|
const camera = new THREE.PerspectiveCamera(80, window.innerWidth / window.innerHeight, 0.1, 1000)
|
||||||
camera.rotation.order = 'YXZ';
|
camera.rotation.order = 'YXZ'
|
||||||
camera.position.set(0, 25 + playerHeight, 0);
|
camera.position.set(0, 25 + playerHeight, 0)
|
||||||
|
|
||||||
const mazeCollisionner = new THREE.Group();
|
const mazeCollisionner = new THREE.Group()
|
||||||
|
|
||||||
// Maze
|
// Maze
|
||||||
|
|
||||||
const wallMaterial = new THREE.MeshStandardMaterial({
|
const wallMaterial = new THREE.MeshStandardMaterial({
|
||||||
map : loader.load('textures/Poly-cobblestone-wall/color_map.webp'),
|
map : loader.load('Poly-cobblestone-wall/color_map.webp'),
|
||||||
normalMap : loader.load('textures/Poly-cobblestone-wall/normal_map_opengl.webp'),
|
normalMap : loader.load('Poly-cobblestone-wall/normal_map_opengl.webp'),
|
||||||
aoMap : loader.load('textures/Poly-cobblestone-wall/ao_map.webp'),
|
aoMap : loader.load('Poly-cobblestone-wall/ao_map.webp'),
|
||||||
roughnessMap : loader.load('textures/Poly-cobblestone-wall/roughness_map.webp'),
|
roughnessMap : loader.load('Poly-cobblestone-wall/roughness_map.webp'),
|
||||||
roughness : 1
|
roughness : 1
|
||||||
})
|
})
|
||||||
|
|
||||||
const maze = new MazeMesh(mazeWidth, mazeWidth, 1, wallMaterial);
|
const maze = new MazeMesh(mazeWidth, mazeWidth, 1, wallMaterial)
|
||||||
maze.castShadow = true;
|
maze.castShadow = true
|
||||||
maze.receiveShadow = true;
|
maze.receiveShadow = true
|
||||||
maze.matrixAutoUpdate = false
|
maze.matrixAutoUpdate = false
|
||||||
scene.add(maze)
|
scene.add(maze)
|
||||||
|
|
||||||
console.log(String(maze))
|
console.log(String(maze))
|
||||||
|
|
||||||
|
const dev = window.location.search.includes("dev")
|
||||||
|
|
||||||
if (!dev) {
|
if (!dev) {
|
||||||
const invisibleWall = new THREE.Mesh(new THREE.BoxGeometry( .9, 1.8, .9 ));
|
const invisibleWall = new THREE.Mesh(new THREE.BoxGeometry( .9, 1.8, .9 ))
|
||||||
invisibleWall.material.visible = false;
|
invisibleWall.material.visible = false
|
||||||
let matrix = new THREE.Matrix4()
|
let matrix = new THREE.Matrix4()
|
||||||
|
|
||||||
for (let i = 0; i < maze.count; i++) {
|
for (let i = 0; i < maze.count; i++) {
|
||||||
maze.getMatrixAt(i, matrix)
|
maze.getMatrixAt(i, matrix)
|
||||||
const clone = invisibleWall.clone()
|
const clone = invisibleWall.clone()
|
||||||
clone.position.setFromMatrixPosition(matrix);
|
clone.position.setFromMatrixPosition(matrix)
|
||||||
clone.position.y = 1;
|
clone.position.y = 1
|
||||||
mazeCollisionner.add(clone);
|
mazeCollisionner.add(clone)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -139,11 +202,11 @@ function repeatGroundMaterial (texture) {
|
|||||||
texture.repeat.set(mazeWidth / 4, mazeWidth / 4)
|
texture.repeat.set(mazeWidth / 4, mazeWidth / 4)
|
||||||
}
|
}
|
||||||
const groundMaterial = new THREE.MeshStandardMaterial({
|
const groundMaterial = new THREE.MeshStandardMaterial({
|
||||||
map : loader.load('textures/angled-blocks-vegetation/albedo.webp', repeatGroundMaterial),
|
map : loader.load('angled-blocks-vegetation/albedo.webp', repeatGroundMaterial),
|
||||||
aoMap : loader.load('textures/angled-blocks-vegetation/ao.webp', repeatGroundMaterial),
|
aoMap : loader.load('angled-blocks-vegetation/ao.webp', repeatGroundMaterial),
|
||||||
metalnessMap: loader.load('textures/angled-blocks-vegetation/metallic.webp', repeatGroundMaterial),
|
metalnessMap: loader.load('angled-blocks-vegetation/metallic.webp', repeatGroundMaterial),
|
||||||
normalMap : loader.load('textures/angled-blocks-vegetation/normal-dx.webp', repeatGroundMaterial),
|
normalMap : loader.load('angled-blocks-vegetation/normal-dx.webp', repeatGroundMaterial),
|
||||||
roughnessMap: loader.load('textures/angled-blocks-vegetation/roughness.webp', repeatGroundMaterial),
|
roughnessMap: loader.load('angled-blocks-vegetation/roughness.webp', repeatGroundMaterial),
|
||||||
/*hexTiling : {
|
/*hexTiling : {
|
||||||
patchScale: 1,
|
patchScale: 1,
|
||||||
useContrastCorrectedBlending: true,
|
useContrastCorrectedBlending: true,
|
||||||
@ -184,29 +247,29 @@ const ground = new THREE.Mesh(
|
|||||||
groundMaterial,
|
groundMaterial,
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
ground.rotation.x = -Math.PI / 2;
|
ground.rotation.x = -Math.PI / 2
|
||||||
ground.position.y = -10
|
ground.position.y = -10
|
||||||
ground.receiveShadow = true;
|
ground.receiveShadow = true
|
||||||
ground.matrixAutoUpdate = false
|
ground.matrixAutoUpdate = false
|
||||||
ground.updateMatrix();
|
ground.updateMatrix()
|
||||||
|
|
||||||
mazeCollisionner.add(ground)
|
mazeCollisionner.add(ground)
|
||||||
|
|
||||||
scene.add(mazeCollisionner);
|
scene.add(mazeCollisionner)
|
||||||
|
|
||||||
const mazeOctree = new Octree().fromGraphNode(mazeCollisionner);
|
const mazeOctree = new Octree().fromGraphNode(mazeCollisionner)
|
||||||
|
|
||||||
// Water
|
// Water
|
||||||
|
|
||||||
const waterGeometry = new THREE.PlaneGeometry(1024, 1024, 512, 512);
|
const waterGeometry = new THREE.PlaneGeometry(512, 512, 128, 128)
|
||||||
|
|
||||||
const ocean = new Water(waterGeometry, {
|
const ocean = new Water(waterGeometry, {
|
||||||
textureWidth : 512,
|
textureWidth : 256,
|
||||||
textureHeight: 512,
|
textureHeight: 256,
|
||||||
waterNormals : loader.load(
|
waterNormals : loader.load(
|
||||||
'textures/waternormals.webp',
|
'waternormals.webp',
|
||||||
function (texture) {
|
function (texture) {
|
||||||
texture.wrapS = texture.wrapT = THREE.RepeatWrapping;
|
texture.wrapS = texture.wrapT = THREE.RepeatWrapping
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
sunDirection : new THREE.Vector3(),
|
sunDirection : new THREE.Vector3(),
|
||||||
@ -214,12 +277,12 @@ const ocean = new Water(waterGeometry, {
|
|||||||
waterColor : 0x001e0f,
|
waterColor : 0x001e0f,
|
||||||
distortionScale: 3.7,
|
distortionScale: 3.7,
|
||||||
fog : scene.fog !== undefined,
|
fog : scene.fog !== undefined,
|
||||||
alpha : 0.9
|
alpha : 0.7
|
||||||
});
|
})
|
||||||
ocean.rotation.x = - Math.PI / 2;
|
ocean.rotation.x = - Math.PI / 2
|
||||||
ocean.position.y = -0.2;
|
ocean.position.y = -0.2
|
||||||
|
|
||||||
ocean.material.transparent = true;
|
ocean.material.transparent = true
|
||||||
ocean.material.onBeforeCompile = function (shader) {
|
ocean.material.onBeforeCompile = function (shader) {
|
||||||
|
|
||||||
shader.uniforms.size = { value: 6 }
|
shader.uniforms.size = { value: 6 }
|
||||||
@ -231,7 +294,7 @@ ocean.material.onBeforeCompile = function (shader) {
|
|||||||
waves.A.steepness,
|
waves.A.steepness,
|
||||||
waves.A.wavelength,
|
waves.A.wavelength,
|
||||||
],
|
],
|
||||||
};
|
}
|
||||||
shader.uniforms.waveB = {
|
shader.uniforms.waveB = {
|
||||||
value: [
|
value: [
|
||||||
Math.sin((waves.B.direction * Math.PI) / 180),
|
Math.sin((waves.B.direction * Math.PI) / 180),
|
||||||
@ -239,7 +302,7 @@ ocean.material.onBeforeCompile = function (shader) {
|
|||||||
waves.B.steepness,
|
waves.B.steepness,
|
||||||
waves.B.wavelength,
|
waves.B.wavelength,
|
||||||
],
|
],
|
||||||
};
|
}
|
||||||
shader.uniforms.waveC = {
|
shader.uniforms.waveC = {
|
||||||
value: [
|
value: [
|
||||||
Math.sin((waves.C.direction * Math.PI) / 180),
|
Math.sin((waves.C.direction * Math.PI) / 180),
|
||||||
@ -247,50 +310,50 @@ ocean.material.onBeforeCompile = function (shader) {
|
|||||||
waves.C.steepness,
|
waves.C.steepness,
|
||||||
waves.C.wavelength,
|
waves.C.wavelength,
|
||||||
],
|
],
|
||||||
};
|
}
|
||||||
shader.vertexShader = document.getElementById('vertexShader').textContent;
|
shader.vertexShader = document.getElementById('vertexShader').textContent
|
||||||
shader.fragmentShader = document.getElementById('fragmentShader').textContent;
|
shader.fragmentShader = document.getElementById('fragmentShader').textContent
|
||||||
|
|
||||||
};
|
}
|
||||||
|
|
||||||
scene.add(ocean);
|
scene.add(ocean)
|
||||||
const oceanOctree = new Octree().fromGraphNode(ocean);
|
const oceanOctree = new Octree().fromGraphNode(ocean)
|
||||||
|
|
||||||
// Lights
|
// Lights
|
||||||
|
|
||||||
const sun = new THREE.Vector3();
|
const sun = new THREE.Vector3()
|
||||||
|
|
||||||
const ambientLight = new THREE.AmbientLight(0x404040, 5);
|
const ambientLight = new THREE.AmbientLight(0x404040, 5)
|
||||||
scene.add(ambientLight);
|
scene.add(ambientLight)
|
||||||
|
|
||||||
const sunLight = new THREE.DirectionalLight(0xffffff, 1);
|
const sunLight = new THREE.DirectionalLight(0xffffff, 1)
|
||||||
sunLight.castShadow = true;
|
sunLight.castShadow = true
|
||||||
sunLight.shadow.camera.near = 0.1;
|
sunLight.shadow.camera.near = 0.1
|
||||||
sunLight.shadow.camera.far = 1.4 * mazeWidth;
|
sunLight.shadow.camera.far = 1.4 * mazeWidth
|
||||||
sunLight.shadow.camera.left = -1.4 * mazeWidth/2;
|
sunLight.shadow.camera.left = -1.4 * mazeWidth/2
|
||||||
sunLight.shadow.camera.right = 1.4 * mazeWidth/2;
|
sunLight.shadow.camera.right = 1.4 * mazeWidth/2
|
||||||
sunLight.shadow.camera.bottom = -1.4 * mazeWidth/2;
|
sunLight.shadow.camera.bottom = -1.4 * mazeWidth/2
|
||||||
sunLight.shadow.camera.top = 1.4 * mazeWidth/2;
|
sunLight.shadow.camera.top = 1.4 * mazeWidth/2
|
||||||
sunLight.shadow.mapSize.width = 1024;
|
sunLight.shadow.mapSize.width = 1024
|
||||||
sunLight.shadow.mapSize.height = 1024;
|
sunLight.shadow.mapSize.height = 1024
|
||||||
//sunLight.shadow.radius = 0.01;
|
sunLight.shadow.radius = 0.01
|
||||||
sunLight.shadow.bias = 0.0001;
|
sunLight.shadow.bias = 0.0001
|
||||||
sunLight.target = maze
|
sunLight.target = maze
|
||||||
scene.add(sunLight);
|
scene.add(sunLight)
|
||||||
|
|
||||||
updateSun();
|
updateSun()
|
||||||
|
|
||||||
function updateSun() {
|
function updateSun() {
|
||||||
|
|
||||||
const phi = THREE.MathUtils.degToRad(90 - parameters.elevation);
|
const phi = THREE.MathUtils.degToRad(90 - parameters.elevation)
|
||||||
const theta = THREE.MathUtils.degToRad(parameters.azimuth);
|
const theta = THREE.MathUtils.degToRad(parameters.azimuth)
|
||||||
|
|
||||||
sun.setFromSphericalCoords(1.4 * mazeWidth/2, phi, theta);
|
sun.setFromSphericalCoords(1.4 * mazeWidth/2, phi, theta)
|
||||||
ocean.material.uniforms['sunDirection'].value.copy(sun).normalize();
|
ocean.material.uniforms['sunDirection'].value.copy(sun).normalize()
|
||||||
|
|
||||||
sunLight.position.copy(sun)
|
sunLight.position.copy(sun)
|
||||||
|
|
||||||
//ambientLight.intensity = 5 + 5 * Math.sin(Math.max(THREE.MathUtils.degToRad(parameters.elevation), 0));
|
//ambientLight.intensity = 5 + 5 * Math.sin(Math.max(THREE.MathUtils.degToRad(parameters.elevation), 0))
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -302,49 +365,49 @@ function repeatRaftMaterial(texture) {
|
|||||||
texture.repeat.set(2, 1)
|
texture.repeat.set(2, 1)
|
||||||
}
|
}
|
||||||
const raftMaterial = new THREE.MeshStandardMaterial({
|
const raftMaterial = new THREE.MeshStandardMaterial({
|
||||||
map: loader.load("textures/Poly-wood/color_map.webp", repeatRaftMaterial),
|
map: loader.load("Poly-wood/color_map.webp", repeatRaftMaterial),
|
||||||
aoMap: loader.load("textures/Poly-wood/ao_map.webp", repeatRaftMaterial),
|
aoMap: loader.load("Poly-wood/ao_map.webp", repeatRaftMaterial),
|
||||||
normalMap: loader.load("textures/Poly-wood/normal_map_opengl.webp", repeatRaftMaterial),
|
normalMap: loader.load("Poly-wood/normal_map_opengl.webp", repeatRaftMaterial),
|
||||||
normalScale : new THREE.Vector2(2, 2),
|
normalScale : new THREE.Vector2(2, 2),
|
||||||
roughnessMap: loader.load("textures/Poly-wood/roughness_map.webp", repeatRaftMaterial),
|
roughnessMap: loader.load("Poly-wood/roughness_map.webp", repeatRaftMaterial),
|
||||||
depthFunc: 3,
|
depthFunc: 3,
|
||||||
depthTest: true,
|
depthTest: true,
|
||||||
depthWrite: true,
|
depthWrite: true,
|
||||||
displacementMap: loader.load("textures/Poly-wood/displacement_map.webp", repeatRaftMaterial),
|
displacementMap: loader.load("Poly-wood/displacement_map.webp", repeatRaftMaterial),
|
||||||
displacementScale: -0.3,
|
displacementScale: -0.3,
|
||||||
displacementBias: 0.15,
|
displacementBias: 0.15,
|
||||||
})
|
})
|
||||||
const raft = new THREE.Mesh(raftGeometry, raftMaterial)
|
const raft = new THREE.Mesh(raftGeometry, raftMaterial)
|
||||||
raft.position.set( .25, ocean.position.y, -mazeWidth/2 - 1.1 )
|
raft.position.set( .25, ocean.position.y, -mazeWidth/2 - 1.1 )
|
||||||
raft.castShadow = true;
|
raft.castShadow = true
|
||||||
|
|
||||||
scene.add(raft);
|
scene.add(raft)
|
||||||
const raftOctree = new Octree().fromGraphNode(raft);
|
const raftOctree = new Octree().fromGraphNode(raft)
|
||||||
|
|
||||||
// GUI
|
// GUI
|
||||||
|
|
||||||
const stats = new Stats();
|
const stats = new Stats()
|
||||||
|
|
||||||
if (dev) {
|
if (dev) {
|
||||||
|
|
||||||
container.appendChild(stats.dom);
|
container.appendChild(stats.dom)
|
||||||
|
|
||||||
const gui = new GUI();
|
const gui = new GUI()
|
||||||
|
|
||||||
const lightHelper = new THREE.DirectionalLightHelper(sunLight, .5)
|
const lightHelper = new THREE.DirectionalLightHelper(sunLight, .5)
|
||||||
lightHelper.position.copy(maze.start)
|
lightHelper.position.copy(maze.start)
|
||||||
lightHelper.visible = false;
|
lightHelper.visible = false
|
||||||
|
|
||||||
const octreeHelper = new OctreeHelper(mazeOctree);
|
const octreeHelper = new OctreeHelper(mazeOctree)
|
||||||
octreeHelper.visible = false;
|
octreeHelper.visible = false
|
||||||
scene.add(octreeHelper);
|
scene.add(octreeHelper)
|
||||||
const showHelper = gui.add({ helpers: false }, "helpers")
|
const showHelper = gui.add({ helpers: false }, "helpers")
|
||||||
showHelper.onChange(function (value) {
|
showHelper.onChange(function (value) {
|
||||||
|
|
||||||
lightHelper.visible = value;
|
lightHelper.visible = value
|
||||||
octreeHelper.visible = value;
|
octreeHelper.visible = value
|
||||||
|
|
||||||
});
|
})
|
||||||
|
|
||||||
const cameraFolder = gui.addFolder("camera")
|
const cameraFolder = gui.addFolder("camera")
|
||||||
cameraFolder.add(camera, "focus", 0, 200).onChange(() => camera.updateProjectionMatrix())
|
cameraFolder.add(camera, "focus", 0, 200).onChange(() => camera.updateProjectionMatrix())
|
||||||
@ -360,91 +423,91 @@ if (dev) {
|
|||||||
raftRotationFolder.add(raft.rotation, "x")
|
raftRotationFolder.add(raft.rotation, "x")
|
||||||
raftRotationFolder.add(raft.rotation, "y")
|
raftRotationFolder.add(raft.rotation, "y")
|
||||||
raftRotationFolder.add(raft.rotation, "z")
|
raftRotationFolder.add(raft.rotation, "z")
|
||||||
raftFolder.close();
|
raftFolder.close()
|
||||||
|
|
||||||
const skyFolder = gui.addFolder('Sky');
|
const skyFolder = gui.addFolder('Sky')
|
||||||
skyFolder.add(parameters, 'elevation', 0, 90, 0.1).onChange(updateSun);
|
skyFolder.add(parameters, 'elevation', 0, 90, 0.1).onChange(updateSun)
|
||||||
skyFolder.add(parameters, 'azimuth', - 180, 180, 0.1).onChange(updateSun);
|
skyFolder.add(parameters, 'azimuth', - 180, 180, 0.1).onChange(updateSun)
|
||||||
skyFolder.close();
|
skyFolder.close()
|
||||||
|
|
||||||
const waterUniforms = ocean.material.uniforms;
|
const waterUniforms = ocean.material.uniforms
|
||||||
|
|
||||||
const waterFolder = gui.addFolder('Water');
|
const waterFolder = gui.addFolder('Water')
|
||||||
waterFolder
|
waterFolder
|
||||||
.add(waterUniforms.distortionScale, 'value', 0, 8, 0.1)
|
.add(waterUniforms.distortionScale, 'value', 0, 8, 0.1)
|
||||||
.name('distortionScale');
|
.name('distortionScale')
|
||||||
waterFolder.add(waterUniforms.size, 'value', 0.1, 10, 0.1).name('size');
|
waterFolder.add(waterUniforms.size, 'value', 0.1, 10, 0.1).name('size')
|
||||||
waterFolder.add(ocean.material, 'wireframe');
|
waterFolder.add(ocean.material, 'wireframe')
|
||||||
waterFolder.close();
|
waterFolder.close()
|
||||||
|
|
||||||
const waveAFolder = waterFolder.addFolder('Wave A');
|
const waveAFolder = waterFolder.addFolder('Wave A')
|
||||||
waveAFolder
|
waveAFolder
|
||||||
.add(waves.A, 'direction', 0, 359)
|
.add(waves.A, 'direction', 0, 359)
|
||||||
.name('Direction')
|
.name('Direction')
|
||||||
.onChange((v) => {
|
.onChange((v) => {
|
||||||
const x = (v * Math.PI) / 180;
|
const x = (v * Math.PI) / 180
|
||||||
ocean.material.uniforms.waveA.value[0] = Math.sin(x);
|
ocean.material.uniforms.waveA.value[0] = Math.sin(x)
|
||||||
ocean.material.uniforms.waveA.value[1] = Math.cos(x);
|
ocean.material.uniforms.waveA.value[1] = Math.cos(x)
|
||||||
});
|
})
|
||||||
waveAFolder
|
waveAFolder
|
||||||
.add(waves.A, 'steepness', 0, 1, 0.01)
|
.add(waves.A, 'steepness', 0, 1, 0.01)
|
||||||
.name('Steepness')
|
.name('Steepness')
|
||||||
.onChange((v) => {
|
.onChange((v) => {
|
||||||
ocean.material.uniforms.waveA.value[2] = v;
|
ocean.material.uniforms.waveA.value[2] = v
|
||||||
});
|
})
|
||||||
waveAFolder
|
waveAFolder
|
||||||
.add(waves.A, 'wavelength', 1, 100)
|
.add(waves.A, 'wavelength', 1, 100)
|
||||||
.name('Wavelength')
|
.name('Wavelength')
|
||||||
.onChange((v) => {
|
.onChange((v) => {
|
||||||
ocean.material.uniforms.waveA.value[3] = v;
|
ocean.material.uniforms.waveA.value[3] = v
|
||||||
});
|
})
|
||||||
waveAFolder.open();
|
waveAFolder.open()
|
||||||
|
|
||||||
const waveBFolder = waterFolder.addFolder('Wave B');
|
const waveBFolder = waterFolder.addFolder('Wave B')
|
||||||
waveBFolder
|
waveBFolder
|
||||||
.add(waves.B, 'direction', 0, 359)
|
.add(waves.B, 'direction', 0, 359)
|
||||||
.name('Direction')
|
.name('Direction')
|
||||||
.onChange((v) => {
|
.onChange((v) => {
|
||||||
const x = (v * Math.PI) / 180;
|
const x = (v * Math.PI) / 180
|
||||||
ocean.material.uniforms.waveB.value[0] = Math.sin(x);
|
ocean.material.uniforms.waveB.value[0] = Math.sin(x)
|
||||||
ocean.material.uniforms.waveB.value[1] = Math.cos(x);
|
ocean.material.uniforms.waveB.value[1] = Math.cos(x)
|
||||||
});
|
})
|
||||||
waveBFolder
|
waveBFolder
|
||||||
.add(waves.B, 'steepness', 0, 1, 0.01)
|
.add(waves.B, 'steepness', 0, 1, 0.01)
|
||||||
.name('Steepness')
|
.name('Steepness')
|
||||||
.onChange((v) => {
|
.onChange((v) => {
|
||||||
ocean.material.uniforms.waveB.value[2] = v;
|
ocean.material.uniforms.waveB.value[2] = v
|
||||||
});
|
})
|
||||||
waveBFolder
|
waveBFolder
|
||||||
.add(waves.B, 'wavelength', 1, 100)
|
.add(waves.B, 'wavelength', 1, 100)
|
||||||
.name('Wavelength')
|
.name('Wavelength')
|
||||||
.onChange((v) => {
|
.onChange((v) => {
|
||||||
ocean.material.uniforms.waveB.value[3] = v;
|
ocean.material.uniforms.waveB.value[3] = v
|
||||||
});
|
})
|
||||||
waveBFolder.open();
|
waveBFolder.open()
|
||||||
|
|
||||||
const waveCFolder = waterFolder.addFolder('Wave C');
|
const waveCFolder = waterFolder.addFolder('Wave C')
|
||||||
waveCFolder
|
waveCFolder
|
||||||
.add(waves.C, 'direction', 0, 359)
|
.add(waves.C, 'direction', 0, 359)
|
||||||
.name('Direction')
|
.name('Direction')
|
||||||
.onChange((v) => {
|
.onChange((v) => {
|
||||||
const x = (v * Math.PI) / 180;
|
const x = (v * Math.PI) / 180
|
||||||
ocean.material.uniforms.waveC.value[0] = Math.sin(x);
|
ocean.material.uniforms.waveC.value[0] = Math.sin(x)
|
||||||
ocean.material.uniforms.waveC.value[1] = Math.cos(x);
|
ocean.material.uniforms.waveC.value[1] = Math.cos(x)
|
||||||
});
|
})
|
||||||
waveCFolder
|
waveCFolder
|
||||||
.add(waves.C, 'steepness', 0, 1, 0.01)
|
.add(waves.C, 'steepness', 0, 1, 0.01)
|
||||||
.name('Steepness')
|
.name('Steepness')
|
||||||
.onChange((v) => {
|
.onChange((v) => {
|
||||||
ocean.material.uniforms.waveC.value[2] = v;
|
ocean.material.uniforms.waveC.value[2] = v
|
||||||
});
|
})
|
||||||
waveCFolder
|
waveCFolder
|
||||||
.add(waves.C, 'wavelength', 1, 100)
|
.add(waves.C, 'wavelength', 1, 100)
|
||||||
.name('Wavelength')
|
.name('Wavelength')
|
||||||
.onChange((v) => {
|
.onChange((v) => {
|
||||||
ocean.material.uniforms.waveC.value[3] = v;
|
ocean.material.uniforms.waveC.value[3] = v
|
||||||
});
|
})
|
||||||
waveCFolder.open();
|
waveCFolder.open()
|
||||||
|
|
||||||
const hexTilingFolder = gui.addFolder('Hex Tiling')
|
const hexTilingFolder = gui.addFolder('Hex Tiling')
|
||||||
if (wallMaterial?.hexTiling?.patchScale) {
|
if (wallMaterial?.hexTiling?.patchScale) {
|
||||||
@ -466,66 +529,66 @@ if (dev) {
|
|||||||
|
|
||||||
//
|
//
|
||||||
|
|
||||||
const clock = new THREE.Clock();
|
const clock = new THREE.Clock()
|
||||||
|
|
||||||
// Controls
|
// Controls
|
||||||
|
|
||||||
const GRAVITY = 30;
|
const GRAVITY = 30
|
||||||
|
|
||||||
const STEPS_PER_FRAME = 10;
|
const STEPS_PER_FRAME = 10
|
||||||
|
|
||||||
const playerCollider = new Capsule(
|
const playerCollider = new Capsule(
|
||||||
new THREE.Vector3(0, 25.0, 0),
|
new THREE.Vector3(0, 25.0, 0),
|
||||||
new THREE.Vector3(0, 25 + playerHeight, 0),
|
new THREE.Vector3(0, 25 + playerHeight, 0),
|
||||||
0.3
|
0.3
|
||||||
);
|
)
|
||||||
|
|
||||||
const playerVelocity = new THREE.Vector3();
|
const playerVelocity = new THREE.Vector3()
|
||||||
const playerDirection = new THREE.Vector3();
|
const playerDirection = new THREE.Vector3()
|
||||||
|
|
||||||
let playerOnFloor = false;
|
let playerOnFloor = false
|
||||||
let jumping = false;
|
let jumping = false
|
||||||
let escaped = false;
|
let escaped = false
|
||||||
|
|
||||||
const pointerLockControls = new PointerLockControls(camera, document.body);
|
const pointerLockControls = new PointerLockControls(camera, document.body)
|
||||||
pointerLockControls.pointerSpeed = 0.7;
|
pointerLockControls.pointerSpeed = 0.7
|
||||||
|
|
||||||
const keyStates = {};
|
const keyStates = {}
|
||||||
|
|
||||||
document.addEventListener('keydown', (event) => {
|
document.addEventListener('keydown', (event) => {
|
||||||
keyStates[event.code] = true;
|
keyStates[event.code] = true
|
||||||
});
|
})
|
||||||
|
|
||||||
document.addEventListener('keyup', (event) => {
|
document.addEventListener('keyup', (event) => {
|
||||||
keyStates[event.code] = false;
|
keyStates[event.code] = false
|
||||||
if (event.code == 'Space') jumping = false
|
if (event.code == 'Space') jumping = false
|
||||||
});
|
})
|
||||||
|
|
||||||
var mouseButtonsStates = [];
|
let mouseButtonsStates = []
|
||||||
|
|
||||||
function onMouseChange(event) {
|
function onMouseChange(event) {
|
||||||
for(var i=0; i < mouseButtonsStates.length || i <= Math.log2(event.buttons); i++) {
|
for(let i=0; i < mouseButtonsStates.length || i <= Math.log2(event.buttons); i++) {
|
||||||
mouseButtonsStates[i] = (event.buttons & (1 << i)) > 0
|
mouseButtonsStates[i] = (event.buttons & (1 << i)) > 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
container.addEventListener('click', function () {
|
container.addEventListener('click', function () {
|
||||||
pointerLockControls.lock();
|
pointerLockControls.lock()
|
||||||
})
|
})
|
||||||
|
|
||||||
pointerLockControls.addEventListener('lock', function () {
|
pointerLockControls.addEventListener('lock', function () {
|
||||||
ambiance.play();
|
ambiance.play()
|
||||||
document.addEventListener('mousedown', onMouseChange)
|
document.addEventListener('mousedown', onMouseChange)
|
||||||
document.addEventListener('mouseup', onMouseChange)
|
document.addEventListener('mouseup', onMouseChange)
|
||||||
})
|
})
|
||||||
|
|
||||||
pointerLockControls.addEventListener('unlock', function () {
|
pointerLockControls.addEventListener('unlock', function () {
|
||||||
ambiance.pause();
|
ambiance.pause()
|
||||||
document.removeEventListener('mousedown', onMouseChange)
|
document.removeEventListener('mousedown', onMouseChange)
|
||||||
document.removeEventListener('mouseup', onMouseChange)
|
document.removeEventListener('mouseup', onMouseChange)
|
||||||
})
|
})
|
||||||
|
|
||||||
scene.add(pointerLockControls.getObject());
|
scene.add(pointerLockControls.getObject())
|
||||||
|
|
||||||
function playerCollisions() {
|
function playerCollisions() {
|
||||||
|
|
||||||
@ -535,23 +598,23 @@ function playerCollisions() {
|
|||||||
|
|
||||||
const result = playerOnMaze || playerOnRaft || playerOnWater
|
const result = playerOnMaze || playerOnRaft || playerOnWater
|
||||||
|
|
||||||
playerOnFloor = false;
|
playerOnFloor = false
|
||||||
|
|
||||||
if ( result ) {
|
if ( result ) {
|
||||||
playerOnFloor = result.normal.y > 0;
|
playerOnFloor = result.normal.y > 0
|
||||||
|
|
||||||
if (!playerOnFloor) {
|
if (!playerOnFloor) {
|
||||||
playerVelocity.addScaledVector(result.normal, - result.normal.dot(playerVelocity));
|
playerVelocity.addScaledVector(result.normal, - result.normal.dot(playerVelocity))
|
||||||
}
|
}
|
||||||
|
|
||||||
playerCollider.translate(result.normal.multiplyScalar(result.depth));
|
playerCollider.translate(result.normal.multiplyScalar(result.depth))
|
||||||
|
|
||||||
if (playerOnRaft) {
|
if (playerOnRaft) {
|
||||||
camera.position.y = playerCollider.end.y + raft.position.y
|
camera.position.y = playerCollider.end.y + raft.position.y
|
||||||
if (!escaped) gameEnd()
|
if (!escaped) gameEnd()
|
||||||
|
|
||||||
} else if (playerOnWater) {
|
} else if (playerOnWater) {
|
||||||
const t = ocean.material.uniforms['time'].value;
|
const t = ocean.material.uniforms['time'].value
|
||||||
const waveInfo = getWaveInfo(playerCollider.end.x, playerCollider.end.z, t)
|
const waveInfo = getWaveInfo(playerCollider.end.x, playerCollider.end.z, t)
|
||||||
camera.position.y = ocean.position.y + waveInfo.position.y + 0.2
|
camera.position.y = ocean.position.y + waveInfo.position.y + 0.2
|
||||||
}
|
}
|
||||||
@ -561,61 +624,61 @@ function playerCollisions() {
|
|||||||
|
|
||||||
function gameEnd() {
|
function gameEnd() {
|
||||||
|
|
||||||
escaped = true;
|
escaped = true
|
||||||
message.innerHTML = '<h2>Libre !</h2><a href="">Rejouer</a>';
|
message.innerHTML = '<h2>Libre !</h2><a href="">Rejouer</a>'
|
||||||
message.className = "escaped";
|
message.className = "escaped"
|
||||||
piano.play();
|
piano.play()
|
||||||
|
|
||||||
document.exitPointerLock();
|
document.exitPointerLock()
|
||||||
//container.style.cursor = "default";
|
//container.style.cursor = "default"
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function updatePlayer(deltaTime) {
|
function updatePlayer(deltaTime) {
|
||||||
|
|
||||||
let damping = Math.exp(- 4 * deltaTime) - 1;
|
let damping = Math.exp(- 4 * deltaTime) - 1
|
||||||
|
|
||||||
if (!playerOnFloor) {
|
if (!playerOnFloor) {
|
||||||
playerVelocity.y -= GRAVITY * deltaTime;
|
playerVelocity.y -= GRAVITY * deltaTime
|
||||||
damping *= 0.1; // small air resistance
|
damping *= 0.1; // small air resistance
|
||||||
}
|
}
|
||||||
|
|
||||||
playerVelocity.addScaledVector(playerVelocity, damping);
|
playerVelocity.addScaledVector(playerVelocity, damping)
|
||||||
|
|
||||||
const deltaPosition = playerVelocity.clone().multiplyScalar(deltaTime);
|
const deltaPosition = playerVelocity.clone().multiplyScalar(deltaTime)
|
||||||
playerCollider.translate(deltaPosition);
|
playerCollider.translate(deltaPosition)
|
||||||
|
|
||||||
camera.position.copy(playerCollider.end);
|
camera.position.copy(playerCollider.end)
|
||||||
|
|
||||||
playerCollisions();
|
playerCollisions()
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function getForwardVector() {
|
function getForwardVector() {
|
||||||
|
|
||||||
camera.getWorldDirection(playerDirection);
|
camera.getWorldDirection(playerDirection)
|
||||||
playerDirection.y = 0;
|
playerDirection.y = 0
|
||||||
playerDirection.normalize();
|
playerDirection.normalize()
|
||||||
|
|
||||||
return playerDirection;
|
return playerDirection
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function getSideVector() {
|
function getSideVector() {
|
||||||
|
|
||||||
camera.getWorldDirection(playerDirection);
|
camera.getWorldDirection(playerDirection)
|
||||||
playerDirection.y = 0;
|
playerDirection.y = 0
|
||||||
playerDirection.normalize();
|
playerDirection.normalize()
|
||||||
playerDirection.cross(camera.up);
|
playerDirection.cross(camera.up)
|
||||||
|
|
||||||
return playerDirection;
|
return playerDirection
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function controls(deltaTime) {
|
function controls(deltaTime) {
|
||||||
|
|
||||||
// gives a bit of air control
|
// gives a bit of air control
|
||||||
const speedDelta = deltaTime * (playerOnFloor ? 100 : 20) / STEPS_PER_FRAME;
|
const speedDelta = deltaTime * (playerOnFloor ? 100 : 20) / STEPS_PER_FRAME
|
||||||
|
|
||||||
if (keyStates["ArrowUp"] || keyStates['KeyW'] || mouseButtonsStates[0]) {
|
if (keyStates["ArrowUp"] || keyStates['KeyW'] || mouseButtonsStates[0]) {
|
||||||
playerVelocity.add(getForwardVector().multiplyScalar(speedDelta))
|
playerVelocity.add(getForwardVector().multiplyScalar(speedDelta))
|
||||||
@ -631,7 +694,7 @@ function controls(deltaTime) {
|
|||||||
}
|
}
|
||||||
if (playerOnFloor && jumping == false) {
|
if (playerOnFloor && jumping == false) {
|
||||||
if (keyStates['Space']) {
|
if (keyStates['Space']) {
|
||||||
playerVelocity.y = 9;
|
playerVelocity.y = 9
|
||||||
jumping = true
|
jumping = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -639,88 +702,88 @@ function controls(deltaTime) {
|
|||||||
|
|
||||||
function getWaveInfo(x, z, time) {
|
function getWaveInfo(x, z, time) {
|
||||||
|
|
||||||
const pos = new THREE.Vector3();
|
const pos = new THREE.Vector3()
|
||||||
const tangent = new THREE.Vector3(1, 0, 0);
|
const tangent = new THREE.Vector3(1, 0, 0)
|
||||||
const binormal = new THREE.Vector3(0, 0, 1);
|
const binormal = new THREE.Vector3(0, 0, 1)
|
||||||
Object.keys(waves).forEach((wave) => {
|
Object.keys(waves).forEach((wave) => {
|
||||||
const w = waves[wave];
|
const w = waves[wave]
|
||||||
const k = (Math.PI * 2) / w.wavelength;
|
const k = (Math.PI * 2) / w.wavelength
|
||||||
const c = Math.sqrt(9.8 / k);
|
const c = Math.sqrt(9.8 / k)
|
||||||
const d = new THREE.Vector2(
|
const d = new THREE.Vector2(
|
||||||
Math.sin((w.direction * Math.PI) / 180),
|
Math.sin((w.direction * Math.PI) / 180),
|
||||||
- Math.cos((w.direction * Math.PI) / 180)
|
- Math.cos((w.direction * Math.PI) / 180)
|
||||||
);
|
)
|
||||||
const f = k * (d.dot(new THREE.Vector2(x, z)) - c * time);
|
const f = k * (d.dot(new THREE.Vector2(x, z)) - c * time)
|
||||||
const a = w.steepness / k;
|
const a = w.steepness / k
|
||||||
|
|
||||||
pos.x += d.y * (a * Math.cos(f));
|
pos.x += d.y * (a * Math.cos(f))
|
||||||
pos.y += a * Math.sin(f);
|
pos.y += a * Math.sin(f)
|
||||||
pos.z += d.x * (a * Math.cos(f));
|
pos.z += d.x * (a * Math.cos(f))
|
||||||
|
|
||||||
tangent.x += - d.x * d.x * (w.steepness * Math.sin(f));
|
tangent.x += - d.x * d.x * (w.steepness * Math.sin(f))
|
||||||
tangent.y += d.x * (w.steepness * Math.cos(f));
|
tangent.y += d.x * (w.steepness * Math.cos(f))
|
||||||
tangent.z += - d.x * d.y * (w.steepness * Math.sin(f));
|
tangent.z += - d.x * d.y * (w.steepness * Math.sin(f))
|
||||||
|
|
||||||
binormal.x += - d.x * d.y * (w.steepness * Math.sin(f));
|
binormal.x += - d.x * d.y * (w.steepness * Math.sin(f))
|
||||||
binormal.y += d.y * (w.steepness * Math.cos(f));
|
binormal.y += d.y * (w.steepness * Math.cos(f))
|
||||||
binormal.z += - d.y * d.y * (w.steepness * Math.sin(f));
|
binormal.z += - d.y * d.y * (w.steepness * Math.sin(f))
|
||||||
})
|
})
|
||||||
|
|
||||||
const normal = binormal.cross(tangent).normalize();
|
const normal = binormal.cross(tangent).normalize()
|
||||||
|
|
||||||
return { position: pos, normal: normal };
|
return { position: pos, normal: normal }
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateRaft(delta) {
|
function updateRaft(delta) {
|
||||||
|
|
||||||
const t = ocean.material.uniforms['time'].value;
|
const t = ocean.material.uniforms['time'].value
|
||||||
|
|
||||||
const waveInfo = getWaveInfo(raft.position.x, raft.position.z, t);
|
const waveInfo = getWaveInfo(raft.position.x, raft.position.z, t)
|
||||||
raft.position.y = ocean.position.y + waveInfo.position.y;
|
raft.position.y = ocean.position.y + waveInfo.position.y
|
||||||
const quat = new THREE.Quaternion().setFromEuler(
|
const quat = new THREE.Quaternion().setFromEuler(
|
||||||
new THREE.Euler().setFromVector3(waveInfo.normal)
|
new THREE.Euler().setFromVector3(waveInfo.normal)
|
||||||
);
|
)
|
||||||
raft.quaternion.rotateTowards(quat, delta * 0.5);
|
raft.quaternion.rotateTowards(quat, delta * 0.5)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
window.addEventListener('resize', onWindowResize);
|
window.addEventListener('resize', onWindowResize)
|
||||||
|
|
||||||
function onWindowResize() {
|
function onWindowResize() {
|
||||||
|
|
||||||
camera.aspect = window.innerWidth / window.innerHeight;
|
camera.aspect = window.innerWidth / window.innerHeight
|
||||||
camera.updateProjectionMatrix();
|
camera.updateProjectionMatrix()
|
||||||
|
|
||||||
renderer.setSize(window.innerWidth, window.innerHeight);
|
renderer.setSize(window.innerWidth, window.innerHeight)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function animate() {
|
function animate() {
|
||||||
|
|
||||||
const delta = Math.min(0.05, clock.getDelta())
|
const delta = Math.min(0.05, clock.getDelta())
|
||||||
const deltaTime = delta / STEPS_PER_FRAME;
|
const deltaTime = delta / STEPS_PER_FRAME
|
||||||
|
|
||||||
ocean.material.uniforms['time'].value += delta;
|
ocean.material.uniforms['time'].value += delta
|
||||||
|
|
||||||
updateRaft(delta);
|
updateRaft(delta)
|
||||||
|
|
||||||
// we look for collisions in substeps to mitigate the risk of
|
// we look for collisions in substeps to mitigate the risk of
|
||||||
// an object traversing another too quickly for detection.
|
// an object traversing another too quickly for detection.
|
||||||
|
|
||||||
for (let i = 0; i < STEPS_PER_FRAME; i++) {
|
for (let i = 0; i < STEPS_PER_FRAME; i++) {
|
||||||
|
|
||||||
controls(deltaTime);
|
controls(deltaTime)
|
||||||
|
|
||||||
updatePlayer(deltaTime);
|
updatePlayer(deltaTime)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (camera.position.y > 3.5)
|
if (camera.position.y > 3.5)
|
||||||
camera.lookAt(raft.position.x, raft.position.y, raft.position.z);
|
camera.lookAt(raft.position.x, raft.position.y, raft.position.z)
|
||||||
|
|
||||||
renderer.render(scene, camera);
|
renderer.render(scene, camera)
|
||||||
|
|
||||||
if (dev) stats.update();
|
if (dev) stats.update()
|
||||||
|
|
||||||
}
|
}
|
85
style.css
85
style.css
@ -1,9 +1,45 @@
|
|||||||
body {
|
body {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
background-color: #000;
|
background-color: #041626;
|
||||||
color: #fff;
|
font-size: 1.3em;
|
||||||
font-family: Georgia, serif;
|
|
||||||
overscroll-behavior: none;
|
overscroll-behavior: none;
|
||||||
|
cursor: wait;
|
||||||
|
}
|
||||||
|
|
||||||
|
#loading {
|
||||||
|
width: fit-content;
|
||||||
|
color: #2c5c88;
|
||||||
|
font-size: 1.3em;
|
||||||
|
top: 20vh;
|
||||||
|
margin: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
#loadingMessage {
|
||||||
|
margin-bottom: 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
#labyTable {
|
||||||
|
width: 230px;
|
||||||
|
height: 230px;
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
margin-top: 20vh;
|
||||||
|
margin-bottom: 5vh;
|
||||||
|
border-collapse: collapse;
|
||||||
|
}
|
||||||
|
|
||||||
|
td {
|
||||||
|
width: 10px;
|
||||||
|
height: 10px;
|
||||||
|
transition: background-color 1s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wall {
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ground {
|
||||||
|
background-color: #214464;
|
||||||
}
|
}
|
||||||
|
|
||||||
#container {
|
#container {
|
||||||
@ -32,48 +68,7 @@ body {
|
|||||||
justify-content: center;
|
justify-content: center;
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
color: gray;
|
color: gray;
|
||||||
}
|
font-family: Times, "Times New Roman", Georgia, serif;
|
||||||
|
|
||||||
#message.loading {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
top: 20vh;
|
|
||||||
width: 100%;
|
|
||||||
margin: auto;
|
|
||||||
align-items: center;
|
|
||||||
gap: 5rem;
|
|
||||||
text-align: center;
|
|
||||||
font-size: 1.7em;
|
|
||||||
cursor: progress;
|
|
||||||
}
|
|
||||||
|
|
||||||
#progressCircle {
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
position: relative;
|
|
||||||
width: 200px;
|
|
||||||
height: 200px;
|
|
||||||
border: 4px solid dimgray;
|
|
||||||
border-radius: 50%;
|
|
||||||
font-size: 0;
|
|
||||||
font-size: 3vh;
|
|
||||||
font-weight: 700;
|
|
||||||
font-family: system-ui;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
#progressCircle::after {
|
|
||||||
content: "";
|
|
||||||
display: flex;
|
|
||||||
position: absolute;
|
|
||||||
width: 200px;
|
|
||||||
height: 200px;
|
|
||||||
top: -4px;
|
|
||||||
left: -4px;
|
|
||||||
border: 4px solid #1da8b7;
|
|
||||||
border-radius: 50%;
|
|
||||||
mask: conic-gradient(black var(--progress), transparent var(--progress));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#message a {
|
#message a {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user