Compare commits

..

No commits in common. "90eb3247e01dd403ca8b8384d041433b2bb79966" and "ae8dcb707786c5bcd96fd931960798c7bb12022d" have entirely different histories.

8 changed files with 302 additions and 110 deletions

8
app.js
View File

@ -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, 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 { TetraGUI } from './jsm/TetraGUI.js' import { TetraGUI } from './jsm/TetraGUI.js'
import TetraControls from './jsm/TetraControls.js' import { TetraControls } from './jsm/TetraControls.js'
import { TetraScene } from './jsm/TetraScene.js' import { TetraScene } from './jsm/TetraScene.js'
@ -277,7 +277,7 @@ renderer.domElement.tabIndex = 1
let loadingManager = new THREE.LoadingManager( let loadingManager = new THREE.LoadingManager(
function() { function() {
loadingDiv.style.display = "none" loaddingCircle.style.display = "none"
gui.startButton.show() gui.startButton.show()
renderer.setAnimationLoop(animate) renderer.setAnimationLoop(animate)
}, },
@ -290,7 +290,7 @@ let loadingManager = new THREE.LoadingManager(
) )
loadingManager.onStart = function (url, itemsLoaded, itemsTotal) { loadingManager.onStart = function (url, itemsLoaded, itemsTotal) {
loadingPercent.innerText = "0%" loadingPercent.innerText = "0%"
loadingDiv.style.display = "block" loaddingCircle.style.display = "block"
} }
const stats = new Stats() const stats = new Stats()

View File

@ -1,64 +1,279 @@
body { background-color: black; } @-webkit-keyframes outerRotate1 {
0% {
transform: translate(-50%, -50%) rotate(0);
}
#loadingDiv { 100% {
box-sizing: border-box; transform: translate(-50%, -50%) rotate(360deg);
font-family: "Open Sans", sans-serif; }
font-size: 1.4rem;
color: hsla(240, 100%, 70%, 0.6);
text-align: center;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
} }
.scene { @-moz-keyframes outerRotate1 {
width: 200px; 0% {
height: 200px; transform: translate(-50%, -50%) rotate(0);
margin: auto; }
perspective: 200px;
100% {
transform: translate(-50%, -50%) rotate(360deg);
}
} }
.cube { @-o-keyframes outerRotate1 {
width: 40px; 0% {
height: 40px; transform: translate(-50%, -50%) rotate(0);
position: relative; }
transform-style: preserve-3d;
transform: translateZ(20px); 100% {
left: 80px; transform: translate(-50%, -50%) rotate(360deg);
top: 80px; }
} }
.cube.is-spinning { @keyframes outerRotate1 {
animation: spinCube 5s infinite ease-in-out; 0% {
transform: translate(-50%, -50%) rotate(0);
}
100% {
transform: translate(-50%, -50%) rotate(360deg);
}
} }
@keyframes spinCube { @-webkit-keyframes outerRotate2 {
0% { transform: translateZ(20px) rotateX( 0deg) rotateY( 0deg) rotateZ( 0deg); } 0% {
100% { transform: translateZ(20px) rotateX(360deg) rotateY( 0deg) rotateZ(360deg); } transform: translate(-50%, -50%) rotate(0);
}
100% {
transform: translate(-50%, -50%) rotate(-360deg);
}
} }
.face { @-moz-keyframes outerRotate2 {
position: absolute; 0% {
width: 40px; transform: translate(-50%, -50%) rotate(0);
height: 40px; }
padding: 0;
background: hsla(240, 100%, 0%, 0.4); 100% {
border: 1px solid hsla(240, 100%, 70%, 0.6); transform: translate(-50%, -50%) rotate(-360deg);
}
} }
.front { transform: rotateY( 0deg) translateZ(20px); } @-o-keyframes outerRotate2 {
.right { transform: rotateY( 90deg) translateZ(20px); } 0% {
.back { transform: rotateY(180deg) translateZ(20px); } transform: translate(-50%, -50%) rotate(0);
.left { transform: rotateY(-90deg) translateZ(20px); } }
.top { transform: rotateX( 90deg) translateZ(20px); }
.bottom { transform: rotateX(-90deg) translateZ(20px); }
.cube.is-backface-hidden .face { 100% {
backface-visibility: hidden; transform: translate(-50%, -50%) rotate(-360deg);
}
} }
.mino0 { left: -40px; top: -20px; } @keyframes outerRotate2 {
.mino1 { left: 0px; top: -20px; } 0% {
.mino2 { left: 40px; top: -20px; } transform: translate(-50%, -50%) rotate(0);
.mino3 { left: 0px; top: 20px; } }
100% {
transform: translate(-50%, -50%) rotate(-360deg);
}
}
@-webkit-keyframes textColour {
0% {
color: #fff;
}
100% {
color: #3BB2D0;
}
}
@-moz-keyframes textColour {
0% {
color: #fff;
}
100% {
color: #3BB2D0;
}
}
@-o-keyframes textColour {
0% {
color: #fff;
}
100% {
color: #3BB2D0;
}
}
@keyframes textColour {
0% {
color: #fff;
}
100% {
color: #3BB2D0;
}
}
body {
background-color: #222;
}
#loaddingCircle {
margin: 0;
padding: 0;
width: 100vw;
height: 100vh;
cursor: progress;
}
.e-loadholder {
position: absolute;
top: 50%;
left: 50%;
-webkit-transform: translate(-51%, -50%);
-moz-transform: translate(-51%, -50%);
-ms-transform: translate(-51%, -50%);
-o-transform: translate(-51%, -50%);
transform: translate(-51%, -50%);
width: 240px;
height: 240px;
border: 5px solid #1B5F70;
border-radius: 120px;
box-sizing: border-box;
}
.e-loadholder:after {
position: absolute;
top: 50%;
left: 50%;
-webkit-transform: translate(-51%, -50%);
-moz-transform: translate(-51%, -50%);
-ms-transform: translate(-51%, -50%);
-o-transform: translate(-51%, -50%);
transform: translate(-51%, -50%);
content: " ";
display: block;
background: #222;
transform-origin: center;
z-index: 0;
}
.e-loadholder:after {
width: 100px;
height: 200%;
-webkit-animation: outerRotate2 30s infinite linear;
-moz-animation: outerRotate2 30s infinite linear;
-o-animation: outerRotate2 30s infinite linear;
animation: outerRotate2 30s infinite linear;
}
.e-loadholder .m-loader {
position: absolute;
top: 50%;
left: 50%;
-webkit-transform: translate(-51%, -50%);
-moz-transform: translate(-51%, -50%);
-ms-transform: translate(-51%, -50%);
-o-transform: translate(-51%, -50%);
transform: translate(-51%, -50%);
width: 200px;
height: 200px;
color: #888;
text-align: center;
border: 5px solid #2a93ae;
border-radius: 100px;
box-sizing: border-box;
z-index: 20;
text-transform: uppercase;
}
.e-loadholder .m-loader:after {
position: absolute;
top: 50%;
left: 50%;
-webkit-transform: translate(-51%, -50%);
-moz-transform: translate(-51%, -50%);
-ms-transform: translate(-51%, -50%);
-o-transform: translate(-51%, -50%);
transform: translate(-51%, -50%);
content: " ";
display: block;
background: #222;
transform-origin: center;
z-index: -1;
}
.e-loadholder .m-loader:after {
width: 100px;
height: 106%;
-webkit-animation: outerRotate1 15s infinite linear;
-moz-animation: outerRotate1 15s infinite linear;
-o-animation: outerRotate1 15s infinite linear;
animation: outerRotate1 15s infinite linear;
}
.e-loadholder .m-loader .e-text {
font-family: "Open Sans", sans-serif;
font-size: 10px;
font-size: 1rem;
position: absolute;
top: 50%;
left: 50%;
-webkit-transform: translate(-51%, -50%);
-moz-transform: translate(-51%, -50%);
-ms-transform: translate(-51%, -50%);
-o-transform: translate(-51%, -50%);
transform: translate(-51%, -50%);
-webkit-animation: textColour 1s alternate linear infinite;
-moz-animation: textColour 1s alternate linear infinite;
-o-animation: textColour 1s alternate linear infinite;
animation: textColour 1s alternate linear infinite;
display: flex;
flex-direction: column;
justify-content: center;
width: 140px;
height: 140px;
text-align: center;
border: 5px solid #3bb2d0;
border-radius: 70px;
box-sizing: border-box;
z-index: 20;
}
.e-loadholder .m-loader .e-text:before, .e-loadholder .m-loader .e-text:after {
position: absolute;
top: 50%;
left: 50%;
-webkit-transform: translate(-51%, -50%);
-moz-transform: translate(-51%, -50%);
-ms-transform: translate(-51%, -50%);
-o-transform: translate(-51%, -50%);
transform: translate(-51%, -50%);
content: " ";
display: block;
background: #222;
transform-origin: center;
z-index: -1;
}
.e-loadholder .m-loader .e-text:before {
width: 110%;
height: 40px;
-webkit-animation: outerRotate2 3.5s infinite linear;
-moz-animation: outerRotate2 3.5s infinite linear;
-o-animation: outerRotate2 3.5s infinite linear;
animation: outerRotate2 3.5s infinite linear;
}
.e-loadholder .m-loader .e-text:after {
width: 40px;
height: 110%;
-webkit-animation: outerRotate1 8s infinite linear;
-moz-animation: outerRotate1 8s infinite linear;
-o-animation: outerRotate1 8s infinite linear;
animation: outerRotate1 8s infinite linear;
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 761 B

After

Width:  |  Height:  |  Size: 792 B

View File

@ -20,39 +20,15 @@
</head> </head>
<body> <body>
<div id="loadingDiv"> <div id="loaddingCircle">
<div class="scene"> <div class="e-loadholder">
<div class="cube is-spinning"> <div class="m-loader">
<div class="mino0 front face"></div> <span class="e-text">
<div class="mino0 back face"></div>
<div class="mino0 right face"></div>
<div class="mino0 left face"></div>
<div class="mino0 top face"></div>
<div class="mino0 bottom face"></div>
<div class="mino1 front face"></div>
<div class="mino1 back face"></div>
<div class="mino1 right face"></div>
<div class="mino1 left face"></div>
<div class="mino1 top face"></div>
<div class="mino1 bottom face"></div>
<div class="mino2 front face"></div>
<div class="mino2 back face"></div>
<div class="mino2 right face"></div>
<div class="mino2 left face"></div>
<div class="mino2 top face"></div>
<div class="mino2 bottom face"></div>
<div class="mino3 front face"></div>
<div class="mino3 back face"></div>
<div class="mino3 right face"></div>
<div class="mino3 left face"></div>
<div class="mino3 top face"></div>
<div class="mino3 bottom face"></div>
</div>
</div>
<div>
<div>Chargement</div> <div>Chargement</div>
<div id="loadingPercent">0%</div> <div id="loadingPercent">0%</div>
</div> </span>
</div>
</div>
</div> </div>
<span id="messagesSpan"></span> <span id="messagesSpan"></span>
<span id="pauseSpan" tabindex="1">II</span> <span id="pauseSpan" tabindex="1">II</span>

View File

@ -27,7 +27,7 @@ let friendyKeyRenamer = new Proxy({
} }
}) })
export default class Settings { class Settings {
constructor() { constructor() {
this.startLevel = 1 this.startLevel = 1
@ -79,4 +79,7 @@ export default class Settings {
this.theme = "Plasma" this.theme = "Plasma"
} }
} }
export { Settings }

View File

@ -1,7 +1,8 @@
import { OrbitControls } from 'three/addons/controls/OrbitControls.js' import { OrbitControls } from 'three/addons/controls/OrbitControls.js'
export default class TetraControls extends OrbitControls { class TetraControls extends OrbitControls {
constructor(camera, domElement) { constructor(camera, domElement) {
super(camera, domElement) super(camera, domElement)
this.autoRotate this.autoRotate
@ -9,7 +10,7 @@ export default class TetraControls extends OrbitControls {
this.dampingFactor = 0.04 this.dampingFactor = 0.04
this.maxDistance = 21 this.maxDistance = 21
this.keys = {} this.keys = {}
this.minPolarAngle = 1.05 this.minPolarAngle = 1
this.maxPolarAngle = 2.1 this.maxPolarAngle = 2.1
this.minAzimuthAngle = 0.9 - Math.PI / 2 this.minAzimuthAngle = 0.9 - Math.PI / 2
this.maxAzimuthAngle = 2.14 - Math.PI / 2 this.maxAzimuthAngle = 2.14 - Math.PI / 2
@ -18,4 +19,6 @@ export default class TetraControls extends OrbitControls {
this.addEventListener("start", () => domElement.style.cursor = "grabbing") this.addEventListener("start", () => domElement.style.cursor = "grabbing")
this.addEventListener("end", () => domElement.style.cursor = "grab") this.addEventListener("end", () => domElement.style.cursor = "grab")
} }
} }
export { TetraControls }

View File

@ -91,8 +91,7 @@ class InstancedMino extends THREE.InstancedMesh {
} }
setOffsetAt(index, offset) { setOffsetAt(index, offset) {
this.offsets[2*index] = offset.x this.offsets[index * 2] = offset
this.offsets[2*index + 1] = offset.y
} }
resetColor() { resetColor() {
@ -137,9 +136,9 @@ class Mino extends THREE.Object3D {
envMap: environment, envMap: environment,
side: THREE.DoubleSide, side: THREE.DoubleSide,
transparent: true, transparent: true,
opacity: 0.7, opacity: 0.8,
roughness: 0.5, roughness: 0.48,
metalness: 1, metalness: 0.67,
}), }),
Espace: new THREE.MeshStandardMaterial({ Espace: new THREE.MeshStandardMaterial({
envMap: environment, envMap: environment,
@ -296,7 +295,7 @@ class Tetromino extends THREE.Group {
} }
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.facing][rotation] // freedom of movement = srs[this.parent.piece.facing][rotation]
Tetromino.prototype.srs = [ Tetromino.prototype.srs = [
{ [ROTATION.CW]: [P(0, 0), P(-1, 0), P(-1, 1), P(0, -2), P(-1, -2)], [ROTATION.CCW]: [P(0, 0), P(1, 0), P(1, 1), P(0, -2), P(1, -2)] }, { [ROTATION.CW]: [P(0, 0), P(-1, 0), P(-1, 1), P(0, -2), P(-1, -2)], [ROTATION.CCW]: [P(0, 0), P(1, 0), P(1, 1), P(0, -2), P(1, -2)] },
{ [ROTATION.CW]: [P(0, 0), P(1, 0), P(1, -1), P(0, 2), P(1, 2)], [ROTATION.CCW]: [P(0, 0), P(1, 0), P(1, -1), P(0, 2), P(1, 2)] }, { [ROTATION.CW]: [P(0, 0), P(1, 0), P(1, -1), P(0, 2), P(1, 2)], [ROTATION.CCW]: [P(0, 0), P(1, 0), P(1, -1), P(0, 2), P(1, 2)] },
@ -310,7 +309,7 @@ class Ghost extends Tetromino {
copy(piece) { copy(piece) {
this.position.copy(piece.position) this.position.copy(piece.position)
this.minoesPosition = piece.minoesPosition this.minoesPosition = piece.minoesPosition
this.children.forEach(mino => {mino.offset = piece.ghostOffset}) //this.children.forEach(mino => mino.offset = piece.offset)
this.facing = piece.facing this.facing = piece.facing
this.visible = true this.visible = true
while (this.canMove(TRANSLATION.DOWN)) this.position.y-- while (this.canMove(TRANSLATION.DOWN)) this.position.y--
@ -320,6 +319,7 @@ 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)],
] ]
Ghost.prototype.offset = 0
class I extends Tetromino { } class I extends Tetromino { }
@ -336,8 +336,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)] }, { [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.freeColor = new THREE.Color(COLORS.I)
I.prototype.offset = P(0, 1) I.prototype.offset = 1
I.prototype.ghostOffset = P(0, 0)
class J extends Tetromino { } class J extends Tetromino { }
J.prototype.minoesPosition = [ J.prototype.minoesPosition = [
@ -347,8 +346,7 @@ J.prototype.minoesPosition = [
[P(0, 1), P(-1, -1), P(0, 0), P(0, -1)], [P(0, 1), P(-1, -1), P(0, 0), P(0, -1)],
] ]
J.prototype.freeColor = new THREE.Color(COLORS.J) J.prototype.freeColor = new THREE.Color(COLORS.J)
J.prototype.offset = P(1, 1) J.prototype.offset = 2
J.prototype.ghostOffset = P(1, 0)
class L extends Tetromino { class L extends Tetromino {
} }
@ -359,8 +357,7 @@ L.prototype.minoesPosition = [
[P(0, 1), P(0, 0), P(0, -1), P(-1, 1)], [P(0, 1), P(0, 0), P(0, -1), P(-1, 1)],
] ]
L.prototype.freeColor = new THREE.Color(COLORS.L) L.prototype.freeColor = new THREE.Color(COLORS.L)
L.prototype.offset = P(2, 1) L.prototype.offset = 3
L.prototype.ghostOffset = P(2, 0)
class O extends Tetromino { } class O extends Tetromino { }
O.prototype.minoesPosition = [ O.prototype.minoesPosition = [
@ -370,8 +367,7 @@ O.prototype.srs = [
{ [ROTATION.CW]: [], [ROTATION.CCW]: [] } { [ROTATION.CW]: [], [ROTATION.CCW]: [] }
] ]
O.prototype.freeColor = new THREE.Color(COLORS.O) O.prototype.freeColor = new THREE.Color(COLORS.O)
O.prototype.offset = P(3, 1) O.prototype.offset = 4
O.prototype.ghostOffset = P(3, 0)
class S extends Tetromino { } class S extends Tetromino { }
S.prototype.minoesPosition = [ S.prototype.minoesPosition = [
@ -381,8 +377,7 @@ S.prototype.minoesPosition = [
[P(-1, 1), P(0, 0), P(-1, 0), P(0, -1)], [P(-1, 1), P(0, 0), P(-1, 0), P(0, -1)],
] ]
S.prototype.freeColor = new THREE.Color(COLORS.S) S.prototype.freeColor = new THREE.Color(COLORS.S)
S.prototype.offset = P(4, 1) S.prototype.offset = 5
S.prototype.ghostOffset = P(4, 0)
class T extends Tetromino { class T extends Tetromino {
get tSpin() { get tSpin() {
@ -410,8 +405,7 @@ T.prototype.tSlots = [
[P(-1, -1), P(-1, 1), P(1, 1), P(1, -1)], [P(-1, -1), P(-1, 1), P(1, 1), P(1, -1)],
] ]
T.prototype.freeColor = new THREE.Color(COLORS.T) T.prototype.freeColor = new THREE.Color(COLORS.T)
T.prototype.offset = P(5, 1) T.prototype.offset = 6
T.prototype.ghostOffset = P(5, 0)
class Z extends Tetromino { } class Z extends Tetromino { }
Z.prototype.minoesPosition = [ Z.prototype.minoesPosition = [
@ -421,8 +415,7 @@ Z.prototype.minoesPosition = [
[P(0, 1), P(-1, 0), P(0, 0), P(-1, -1)] [P(0, 1), P(-1, 0), P(0, 0), P(-1, -1)]
] ]
Z.prototype.freeColor = new THREE.Color(COLORS.Z) Z.prototype.freeColor = new THREE.Color(COLORS.Z)
Z.prototype.offset = P(6, 1) Z.prototype.offset = 7
Z.prototype.ghostOffset = P(6, 0)
class Playfield extends THREE.Group { class Playfield extends THREE.Group {

View File

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