SINGLE INSTANCEDMESH
This commit is contained in:
parent
d2a0e241c8
commit
cfa73565f0
5
app.js
5
app.js
@ -1,6 +1,6 @@
|
||||
import * as THREE from 'three'
|
||||
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 { Stats } from './jsm/Stats.js'
|
||||
import { TetraGUI } from './jsm/TetraGUI.js'
|
||||
@ -288,6 +288,8 @@ const gui = new TetraGUI(game, settings, stats, scene)
|
||||
|
||||
const clock = new THREE.Clock()
|
||||
|
||||
scene.add(Mino.mesh)
|
||||
|
||||
const holdQueue = new HoldQueue()
|
||||
scene.add(holdQueue)
|
||||
const playfield = new Playfield()
|
||||
@ -307,6 +309,7 @@ function animate() {
|
||||
const delta = clock.getDelta()
|
||||
scene.update(delta)
|
||||
playfield.update(delta)
|
||||
Mino.update()
|
||||
controls.update()
|
||||
gui.update()
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
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 { COLORS, environnement, minoMaterial, I, J, L, O, S, T, Z, Tetromino } from './gamelogic.js'
|
||||
import { Mino } from './gamelogic.js'
|
||||
|
||||
|
||||
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)
|
||||
|
||||
let material = this.debug.addFolder("minoes material").close()
|
||||
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 ("roughness" in minoMaterial) material.add(minoMaterial, "roughness").min(0).max(1)
|
||||
if ("metalness" in minoMaterial) material.add(minoMaterial, "metalness").min(0).max(1)
|
||||
if ("attenuationDistance" in minoMaterial) material.add(minoMaterial, "attenuationDistance").min(0).max(1)
|
||||
if ("ior" in minoMaterial) material.add(minoMaterial, "ior").min(1).max(2)
|
||||
if ("sheen" in minoMaterial) material.add(minoMaterial, "sheen").min(0).max(1)
|
||||
if ("sheenRoughness" in minoMaterial) material.add(minoMaterial, "sheenRoughness").min(0).max(1)
|
||||
if ("specularIntensity" in minoMaterial) material.add(minoMaterial, "specularIntensity").min(0).max(1)
|
||||
if ("thickness" in minoMaterial) material.add(minoMaterial, "thickness").min(0).max(5)
|
||||
if ("transmission" in minoMaterial) material.add(minoMaterial, "transmission").min(0).max(1)
|
||||
if ("opacity" in Mino.mesh.material) material.add(Mino.mesh.material, "opacity").min(0).max(1)
|
||||
if ("reflectivity" in Mino.mesh.material) material.add(Mino.mesh.material, "reflectivity").min(0).max(1)
|
||||
if ("roughness" in Mino.mesh.material) material.add(Mino.mesh.material, "roughness").min(0).max(1)
|
||||
if ("metalness" in Mino.mesh.material) material.add(Mino.mesh.material, "metalness").min(0).max(1)
|
||||
if ("attenuationDistance" in Mino.mesh.material) material.add(Mino.mesh.material, "attenuationDistance").min(0).max(1)
|
||||
if ("ior" in Mino.mesh.material) material.add(Mino.mesh.material, "ior").min(1).max(2)
|
||||
if ("sheen" in Mino.mesh.material) material.add(Mino.mesh.material, "sheen").min(0).max(1)
|
||||
if ("sheenRoughness" in Mino.mesh.material) material.add(Mino.mesh.material, "sheenRoughness").min(0).max(1)
|
||||
if ("specularIntensity" in Mino.mesh.material) material.add(Mino.mesh.material, "specularIntensity").min(0).max(1)
|
||||
if ("thickness" in Mino.mesh.material) material.add(Mino.mesh.material, "thickness").min(0).max(5)
|
||||
if ("transmission" in Mino.mesh.material) material.add(Mino.mesh.material, "transmission").min(0).max(1)
|
||||
|
||||
this.fps = new FPS.default()
|
||||
document.body.appendChild(this.fps.dom)
|
||||
|
231
jsm/gamelogic.js
231
jsm/gamelogic.js
@ -60,85 +60,118 @@ environnement.camera = new THREE.CubeCamera(1, 1000, envRenderTarget)
|
||||
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 {
|
||||
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()
|
||||
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.rotationAngle = P(Math.random(), Math.random(), Math.random()).normalize()
|
||||
this.angularVelocity = 5 - 10 * Math.random()
|
||||
Mino.instances.add(this)
|
||||
}
|
||||
|
||||
update(delta) {
|
||||
explode(delta) {
|
||||
this.velocity.y += delta * GRAVITY
|
||||
this.position.addScaledVector(this.velocity, delta)
|
||||
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 get random() {
|
||||
if (!this.randomBag.length) this.randomBag = [I, J, L, O, S, T, Z]
|
||||
return this.randomBag.pick()
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super(minoGeometry, undefined, 4)
|
||||
this.material = this.minoMaterial
|
||||
constructor(position) {
|
||||
super()
|
||||
if (position) this.position.copy(position)
|
||||
this.minoesPosition[FACING.NORTH].forEach(position => this.add(new Mino(this.freeColor)))
|
||||
this.facing = FACING.NORTH
|
||||
this.rotatedLast = false
|
||||
this.rotationPoint4Used = false
|
||||
@ -148,12 +181,7 @@ class Tetromino extends THREE.InstancedMesh {
|
||||
|
||||
set facing(facing) {
|
||||
this._facing = facing
|
||||
let matrix4 = new THREE.Matrix4()
|
||||
this.minoesPosition[this.facing].forEach((position, i) => {
|
||||
matrix4.setPosition(position)
|
||||
this.setMatrixAt(i, matrix4)
|
||||
})
|
||||
this.instanceMatrix.needsUpdate = true
|
||||
this.children.forEach((mino, i) => mino.position.copy(this.minoesPosition[facing][i]))
|
||||
}
|
||||
|
||||
get facing() {
|
||||
@ -169,10 +197,7 @@ class Tetromino extends THREE.InstancedMesh {
|
||||
}
|
||||
|
||||
set color(color) {
|
||||
for (let i = 0; i < this.count; i++) {
|
||||
this.setColorAt(i, color)
|
||||
}
|
||||
this.instanceColor.needsUpdate = true
|
||||
this.children.forEach((mino) => mino.color = color)
|
||||
}
|
||||
|
||||
canMove(translation, facing=this.facing) {
|
||||
@ -190,7 +215,6 @@ class Tetromino extends THREE.InstancedMesh {
|
||||
}
|
||||
if (this.canMove(TRANSLATION.DOWN)) {
|
||||
this.locking = false
|
||||
this.parent.ghost.visible = true
|
||||
this.parent.ghost.copy(this)
|
||||
scheduler.clearTimeout(this.onLockDown)
|
||||
} else {
|
||||
@ -238,6 +262,7 @@ class Ghost extends Tetromino {
|
||||
this.minoesPosition = piece.minoesPosition
|
||||
this.facing = piece.facing
|
||||
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.ghost.visible = false
|
||||
|
||||
this.lockedMeshes = new THREE.InstancedMesh(minoGeometry, minoMaterial, 200)
|
||||
this.add(this.lockedMeshes)
|
||||
|
||||
this.freedMinoes = []
|
||||
this.freedMeshes = new THREE.InstancedMesh(minoGeometry, minoMaterial, 200)
|
||||
this.freedMeshes.count = 0
|
||||
this.add(this.freedMeshes)
|
||||
this.freedMinoes = new Set()
|
||||
|
||||
this.init()
|
||||
}
|
||||
@ -414,9 +433,7 @@ class Playfield extends THREE.Group {
|
||||
if (piece) {
|
||||
this.add(piece)
|
||||
piece.position.set(4, SKYLINE)
|
||||
//this.ghost.color = piece.freeColor
|
||||
this.ghost.copy(piece)
|
||||
this.ghost.visible = true
|
||||
}
|
||||
this._piece = piece
|
||||
}
|
||||
@ -426,62 +443,37 @@ class Playfield extends THREE.Group {
|
||||
}
|
||||
|
||||
lock() {
|
||||
this.piece.minoesPosition[this.piece.facing].forEach(position => {
|
||||
position = position.clone()
|
||||
position.add(this.piece.position)
|
||||
if (this.cellIsEmpty(position)) {
|
||||
this.cells[position.y][position.x] = this.piece.freeColor
|
||||
this.piece.locking = false
|
||||
return Array.from(this.piece.children).every(mino => {
|
||||
this.add(mino)
|
||||
mino.position.add(this.piece.position)
|
||||
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() {
|
||||
let nbClearedLines = this.cells.reduceRight((nbClearedLines, row, y) => {
|
||||
if (row.filter(color => color).length == COLUMNS) {
|
||||
row.forEach((color, x) => {
|
||||
this.freedMinoes.push(new Mino(color, x, y))
|
||||
})
|
||||
row.forEach(mino => this.freedMinoes.add(mino))
|
||||
this.cells.splice(y, 1)
|
||||
this.cells.push(Array(COLUMNS))
|
||||
return ++nbClearedLines
|
||||
}
|
||||
return nbClearedLines
|
||||
}, 0)
|
||||
this.updateLockedMinoes()
|
||||
if (nbClearedLines) this.cells.forEach((row, y) => row.forEach((mino, x) => mino.position.set(x, y, 0)))
|
||||
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) {
|
||||
this.freedMinoes.forEach(mino => mino.update(delta))
|
||||
this.freedMinoes = this.freedMinoes.filter(mino =>
|
||||
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
|
||||
}
|
||||
this.freedMinoes.forEach(mino => {
|
||||
if (mino.explode(delta)) this.freedMinoes.delete(this)
|
||||
})
|
||||
}
|
||||
|
||||
update(delta) {
|
||||
@ -522,8 +514,7 @@ class NextQueue extends THREE.Group {
|
||||
|
||||
init() {
|
||||
this.pieces = this.positions.map((position) => {
|
||||
let piece = new Tetromino.random()
|
||||
piece.position.copy(position)
|
||||
let piece = new Tetromino.random(position)
|
||||
this.add(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)]
|
||||
|
||||
|
||||
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 }
|
Loading…
x
Reference in New Issue
Block a user