Compare commits
19 Commits
Author | SHA1 | Date |
---|---|---|
|
2525f321ee | |
|
e842eb3d66 | |
|
6f50150a6b | |
|
c4599824c6 | |
|
2d7fb37165 | |
|
57b6851c8d | |
|
0c3f1577ce | |
|
de72dafd2d | |
|
f56912b41e | |
|
5a0cbe6e35 | |
|
7f3e1da3b9 | |
|
0ec890f026 | |
|
5efb9c9b2a | |
|
0b8fa336ce | |
|
4c4087a8a9 | |
|
505aee8ecc | |
|
64bf69ec71 | |
|
65d7ee8e82 | |
|
27be22842b |
|
@ -4,42 +4,69 @@
|
|||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>CG1 MDI</title>
|
||||
<script src="matrix-math.js"></script>
|
||||
<script src="shader.js"></script>
|
||||
<script src="wavefront.js"></script>
|
||||
<script src="script.js"></script>
|
||||
<style>
|
||||
body {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
min-height: 100vh;
|
||||
:root {
|
||||
font-size: 20px;
|
||||
--padding: 10px;
|
||||
}
|
||||
|
||||
html, body {
|
||||
margin: 0;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
body {
|
||||
padding: 1vw;
|
||||
box-sizing: border-box;
|
||||
background-color: #1e1e2e;
|
||||
color: #cdd6f4;
|
||||
font-family: monospace;
|
||||
font-size: 20px;
|
||||
gap: 20px;
|
||||
overflow: hidden;
|
||||
}
|
||||
#display {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
box-sizing: border-box;
|
||||
border-radius: 20px;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
|
||||
border: solid 2px #11111b;
|
||||
}
|
||||
#cv {
|
||||
border: solid 2px #11111b;
|
||||
border-radius: 20px;
|
||||
position: relative;
|
||||
max-width: 100%;
|
||||
max-height: 100%;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: block;
|
||||
background-color: #181825;
|
||||
}
|
||||
#stats {
|
||||
width: 100%;
|
||||
max-width: 800px;
|
||||
box-sizing: border-box;
|
||||
padding: 20px;
|
||||
position: absolute;
|
||||
top: var(--padding);
|
||||
padding: var(--padding);
|
||||
display: block;
|
||||
border: solid 2px #11111b;
|
||||
background-color: #313244;
|
||||
border-radius: 20px;
|
||||
background-color: #181825;
|
||||
}
|
||||
|
||||
#stats {
|
||||
right: var(--padding);
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<canvas id="cv" width="800" height="600"></canvas>
|
||||
<div id="stats">
|
||||
<div>FPS: <span id="fps"></span></div>
|
||||
<div>Frame time: <span id="ms"></span></div>
|
||||
<div id="display">
|
||||
<canvas id="cv"></canvas>
|
||||
<div id="stats">
|
||||
FPS: <span id="fps"></span> - <span id="ms"></span>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,530 @@
|
|||
"use strict";
|
||||
/**
|
||||
* overwrites a 4x4 matrix with the identity matrix
|
||||
*/
|
||||
function mat4Identity(mat) {
|
||||
for (let i = 0; i < 16; i++) {
|
||||
mat[i] = (i % 5 == 0) ? 1 : 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* copies a mat4 from src to dst
|
||||
*/
|
||||
function mat4Copy(src, dst) {
|
||||
for (let i = 0; i < 16; i++) {
|
||||
dst[i] = src[i];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* sets all the values in a mat4 to zero
|
||||
*/
|
||||
function mat4Empty(mat) {
|
||||
for (let i = 0; i < 16; i++) {
|
||||
mat[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* mutliplies A with B and stores the result in result
|
||||
*/
|
||||
function mat4Multiply(result, A, B) {
|
||||
// if result is one of the arguments, modify copy instead of result directly
|
||||
if (result == A || result == B) {
|
||||
let tempResult = [];
|
||||
mat4Multiply(tempResult, A, B);
|
||||
mat4Copy(tempResult, result);
|
||||
return;
|
||||
}
|
||||
|
||||
// loops over cells of the result matrix
|
||||
for (let i = 0; i < 16; i++) {
|
||||
let col = (i / 4) | 0;
|
||||
let row = i % 4;
|
||||
|
||||
// initialise current cell with 0
|
||||
result[i] = 0;
|
||||
|
||||
// loop over the row of A and column of B
|
||||
// continuously adding the multiplication of the two values to the result cell
|
||||
for (let j = 0; j < 4; j++) {
|
||||
result[i] += A[row + j * 4] * B[j + col * 4];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* prints a mat4 to the screen
|
||||
*/
|
||||
function mat4Print(m) {
|
||||
let str = "";
|
||||
for (let i = 0; i < 4; i++) {
|
||||
for (let j = 0; j < 4; j++) {
|
||||
str += m[j * 4 + i] + " ";
|
||||
}
|
||||
str += "\n";
|
||||
}
|
||||
console.log(str);
|
||||
}
|
||||
|
||||
/**
|
||||
* translates by the vector v
|
||||
*/
|
||||
function mat4Translate(out, inm, v) {
|
||||
let t = [];
|
||||
mat4Identity(t);
|
||||
|
||||
t[12] = v[0];
|
||||
t[13] = v[1];
|
||||
t[14] = v[2];
|
||||
|
||||
mat4Multiply(out, t, inm);
|
||||
}
|
||||
|
||||
/**
|
||||
* scales by the vector v
|
||||
*/
|
||||
function mat4Scale(out, inm, v) {
|
||||
let t = [];
|
||||
mat4Identity(t);
|
||||
|
||||
t[0] = v[0];
|
||||
t[5] = v[1];
|
||||
t[10] = v[2];
|
||||
|
||||
mat4Multiply(out, t, inm);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* rotates around the X axis by the angle a (in radians)
|
||||
*/
|
||||
function mat4RotateX(out, inm, a) {
|
||||
let t = [];
|
||||
mat4Identity(t);
|
||||
|
||||
t[5] = Math.cos(a);
|
||||
t[6] = Math.sin(a);
|
||||
t[9] = -Math.sin(a);
|
||||
t[10] = Math.cos(a);
|
||||
|
||||
mat4Multiply(out, t, inm);
|
||||
}
|
||||
|
||||
/**
|
||||
* rotates around the Y axis by the angle a (in radians)
|
||||
*/
|
||||
function mat4RotateY(out, inm, a) {
|
||||
let t = [];
|
||||
mat4Identity(t);
|
||||
|
||||
t[0] = Math.cos(a);
|
||||
t[2] = -Math.sin(a);
|
||||
t[8] = Math.sin(a);
|
||||
t[10] = Math.cos(a);
|
||||
|
||||
mat4Multiply(out, t, inm);
|
||||
}
|
||||
|
||||
/**
|
||||
* rotates around the Z axis by the angle a (in radians)
|
||||
*/
|
||||
function mat4RotateZ(out, inm, a) {
|
||||
let t = [];
|
||||
mat4Identity(t);
|
||||
|
||||
t[0] = Math.cos(a);
|
||||
t[1] = Math.sin(a);
|
||||
t[4] = -Math.sin(a);
|
||||
t[5] = Math.cos(a);
|
||||
|
||||
mat4Multiply(out, t, inm);
|
||||
}
|
||||
|
||||
/**
|
||||
* builds a look-at matrix, overwriting out
|
||||
* eye is the position of the camera
|
||||
* look is the position of the target to be looked at
|
||||
* up is the up vector
|
||||
*/
|
||||
function mat4BuildLookAt(out, eye, look, up) {
|
||||
let n = [];
|
||||
let u = [];
|
||||
let v = [];
|
||||
let t = [];
|
||||
|
||||
vec3Subtract(n, eye, look);
|
||||
|
||||
vec3CrossProduct(u, up, n);
|
||||
|
||||
vec3CrossProduct(v, n, u);
|
||||
|
||||
vec3Normalise(n, n);
|
||||
vec3Normalise(u, u);
|
||||
vec3Normalise(v, v);
|
||||
|
||||
t[0] = - vec3DotProduct(u, eye);
|
||||
t[1] = - vec3DotProduct(v, eye);
|
||||
t[2] = - vec3DotProduct(n, eye);
|
||||
|
||||
out[0] = u[0]; out[4] = u[1]; out[8] = u[2]; out[12] = t[0];
|
||||
out[1] = v[0]; out[5] = v[1]; out[9] = v[2]; out[13] = t[1];
|
||||
out[2] = n[0]; out[6] = n[1]; out[10] = n[2]; out[14] = t[2];
|
||||
out[3] = 0; out[7] = 0; out[11] = 0; out[15] = 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* builds a projection matrix, overwriting out
|
||||
* r, l, t, b are the right, left, top and bottom of the frustum
|
||||
* n and f are the distance of the near and far planes from the camera
|
||||
*/
|
||||
function mat4BuildProjection(out, r, l, t, b, n, f) {
|
||||
mat4Identity(out);
|
||||
|
||||
out[0] = 2.0 / (r - l);
|
||||
out[5] = 2.0 / (t - b);
|
||||
|
||||
out[8] = 1.0 / n * (r + l) / (r - l);
|
||||
out[9] = 1.0 / n * (t + b) / (t - b);
|
||||
out[10] = -1.0 / n * (f + n) / (f - n);
|
||||
out[11] = -1.0 / n;
|
||||
|
||||
out[14] = - 2.0 * f / (f - n);
|
||||
out[15] = 0.0;
|
||||
}
|
||||
|
||||
/**
|
||||
* builds a perspective projection matrix, overwriting out
|
||||
* fovy is the field of view in the y direction
|
||||
* aspect is the aspect ratio
|
||||
* n and f are the distance of the near and far planes from the camera
|
||||
*/
|
||||
function mat4BuildPerspective(out, fovy, aspect, n, f) {
|
||||
let t = n * Math.tan(0.5 * fovy);
|
||||
let b = -t;
|
||||
|
||||
let r = aspect * t;
|
||||
let l = -r;
|
||||
|
||||
mat4BuildProjection(out, r, l, t, b, n, f);
|
||||
}
|
||||
|
||||
/**
|
||||
* linearly interpolates between a and b, storing the result in out
|
||||
*/
|
||||
function mat4Interpolate(out, a, b, f) {
|
||||
for (let i = 0; i < 16; i++) {
|
||||
out[i] = (1 - f) * a[i] + f * b[i];
|
||||
}
|
||||
}
|
||||
|
||||
function mat4From3(out, inm) {
|
||||
mat4Identity(out);
|
||||
out[0] = inm[0];
|
||||
out[1] = inm[1];
|
||||
out[2] = inm[2];
|
||||
|
||||
out[4] = inm[3];
|
||||
out[5] = inm[4];
|
||||
out[6] = inm[5];
|
||||
|
||||
out[8] = inm[6];
|
||||
out[9] = inm[7];
|
||||
out[10] = inm[8];
|
||||
}
|
||||
|
||||
/**
|
||||
* https://stackoverflow.com/questions/1148309/inverting-a-4x4-matrix
|
||||
*/
|
||||
function mat4Inverse(out, m) {
|
||||
let inv = [];
|
||||
let det;
|
||||
let i;
|
||||
|
||||
inv[0] = m[5] * m[10] * m[15] -
|
||||
m[5] * m[11] * m[14] -
|
||||
m[9] * m[6] * m[15] +
|
||||
m[9] * m[7] * m[14] +
|
||||
m[13] * m[6] * m[11] -
|
||||
m[13] * m[7] * m[10];
|
||||
|
||||
inv[4] = -m[4] * m[10] * m[15] +
|
||||
m[4] * m[11] * m[14] +
|
||||
m[8] * m[6] * m[15] -
|
||||
m[8] * m[7] * m[14] -
|
||||
m[12] * m[6] * m[11] +
|
||||
m[12] * m[7] * m[10];
|
||||
|
||||
inv[8] = m[4] * m[9] * m[15] -
|
||||
m[4] * m[11] * m[13] -
|
||||
m[8] * m[5] * m[15] +
|
||||
m[8] * m[7] * m[13] +
|
||||
m[12] * m[5] * m[11] -
|
||||
m[12] * m[7] * m[9];
|
||||
|
||||
inv[12] = -m[4] * m[9] * m[14] +
|
||||
m[4] * m[10] * m[13] +
|
||||
m[8] * m[5] * m[14] -
|
||||
m[8] * m[6] * m[13] -
|
||||
m[12] * m[5] * m[10] +
|
||||
m[12] * m[6] * m[9];
|
||||
|
||||
inv[1] = -m[1] * m[10] * m[15] +
|
||||
m[1] * m[11] * m[14] +
|
||||
m[9] * m[2] * m[15] -
|
||||
m[9] * m[3] * m[14] -
|
||||
m[13] * m[2] * m[11] +
|
||||
m[13] * m[3] * m[10];
|
||||
|
||||
inv[5] = m[0] * m[10] * m[15] -
|
||||
m[0] * m[11] * m[14] -
|
||||
m[8] * m[2] * m[15] +
|
||||
m[8] * m[3] * m[14] +
|
||||
m[12] * m[2] * m[11] -
|
||||
m[12] * m[3] * m[10];
|
||||
|
||||
inv[9] = -m[0] * m[9] * m[15] +
|
||||
m[0] * m[11] * m[13] +
|
||||
m[8] * m[1] * m[15] -
|
||||
m[8] * m[3] * m[13] -
|
||||
m[12] * m[1] * m[11] +
|
||||
m[12] * m[3] * m[9];
|
||||
|
||||
inv[13] = m[0] * m[9] * m[14] -
|
||||
m[0] * m[10] * m[13] -
|
||||
m[8] * m[1] * m[14] +
|
||||
m[8] * m[2] * m[13] +
|
||||
m[12] * m[1] * m[10] -
|
||||
m[12] * m[2] * m[9];
|
||||
|
||||
inv[2] = m[1] * m[6] * m[15] -
|
||||
m[1] * m[7] * m[14] -
|
||||
m[5] * m[2] * m[15] +
|
||||
m[5] * m[3] * m[14] +
|
||||
m[13] * m[2] * m[7] -
|
||||
m[13] * m[3] * m[6];
|
||||
|
||||
inv[6] = -m[0] * m[6] * m[15] +
|
||||
m[0] * m[7] * m[14] +
|
||||
m[4] * m[2] * m[15] -
|
||||
m[4] * m[3] * m[14] -
|
||||
m[12] * m[2] * m[7] +
|
||||
m[12] * m[3] * m[6];
|
||||
|
||||
inv[10] = m[0] * m[5] * m[15] -
|
||||
m[0] * m[7] * m[13] -
|
||||
m[4] * m[1] * m[15] +
|
||||
m[4] * m[3] * m[13] +
|
||||
m[12] * m[1] * m[7] -
|
||||
m[12] * m[3] * m[5];
|
||||
|
||||
inv[14] = -m[0] * m[5] * m[14] +
|
||||
m[0] * m[6] * m[13] +
|
||||
m[4] * m[1] * m[14] -
|
||||
m[4] * m[2] * m[13] -
|
||||
m[12] * m[1] * m[6] +
|
||||
m[12] * m[2] * m[5];
|
||||
|
||||
inv[3] = -m[1] * m[6] * m[11] +
|
||||
m[1] * m[7] * m[10] +
|
||||
m[5] * m[2] * m[11] -
|
||||
m[5] * m[3] * m[10] -
|
||||
m[9] * m[2] * m[7] +
|
||||
m[9] * m[3] * m[6];
|
||||
|
||||
inv[7] = m[0] * m[6] * m[11] -
|
||||
m[0] * m[7] * m[10] -
|
||||
m[4] * m[2] * m[11] +
|
||||
m[4] * m[3] * m[10] +
|
||||
m[8] * m[2] * m[7] -
|
||||
m[8] * m[3] * m[6];
|
||||
|
||||
inv[11] = -m[0] * m[5] * m[11] +
|
||||
m[0] * m[7] * m[9] +
|
||||
m[4] * m[1] * m[11] -
|
||||
m[4] * m[3] * m[9] -
|
||||
m[8] * m[1] * m[7] +
|
||||
m[8] * m[3] * m[5];
|
||||
|
||||
inv[15] = m[0] * m[5] * m[10] -
|
||||
m[0] * m[6] * m[9] -
|
||||
m[4] * m[1] * m[10] +
|
||||
m[4] * m[2] * m[9] +
|
||||
m[8] * m[1] * m[6] -
|
||||
m[8] * m[2] * m[5];
|
||||
|
||||
det = m[0] * inv[0] + m[1] * inv[4] + m[2] * inv[8] + m[3] * inv[12];
|
||||
|
||||
if (det == 0) {
|
||||
mat4Copy(m, out); // singular matrix, can't invert
|
||||
return;
|
||||
}
|
||||
|
||||
det = 1.0 / det;
|
||||
|
||||
for (i = 0; i < 16; i++)
|
||||
out[i] = inv[i] * det;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
function mat4vec4Multiply(out, inMat, inVec) {
|
||||
let result = [0, 0, 0, 0];
|
||||
for (let i = 0; i < 4; i++) {
|
||||
for (let j = 0; j < 4; j++) {
|
||||
result[i] += inMat[i + j * 4] * inVec[j];
|
||||
}
|
||||
}
|
||||
out[0] = result[0];
|
||||
out[1] = result[1];
|
||||
out[2] = result[2];
|
||||
out[3] = result[3];
|
||||
}
|
||||
|
||||
/**
|
||||
* subtracts b from a, storing the result in out
|
||||
*/
|
||||
function vec3Subtract(out, a, b) {
|
||||
out[0] = a[0] - b[0];
|
||||
out[1] = a[1] - b[1];
|
||||
out[2] = a[2] - b[2];
|
||||
}
|
||||
|
||||
/**
|
||||
* calculates the cross product of a and b, storing the result in out
|
||||
*/
|
||||
function vec3CrossProduct(out, a, b) {
|
||||
let result = [];
|
||||
result[0] = a[1] * b[2] - a[2] * b[1];
|
||||
result[1] = a[2] * b[0] - a[0] * b[2];
|
||||
result[2] = a[0] * b[1] - a[1] * b[0];
|
||||
|
||||
out[0] = result[0];
|
||||
out[1] = result[1];
|
||||
out[2] = result[2];
|
||||
}
|
||||
|
||||
/**
|
||||
* normalizes in storing the result in out
|
||||
*/
|
||||
function vec3Normalise(out, inv) {
|
||||
let length = vec3Length(inv);
|
||||
out[0] = inv[0] / length;
|
||||
out[1] = inv[1] / length;
|
||||
out[2] = inv[2] / length;
|
||||
}
|
||||
|
||||
/**
|
||||
* returns the length of the vector in
|
||||
*/
|
||||
function vec3Length(inv) {
|
||||
return Math.sqrt(inv[0] * inv[0] + inv[1] * inv[1] + inv[2] * inv[2]);
|
||||
}
|
||||
|
||||
/**
|
||||
* returns the dot product of the vectors a and b
|
||||
*/
|
||||
function vec3DotProduct(a, b) {
|
||||
return a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
|
||||
}
|
||||
|
||||
/**
|
||||
* sets the values of out to x, y and z
|
||||
*/
|
||||
function vec3Set(out, x, y, z) {
|
||||
out[0] = x;
|
||||
out[1] = y;
|
||||
out[2] = z;
|
||||
}
|
||||
|
||||
|
||||
function mat3Copy(src, dst) {
|
||||
for (let i = 0; i < 9; i++) {
|
||||
dst[i] = src[i];
|
||||
}
|
||||
}
|
||||
|
||||
function mat3From4(out, inm) {
|
||||
out[0] = inm[0];
|
||||
out[1] = inm[1];
|
||||
out[2] = inm[2];
|
||||
|
||||
out[3] = inm[4];
|
||||
out[4] = inm[5];
|
||||
out[5] = inm[6];
|
||||
|
||||
out[6] = inm[8];
|
||||
out[7] = inm[9];
|
||||
out[8] = inm[10];
|
||||
}
|
||||
|
||||
function mat3Transpose(out, inm) {
|
||||
let result = [];
|
||||
|
||||
for (let i = 0; i < 3; i++) {
|
||||
for (let j = 0; j < 3; j++) {
|
||||
result[i * 3 + j] = inm[j * 3 + i];
|
||||
}
|
||||
}
|
||||
|
||||
mat3Copy(result, out);
|
||||
}
|
||||
|
||||
function mat3Minor(out, inm) {
|
||||
let result = [];
|
||||
|
||||
// TODO: check if this is correct
|
||||
result[0] = inm[4] * inm[8] - inm[5] * inm[7];
|
||||
result[1] = inm[3] * inm[8] - inm[5] * inm[6];
|
||||
result[2] = inm[3] * inm[7] - inm[4] * inm[6];
|
||||
|
||||
result[3] = inm[1] * inm[8] - inm[2] * inm[7];
|
||||
result[4] = inm[0] * inm[8] - inm[2] * inm[6];
|
||||
result[5] = inm[0] * inm[7] - inm[1] * inm[6];
|
||||
|
||||
result[6] = inm[1] * inm[5] - inm[2] * inm[4];
|
||||
result[7] = inm[0] * inm[5] - inm[2] * inm[3];
|
||||
result[8] = inm[0] * inm[4] - inm[1] * inm[3];
|
||||
|
||||
mat3Copy(result, out);
|
||||
}
|
||||
|
||||
function mat3Cofactor(out, inm) {
|
||||
mat3Minor(out, inm);
|
||||
|
||||
out[1] *= -1;
|
||||
out[3] *= -1;
|
||||
out[5] *= -1;
|
||||
out[7] *= -1;
|
||||
}
|
||||
|
||||
function mat3Adjoint(out, inm) {
|
||||
mat3Cofactor(out, inm);
|
||||
mat3Transpose(out, inm);
|
||||
}
|
||||
|
||||
function mat3MultiplyScalar(out, inm, x) {
|
||||
for (let i = 0; i < 9; i++) {
|
||||
out[i] = inm[i] * x;
|
||||
}
|
||||
}
|
||||
|
||||
function mat3Determinant(m) {
|
||||
return + m[0] * m[4] * m[8] + m[3] * m[7] * m[2] + m[6] * m[1] * m[5] - m[2] * m[4] * m[6] - m[5] * m[7] * m[0] - m[8] * m[1] * m[3];
|
||||
}
|
||||
|
||||
function mat3Inverse(out, inm) {
|
||||
let result = [];
|
||||
mat3Adjoint(result, inm);
|
||||
mat3MultiplyScalar(result, result, 1 / mat3Determinant(inm));
|
||||
mat3Copy(result, out);
|
||||
}
|
||||
|
||||
function mat3MakeNormal(out, inm) {
|
||||
let result = [];
|
||||
mat3From4(result, inm);
|
||||
mat3Inverse(result, result);
|
||||
mat3Transpose(result, result);
|
||||
mat3Copy(result, out);
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
# Blender 4.4.3
|
||||
# www.blender.org
|
||||
o Cube
|
||||
v 1.000000 1.000000 -1.000000
|
||||
v 1.000000 -1.000000 -1.000000
|
||||
v 1.000000 1.000000 1.000000
|
||||
v 1.000000 -1.000000 1.000000
|
||||
v -1.000000 1.000000 -1.000000
|
||||
v -1.000000 -1.000000 -1.000000
|
||||
v -1.000000 1.000000 1.000000
|
||||
v -1.000000 -1.000000 1.000000
|
||||
vn -0.0000 1.0000 -0.0000
|
||||
vn -0.0000 -0.0000 1.0000
|
||||
vn -1.0000 -0.0000 -0.0000
|
||||
vn -0.0000 -1.0000 -0.0000
|
||||
vn 1.0000 -0.0000 -0.0000
|
||||
vn -0.0000 -0.0000 -1.0000
|
||||
vt 1.000000 0.000000
|
||||
vt 0.000000 1.000000
|
||||
vt 0.000000 0.000000
|
||||
vt 1.000000 1.000000
|
||||
s 0
|
||||
f 5/1/1 3/2/1 1/3/1
|
||||
f 3/1/2 8/2/2 4/3/2
|
||||
f 7/1/3 6/2/3 8/3/3
|
||||
f 2/1/4 8/2/4 6/3/4
|
||||
f 1/1/5 4/2/5 2/3/5
|
||||
f 5/1/6 2/2/6 6/3/6
|
||||
f 5/1/1 7/4/1 3/2/1
|
||||
f 3/1/2 7/4/2 8/2/2
|
||||
f 7/1/3 5/4/3 6/2/3
|
||||
f 2/1/4 4/4/4 8/2/4
|
||||
f 1/1/5 3/4/5 4/2/5
|
||||
f 5/1/6 1/4/6 2/2/6
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
232
src/script.js
232
src/script.js
|
@ -4,122 +4,120 @@ let cv, gl;
|
|||
|
||||
let program;
|
||||
|
||||
let vertices;
|
||||
let vertexBuffer;
|
||||
class Object {
|
||||
vertexBuffer;
|
||||
numVertices;
|
||||
}
|
||||
|
||||
let o = new Object();
|
||||
|
||||
let lastFrame = Date.now();
|
||||
|
||||
let cameraPitch = 0.565;
|
||||
let cameraYaw = 0.375;
|
||||
let cameraDistance = 4;
|
||||
let smoothCameraDistance = 4;
|
||||
|
||||
let mouseDragging = false;
|
||||
|
||||
let frameTimes = [];
|
||||
|
||||
function resizeCanvas() {
|
||||
const dpr = window.devicePixelRatio;
|
||||
cv.width = cv.parentElement.clientWidth * dpr;
|
||||
cv.height = cv.parentElement.clientHeight * dpr;
|
||||
gl.viewport(0, 0, cv.width, cv.height);
|
||||
}
|
||||
|
||||
async function init() {
|
||||
cv = document.getElementById("cv");
|
||||
|
||||
gl = cv.getContext("webgl");
|
||||
|
||||
if (!gl) {
|
||||
window.alert("WebGL not supported");
|
||||
}
|
||||
|
||||
const resizeObserver = new ResizeObserver(resizeCanvas);
|
||||
resizeObserver.observe(cv.parentElement);
|
||||
resizeCanvas();
|
||||
|
||||
|
||||
// input handling
|
||||
cv.addEventListener("mousedown", (e) => {
|
||||
if (e.button == 0) {
|
||||
mouseDragging = true;
|
||||
}
|
||||
});
|
||||
document.addEventListener("mouseup", (e) => {
|
||||
if (e.button == 0) {
|
||||
mouseDragging = false;
|
||||
}
|
||||
});
|
||||
document.addEventListener("mouseleave", (e) => {
|
||||
mouseDragging = false;
|
||||
});
|
||||
|
||||
document.addEventListener("mousemove", (e) => {
|
||||
if (mouseDragging) {
|
||||
cameraYaw += e.movementX / 100;
|
||||
cameraPitch += e.movementY / 100;
|
||||
|
||||
if (cameraPitch < -Math.PI / 2) cameraPitch = -Math.PI / 2;
|
||||
if (cameraPitch > Math.PI / 2) cameraPitch = Math.PI / 2;
|
||||
}
|
||||
});
|
||||
cv.addEventListener("wheel", (e) => {
|
||||
cameraDistance += e.deltaY / 100;
|
||||
if (cameraDistance < 1) cameraDistance = 1;
|
||||
if (cameraDistance > 15) cameraDistance = 15;
|
||||
e.preventDefault();
|
||||
});
|
||||
// end input handling
|
||||
|
||||
console.log("compiling shaders...");
|
||||
|
||||
console.log("compiling vertex shader...");
|
||||
let vertexShader = await fetchAndCompileShader(gl, gl.VERTEX_SHADER, "shaders/vertex.glsl");
|
||||
|
||||
console.log("compiling fragment shader...");
|
||||
let fragmentShader = await fetchAndCompileShader(gl, gl.FRAGMENT_SHADER, "shaders/fragment.glsl");
|
||||
|
||||
console.log("linking shader program...");
|
||||
program = linkShaderProgram(gl, vertexShader, fragmentShader);
|
||||
|
||||
/**
|
||||
* -0.35 0.35
|
||||
* | -0.2 0.2 |
|
||||
* | | | |
|
||||
*
|
||||
* +----+ +----+ --- 0.6
|
||||
* | | | |
|
||||
* | | | |
|
||||
* | +--------+ | --- 0.1
|
||||
* | |
|
||||
* | +--------+ | --- -0.1
|
||||
* | | | |
|
||||
* | | | |
|
||||
* +----+ +----+ --- -0.6
|
||||
*
|
||||
* +------------------+ --- -0.7
|
||||
* | |
|
||||
* +------------------+ --- -0.9
|
||||
*
|
||||
*/
|
||||
|
||||
vertices = [
|
||||
// X // Y
|
||||
|
||||
// left vertical bar
|
||||
-0.35, -0.6,
|
||||
-0.35, 0.6,
|
||||
-0.2, 0.6,
|
||||
|
||||
-0.35, -0.6,
|
||||
-0.2, 0.6,
|
||||
-0.2, -0.6,
|
||||
|
||||
// right vertical bar
|
||||
0.35, -0.6,
|
||||
0.35, 0.6,
|
||||
0.2, 0.6,
|
||||
|
||||
0.35, -0.6,
|
||||
0.2, 0.6,
|
||||
0.2, -0.6,
|
||||
|
||||
// middle bar
|
||||
-0.2, -0.1,
|
||||
-0.2, 0.1,
|
||||
0.2, 0.1,
|
||||
|
||||
-0.2, -0.1,
|
||||
0.2, 0.1,
|
||||
0.2, -0.1,
|
||||
|
||||
// bottom bar
|
||||
-0.35, -0.7,
|
||||
-0.35, -0.9,
|
||||
0.35, -0.9,
|
||||
|
||||
-0.35, -0.7,
|
||||
0.35, -0.9,
|
||||
0.35, -0.7
|
||||
];
|
||||
program = await buildShaderProgram(gl, "shaders/vertex.glsl", "shaders/fragment.glsl");
|
||||
|
||||
console.log("creating vertex buffer");
|
||||
|
||||
vertexBuffer = gl.createBuffer();
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
|
||||
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);
|
||||
|
||||
// get location of vertPosition attribute
|
||||
let positionAttribLocation = gl.getAttribLocation(program, "vertPosition");
|
||||
let f = await readObjFile("obj/smooth_monkey.obj");
|
||||
o = new Object();
|
||||
|
||||
gl.vertexAttribPointer(
|
||||
positionAttribLocation, // which attribute is read
|
||||
2, // number of values to read
|
||||
gl.FLOAT, // type of values
|
||||
gl.FALSE, // whether to normalize
|
||||
2 * Float32Array.BYTES_PER_ELEMENT, // size of individual vertex / distance between values to read
|
||||
0 // offset where to start reading
|
||||
);
|
||||
|
||||
// enable attribute
|
||||
gl.enableVertexAttribArray(positionAttribLocation);
|
||||
|
||||
// unbind buffer
|
||||
o.vertexBuffer = gl.createBuffer();
|
||||
o.numVertices = f.length / 8;
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, o.vertexBuffer);
|
||||
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(f), gl.STATIC_DRAW);
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, null);
|
||||
|
||||
|
||||
// set clear colour
|
||||
gl.clearColor(0.1, 0.1, 0.1, 1.0);
|
||||
gl.clearColor(...hexToRgb("181825"), 1.0);
|
||||
|
||||
gl.enable(gl.DEPTH_TEST);
|
||||
gl.enable(gl.BLEND);
|
||||
gl.enable(gl.CULL_FACE);
|
||||
gl.frontFace(gl.CCW);
|
||||
gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
|
||||
|
||||
// start drawing
|
||||
requestAnimationFrame(draw);
|
||||
}
|
||||
|
||||
function setAttribPointers() {
|
||||
let positionAttribLocation = gl.getAttribLocation(program, "vertPosition");
|
||||
let normalAttribLocation = gl.getAttribLocation(program, "vertNormal");
|
||||
let textureAttribLocation = gl.getAttribLocation(program, "vertTexture");
|
||||
|
||||
gl.vertexAttribPointer(positionAttribLocation, 3, gl.FLOAT, gl.FALSE, 8 * Float32Array.BYTES_PER_ELEMENT, 0);
|
||||
gl.vertexAttribPointer(textureAttribLocation, 2, gl.FLOAT, gl.FALSE, 8 * Float32Array.BYTES_PER_ELEMENT, 3 * Float32Array.BYTES_PER_ELEMENT);
|
||||
gl.vertexAttribPointer(normalAttribLocation, 3, gl.FLOAT, gl.FALSE, 8 * Float32Array.BYTES_PER_ELEMENT, 5 * Float32Array.BYTES_PER_ELEMENT);
|
||||
|
||||
gl.enableVertexAttribArray(positionAttribLocation);
|
||||
gl.enableVertexAttribArray(textureAttribLocation);
|
||||
gl.enableVertexAttribArray(normalAttribLocation);
|
||||
}
|
||||
|
||||
let frameCount = 0;
|
||||
let lastStatUpdate = Date.now();
|
||||
|
||||
|
@ -137,8 +135,11 @@ function updateStats(deltaTime) {
|
|||
fpsDisplay.innerText = Math.round(frameCount / timeSinceLastUpdate)
|
||||
|
||||
let msDisplay = document.getElementById("ms");
|
||||
msDisplay.innerText = deltaTime * 1000 + "ms";
|
||||
if (frameTimes.length > 0) {
|
||||
msDisplay.innerText = (Math.round(frameTimes.reduce((prev, curr) => prev + curr) / frameTimes.length * 100) / 100) + "ms";
|
||||
}
|
||||
|
||||
frameTimes = [];
|
||||
frameCount = 0;
|
||||
}
|
||||
|
||||
|
@ -147,31 +148,60 @@ let hue = 0;
|
|||
async function draw() {
|
||||
let now = Date.now();
|
||||
let deltaTime = (now - lastFrame) / 1000;
|
||||
let frameStart = performance.now();
|
||||
|
||||
hue += deltaTime / 5;
|
||||
if (hue > 1) hue -= 1;
|
||||
if (hue > 1) hue = 0;
|
||||
|
||||
smoothCameraDistance += (cameraDistance - smoothCameraDistance) / 8;
|
||||
|
||||
updateStats(deltaTime);
|
||||
|
||||
gl.clear(gl.COLOR_BUFFER_BIT);
|
||||
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
|
||||
|
||||
let identity = [];
|
||||
mat4Identity(identity);
|
||||
|
||||
// Real Matrices
|
||||
let projectionMatrix = [];
|
||||
mat4BuildPerspective(projectionMatrix, 90.0 / 180.0 * Math.PI, cv.width / cv.height, 0.1, 30);
|
||||
|
||||
let viewMatrix = [];
|
||||
let cameraPosition = [Math.cos(cameraYaw) * smoothCameraDistance * Math.cos(cameraPitch), Math.sin(cameraPitch) * smoothCameraDistance, Math.sin(cameraYaw) * smoothCameraDistance * Math.cos(cameraPitch)];
|
||||
let cameraLookAt = [0, 0, 0];
|
||||
let cameraUp = [0, 1, 0];
|
||||
mat4BuildLookAt(viewMatrix, cameraPosition, cameraLookAt, cameraUp);
|
||||
|
||||
let normalMatrix = [];
|
||||
mat3MakeNormal(normalMatrix, viewMatrix);
|
||||
|
||||
gl.useProgram(program);
|
||||
gl.uniformMatrix4fv(gl.getUniformLocation(program, "projectionMatrix"), gl.FALSE, new Float32Array(projectionMatrix));
|
||||
gl.uniformMatrix4fv(gl.getUniformLocation(program, "modelViewMatrix"), gl.FALSE, new Float32Array(viewMatrix));
|
||||
gl.uniformMatrix3fv(gl.getUniformLocation(program, "normalMatrix"), gl.FALSE, new Float32Array(normalMatrix));
|
||||
|
||||
let colorLocation = gl.getUniformLocation(program, "color");
|
||||
gl.uniform3f(colorLocation, ...hslToRgb(hue, 1, 0.5));
|
||||
let lightPosition = [Math.cos(hue * Math.PI * 2) * 3, 2.0, Math.sin(hue * Math.PI * 2) * 3, 1.0];
|
||||
mat4vec4Multiply(lightPosition, viewMatrix, lightPosition);
|
||||
|
||||
let positionLocation = gl.getUniformLocation(program, "position");
|
||||
gl.uniform2f(positionLocation, Math.cos(hue * 2 * Math.PI) * 0.2, Math.sin(hue * 2 * Math.PI) * 0.2);
|
||||
gl.uniform3f(gl.getUniformLocation(program, "Light.position"), ...lightPosition.slice(0, 3));
|
||||
gl.uniform3f(gl.getUniformLocation(program, "Light.color"), 1, 1, 1);
|
||||
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
|
||||
gl.uniform3f(gl.getUniformLocation(program, "Material.ambientColor"), 0.1, 0.1, 0.1);
|
||||
gl.uniform3f(gl.getUniformLocation(program, "Material.emitColor"), 0.05, 0.05, 0.0);
|
||||
gl.uniform3f(gl.getUniformLocation(program, "Material.diffColor"), 0.7, 0.8, 0.1);
|
||||
gl.uniform3f(gl.getUniformLocation(program, "Material.specColor"), 1, 1, 0.8);
|
||||
gl.uniform1f(gl.getUniformLocation(program, "Material.shininess"), 10);
|
||||
|
||||
gl.drawArrays(gl.TRIANGLES, 0, vertices.length / 2);
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, o.vertexBuffer);
|
||||
setAttribPointers();
|
||||
gl.drawArrays(gl.TRIANGLES, 0, o.numVertices);
|
||||
|
||||
frameTimes.push(performance.now() - frameStart);
|
||||
lastFrame = now;
|
||||
requestAnimationFrame(draw);
|
||||
}
|
||||
|
||||
function hexColorToFloatArray(hex) {
|
||||
function hexToRgb(hex) {
|
||||
let r = parseInt(hex.substring(0, 2), 16) / 255;
|
||||
let g = parseInt(hex.substring(2, 4), 16) / 255;
|
||||
let b = parseInt(hex.substring(4, 6), 16) / 255;
|
||||
|
|
|
@ -45,4 +45,10 @@ function linkShaderProgram(gl, vertexShader, fragmentShader) {
|
|||
}
|
||||
|
||||
return program;
|
||||
}
|
||||
|
||||
async function buildShaderProgram(gl, vertexPath, fragmentPath) {
|
||||
let vertexShader = await fetchAndCompileShader(gl, gl.VERTEX_SHADER, vertexPath);
|
||||
let fragmentShader = await fetchAndCompileShader(gl, gl.FRAGMENT_SHADER, fragmentPath);
|
||||
return linkShaderProgram(gl, vertexShader, fragmentShader);
|
||||
}
|
|
@ -1,6 +1,33 @@
|
|||
precision mediump float;
|
||||
varying vec3 fragColor;
|
||||
|
||||
uniform struct {
|
||||
vec3 position;
|
||||
vec3 color;
|
||||
} Light;
|
||||
|
||||
uniform struct {
|
||||
vec3 ambientColor;
|
||||
vec3 emitColor;
|
||||
vec3 diffColor;
|
||||
vec3 specColor;
|
||||
float shininess;
|
||||
} Material;
|
||||
|
||||
varying vec3 vNormal;
|
||||
varying vec2 vTexture;
|
||||
varying vec3 vPosition;
|
||||
|
||||
void main() {
|
||||
gl_FragColor = vec4(fragColor, 1.0);
|
||||
vec3 pleaseDontDeleteMe = vec3(vTexture, 0.0);
|
||||
|
||||
vec3 eyeDir = normalize(-vPosition);
|
||||
vec3 lightDir = normalize(Light.position - vPosition);
|
||||
vec3 r = reflect(-lightDir, vNormal);
|
||||
|
||||
vec3 ambient = Material.ambientColor;
|
||||
vec3 emit = Material.emitColor;
|
||||
vec3 diff = max(dot(lightDir, vNormal), 0.0) * Material.diffColor * Light.color;
|
||||
vec3 spec = pow(max(dot(r, eyeDir), 0.0), Material.shininess) * Material.specColor * Light.color;
|
||||
|
||||
gl_FragColor = vec4(ambient + emit + diff + spec, 1);
|
||||
}
|
|
@ -1,11 +1,21 @@
|
|||
precision mediump float;
|
||||
|
||||
attribute vec2 vertPosition;
|
||||
uniform vec3 color;
|
||||
uniform vec2 position;
|
||||
varying vec3 fragColor;
|
||||
attribute vec3 vertPosition;
|
||||
attribute vec3 vertNormal;
|
||||
attribute vec2 vertTexture;
|
||||
|
||||
uniform mat4 modelViewMatrix;
|
||||
uniform mat4 projectionMatrix;
|
||||
uniform mat3 normalMatrix;
|
||||
|
||||
varying vec3 vNormal;
|
||||
varying vec2 vTexture;
|
||||
varying vec3 vPosition;
|
||||
|
||||
void main() {
|
||||
fragColor = color;
|
||||
gl_Position = vec4(position + vertPosition, 0.0, 1.0);
|
||||
vNormal = normalize(normalMatrix * vertNormal);
|
||||
vTexture = vertTexture;
|
||||
vPosition = (modelViewMatrix * vec4(vertPosition, 1.0)).xyz;
|
||||
|
||||
gl_Position = projectionMatrix * modelViewMatrix * vec4(vertPosition, 1.0);
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
async function readObjFile(path) {
|
||||
const res = await fetch(path);
|
||||
const text = await res.text();
|
||||
|
||||
let vertices = [];
|
||||
let textures = [];
|
||||
let normals = [];
|
||||
|
||||
let result = [];
|
||||
|
||||
const lines = text.replace("\r","").split("\n");
|
||||
|
||||
for (let line of lines) {
|
||||
const parts = line.split(/\s+/);
|
||||
|
||||
switch (parts[0]) {
|
||||
case "v":
|
||||
vertices.push(parts.slice(1, 4).map(parseFloat));
|
||||
break;
|
||||
case "vt":
|
||||
textures.push(parts.slice(1, 3).map(parseFloat));
|
||||
break;
|
||||
case "vn":
|
||||
normals.push(parts.slice(1, 4).map(parseFloat));
|
||||
break;
|
||||
case "f":
|
||||
for (const part of parts.slice(1, 4)) {
|
||||
const [v, vt, vn] = part.split("/").map((i) => parseInt(i) - 1);
|
||||
result.push(...vertices[v], ...textures[vt], ...normals[vn]);
|
||||
}
|
||||
|
||||
// support for quads:
|
||||
if (parts.length > 4) {
|
||||
for (let i of [1, 3, 4]) {
|
||||
const part = parts[i];
|
||||
const [v, vt, vn] = part.split("/").map((i) => parseInt(i) - 1);
|
||||
result.push(...vertices[v], ...textures[vt], ...normals[vn]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
Loading…
Reference in New Issue