Skip to content

Instantly share code, notes, and snippets.

@apipkin
Last active February 23, 2019 19:48
Show Gist options
  • Save apipkin/2d39382e53abe9831a7139f25b9766d3 to your computer and use it in GitHub Desktop.
Save apipkin/2d39382e53abe9831a7139f25b9766d3 to your computer and use it in GitHub Desktop.
Bumper Ball
6<canvas id="field" width="100%" height="100%"></canvas>
<!-- <div class="controls">
<label><input type="checkbox" id="ambientLight"> Ambient Light</label>
</div> -->
class Stage {
constructor(scene) {
this.ballProps = {
radius: 0.2,
widthSegments: 20,
heightSegments: 20,
color: 0x550000,
dimColor: 0x110000,
lineColor: 0xDDAAAA
};
this.bumperProps = {
radius: 0.3,
detail: 2,
color: 0x31E815,
dimColor: 0x19750B,
lineColor: 0xBDFFCA
};
this.scene = scene;
this.createFloor();
this.addBall();
this.addBumpers();
this.turnOnLights();
}
createFloor() {
let geometry = new THREE.BoxGeometry(1, 1, 1);
let material = new THREE.MeshPhongMaterial({
color: 0x156289,
emissive: 0x072534,
side: THREE.DoubleSide,
flatShading: true
});
let floor = new THREE.Mesh(geometry, material);
floor.position.set(0,-.1,0);
floor.scale.set(10, .1, 5);
floor.receiveShadow = true;
this.scene.add(floor);
this.floor = floor;
}
turnOnLights() {
const scene = this.scene;
let key = new THREE.PointLight(0xffffff, 1.5, 10000);
key.position.set(-10, 15, 10);
key.lookAt(0, 0, 0);
key.castShadow = true;
key.shadow.camera.near = 1;
key.shadow.camera.far = 25;
key.target = this.ball;
let fill = new THREE.DirectionalLight(0xffffff, 0.5);
fill.position.set(10, 10, 10);
fill.lookAt(0, 0, 0);
let back = new THREE.DirectionalLight(0xffffff, 0.2);
back.position.set(-10, 20, -10);
back.lookAt(0, 0, 0);
let lights = new THREE.Group();
lights.add(key);
// lights.add(fill);
// lights.add(back);
scene.add(lights);
this.lights = lights;
}
addBall() {
let geometry = new THREE.SphereGeometry(this.ballProps.radius, this.ballProps.widthSegments, this.ballProps.heightSegments);
let material = new THREE.MeshPhongMaterial({
color: this.ballProps.color,
emissive: this.ballProps.dimColor,
side: THREE.DoubleSide,
flatShading: true
});
let edgeMaterial = new THREE.LineBasicMaterial({
color: this.ballProps.lineColor,
transparent: true,
opacity: 0.5
});
let ball = new THREE.Mesh(geometry, material);
let ballEdges = new THREE.LineSegments( geometry, edgeMaterial );
ball.receiveShadow = true;
ball.castShadow = true;
let group = new THREE.Group();
group.add(ball);
group.add(ballEdges);
group.position.set(0, this.ballProps.radius, 0);
this.scene.add(group);
this.ball = group;
}
addBumpers() {
let bumpers = [
this.createBumper(),
this.createBumper(),
this.createBumper()
];
bumpers.forEach((bumper, index) => {
switch (index) {
case 0:
bumper.position.set(-3.5, this.bumperProps.radius / 2, 2);
break;
case 1:
bumper.position.set(3.5, this.bumperProps.radius / 2, 2);
break;
case 2:
bumper.position.set(0, this.bumperProps.radius / 2, -2);
break;
}
this.scene.add(bumper);
});
}
createBumper() {
let geometry = new THREE.DodecahedronGeometry(this.bumperProps.radius, this.bumperProps.detail);
let material = new THREE.MeshPhongMaterial({
color: this.bumperProps.color,
emissive: this.bumperProps.dimColor,
side: THREE.DoubleSide,
flatShading: true
});
let edgeMaterial = new THREE.LineBasicMaterial({
color: this.bumperProps.lineColor,
transparent: true,
opacity: 0.5
});
let thing = new THREE.Mesh(geometry, material);
let edges = new THREE.LineSegments( geometry, edgeMaterial );
let group = new THREE.Group();
group.add(thing);
group.add(edges);
group.scale.set(1, 0.25, 1);
let stemGeometryHeight = this.bumperProps.radius * 5;
let stemGeometry = new THREE.CylinderGeometry( 0.1, 0.1, stemGeometryHeight, 12);
let stemMaterial = new THREE.MeshPhongMaterial({ color: 0x333333 });
let stem = new THREE.Mesh(stemGeometry, stemMaterial);
stem.position.set(0, -(stemGeometryHeight / 2), 0);
group.add(stem);
return group;
}
}
class Project {
constructor(canvas) {
if (!canvas){
throw Error('Must provide a canvas node to the constructor.');
}
this.resizeRequestId = null;
this.canvas = canvas;
this.scene = new THREE.Scene();
this.createCamera();
this.createRenderer();
this.createAmbientLight();
this.stage = new Stage(this.scene);
this.bind();
this.sync();
}
createCamera() {
const canvas = this.canvas;
let camera = new THREE.PerspectiveCamera( 50, canvas.width / canvas.height, 0.1, 1000);
camera.position.set(0, 3, 5);
camera.lookAt( new THREE.Vector3(0,0,0) );
var axesHelper = new THREE.AxesHelper( 5 );
this.scene.add( axesHelper );
this.camera = camera;
}
createRenderer() {
let renderer = new THREE.WebGLRenderer({
canvas: field,
antialias: true
});
renderer.setPixelRatio(window.devicePixelRatio * 1.5);
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.BasicShadowMap;
this.renderer = renderer;
}
createAmbientLight() {
let ambientLight = new THREE.AmbientLight(0xffffff, 0.2);
this.scene.add(ambientLight);
}
bind() {
window.addEventListener('resize', this.handleWindowResize.bind(this));
}
sync() {
let camera = this.camera;
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
this.renderer.setSize( window.innerWidth, window.innerHeight );
}
handleWindowResize() {
this.sync();
if (this.resizeRequestId) {
window.cancelAnimationFrame(this.resizeRequestId);
}
this.resizeRequestId = window.requestAnimationFrame(this.render.bind(this));
}
render() {
this.renderer.render( this.scene, this.camera );
}
}
const project = new Project(document.getElementById('field'));
project.render();
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/101/three.min.js"></script>
body {
margin: 0;
}
#field {
box-sizing: border-box;
border: 3px dashed red;
width: 100vw;
height: 100vh;
}
.controls {
position: fixed;
top: 0;
left: 0;
z-index: 200;
background: lightgray;
padding: 1em;
display: flex;
flex-direction: column;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment