-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
15 changed files
with
2,199 additions
and
0 deletions.
There are no files selected for viewing
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,139 @@ | ||
// Defines several possible options for camera movement. Used as abstraction to stay away from window-system specific input methods | ||
const CameraMovement = { | ||
FORWARD: 0, | ||
BACKWARD: 1, | ||
LEFT: 2, | ||
RIGHT: 3, | ||
UP: 4, | ||
DOWN: 5 | ||
} | ||
|
||
// Default camera values | ||
const YAW = -90.0; | ||
const PITCH = 0.0; | ||
const SPEED = 3.0; | ||
const SENSITIVTY = 0.25; | ||
const ZOOM = 45.0; | ||
|
||
// An abstract camera class that processes input and calculates the corresponding Eular Angles, Vectors and Matrices for use in OpenGL | ||
class Camera3D { | ||
constructor(position = math.matrix([[0],[0],[2]]), up = math.matrix([[0],[1],[0]]), yaw=YAW, pitch=PITCH, movementSpeed=SPEED, mouseSensitivity=SENSITIVTY, zoom=ZOOM){ | ||
this.position = position; | ||
this.worldUp = up; | ||
this.yaw = yaw; | ||
this.pitch = pitch; | ||
this.movementSpeed=movementSpeed; | ||
this.mouseSensitivity=mouseSensitivity; | ||
this.zoom=zoom; | ||
this.updateCameraVectors(); | ||
} | ||
|
||
get viewMatrix(){ | ||
let lookAt = math.zeros(4,4); | ||
set(lookAt, 0, 0, this.right._data[0][0]) | ||
set(lookAt, 0, 1, this.right._data[1][0]) | ||
set(lookAt, 0, 2, this.right._data[2][0]) | ||
|
||
set(lookAt, 1, 0, this.up._data[0][0]) | ||
set(lookAt, 1, 1, this.up._data[1][0]) | ||
set(lookAt, 1, 2, this.up._data[2][0]) | ||
|
||
set(lookAt, 2, 0, this.front._data[0][0]) | ||
set(lookAt, 2, 1, this.front._data[1][0]) | ||
set(lookAt, 2, 2, this.front._data[2][0]) | ||
|
||
set(lookAt, 3, 0, this.position._data[0][0]) | ||
set(lookAt, 3, 1, this.position._data[1][0]) | ||
set(lookAt, 3, 2, this.position._data[2][0]) | ||
|
||
return lookAt | ||
} | ||
|
||
projectionMatrix(w, h){ | ||
let near = 0; | ||
let far = 100000 | ||
|
||
let one_over_tan_eyeangle = 1/math.tan(math.unit(this.zoom, 'deg')); | ||
let aspect = w/h; | ||
|
||
let projection = math.zeros(4,4); | ||
set(projection, 0, 0, one_over_tan_eyeangle / aspect) | ||
set(projection, 1, 1, one_over_tan_eyeangle) | ||
set(projection, 2, 2, (near + far) / (near - far)) | ||
set(projection, 2, 3, 2 * (near * far) / (near - far)) | ||
set(projection, 3, 2, -1) | ||
|
||
return projection; | ||
} | ||
|
||
get modelMatrix(){ | ||
return translateMatrixN(3, this.position) | ||
} | ||
|
||
processKeyboard(direction, deltaTime){ | ||
let velocity = this.movementSpeed * deltaTime; | ||
|
||
if (direction == CameraMovement.FORWARD){ | ||
this.position = math.add(this.position, math.multiply(this.front, velocity)); | ||
} | ||
if (direction == CameraMovement.BACKWARD){ | ||
this.position = math.subtract(this.position, math.multiply(this.front, velocity)); | ||
} | ||
if (direction == CameraMovement.LEFT){ | ||
this.position = math.subtract(this.position, math.multiply(this.right, -velocity)); | ||
} | ||
if (direction == CameraMovement.RIGHT){ | ||
this.position = math.add(this.position, math.multiply(this.right, -velocity)); | ||
} | ||
if (direction == CameraMovement.UP){ | ||
this.position = math.add(this.position, math.multiply(this.up, -velocity)); | ||
} | ||
if (direction == CameraMovement.DOWN){ | ||
this.position = math.subtract(this.position, math.multiply(this.up, -velocity)); | ||
} | ||
} | ||
|
||
// Processes input received from a mouse input system. Expects the offset value in both the x and y direction. | ||
processMouseMovement(xoffset, yoffset, constrainPitch=true){ | ||
xoffset *= this.mouseSensitivity; | ||
yoffset *= this.mouseSensitivity; | ||
|
||
this.yaw -= xoffset; | ||
this.pitch += yoffset; | ||
|
||
// Make sure that when pitch is out of bounds, screen doesn't get flipped | ||
if (constrainPitch) { | ||
if (this.pitch > 89.0) | ||
this.pitch = 89.0; | ||
if (this.pitch < -89.0) | ||
this.pitch = -89.0; | ||
} | ||
|
||
// Update Front, Right and Up Vectors using the updated Eular angles | ||
this.updateCameraVectors(); | ||
} | ||
|
||
processMouseScroll(yoffset) { | ||
if (this.zoom >= 1.0 && this.zoom <= 45.0) | ||
this.zoom -= yoffset; | ||
if (this.zoom <= 1.0) | ||
this.zoom = 1.0; | ||
if (this.zoom >= 45.0) | ||
this.zoom = 45.0; | ||
} | ||
|
||
updateCameraVectors() { | ||
// Calculate the new Front vector | ||
let front = []; | ||
front[0] = math.cos(math.unit(this.yaw, "deg")) * math.cos(math.unit(this.pitch, "deg")) | ||
front[1] = math.sin(math.unit(this.pitch, "deg")) | ||
front[2] = math.sin(math.unit(this.yaw, "deg")) * math.cos(math.unit(this.pitch, "deg")) | ||
front = math.matrix(front) | ||
this.front = normalized(front) | ||
|
||
// Also re-calculate the Right and Up vector | ||
// Normalize the vectors, because their length gets closer to 0 the more you look up or down which results in slower movement. | ||
this.right = normalized(math.flatten(math.cross(this.front, this.worldUp))) | ||
this.up = normalized(math.flatten(math.cross(this.right, this.front))) | ||
} | ||
} |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
const CAMERA_N_EYE_ANGLE = 45.0; | ||
|
||
class CameraND { | ||
constructor(N, position, target, usePerspective = true, eyeAngle = CAMERA_N_EYE_ANGLE){ | ||
assert(rows(position) == N); | ||
assert(N >= 4); | ||
|
||
this.N = N; | ||
|
||
this.position = position; | ||
this.target = target; | ||
this.eyeAngle = eyeAngle; | ||
this.usePerspective = usePerspective; | ||
|
||
this.ups = math.zeros(N, N - 2); | ||
set(this.ups, N - 1, N - 3, 1); | ||
for(let i = 0; i < N - 3; i++){ | ||
set(this.ups, N - 3 - i, i, 1) | ||
} | ||
|
||
//This is added so the user is able to look from the Up axes | ||
//It is a minimal perturbation, which does not affect the rendering | ||
setRowInplace(this.ups, 0, math.multiply(0.0000001, math.ones(N - 2))) | ||
} | ||
|
||
get viewProjectionModelMatrix(){ | ||
return viewMatrixN(this.N, this.position, this.target, this.ups, this.eyeAngle, 0, 0, 0); | ||
} | ||
} |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,102 @@ | ||
|
||
|
||
class CameraNPropertyComponent{ | ||
generateInputOnchange(signal, dimensonCamera, dim){ | ||
return function(e){ | ||
console.log(e) | ||
let isValidDouble = /^\-?[0-9]+(e[0-9]+)?\.?([0-9]+)?$/.test(e.srcElement.value); | ||
if(isValidDouble){ | ||
console.log(signal, dimensonCamera, dim, parseFloat(e.srcElement.value)) | ||
emit(signal, dimensonCamera, dim, parseFloat(e.srcElement.value)) | ||
} | ||
return isValidDouble; | ||
} | ||
} | ||
|
||
constructor(N, originPosition, targetPosition, isEditable, parent){ | ||
const cartesianDimensions = "XYZWVUTSRQPONMLKJIHGFEDCBAzyxwvutsrqponmlkjihgfedcba"; | ||
|
||
this.N = N; | ||
this.firstThreeEye = [] | ||
this.firstThreeTarget = [] | ||
|
||
var mainDiv = document.createElement('div'); | ||
mainDiv.appendChild(document.createTextNode('Dimension ' + N + ' Camera')); | ||
br(mainDiv) | ||
|
||
if(isEditable){ | ||
checkbox(mainDiv, 'camera'+N+'Perspective', 'Perspective', true, false, function(e){emit('signalCameraChangedPerspective', N, e.srcElement.checked)}); | ||
br(mainDiv); | ||
} | ||
|
||
var table = document.createElement('table'); | ||
var titleTr = document.createElement('tr'); | ||
var eyeTitle = document.createElement('th'); | ||
var targetTitle = document.createElement('th'); | ||
|
||
eyeTitle.innerHTML = 'Eye Point'; | ||
targetTitle.innerHTML = 'Target Point'; | ||
titleTr.appendChild(eyeTitle); | ||
titleTr.appendChild(targetTitle); | ||
table.appendChild(titleTr) | ||
|
||
for(let i = 0; i < N; i++){ | ||
var lineTr = document.createElement('tr'); | ||
|
||
var eyeTd = document.createElement('td'); | ||
var labelEye = document.createElement('label'); | ||
var inputEye = document.createElement('input'); | ||
inputEye.id = 'camera'+N+'EyeInput'+i; | ||
inputEye.disabled = !isEditable; | ||
inputEye.value = originPosition[i]; | ||
inputEye.size = 6; | ||
inputEye.style = 'text-align: right; float: right;' | ||
inputEye.onchange = this.generateInputOnchange('signalCameraOriginMovement', N, i); | ||
|
||
labelEye.htmlFor = inputEye.id; | ||
labelEye.appendChild(document.createTextNode(getNameOfDimension(i))); | ||
labelEye.style = 'text-align: left; float: left;' | ||
|
||
eyeTd.appendChild(labelEye); | ||
eyeTd.appendChild(inputEye); | ||
lineTr.appendChild(eyeTd); | ||
|
||
var targetTd = document.createElement('td'); | ||
var labelTarget = document.createElement('label'); | ||
var inputTarget = document.createElement('input'); | ||
inputTarget.id = 'camera'+N+'TargetInput'+i; | ||
inputTarget.disabled = !isEditable; | ||
inputTarget.value = targetPosition[i]; | ||
inputTarget.size = 6; | ||
inputTarget.style = 'text-align: right; float: right;' | ||
inputTarget.onchange = this.generateInputOnchange('signalCameraTargetMovement', N, i); | ||
|
||
labelTarget.htmlFor = inputTarget.id; | ||
labelTarget.appendChild(document.createTextNode(getNameOfDimension(i))); | ||
|
||
targetTd.appendChild(labelTarget); | ||
targetTd.appendChild(inputTarget); | ||
lineTr.appendChild(targetTd); | ||
|
||
table.appendChild(lineTr) | ||
|
||
if (i < 3) { | ||
this.firstThreeEye.push(inputEye); | ||
this.firstThreeTarget.push(inputTarget); | ||
} | ||
} | ||
|
||
mainDiv.appendChild(table); | ||
parent.appendChild(mainDiv); | ||
} | ||
|
||
changedFirstThreePositions(ox, oy, oz, tx, ty, tz) { | ||
this.firstThreeEye[0].value = ox; | ||
this.firstThreeEye[1].value = oy; | ||
this.firstThreeEye[2].value = oz; | ||
|
||
this.firstThreeTarget[0].value = tx; | ||
this.firstThreeTarget[1].value = ty; | ||
this.firstThreeTarget[2].value = tz; | ||
} | ||
} |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
class CutNPropertyComponent{ | ||
textboxOnChange(cutRange, multiplier, dimension){ | ||
return function(e){ | ||
let newValue = parseFloat(e.srcElement.value); | ||
cutRange.value = newValue * multiplier; | ||
emit('signalCutLocationChange', dimension, newValue); | ||
} | ||
} | ||
|
||
rangeOnChange(cutTextbox, multiplier, dimension){ | ||
return function(e){ | ||
let newValue = parseFloat(e.srcElement.value); | ||
cutTextbox.value = Math.round(newValue / multiplier * 100) / 100; | ||
emit('signalCutLocationChange', dimension, newValue / multiplier); | ||
} | ||
} | ||
|
||
constructor(N, relativeN, startingValue, maxValue, parent){ | ||
this.N = N; | ||
this.relativeN = relativeN; | ||
const sliderMultiplier = 100; | ||
|
||
console.log('cut', N, startingValue, maxValue) | ||
|
||
var mainDiv = document.createElement('div'); | ||
mainDiv.appendChild(document.createTextNode('Dimension ' + N + ' Cut')); | ||
br(mainDiv) | ||
|
||
let locationDiv = document.createElement('div'); | ||
mainDiv.appendChild(locationDiv) | ||
|
||
var cutTextbox = labeledTextbox(locationDiv, 'cut'+N+'Textbox', 'Location:', startingValue, 10, false, false) | ||
br(mainDiv) | ||
var cutRange = range(mainDiv, 'cut'+N+"Range", -maxValue* sliderMultiplier, maxValue*sliderMultiplier, startingValue) | ||
|
||
cutTextbox.onchange = this.textboxOnChange(cutRange, sliderMultiplier, this.relativeN) | ||
onRangeChange(cutRange, this.rangeOnChange(cutTextbox, sliderMultiplier, this.relativeN)) | ||
|
||
parent.appendChild(mainDiv) | ||
} | ||
} |
Oops, something went wrong.