Skip to content

Instantly share code, notes, and snippets.

@cr1901
Created March 21, 2016 04:28
Show Gist options
  • Save cr1901/33cb3fd0fdbb2737b189 to your computer and use it in GitHub Desktop.
Save cr1901/33cb3fd0fdbb2737b189 to your computer and use it in GitHub Desktop.
Solvespace.js Progress
SolvespaceCamera = function( render_width, render_height, scale, up, right, offset ) {
THREE.Camera.call( this );
this.type = 'SolvespaceCamera';
this.render_width = render_width;
this.render_height = render_height;
this.zoom_scale = scale; /* Avoid namespace collision w/ THREE.Object.scale */
this.up = up;
this.right = right;
this.offset = offset;
this.depth_bias = 0;
this.updateProjectionMatrix();
};
SolvespaceCamera.prototype = Object.create( THREE.Camera.prototype );
SolvespaceCamera.prototype.constructor = SolvespaceCamera;
SolvespaceCamera.prototype.updateProjectionMatrix = function() {
temp = new THREE.Matrix4();
offset = new THREE.Matrix4().makeTranslation(this.offset.x, this.offset.y, this.offset.z);
n = new THREE.Vector3().crossVectors(this.up, this.right);
rotate = new THREE.Matrix4().makeBasis(this.right, this.up, n);
/* TODO: If we want perspective, we need an additional matrix
here which will modify w for perspective divide. */
scale = new THREE.Matrix4().makeScale(2*this.zoom_scale/this.render_width,
2*this.zoom_scale/this.render_height, -this.zoom_scale/30000.0);
/* FIXME: Where did the negative sign in the zScale come from?
It's not in Solvespace, but it's required here to prevent the depth buffer
from being reversed. Notably, THREE.js uses an Orthographic projection
which inverts the z-coordinate. */
render_correct = new THREE.Matrix4()
.makeScale(1, 1,
1);
/* FIXME: This adds a (negative, thanks to scaling) bias to the
z coordinate to push certain layers (edges) closer to the front of the
screen. Biases don't match solvespace because solvespace uses Open GL
1.0's glDepthRange to change the offset/normalization. We do it manually
here. Solvespace uses a depth_bias of 2/60000 for edges. This also works
here, but it looks bad. */
// depth_correct = new THREE.Matrix4().makeTranslation(0, 0, this.depth_bias);
temp.multiply( scale );
temp.multiply( offset );
temp.multiply( rotate );
// temp.multiply( depth_correct );
this.projectionMatrix.copy( temp );
};
SolvespaceCamera.prototype.NormalizeProjectionVectors = function() {
/* After rotating, up and right may no longer be orthogonal.
However, their cross product will produce the correct
rotated plane, and we can recover an orthogonal basis. */
var n = new THREE.Vector3().crossVectors(this.right, this.up);
this.up = new THREE.Vector3().crossVectors(n, this.right);
this.right.normalize();
this.up.normalize();
};
SolvespaceCamera.prototype.rotate = function(right, up) {
var oldRight = new THREE.Vector3().copy(this.right).normalize();
var oldUp = new THREE.Vector3().copy(this.up).normalize();
this.up.applyAxisAngle(oldRight, up);
this.right.applyAxisAngle(oldUp, right);
this.NormalizeProjectionVectors();
console.log("this.right", this.right);
console.log("this.up", this.up);
}
SolvespaceControls = function(object, domElement) {
var _this = this;
this.object = object;
this.domElement = domElement;
this.screen = { left: 0, top: 0, width: 0, height: 0 };
var changeEvent = { type: 'change' };
var startEvent = { type: 'start' };
var endEvent = { type: 'end' };
var _changed;
var _rotatePrev = new THREE.Vector2(0, 0);
var _rotateCur = new THREE.Vector2(0, 0);
this.handleEvent = function (event) {
if ( typeof this[ event.type ] == 'function' ) {
this[ event.type ](event);
}
}
function mousedown( event ) {
event.preventDefault();
event.stopPropagation();
console.log(event)
if ( event.button === 1) {
_rotateCur.set(event.screenX, event.screenY);
_rotatePrev.copy(_rotateCur);
}
document.addEventListener( 'mousemove', mousemove, false );
document.addEventListener( 'mouseup', mouseup, false );
}
function mousemove(event) {
_rotateCur.set(event.screenX, event.screenY);
var diff = new THREE.Vector2().subVectors(_rotateCur, _rotatePrev)
.multiplyScalar(1/object.zoom_scale);
object.rotate(0.3*Math.PI/180*diff.x*object.zoom_scale,
0.3*Math.PI/180*diff.y*object.zoom_scale);
_changed = true;
_rotatePrev.copy(_rotateCur);
}
function mouseup(event) {
event.preventDefault();
event.stopPropagation();
document.removeEventListener( 'mousemove', mousemove );
document.removeEventListener( 'mouseup', mouseup );
_this.dispatchEvent( endEvent );
}
this.update = function( ) {
if (_changed) {
_this.dispatchEvent( changeEvent );
_changed = false;
}
}
this.domElement.addEventListener( 'mousedown', mousedown, false);
}
SolvespaceControls.prototype = Object.create( THREE.EventDispatcher.prototype );
SolvespaceControls.prototype.constructor = SolvespaceControls;
solvespace = function(obj, params) {
var scene, edgeScene, camera, edgeCamera, renderer;
var geometry, controls, material, mesh, edges;
var width, height, edgeBias;
var directionalLightArray = [];
if(typeof params === "undefined" || !("width" in params)) {
width = window.innerWidth;
} else {
width = params.width;
}
if(typeof params === "undefined" || !("height" in params)) {
height = window.innerHeight;
} else {
height = params.height;
}
edgeBias = obj.bounds.edgeBias;
domElement = init();
render();
return domElement;
function init() {
scene = new THREE.Scene();
edgeScene = new THREE.Scene();
camera = new SolvespaceCamera(window.innerWidth,
window.innerHeight, 5, new THREE.Vector3(0, 1,0),
new THREE.Vector3(1, 0, 0), new THREE.Vector3(0, 0, 0));
mesh = createMesh(obj);
scene.add(mesh);
edges = createEdges(obj);
edgeScene.add(edges);
for(var i = 0; i < obj.lights.d.length; i++) {
var lightColor = new THREE.Color(obj.lights.d[i].intensity,
obj.lights.d[i].intensity, obj.lights.d[i].intensity);
var directionalLight = new THREE.DirectionalLight(lightColor, 1);
directionalLight.position.set(obj.lights.d[i].direction[0],
obj.lights.d[i].direction[1], obj.lights.d[i].direction[2]);
directionalLightArray.push(directionalLight);
scene.add(directionalLight);
}
var lightColor = new THREE.Color(obj.lights.a, obj.lights.a, obj.lights.a);
var ambientLight = new THREE.AmbientLight(lightColor.getHex());
scene.add(ambientLight);
renderer = new THREE.WebGLRenderer();
renderer.setSize(width, height);
renderer.autoClear = false;
controls = new SolvespaceControls(camera, renderer.domElement);
controls.screen.width = width;
controls.screen.height = height;
controls.addEventListener("change", render);
controls.addEventListener("change", lightUpdate);
//controls.addEventListener("change", setControlsCenter);
animate();
return renderer.domElement;
}
function animate() {
requestAnimationFrame(animate);
controls.update();
}
function render() {
requestAnimationFrame( render );
renderer.clear();
renderer.render(scene, camera);
camera.depth_bias = 0.2;
camera.updateProjectionMatrix();
renderer.render(edgeScene, camera);
camera.depth_bias = 0;
camera.updateProjectionMatrix();
}
function lightUpdate() {
var changeBasis = new THREE.Matrix4();
// The original light positions were in camera space.
// Project them into standard space using camera's basis
// vectors (up, target, and their cross product).
n = new THREE.Vector3().crossVectors(camera.up, camera.right);
changeBasis.makeBasis(camera.right, camera.up, n);
for(var i = 0; i < 2; i++) {
var newLightPos = changeBasis.applyToVector3Array(
[obj.lights.d[i].direction[0], obj.lights.d[i].direction[1],
obj.lights.d[i].direction[2]]);
directionalLightArray[i].position.set(newLightPos[0],
newLightPos[1], newLightPos[2]);
}
}
function setControlsCenter() {
var rect = renderer.domElement.getBoundingClientRect()
controls.screen.left = rect.left + document.body.scrollLeft;
controls.screen.top = rect.top + document.body.scrollTop;
}
function createMesh(mesh_obj) {
var geometry = new THREE.Geometry();
var materialIndex = 0, materialList = [];
var opacitiesSeen = {};
for(var i = 0; i < mesh_obj.points.length; i++) {
geometry.vertices.push(new THREE.Vector3(mesh_obj.points[i][0],
mesh_obj.points[i][1], mesh_obj.points[i][2]));
}
for(var i = 0; i < mesh_obj.faces.length; i++) {
var currOpacity = ((mesh_obj.colors[i] & 0xFF000000) >>> 24)/255.0;
if(opacitiesSeen[currOpacity] === undefined) {
opacitiesSeen[currOpacity] = materialIndex;
materialIndex++;
materialList.push(new THREE.MeshLambertMaterial(
{vertexColors: THREE.FaceColors, opacity: currOpacity,
transparent: true}));
}
geometry.faces.push(new THREE.Face3(mesh_obj.faces[i][0],
mesh_obj.faces[i][1], mesh_obj.faces[i][2],
new THREE.Vector3(mesh_obj.normals[i][0],
mesh_obj.normals[i][1], mesh_obj.normals[i][2]),
new THREE.Color(mesh_obj.colors[i] & 0x00FFFFFF),
opacitiesSeen[currOpacity]));
}
geometry.computeBoundingSphere();
return new THREE.Mesh(geometry, new THREE.MeshFaceMaterial(materialList));
}
function createEdges(mesh_obj) {
var geometry = new THREE.Geometry();
var material = new THREE.LineBasicMaterial();
for(var i = 0; i < mesh_obj.edges.length; i++) {
geometry.vertices.push(new THREE.Vector3(mesh_obj.edges[i][0][0],
mesh_obj.edges[i][0][1], mesh_obj.edges[i][0][2]),
new THREE.Vector3(mesh_obj.edges[i][1][0],
mesh_obj.edges[i][1][1], mesh_obj.edges[i][1][2]));
}
geometry.computeBoundingSphere();
return new THREE.LineSegments(geometry, material);
}
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment