Skip to content

Instantly share code, notes, and snippets.

@blackslate
Last active December 5, 2015 01:08
Show Gist options
  • Save blackslate/aae4f000d54083f55e0b to your computer and use it in GitHub Desktop.
Save blackslate/aae4f000d54083f55e0b to your computer and use it in GitHub Desktop.
Trackball inset to control a perspective camera
<!-- Demo: http://codepen.io/anon/pen/MKWrOr -->
<!DOCTYPE html>
<html>
<head>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r73/three.js"></script>
<style>
body {
margin: 0;
overflow: hidden;
}
</style>
</head>
<body>
<div id="WebGL-output"></div>
<script>
function init() {
var scene = new THREE.Scene()
var renderer = new THREE.WebGLRenderer()
var camera
var cameras = []
var WIDTH = window.innerWidth
var HEIGHT = window.innerHeight
;(function createPerspectiveCamera(){
var FOV = 45
var ASPECT = WIDTH / HEIGHT
var NEAR = 1
var FAR = 360
camera = new THREE.PerspectiveCamera(FOV, ASPECT, NEAR, FAR)
camera.position.x = 100
camera.position.y = 100
camera.position.z = 100
camera.viewport = { x: 0, y: 0, width: WIDTH, height: HEIGHT }
camera.lookAt(scene.position)
cameras.push(camera)
})()
;(function initializeRenderer(){
renderer.setClearColor(new THREE.Color(0xEEEEFF))
renderer.setSize(WIDTH, HEIGHT)
renderer.autoClear = false;
document.getElementById("WebGL-output").appendChild(renderer.domElement)
;(function render() {
var viewport
renderer.setViewport( 0, 0, WIDTH, HEIGHT );
renderer.clear();
cameras.forEach(function (camera) {
viewport = camera.viewport // custom property
renderer.setViewport(
viewport.x
, viewport.y
, viewport.width
, viewport.height
)
renderer.render(scene, camera)
})
requestAnimationFrame(render)
})()
})()
;(function createCameraController(){
var viewport = {
x: WIDTH - 100
, y: HEIGHT - 100
, width: 100
, height: 100
}
var circle = {
x: WIDTH - 50
, y: 50
, radius: 50
}
var settings = {
viewport: viewport
, circle: circle
}
addCameraController(scene, camera, cameras, settings)
})()
// Something to look at
scene.add(new THREE.AxisHelper(70))
}
function addCameraController(scene, camera, cameras, settings) {
var controlCamera
var viewport = settings.viewport
// For mouse interactions
var centreX = settings.circle.x
var centreY = settings.circle.y
var radius = settings.circle.radius
var radius2 = radius * radius
var rotationMatrix = new THREE.Matrix4()
var pivotMatrix = new THREE.Matrix4()
var startMatrix = new THREE.Matrix4()
var start = new THREE.Vector3()
var end = new THREE.Vector3()
var angle
camera.matrixAutoUpdate = false /** takes control of main camera **/
;(function createControlCameraCubeAndCircle(){
var side = 1
var radius = Math.sqrt(side/2 * side/2 * 3)
;(function createCamera(){
controlCamera = new THREE.OrthographicCamera(
-radius, radius
, radius, -radius
, -radius, radius
);
controlCamera.viewport = viewport
controlCamera.rotation.copy(camera.rotation)
// If matrixAutoUpdate is set immediately, the camera rotation
// is not applied
setTimeout(function () {
controlCamera.matrixAutoUpdate = false
}, 1)
scene.add(controlCamera)
cameras.push( controlCamera )
})()
;(function createCompanionCube(){
var cube = new THREE.Object3D()
var cubeGeometry = new THREE.BoxGeometry( side, side, side )
var lineMaterial = new THREE.LineBasicMaterial({
color: 0xffffff
, transparent: true
, opacity: 0.5
})
var faceMaterial = new THREE.MeshPhongMaterial({
color: 0x006699
, emissive: 0x006699
, shading: THREE.FlatShading
, transparent: true
, opacity: 0.2
})
cube.add(
new THREE.LineSegments(
new THREE.WireframeGeometry( cubeGeometry )
, lineMaterial
)
)
cube.add(
new THREE.Mesh(
cubeGeometry
, faceMaterial
)
)
cube.add(new THREE.AxisHelper(radius))
scene.add(cube);
})()
;(function createCircle(){
var circleGeometry = new THREE.CircleGeometry( radius, 36 );
var material = new THREE.MeshBasicMaterial( {
color: 0xccccff
} );
var circle = new THREE.Mesh( circleGeometry, material );
controlCamera.add( circle );
circle.translateZ(-radius)
})()
})()
window.addEventListener("mousedown", startDrag, false)
function startDrag(event) {
controlCamera.matrixAutoUpdate = false
var x = event.clientX - centreX
var y = centreY - event.clientY
var delta2 = x * x + y * y
if (delta2 > radius2) {
return
}
var z = Math.sqrt(radius2 - delta2)
start.set(x, y, z)
window.addEventListener("mousemove", drag, false)
window.addEventListener("mouseup", stopDrag, false)
function drag(event) {
var delta
x = event.clientX - centreX
y = centreY - event.clientY
delta2 = x * x + y * y
if (delta2 > radius2) {
// constrain to adge of sphere
delta = Math.sqrt(delta2)
x = x / delta * radius
y = y / delta * radius
z = 0
} else {
z = Math.sqrt(radius2 - delta2)
}
end.set(x, y, z)
angle = start.angleTo(end)
start.cross(end).normalize()
rotationMatrix.makeRotationAxis(start, -angle)
controlCamera.matrix.multiply(rotationMatrix)
controlCamera.matrixWorldNeedsUpdate = true
rotationMatrix.extractRotation(camera.matrixWorld)
start.applyMatrix4(rotationMatrix).normalize()
rotationMatrix.makeRotationAxis(start, -angle)
camera.applyMatrix(rotationMatrix)
camera.matrixWorldNeedsUpdate = true
start.copy(end)
}
function stopDrag(event) {
window.removeEventListener("mousemove", drag, false)
window.removeEventListener("mouseup", stopDrag, false)
}
}
}
window.onload = init
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment