three.js testbed
with orbitControls, XZgrid, info
by RGMGx
HTML
<div id="info">
hw2 Air Hockey Collision<br>
<button id="toggle" style="width:20%" onclick="audio.play()" onclick="audio.pause()">BGM_PLAY</button><br>
</div>
<audio id="collisionsound" style="display:none">
<source src="
https://rgmgx.github.io/sounds/putting_a_bottle2.mp3
" type='audio/mp3'>
</audio>
<audio id="soundtrack" autoplay loop style="display:none">
<source src="https://rgmgx.github.io/sounds/bgm_maoudamashii_ethnic31.mp3" type='audio/mp3'>
</audio>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/84/three.min.js"></script>
<script src="https://threejs.org/build/three.min.js"></script>
<script src="https://code.jquery.com/jquery-2.1.4.min.js"></script>
<script src="https://threejs.org/examples/js/WebGL.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/stats.js/r16/Stats.min.js"></script>
<script src="https://threejs.org/examples/js/objects/Sky.js"></script>
<script src="https://threejs.org/examples/js/controls/OrbitControls.js"></script>
CSS
#info {
position: absolute;
top: 0px;
width: 100%;
padding: 10px;
text-align: center;
color: #ff0000
}
body {
overflow: hidden;
}
JavaScript
var turn = true
$("#toggle").click(function() {
turn = !turn;
});
class Ball {
constructor(mesh, rad = 2, color) {
this.pos = new THREE.Vector3()
this.vel = new THREE.Vector3()
this.force = new THREE.Vector3()
this.mesh = mesh;
this.radius = rad;
this.light = new THREE.PointLight(color, 1, 50);
scene.add(this.light)
scene.add(this.mesh) // add to scene when particle is created
this.mesh.material.color.copy(color.clone());
}
update(dt) {
this.vel.add(this.force.clone().multiplyScalar(dt))
this.pos.add(this.vel.clone().multiplyScalar(dt))
this.collidingPlanes(planes)
this.mesh.position.copy(this.pos)
this.light.position.copy(this.pos)
this.light.position.y += 20
}
collidingPlanes(planes) {
const EPS = 1e-3
const CR = 0.99
for (let i = 0; i < planes.length; i++) {
let plane = planes[i]
let point = this.pos.clone().sub(plane.ptOnPl)
if (point.dot(plane.normal) < EPS + this.radius) {
// position correction
this.pos.copy(plane.ptOnPl.clone().add(point.projectOnPlane(plane.normal)))
this.pos.add(plane.normal.clone().multiplyScalar(this.radius))
// velocity update
this.vel.sub(plane.normal.clone().multiplyScalar((1 + CR) * this.vel.dot(plane.normal)))
//return; // assume particle collides with AT MOST one plane
}
}
}
}
class Plane {
constructor(localPointOnPlane, localNormal, mesh, color) {
this.localPtOnPl = localPointOnPlane.clone();
this.localNormal = localNormal.clone();
this.mesh = mesh; // the graphics representation
scene.add(this.mesh)
}
update() {
this.mesh.updateMatrixWorld() // important!
this.ptOnPl = this.mesh.localToWorld(this.localPtOnPl.clone());
let normalMat = new THREE.Matrix3().getNormalMatrix(this.mesh.matrixWorld);
this.normal = this.localNormal.clone().applyMatrix3(normalMat).normalize()
...