A Pen by Anthony Pipkin on CodePen.
Last active
February 23, 2019 19:48
-
-
Save apipkin/2d39382e53abe9831a7139f25b9766d3 to your computer and use it in GitHub Desktop.
Bumper Ball
A Pen by Anthony Pipkin on CodePen.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
6<canvas id="field" width="100%" height="100%"></canvas> | |
<!-- <div class="controls"> | |
<label><input type="checkbox" id="ambientLight"> Ambient Light</label> | |
</div> --> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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(); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/101/three.min.js"></script> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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