Skip to content

Instantly share code, notes, and snippets.

@OrangoMango
Created July 22, 2024 18:12
Show Gist options
  • Save OrangoMango/a2e712ece044d3ee382686d96921c250 to your computer and use it in GitHub Desktop.
Save OrangoMango/a2e712ece044d3ee382686d96921c250 to your computer and use it in GitHub Desktop.
3D rendering in the console
public class CubeRenderer{
private static class Point3D{
private double x, y, z;
public Point3D(double x, double y, double z){
this.x = x;
this.y = y;
this.z = z;
}
public double getX(){
return this.x;
}
public double getY(){
return this.y;
}
public double getZ(){
return this.z;
}
}
private static class Mesh{
private Point3D[][] triangles;
private String[] faces;
public Mesh(Point3D[] vertices, int[][] faces, String[] type){
this.faces = type;
this.triangles = new Point3D[faces.length][3];
for (int i = 0; i < faces.length; i++){
this.triangles[i][0] = vertices[faces[i][0]];
this.triangles[i][1] = vertices[faces[i][1]];
this.triangles[i][2] = vertices[faces[i][2]];
}
}
private Point3D rotate(Point3D pt){
double[] point = new double[]{pt.getX(), pt.getY(), pt.getZ()};
double[] p1 = CubeRenderer.multiply(CubeRenderer.getRotateX(0.05), point);
p1 = CubeRenderer.multiply(CubeRenderer.getRotateY(0.05), p1);
p1 = CubeRenderer.multiply(CubeRenderer.getRotateZ(0.05), p1);
return new Point3D(p1[0], p1[1], p1[2]);
}
public void update(){
for (int i = 0; i < this.triangles.length; i++){
this.triangles[i][0] = rotate(this.triangles[i][0]);
this.triangles[i][1] = rotate(this.triangles[i][1]);
this.triangles[i][2] = rotate(this.triangles[i][2]);
}
}
public void render(String[][] canvas, double[][] depthBuffer){
for (int n = 0; n < this.triangles.length; n++){
Point3D a = this.triangles[n][0];
Point3D b = this.triangles[n][1];
Point3D c = this.triangles[n][2];
int x1 = (int)((a.getX()+1)/2*(WIDTH-1));
int y1 = (int)((a.getY()+1)/2*(HEIGHT-1));
int z1 = (int)((a.getZ()+1)/2*(DEPTH-1));
int x2 = (int)((b.getX()+1)/2*(WIDTH-1));
int y2 = (int)((b.getY()+1)/2*(HEIGHT-1));
int z2 = (int)((b.getZ()+1)/2*(DEPTH-1));
int x3 = (int)((c.getX()+1)/2*(WIDTH-1));
int y3 = (int)((c.getY()+1)/2*(HEIGHT-1));
int z3 = (int)((c.getZ()+1)/2*(DEPTH-1));
if (y2 < y1){
y1 = swap(y2, y2 = y1);
x1 = swap(x2, x2 = x1);
z1 = swap(z2, z2 = z1);
}
if (y3 < y1){
y1 = swap(y3, y3 = y1);
x1 = swap(x3, x3 = x1);
z1 = swap(z3, z3 = z1);
}
if (y3 < y2){
y2 = swap(y3, y3 = y2);
x2 = swap(x3, x3 = x2);
z2 = swap(z3, z3 = z2);
}
int dx1 = x2-x1;
int dy1 = y2-y1;
double dz1 = z2-z1;
int dx2 = x3-x1;
int dy2 = y3-y1;
double dz2 = z3-z1;
double dax_step = 0, dbx_step = 0, dz1_step = 0, dz2_step = 0;
double finalZ;
if (dy1 != 0) dax_step = dx1/(double)Math.abs(dy1);
if (dy2 != 0) dbx_step = dx2/(double)Math.abs(dy2);
if (dy1 != 0) dz1_step = dz1/Math.abs(dy1);
if (dy2 != 0) dz2_step = dz2/Math.abs(dy2);
if (dy1 != 0){
for (int i = y1; i <= y2; i++){
int ax = x1+(int)((i-y1)*dax_step);
int bx = x1+(int)((i-y1)*dbx_step);
double sz = z1+(i-y1)*dz1_step;
double ez = z1+(i-y1)*dz2_step;
if (ax > bx){
ax = swap(bx, bx = ax);
sz = swap(ez, ez = sz);
}
double tstep = 1.0/(bx-ax);
double t = 0.0;
for (int j = ax; j < bx; j++){
finalZ = (1-t)*sz+t*ez;
putInBuffer(depthBuffer, j, i, 1/finalZ);
putInCanvas(canvas, j, i, this.faces[n], depthBuffer, 1/finalZ);
t += tstep;
}
}
}
dx1 = x3-x2;
dy1 = y3-y2;
dz1 = z3-z2;
if (dy1 != 0) dax_step = dx1/(double)Math.abs(dy1);
dz1_step = 0;
if (dy1 != 0) dz1_step = dz1/Math.abs(dy1);
if (dy1 != 0){
for (int i = y2; i <= y3; i++){
int ax = x2+(int)((i-y2)*dax_step);
int bx = x1+(int)((i-y1)*dbx_step);
double sz = z2+(i-y2)*dz1_step;
double ez = z1+(i-y1)*dz2_step;
if (ax > bx){
ax = swap(bx, bx = ax);
sz = swap(ez, ez = sz);
}
double tstep = 1.0/(bx-ax);
double t = 0.0;
for (int j = ax; j < bx; j++){
finalZ = (1-t)*sz+t*ez;
putInBuffer(depthBuffer, j, i, 1/finalZ);
putInCanvas(canvas, j, i, this.faces[n], depthBuffer, 1/finalZ);
t += tstep;
}
}
}
}
}
}
private static final int WIDTH = 100;
private static final int HEIGHT = 50;
private static final int DEPTH = 50;
public static void main(String[] args) throws InterruptedException{
double size = 0.5;
Mesh mesh = new Mesh(new Point3D[]{new Point3D(0, 0, 0), new Point3D(0, size, 0), new Point3D(size, size, 0), new Point3D(size, 0, 0),
new Point3D(0, 0, size), new Point3D(0, size, size), new Point3D(size, size, size), new Point3D(size, 0, size)},
new int[][]{{0, 1, 2}, {0, 2, 3}, {3, 2, 6},
{3, 6, 7}, {7, 6, 5}, {7, 5, 4},
{4, 5, 1}, {4, 1, 0}, {1, 5, 6},
{1, 6, 2}, {4, 0, 3}, {4, 3, 7}}, new String[]{"#", "#", "$", "$", "*", "*", ",", ",", "-", "-", ":", ":"});
String[][] canvas = new String[WIDTH][HEIGHT];
double[][] depthBuffer = new double[WIDTH][HEIGHT];
while (true){
System.out.print("\033[H");
for (int i = 0; i < WIDTH; i++){
for (int j = 0; j < HEIGHT; j++){
canvas[i][j] = String.valueOf(' '); // Background
depthBuffer[i][j] = 0;
}
}
mesh.update();
mesh.render(canvas, depthBuffer);
// Render
for (int y = 0; y < HEIGHT; y++){
for (int x = 0; x < WIDTH; x++){
System.out.print(canvas[x][y]);
}
System.out.println();
}
Thread.sleep(50);
}
}
public static <T> T swap(T a, T b){
return a;
}
public static void putInBuffer(double[][] buffer, int x, int y, double value){
if (x >= 0 && y >= 0 && x < WIDTH && y < HEIGHT){
if (value >= buffer[x][y]) buffer[x][y] = value;
}
}
public static void putInCanvas(String[][] canvas, int x, int y, String pixel, double[][] db, double v){
if (x >= 0 && y >= 0 && x < WIDTH && y < HEIGHT){
if (v >= db[x][y]) canvas[x][y] = pixel;
}
}
public static double[] multiply(double[][] mat, double[] vect){
double[] out = new double[mat.length];
for (int i = 0; i < out.length; i++){
double sum = 0;
for (int j = 0; j < vect.length; j++){
sum += mat[i][j]*vect[j];
}
out[i] = sum;
}
return out;
}
public static double[][] getRotateX(double angle){
return new double[][]{
{1, 0, 0, 0},
{0, Math.cos(angle), -Math.sin(angle), 0},
{0, Math.sin(angle), Math.cos(angle), 0},
{0, 0, 0, 1}
};
}
public static double[][] getRotateY(double angle){
return new double[][]{
{Math.cos(angle), 0, -Math.sin(angle), 0},
{0, 1, 0, 0},
{Math.sin(angle), 0, Math.cos(angle), 0},
{0, 0, 0, 1}
};
}
public static double[][] getRotateZ(double angle){
return new double[][]{
{Math.cos(angle), Math.sin(angle), 0, 0},
{-Math.sin(angle), Math.cos(angle), 0, 0},
{0, 0, 1, 0},
{0, 0, 0, 1}
};
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment