331 lines
9.1 KiB
Plaintext
331 lines
9.1 KiB
Plaintext
#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();
|
|
}
|