Compare commits
11 Commits
9fca05ae6e
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| c52a604f0f | |||
| 8e9a089d34 | |||
| d5893eb8ef | |||
| 1e006d46b9 | |||
| 0f84f90e05 | |||
| 4287edab71 | |||
| 5c2eaca35a | |||
| e7dc780173 | |||
| 9721b311eb | |||
| d3f6cf9b71 | |||
| 5b058a58b3 |
5
README.md
Normal file
5
README.md
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
# teTra
|
||||||
|
|
||||||
|
Falling blocks web game made with three.js librairy
|
||||||
|
|
||||||
|

|
||||||
15
app.js
15
app.js
@ -1,10 +1,10 @@
|
|||||||
import * as THREE from 'three'
|
import * as THREE from 'three'
|
||||||
import { scheduler } from './jsm/scheduler.js'
|
import { scheduler } from './jsm/scheduler.js'
|
||||||
import { TRANSLATION, ROTATION, environment, Mino, Playfield, HoldQueue, NextQueue } from './jsm/Tetrominoes.js'
|
import { TRANSLATION, ROTATION, environment, InstancedMino, Mino, Playfield, HoldQueue, NextQueue } from './jsm/Tetrominoes.js'
|
||||||
import Settings from './jsm/Settings.js'
|
import Settings from './jsm/Settings.js'
|
||||||
import { Stats } from './jsm/Stats.js'
|
import { Stats } from './jsm/Stats.js'
|
||||||
import { Menu } from './jsm/Menu.js'
|
import { Menu } from './jsm/Menu.js'
|
||||||
import TetraControls from './jsm/TetraControls.js'
|
import CameraControls from './jsm/CameraControls.js'
|
||||||
import { TetraScene } from './jsm/TetraScene.js'
|
import { TetraScene } from './jsm/TetraScene.js'
|
||||||
import * as FPS from 'three/addons/libs/stats.module.js'
|
import * as FPS from 'three/addons/libs/stats.module.js'
|
||||||
|
|
||||||
@ -30,7 +30,7 @@ let game = {
|
|||||||
menu.stats.show()
|
menu.stats.show()
|
||||||
menu.settings.close()
|
menu.settings.close()
|
||||||
|
|
||||||
Mino.meshes.clear()
|
Mino.instances.clear()
|
||||||
|
|
||||||
nextQueue.init()
|
nextQueue.init()
|
||||||
holdQueue.piece = undefined
|
holdQueue.piece = undefined
|
||||||
@ -297,9 +297,10 @@ loadingManager.onStart = function (url, itemsLoaded, itemsTotal) {
|
|||||||
const stats = new Stats()
|
const stats = new Stats()
|
||||||
const settings = new Settings()
|
const settings = new Settings()
|
||||||
const scene = new TetraScene(settings, loadingManager)
|
const scene = new TetraScene(settings, loadingManager)
|
||||||
const controls = new TetraControls(scene.camera, renderer.domElement)
|
const controls = new CameraControls(scene.camera, renderer.domElement)
|
||||||
|
|
||||||
scene.add(Mino.meshes)
|
const minoes = new InstancedMino()
|
||||||
|
scene.add(minoes)
|
||||||
const holdQueue = new HoldQueue()
|
const holdQueue = new HoldQueue()
|
||||||
scene.add(holdQueue)
|
scene.add(holdQueue)
|
||||||
const playfield = new Playfield(loadingManager)
|
const playfield = new Playfield(loadingManager)
|
||||||
@ -307,7 +308,7 @@ scene.add(playfield)
|
|||||||
const nextQueue = new NextQueue()
|
const nextQueue = new NextQueue()
|
||||||
scene.add(nextQueue)
|
scene.add(nextQueue)
|
||||||
|
|
||||||
const menu = new Menu(game, settings, stats, scene, controls, playfield)
|
const menu = new Menu(game, settings, stats, scene, minoes, playfield)
|
||||||
menu.load()
|
menu.load()
|
||||||
|
|
||||||
let fps
|
let fps
|
||||||
@ -328,7 +329,7 @@ function animate() {
|
|||||||
scene.updateMatrixWorld()
|
scene.updateMatrixWorld()
|
||||||
scene.update(delta)
|
scene.update(delta)
|
||||||
playfield.update(delta)
|
playfield.update(delta)
|
||||||
Mino.meshes.update()
|
minoes.update()
|
||||||
controls.update()
|
controls.update()
|
||||||
|
|
||||||
renderer.render(scene, scene.camera)
|
renderer.render(scene, scene.camera)
|
||||||
|
|||||||
@ -48,10 +48,10 @@
|
|||||||
transform-style: preserve-3d;
|
transform-style: preserve-3d;
|
||||||
}
|
}
|
||||||
|
|
||||||
.first.mino { top: -0.5em; left: -1em; }
|
.T.tetromino .first.mino { top: -0.5em; left: -1em; }
|
||||||
.second.mino { top: -0.5em; left: 0em; }
|
.T.tetromino .second.mino { top: -0.5em; left: 0em; }
|
||||||
.third.mino { top: -0.5em; left: 1em; }
|
.T.tetromino .third.mino { top: -0.5em; left: 1em; }
|
||||||
.fourth.mino { top: 0.5em; left: 0em; }
|
.T.tetromino .fourth.mino { top: 0.5em; left: 0em; }
|
||||||
|
|
||||||
.face {
|
.face {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
|||||||
11
index.html
11
index.html
@ -8,6 +8,15 @@
|
|||||||
<link rel="icon" href="favicon.ico">
|
<link rel="icon" href="favicon.ico">
|
||||||
<link rel="stylesheet" href="css/style.css">
|
<link rel="stylesheet" href="css/style.css">
|
||||||
<link rel="stylesheet" href="css/loading.css">
|
<link rel="stylesheet" href="css/loading.css">
|
||||||
|
<meta property="og:title" content="ᵀᴱTᴿᴬ"/>
|
||||||
|
<meta property="og:type" content="game"/>
|
||||||
|
<meta property="og:url" content="https://adrien.malingrey.fr/jeux/tetra/"/>
|
||||||
|
<meta property="og:image" content="https://adrien.malingrey.fr/jeux/tetra/thumbnail.png"/>
|
||||||
|
<meta property="og:image:width" content="250"/>
|
||||||
|
<meta property="og:image:height" content="250"/>
|
||||||
|
<meta property="og:description" content="Des blocs qui tombent en 3D"/>
|
||||||
|
<meta property="og:locale" content="fr_FR"/>
|
||||||
|
<meta property="og:site_name" content="adrien.malingrey.fr"/>
|
||||||
<script async src="https://unpkg.com/es-module-shims@1.6.3/dist/es-module-shims.js"></script>
|
<script async src="https://unpkg.com/es-module-shims@1.6.3/dist/es-module-shims.js"></script>
|
||||||
<script type="importmap">
|
<script type="importmap">
|
||||||
{
|
{
|
||||||
@ -22,7 +31,7 @@
|
|||||||
<body>
|
<body>
|
||||||
<span id="loadingDiv">
|
<span id="loadingDiv">
|
||||||
<div class="scene">
|
<div class="scene">
|
||||||
<div class="tetromino">
|
<div class="T tetromino">
|
||||||
<div class="first mino">
|
<div class="first mino">
|
||||||
<div class="front face"></div>
|
<div class="front face"></div>
|
||||||
<div class="back face"></div>
|
<div class="back face"></div>
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import { OrbitControls } from 'three/addons/controls/OrbitControls.js'
|
import { OrbitControls } from 'three/addons/controls/OrbitControls.js'
|
||||||
|
|
||||||
|
|
||||||
export default class TetraControls extends OrbitControls {
|
export default class CameraControls extends OrbitControls {
|
||||||
constructor(camera, domElement) {
|
constructor(camera, domElement) {
|
||||||
super(camera, domElement)
|
super(camera, domElement)
|
||||||
this.autoRotate
|
this.autoRotate
|
||||||
41
jsm/Menu.js
41
jsm/Menu.js
@ -1,10 +1,10 @@
|
|||||||
import * as THREE from 'three'
|
import * as THREE from 'three'
|
||||||
import { GUI } from 'three/addons/libs/lil-gui.module.min.js'
|
import { GUI } from 'three/addons/libs/lil-gui.module.min.js'
|
||||||
import { Mino, environment } from './Tetrominoes.js'
|
import { environment } from './Tetrominoes.js'
|
||||||
|
|
||||||
|
|
||||||
export class Menu extends GUI {
|
export class Menu extends GUI {
|
||||||
constructor(game, settings, stats, scene, controls, playfield) {
|
constructor(game, settings, stats, scene, minoes, playfield) {
|
||||||
super({title: "ᵀᴱTᴿᴬ"})
|
super({title: "ᵀᴱTᴿᴬ"})
|
||||||
|
|
||||||
this.startButton = this.add(game, "start").name("Jouer").hide()
|
this.startButton = this.add(game, "start").name("Jouer").hide()
|
||||||
@ -28,7 +28,7 @@ export class Menu extends GUI {
|
|||||||
|
|
||||||
this.settings.add(settings, "theme", ["Plasma", "Espace", "Rétro"]).name("Thème").onChange(theme => {
|
this.settings.add(settings, "theme", ["Plasma", "Espace", "Rétro"]).name("Thème").onChange(theme => {
|
||||||
scene.theme = theme
|
scene.theme = theme
|
||||||
Mino.meshes.theme = theme
|
minoes.theme = theme
|
||||||
if (theme == "Rétro") {
|
if (theme == "Rétro") {
|
||||||
playfield.edge.visible = false
|
playfield.edge.visible = false
|
||||||
playfield.retroEdge.visible = true
|
playfield.retroEdge.visible = true
|
||||||
@ -43,21 +43,21 @@ export class Menu extends GUI {
|
|||||||
|
|
||||||
this.settings.key = this.settings.addFolder("Commandes").open()
|
this.settings.key = this.settings.addFolder("Commandes").open()
|
||||||
let moveLeftKeyController = this.settings.key.add(settings.key, "moveLeft").name('Gauche')
|
let moveLeftKeyController = this.settings.key.add(settings.key, "moveLeft").name('Gauche')
|
||||||
moveLeftKeyController.domElement.onclick = this.changeKey.bind(moveLeftKeyController)
|
moveLeftKeyController.domElement.onclick = this.changeKey(moveLeftKeyController)
|
||||||
let moveRightKeyController = this.settings.key.add(settings.key, "moveRight").name('Droite')
|
let moveRightKeyController = this.settings.key.add(settings.key, "moveRight").name('Droite')
|
||||||
moveRightKeyController.domElement.onclick = this.changeKey.bind(moveRightKeyController)
|
moveRightKeyController.domElement.onclick = this.changeKey(moveRightKeyController)
|
||||||
let rotateCWKeyController = this.settings.key.add(settings.key, "rotateCW").name('Rotation horaire')
|
let rotateCWKeyController = this.settings.key.add(settings.key, "rotateCW").name('Rotation horaire')
|
||||||
rotateCWKeyController.domElement.onclick = this.changeKey.bind(rotateCWKeyController)
|
rotateCWKeyController.domElement.onclick = this.changeKey(rotateCWKeyController)
|
||||||
let rotateCCWKeyController = this.settings.key.add(settings.key, "rotateCCW").name('anti-horaire')
|
let rotateCCWKeyController = this.settings.key.add(settings.key, "rotateCCW").name('anti-horaire')
|
||||||
rotateCCWKeyController.domElement.onclick = this.changeKey.bind(rotateCCWKeyController)
|
rotateCCWKeyController.domElement.onclick = this.changeKey(rotateCCWKeyController)
|
||||||
let softDropKeyController = this.settings.key.add(settings.key, "softDrop").name('Chute lente')
|
let softDropKeyController = this.settings.key.add(settings.key, "softDrop").name('Chute lente')
|
||||||
softDropKeyController.domElement.onclick = this.changeKey.bind(softDropKeyController)
|
softDropKeyController.domElement.onclick = this.changeKey(softDropKeyController)
|
||||||
let hardDropKeyController = this.settings.key.add(settings.key, "hardDrop").name('Chute rapide')
|
let hardDropKeyController = this.settings.key.add(settings.key, "hardDrop").name('Chute rapide')
|
||||||
hardDropKeyController.domElement.onclick = this.changeKey.bind(hardDropKeyController)
|
hardDropKeyController.domElement.onclick = this.changeKey(hardDropKeyController)
|
||||||
let holdKeyController = this.settings.key.add(settings.key, "hold").name('Garder')
|
let holdKeyController = this.settings.key.add(settings.key, "hold").name('Garder')
|
||||||
holdKeyController.domElement.onclick = this.changeKey.bind(holdKeyController)
|
holdKeyController.domElement.onclick = this.changeKey(holdKeyController)
|
||||||
let pauseKeyController = this.settings.key.add(settings.key, "pause").name('Pause')
|
let pauseKeyController = this.settings.key.add(settings.key, "pause").name('Pause')
|
||||||
pauseKeyController.domElement.onclick = this.changeKey.bind(pauseKeyController)
|
pauseKeyController.domElement.onclick = this.changeKey(pauseKeyController)
|
||||||
|
|
||||||
this.settings.delay = this.settings.addFolder("Répétition automatique").open()
|
this.settings.delay = this.settings.addFolder("Répétition automatique").open()
|
||||||
this.settings.delay.add(settings,"arrDelay").name("ARR (ms)").min(2).max(200).step(1);
|
this.settings.delay.add(settings,"arrDelay").name("ARR (ms)").min(2).max(200).step(1);
|
||||||
@ -77,10 +77,10 @@ export class Menu extends GUI {
|
|||||||
function changeMaterial() {
|
function changeMaterial() {
|
||||||
material?.destroy()
|
material?.destroy()
|
||||||
material = dev.addFolder("minoes material")
|
material = dev.addFolder("minoes material")
|
||||||
material.add(Mino.meshes.material, "constructor", ["MeshBasicMaterial", "MeshStandardMaterial", "MeshPhysicalMaterial"]).listen().onChange(type => {
|
material.add(minoes.material, "constructor", ["MeshBasicMaterial", "MeshStandardMaterial", "MeshPhysicalMaterial"]).listen().onChange(type => {
|
||||||
switch(type) {
|
switch(type) {
|
||||||
case "MeshBasicMaterial":
|
case "MeshBasicMaterial":
|
||||||
Mino.meshes.material = new THREE.MeshBasicMaterial({
|
minoes.material = new THREE.MeshBasicMaterial({
|
||||||
envMap: environment,
|
envMap: environment,
|
||||||
side: THREE.DoubleSide,
|
side: THREE.DoubleSide,
|
||||||
transparent: true,
|
transparent: true,
|
||||||
@ -89,7 +89,7 @@ export class Menu extends GUI {
|
|||||||
})
|
})
|
||||||
break
|
break
|
||||||
case "MeshStandardMaterial":
|
case "MeshStandardMaterial":
|
||||||
Mino.meshes.material = new THREE.MeshStandardMaterial({
|
minoes.material = new THREE.MeshStandardMaterial({
|
||||||
envMap: environment,
|
envMap: environment,
|
||||||
side: THREE.DoubleSide,
|
side: THREE.DoubleSide,
|
||||||
transparent: true,
|
transparent: true,
|
||||||
@ -99,7 +99,7 @@ export class Menu extends GUI {
|
|||||||
})
|
})
|
||||||
break
|
break
|
||||||
case "MeshPhysicalMaterial":
|
case "MeshPhysicalMaterial":
|
||||||
Mino.meshes.material = new THREE.MeshPhysicalMaterial({
|
minoes.material = new THREE.MeshPhysicalMaterial({
|
||||||
envMap: environment,
|
envMap: environment,
|
||||||
side: THREE.DoubleSide,
|
side: THREE.DoubleSide,
|
||||||
transparent: true,
|
transparent: true,
|
||||||
@ -111,12 +111,11 @@ export class Menu extends GUI {
|
|||||||
})
|
})
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
Mino.meshes.update = Mino.meshes.updateColor
|
minoes.update = minoes.updateColor
|
||||||
changeMaterial()
|
changeMaterial()
|
||||||
})
|
})
|
||||||
console.log("lnlnl")
|
|
||||||
|
|
||||||
let minoMaterial = Mino.meshes.material instanceof Array ? Mino.meshes.material[0] : Mino.meshes.material
|
let minoMaterial = minoes.material instanceof Array ? minoes.material[0] : minoes.material
|
||||||
if ("opacity" in minoMaterial) material.add(minoMaterial, "opacity" ).min(0).max(1)
|
if ("opacity" in minoMaterial) material.add(minoMaterial, "opacity" ).min(0).max(1)
|
||||||
if ("reflectivity" in minoMaterial) material.add(minoMaterial, "reflectivity" ).min(0).max(1)
|
if ("reflectivity" in minoMaterial) material.add(minoMaterial, "reflectivity" ).min(0).max(1)
|
||||||
if ("roughness" in minoMaterial) material.add(minoMaterial, "roughness" ).min(0).max(1)
|
if ("roughness" in minoMaterial) material.add(minoMaterial, "roughness" ).min(0).max(1)
|
||||||
@ -153,7 +152,7 @@ export class Menu extends GUI {
|
|||||||
vortex.add(scene.vortex.darkCylinder.material, "opacity").name("dark").min(0).max(1)
|
vortex.add(scene.vortex.darkCylinder.material, "opacity").name("dark").min(0).max(1)
|
||||||
vortex.add(scene.vortex.colorFullCylinder.material, "opacity").name("colorFull").min(0).max(1)
|
vortex.add(scene.vortex.colorFullCylinder.material, "opacity").name("colorFull").min(0).max(1)
|
||||||
|
|
||||||
changeMaterial(Mino.meshes.material.constructor.name)
|
changeMaterial(minoes.material.constructor.name)
|
||||||
material.close()
|
material.close()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -168,8 +167,8 @@ export class Menu extends GUI {
|
|||||||
localStorage["teTraSettings"] = JSON.stringify(this.settings.save())
|
localStorage["teTraSettings"] = JSON.stringify(this.settings.save())
|
||||||
}
|
}
|
||||||
|
|
||||||
changeKey() {
|
changeKey(settings) {
|
||||||
let controller = this.settings
|
let controller = settings
|
||||||
let input = controller.domElement.getElementsByTagName("input")[0]
|
let input = controller.domElement.getElementsByTagName("input")[0]
|
||||||
input.select()
|
input.select()
|
||||||
input.onkeydown = function (event) {
|
input.onkeydown = function (event) {
|
||||||
|
|||||||
@ -69,129 +69,8 @@ const sideMaterial = new THREE.MeshStandardMaterial({
|
|||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
class InstancedMino extends THREE.InstancedMesh {
|
export class InstancedMino extends THREE.InstancedMesh {
|
||||||
constructor(geometry, material, count) {
|
constructor() {
|
||||||
super(geometry, material, count)
|
|
||||||
this.instances = new Set()
|
|
||||||
this.count = 0
|
|
||||||
this.offsets = new Uint8Array(2*count)
|
|
||||||
this.update = this.updateColor
|
|
||||||
}
|
|
||||||
|
|
||||||
add(instance) {
|
|
||||||
this.instances.add(instance)
|
|
||||||
}
|
|
||||||
|
|
||||||
delete(instance) {
|
|
||||||
this.instances.delete(instance)
|
|
||||||
}
|
|
||||||
|
|
||||||
clear() {
|
|
||||||
this.instances.clear()
|
|
||||||
}
|
|
||||||
|
|
||||||
set theme(theme) {
|
|
||||||
this._theme = theme
|
|
||||||
this.material = Mino.materials[theme]
|
|
||||||
if (theme == "Rétro") {
|
|
||||||
this.resetColor()
|
|
||||||
this.update = this.updateOffset
|
|
||||||
} else {
|
|
||||||
this.update = this.updateColor
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
get theme() {
|
|
||||||
return this._theme
|
|
||||||
}
|
|
||||||
|
|
||||||
setOffsetAt(index, offset) {
|
|
||||||
this.offsets[2*index] = offset.x
|
|
||||||
this.offsets[2*index + 1] = offset.y
|
|
||||||
}
|
|
||||||
|
|
||||||
resetColor() {
|
|
||||||
this.instanceColor = null
|
|
||||||
}
|
|
||||||
|
|
||||||
updateColor() {
|
|
||||||
this.count = 0
|
|
||||||
this.instances.forEach(mino => {
|
|
||||||
if (mino.parent?.visible) {
|
|
||||||
this.setMatrixAt(this.count, mino.matrixWorld)
|
|
||||||
this.setColorAt(this.count, mino.color)
|
|
||||||
this.count++
|
|
||||||
}
|
|
||||||
})
|
|
||||||
if (this.count) {
|
|
||||||
this.instanceMatrix.needsUpdate = true
|
|
||||||
this.instanceColor.needsUpdate = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
updateOffset() {
|
|
||||||
this.count = 0
|
|
||||||
this.instances.forEach(mino => {
|
|
||||||
if (mino.parent?.visible) {
|
|
||||||
this.setMatrixAt(this.count, mino.matrixWorld)
|
|
||||||
this.setOffsetAt(this.count, mino.offset)
|
|
||||||
this.count++
|
|
||||||
}
|
|
||||||
})
|
|
||||||
if (this.count) {
|
|
||||||
this.instanceMatrix.needsUpdate = true
|
|
||||||
this.geometry.setAttribute('offset', new THREE.InstancedBufferAttribute(this.offsets, 2))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class Mino extends THREE.Object3D {
|
|
||||||
static materials = {
|
|
||||||
Plasma: new THREE.MeshStandardMaterial({
|
|
||||||
envMap: environment,
|
|
||||||
side: THREE.DoubleSide,
|
|
||||||
transparent: true,
|
|
||||||
opacity: 0.7,
|
|
||||||
roughness: 0.6,
|
|
||||||
metalness: 1,
|
|
||||||
}),
|
|
||||||
Espace: new THREE.MeshStandardMaterial({
|
|
||||||
envMap: environment,
|
|
||||||
side: THREE.DoubleSide,
|
|
||||||
transparent: true,
|
|
||||||
opacity: 0.8,
|
|
||||||
roughness: 0.1,
|
|
||||||
metalness: 0.99,
|
|
||||||
}),
|
|
||||||
Rétro: [sideMaterial, sideMaterial, sideMaterial, sideMaterial, sideMaterial, sideMaterial]
|
|
||||||
}
|
|
||||||
static {
|
|
||||||
new THREE.TextureLoader().load("images/sprites.png", (texture) => {
|
|
||||||
this.materials.Rétro[0] = this.materials.Rétro[2] = new TileMaterial({
|
|
||||||
color: COLORS.RETRO,
|
|
||||||
map: texture,
|
|
||||||
bumpMap: texture,
|
|
||||||
bumpScale: 1.5,
|
|
||||||
roughness: 0.25,
|
|
||||||
metalness: 0.9,
|
|
||||||
transparent: true,
|
|
||||||
}, 8, 8)
|
|
||||||
})
|
|
||||||
new THREE.TextureLoader().load("images/edges.png", (texture) => {
|
|
||||||
this.materials.Rétro[1] = this.materials.Rétro[3] =this.materials.Rétro[4] = this.materials.Rétro[4] = new TileMaterial({
|
|
||||||
color: COLORS.RETRO,
|
|
||||||
map: texture,
|
|
||||||
bumpMap: texture,
|
|
||||||
bumpScale: 1.5,
|
|
||||||
roughness: 0.25,
|
|
||||||
metalness: 0.9,
|
|
||||||
transparent: true,
|
|
||||||
}, 1, 1)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
static meshes
|
|
||||||
static {
|
|
||||||
let minoFaceShape = new THREE.Shape()
|
let minoFaceShape = new THREE.Shape()
|
||||||
minoFaceShape.moveTo(.1, .1)
|
minoFaceShape.moveTo(.1, .1)
|
||||||
minoFaceShape.lineTo(.1, .9)
|
minoFaceShape.lineTo(.1, .9)
|
||||||
@ -208,9 +87,110 @@ class Mino extends THREE.Object3D {
|
|||||||
bevelSegments: 1
|
bevelSegments: 1
|
||||||
}
|
}
|
||||||
const geometry = new THREE.ExtrudeGeometry(minoFaceShape, minoExtrudeSettings)
|
const geometry = new THREE.ExtrudeGeometry(minoFaceShape, minoExtrudeSettings)
|
||||||
this.meshes = new InstancedMino(geometry, this.materials.Plasma, 2*ROWS*COLUMNS)
|
super(geometry, undefined, 2*ROWS*COLUMNS)
|
||||||
|
this.offsets = new Uint8Array(2*this.count)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
set theme(theme) {
|
||||||
|
if (theme == "Rétro") {
|
||||||
|
this.resetColor()
|
||||||
|
this.update = this.updateOffset
|
||||||
|
if (this.materials["Rétro"]) {
|
||||||
|
this.material = this.materials["Rétro"]
|
||||||
|
} else {
|
||||||
|
this.materials["Rétro"] = []
|
||||||
|
const loadingManager = new THREE.LoadingManager(() => this.material = this.materials["Rétro"])
|
||||||
|
new THREE.TextureLoader(loadingManager).load("images/sprites.png", (texture) => {
|
||||||
|
this.materials.Rétro[0] = this.materials.Rétro[2] = new TileMaterial({
|
||||||
|
color: COLORS.RETRO,
|
||||||
|
map: texture,
|
||||||
|
bumpMap: texture,
|
||||||
|
bumpScale: 1.5,
|
||||||
|
roughness: 0.25,
|
||||||
|
metalness: 0.9,
|
||||||
|
transparent: true,
|
||||||
|
}, 8, 8)
|
||||||
|
})
|
||||||
|
new THREE.TextureLoader(loadingManager).load("images/edges.png", (texture) => {
|
||||||
|
this.materials.Rétro[1] = this.materials.Rétro[3] = this.materials.Rétro[4] = this.materials.Rétro[5] = new TileMaterial({
|
||||||
|
color: COLORS.RETRO,
|
||||||
|
map: texture,
|
||||||
|
bumpMap: texture,
|
||||||
|
bumpScale: 1.5,
|
||||||
|
roughness: 0.25,
|
||||||
|
metalness: 0.9,
|
||||||
|
transparent: true,
|
||||||
|
}, 1, 1)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.update = this.updateColor
|
||||||
|
this.material = this.materials[theme]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setOffsetAt(index, offset) {
|
||||||
|
this.offsets[2*index] = offset.x
|
||||||
|
this.offsets[2*index + 1] = offset.y
|
||||||
|
}
|
||||||
|
|
||||||
|
resetColor() {
|
||||||
|
this.instanceColor = null
|
||||||
|
}
|
||||||
|
|
||||||
|
updateColor() {
|
||||||
|
this.count = 0
|
||||||
|
Mino.instances.forEach(mino => {
|
||||||
|
if (mino.parent?.visible) {
|
||||||
|
this.setMatrixAt(this.count, mino.matrixWorld)
|
||||||
|
this.setColorAt(this.count, mino.color)
|
||||||
|
this.count++
|
||||||
|
}
|
||||||
|
})
|
||||||
|
if (this.count) {
|
||||||
|
this.instanceMatrix.needsUpdate = true
|
||||||
|
this.instanceColor.needsUpdate = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
updateOffset() {
|
||||||
|
this.count = 0
|
||||||
|
Mino.instances.forEach(mino => {
|
||||||
|
if (mino.parent?.visible) {
|
||||||
|
this.setMatrixAt(this.count, mino.matrixWorld)
|
||||||
|
this.setOffsetAt(this.count, mino.offset)
|
||||||
|
this.count++
|
||||||
|
}
|
||||||
|
})
|
||||||
|
if (this.count) {
|
||||||
|
this.instanceMatrix.needsUpdate = true
|
||||||
|
this.geometry.setAttribute('offset', new THREE.InstancedBufferAttribute(this.offsets, 2))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
InstancedMino.prototype.materials = {
|
||||||
|
Plasma: new THREE.MeshStandardMaterial({
|
||||||
|
envMap: environment,
|
||||||
|
side: THREE.DoubleSide,
|
||||||
|
transparent: true,
|
||||||
|
opacity: 0.7,
|
||||||
|
roughness: 0.6,
|
||||||
|
metalness: 1,
|
||||||
|
}),
|
||||||
|
Espace: new THREE.MeshStandardMaterial({
|
||||||
|
envMap: environment,
|
||||||
|
side: THREE.DoubleSide,
|
||||||
|
transparent: true,
|
||||||
|
opacity: 0.8,
|
||||||
|
roughness: 0.1,
|
||||||
|
metalness: 0.99,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class Mino extends THREE.Object3D {
|
||||||
|
static instances = new Set()
|
||||||
|
|
||||||
constructor(color, offset) {
|
constructor(color, offset) {
|
||||||
super()
|
super()
|
||||||
this.color = color
|
this.color = color
|
||||||
@ -218,7 +198,7 @@ class Mino extends THREE.Object3D {
|
|||||||
this.velocity = P(50 - 100 * Math.random(), 60 - 100 * Math.random(), 50 - 100 * Math.random())
|
this.velocity = P(50 - 100 * Math.random(), 60 - 100 * Math.random(), 50 - 100 * Math.random())
|
||||||
this.rotationAngle = P(Math.random(), Math.random(), Math.random()).normalize()
|
this.rotationAngle = P(Math.random(), Math.random(), Math.random()).normalize()
|
||||||
this.angularVelocity = 5 - 10 * Math.random()
|
this.angularVelocity = 5 - 10 * Math.random()
|
||||||
this.constructor.meshes.add(this)
|
this.constructor.instances.add(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
explode(delta) {
|
explode(delta) {
|
||||||
@ -234,7 +214,7 @@ class Mino extends THREE.Object3D {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dispose() {
|
dispose() {
|
||||||
this.constructor.meshes.delete(this)
|
this.constructor.instances.delete(this)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -70,10 +70,10 @@ export class Vortex extends THREE.Group {
|
|||||||
new THREE.TextureLoader(this.loadingManager).load("./images/dark.jpg", texture => {
|
new THREE.TextureLoader(this.loadingManager).load("./images/dark.jpg", texture => {
|
||||||
texture.wrapS = THREE.RepeatWrapping
|
texture.wrapS = THREE.RepeatWrapping
|
||||||
texture.wrapT = THREE.MirroredRepeatWrapping
|
texture.wrapT = THREE.MirroredRepeatWrapping
|
||||||
texture.repeat.set(2, 2)
|
texture.repeat.set(2, 4)
|
||||||
this.darkCylinder.material.map = texture
|
this.darkCylinder.material.map = texture
|
||||||
})
|
})
|
||||||
this.darkCylinder.material.opacity = 0.05
|
this.darkCylinder.material.opacity = 0.08
|
||||||
|
|
||||||
new THREE.TextureLoader(this.loadingManager).load("./images/colorfull.jpg", texture => {
|
new THREE.TextureLoader(this.loadingManager).load("./images/colorfull.jpg", texture => {
|
||||||
texture.wrapS = THREE.RepeatWrapping
|
texture.wrapS = THREE.RepeatWrapping
|
||||||
@ -81,7 +81,7 @@ export class Vortex extends THREE.Group {
|
|||||||
texture.repeat.set(2, 2)
|
texture.repeat.set(2, 2)
|
||||||
this.colorFullCylinder.material.map = texture
|
this.colorFullCylinder.material.map = texture
|
||||||
})
|
})
|
||||||
this.colorFullCylinder.material.opacity = 0.14
|
this.colorFullCylinder.material.opacity = 0.15
|
||||||
|
|
||||||
this.globalRotation = 0.028
|
this.globalRotation = 0.028
|
||||||
this.darkTextureRotation = 0.006
|
this.darkTextureRotation = 0.006
|
||||||
|
|||||||
Reference in New Issue
Block a user