SINGLE INSTANCEDMESH

This commit is contained in:
Adrien MALINGREY 2023-07-17 02:22:36 +02:00
parent d2a0e241c8
commit cfa73565f0
3 changed files with 127 additions and 133 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,85 +60,118 @@ 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()
minoFaceShape.moveTo(.1, .1)
minoFaceShape.lineTo(.1, .9)
minoFaceShape.lineTo(.9, .9)
minoFaceShape.lineTo(.9, .1)
minoFaceShape.lineTo(.1, .1)
const minoExtrudeSettings = {
steps: 1,
depth: .8,
bevelEnabled: true,
bevelThickness: .1,
bevelSize: .1,
bevelOffset: 0,
bevelSegments: 1
}
let minoGeometry = new THREE.ExtrudeGeometry(minoFaceShape, minoExtrudeSettings)
/*let minoMaterial = new THREE.MeshBasicMaterial({
envMap: environnement,
side: THREE.DoubleSide,
transparent: true,
opacity: 0.8,
reflectivity: 0.9,
})*/
let minoMaterial = new THREE.MeshStandardMaterial({
envMap: environnement,
side: THREE.DoubleSide,
transparent: true,
opacity: 0.6,
roughness: 0.1,
metalness: 0.95,
})
/*
let minoMaterial = new THREE.MeshPhysicalMaterial({
envMap: environnement,
side: THREE.DoubleSide,
transparent: true,
opacity: 0.6,
roughness: 0.1,
metalness: 0.90,
attenuationDistance: 0.5,
ior: 2,
sheen: 0,
sheenRoughness: 1,
specularIntensity: 1,
thickness: 5,
transmission: 1,
})*/
class Mino extends THREE.Object3D { class Mino extends THREE.Object3D {
constructor(color, x, y, z=0) { static instances = new Set()
static mesh
static {
let minoFaceShape = new THREE.Shape()
minoFaceShape.moveTo(.1, .1)
minoFaceShape.lineTo(.1, .9)
minoFaceShape.lineTo(.9, .9)
minoFaceShape.lineTo(.9, .1)
minoFaceShape.lineTo(.1, .1)
let minoExtrudeSettings = {
steps: 1,
depth: .8,
bevelEnabled: true,
bevelThickness: .1,
bevelSize: .1,
bevelOffset: 0,
bevelSegments: 1
}
let minoGeometry = new THREE.ExtrudeGeometry(minoFaceShape, minoExtrudeSettings)
/*let minoMaterial = new THREE.MeshBasicMaterial({
envMap: environnement,
side: THREE.DoubleSide,
transparent: true,
opacity: 0.8,
reflectivity: 0.9,
})*/
let minoMaterial = new THREE.MeshStandardMaterial({
envMap: environnement,
side: THREE.DoubleSide,
transparent: true,
opacity: 0.6,
roughness: 0.1,
metalness: 0.95,
})
/*
let minoMaterial = new THREE.MeshPhysicalMaterial({
envMap: environnement,
side: THREE.DoubleSide,
transparent: true,
opacity: 0.6,
roughness: 0.1,
metalness: 0.90,
attenuationDistance: 0.5,
ior: 2,
sheen: 0,
sheenRoughness: 1,
specularIntensity: 1,
thickness: 5,
transmission: 1,
})*/
this.mesh = new THREE.InstancedMesh(minoGeometry, minoMaterial, 2*ROWS*COLUMNS)
}
static update(delta) {
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)
} }
update(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)
this.updateMatrix() 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()
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 {
@ -238,6 +262,7 @@ 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
} }
} }
@ -388,13 +413,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 +433,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 +443,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.update(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 +514,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 +535,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 }