Thingiview = function(containerId) {
scope = this;
this.containerId = containerId;
var container = document.getElementById(containerId);
// var stats = null;
var camera = null;
var scene = null;
var renderer = null;
var object = null;
var plane = null;
var ambientLight = null;
var directionalLight = null;
var pointLight = null;
var targetXRotation = 0;
var targetXRotationOnMouseDown = 0;
var mouseX = 0;
var mouseXOnMouseDown = 0;
var targetYRotation = 0;
var targetYRotationOnMouseDown = 0;
var mouseY = 0;
var mouseYOnMouseDown = 0;
var mouseDown = false;
var mouseOver = false;
var windowHalfX = window.innerWidth / 2;
var windowHalfY = window.innerHeight / 2
var view = null;
var infoMessage = null;
var progressBar = null;
var alertBox = null;
var timer = null;
var rotateTimer = null;
var rotateListener = null;
var wasRotating = null;
var cameraView = 'diagonal';
var cameraZoom = 0;
var rotate = false;
var backgroundColor = '#606060';
var objectMaterial = 'solid';
var objectColor = 0xffffff;
var showPlane = true;
var isWebGl = false;
if (document.defaultView && document.defaultView.getComputedStyle) {
var width = parseFloat(document.defaultView.getComputedStyle(container,null).getPropertyValue('width'));
var height = parseFloat(document.defaultView.getComputedStyle(container,null).getPropertyValue('height'));
} else {
var width = parseFloat(container.currentStyle.width);
var height = parseFloat(container.currentStyle.height);
}
var geometry;
this.initScene = function() {
container.style.position = 'relative';
container.innerHTML = '';
camera = new THREE.Camera(45, width/ height, 1, 100000);
camera.updateMatrix();
scene = new THREE.Scene();
ambientLight = new THREE.AmbientLight(0x202020);
scene.addLight(ambientLight);
directionalLight = new THREE.DirectionalLight(0xffffff, 0.75);
directionalLight.position.x = 1;
directionalLight.position.y = 1;
directionalLight.position.z = 2;
directionalLight.position.normalize();
scene.addLight(directionalLight);
pointLight = new THREE.PointLight(0xffffff, 0.3);
pointLight.position.x = 0;
pointLight.position.y = -25;
pointLight.position.z = 10;
scene.addLight(pointLight);
progressBar = document.createElement('div');
progressBar.style.position = 'absolute';
progressBar.style.top = '0px';
progressBar.style.left = '0px';
progressBar.style.backgroundColor = 'red';
progressBar.style.padding = '5px';
progressBar.style.display = 'none';
progressBar.style.overflow = 'visible';
progressBar.style.whiteSpace = 'nowrap';
progressBar.style.zIndex = 100;
container.appendChild(progressBar);
alertBox = document.createElement('div');
alertBox.id = 'alertBox';
alertBox.style.position = 'absolute';
alertBox.style.top = '25%';
alertBox.style.left = '25%';
alertBox.style.width = '50%';
alertBox.style.height = '50%';
alertBox.style.backgroundColor = '#dddddd';
alertBox.style.padding = '10px';
// alertBox.style.overflowY = 'scroll';
alertBox.style.display = 'none';
alertBox.style.zIndex = 100;
container.appendChild(alertBox);
// load a blank object
// this.loadSTLString('');
if (showPlane) {
loadPlaneGeometry();
}
this.setCameraView(cameraView);
this.setObjectMaterial(objectMaterial);
testCanvas = document.createElement('canvas');
try {
if (testCanvas.getContext('experimental-webgl')) {
// showPlane = false;
isWebGl = true;
renderer = new THREE.WebGLRenderer();
// renderer = new THREE.CanvasRenderer();
} else {
renderer = new THREE.CanvasRenderer();
}
} catch(e) {
renderer = new THREE.CanvasRenderer();
// log("failed webgl detection");
}
// renderer.setSize(container.innerWidth, container.innerHeight);
renderer.setSize(width, height);
renderer.domElement.style.backgroundColor = backgroundColor;
container.appendChild(renderer.domElement);
// stats = new Stats();
// stats.domElement.style.position = 'absolute';
// stats.domElement.style.top = '0px';
// container.appendChild(stats.domElement);
// TODO: figure out how to get the render window to resize when window resizes
// window.addEventListener('resize', onContainerResize(), false);
// container.addEventListener('resize', onContainerResize(), false);
// renderer.domElement.addEventListener('mousemove', onRendererMouseMove, false);
window.addEventListener('mousemove', onRendererMouseMove, false);
renderer.domElement.addEventListener('mouseover', onRendererMouseOver, false);
renderer.domElement.addEventListener('mouseout', onRendererMouseOut, false);
renderer.domElement.addEventListener('mousedown', onRendererMouseDown, false);
// renderer.domElement.addEventListener('mouseup', onRendererMouseUp, false);
window.addEventListener('mouseup', onRendererMouseUp, false);
renderer.domElement.addEventListener('touchstart', onRendererTouchStart, false);
renderer.domElement.addEventListener('touchend', onRendererTouchEnd, false);
renderer.domElement.addEventListener('touchmove', onRendererTouchMove, false);
renderer.domElement.addEventListener('DOMMouseScroll', onRendererScroll, false);
renderer.domElement.addEventListener('mousewheel', onRendererScroll, false);
renderer.domElement.addEventListener('gesturechange', onRendererGestureChange, false);
}
// FIXME
// onContainerResize = function(event) {
// width = parseFloat(document.defaultView.getComputedStyle(container,null).getPropertyValue('width'));
// height = parseFloat(document.defaultView.getComputedStyle(container,null).getPropertyValue('height'));
//
// // log("resized width: " + width + ", height: " + height);
//
// if (renderer) {
// renderer.setSize(width, height);
// camera.projectionMatrix = THREE.Matrix4.makePerspective(70, width / height, 1, 10000);
// sceneLoop();
// }
// };
onRendererScroll = function(event) {
event.preventDefault();
var rolled = 0;
if (event.wheelDelta === undefined) {
// Firefox
// The measurement units of the detail and wheelDelta properties are different.
rolled = -40 * event.detail;
} else {
rolled = event.wheelDelta;
}
if (rolled > 0) {
// up
scope.setCameraZoom(+10);
} else {
// down
scope.setCameraZoom(-10);
}
}
onRendererGestureChange = function(event) {
event.preventDefault();
if (event.scale > 1) {
scope.setCameraZoom(+5);
} else {
scope.setCameraZoom(-5);
}
}
onRendererMouseOver = function(event) {
mouseOver = true;
// targetRotation = object.rotation.z;
if (timer == null) {
// log('starting loop');
timer = setInterval(sceneLoop, 1000/60);
}
}
onRendererMouseDown = function(event) {
// log("down");
event.preventDefault();
mouseDown = true;
if(scope.getRotation()){
wasRotating = true;
scope.setRotation(false);
} else {
wasRotating = false;
}
mouseXOnMouseDown = event.clientX - windowHalfX;
mouseYOnMouseDown = event.clientY - windowHalfY;
targetXRotationOnMouseDown = targetXRotation;
targetYRotationOnMouseDown = targetYRotation;
}
onRendererMouseMove = function(event) {
// log("move");
if (mouseDown) {
mouseX = event.clientX - windowHalfX;
// targetXRotation = targetXRotationOnMouseDown + (mouseX - mouseXOnMouseDown) * 0.02;
xrot = targetXRotationOnMouseDown + (mouseX - mouseXOnMouseDown) * 0.02;
mouseY = event.clientY - windowHalfY;
// targetYRotation = targetYRotationOnMouseDown + (mouseY - mouseYOnMouseDown) * 0.02;
yrot = targetYRotationOnMouseDown + (mouseY - mouseYOnMouseDown) * 0.02;
targetXRotation = xrot;
targetYRotation = yrot;
}
}
onRendererMouseUp = function(event) {
// log("up");
if (mouseDown) {
mouseDown = false;
if (!mouseOver) {
clearInterval(timer);
timer = null;
}
if (wasRotating) {
scope.setRotation(true);
}
}
}
onRendererMouseOut = function(event) {
if (!mouseDown) {
clearInterval(timer);
timer = null;
}
mouseOver = false;
}
onRendererTouchStart = function(event) {
targetXRotation = object.rotation.z;
targetYRotation = object.rotation.x;
timer = setInterval(sceneLoop, 1000/60);
if (event.touches.length == 1) {
event.preventDefault();
mouseXOnMouseDown = event.touches[0].pageX - windowHalfX;
targetXRotationOnMouseDown = targetXRotation;
mouseYOnMouseDown = event.touches[0].pageY - windowHalfY;
targetYRotationOnMouseDown = targetYRotation;
}
}
onRendererTouchEnd = function(event) {
clearInterval(timer);
timer = null;
// targetXRotation = object.rotation.z;
// targetYRotation = object.rotation.x;
}
onRendererTouchMove = function(event) {
if (event.touches.length == 1) {
event.preventDefault();
mouseX = event.touches[0].pageX - windowHalfX;
targetXRotation = targetXRotationOnMouseDown + (mouseX - mouseXOnMouseDown) * 0.05;
mouseY = event.touches[0].pageY - windowHalfY;
targetYRotation = targetYRotationOnMouseDown + (mouseY - mouseYOnMouseDown) * 0.05;
}
}
sceneLoop = function() {
if (object) {
// if (view == 'bottom') {
// if (showPlane) {
// plane.rotation.z = object.rotation.z -= (targetRotation + object.rotation.z) * 0.05;
// } else {
// object.rotation.z -= (targetRotation + object.rotation.z) * 0.05;
// }
// } else {
// if (showPlane) {
// plane.rotation.z = object.rotation.z += (targetRotation - object.rotation.z) * 0.05;
// } else {
// object.rotation.z += (targetRotation - object.rotation.z) * 0.05;
// }
// }
if (showPlane) {
plane.rotation.z = object.rotation.z = (targetXRotation - object.rotation.z) * 0.2;
plane.rotation.x = object.rotation.x = (targetYRotation - object.rotation.x) * 0.2;
} else {
object.rotation.z = (targetXRotation - object.rotation.z) * 0.2;
object.rotation.x = (targetYRotation - object.rotation.x) * 0.2;
}
// log(object.rotation.x);
camera.updateMatrix();
object.updateMatrix();
if (showPlane) {
plane.updateMatrix();
}
renderer.render(scene, camera);
// stats.update();
}
}
rotateLoop = function() {
// targetRotation += 0.01;
targetXRotation += 0.05;
sceneLoop();
}
this.getShowPlane = function(){
return showPlane;
}
this.setShowPlane = function(show) {
showPlane = show;
if (show) {
if (scene && !plane) {
loadPlaneGeometry();
}
plane.material[0].opacity = 1;
// plane.updateMatrix();
} else {
if (scene && plane) {
// alert(plane.material[0].opacity);
plane.material[0].opacity = 0;
// plane.updateMatrix();
}
}
sceneLoop();
}
this.getRotation = function() {
return rotateTimer !== null;
}
this.resetRotation = function () {
if (rotate) {
this.setRotation(false);
this.setRotation(true);
}
}
this.setRotation = function(rotate) {
rotation = rotate;
if (rotate) {
rotateTimer = setInterval(rotateLoop, 1000/60);
} else {
clearInterval(rotateTimer);
rotateTimer = null;
}
scope.onSetRotation();
}
this.onSetRotation = function(callback) {
if(callback === undefined){
if(rotateListener !== null){
try{
rotateListener(scope.getRotation());
} catch(ignored) {}
}
} else {
rotateListener = callback;
}
}
this.setCameraView = function(dir) {
cameraView = dir;
targetXRotation = 0;
targetYRotation = 0;
if (object) {
object.rotation.x = 0;
object.rotation.y = 0;
object.rotation.z = 0;
}
if (showPlane && object) {
plane.rotation.x = object.rotation.x;
plane.rotation.y = object.rotation.y;
plane.rotation.z = object.rotation.z;
}
if (dir == 'top') {
// camera.position.y = 0;
// camera.position.z = 100;
// camera.target.position.z = 0;
if (showPlane) {
plane.flipSided = false;
}
} else if (dir == 'side') {
// camera.position.y = -70;
// camera.position.z = 70;
// camera.target.position.z = 0;
targetYRotation = -4.5;
if (showPlane) {
plane.flipSided = false;
}
} else if (dir == 'bottom') {
// camera.position.y = 0;
// camera.position.z = -100;
// camera.target.position.z = 0;
if (showPlane) {
plane.flipSided = true;
}
} else {
// camera.position.y = -70;
// camera.position.z = 70;
// camera.target.position.z = 0;
if (showPlane) {
plane.flipSided = false;
}
}
mouseX = targetXRotation;
mouseXOnMouseDown = targetXRotation;
mouseY = targetYRotation;
mouseYOnMouseDown = targetYRotation;
scope.centerCamera();
sceneLoop();
}
this.setCameraZoom = function(factor) {
cameraZoom = factor;
if (cameraView == 'bottom') {
if (camera.position.z + factor > 0) {
factor = 0;
}
} else {
if (camera.position.z - factor < 0) {
factor = 0;
}
}
if (cameraView == 'top') {
camera.position.z -= factor;
} else if (cameraView == 'bottom') {
camera.position.z += factor;
} else if (cameraView == 'side') {
camera.position.y += factor;
camera.position.z -= factor;
} else {
camera.position.y += factor;
camera.position.z -= factor;
}
sceneLoop();
}
this.getObjectMaterial = function() {
return objectMaterial;
}
this.setObjectMaterial = function(type) {
objectMaterial = type;
loadObjectGeometry();
}
this.setBackgroundColor = function(color) {
backgroundColor = color
if (renderer) {
renderer.domElement.style.backgroundColor = color;
}
}
this.setObjectColor = function(color) {
objectColor = parseInt(color.replace(/\#/g, ''), 16);
loadObjectGeometry();
}
this.loadSTL = function(url) {
scope.newWorker('loadSTL', url);
}
this.loadOBJ = function(url) {
scope.newWorker('loadOBJ', url);
}
this.loadSTLString = function(STLString) {
scope.newWorker('loadSTLString', STLString);
}
this.loadSTLBinary = function(STLBinary) {
scope.newWorker('loadSTLBinary', STLBinary);
}
this.loadOBJString = function(OBJString) {
scope.newWorker('loadOBJString', OBJString);
}
this.loadJSON = function(url) {
scope.newWorker('loadJSON', url);
}
this.loadPLY = function(url) {
scope.newWorker('loadPLY', url);
}
this.loadPLYString = function(PLYString) {
scope.newWorker('loadPLYString', PLYString);
}
this.loadPLYBinary = function(PLYBinary) {
scope.newWorker('loadPLYBinary', PLYBinary);
}
this.centerCamera = function() {
if (geometry) {
// Using method from http://msdn.microsoft.com/en-us/library/bb197900(v=xnagamestudio.10).aspx
// log("bounding sphere radius = " + geometry.boundingSphere.radius);
// look at the center of the object
camera.target.position.x = geometry.center_x;
camera.target.position.y = geometry.center_y;
camera.target.position.z = geometry.center_z;
// set camera position to center of sphere
camera.position.x = geometry.center_x;
camera.position.y = geometry.center_y;
camera.position.z = geometry.center_z;
// find distance to center
distance = geometry.boundingSphere.radius / Math.sin((camera.fov/2) * (Math.PI / 180));
// zoom backwards about half that distance, I don't think I'm doing the math or backwards vector calculation correctly?
// scope.setCameraZoom(-distance/1.8);
// scope.setCameraZoom(-distance/1.5);
scope.setCameraZoom(-distance/1.9);
directionalLight.position.x = geometry.min_y * 2;
directionalLight.position.y = geometry.min_y * 2;
directionalLight.position.z = geometry.max_z * 2;
pointLight.position.x = geometry.center_y;
pointLight.position.y = geometry.center_y;
pointLight.position.z = geometry.max_z * 2;
} else {
// set to any valid position so it doesn't fail before geometry is available
camera.position.y = -70;
camera.position.z = 70;
camera.target.position.z = 0;
}
}
this.loadArray = function(array) {
log("loading array...");
geometry = new STLGeometry(array);
loadObjectGeometry();
scope.resetRotation();
scope.centerCamera();
log("finished loading " + geometry.faces.length + " faces.");
}
this.newWorker = function(cmd, param) {
scope.setRotation(false);
var worker = new WorkerFacade(thingiurlbase + '/thingiloader.js');
worker.onmessage = function(event) {
if (event.data.status == "complete") {
progressBar.innerHTML = 'Initializing geometry...';
// scene.removeObject(object);
geometry = new STLGeometry(event.data.content);
loadObjectGeometry();
progressBar.innerHTML = '';
progressBar.style.display = 'none';
scope.resetRotation();
log("finished loading " + geometry.faces.length + " faces.");
scope.centerCamera();
} else if (event.data.status == "complete_points") {
progressBar.innerHTML = 'Initializing points...';
geometry = new THREE.Geometry();
var material = new THREE.ParticleBasicMaterial( { color: 0xff0000, opacity: 1 } );
// material = new THREE.ParticleBasicMaterial( { size: 35, sizeAttenuation: false} );
// material.color.setHSV( 1.0, 0.2, 0.8 );
for (i in event.data.content[0]) {
// for (var i=0; i<10; i++) {
vector = new THREE.Vector3( event.data.content[0][i][0], event.data.content[0][i][1], event.data.content[0][i][2] );
geometry.vertices.push( new THREE.Vertex( vector ) );
}
particles = new THREE.ParticleSystem( geometry, material );
particles.sortParticles = true;
particles.updateMatrix();
scene.addObject( particles );
camera.updateMatrix();
renderer.render(scene, camera);
progressBar.innerHTML = '';
progressBar.style.display = 'none';
scope.resetRotation();
log("finished loading " + event.data.content[0].length + " points.");
// scope.centerCamera();
} else if (event.data.status == "progress") {
progressBar.style.display = 'block';
progressBar.style.width = event.data.content;
// log(event.data.content);
} else if (event.data.status == "message") {
progressBar.style.display = 'block';
progressBar.innerHTML = event.data.content;
log(event.data.content);
} else if (event.data.status == "alert") {
scope.displayAlert(event.data.content);
} else {
alert('Error: ' + event.data);
log('Unknown Worker Message: ' + event.data);
}
}
worker.onerror = function(error) {
log(error);
error.preventDefault();
}
worker.postMessage({'cmd':cmd, 'param':param});
}
this.displayAlert = function(msg) {
msg = msg + "