#import "modules/std.sx"; #import "modules/ffi/sdl3.sx"; #import "modules/ffi/opengl.sx"; #import "modules/math"; WIDTH :f32: 800; HEIGHT :f32: 600; vec4 :: (x: f32, y: f32, z: f32, w: f32) -> Vector(4, f32) { .[x, y, z, w] } // ---- Matrix44: column-major 4×4 matrix ---- Matrix44 :: union { data: [16]f32; struct { c0, c1, c2, c3: Vector(4, f32); }; } mat4_multiply :: (a: *Matrix44, b: *Matrix44) -> Matrix44 { out : Matrix44 = ---; out.c0 = a.c0 * b.c0.x + a.c1 * b.c0.y + a.c2 * b.c0.z + a.c3 * b.c0.w; out.c1 = a.c0 * b.c1.x + a.c1 * b.c1.y + a.c2 * b.c1.z + a.c3 * b.c1.w; out.c2 = a.c0 * b.c2.x + a.c1 * b.c2.y + a.c2 * b.c2.z + a.c3 * b.c2.w; out.c3 = a.c0 * b.c3.x + a.c1 * b.c3.y + a.c2 * b.c3.z + a.c3 * b.c3.w; out } multiply :: ufcs mat4_multiply; mat4_perspective :: (fov: f32, aspect: f32, near: f32, far: f32) -> Matrix44 { half := fov / 2.0; f := cos(half) / sin(half); m : Matrix44 = ---; m.c0 = vec4(f / aspect, 0.0, 0.0, 0.0); m.c1 = vec4(0.0, f, 0.0, 0.0); m.c2 = vec4(0.0, 0.0, (far + near) / (near - far), -1.0); m.c3 = vec4(0.0, 0.0, (2.0 * far * near) / (near - far), 0.0); m } mat4_rotate_y :: (angle: f32) -> Matrix44 { c := cos(angle); s := sin(angle); m : Matrix44 = ---; m.c0 = vec4(c, 0.0, 0.0 - s, 0.0); m.c1 = vec4(0.0, 1.0, 0.0, 0.0); m.c2 = vec4(s, 0.0, c, 0.0); m.c3 = vec4(0.0, 0.0, 0.0, 1.0); m } mat4_rotate_x :: (angle: f32) -> Matrix44 { c := cos(angle); s := sin(angle); m : Matrix44 = ---; m.c0 = vec4(1.0, 0.0, 0.0, 0.0); m.c1 = vec4(0.0, c, s, 0.0); m.c2 = vec4(0.0, 0.0 - s, c, 0.0); m.c3 = vec4(0.0, 0.0, 0.0, 1.0); m } mat4_translate :: (tx: f32, ty: f32, tz: f32) -> Matrix44 { m : Matrix44 = ---; m.c0 = vec4(1.0, 0.0, 0.0, 0.0); m.c1 = vec4(0.0, 1.0, 0.0, 0.0); m.c2 = vec4(0.0, 0.0, 1.0, 0.0); m.c3 = vec4(tx, ty, tz, 1.0); m } // ---- Main ---- main :: () { SDL_Init(SDL_INIT_VIDEO); SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3); SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_FORWARD_COMPATIBLE_FLAG); SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24); window := SDL_CreateWindow("sx GL cube", xx WIDTH, xx HEIGHT, SDL_WINDOW_OPENGL); gl_ctx := SDL_GL_CreateContext(window); SDL_GL_MakeCurrent(window, gl_ctx); SDL_GL_SetSwapInterval(1); load_gl(SDL_GL_GetProcAddress); glEnable(GL_DEPTH_TEST); glDepthFunc(GL_LESS); print("create program: {}\n{}\n", VERT_SHADER_SRC, FRAG_SHADER_SRC); program := create_program(VERT_SHADER_SRC, FRAG_SHADER_SRC); glUseProgram(program); mvp_loc : i32 = glGetUniformLocation(program, "uMVP"); light_loc : i32 = glGetUniformLocation(program, "uLightDir"); wire_loc : i32 = glGetUniformLocation(program, "uWire"); // Cube vertices: pos(vec4 w=1) + normal(vec4 w=0), 36 vertices × 2 vec4s = 72 vertices : []Vector(4, f32) = .[ // Front face (z = +0.5) .[-0.5, -0.5, 0.5, 1.0], vec4( 0.0, 0.0, 1.0, 0.0), vec4( 0.5, -0.5, 0.5, 1.0), vec4( 0.0, 0.0, 1.0, 0.0), vec4( 0.5, 0.5, 0.5, 1.0), vec4( 0.0, 0.0, 1.0, 0.0), vec4(-0.5, -0.5, 0.5, 1.0), vec4( 0.0, 0.0, 1.0, 0.0), vec4( 0.5, 0.5, 0.5, 1.0), vec4( 0.0, 0.0, 1.0, 0.0), vec4(-0.5, 0.5, 0.5, 1.0), vec4( 0.0, 0.0, 1.0, 0.0), // Back face (z = -0.5) vec4( 0.5, -0.5, -0.5, 1.0), vec4( 0.0, 0.0, -1.0, 0.0), vec4(-0.5, -0.5, -0.5, 1.0), vec4( 0.0, 0.0, -1.0, 0.0), vec4(-0.5, 0.5, -0.5, 1.0), vec4( 0.0, 0.0, -1.0, 0.0), vec4( 0.5, -0.5, -0.5, 1.0), vec4( 0.0, 0.0, -1.0, 0.0), vec4(-0.5, 0.5, -0.5, 1.0), vec4( 0.0, 0.0, -1.0, 0.0), vec4( 0.5, 0.5, -0.5, 1.0), vec4( 0.0, 0.0, -1.0, 0.0), // Top face (y = +0.5) vec4(-0.5, 0.5, 0.5, 1.0), vec4( 0.0, 1.0, 0.0, 0.0), vec4( 0.5, 0.5, 0.5, 1.0), vec4( 0.0, 1.0, 0.0, 0.0), vec4( 0.5, 0.5, -0.5, 1.0), vec4( 0.0, 1.0, 0.0, 0.0), vec4(-0.5, 0.5, 0.5, 1.0), vec4( 0.0, 1.0, 0.0, 0.0), vec4( 0.5, 0.5, -0.5, 1.0), vec4( 0.0, 1.0, 0.0, 0.0), vec4(-0.5, 0.5, -0.5, 1.0), vec4( 0.0, 1.0, 0.0, 0.0), // Bottom face (y = -0.5) vec4(-0.5, -0.5, -0.5, 1.0), vec4( 0.0, -1.0, 0.0, 0.0), vec4( 0.5, -0.5, -0.5, 1.0), vec4( 0.0, -1.0, 0.0, 0.0), vec4( 0.5, -0.5, 0.5, 1.0), vec4( 0.0, -1.0, 0.0, 0.0), vec4(-0.5, -0.5, -0.5, 1.0), vec4( 0.0, -1.0, 0.0, 0.0), vec4( 0.5, -0.5, 0.5, 1.0), vec4( 0.0, -1.0, 0.0, 0.0), vec4(-0.5, -0.5, 0.5, 1.0), vec4( 0.0, -1.0, 0.0, 0.0), // Right face (x = +0.5) vec4( 0.5, -0.5, 0.5, 1.0), vec4( 1.0, 0.0, 0.0, 0.0), vec4( 0.5, -0.5, -0.5, 1.0), vec4( 1.0, 0.0, 0.0, 0.0), vec4( 0.5, 0.5, -0.5, 1.0), vec4( 1.0, 0.0, 0.0, 0.0), vec4( 0.5, -0.5, 0.5, 1.0), vec4( 1.0, 0.0, 0.0, 0.0), vec4( 0.5, 0.5, -0.5, 1.0), vec4( 1.0, 0.0, 0.0, 0.0), vec4( 0.5, 0.5, 0.5, 1.0), vec4( 1.0, 0.0, 0.0, 0.0), // Left face (x = -0.5) vec4(-0.5, -0.5, -0.5, 1.0), vec4(-1.0, 0.0, 0.0, 0.0), vec4(-0.5, -0.5, 0.5, 1.0), vec4(-1.0, 0.0, 0.0, 0.0), vec4(-0.5, 0.5, 0.5, 1.0), vec4(-1.0, 0.0, 0.0, 0.0), vec4(-0.5, -0.5, -0.5, 1.0), vec4(-1.0, 0.0, 0.0, 0.0), vec4(-0.5, 0.5, 0.5, 1.0), vec4(-1.0, 0.0, 0.0, 0.0), vec4(-0.5, 0.5, -0.5, 1.0), vec4(-1.0, 0.0, 0.0, 0.0) ]; // Create VAO and VBO vao : u32 = 0; vbo : u32 = 0; glGenVertexArrays(1, vao); glGenBuffers(1, vbo); glBindVertexArray(vao); glBindBuffer(GL_ARRAY_BUFFER, vbo); glBufferData(GL_ARRAY_BUFFER, 1152, vertices.ptr, GL_STATIC_DRAW); // Position attribute (location 0): 3 floats, stride 32 bytes, offset 0 glVertexAttribPointer(0, 3, GL_FLOAT, 0, 32, xx 0); glEnableVertexAttribArray(0); // Normal attribute (location 1): 3 floats, stride 32 bytes, offset 16 glVertexAttribPointer(1, 3, GL_FLOAT, 0, 32, xx 16); glEnableVertexAttribArray(1); // Set light direction glUniform3f(light_loc, 0.5, 0.7, 1.0); glUniform1f(wire_loc, 0.0); // Render loop running := true; event : SDL_Event = .none; while running { while SDL_PollEvent(event) { if event == { case .quit: running = false; case .key_up: (e) { if e.key == { case .escape: running = false; //case . } } case .window_exposed: (e) { } case .key_down: (e) { k : u32 = xx e.key; print("ts={} wid={} sc={} key={}\n", e.timestamp, e.window_id, e.scancode, k); } } } // Compute rotation angle from time ticks := SDL_GetTicks(); ms : f32 = xx ticks; angle := ms * 0.001; // Build matrices proj := mat4_perspective(PI / 4.0, WIDTH / HEIGHT, 0.1, 100.0); view := mat4_translate(0.0, 0.0, -3.0); rot_y := mat4_rotate_y(angle); rot_x := mat4_rotate_x(angle * 0.7); model := mat4_multiply(rot_y, rot_x); vm := mat4_multiply(view, model); mvp := mat4_multiply(proj, vm); glUniformMatrix4fv(mvp_loc, 1, 0, mvp.data); glClearColor(0.1, 0.1, 0.15, 1.0); glClear(GL_COLOR_BUFFER_BIT + GL_DEPTH_BUFFER_BIT); // Solid pass glUniform1f(wire_loc, 0.0); glBindVertexArray(vao); glDrawArrays(GL_TRIANGLES, 0, 36); // Wireframe overlay glDepthFunc(GL_LEQUAL); glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); glLineWidth(2.0); glUniform1f(wire_loc, 1.0); glDrawArrays(GL_TRIANGLES, 0, 36); // Restore glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); glDepthFunc(GL_LESS); glUniform1f(wire_loc, 0.0); SDL_GL_SwapWindow(window); } SDL_GL_DestroyContext(gl_ctx); SDL_DestroyWindow(window); SDL_Quit(); } VERT_SHADER_SRC : string = #string GLSL #version 330 core layout (location = 0) in vec3 aPos; layout (location = 1) in vec3 aNormal; uniform mat4 uMVP; out vec3 vNormal; out vec3 vPos; void main() { gl_Position = uMVP * vec4(aPos, 1.0); vNormal = aNormal; vPos = aPos; } GLSL; FRAG_SHADER_SRC : string = #string GLSL #version 330 core in vec3 vNormal; in vec3 vPos; out vec4 FragColor; uniform vec3 uLightDir; uniform float uWire; void main() { if (uWire > 0.5) { FragColor = vec4(0.05, 0.05, 0.05, 1.0); return; } vec3 n = normalize(vNormal); vec3 l = normalize(uLightDir); float diff = max(dot(n, l), 0.15); float cx = floor(vPos.x * 2.0 + 0.001); float cy = floor(vPos.y * 2.0 + 0.001); float cz = floor(vPos.z * 2.0 + 0.001); float check = mod(cx + cy + cz, 2.0); vec3 col1 = vec3(0.9, 0.5, 0.2); vec3 col2 = vec3(0.2, 0.6, 0.9); vec3 base = mix(col1, col2, check); FragColor = vec4(base * diff, 1.0); } GLSL;