417 lines
11 KiB
Odin
417 lines
11 KiB
Odin
package main
|
|
|
|
import "base:runtime"
|
|
import "base:intrinsics"
|
|
|
|
import "core:log"
|
|
import "core:mem"
|
|
import "core:time"
|
|
import "core:fmt"
|
|
import "core:testing"
|
|
import "core:strings"
|
|
import "core:path/filepath"
|
|
import "core:math"
|
|
import "core:math/linalg"
|
|
import "core:os"
|
|
|
|
import stbi "vendor:stb/image"
|
|
|
|
import sa "sokol/app"
|
|
import sh "sokol/helpers"
|
|
import sg "sokol/gfx"
|
|
|
|
import "ecs"
|
|
|
|
Vec2 :: [2]f32
|
|
Vec3 :: [3]f32
|
|
Vec4 :: [4]f32
|
|
|
|
Mat4 :: matrix[4, 4]f32
|
|
|
|
VertexData :: struct {
|
|
pos: Vec3,
|
|
col: sg.Color,
|
|
uv: Vec2,
|
|
}
|
|
|
|
Scene :: struct {
|
|
physics_system: ^ecs.PhysicsSystem,
|
|
entities: []ecs.EntityID,
|
|
}
|
|
|
|
Globals :: struct {
|
|
shader: sg.Shader,
|
|
pipeline: sg.Pipeline,
|
|
vertex_buffer: sg.Buffer,
|
|
index_buffer: sg.Buffer,
|
|
image: sg.Image,
|
|
sampler: sg.Sampler,
|
|
rotation: f32,
|
|
|
|
camera: struct {
|
|
pos: Vec3,
|
|
target: Vec3,
|
|
look: Vec2,
|
|
},
|
|
|
|
coordinator: ecs.Coordinator,
|
|
scene: Scene,
|
|
dt: f32,
|
|
}
|
|
g: ^Globals
|
|
|
|
mouse_move: Vec2
|
|
key_down: #sparse[sa.Keycode]bool
|
|
|
|
default_context: runtime.Context
|
|
|
|
main :: proc() {
|
|
context.logger = log.create_console_logger()
|
|
|
|
tracking_allocator: mem.Tracking_Allocator
|
|
mem.tracking_allocator_init(&tracking_allocator, context.allocator)
|
|
context.allocator = mem.tracking_allocator(&tracking_allocator)
|
|
defer reset_tracking_allocator(&tracking_allocator)
|
|
|
|
default_context = context
|
|
|
|
sa.run({
|
|
window_title = "Ecs Test",
|
|
|
|
allocator = sa.Allocator(sh.allocator(&default_context)),
|
|
logger = sa.Logger(sh.logger(&default_context)),
|
|
|
|
init_cb = init_cb,
|
|
frame_cb = frame_cb,
|
|
cleanup_cb = cleanup_cb,
|
|
event_cb = event_cb,
|
|
})
|
|
|
|
g.coordinator = ecs.coordinator_create()
|
|
defer ecs.coordinator_destroy(&g.coordinator)
|
|
|
|
g.scene = create_scene(&g.coordinator)
|
|
defer delete(g.scene.entities)
|
|
}
|
|
|
|
init_cb :: proc "c" () {
|
|
context = default_context
|
|
|
|
sg.setup({
|
|
environment = sh.glue_environment(),
|
|
allocator = sg.Allocator(sh.allocator(&default_context)),
|
|
logger = sg.Logger(sh.logger(&default_context)),
|
|
})
|
|
sa.show_mouse(false)
|
|
sa.lock_mouse(true)
|
|
|
|
g = new(Globals)
|
|
|
|
g.camera = {
|
|
pos = { 0, 0, 2 },
|
|
target = { 0, 0, 1 },
|
|
}
|
|
|
|
g.shader = sg.make_shader(main_shader_desc(sg.query_backend()))
|
|
g.pipeline = sg.make_pipeline({
|
|
shader = g.shader,
|
|
layout = {
|
|
attrs = {
|
|
ATTR_main_pos = { format = .FLOAT3 },
|
|
ATTR_main_col = { format = .FLOAT4 },
|
|
ATTR_main_uv = { format = .FLOAT2 },
|
|
},
|
|
},
|
|
index_type = .UINT16,
|
|
cull_mode = .BACK,
|
|
depth = {
|
|
pixel_format = .DEFAULT,
|
|
write_enabled = true,
|
|
bias = 0.001,
|
|
bias_clamp = 0.0,
|
|
bias_slope_scale = 1.0,
|
|
compare = .LESS_EQUAL,
|
|
},
|
|
})
|
|
|
|
vertices := []VertexData {
|
|
{ pos = { -0.5, -0.5, 0.5 }, col = { 1, 1, 1, 1 }, uv = { 0, 0 } },
|
|
{ pos = { 0.5, -0.5, 0.5 }, col = { 1, 1, 1, 1 }, uv = { 1, 0 } },
|
|
{ pos = { 0.5, 0.5, 0.5 }, col = { 1, 1, 1, 1 }, uv = { 1, 1 } },
|
|
{ pos = { -0.5, 0.5, 0.5 }, col = { 1, 1, 1, 1 }, uv = { 0, 1 } },
|
|
|
|
{ pos = { -0.5, -0.5, -0.5 }, col = { 1, 1, 1, 1 }, uv = { 1, 0 } },
|
|
{ pos = { 0.5, -0.5, -0.5 }, col = { 1, 1, 1, 1 }, uv = { 0, 0 } },
|
|
{ pos = { 0.5, 0.5, -0.5 }, col = { 1, 1, 1, 1 }, uv = { 0, 1 } },
|
|
{ pos = { -0.5, 0.5, -0.5 }, col = { 1, 1, 1, 1 }, uv = { 1, 1 } },
|
|
|
|
{ pos = { -0.5, 0.5, 0.5 }, col = { 1, 1, 1, 1 }, uv = { 0, 0 } },
|
|
{ pos = { 0.5, 0.5, 0.5 }, col = { 1, 1, 1, 1 }, uv = { 1, 0 } },
|
|
{ pos = { 0.5, 0.5, -0.5 }, col = { 1, 1, 1, 1 }, uv = { 1, 1 } },
|
|
{ pos = { -0.5, 0.5, -0.5 }, col = { 1, 1, 1, 1 }, uv = { 0, 1 } },
|
|
|
|
{ pos = { -0.5, -0.5, 0.5 }, col = { 1, 1, 1, 1 }, uv = { 0, 0 } },
|
|
{ pos = { 0.5, -0.5, 0.5 }, col = { 1, 1, 1, 1 }, uv = { 1, 0 } },
|
|
{ pos = { 0.5, -0.5, -0.5 }, col = { 1, 1, 1, 1 }, uv = { 1, 1 } },
|
|
{ pos = { -0.5, -0.5, -0.5 }, col = { 1, 1, 1, 1 }, uv = { 0, 1 } },
|
|
|
|
{ pos = { 0.5, -0.5, 0.5 }, col = { 1, 1, 1, 1 }, uv = { 0, 0 } },
|
|
{ pos = { 0.5, -0.5, -0.5 }, col = { 1, 1, 1, 1 }, uv = { 1, 0 } },
|
|
{ pos = { 0.5, 0.5, -0.5 }, col = { 1, 1, 1, 1 }, uv = { 1, 1 } },
|
|
{ pos = { 0.5, 0.5, 0.5 }, col = { 1, 1, 1, 1 }, uv = { 0, 1 } },
|
|
|
|
{ pos = { -0.5, -0.5, 0.5 }, col = { 1, 1, 1, 1 }, uv = { 1, 0 } },
|
|
{ pos = { -0.5, -0.5, -0.5 }, col = { 1, 1, 1, 1 }, uv = { 0, 0 } },
|
|
{ pos = { -0.5, 0.5, -0.5 }, col = { 1, 1, 1, 1 }, uv = { 0, 1 } },
|
|
{ pos = { -0.5, 0.5, 0.5 }, col = { 1, 1, 1, 1 }, uv = { 1, 1 } },
|
|
}
|
|
g.vertex_buffer = sg.make_buffer({
|
|
data = sg_range(vertices)
|
|
})
|
|
|
|
indices := []u16 {
|
|
1, 0, 2,
|
|
3, 2, 0,
|
|
7, 4, 6,
|
|
5, 6, 4,
|
|
9, 8, 10,
|
|
11, 10, 8,
|
|
15, 12, 14,
|
|
13, 14, 12,
|
|
17, 16, 18,
|
|
19, 18, 16,
|
|
23, 20, 22,
|
|
21, 22, 20,
|
|
}
|
|
|
|
g.index_buffer = sg.make_buffer({
|
|
usage = { index_buffer = true },
|
|
data = sg_range(indices),
|
|
})
|
|
|
|
w, h: i32
|
|
pixels := stbi.load("res/texture_blue.png", &w, &h, nil, 4)
|
|
assert(pixels != nil)
|
|
defer(stbi.image_free(pixels))
|
|
|
|
g.image = sg.make_image({
|
|
width = w,
|
|
height = h,
|
|
pixel_format = .RGBA8,
|
|
data = {
|
|
subimage = {
|
|
0 = {
|
|
0 = {
|
|
ptr = pixels,
|
|
size = uint(w * h * 4)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
})
|
|
|
|
g.sampler = sg.make_sampler({})
|
|
}
|
|
|
|
frame_cb:: proc "c" () {
|
|
context = default_context
|
|
|
|
if key_down[.ESCAPE] {
|
|
sa.quit()
|
|
return
|
|
}
|
|
|
|
start_time := time.now()
|
|
//ecs.physics_system_update(g.scene.physics_system, &g.coordinator, g.dt) // seg fault
|
|
|
|
g.dt = f32(sa.frame_duration())
|
|
|
|
update_camera(g.dt)
|
|
|
|
g.rotation += linalg.to_radians(50 * g.dt)
|
|
|
|
proj := linalg.matrix4_perspective_f32(70, sa.widthf() / sa.heightf(), 0.001, 1000)
|
|
view := linalg.matrix4_look_at_f32(g.camera.pos, g.camera.target, { 0, 1, 0 } )
|
|
|
|
Object :: struct {
|
|
pos: Vec3,
|
|
rot: Vec3,
|
|
}
|
|
objects := []Object {
|
|
{ { -2, 0, 0 }, { 0, 0, 0 } },
|
|
{ { 0, 0, 0 }, { 0, 0, 0 } },
|
|
{ { 2, 0, 0 }, { 0, 0, 0 } },
|
|
}
|
|
|
|
sg.begin_pass({ swapchain = sh.glue_swapchain() })
|
|
|
|
sg.apply_pipeline(g.pipeline)
|
|
sg.apply_bindings({
|
|
vertex_buffers = { 0 = g.vertex_buffer },
|
|
index_buffer = g.index_buffer,
|
|
images = { IMG_tex = g.image },
|
|
samplers = { SMP_smp = g.sampler },
|
|
})
|
|
|
|
for obj in objects {
|
|
model := linalg.matrix4_translate_f32(obj.pos) * linalg.matrix4_from_yaw_pitch_roll_f32(obj.rot.y, obj.rot.x, obj.rot.z)
|
|
|
|
sg.apply_uniforms(UB_VsParams, sg_range(&Vsparams {
|
|
mvp = proj * view * model
|
|
}))
|
|
|
|
sg.draw(0, 36, 1)
|
|
}
|
|
|
|
|
|
sg.end_pass()
|
|
sg.commit()
|
|
|
|
mouse_move = {}
|
|
}
|
|
|
|
update_camera :: proc(dt: f32) {
|
|
MOVE_SPEED :: 3
|
|
LOOK_SENSITIVITY :: 0.15
|
|
|
|
move_input: Vec3
|
|
if key_down[.W] do move_input.y = 1
|
|
else if key_down[.S] do move_input.y = -1
|
|
|
|
if key_down[.D] do move_input.x = 1
|
|
else if key_down[.A] do move_input.x = -1
|
|
|
|
if key_down[.SPACE] do move_input.z = 1
|
|
else if key_down[.LEFT_SHIFT] do move_input.z = -1
|
|
|
|
look_input: Vec2 = -mouse_move * LOOK_SENSITIVITY
|
|
g.camera.look += look_input
|
|
g.camera.look.x = math.wrap(g.camera.look.x, 360)
|
|
g.camera.look.y = math.clamp(g.camera.look.y, -89.5, 89.5)
|
|
|
|
look_mat := linalg.matrix4_from_yaw_pitch_roll_f32(
|
|
linalg.to_radians(g.camera.look.x),
|
|
linalg.to_radians(g.camera.look.y),
|
|
0,
|
|
)
|
|
|
|
forward := (look_mat * Vec4{0, 0, -1, 1}).xyz
|
|
right := (look_mat * Vec4{1, 0, 0, 1}).xyz
|
|
up := (look_mat * Vec4{0, 1, 0, 1}).xyz
|
|
|
|
move_dir := forward * move_input.y + right * move_input.x + up * move_input.z
|
|
motion := linalg.normalize0(move_dir) * MOVE_SPEED * dt
|
|
g.camera.pos += motion
|
|
|
|
g.camera.target = g.camera.pos + forward
|
|
}
|
|
|
|
cleanup_cb :: proc "c" () {
|
|
context = default_context
|
|
|
|
sg.destroy_buffer(g.index_buffer)
|
|
sg.destroy_buffer(g.vertex_buffer)
|
|
sg.destroy_image(g.image)
|
|
sg.destroy_sampler(g.sampler)
|
|
sg.destroy_pipeline(g.pipeline)
|
|
sg.destroy_shader(g.shader)
|
|
|
|
free(g)
|
|
sg.shutdown()
|
|
}
|
|
|
|
event_cb :: proc "c" (event: ^sa.Event) {
|
|
context = default_context
|
|
|
|
#partial switch event.type {
|
|
case .MOUSE_MOVE:
|
|
mouse_move += {event.mouse_dx, event.mouse_dy}
|
|
case .KEY_DOWN:
|
|
key_down[event.key_code] = true
|
|
case .KEY_UP:
|
|
key_down[event.key_code] = false
|
|
}
|
|
}
|
|
|
|
create_scene :: proc(coordinator: ^ecs.Coordinator) -> Scene {
|
|
scene := Scene{}
|
|
|
|
ecs.coordinator_register_component(ecs.Gravity, coordinator)
|
|
ecs.coordinator_register_component(ecs.RigidBody, coordinator)
|
|
ecs.coordinator_register_component(ecs.Transform, coordinator)
|
|
|
|
scene.physics_system = ecs.coordinator_register_system(ecs.PhysicsSystem, coordinator)
|
|
|
|
signature := ecs.signature_create()
|
|
ecs.signature_set(&signature, ecs.coordinator_get_component_type(ecs.Gravity, coordinator))
|
|
ecs.signature_set(&signature, ecs.coordinator_get_component_type(ecs.RigidBody, coordinator))
|
|
ecs.signature_set(&signature, ecs.coordinator_get_component_type(ecs.Transform, coordinator))
|
|
ecs.coordinator_set_system_signature(ecs.PhysicsSystem, coordinator, signature)
|
|
|
|
scene.entities = make([]ecs.EntityID, ecs.ENTITY_MAX)
|
|
for &entity in scene.entities {
|
|
entity = ecs.coordinator_create_entity(coordinator)
|
|
|
|
ecs.coordinator_add_component(
|
|
ecs.Gravity,
|
|
coordinator,
|
|
entity,
|
|
ecs.Gravity{
|
|
ecs.Vec3{0.0, -9.82, 0.0}
|
|
})
|
|
ecs.coordinator_add_component(
|
|
ecs.RigidBody,
|
|
coordinator,
|
|
entity,
|
|
ecs.RigidBody{
|
|
velocity = ecs.Vec3{0.0, 0.0, 0.0},
|
|
acceleration = ecs.Vec3{0.0, 0.0, 0.0},
|
|
})
|
|
ecs.coordinator_add_component(
|
|
ecs.Transform,
|
|
coordinator,
|
|
entity,
|
|
ecs.Transform{
|
|
position = ecs.Vec3{0.0, 0.0, 0.0},
|
|
rotation = ecs.Vec3{0.0, 0.0, 0.0},
|
|
scale = ecs.Vec3{1.0, 1.0, 1.0},
|
|
})
|
|
}
|
|
|
|
return scene
|
|
}
|
|
|
|
sg_range :: proc {
|
|
sg_range_from_slice,
|
|
sg_range_from_struct,
|
|
}
|
|
|
|
sg_range_from_slice :: proc(s: []$T) -> sg.Range {
|
|
return {
|
|
ptr = raw_data(s),
|
|
size = len(s) * size_of(T),
|
|
}
|
|
}
|
|
|
|
sg_range_from_struct :: proc(s: ^$T) -> sg.Range where intrinsics.type_is_struct(T) {
|
|
return {
|
|
ptr = s,
|
|
size = size_of(T),
|
|
}
|
|
}
|
|
|
|
reset_tracking_allocator :: proc(track: ^mem.Tracking_Allocator) {
|
|
if len(track.allocation_map) > 0 {
|
|
fmt.eprintf("=== %v allocations not freed: ===\n", len(track.allocation_map))
|
|
for _, entry in track.allocation_map {
|
|
fmt.eprintf("- %v bytes @ %v\n", entry.size, entry.location)
|
|
}
|
|
}
|
|
if len(track.bad_free_array) > 0 {
|
|
fmt.eprintf("=== %v incorrect frees: ===\n", len(track.bad_free_array))
|
|
for entry in track.bad_free_array {
|
|
fmt.eprintf("- %p @ %v\n", entry.memory, entry.location)
|
|
}
|
|
}
|
|
mem.tracking_allocator_destroy(track)
|
|
}
|