Skip to main content

render_test/
render_test.rs

1//! Render test: produces a PPM image demonstrating the software rasterizer
2//!
3//! Usage: cargo run -p nexcore-softrender --example render_test > output.ppm
4
5use nexcore_softrender::geometry::shapes;
6use nexcore_softrender::geometry::{Mesh, Triangle, Vertex};
7use nexcore_softrender::math::transform::{compose_2d, rotate_2d, scale_2d, translate_2d};
8use nexcore_softrender::math::{Color, Vec2};
9use nexcore_softrender::pipeline::{Framebuffer, rasterize_mesh, rasterize_mesh_transformed};
10
11const WIDTH: u32 = 800;
12const HEIGHT: u32 = 600;
13
14fn main() {
15    let mut fb = Framebuffer::new(WIDTH, HEIGHT);
16
17    // Background: NexVigilant Navy
18    fb.clear(Color::NAVY);
19
20    // === Section 1: Basic shapes (top-left) ===
21
22    // Colored rectangles
23    let r1 = shapes::rect(20.0, 20.0, 120.0, 80.0, Color::ACCENT_CYAN);
24    rasterize_mesh(&mut fb, &r1);
25
26    let r2 = shapes::rect(160.0, 20.0, 120.0, 80.0, Color::ACCENT_GREEN);
27    rasterize_mesh(&mut fb, &r2);
28
29    let r3 = shapes::rect(300.0, 20.0, 120.0, 80.0, Color::ACCENT_GOLD);
30    rasterize_mesh(&mut fb, &r3);
31
32    let r4 = shapes::rect(440.0, 20.0, 120.0, 80.0, Color::ACCENT_RED);
33    rasterize_mesh(&mut fb, &r4);
34
35    // === Section 2: Gradient rectangle (top-right) ===
36    let grad = shapes::rect_gradient(
37        600.0,
38        20.0,
39        180.0,
40        80.0,
41        Color::ACCENT_CYAN,
42        Color::ACCENT_GREEN,
43        Color::ACCENT_GOLD,
44        Color::ACCENT_RED,
45    );
46    rasterize_mesh(&mut fb, &grad);
47
48    // === Section 3: Circles (middle row) ===
49    let c1 = shapes::circle(80.0, 180.0, 50.0, 64, Color::ACCENT_CYAN);
50    rasterize_mesh(&mut fb, &c1);
51
52    let c2 = shapes::circle(220.0, 180.0, 50.0, 6, Color::ACCENT_GREEN); // hexagon
53    rasterize_mesh(&mut fb, &c2);
54
55    let c3 = shapes::circle(360.0, 180.0, 50.0, 3, Color::ACCENT_GOLD); // triangle
56    rasterize_mesh(&mut fb, &c3);
57
58    let c4 = shapes::circle(500.0, 180.0, 50.0, 32, Color::ACCENT_RED);
59    rasterize_mesh(&mut fb, &c4);
60
61    // === Section 4: Lines (middle) ===
62    for i in 0..8 {
63        let angle = (i as f64) * core::f64::consts::PI / 8.0;
64        let cx = 680.0;
65        let cy = 180.0;
66        let r = 50.0;
67        let x1 = cx + r * angle.cos();
68        let y1 = cy + r * angle.sin();
69        let color = Color::rgba(i as f64 / 8.0, 1.0 - (i as f64 / 8.0), 0.5, 1.0);
70        let l = shapes::line(cx, cy, x1, y1, 2.0, color);
71        rasterize_mesh(&mut fb, &l);
72    }
73
74    // === Section 5: Rounded rectangles (lower-middle) ===
75    let rr1 = shapes::rounded_rect(20.0, 260.0, 160.0, 80.0, 15.0, Color::ACCENT_CYAN, 8);
76    rasterize_mesh(&mut fb, &rr1);
77
78    let rr2 = shapes::rounded_rect(200.0, 260.0, 160.0, 80.0, 30.0, Color::ACCENT_GREEN, 8);
79    rasterize_mesh(&mut fb, &rr2);
80
81    let rr3 = shapes::rounded_rect(380.0, 260.0, 160.0, 80.0, 40.0, Color::ACCENT_GOLD, 16);
82    rasterize_mesh(&mut fb, &rr3);
83
84    // === Section 6: Barycentric color interpolation (RGB triangle) ===
85    let mut tri_mesh = Mesh::new();
86    tri_mesh.push(Triangle::new(
87        Vertex::colored(620.0, 260.0, Color::RED),
88        Vertex::colored(780.0, 260.0, Color::GREEN),
89        Vertex::colored(700.0, 340.0, Color::BLUE),
90    ));
91    rasterize_mesh(&mut fb, &tri_mesh);
92
93    // === Section 7: Transformed shapes (bottom) ===
94
95    // A small square, rotated 45° and translated
96    let sq = shapes::rect(0.0, 0.0, 60.0, 60.0, Color::WHITE);
97    let t = compose_2d(
98        &translate_2d(80.0, 430.0),
99        &compose_2d(
100            &rotate_2d(core::f64::consts::FRAC_PI_4),
101            &translate_2d(-30.0, -30.0), // center the square on origin before rotation
102        ),
103    );
104    rasterize_mesh_transformed(&mut fb, &sq, &t);
105
106    // Scaled circle
107    let small_c = shapes::circle(0.0, 0.0, 30.0, 32, Color::ACCENT_CYAN);
108    let t2 = compose_2d(
109        &translate_2d(220.0, 430.0),
110        &scale_2d(2.0, 1.0), // ellipse via non-uniform scale
111    );
112    rasterize_mesh_transformed(&mut fb, &small_c, &t2);
113
114    // Chain of rotated rectangles (fan)
115    for i in 0..12 {
116        let angle = (i as f64) * core::f64::consts::PI / 6.0;
117        let bar = shapes::rect(
118            0.0,
119            -3.0,
120            40.0,
121            6.0,
122            Color::rgba(0.5 + 0.5 * angle.cos(), 0.5 + 0.5 * angle.sin(), 0.7, 0.8),
123        );
124        let t_fan = compose_2d(&translate_2d(420.0, 460.0), &rotate_2d(angle));
125        rasterize_mesh_transformed(&mut fb, &bar, &t_fan);
126    }
127
128    // === Section 8: Primitive symbols (bottom-right) ===
129    // Draw 16 colored squares in a 4×4 grid representing the Lex Primitiva
130    let prim_colors = [
131        Color::from_hex(0x00CCFF), // σ Sequence
132        Color::from_hex(0x4488FF), // μ Mapping
133        Color::from_hex(0x00CC66), // ς State
134        Color::from_hex(0xCC66FF), // ρ Recursion
135        Color::from_hex(0x666666), // ∅ Void
136        Color::from_hex(0xFFCC00), // ∂ Boundary
137        Color::from_hex(0x008888), // ν Frequency
138        Color::from_hex(0xFFFFFF), // ∃ Existence
139        Color::from_hex(0x006633), // π Persistence
140        Color::from_hex(0xFF4444), // → Causality
141        Color::from_hex(0xCC8800), // κ Comparison
142        Color::from_hex(0x00CCFF), // N Quantity
143        Color::from_hex(0x8800CC), // λ Location
144        Color::from_hex(0x880000), // ∝ Irreversibility
145        Color::from_hex(0x00CC66), // Σ Sum
146        Color::from_hex(0xFFCC00), // × Product
147    ];
148
149    for (i, color) in prim_colors.iter().enumerate() {
150        let row = i / 4;
151        let col = i % 4;
152        let x = 580.0 + col as f64 * 55.0;
153        let y = 380.0 + row as f64 * 55.0;
154        let sq = shapes::rounded_rect(x, y, 45.0, 45.0, 8.0, *color, 6);
155        rasterize_mesh(&mut fb, &sq);
156    }
157
158    // === Output PPM to stdout ===
159    // PPM P6: binary RGB
160    let header = format!("P6\n{} {}\n255\n", WIDTH, HEIGHT);
161    let mut output: Vec<u8> = Vec::with_capacity(header.len() + (WIDTH * HEIGHT * 3) as usize);
162    output.extend_from_slice(header.as_bytes());
163
164    for &pixel in &fb.pixels {
165        output.push(((pixel >> 16) & 0xFF) as u8); // R
166        output.push(((pixel >> 8) & 0xFF) as u8); // G
167        output.push((pixel & 0xFF) as u8); // B
168    }
169
170    // Write binary to stdout
171    use std::io::Write;
172    let stdout = std::io::stdout();
173    let mut lock = stdout.lock();
174    let _ = lock.write_all(&output);
175}