#import "modules/std.sx"; #import "modules/sdl3.sx"; #import "modules/opengl.sx"; PI :f32: 3.14159265; // ---- Matrix math (column-major [16]f32, passed as [*]f32) ---- mat4_zero :: (m: [*]f32) { i := 0; while i < 16 { m[i] = 0.0; i += 1; } } mat4_identity :: (m: [*]f32) { mat4_zero(m); m[0] = 1.0; m[5] = 1.0; m[10] = 1.0; m[15] = 1.0; } mat4_multiply :: (out: [*]f32, a: [*]f32, b: [*]f32) { tmp : [16]f32 = ---; j := 0; while j < 4 { i := 0; while i < 4 { sum : f32 = 0.0; k := 0; while k < 4 { sum = sum + a[k * 4 + i] * b[j * 4 + k]; k += 1; } tmp[j * 4 + i] = sum; i += 1; } j += 1; } i := 0; while i < 16 { out[i] = tmp[i]; i += 1; } } mat4_perspective :: (m: [*]f32, fov: f32, aspect: f32, near: f32, far: f32) { mat4_zero(m); half := fov / 2.0; f := cos(half) / sin(half); m[0] = f / aspect; m[5] = f; m[10] = (far + near) / (near - far); m[11] = -1.0; m[14] = (2.0 * far * near) / (near - far); } mat4_rotate_y :: (m: [*]f32, angle: f32) { mat4_zero(m); c := cos(angle); s := sin(angle); m[0] = c; m[2] = 0.0 - s; m[5] = 1.0; m[8] = s; m[10] = c; m[15] = 1.0; } mat4_rotate_x :: (m: [*]f32, angle: f32) { mat4_zero(m); c := cos(angle); s := sin(angle); m[0] = 1.0; m[5] = c; m[6] = s; m[9] = 0.0 - s; m[10] = c; m[15] = 1.0; } mat4_translate :: (m: [*]f32, tx: f32, ty: f32, tz: f32) { mat4_identity(m); m[12] = tx; m[13] = ty; m[14] = tz; } // ---- Shader helpers ---- compile_shader :: (shader_type: u32, source: [:0]u8) -> u32 { shader : u32 = glCreateShader(shader_type); src_ptr : *void = xx &source[0]; glShaderSource(shader, 1, xx &src_ptr, null); glCompileShader(shader); status : s32 = 0; glGetShaderiv(shader, GL_COMPILE_STATUS, xx &status); if status == GL_FALSE { log_buf : [512]u8 = ---; glGetShaderInfoLog(shader, 512, null, xx &log_buf[0]); print("Shader compile error\n"); } shader; } create_program :: (vert_src: [:0]u8, frag_src: [:0]u8) -> u32 { vs : u32 = compile_shader(GL_VERTEX_SHADER, vert_src); fs : u32 = compile_shader(GL_FRAGMENT_SHADER, frag_src); prog : u32 = glCreateProgram(); glAttachShader(prog, vs); glAttachShader(prog, fs); glLinkProgram(prog); status : s32 = 0; glGetProgramiv(prog, GL_LINK_STATUS, xx &status); if status == GL_FALSE { log_buf : [512]u8 = ---; glGetProgramInfoLog(prog, 512, null, xx &log_buf[0]); print("Program link error\n"); } glDeleteShader(vs); glDeleteShader(fs); prog; } // ---- 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", 800, 600, 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); // Shaders vert_src : [:0]u8 = #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_src : [:0]u8 = #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; program : u32 = create_program(vert_src, frag_src); glUseProgram(program); mvp_loc : s32 = glGetUniformLocation(program, "uMVP"); light_loc : s32 = glGetUniformLocation(program, "uLightDir"); wire_loc : s32 = glGetUniformLocation(program, "uWire"); // Cube vertices: position(3) + normal(3), 36 vertices = 216 floats vertices : [216]f32 = .[ // Front face (z = +0.5) -0.5, -0.5, 0.5, 0.0, 0.0, 1.0, 0.5, -0.5, 0.5, 0.0, 0.0, 1.0, 0.5, 0.5, 0.5, 0.0, 0.0, 1.0, -0.5, -0.5, 0.5, 0.0, 0.0, 1.0, 0.5, 0.5, 0.5, 0.0, 0.0, 1.0, -0.5, 0.5, 0.5, 0.0, 0.0, 1.0, // Back face (z = -0.5) 0.5, -0.5, -0.5, 0.0, 0.0, -1.0, -0.5, -0.5, -0.5, 0.0, 0.0, -1.0, -0.5, 0.5, -0.5, 0.0, 0.0, -1.0, 0.5, -0.5, -0.5, 0.0, 0.0, -1.0, -0.5, 0.5, -0.5, 0.0, 0.0, -1.0, 0.5, 0.5, -0.5, 0.0, 0.0, -1.0, // Top face (y = +0.5) -0.5, 0.5, 0.5, 0.0, 1.0, 0.0, 0.5, 0.5, 0.5, 0.0, 1.0, 0.0, 0.5, 0.5, -0.5, 0.0, 1.0, 0.0, -0.5, 0.5, 0.5, 0.0, 1.0, 0.0, 0.5, 0.5, -0.5, 0.0, 1.0, 0.0, -0.5, 0.5, -0.5, 0.0, 1.0, 0.0, // Bottom face (y = -0.5) -0.5, -0.5, -0.5, 0.0, -1.0, 0.0, 0.5, -0.5, -0.5, 0.0, -1.0, 0.0, 0.5, -0.5, 0.5, 0.0, -1.0, 0.0, -0.5, -0.5, -0.5, 0.0, -1.0, 0.0, 0.5, -0.5, 0.5, 0.0, -1.0, 0.0, -0.5, -0.5, 0.5, 0.0, -1.0, 0.0, // Right face (x = +0.5) 0.5, -0.5, 0.5, 1.0, 0.0, 0.0, 0.5, -0.5, -0.5, 1.0, 0.0, 0.0, 0.5, 0.5, -0.5, 1.0, 0.0, 0.0, 0.5, -0.5, 0.5, 1.0, 0.0, 0.0, 0.5, 0.5, -0.5, 1.0, 0.0, 0.0, 0.5, 0.5, 0.5, 1.0, 0.0, 0.0, // Left face (x = -0.5) -0.5, -0.5, -0.5, -1.0, 0.0, 0.0, -0.5, -0.5, 0.5, -1.0, 0.0, 0.0, -0.5, 0.5, 0.5, -1.0, 0.0, 0.0, -0.5, -0.5, -0.5, -1.0, 0.0, 0.0, -0.5, 0.5, 0.5, -1.0, 0.0, 0.0, -0.5, 0.5, -0.5, -1.0, 0.0, 0.0 ]; // Create VAO and VBO vao : u32 = 0; vbo : u32 = 0; glGenVertexArrays(1, xx &vao); glGenBuffers(1, xx &vbo); glBindVertexArray(vao); glBindBuffer(GL_ARRAY_BUFFER, vbo); glBufferData(GL_ARRAY_BUFFER, 864, xx &vertices[0], GL_STATIC_DRAW); // Position attribute (location 0): 3 floats, stride 24 bytes, offset 0 glVertexAttribPointer(0, 3, GL_FLOAT, 0, 24, xx 0); glEnableVertexAttribArray(0); // Normal attribute (location 1): 3 floats, stride 24 bytes, offset 12 glVertexAttribPointer(1, 3, GL_FLOAT, 0, 24, xx 12); glEnableVertexAttribArray(1); // Set light direction glUniform3f(light_loc, 0.5, 0.7, 1.0); glUniform1f(wire_loc, 0.0); // Render loop running := true; event : [128]u8 = ---; while running { while SDL_PollEvent(xx &event[0]) { etype : u32 = xx event[0]; if etype == SDL_EVENT_QUIT { running = false; } } // Compute rotation angle from time ticks := SDL_GetTicks(); ms : f32 = xx ticks; angle := ms * 0.001; // Build matrices proj : [16]f32 = ---; mat4_perspective(xx &proj[0], PI / 4.0, 800.0 / 600.0, 0.1, 100.0); view : [16]f32 = ---; mat4_translate(xx &view[0], 0.0, 0.0, -3.0); rot_y : [16]f32 = ---; mat4_rotate_y(xx &rot_y[0], angle); rot_x : [16]f32 = ---; mat4_rotate_x(xx &rot_x[0], angle * 0.7); // model = rot_y * rot_x model : [16]f32 = ---; mat4_multiply(xx &model[0], xx &rot_y[0], xx &rot_x[0]); // view_model = view * model vm : [16]f32 = ---; mat4_multiply(xx &vm[0], xx &view[0], xx &model[0]); // mvp = proj * view_model mvp : [16]f32 = ---; mat4_multiply(xx &mvp[0], xx &proj[0], xx &vm[0]); glUniformMatrix4fv(mvp_loc, 1, 0, xx &mvp[0]); 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(); }