RETRO THEME

This commit is contained in:
Adrien MALINGREY 2024-09-26 01:30:31 +02:00
parent 8ed998f255
commit 7f6795109b
15 changed files with 332 additions and 175 deletions

17
app.js
View File

@ -1,6 +1,6 @@
import * as THREE from 'three'
import { scheduler } from './jsm/scheduler.js'
import { TRANSLATION, ROTATION, environnement, Mino, Playfield, HoldQueue, NextQueue } from './jsm/gamelogic.js'
import { TRANSLATION, ROTATION, environment, Mino, Playfield, HoldQueue, NextQueue } from './jsm/Tetrominoes.js'
import { Settings } from './jsm/Settings.js'
import { Stats } from './jsm/Stats.js'
import { TetraGUI } from './jsm/TetraGUI.js'
@ -29,7 +29,7 @@ let game = {
gui.settings.close()
gui.stats.show()
Mino.mesh.clear()
Mino.meshes.clear()
nextQueue.init()
holdQueue.piece = undefined
@ -298,17 +298,18 @@ const stats = new Stats()
const settings = new Settings()
const scene = new TetraScene(settings, loadingManager)
const controls = new TetraControls(scene.camera, renderer.domElement)
const gui = new TetraGUI(game, settings, stats, scene, controls)
scene.add(Mino.mesh)
scene.add(Mino.meshes)
const holdQueue = new HoldQueue()
scene.add(holdQueue)
const playfield = new Playfield()
const playfield = new Playfield(loadingManager)
scene.add(playfield)
const nextQueue = new NextQueue()
scene.add(nextQueue)
const gui = new TetraGUI(game, settings, stats, scene, controls, playfield)
gui.load()
messagesSpan.onanimationend = function (event) {
event.target.remove()
}
@ -321,12 +322,12 @@ function animate() {
scene.updateMatrixWorld()
scene.update(delta)
playfield.update(delta)
Mino.mesh.update()
Mino.meshes.update()
controls.update()
gui.update()
renderer.render(scene, scene.camera)
environnement.camera.update(renderer, scene)
environment.camera.update(renderer, scene)
}
window.addEventListener("resize", () => {

Binary file not shown.

BIN
images/edge.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

BIN
images/sprites.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 792 B

View File

@ -6,8 +6,8 @@
<meta charset="utf-8" />
<title>teTra</title>
<link rel="icon" href="favicon.ico">
<link rel="stylesheet" href="style.css">
<link rel="stylesheet" href="loading.css">
<link rel="stylesheet" href="css/style.css">
<link rel="stylesheet" href="css/loading.css">
<script async src="https://unpkg.com/es-module-shims@1.6.3/dist/es-module-shims.js"></script>
<script type="importmap">
{
@ -35,6 +35,6 @@
<audio id="music" src="audio/benevolence.m4a" loop></audio>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha3/dist/js/bootstrap.bundle.min.js" integrity="sha384-ENjdO4Dr2bkBIFxQpeoTz1HIcje39Wm4jDKdf19U8gI4ddQ3GYNS7NTKfAdVQSZe" crossorigin="anonymous"></script>
<script type="module" src="app.js"></script>
<script>navigator?.serviceWorker.register('./jsm/service-worker.js')</script>
<script>navigator?.serviceWorker?.register('./jsm/service-worker.js')</script>
</body>
</html>

View File

@ -1,5 +1,5 @@
import { Clock } from 'three'
import { T_SPIN } from './gamelogic.js'
import { T_SPIN } from './Tetrominoes.js'
// score = AWARDED_LINE_CLEARS[tSpin][nbClearedLines]

View File

@ -10,8 +10,8 @@ class TetraControls extends OrbitControls {
this.dampingFactor = 0.04
this.maxDistance = 21
this.keys = {}
this.minPolarAngle = 0.9
this.maxPolarAngle = 2.14
this.minPolarAngle = 1
this.maxPolarAngle = 2.1
this.minAzimuthAngle = 0.9 - Math.PI / 2
this.maxAzimuthAngle = 2.14 - Math.PI / 2
this.target.set(5, 7, 0)

View File

@ -1,92 +1,15 @@
import * as THREE from 'three'
import { GUI } from 'three/addons/libs/lil-gui.module.min.js'
import * as FPS from 'three/addons/libs/stats.module.js'
import { Mino, environnement } from './gamelogic.js'
let jsKeyRenamer = new Proxy({
["←"]: "ArrowLeft",
["→"]: "ArrowRight",
["↑"]: "ArrowUp",
["↓"]: "ArrowDown",
["Espace"]: " ",
["Échap."]: "Escape",
["Ret. arrière"]: "Backspace",
["Entrée"]: "Enter",
}, {
get(obj, keyName) {
return keyName in obj ? obj[keyName] : keyName
}
})
let friendyKeyRenamer = new Proxy({
["ArrowLeft"]: "←",
["ArrowRight"]: "→",
["ArrowUp"]: "↑",
["ArrowDown"]: "↓",
[" "]: "Espace",
["Escape"]: "Échap.",
["Backspace"]: "Ret. arrière",
["Enter"]: "Entrée",
}, {
get(obj, keyName) {
return keyName in obj ? obj[keyName] : keyName.toUpperCase()
}
})
import { Mino, environment } from './Tetrominoes.js'
export class TetraGUI extends GUI {
constructor(game, settings, stats, scene, controls) {
constructor(game, settings, stats, scene, controls, playfield, loadingManager) {
super({title: "teTra"})
this.startLevel = 1
let keyMaps = {
key: {},
action: {}
}
this.key = new Proxy(keyMaps, {
set(km, action, key) {
key = jsKeyRenamer[key]
km.action[key.toLowerCase()] = action
return km.key[action] = key
},
has(km, action) {
return action in km.key
},
get(km, action) {
return friendyKeyRenamer[km.key[action]]
}
})
this.action = new Proxy(keyMaps, {
set(km, key, action) {
km.key[action] = key
return km.action[key.toLowerCase()] = action
},
has(km, key) {
return key.toLowerCase() in km.action
},
get(km, key) {
return km.action[key.toLowerCase()]
}
})
this.key.moveLeft = "ArrowLeft"
this.key.moveRight = "ArrowRight"
this.key.rotateCCW = "w"
this.key.rotateCW = "ArrowUp"
this.key.softDrop = "ArrowDown"
this.key.hardDrop = " "
this.key.hold = "c"
this.key.pause = "Escape"
this.arrDelay = 50
this.dasDelay = 300
this.musicVolume = 50
this.sfxVolume = 50
this.startButton = this.add(game, "start").name("Jouer").hide()
this.pauseButton = this.add(game, "pause").name("Pause").hide()
this.startButton = this.add(game, "start").name("Jouer").hide()
this.pauseButton = this.add(game, "pause").name("Pause").hide()
this.resumeButton = this.add(game, "resume").name("Reprendre").hide()
this.stats = this.addFolder("Stats").hide()
@ -104,25 +27,21 @@ export class TetraGUI extends GUI {
this.settings = this.addFolder("Options").open()
this.settings.add(settings, "startLevel").name("Niveau initial").min(1).max(15).step(1)
this.settings.add(settings, "theme", ["Plasma", "Espace"]).name("Thème").onChange(theme => {
scene.vortex.theme = theme
switch (theme) {
case "Plasma":
scene.ambientLight.intensity = 0.6
scene.directionalLight.intensity = 5
Mino.mesh.material.opacity = 0.7
Mino.mesh.material.roughness = 0.48
Mino.mesh.material.metalness = 0.9
break
case "Espace":
scene.ambientLight.intensity = 20
scene.directionalLight.intensity = 10
Mino.mesh.material.opacity = 0.6
Mino.mesh.material.roughness = 0.05
Mino.mesh.material.metalness = 0.997
break
this.settings.add(settings, "theme", ["Plasma", "Espace", "Rétro"]).name("Thème").onChange(theme => {
scene.theme = theme
Mino.meshes.material = Mino.materials[theme]
if (theme == "Rétro") {
playfield.edge.visible = false
playfield.retroEdge.visible = true
Mino.meshes.resetColor()
Mino.meshes.update = Mino.meshes.updateOffset
music.src = "audio/Tetris_MkVaffQuasi_Ultimix_OC_ReMix.mp3"
} else {
playfield.edge.visible = true
playfield.retroEdge.visible = false
Mino.meshes.update = Mino.meshes.updateColor
music.src = "audio/benevolence.m4a"
}
})
@ -184,11 +103,11 @@ export class TetraGUI extends GUI {
function changeMaterial(type) {
material?.destroy()
material = dev.addFolder("minoes material")
material.add(Mino.mesh.material, "constructor", ["MeshBasicMaterial", "MeshStandardMaterial", "MeshPhysicalMaterial"]).name("type").onChange(changeMaterial)
material.add(Mino.meshes.material, "constructor", ["MeshBasicMaterial", "MeshStandardMaterial", "MeshPhysicalMaterial"]).name("type").onChange(changeMaterial)
switch(type) {
case "MeshBasicMaterial":
Mino.mesh.material = new THREE.MeshBasicMaterial({
envMap: environnement,
Mino.meshes.material = new THREE.MeshBasicMaterial({
envMap: environment,
side: THREE.DoubleSide,
transparent: true,
opacity: 0.5,
@ -196,8 +115,8 @@ export class TetraGUI extends GUI {
})
break
case "MeshStandardMaterial":
Mino.mesh.material = new THREE.MeshStandardMaterial({
envMap: environnement,
Mino.meshes.material = new THREE.MeshStandardMaterial({
envMap: environment,
side: THREE.DoubleSide,
transparent: true,
opacity: 0.7,
@ -206,8 +125,8 @@ export class TetraGUI extends GUI {
})
break
case "MeshPhysicalMaterial":
Mino.mesh.material = new THREE.MeshPhysicalMaterial({
envMap: environnement,
Mino.meshes.material = new THREE.MeshPhysicalMaterial({
envMap: environment,
side: THREE.DoubleSide,
transparent: true,
opacity: 0.6,
@ -223,17 +142,17 @@ export class TetraGUI extends GUI {
})
break
}
if ("opacity" in Mino.mesh.material) material.add(Mino.mesh.material, "opacity" ).min(0).max(1).listen()
if ("reflectivity" in Mino.mesh.material) material.add(Mino.mesh.material, "reflectivity" ).min(0).max(1).listen()
if ("roughness" in Mino.mesh.material) material.add(Mino.mesh.material, "roughness" ).min(0).max(1).listen()
if ("metalness" in Mino.mesh.material) material.add(Mino.mesh.material, "metalness" ).min(0).max(1).listen()
if ("attenuationDistance" in Mino.mesh.material) material.add(Mino.mesh.material, "attenuationDistance").min(0).listen()
if ("ior" in Mino.mesh.material) material.add(Mino.mesh.material, "ior" ).min(1).max(2).listen()
if ("sheen" in Mino.mesh.material) material.add(Mino.mesh.material, "sheen" ).min(0).max(1).listen()
if ("sheenRoughness" in Mino.mesh.material) material.add(Mino.mesh.material, "sheenRoughness" ).min(0).max(1).listen()
if ("specularIntensity" in Mino.mesh.material) material.add(Mino.mesh.material, "specularIntensity" ).min(0).max(1).listen()
if ("thickness" in Mino.mesh.material) material.add(Mino.mesh.material, "thickness" ).min(0).max(5).listen()
if ("transmission" in Mino.mesh.material) material.add(Mino.mesh.material, "transmission" ).min(0).max(1).listen()
if ("opacity" in Mino.meshes.material) material.add(Mino.meshes.material, "opacity" ).min(0).max(1).listen()
if ("reflectivity" in Mino.meshes.material) material.add(Mino.meshes.material, "reflectivity" ).min(0).max(1).listen()
if ("roughness" in Mino.meshes.material) material.add(Mino.meshes.material, "roughness" ).min(0).max(1).listen()
if ("metalness" in Mino.meshes.material) material.add(Mino.meshes.material, "metalness" ).min(0).max(1).listen()
if ("attenuationDistance" in Mino.meshes.material) material.add(Mino.meshes.material, "attenuationDistance").min(0).listen()
if ("ior" in Mino.meshes.material) material.add(Mino.meshes.material, "ior" ).min(1).max(2).listen()
if ("sheen" in Mino.meshes.material) material.add(Mino.meshes.material, "sheen" ).min(0).max(1).listen()
if ("sheenRoughness" in Mino.meshes.material) material.add(Mino.meshes.material, "sheenRoughness" ).min(0).max(1).listen()
if ("specularIntensity" in Mino.meshes.material) material.add(Mino.meshes.material, "specularIntensity" ).min(0).max(1).listen()
if ("thickness" in Mino.meshes.material) material.add(Mino.meshes.material, "thickness" ).min(0).max(5).listen()
if ("transmission" in Mino.meshes.material) material.add(Mino.meshes.material, "transmission" ).min(0).max(1).listen()
}
changeMaterial(this.materialType)
material.close()
@ -250,8 +169,6 @@ export class TetraGUI extends GUI {
}))
}
this.load()
}
load() {

View File

@ -44,6 +44,24 @@ export class TetraScene extends THREE.Scene {
}.bind(this))
}
set theme(theme) {
switch (theme) {
case "Plasma":
this.ambientLight.intensity = 0.6
this.directionalLight.intensity = 5
break
case "Espace":
this.ambientLight.intensity = 20
this.directionalLight.intensity = 10
break
case "Rétro":
this.ambientLight.intensity = 1
this.directionalLight.intensity = 10
break
}
this.vortex.theme = theme
}
update(delta) {
this.vortex.update(delta)
}

View File

@ -1,5 +1,6 @@
import * as THREE from 'three'
import { scheduler } from './scheduler.js'
import { TileMaterial } from './TileMaterial.js'
Array.prototype.pick = function () { return this.splice(Math.floor(Math.random() * this.length), 1)[0] }
@ -54,10 +55,17 @@ const COLUMNS = 10
const envRenderTarget = new THREE.WebGLCubeRenderTarget(256)
const environnement = envRenderTarget.texture
environnement.type = THREE.HalfFloatType
environnement.camera = new THREE.CubeCamera(1, 1000, envRenderTarget)
environnement.camera.position.set(5, 10, 0)
const environment = envRenderTarget.texture
environment.type = THREE.HalfFloatType
environment.camera = new THREE.CubeCamera(1, 1000, envRenderTarget)
environment.camera.position.set(5, 10, 0)
const sideMaterial = new THREE.MeshStandardMaterial({
color: 0x222222,
roughness: 0.8,
metalness: 0.8,
})
class InstancedMino extends THREE.InstancedMesh {
@ -65,6 +73,8 @@ class InstancedMino extends THREE.InstancedMesh {
super(geometry, material, count)
this.instances = new Set()
this.count = 0
this.offsets = new Uint8Array(2*count)
this.update = this.updateColor
}
add(instance) {
@ -79,26 +89,83 @@ class InstancedMino extends THREE.InstancedMesh {
this.instances.clear()
}
update() {
setOffsetAt(index, offset) {
this.offsets[index * 2] = offset
}
resetColor() {
if (this.instanceColor) {
this.instanceColor.array.fill(0xffffff)
this.instanceColor.needsUpdate = true
}
}
updateColor() {
this.count = 0
this.instances.forEach(mino => {
if (mino.parent?.visible) {
this.setColorAt(this.count, mino.color)
this.setMatrixAt(this.count, mino.matrixWorld)
this.setColorAt(this.count, mino.color)
this.count++
}
})
if (this.count) {
this.instanceColor.needsUpdate = true
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 instances = new Set()
static mesh
static materials = {
Plasma: new THREE.MeshStandardMaterial({
envMap: environment,
side: THREE.DoubleSide,
transparent: true,
opacity: 0.7,
roughness: 0.48,
metalness: 0.67,
}),
Espace: new THREE.MeshStandardMaterial({
envMap: environment,
side: THREE.DoubleSide,
transparent: true,
opacity: 0.8,
roughness: 0.05,
metalness: 0.997,
}),
Rétro: [sideMaterial, sideMaterial, sideMaterial, sideMaterial, sideMaterial, sideMaterial]
}
static {
new THREE.TextureLoader().load("images/sprites.png", (texture) => {
this.materials.Rétro[0] = new TileMaterial({
color: 0xd0d4c1,
map: texture,
bumpMap: texture,
bumpScale: 4,
roughness: 0.25,
metalness: 0.8,
transparent: true,
}, 8, 8)
})
}
static meshes
static {
let minoFaceShape = new THREE.Shape()
minoFaceShape.moveTo(.1, .1)
@ -116,24 +183,17 @@ class Mino extends THREE.Object3D {
bevelSegments: 1
}
let minoGeometry = new THREE.ExtrudeGeometry(minoFaceShape, minoExtrudeSettings)
let minoMaterial = new THREE.MeshStandardMaterial({
envMap: environnement,
side: THREE.DoubleSide,
transparent: true,
opacity: 0.7,
roughness: 0.48,
metalness: 0.67,
})
this.mesh = new InstancedMino(minoGeometry, minoMaterial, 2*ROWS*COLUMNS)
this.meshes = new InstancedMino(minoGeometry, this.materials.Plasma, 2*ROWS*COLUMNS)
}
constructor(color) {
constructor(color, offset) {
super()
this.color = color
this.offset = offset
this.velocity = P(50 - 100 * Math.random(), 50 - 100 * Math.random(), 50 - 100 * Math.random())
this.rotationAngle = P(Math.random(), Math.random(), Math.random()).normalize()
this.angularVelocity = 5 - 10 * Math.random()
this.constructor.mesh.add(this)
this.constructor.meshes.add(this)
}
explode(delta) {
@ -149,7 +209,7 @@ class Mino extends THREE.Object3D {
}
dispose() {
this.constructor.mesh.delete(this)
this.constructor.meshes.delete(this)
}
}
@ -164,7 +224,7 @@ class Tetromino extends THREE.Group {
constructor(position) {
super()
if (position) this.position.copy(position)
this.minoesPosition[FACING.NORTH].forEach(() => this.add(new Mino(this.freeColor)))
this.minoesPosition[FACING.NORTH].forEach(() => this.add(new Mino(this.freeColor, this.offset)))
this.facing = FACING.NORTH
this.rotatedLast = false
this.rotationPoint4Used = false
@ -251,6 +311,7 @@ class Ghost extends Tetromino {
copy(piece) {
this.position.copy(piece.position)
this.minoesPosition = piece.minoesPosition
//this.children.forEach(mino => mino.offset = piece.offset)
this.facing = piece.facing
this.visible = true
while (this.canMove(TRANSLATION.DOWN)) this.position.y--
@ -260,6 +321,7 @@ Ghost.prototype.freeColor = new THREE.Color(COLORS.GHOST)
Ghost.prototype.minoesPosition = [
[P(0, 0, 0), P(0, 0, 0), P(0, 0, 0), P(0, 0, 0)],
]
Ghost.prototype.offset = 0
class I extends Tetromino { }
@ -276,6 +338,7 @@ I.prototype.srs = [
{ [ROTATION.CW]: [P(0, 0), P(1, 0), P(-2, 0), P(1, -2), P(-2, 1)], [ROTATION.CCW]: [P(0, 0), P(-2, 0), P(1, 0), P(-2, -1), P(1, 2)] },
]
I.prototype.freeColor = new THREE.Color(COLORS.I)
I.prototype.offset = 1
class J extends Tetromino { }
J.prototype.minoesPosition = [
@ -285,8 +348,10 @@ J.prototype.minoesPosition = [
[P(0, 1), P(-1, -1), P(0, 0), P(0, -1)],
]
J.prototype.freeColor = new THREE.Color(COLORS.J)
J.prototype.offset = 2
class L extends Tetromino { }
class L extends Tetromino {
}
L.prototype.minoesPosition = [
[P(-1, 0), P(0, 0), P(1, 0), P(1, 1)],
[P(0, 1), P(0, 0), P(0, -1), P(1, -1)],
@ -294,6 +359,7 @@ L.prototype.minoesPosition = [
[P(0, 1), P(0, 0), P(0, -1), P(-1, 1)],
]
L.prototype.freeColor = new THREE.Color(COLORS.L)
L.prototype.offset = 3
class O extends Tetromino { }
O.prototype.minoesPosition = [
@ -303,6 +369,7 @@ O.prototype.srs = [
{ [ROTATION.CW]: [], [ROTATION.CCW]: [] }
]
O.prototype.freeColor = new THREE.Color(COLORS.O)
O.prototype.offset = 4
class S extends Tetromino { }
S.prototype.minoesPosition = [
@ -312,6 +379,7 @@ S.prototype.minoesPosition = [
[P(-1, 1), P(0, 0), P(-1, 0), P(0, -1)],
]
S.prototype.freeColor = new THREE.Color(COLORS.S)
S.prototype.offset = 5
class T extends Tetromino {
get tSpin() {
@ -339,6 +407,7 @@ T.prototype.tSlots = [
[P(-1, -1), P(-1, 1), P(1, 1), P(1, -1)],
]
T.prototype.freeColor = new THREE.Color(COLORS.T)
T.prototype.offset = 6
class Z extends Tetromino { }
Z.prototype.minoesPosition = [
@ -348,18 +417,19 @@ Z.prototype.minoesPosition = [
[P(0, 1), P(-1, 0), P(0, 0), P(-1, -1)]
]
Z.prototype.freeColor = new THREE.Color(COLORS.Z)
Z.prototype.offset = 7
class Playfield extends THREE.Group {
constructor() {
constructor(loadingManager) {
super()
//this.visible = false
const edgeMaterial = new THREE.MeshStandardMaterial({
color: COLORS.EDGE,
envMap: environnement,
envMap: environment,
transparent: true,
opacity: 0.2,
opacity: 0.3,
roughness: 0.1,
metalness: 0.67,
})
@ -373,14 +443,56 @@ class Playfield extends THREE.Group {
.lineTo(COLUMNS + .3, -.3)
.lineTo(-.3, -.3)
.moveTo(-.3, SKYLINE)
const edge = new THREE.Mesh(
this.edge = new THREE.Mesh(
new THREE.ExtrudeGeometry(edgeShape, {
depth: 1,
bevelEnabled: false,
}),
edgeMaterial
)
this.add(edge)
this.add(this.edge)
const retroEdgeShape = new THREE.Shape()
.moveTo(-1, SKYLINE)
.lineTo(0, SKYLINE)
.lineTo(0, 0)
.lineTo(COLUMNS, 0)
.lineTo(COLUMNS, SKYLINE)
.lineTo(COLUMNS + 1, SKYLINE)
.lineTo(COLUMNS + 1, -1)
.lineTo(-1, -1)
.moveTo(-1, SKYLINE)
const retroEdgeTexture = new THREE.TextureLoader(loadingManager).load("images/edge.png", (texture) => {
texture.wrapS = THREE.RepeatWrapping
texture.wrapT = THREE.RepeatWrapping
})
const retroEdgeMaterial = new THREE.MeshStandardMaterial({
color: 0xd0d4c1,
map: retroEdgeTexture,
bumpMap: retroEdgeTexture,
bumpScale: 0.5,
roughness: 0.25,
metalness: 0.8,
})
this.retroEdge = new THREE.Mesh(
new THREE.ExtrudeGeometry(retroEdgeShape, {
depth: 1,
bevelEnabled: false,
}),
[retroEdgeMaterial, sideMaterial, sideMaterial, sideMaterial, sideMaterial, sideMaterial],
)
this.add(this.retroEdge)
const back = new THREE.Mesh(
new THREE.PlaneGeometry(COLUMNS, SKYLINE),
new THREE.MeshStandardMaterial({
color: 0xc5d0a1,
roughness: 0.9,
metalness: 0.9,
})
)
back.position.set(COLUMNS/2, SKYLINE/2, 0)
this.retroEdge.add(back)
this.retroEdge.visible = false
const positionKF = new THREE.VectorKeyframeTrack('.position', [0, 1, 2], [0, 0, 0, 0, -0.2, 0, 0, 0, 0])
const clip = new THREE.AnimationClip('HardDrop', 3, [positionKF])
@ -515,4 +627,4 @@ class NextQueue extends THREE.Group {
NextQueue.prototype.positions = [P(0, 0), P(0, -3), P(0, -6), P(0, -9), P(0, -12), P(0, -15), P(0, -18)]
export { T_SPIN, FACING, TRANSLATION, ROTATION, COLORS, environnement, Mino, Tetromino, Playfield, HoldQueue, NextQueue }
export { T_SPIN, FACING, TRANSLATION, ROTATION, COLORS, environment, Mino, Tetromino, Playfield, HoldQueue, NextQueue }

93
jsm/TileMaterial.js Normal file
View File

@ -0,0 +1,93 @@
import { MeshStandardMaterial, Texture, Vector2 } from 'three';
export class TileMaterial extends MeshStandardMaterial {
constructor(params, tileSizeX, tileSizeY) {
super(params);
this.tileSize = { value: new Vector2(tileSizeX / this.map.image.width, tileSizeY / this.map.image.height) };
}
onBeforeCompile(shader) {
shader.uniforms.tileSize = this.tileSize
shader.vertexShader = shader.vertexShader.replace(
`void main() {`,
`varying vec2 vUv;
varying vec2 vOffset;
attribute vec2 offset;
void main() {
vUv = uv;
vOffset = offset;
gl_Position = projectionMatrix * modelViewMatrix * instanceMatrix * vec4(position, 1.0);`
)
shader.fragmentShader = `varying vec2 vUv;
varying vec2 vOffset;
//uniform sampler2D map;
uniform vec2 tileSize;
varying vec2 vBumpMapUvOffset;
` + shader.fragmentShader
shader.fragmentShader = shader.fragmentShader.replace(
`#include <map_fragment>`,
`#ifdef USE_MAP
vec4 sampledDiffuseColor = texture2D(map, vUv * tileSize + vOffset * tileSize);
#ifdef DECODE_VIDEO_TEXTURE
// use inline sRGB decode until browsers properly support SRGB8_ALPHA8 with video textures (#26516)
sampledDiffuseColor = vec4( mix( pow( sampledDiffuseColor.rgb * 0.9478672986 + vec3( 0.0521327014 ), vec3( 2.4 ) ), sampledDiffuseColor.rgb * 0.0773993808, vec3( lessThanEqual( sampledDiffuseColor.rgb, vec3( 0.04045 ) ) ) ), sampledDiffuseColor.w );
#endif
diffuseColor *= sampledDiffuseColor;
#endif`
)
shader.fragmentShader = shader.fragmentShader.replace(
`#include <bumpmap_pars_fragment>`,
`#ifdef USE_BUMPMAP
uniform sampler2D bumpMap;
uniform float bumpScale;
// Bump Mapping Unparametrized Surfaces on the GPU by Morten S. Mikkelsen
// https://mmikk.github.io/papers3d/mm_sfgrad_bump.pdf
// Evaluate the derivative of the height w.r.t. screen-space using forward differencing (listing 2)
vec2 dHdxy_fwd() {
vec2 vBumpMapUvOffset = vBumpMapUv * tileSize + vOffset * tileSize;
vec2 dSTdx = dFdx( vBumpMapUvOffset );
vec2 dSTdy = dFdy( vBumpMapUvOffset );
float Hll = bumpScale * texture2D( bumpMap, vBumpMapUvOffset ).x;
float dBx = bumpScale * texture2D( bumpMap, vBumpMapUvOffset + dSTdx ).x - Hll;
float dBy = bumpScale * texture2D( bumpMap, vBumpMapUvOffset + dSTdy ).x - Hll;
return vec2( dBx, dBy );
}
vec3 perturbNormalArb( vec3 surf_pos, vec3 surf_norm, vec2 dHdxy, float faceDirection ) {
// normalize is done to ensure that the bump map looks the same regardless of the texture's scale
vec3 vSigmaX = normalize( dFdx( surf_pos.xyz ) );
vec3 vSigmaY = normalize( dFdy( surf_pos.xyz ) );
vec3 vN = surf_norm; // normalized
vec3 R1 = cross( vSigmaY, vN );
vec3 R2 = cross( vN, vSigmaX );
float fDet = dot( vSigmaX, R1 ) * faceDirection;
vec3 vGrad = sign( fDet ) * ( dHdxy.x * R1 + dHdxy.y * R2 );
return normalize( abs( fDet ) * surf_norm - vGrad );
}
#endif`
)
}
}

View File

@ -15,7 +15,7 @@ export class Vortex extends THREE.Group {
this.colorFullTextureRotation = 0.006
this.colorFullMoveForward = 0.025
const commonCylinderGeometry = new THREE.CylinderGeometry(35, 35, 500, 12, 1, true)
const commonCylinderGeometry = new THREE.CylinderGeometry(35, 35, 1000, 12, 1, true)
this.darkCylinder = new THREE.Mesh(
commonCylinderGeometry,
@ -24,6 +24,7 @@ export class Vortex extends THREE.Group {
blending: THREE.AdditiveBlending,
})
)
this.darkCylinder.position.y = -100
this.add(this.darkCylinder)
this.colorFullCylinder = new THREE.Mesh(
@ -33,6 +34,7 @@ export class Vortex extends THREE.Group {
blending: THREE.AdditiveBlending,
})
)
this.colorFullCylinder.position.y = -100
this.add(this.colorFullCylinder)
this.position.set(5, 10, -10)
@ -44,7 +46,7 @@ export class Vortex extends THREE.Group {
new THREE.TextureLoader(this.loadingManager).load("./images/plasma.jpg", texture => {
texture.wrapS = THREE.RepeatWrapping
texture.wrapT = THREE.MirroredRepeatWrapping
texture.repeat.set(1, 1)
texture.repeat.set(1, 2)
this.darkCylinder.material.map = texture
})
this.darkCylinder.material.opacity = 0.17
@ -52,7 +54,7 @@ export class Vortex extends THREE.Group {
new THREE.TextureLoader(this.loadingManager).load("./images/plasma2.jpg", texture => {
texture.wrapS = THREE.RepeatWrapping
texture.wrapT = THREE.MirroredRepeatWrapping
texture.repeat.set(2, 1)
texture.repeat.set(2, 2)
this.colorFullCylinder.material.map = texture
})
this.colorFullCylinder.material.opacity = 0.7
@ -62,6 +64,8 @@ export class Vortex extends THREE.Group {
this.darkMoveForward = 0.009
this.colorFullTextureRotation = 0.006
this.colorFullMoveForward = 0.025
this.visible = true
break
case "Espace":
@ -86,17 +90,29 @@ export class Vortex extends THREE.Group {
this.darkMoveForward = 0.03
this.colorFullTextureRotation = 0.006
this.colorFullMoveForward = 0.012
this.visible = true
break
case "Rétro":
this.visible = false
break
}
}
update(delta) {
this.rotation.y += this.globalRotation * delta
this.darkCylinder.material.map.offset.y += this.darkMoveForward * delta
this.darkCylinder.material.map.offset.x += this.darkTextureRotation * delta
this.colorFullCylinder.material.map.offset.y += this.colorFullMoveForward * delta
this.colorFullCylinder.material.map.offset.x += this.colorFullTextureRotation * delta
if (this.visible) {
this.rotation.y += this.globalRotation * delta
if (this.darkCylinder.material.map) {
this.darkCylinder.material.map.offset.y += this.darkMoveForward * delta
this.darkCylinder.material.map.offset.x += this.darkTextureRotation * delta
}
if (this.colorFullCylinder.material.map) {
this.colorFullCylinder.material.map.offset.y += this.colorFullMoveForward * delta
this.colorFullCylinder.material.map.offset.x += this.colorFullTextureRotation * delta
}
}
}
}

View File

@ -33,7 +33,7 @@ class Scheduler {
}
const scheduler = new Scheduler
const scheduler = new Scheduler()
export { scheduler }