This demo shows the algorithm for rendering a rotating 3d cube, all without a pre-existing 3d framework. The solution is to use a bunch of matrix multiplication and trigonometric functions. On retro systems, these functions would likely be pre-calculated and turned into a lookup table. Bby trading ROM space by cpu time, the cube can quickly be display. On more modern fantasy consoles, such as pico-8, this algorithm works as shown.
const ra = require('raster');
var angle = 0;
ra.setSize({w:140, h:140});
ra.setZoom(3);
ra.originAtCenter();
function draw() {
ra.fillColor(0x00);
ra.setColor(0x23);
let points = [
[ 10, 10, 10],
[ 10, -10, 10],
[-10, -10, 10],
[-10, 10, 10],
[ 10, 10, -10],
[ 10, -10, -10],
[-10, -10, -10],
[-10, 10, -10]
];
let distance = -50;
rotateByZ(points, angle);
rotateByY(points, angle);
rotateByX(points, angle);
projection(points, distance);
angle += 0.01;
for (let i = 0; i < 4; i++) {
putLine(points[i ], points[(i + 1) % 4]);
putLine(points[i + 4], points[(i + 1) % 4 + 4]);
putLine(points[i], points[i + 4]);
}
}
ra.run(draw);
function projection(points, distance) {
let scale = 180;
for (let i = 0; i < points.length; i++) {
let x = points[i][0];
let y = points[i][1];
let z = points[i][2];
let d = z - distance;
let proj_x = x / d * scale;
let proj_y = y / d * scale;
points[i] = [proj_x, proj_y, 1];
}
}
function rotateByX(points, angle) {
let mat = [[Math.cos(angle), -Math.sin(angle), 0],
[Math.sin(angle), Math.cos(angle), 0],
[ 0, 0, 1]];
applyMatrixToPoints(points, mat);
}
function rotateByY(points, angle) {
let mat = [[Math.cos(angle), 0, -Math.sin(angle)],
[ 0, 1, 0],
[Math.sin(angle), 0, Math.cos(angle)]];
applyMatrixToPoints(points, mat);
}
function rotateByZ(points, angle) {
let mat = [[1 , 0, 0],
[0, Math.cos(angle), -Math.sin(angle)],
[0, Math.sin(angle), Math.cos(angle)]];
applyMatrixToPoints(points, mat);
}
function applyMatrixToPoints(points, mat) {
for (let i = 0; i < points.length; i++) {
let p = points[i];
points[i] = matrixMult(mat, [p[0], p[1], p[2]]);
}
}
function matrixMult(matrix, p) {
let x = p[0];
let y = p[1];
let z = p[2];
let new_x = x * matrix[0][0] + y * matrix[0][1] + z * matrix[0][2];
let new_y = x * matrix[1][0] + y * matrix[1][1] + z * matrix[1][2];
let new_z = x * matrix[2][0] + y * matrix[2][1] + z * matrix[2][2];
return [new_x, new_y, new_z];
}
function putLine(start, end) {
ra.drawLine(start[0], start[1], end[0], end[1]);
}