Skip to content

Instantly share code, notes, and snippets.

@kennyng
Last active February 15, 2016 02:13
Show Gist options
  • Save kennyng/7467fe39eb4165b77a6f to your computer and use it in GitHub Desktop.
Save kennyng/7467fe39eb4165b77a6f to your computer and use it in GitHub Desktop.
3D Particles Simulation

This is a 3D particles simulation created using three.js for CME151: Introduction to Data Visualization (Stanford University, Winter 2016).

<!DOCTYPE html>
<meta charset="utf-8">
<style>
body {
color: #808080;
font-family:Monospace;
font-size:13px;
text-align:center;
background-color: black;
margin: 0px;
overflow: hidden;
}
</style>
<body>
<div><canvas id="three_particle"></canvas></div>
<!-- JavaScript -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r74/three.min.js">
</script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/dat-gui/0.5.1/dat.gui.min.js">
</script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/stats.js/r14/Stats.min.js">
</script>
<script src="three_particle.js"></script>
</body>
// Setup scene, camera, and renderer.
// ----------------------------------
var WIDTH = HEIGHT = 720;
var scene = new THREE.Scene();
// Create a canvas and renderer (use WebGL).
var canvas = document.getElementById("three_particle");
var renderer = new THREE.WebGLRenderer({
canvas: canvas,
antialias: true
});
renderer.setSize(WIDTH, HEIGHT);
var camera = new THREE.PerspectiveCamera(80, WIDTH / HEIGHT, 0.1, 3*WIDTH);
camera.position.set(0, 200, 1000);
camera.rotation.set(-.2, 0, 0);
scene.add(camera);
// Add FPS using Stats.js
var stats = new Stats();
stats.setMode(0); // 0: fps, 1: ms
// document.getElementById("three_particle_container").appendChild(stats.domElement);
document.body.appendChild(stats.domElement);
stats.domElement.style.position = 'absolute';
stats.domElement.style.left = '0px';
stats.domElement.style.top = '0px';
var gui = new dat.GUI();
// document.getElementById('three_particle_container').appendChild(gui.domElement);
// Particle geometry.
// ------------------
var particle = function(width, height) {
this.type = "particle";
this.gravity = -0.05;
this.drag = 0.995;
this.radius = Math.random() * 20;
this.y = 18 * Math.random() - 9;
this.x = 18 * Math.random() - 9;
this.z = 18 * Math.random() - 9;
this.max_v = 3;
this.x_v = Math.random() * 2 * this.max_v - 2 * this.max_v;
this.y_v = Math.random() * this.max_v;
this.z_v = Math.random() * 2 * this.max_v - 2 * this.max_v;
this.rx = 0;
this.ry = 0;
this.rz = 0;
this.rx_v = Math.random() / 10;
this.ry_v = Math.random() / 10;
this.rz_v = Math.random() / 10;
this.hue = 180 * Math.random();
this.y_max = height - (this.radius / 2) - 10;
this.x_max = width - (this.radius / 2) - 10;
this.z_max = width - (this.radius / 2) - 10;
// ThreeJS stuff.
var geom = new THREE.SphereGeometry(this.radius, 20, 12);
this.obj = new THREE.Mesh(geom,
new THREE.MeshPhongMaterial({
color: Math.floor(Math.random() * 0x1000000),
specular: 0x333333,
shininess: 100
}));
this.constrain_x = function() {
if (this.x >= this.x_max) {
this.x = this.x_max;
this.x_v *= -1;
}
if (this.x <= -this.x_max) {
this.x = -this.x_max;
this.x_v *= -1;
}
}
this.constrain_y = function() {
if (this.y >= this.y_max) {
this.y = this.y_max;
this.y_v *= -1;
}
if (this.y <= -this.y_max) {
this.y = -this.y_max;
this.y_v *= -1;
}
}
this.constrain_z = function() {
if (this.z >= this.z_max) {
this.z = this.z_max;
this.z_v *= -1;
}
if (this.z <= -this.z_max) {
this.z = -this.z_max;
this.z_v *= -1;
}
}
this.update = function() {
// Y direction.
if ((Math.abs(this.y_v) > 0)) {
this.y_v *= this.drag;
this.y_v += this.gravity;
this.y += this.y_v;
this.constrain_y();
}
// X direction.
if ((Math.abs(this.x_v) > 0)) {
this.x_v *= this.drag;
this.x += this.x_v;
this.constrain_x();
}
// Z direction.
if ((Math.abs(this.z_v) > 0)) {
this.z_v *= this.drag;
this.z += this.z_v;
this.constrain_z();
}
// Set hue.
this.hue += 1;
this.hue = this.hue % 360;
// Rotateon update.
this.rx += this.rx_v;
this.ry += this.ry_v;
this.rz += this.rz_v;
}
}
// Initialize arent object (like a sub-scene).
var parent = new THREE.Object3D();
// Add Axes
// ---------
// Reference:
// http://soledadpenades.com/articles/three-js-tutorials/drawing-the-coordinate-axes
function buildAxes(length) {
var axes = new THREE.Object3D();
var axisPosX = buildAxis(new THREE.Vector3(0,0,0),
new THREE.Vector3(length, 0, 0), 0xFF0000, false);
var axisNegX = buildAxis(new THREE.Vector3(0, 0, 0),
new THREE.Vector3(-length, 0, 0), 0xFF0000, true);
axes.add(axisPosX); // +X
axes.add(axisNegX); // -X
var axisPosY = buildAxis(new THREE.Vector3(0, 0, 0),
new THREE.Vector3(0, length, 0), 0x00FF00, false);
var axisNegY = buildAxis(new THREE.Vector3(0, 0, 0),
new THREE.Vector3(0, -length, 0 ), 0x00FF00, true);
axes.add(axisPosY); // +Y
axes.add(axisNegY); // -Y
var axisPosZ = buildAxis(new THREE.Vector3(0, 0, 0),
new THREE.Vector3(0, 0, length), 0x0000FF, false);
var axisNegZ = buildAxis(new THREE.Vector3(0, 0, 0),
new THREE.Vector3( 0, 0, -length), 0x0000FF, true);
axes.add(axisPosZ); // +Z
axes.add(axisNegZ); // -Z
return axes;
}
function buildAxis(src, dst, colorHex, dashed) {
var geom = new THREE.Geometry(),
mat;
if (dashed) {
mat = new THREE.LineDashedMaterial({linewidth:3, color: colorHex,
dashSize:3, gapSize:3});
} else {
mat = new THREE.LineBasicMaterial({linewidth:3, color:colorHex});
}
geom.vertices.push(src.clone());
geom.vertices.push(dst.clone());
// This is IMPORTANT, otherwise dashed lines will appear as plain lines.
geom.computeLineDistances();
var axis = new THREE.Line(geom, mat, THREE.LineSegments);
return axis;
}
axes = buildAxes(WIDTH / 2);
parent.add(axes);
/*
Add Bounding Box
----------------
BoundingBoxHelper(object, hex):
object (Object3D) - the object3D to show the world-axis-aligned boundingbox.
hex (optional) - hexadecimal value to define color ex:0x888888
This creates an line object to the bounding box.
github.com/mrdoob/three.js/blob/master/src/extras/helpers/BoundingBoxHelper.js
*/
var bounding_box = new THREE.BoundingBoxHelper(parent); // can be tied to scene
// var bounding_box = new THREE.BoundingBoxHelper( mesh ); // can be tied to mesh
bounding_box.update(); // render
parent.add(bounding_box);
// Add particle objects
// ---------------------
var balls = []; // Array of objects, each object has data for one bouncing ball.
var data = [];
var n = 300;
for (var i = 0; i < n; i++) {
var p = new particle(WIDTH/2, HEIGHT/2);
p.obj.position.set(p.x, p.y, p.z);
parent.add(p.obj);
data.push(p);
}
scene.add(parent);
// Add Light
// ----------
var ambientLight = new THREE.AmbientLight(0x444444);
scene.add(ambientLight);
var directionalLight = new THREE.DirectionalLight(0xffffff);
directionalLight.position.set(10, 10, 10).normalize();
scene.add(directionalLight);
var directionalLight2 = new THREE.DirectionalLight(0xffffff);
directionalLight2.position.set(-10, -10, -10).normalize();
scene.add(directionalLight2);
// Add GUI Controls
// ------------------
var controls = new function() {
this.x_rot_v = 0.02;
this.y_rot_v = 0.02;
this.z_rot_v = 0.02;
this.p_x_rot_v = 0;
this.p_y_rot_v = 0.01;
this.p_z_rot_v = 0;
this.ambient_light = true;
this.direction_light = true;
this.direction_light_2 = true;
}
gui.add(controls, 'x_rot_v', 0, 0.5);
gui.add(controls, 'y_rot_v', 0, 0.5);
gui.add(controls, 'z_rot_v', 0, 0.5);
gui.add(controls, 'p_x_rot_v', 0, 0.5);
gui.add(controls, 'p_y_rot_v', 0, 0.5);
gui.add(controls, 'p_z_rot_v', 0, 0.5);
ambient_light = gui.add(controls, 'ambient_light');
ambient_light.onChange(function (value) {
if (value) {
scene.add(ambientLight);
} else {
scene.remove(ambientLight);
}
});
direction_light = gui.add(controls, 'direction_light');
direction_light.onChange(function (value) {
if (value) {
scene.add(directionalLight);
} else {
scene.remove(directionalLight);
}
});
direction_light_2 = gui.add(controls, 'direction_light_2');
direction_light_2.onChange(function (value) {
if (value) {
scene.add(directionalLight2);
} else {
scene.remove(directionalLight2);
}
});
// Draw Loop
// ----------
function draw() {
// Start recording statistics.
stats.begin();
for (var i = 0; i <n; i++) {
data[i].update();
data[i].obj.position.set(data[i].x, data[i].y, data[i].z);
}
parent.rotation.x += controls.p_x_rot_v;
parent.rotation.y += controls.p_y_rot_v;
parent.rotation.z += controls.p_z_rot_v;
// Render scene.
renderer.render(scene, camera);
// Stop recording statistics.
stats.end();
// Run draw loop again.
requestAnimationFrame(draw);
}
// Start Animation
// ----------------
requestAnimationFrame(draw);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment