Compare commits

..

2 Commits

Author SHA1 Message Date
367f252444 Merge branch 'master' of https://git.malingrey.fr/adrien/tetra 2023-07-17 02:27:44 +02:00
cfa73565f0 SINGLE INSTANCEDMESH 2023-07-17 02:22:36 +02:00
3 changed files with 126 additions and 141 deletions

5
app.js
View File

@ -1,6 +1,6 @@
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, environnement, Playfield, HoldQueue, NextQueue } from './jsm/gamelogic.js' import { TRANSLATION, ROTATION, environnement, Mino, Playfield, HoldQueue, NextQueue } from './jsm/gamelogic.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 { TetraGUI } from './jsm/TetraGUI.js' import { TetraGUI } from './jsm/TetraGUI.js'
@ -288,6 +288,8 @@ const gui = new TetraGUI(game, settings, stats, scene)
const clock = new THREE.Clock() const clock = new THREE.Clock()
scene.add(Mino.mesh)
const holdQueue = new HoldQueue() const holdQueue = new HoldQueue()
scene.add(holdQueue) scene.add(holdQueue)
const playfield = new Playfield() const playfield = new Playfield()
@ -307,6 +309,7 @@ function animate() {
const delta = clock.getDelta() const delta = clock.getDelta()
scene.update(delta) scene.update(delta)
playfield.update(delta) playfield.update(delta)
Mino.update()
controls.update() controls.update()
gui.update() gui.update()

View File

@ -1,7 +1,7 @@
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 * as FPS from 'three/addons/libs/stats.module.js' import * as FPS from 'three/addons/libs/stats.module.js'
import { COLORS, environnement, minoMaterial, I, J, L, O, S, T, Z, Tetromino } from './gamelogic.js' import { Mino } from './gamelogic.js'
export class TetraGUI extends GUI { export class TetraGUI extends GUI {
@ -148,17 +148,17 @@ export class TetraGUI extends GUI {
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)
let material = this.debug.addFolder("minoes material").close() let material = this.debug.addFolder("minoes material").close()
if ("opacity" in minoMaterial) material.add(minoMaterial, "opacity").min(0).max(1) if ("opacity" in Mino.mesh.material) material.add(Mino.mesh.material, "opacity").min(0).max(1)
if ("reflectivity" in minoMaterial) material.add(minoMaterial, "reflectivity").min(0).max(1) if ("reflectivity" in Mino.mesh.material) material.add(Mino.mesh.material, "reflectivity").min(0).max(1)
if ("roughness" in minoMaterial) material.add(minoMaterial, "roughness").min(0).max(1) if ("roughness" in Mino.mesh.material) material.add(Mino.mesh.material, "roughness").min(0).max(1)
if ("metalness" in minoMaterial) material.add(minoMaterial, "metalness").min(0).max(1) if ("metalness" in Mino.mesh.material) material.add(Mino.mesh.material, "metalness").min(0).max(1)
if ("attenuationDistance" in minoMaterial) material.add(minoMaterial, "attenuationDistance").min(0).max(1) if ("attenuationDistance" in Mino.mesh.material) material.add(Mino.mesh.material, "attenuationDistance").min(0).max(1)
if ("ior" in minoMaterial) material.add(minoMaterial, "ior").min(1).max(2) if ("ior" in Mino.mesh.material) material.add(Mino.mesh.material, "ior").min(1).max(2)
if ("sheen" in minoMaterial) material.add(minoMaterial, "sheen").min(0).max(1) if ("sheen" in Mino.mesh.material) material.add(Mino.mesh.material, "sheen").min(0).max(1)
if ("sheenRoughness" in minoMaterial) material.add(minoMaterial, "sheenRoughness").min(0).max(1) if ("sheenRoughness" in Mino.mesh.material) material.add(Mino.mesh.material, "sheenRoughness").min(0).max(1)
if ("specularIntensity" in minoMaterial) material.add(minoMaterial, "specularIntensity").min(0).max(1) if ("specularIntensity" in Mino.mesh.material) material.add(Mino.mesh.material, "specularIntensity").min(0).max(1)
if ("thickness" in minoMaterial) material.add(minoMaterial, "thickness").min(0).max(5) if ("thickness" in Mino.mesh.material) material.add(Mino.mesh.material, "thickness").min(0).max(5)
if ("transmission" in minoMaterial) material.add(minoMaterial, "transmission").min(0).max(1) if ("transmission" in Mino.mesh.material) material.add(Mino.mesh.material, "transmission").min(0).max(1)
this.fps = new FPS.default() this.fps = new FPS.default()
document.body.appendChild(this.fps.dom) document.body.appendChild(this.fps.dom)

View File

@ -60,13 +60,17 @@ environnement.camera = new THREE.CubeCamera(1, 1000, envRenderTarget)
environnement.camera.position.set(5, 10) environnement.camera.position.set(5, 10)
const minoFaceShape = new THREE.Shape() class Mino extends THREE.Object3D {
static instances = new Set()
static mesh
static {
let minoFaceShape = new THREE.Shape()
minoFaceShape.moveTo(.1, .1) minoFaceShape.moveTo(.1, .1)
minoFaceShape.lineTo(.1, .9) minoFaceShape.lineTo(.1, .9)
minoFaceShape.lineTo(.9, .9) minoFaceShape.lineTo(.9, .9)
minoFaceShape.lineTo(.9, .1) minoFaceShape.lineTo(.9, .1)
minoFaceShape.lineTo(.1, .1) minoFaceShape.lineTo(.1, .1)
const minoExtrudeSettings = { let minoExtrudeSettings = {
steps: 1, steps: 1,
depth: .8, depth: .8,
bevelEnabled: true, bevelEnabled: true,
@ -109,36 +113,65 @@ let minoMaterial = new THREE.MeshPhysicalMaterial({
transmission: 1, transmission: 1,
})*/ })*/
this.mesh = new THREE.InstancedMesh(minoGeometry, minoMaterial, 2*ROWS*COLUMNS)
}
class Mino extends THREE.Object3D { static update(delta) {
constructor(color, x, y, z=0) { let i = 0
this.instances.forEach(mino => {
if (mino.parent.visible) {
mino.updateMatrixWorld()
this.mesh.setColorAt(i, mino.color)
this.mesh.setMatrixAt(i, mino.matrixWorld)
i++
}
})
this.mesh.count = i
if (this.mesh.count) {
this.mesh.instanceColor.needsUpdate = true
this.mesh.instanceMatrix.needsUpdate = true
}
}
constructor(color) {
super() super()
this.color = color this.color = color
this.position.set(x, y, z)
this.velocity = P(50 - 100 * Math.random(), 50 - 100 * Math.random(), 50 - 100 * Math.random()) 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.rotationAngle = P(Math.random(), Math.random(), Math.random()).normalize()
this.angularVelocity = 5 - 10 * Math.random() this.angularVelocity = 5 - 10 * Math.random()
Mino.instances.add(this)
} }
explode(delta) { explode(delta) {
this.velocity.y += delta * GRAVITY this.velocity.y += delta * GRAVITY
this.position.addScaledVector(this.velocity, delta) this.position.addScaledVector(this.velocity, delta)
this.rotateOnWorldAxis(this.rotationAngle, delta * this.angularVelocity) this.rotateOnWorldAxis(this.rotationAngle, delta * this.angularVelocity)
if (Math.sqrt(mino.position.x * mino.position.x + mino.position.z * mino.position.z) > 40 || mino.position.y < -50) {
this.dispose()
return false
} else {
this.updateMatrix() this.updateMatrix()
return true
}
}
dispose() {
Mino.instances.delete(this)
} }
} }
class Tetromino extends THREE.InstancedMesh { class Tetromino extends THREE.Group {
static randomBag = [] static randomBag = []
static get random() { static get random() {
if (!this.randomBag.length) this.randomBag = [I, J, L, O, S, T, Z] if (!this.randomBag.length) this.randomBag = [I, J, L, O, S, T, Z]
return this.randomBag.pick() return this.randomBag.pick()
} }
constructor() { constructor(position) {
super(minoGeometry, undefined, 4) super()
this.material = this.minoMaterial if (position) this.position.copy(position)
this.minoesPosition[FACING.NORTH].forEach(position => this.add(new Mino(this.freeColor)))
this.facing = FACING.NORTH this.facing = FACING.NORTH
this.rotatedLast = false this.rotatedLast = false
this.rotationPoint4Used = false this.rotationPoint4Used = false
@ -148,12 +181,7 @@ class Tetromino extends THREE.InstancedMesh {
set facing(facing) { set facing(facing) {
this._facing = facing this._facing = facing
let matrix4 = new THREE.Matrix4() this.children.forEach((mino, i) => mino.position.copy(this.minoesPosition[facing][i]))
this.minoesPosition[this.facing].forEach((position, i) => {
matrix4.setPosition(position)
this.setMatrixAt(i, matrix4)
})
this.instanceMatrix.needsUpdate = true
} }
get facing() { get facing() {
@ -169,10 +197,7 @@ class Tetromino extends THREE.InstancedMesh {
} }
set color(color) { set color(color) {
for (let i = 0; i < this.count; i++) { this.children.forEach((mino) => mino.color = color)
this.setColorAt(i, color)
}
this.instanceColor.needsUpdate = true
} }
canMove(translation, facing=this.facing) { canMove(translation, facing=this.facing) {
@ -190,7 +215,6 @@ class Tetromino extends THREE.InstancedMesh {
} }
if (this.canMove(TRANSLATION.DOWN)) { if (this.canMove(TRANSLATION.DOWN)) {
this.locking = false this.locking = false
this.parent.ghost.visible = true
this.parent.ghost.copy(this) this.parent.ghost.copy(this)
scheduler.clearTimeout(this.onLockDown) scheduler.clearTimeout(this.onLockDown)
} else { } else {
@ -218,7 +242,6 @@ class Tetromino extends THREE.InstancedMesh {
return T_SPIN.NONE return T_SPIN.NONE
} }
} }
Tetromino.prototype.minoMaterial = minoMaterial
Tetromino.prototype.lockingColor = new THREE.Color(COLORS.LOCKING) Tetromino.prototype.lockingColor = new THREE.Color(COLORS.LOCKING)
// Super Rotation System // Super Rotation System
// freedom of movement = srs[this.parent.piece.facing][rotation] // freedom of movement = srs[this.parent.piece.facing][rotation]
@ -238,17 +261,10 @@ class Ghost extends Tetromino {
this.minoesPosition = piece.minoesPosition this.minoesPosition = piece.minoesPosition
this.facing = piece.facing this.facing = piece.facing
while (this.canMove(TRANSLATION.DOWN)) this.position.y-- while (this.canMove(TRANSLATION.DOWN)) this.position.y--
this.visible = true
} }
} }
Ghost.prototype.minoMaterial = new THREE.MeshStandardMaterial({
envMap: environnement,
metalness: 0.9,
roughness: 0.2,
transparent: true,
opacity: 0.15,
side: THREE.DoubleSide,
})
Ghost.prototype.freeColor = new THREE.Color(COLORS.GHOST) Ghost.prototype.freeColor = new THREE.Color(COLORS.GHOST)
Ghost.prototype.minoesPosition = [ Ghost.prototype.minoesPosition = [
[P(0, 0, 0), P(0, 0, 0), P(0, 0, 0), P(0, 0, 0)], [P(0, 0, 0), P(0, 0, 0), P(0, 0, 0), P(0, 0, 0)],
@ -388,13 +404,7 @@ class Playfield extends THREE.Group {
this.add(this.ghost) this.add(this.ghost)
this.ghost.visible = false this.ghost.visible = false
this.lockedMeshes = new THREE.InstancedMesh(minoGeometry, minoMaterial, 200) this.freedMinoes = new Set()
this.add(this.lockedMeshes)
this.freedMinoes = []
this.freedMeshes = new THREE.InstancedMesh(minoGeometry, minoMaterial, 200)
this.freedMeshes.count = 0
this.add(this.freedMeshes)
this.init() this.init()
} }
@ -414,9 +424,7 @@ class Playfield extends THREE.Group {
if (piece) { if (piece) {
this.add(piece) this.add(piece)
piece.position.set(4, SKYLINE) piece.position.set(4, SKYLINE)
//this.ghost.color = piece.freeColor
this.ghost.copy(piece) this.ghost.copy(piece)
this.ghost.visible = true
} }
this._piece = piece this._piece = piece
} }
@ -426,62 +434,37 @@ class Playfield extends THREE.Group {
} }
lock() { lock() {
this.piece.minoesPosition[this.piece.facing].forEach(position => { this.piece.locking = false
position = position.clone() return Array.from(this.piece.children).every(mino => {
position.add(this.piece.position) this.add(mino)
if (this.cellIsEmpty(position)) { mino.position.add(this.piece.position)
this.cells[position.y][position.x] = this.piece.freeColor if (this.cellIsEmpty(mino.position)) {
this.cells[mino.position.y][mino.position.x] = mino
return mino.position.y < SKYLINE
} else {
return false
} }
}) })
this.updateLockedMinoes()
return this.piece.minoesPosition[this.piece.facing].every(position => position.y + this.piece.position.y < SKYLINE)
} }
clearLines() { clearLines() {
let nbClearedLines = this.cells.reduceRight((nbClearedLines, row, y) => { let nbClearedLines = this.cells.reduceRight((nbClearedLines, row, y) => {
if (row.filter(color => color).length == COLUMNS) { if (row.filter(color => color).length == COLUMNS) {
row.forEach((color, x) => { row.forEach(mino => this.freedMinoes.add(mino))
this.freedMinoes.push(new Mino(color, x, y))
})
this.cells.splice(y, 1) this.cells.splice(y, 1)
this.cells.push(Array(COLUMNS)) this.cells.push(Array(COLUMNS))
return ++nbClearedLines return ++nbClearedLines
} }
return nbClearedLines return nbClearedLines
}, 0) }, 0)
this.updateLockedMinoes() if (nbClearedLines) this.cells.forEach((row, y) => row.forEach((mino, x) => mino.position.set(x, y, 0)))
return nbClearedLines return nbClearedLines
} }
updateLockedMinoes() {
let i = 0
let matrix4 = new THREE.Matrix4()
this.cells.forEach((row, y) => row.forEach((color, x) => {
matrix4.setPosition(x, y, 0)
this.lockedMeshes.setMatrixAt(i, matrix4)
this.lockedMeshes.setColorAt(i, color)
i++
}))
this.lockedMeshes.count = i
this.lockedMeshes.instanceMatrix.needsUpdate = true
this.lockedMeshes.instanceColor.needsUpdate = true
}
updateFreedMinoes(delta) { updateFreedMinoes(delta) {
this.freedMinoes.forEach(mino => mino.explode(delta)) this.freedMinoes.forEach(mino => {
this.freedMinoes = this.freedMinoes.filter(mino => if (mino.explode(delta)) this.freedMinoes.delete(this)
Math.sqrt(mino.position.x * mino.position.x + mino.position.z * mino.position.z) <= 40 && mino.position.y < -50
) || []
this.freedMeshes.count = this.freedMinoes.length
if (this.freedMeshes.count) {
this.freedMinoes.forEach((mino, i) => {
this.freedMeshes.setMatrixAt(i, mino.matrix)
this.freedMeshes.setColorAt(i, mino.color)
}) })
this.freedMeshes.instanceMatrix.needsUpdate = true
this.freedMeshes.instanceColor.needsUpdate = true
}
} }
update(delta) { update(delta) {
@ -522,8 +505,7 @@ class NextQueue extends THREE.Group {
init() { init() {
this.pieces = this.positions.map((position) => { this.pieces = this.positions.map((position) => {
let piece = new Tetromino.random() let piece = new Tetromino.random(position)
piece.position.copy(position)
this.add(piece) this.add(piece)
return piece return piece
}) })
@ -544,4 +526,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)] 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, minoMaterial, Tetromino, I, J, L, O, S, T, Z, Playfield, HoldQueue, NextQueue } export { T_SPIN, FACING, TRANSLATION, ROTATION, COLORS, environnement, Mino, Tetromino, Playfield, HoldQueue, NextQueue }