ui pipeline
This commit is contained in:
261
main.sx
261
main.sx
@@ -2,118 +2,89 @@
|
||||
#import "modules/sdl3.sx";
|
||||
#import "modules/opengl.sx";
|
||||
#import "modules/math";
|
||||
stb :: #import "modules/stb.sx";
|
||||
#import "modules/stb.sx";
|
||||
#import "ui";
|
||||
|
||||
WIDTH :f32: 800;
|
||||
HEIGHT :f32: 600;
|
||||
|
||||
save_snapshot :: (path: [:0]u8, w: s32, h: s32) {
|
||||
stride : s64 = xx (w * 4);
|
||||
buf_size : s64 = stride * xx h;
|
||||
pixels : [*]u8 = xx context.allocator.alloc(buf_size);
|
||||
glReadPixels(0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
|
||||
|
||||
// Flip vertically (GL reads bottom-up, PNG expects top-down)
|
||||
row_buf : [*]u8 = xx context.allocator.alloc(stride);
|
||||
i : s32 = 0;
|
||||
while i < h / 2 {
|
||||
top : s64 = xx i * stride;
|
||||
bot : s64 = xx (h - 1 - i) * stride;
|
||||
memcpy(row_buf, @pixels[top], stride);
|
||||
memcpy(@pixels[top], @pixels[bot], stride);
|
||||
memcpy(@pixels[bot], row_buf, stride);
|
||||
i += 1;
|
||||
}
|
||||
|
||||
stbi_write_png(path, w, h, 4, pixels, xx stride);
|
||||
out("Saved ");
|
||||
out(path);
|
||||
out("\n");
|
||||
}
|
||||
|
||||
main :: () -> void {
|
||||
print("init video: \n");
|
||||
SDL_Init(SDL_INIT_VIDEO);
|
||||
|
||||
print("init opengl: \n");
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3);
|
||||
|
||||
print("init opengl profile: \n");
|
||||
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);
|
||||
|
||||
print("create window: \n");
|
||||
window := SDL_CreateWindow("SX Game", xx WIDTH, xx HEIGHT, SDL_WINDOW_OPENGL);
|
||||
window := SDL_CreateWindow("SX UI Demo", xx WIDTH, xx HEIGHT, SDL_WINDOW_OPENGL);
|
||||
gl_ctx := SDL_GL_CreateContext(window);
|
||||
SDL_GL_MakeCurrent(window, gl_ctx);
|
||||
SDL_GL_SetSwapInterval(1);
|
||||
|
||||
print("load gl: \n");
|
||||
load_gl(SDL_GL_GetProcAddress);
|
||||
load_gl_ui(SDL_GL_GetProcAddress);
|
||||
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
glDepthFunc(GL_LESS);
|
||||
|
||||
program := create_program(VERT_SHADER_SRC, FRAG_SHADER_SRC);
|
||||
glUseProgram(program);
|
||||
|
||||
print("uniform locations: \n");
|
||||
mvp_loc := glGetUniformLocation(program, "uMVP");
|
||||
light_loc := glGetUniformLocation(program, "uLightDir");
|
||||
wire_loc := glGetUniformLocation(program, "uWire");
|
||||
// --- Build UI ---
|
||||
pipeline : UIPipeline = ---;
|
||||
pipeline.init(WIDTH, HEIGHT);
|
||||
|
||||
print("vertices\n");
|
||||
// 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], .[ 0.0, 0.0, 1.0, 0.0],
|
||||
.[ 0.5, -0.5, 0.5, 1.0], .[ 0.0, 0.0, 1.0, 0.0],
|
||||
.[ 0.5, 0.5, 0.5, 1.0], .[ 0.0, 0.0, 1.0, 0.0],
|
||||
.[-0.5, -0.5, 0.5, 1.0], .[ 0.0, 0.0, 1.0, 0.0],
|
||||
.[ 0.5, 0.5, 0.5, 1.0], .[ 0.0, 0.0, 1.0, 0.0],
|
||||
.[-0.5, 0.5, 0.5, 1.0], .[ 0.0, 0.0, 1.0, 0.0],
|
||||
// Back face (z = -0.5)
|
||||
.[ 0.5, -0.5, -0.5, 1.0], .[ 0.0, 0.0, -1.0, 0.0],
|
||||
.[-0.5, -0.5, -0.5, 1.0], .[ 0.0, 0.0, -1.0, 0.0],
|
||||
.[-0.5, 0.5, -0.5, 1.0], .[ 0.0, 0.0, -1.0, 0.0],
|
||||
.[ 0.5, -0.5, -0.5, 1.0], .[ 0.0, 0.0, -1.0, 0.0],
|
||||
.[-0.5, 0.5, -0.5, 1.0], .[ 0.0, 0.0, -1.0, 0.0],
|
||||
.[ 0.5, 0.5, -0.5, 1.0], .[ 0.0, 0.0, -1.0, 0.0],
|
||||
// Top face (y = +0.5)
|
||||
.[-0.5, 0.5, 0.5, 1.0], .[ 0.0, 1.0, 0.0, 0.0],
|
||||
.[ 0.5, 0.5, 0.5, 1.0], .[ 0.0, 1.0, 0.0, 0.0],
|
||||
.[ 0.5, 0.5, -0.5, 1.0], .[ 0.0, 1.0, 0.0, 0.0],
|
||||
.[-0.5, 0.5, 0.5, 1.0], .[ 0.0, 1.0, 0.0, 0.0],
|
||||
.[ 0.5, 0.5, -0.5, 1.0], .[ 0.0, 1.0, 0.0, 0.0],
|
||||
.[-0.5, 0.5, -0.5, 1.0], .[ 0.0, 1.0, 0.0, 0.0],
|
||||
// Bottom face (y = -0.5)
|
||||
.[-0.5, -0.5, -0.5, 1.0], .[0.0, -1.0, 0.0, 0.0],
|
||||
.[ 0.5, -0.5, -0.5, 1.0], .[0.0, -1.0, 0.0, 0.0],
|
||||
.[ 0.5, -0.5, 0.5, 1.0], .[0.0, -1.0, 0.0, 0.0],
|
||||
.[-0.5, -0.5, -0.5, 1.0], .[0.0, -1.0, 0.0, 0.0],
|
||||
.[ 0.5, -0.5, 0.5, 1.0], .[0.0, -1.0, 0.0, 0.0],
|
||||
.[-0.5, -0.5, 0.5, 1.0], .[0.0, -1.0, 0.0, 0.0],
|
||||
// Right face (x = +0.5)
|
||||
.[ 0.5, -0.5, 0.5, 1.0], .[1.0, 0.0, 0.0, 0.0],
|
||||
.[ 0.5, -0.5, -0.5, 1.0], .[1.0, 0.0, 0.0, 0.0],
|
||||
.[ 0.5, 0.5, -0.5, 1.0], .[1.0, 0.0, 0.0, 0.0],
|
||||
.[ 0.5, -0.5, 0.5, 1.0], .[1.0, 0.0, 0.0, 0.0],
|
||||
.[ 0.5, 0.5, -0.5, 1.0], .[1.0, 0.0, 0.0, 0.0],
|
||||
.[ 0.5, 0.5, 0.5, 1.0], .[1.0, 0.0, 0.0, 0.0],
|
||||
// Left face (x = -0.5)
|
||||
.[-0.5, -0.5, -0.5, 1.0], .[-1.0, 0.0, 0.0, 0.0],
|
||||
.[-0.5, -0.5, 0.5, 1.0], .[-1.0, 0.0, 0.0, 0.0],
|
||||
.[-0.5, 0.5, 0.5, 1.0], .[-1.0, 0.0, 0.0, 0.0],
|
||||
.[-0.5, -0.5, -0.5, 1.0], .[-1.0, 0.0, 0.0, 0.0],
|
||||
.[-0.5, 0.5, 0.5, 1.0], .[-1.0, 0.0, 0.0, 0.0],
|
||||
.[-0.5, 0.5, -0.5, 1.0], .[-1.0, 0.0, 0.0, 0.0]
|
||||
];
|
||||
|
||||
print("cube buffer: \n");
|
||||
vao : u32 = 0;
|
||||
vbo : u32 = 0;
|
||||
glGenVertexArrays(1, @vao);
|
||||
glGenBuffers(1, @vbo);
|
||||
// Create a simple layout: VStack with colored rects and a button
|
||||
root := VStack.{ spacing = 10.0, alignment = .center };
|
||||
|
||||
glBindVertexArray(vao);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, vbo);
|
||||
glBufferData(GL_ARRAY_BUFFER, 1152, xx vertices, GL_STATIC_DRAW);
|
||||
header := RectView.{ color = COLOR_YELLOW, preferred_height = 80.0, corner_radius = 8.0 };
|
||||
root.add(xx header);
|
||||
|
||||
glVertexAttribPointer(0, 3, GL_FLOAT, 0, 32, xx 0);
|
||||
glEnableVertexAttribArray(0);
|
||||
btn := Button.{ label = "Click Me", font_size = 14.0, style = ButtonStyle.default(), on_tap = null };
|
||||
root.add(xx btn);
|
||||
|
||||
glVertexAttribPointer(1, 3, GL_FLOAT, 0, 32, xx 16);
|
||||
glEnableVertexAttribArray(1);
|
||||
body := HStack.{ spacing = 10.0, alignment = .center };
|
||||
|
||||
glUniform3f(light_loc, 0.5, 0.7, 1.0);
|
||||
glUniform1f(wire_loc, 0.0);
|
||||
left := RectView.{ color = COLOR_RED, preferred_width = 200.0, preferred_height = 300.0, corner_radius = 4.0 };
|
||||
body.add(xx left);
|
||||
|
||||
right := RectView.{ color = COLOR_GREEN, preferred_width = 200.0, preferred_height = 300.0, corner_radius = 4.0 };
|
||||
body.add(xx right);
|
||||
|
||||
root.add(xx body);
|
||||
|
||||
footer := RectView.{ color = COLOR_DARK_GRAY, preferred_height = 60.0 };
|
||||
root.add(xx footer);
|
||||
|
||||
pipeline.set_root(xx root);
|
||||
|
||||
// --- Main loop ---
|
||||
running := true;
|
||||
event : SDL_Event = .none;
|
||||
sdl_event : SDL_Event = .none;
|
||||
|
||||
print("loop: \n");
|
||||
while running {
|
||||
while SDL_PollEvent(event) {
|
||||
if event == {
|
||||
while SDL_PollEvent(sdl_event) {
|
||||
if sdl_event == {
|
||||
case .quit: running = false;
|
||||
case .key_up: (e) {
|
||||
if e.key == {
|
||||
@@ -121,123 +92,29 @@ main :: () -> void {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Forward to UI
|
||||
ui_event := translate_sdl_event(@sdl_event);
|
||||
if ui_event.type != .none {
|
||||
pipeline.dispatch_event(@ui_event);
|
||||
}
|
||||
}
|
||||
|
||||
ticks := SDL_GetTicks();
|
||||
ms : f32 = xx ticks;
|
||||
angle := ms * 0.001;
|
||||
glClearColor(0.12, 0.12, 0.15, 1.0);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
|
||||
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);
|
||||
|
||||
glUniform1f(wire_loc, 0.0);
|
||||
glBindVertexArray(vao);
|
||||
glDrawArrays(GL_TRIANGLES, 0, 36);
|
||||
|
||||
glDepthFunc(GL_LEQUAL);
|
||||
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
|
||||
glLineWidth(2.0);
|
||||
glUniform1f(wire_loc, 1.0);
|
||||
glDrawArrays(GL_TRIANGLES, 0, 36);
|
||||
|
||||
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
|
||||
glDepthFunc(GL_LESS);
|
||||
glUniform1f(wire_loc, 0.0);
|
||||
|
||||
print("{}\n", ms);
|
||||
pipeline.tick();
|
||||
|
||||
SDL_GL_SwapWindow(window);
|
||||
}
|
||||
|
||||
// Re-render one frame for snapshot (back buffer is stale after swap)
|
||||
glClearColor(0.12, 0.12, 0.15, 1.0);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
pipeline.tick();
|
||||
save_snapshot("goldens/last_frame.png", xx WIDTH, xx HEIGHT);
|
||||
|
||||
SDL_GL_DestroyContext(gl_ctx);
|
||||
SDL_DestroyWindow(window);
|
||||
SDL_Quit();
|
||||
}
|
||||
|
||||
create_program :: (vert_src: [:0]u8, frag_src: [:0]u8) -> u32 {
|
||||
vs := compile_shader(GL_VERTEX_SHADER, vert_src);
|
||||
fs := compile_shader(GL_FRAGMENT_SHADER, frag_src);
|
||||
|
||||
prog := glCreateProgram();
|
||||
glAttachShader(prog, vs);
|
||||
glAttachShader(prog, fs);
|
||||
glLinkProgram(prog);
|
||||
|
||||
status : s32 = 0;
|
||||
glGetProgramiv(prog, GL_LINK_STATUS, @status);
|
||||
if status == GL_FALSE {
|
||||
log_buf: [512]u8 = ---;
|
||||
glGetProgramInfoLog(prog, 512, null, log_buf);
|
||||
print("error program link\n");
|
||||
}
|
||||
|
||||
glDeleteShader(vs);
|
||||
glDeleteShader(fs);
|
||||
return prog;
|
||||
}
|
||||
|
||||
compile_shader :: (shader_type : u32, source: [:0]u8) -> u32 {
|
||||
shader := glCreateShader(shader_type);
|
||||
glShaderSource(shader, 1, source, null);
|
||||
glCompileShader(shader);
|
||||
|
||||
status : s32 = 0;
|
||||
glGetShaderiv(shader, GL_COMPILE_STATUS, @status);
|
||||
if status == GL_FALSE {
|
||||
log_buf : [512]u8 = ---;
|
||||
glGetShaderInfoLog(shader, 512, null, log_buf);
|
||||
print("error compile shader\n");
|
||||
}
|
||||
return shader;
|
||||
}
|
||||
|
||||
VERT_SHADER_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_SHADER_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;
|
||||
|
||||
Reference in New Issue
Block a user