Skip to content

Commit 34c8b94

Browse files
committed
Finished 3d rendering
1 parent 401d716 commit 34c8b94

5 files changed

Lines changed: 130 additions & 38 deletions

File tree

index.html

Lines changed: 21 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -7,21 +7,27 @@
77
<script type="module" src="/src/main.ts"></script>
88
</head>
99
<body>
10-
<div id="container"></div>
11-
<table border="0" cellpadding="0" cellspacing="0" spacing="0" padding="0">
12-
<tr>
13-
<td bgcolor="#FF0000" width="1" height="1"></td>
14-
<td bgcolor="#FF0000" width="1" height="1"></td>
15-
</tr>
16-
<tr>
17-
<td bgcolor="#FF0000" width="1" height="1"></td>
18-
<td bgcolor="#FF0000" width="1" height="1"></td>
19-
</tr>
20-
</table>
21-
22-
<input id="width" type="number">
23-
<input id="height" type="number">
24-
<button id="render">Render</button>
10+
<center>
11+
<table border="1" >
12+
<tr>
13+
<td colspan="2">
14+
<svg
15+
width="400px"
16+
height="300px"
17+
viewBox="-1 -1 2 2"
18+
preserveAspectRatio="none"
19+
stroke-width ="0.01"
20+
id="container">
21+
<path id="render-target" fill="none" stroke="black" stroke-width="0.005" stroke-linejoin="round" />
22+
</svg>
23+
</td>
24+
</tr>
25+
<tr>
26+
<td>Wireframe mode? <input type="checkbox" id="wireframe-mode"></td>
27+
<td><button id="render">Render</button></td>
28+
</tr>
29+
</table>
30+
</center>
2531

2632
</body>
2733
</html>

src/main.ts

Lines changed: 65 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,71 @@
1-
import { build_color_table, build_color_text } from "./to_html";
2-
import { generate_trash_data } from "./image";
1+
import { build_3d_svg} from "./to_html";
2+
import { mat4, vec3 } from "./math/types";
3+
import { PrimitiveAssembler } from "./rendering/primitive_assembler";
4+
import { identity, look_at, perspective, rotate, translate } from "./math/transformations";
5+
import { transform_vertex } from "./rendering/vertex";
6+
import { rasterize } from "./rendering/rasterizer";
7+
import { create_sphere } from "./rendering/primitives";
38

49

5-
export function main(){
10+
11+
export function main_3d() {
612
const target = document.getElementById('container');
7-
const width_el = document.getElementById('width') as HTMLInputElement | null;
8-
const height_el = document.getElementById('height') as HTMLInputElement | null;
9-
const cell_size_el = document.getElementById('cell-size') as HTMLInputElement | null;
10-
11-
const width: number = width_el?.valueAsNumber ?? 1;
12-
const height: number = height_el?.valueAsNumber ?? 1;
13-
const cell_size: number = cell_size_el?.valueAsNumber ?? 2;
14-
15-
const image_data = generate_trash_data(width,height);
16-
const table_html = build_color_table(image_data,cell_size);
17-
//const text_html = build_color_text(image_data, "■");
18-
if(!target){
19-
return;
13+
if (!target) return;
14+
const wireframe_el = document.getElementById('wireframe-mode') as HTMLInputElement;
15+
const do_wireframe:boolean = wireframe_el?.checked;
16+
const sun_mesh = create_sphere(1.5, 16, 16);
17+
const planet_mesh = create_sphere(0.5, 12, 12);
18+
19+
const assembler = new PrimitiveAssembler(12000);
20+
21+
const aspect = 400 / 300;
22+
const proj_mat = perspective(60 * Math.PI / 180, aspect, 0.1, 100);
23+
const view_mat = look_at(
24+
vec3(0, 2, 6.5),
25+
vec3(0, 0, 0),
26+
vec3(0, 1, 0)
27+
);
28+
let time = 0;
29+
const loop = () => {
30+
time += 0.01;
31+
32+
let frame_html = "";
33+
let sun_model = identity();
34+
sun_model = rotate(sun_model, time * 0.5, vec3(0, 1, 0));
35+
frame_html += process_object(sun_mesh, sun_model);
36+
let planet_model = identity();
37+
planet_model = rotate(planet_model, time, vec3(0, 1, 0));
38+
planet_model = translate(planet_model, vec3(3.5, 0, 0));
39+
planet_model = rotate(planet_model, time * 3, vec3(1, 0, 1));
40+
frame_html += process_object(planet_mesh, planet_model);
41+
42+
target!.innerHTML = frame_html;
43+
requestAnimationFrame(loop);
44+
}
45+
46+
const process_object = (mesh: { vertices: Float32Array, indices: Uint16Array }, model: mat4) => {
47+
const vertex_count = mesh.vertices.length / 3;
48+
const projected_buffer = new Float32Array(vertex_count * 4);
49+
50+
for (let i = 0; i < vertex_count; i++) {
51+
const x = mesh.vertices[i * 3 + 0];
52+
const y = mesh.vertices[i * 3 + 1];
53+
const z = mesh.vertices[i * 3 + 2];
54+
55+
const v_clip = transform_vertex(proj_mat, view_mat, model, vec3(x, y, z));
56+
57+
projected_buffer[i * 4 + 0] = v_clip[0];
58+
projected_buffer[i * 4 + 1] = v_clip[1];
59+
projected_buffer[i * 4 + 2] = v_clip[2];
60+
projected_buffer[i * 4 + 3] = v_clip[3];
61+
}
62+
63+
const assembled = assembler.assemble_primitives(projected_buffer, mesh.indices);
64+
65+
const screen_verts = rasterize(assembled,true);
66+
return build_3d_svg(screen_verts, do_wireframe);
2067
}
21-
target.innerHTML = table_html;
68+
requestAnimationFrame(loop);
2269
}
2370

24-
document.getElementById('render')?.addEventListener('click', main);
71+
document.getElementById('render')?.addEventListener('click', main_3d);

src/rendering/primitive_assembler.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ export class PrimitiveAssembler{
77
this.out = new ArrayType(size);
88
}
99

10-
assemble_primitives(vertices:ArrayType, indices:ArrayType, stride:number = 4):ArrayType{
10+
assemble_primitives(vertices:ArrayType, indices:Uint16Array, stride:number = 4):ArrayType{
1111
const required_space = indices.length * stride;
1212
if (this.out.length < required_space){
1313
console.warn("Resizing this.out - this generally shouldn't happen");

src/rendering/primitives.ts

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import { ArrayType } from "../math/types";
2+
3+
export function create_sphere(radius: number, rings: number, slices: number) {
4+
const vertices: number[] = [];
5+
const indices: number[] = [];
6+
for (let lat = 0; lat <= rings; lat++) {
7+
const theta = (lat * Math.PI) / rings;
8+
const sinTheta = Math.sin(theta);
9+
const cosTheta = Math.cos(theta);
10+
11+
for (let lon = 0; lon <= slices; lon++) {
12+
const phi = (lon * 2 * Math.PI) / slices;
13+
const sinPhi = Math.sin(phi);
14+
const cosPhi = Math.cos(phi);
15+
16+
const x = cosPhi * sinTheta;
17+
const y = cosTheta;
18+
const z = sinPhi * sinTheta;
19+
20+
vertices.push(x * radius, y * radius, z * radius);
21+
}
22+
}
23+
for (let lat = 0; lat < rings; lat++) {
24+
for (let lon = 0; lon < slices; lon++) {
25+
const first = (lat * (slices + 1)) + lon;
26+
const second = first + slices + 1;
27+
28+
indices.push(first, second, first + 1);
29+
indices.push(second, second + 1, first + 1);
30+
}
31+
}
32+
return {
33+
vertices: new ArrayType(vertices),
34+
indices: new Uint16Array(indices)
35+
};
36+
}

src/rendering/render.ts

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
1-
import type { ArrayType } from "../math/types";
1+
import { ArrayType } from "../math/types";
2+
import { rasterize } from "./rasterizer";
3+
import { build_3d_svg } from "../to_html";
24

3-
4-
export function render(target:HTMLElement, vertices:ArrayType, use_rect:boolean = true, stride:number = 4) {
5-
5+
export function render(target: HTMLElement, vertices: ArrayType, use_rect: boolean = true, invert_y:boolean = true, stride: number = 4):void {
6+
const screen_verts = rasterize(vertices, invert_y, stride);
7+
const svg_content = build_3d_svg(screen_verts, use_rect);
8+
target.innerHTML = svg_content;
69
}

0 commit comments

Comments
 (0)