Compare commits

..

No commits in common. "28f0d737b1c7e1b893ed7e3e336c6b94741e4ea4" and "cd40b79306c0b9f057a1c215fd0b9f4e9c5bbf61" have entirely different histories.

19 changed files with 493 additions and 3473 deletions

1
.gitignore vendored
View file

@ -3,4 +3,3 @@ bin/
*.dll
*.dylib
*.so
*.spv

View file

@ -1,211 +0,0 @@
package main
import sdl "vendor:sdl3"
import "core:math/linalg"
import "core:mem"
frag_shader_code := #load("shader.spv.frag")
vert_shader_code := #load("shader.spv.vert")
main :: proc() {
ok := sdl.Init({.VIDEO}); assert(ok)
sdl.SetLogPriorities(.VERBOSE)
window := sdl.CreateWindow("Pain and Suffering and Pleasure", 800, 500, nil)
device := sdl.CreateGPUDevice({.SPIRV}, true, nil)
ok = sdl.ClaimWindowForGPUDevice(device, window); assert(ok)
vert_shader := sdl.CreateGPUShader(device, {
code_size = len(vert_shader_code),
code = raw_data(vert_shader_code),
entrypoint = "main",
format = {.SPIRV},
stage = .VERTEX,
num_uniform_buffers = 1,
})
frag_shader := sdl.CreateGPUShader(device, {
code_size = len(frag_shader_code),
code = raw_data(frag_shader_code),
entrypoint = "main",
format = {.SPIRV},
stage = .FRAGMENT,
num_uniform_buffers = 0,
})
Vec3 :: [3]f32
Vertex_Data :: struct {
pos: Vec3,
color: sdl.FColor,
}
size : f32= 1
vertices := []Vertex_Data {
{ pos = {-size, size, 1}, color = {1,0,0,1} },
{pos = {size, size, 1}, color = {0,1,1,1}},
{pos={-size,-size, 1}, color = {1,0,1,1}},
{pos={size,-size, 1}, color = {1,0,1,1}},
}
vertices_byte_size := len(vertices) * size_of(vertices[0])
indicies := []u16 {
0,1,2,
2,1,3
}
indices_byte_size := len(indicies) * size_of(indicies[0])
vertex_buf := sdl.CreateGPUBuffer(device, {
usage = {.VERTEX},
size = u32(vertices_byte_size),
})
index_buf := sdl.CreateGPUBuffer(device, {
usage = {.INDEX},
size = u32(vertices_byte_size),
})
transfer_buf := sdl.CreateGPUTransferBuffer(device, {
usage = .UPLOAD,
size = u32(vertices_byte_size + indices_byte_size),
})
transfer_mem := transmute([^]byte)sdl.MapGPUTransferBuffer(device, transfer_buf, false)
mem.copy(transfer_mem, raw_data(vertices), vertices_byte_size)
mem.copy(transfer_mem[vertices_byte_size:], raw_data(indicies), indices_byte_size)
sdl.UnmapGPUTransferBuffer(device, transfer_buf)
copy_buffer := sdl.AcquireGPUCommandBuffer(device)
copy_pass := sdl.BeginGPUCopyPass(copy_buffer)
sdl.UploadToGPUBuffer(copy_pass, {
transfer_buffer = transfer_buf,
}, {
buffer = vertex_buf,
size = u32(vertices_byte_size),
}, false)
sdl.UploadToGPUBuffer(copy_pass, {
transfer_buffer = transfer_buf, offset = u32(vertices_byte_size)
}, {
buffer = index_buf,
size = u32(indices_byte_size)
}, false)
sdl.EndGPUCopyPass(copy_pass)
ok = sdl.SubmitGPUCommandBuffer(copy_buffer); assert(ok)
sdl.ReleaseGPUTransferBuffer(device, transfer_buf)
vertex_attrs := []sdl.GPUVertexAttribute {
{
location = 0,
format = .FLOAT3,
offset = u32(offset_of(Vertex_Data, pos)),
},
{
location = 1,
format = .FLOAT4,
offset = u32(offset_of(Vertex_Data, color)),
}
}
pipeline := sdl.CreateGPUGraphicsPipeline(device, {
vertex_shader = vert_shader,
fragment_shader = frag_shader,
primitive_type = .TRIANGLELIST,
vertex_input_state = {
num_vertex_buffers = 1,
vertex_buffer_descriptions = &(sdl.GPUVertexBufferDescription{
slot = 0,
pitch = size_of(Vertex_Data),
}),
num_vertex_attributes = u32(len(vertex_attrs)),
vertex_attributes = raw_data(vertex_attrs),
},
target_info = {
num_color_targets = 1,
color_target_descriptions = &(sdl.GPUColorTargetDescription{
format = sdl.GetGPUSwapchainTextureFormat(device, window)
})
}
})
defer sdl.ReleaseGPUShader(device, frag_shader)
defer sdl.ReleaseGPUShader(device, vert_shader)
win_size: [2]i32
ok = sdl.GetWindowSize(window, &win_size.x, &win_size.y); assert(ok)
rotation_speed := linalg.to_radians(f32(90))
rotation := f32(0.62)
proj := linalg.matrix4_perspective_f32(linalg.to_radians(f32(70)), f32(win_size.x) / f32(win_size.y), 0.0001, 1000)
UBO :: struct {
mvp: matrix[4,4]f32
}
last_ticks := sdl.GetTicks()
for {
new_ticks := sdl.GetTicks()
delta_time := f32(new_ticks - last_ticks) / 1000
last_ticks = new_ticks
event: sdl.Event
for sdl.PollEvent(&event) {
#partial switch(event.type) {
case .QUIT:
return
case .KEY_DOWN:
#partial switch(event.key.scancode) {
case .ESCAPE:
return
}
}
}
buffer := sdl.AcquireGPUCommandBuffer(device)
texture: ^sdl.GPUTexture
ok = sdl.WaitAndAcquireGPUSwapchainTexture(buffer, window, &texture, nil, nil); assert(ok)
rotation += rotation_speed * delta_time
model_mat := linalg.matrix4_translate_f32({0,0,-5}) * linalg.matrix4_rotate_f32(rotation, {0,1,0})
ubo := UBO {mvp = proj * model_mat}
if texture != nil {
color_target := sdl.GPUColorTargetInfo{
texture = texture,
load_op = .CLEAR,
clear_color = {0,0.2,0.4,1},
store_op = .STORE,
}
render_pass := sdl.BeginGPURenderPass(buffer, &color_target, 1, nil)
sdl.BindGPUGraphicsPipeline(render_pass, pipeline)
sdl.BindGPUVertexBuffers(render_pass, 0, &(sdl.GPUBufferBinding{
buffer = vertex_buf,
}),1)
sdl.BindGPUIndexBuffer(render_pass, {buffer = index_buf}, ._16BIT)
sdl.PushGPUVertexUniformData(buffer, 0, &ubo, size_of(ubo))
sdl.DrawGPUIndexedPrimitives(render_pass, 6, 1, 0, 0 ,0)
sdl.EndGPURenderPass(render_pass)
}
ok = sdl.SubmitGPUCommandBuffer(buffer); assert(ok)
}
}

View file

@ -1,4 +0,0 @@
glslc ./idk/shader.glsl.vert -o idk/shader.spv.vert
glslc ./idk/shader.glsl.frag -o idk/shader.spv.frag
odin run idk/

View file

@ -1,9 +0,0 @@
#version 460
layout(location=0) out vec4 frag_color;
layout(location=0) in vec4 color;
void main() {
frag_color = color;
}

View file

@ -1,16 +0,0 @@
#version 460
layout(set=1, binding=0) uniform UBO {
mat4 mvp;
};
layout(location=0) in vec3 position;
layout(location=1) in vec4 color;
layout(location=0) out vec4 out_color;
void main() {
gl_Position = mvp * vec4(position, 1);
out_color = color;
}

Binary file not shown.

Binary file not shown.

View file

@ -1,768 +0,0 @@
package main
import "core:c"
import "core:fmt"
import "core:math"
import "core:mem"
import "core:os"
import "core:time"
import "core:path/filepath"
import "core:strings"
import "core:math/linalg"
import sdl "vendor:sdl3"
modelIndex := 0
Vec3 :: [3]f32
Mat4 :: [16]f32 // column-major
Camera :: struct {
center: Vec3,
distance: f32,
orientation: Rotor,
fov_deg: f32,
}
Debug_State :: struct {
enabled: bool,
accum: f32,
}
Rotor :: struct {
s, x, y, z: f32,
}
Cube_Instance :: struct {
pos: Vec3,
scale: f32,
color: Vec3,
}
Vertex :: struct {
pos: Vec3,
}
Push_Constants :: struct {
mvp: matrix[4,4]f32,
color: [4]f32,
}
mat4_identity :: proc() -> Mat4 {
return Mat4{1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1}
}
mat4_mul :: proc(a, b: Mat4) -> Mat4 {
r := Mat4{}
for c in 0 ..< 4 {
for row in 0 ..< 4 {
r[c*4+row] =
a[0*4+row] * b[c*4+0] +
a[1*4+row] * b[c*4+1] +
a[2*4+row] * b[c*4+2] +
a[3*4+row] * b[c*4+3]
}
}
return r
}
vec3_sub :: proc(a, b: Vec3) -> Vec3 {
return Vec3{a[0] - b[0], a[1] - b[1], a[2] - b[2]}
}
vec3_add :: proc(a, b: Vec3) -> Vec3 {
return Vec3{a[0] + b[0], a[1] + b[1], a[2] + b[2]}
}
vec3_scale :: proc(v: Vec3, s: f32) -> Vec3 {
return Vec3{v[0] * s, v[1] * s, v[2] * s}
}
vec3_dot :: proc(a, b: Vec3) -> f32 {
return a[0] * b[0] + a[1] * b[1] + a[2] * b[2]
}
vec3_cross :: proc(a, b: Vec3) -> Vec3 {
return Vec3{
a[1] * b[2] - a[2] * b[1],
a[2] * b[0] - a[0] * b[2],
a[0] * b[1] - a[1] * b[0],
}
}
vec3_normalize :: proc(v: Vec3) -> Vec3 {
len2 := vec3_dot(v, v)
if len2 <= 0.000001 {
return Vec3{0, 0, 0}
}
inv := 1.0 / math.sqrt(len2)
return vec3_scale(v, inv)
}
rotor_identity :: proc() -> Rotor {
return Rotor{1, 0, 0, 0}
}
rotor_normalize :: proc(r: Rotor) -> Rotor {
len2 := r.s*r.s + r.x*r.x + r.y*r.y + r.z*r.z
if len2 <= 0.000001 {
return rotor_identity()
}
inv := 1.0 / math.sqrt(len2)
return Rotor{r.s * inv, r.x * inv, r.y * inv, r.z * inv}
}
rotor_mul :: proc(a, b: Rotor) -> Rotor {
return Rotor{
a.s*b.s - a.x*b.x - a.y*b.y - a.z*b.z,
a.s*b.x + a.x*b.s + a.y*b.z - a.z*b.y,
a.s*b.y - a.x*b.z + a.y*b.s + a.z*b.x,
a.s*b.z + a.x*b.y - a.y*b.x + a.z*b.s,
}
}
rotor_from_axis_angle :: proc(axis: Vec3, angle: f32) -> Rotor {
a := vec3_normalize(axis)
h := angle * 0.5
c := math.cos(h)
s := math.sin(h)
return Rotor{c, a[0] * s, a[1] * s, a[2] * s}
}
rotate_vec3 :: proc(r: Rotor, v: Vec3) -> Vec3 {
u := Vec3{r.x, r.y, r.z}
t := vec3_scale(vec3_cross(u, v), 2.0)
return vec3_add(v, vec3_add(vec3_scale(t, r.s), vec3_cross(u, t)))
}
mat4_translate :: proc(p: Vec3) -> Mat4 {
m := mat4_identity()
m[12] = p[0]
m[13] = p[1]
m[14] = p[2]
return m
}
mat4_scale_uniform :: proc(s: f32) -> Mat4 {
m := mat4_identity()
m[0] = s
m[5] = s
m[10] = s
return m
}
mat4_perspective :: proc(fov_deg, aspect, z_near, z_far: f32) -> Mat4 {
f := 1.0 / math.tan((fov_deg * 0.5) * (math.PI / 180.0))
m := Mat4{}
m[0] = f / aspect
m[5] = f
m[10] = (z_far + z_near) / (z_near - z_far)
m[11] = -1
m[14] = (2.0 * z_far * z_near) / (z_near - z_far)
return m
}
mat4_look_at :: proc(eye, target, up: Vec3) -> Mat4 {
fwd := vec3_normalize(vec3_sub(target, eye))
right := vec3_normalize(vec3_cross(fwd, up))
real_up := vec3_cross(right, fwd)
m := mat4_identity()
m[0] = right[0]
m[1] = real_up[0]
m[2] = -fwd[0]
m[4] = right[1]
m[5] = real_up[1]
m[6] = -fwd[1]
m[8] = right[2]
m[9] = real_up[2]
m[10] = -fwd[2]
m[12] = -vec3_dot(right, eye)
m[13] = -vec3_dot(real_up, eye)
m[14] = vec3_dot(fwd, eye)
return m
}
camera_forward :: proc(c: Camera) -> Vec3 {
return vec3_normalize(rotate_vec3(c.orientation, Vec3{0, 0, -1}))
}
camera_right :: proc(c: Camera) -> Vec3 {
return vec3_normalize(rotate_vec3(c.orientation, Vec3{1, 0, 0}))
}
camera_up :: proc(c: Camera) -> Vec3 {
return vec3_normalize(rotate_vec3(c.orientation, Vec3{0, 1, 0}))
}
camera_position :: proc(c: Camera) -> Vec3 {
return vec3_sub(c.center, vec3_scale(camera_forward(c), c.distance))
}
read_file_or_fail :: proc(path: string) -> []u8 {
file_bytes, err := os.read_entire_file(path, context.allocator)
if err != nil {
fmt.println("failed to read file:", path, "error:", err)
return nil
}
return file_bytes
}
create_gpu_shader :: proc(device: ^sdl.GPUDevice, path: string, stage: sdl.GPUShaderStage) -> ^sdl.GPUShader {
bytes := read_file_or_fail(path)
if len(bytes) == 0 {
return nil
}
ci := sdl.GPUShaderCreateInfo{
code_size = uint(len(bytes)),
code = raw_data(bytes),
entrypoint = cstring("main"),
format = sdl.GPUShaderFormat{.SPIRV},
stage = stage,
num_samplers = 0,
num_storage_textures = 0,
num_storage_buffers = 0,
num_uniform_buffers = 1,
props = 0,
}
shader := sdl.CreateGPUShader(device, ci)
delete(bytes)
if shader == nil {
fmt.println("CreateGPUShader failed:", path, "error:", sdl.GetError())
}
return shader
}
loadModel :: proc(device: ^sdl.GPUDevice, model: ^RSM_Model, vertices: ^[dynamic]Vertex, indices: ^[dynamic]u16) -> (vbuf: ^sdl.GPUBuffer, ibuf: ^sdl.GPUBuffer) {
clear(vertices)
clear(indices)
for node in model.nodes {
fmt.println(node.offset_matrix)
fmt.println(node.translation1)
fmt.println(node.translation2)
fmt.println(node.scale)
fmt.println(node.rotation_angle)
fmt.println(node.rotation_axis)
fmt.println(node.rotation_keyframes[:])
for face in node.faces {
for i in 0..<3 {
v_idx := face.vertex_position_indices[i]
t_idx := face.texture_coordinate_indices[i]
pos := node.vertex_positions[v_idx]
uv := node.texture_coordinates[t_idx].coordinates
append(vertices, Vertex{
pos = pos
// uv = uv,
})
append(indices, u16(len(vertices)-1))
}
}
}
vbuf_info := sdl.GPUBufferCreateInfo{
usage = sdl.GPUBufferUsageFlags{.VERTEX},
size = u32(len(vertices) * size_of(vertices[0])),
props = 0,
}
vbuf = sdl.CreateGPUBuffer(device, vbuf_info)
if vbuf == nil {
fmt.println("CreateGPUBuffer failed:", sdl.GetError())
return
}
ibuf_info := sdl.GPUBufferCreateInfo{
usage = sdl.GPUBufferUsageFlags{.INDEX},
size = u32(len(indices) * size_of(indices[0])),
props = 0,
}
ibuf = sdl.CreateGPUBuffer(device, ibuf_info)
if ibuf == nil {
fmt.println("CreateGPUBuffer failed:", sdl.GetError())
return
}
{
setup_cmd := sdl.AcquireGPUCommandBuffer(device)
if setup_cmd == nil {
fmt.println("AcquireGPUCommandBuffer failed:", sdl.GetError())
return
}
if !copy_data_to_gpu(device, setup_cmd, vertices[:], vbuf, indices[:], ibuf) {
_ = sdl.CancelGPUCommandBuffer(setup_cmd)
return
}
if !sdl.SubmitGPUCommandBuffer(setup_cmd) {
fmt.println("SubmitGPUCommandBuffer (setup) failed:", sdl.GetError())
return
}
}
return vbuf, ibuf
}
copy_data_to_gpu :: proc(device: ^sdl.GPUDevice, cmd: ^sdl.GPUCommandBuffer, vertices: []Vertex, vert_buf: ^sdl.GPUBuffer, indices: []u16, ind_buf: ^sdl.GPUBuffer) -> bool {
vert_byte_count := len(vertices) * size_of(vertices[0])
ind_byte_count := len(indices) * size_of(indices[0])
tb_info := sdl.GPUTransferBufferCreateInfo{
usage = .UPLOAD,
size = u32(vert_byte_count + ind_byte_count),
props = 0,
}
tb := sdl.CreateGPUTransferBuffer(device, tb_info)
if tb == nil {
fmt.println("CreateGPUTransferBuffer failed:", sdl.GetError())
return false
}
defer sdl.ReleaseGPUTransferBuffer(device, tb)
mapped := transmute([^]byte)sdl.MapGPUTransferBuffer(device, tb, false)
if mapped == nil {
fmt.println("MapGPUTransferBuffer failed:", sdl.GetError())
return false
}
mem.copy_non_overlapping(mapped, raw_data(vertices), vert_byte_count)
mem.copy_non_overlapping(mapped[vert_byte_count:], raw_data(indices), ind_byte_count)
sdl.UnmapGPUTransferBuffer(device, tb)
cp := sdl.BeginGPUCopyPass(cmd)
if cp == nil {
fmt.println("BeginGPUCopyPass failed:", sdl.GetError())
return false
}
vert_src := sdl.GPUTransferBufferLocation{transfer_buffer = tb, offset = 0}
vert_dst := sdl.GPUBufferRegion{buffer = vert_buf, offset = 0, size = u32(vert_byte_count)}
sdl.UploadToGPUBuffer(cp, vert_src, vert_dst, false)
ind_src := sdl.GPUTransferBufferLocation{transfer_buffer = tb, offset = u32(vert_byte_count)}
ind_dst := sdl.GPUBufferRegion{buffer = ind_buf, offset = 0, size = u32(ind_byte_count)}
sdl.UploadToGPUBuffer(cp, ind_src, ind_dst, false)
sdl.EndGPUCopyPass(cp)
return true
}
GPU_Vertex :: struct {
position: [3]f32,
uv: [2]f32,
}
main :: proc() {
if !sdl.Init(sdl.INIT_VIDEO) {
fmt.println("SDL init failed:", sdl.GetError())
return
}
defer sdl.Quit()
window := sdl.CreateWindow("Odin + SDL3 GPU Cubes", 1280, 720, sdl.WINDOW_RESIZABLE)
if window == nil {
fmt.println("window create failed:", sdl.GetError())
return
}
defer sdl.DestroyWindow(window)
device := sdl.CreateGPUDevice(sdl.GPUShaderFormat{.SPIRV}, false, cstring("vulkan"))
if device == nil {
fmt.println("CreateGPUDevice failed:", sdl.GetError())
return
}
defer sdl.DestroyGPUDevice(device)
if !sdl.ClaimWindowForGPUDevice(device, window) {
fmt.println("ClaimWindowForGPUDevice failed:", sdl.GetError())
return
}
defer sdl.ReleaseWindowFromGPUDevice(device, window)
driver_name := sdl.GetGPUDeviceDriver(device)
fmt.println("SDL GPU driver:", string(driver_name))
if string(driver_name) != "vulkan" {
fmt.println("expected Vulkan driver, got:", string(driver_name))
return
}
vertex_shader := create_gpu_shader(device, "shaders/cubes.vert.spv", .VERTEX)
if vertex_shader == nil {
return
}
defer sdl.ReleaseGPUShader(device, vertex_shader)
fragment_shader := create_gpu_shader(device, "shaders/cubes.frag.spv", .FRAGMENT)
if fragment_shader == nil {
return
}
defer sdl.ReleaseGPUShader(device, fragment_shader)
color_format := sdl.GetGPUSwapchainTextureFormat(device, window)
if color_format == .INVALID {
fmt.println("invalid swapchain format:", sdl.GetError())
return
}
vb_descs := [1]sdl.GPUVertexBufferDescription{
{slot = 0, pitch = size_of(Vertex), input_rate = .VERTEX, instance_step_rate = 0},
}
vattrs := [1]sdl.GPUVertexAttribute{
{location = 0, buffer_slot = 0, format = .FLOAT3, offset = 0},
}
blend := sdl.GPUColorTargetBlendState{
src_color_blendfactor = .ONE,
dst_color_blendfactor = .ZERO,
color_blend_op = .ADD,
src_alpha_blendfactor = .ONE,
dst_alpha_blendfactor = .ZERO,
alpha_blend_op = .ADD,
color_write_mask = sdl.GPUColorComponentFlags{.R, .G, .B, .A},
enable_blend = false,
enable_color_write_mask = true,
}
color_targets := [1]sdl.GPUColorTargetDescription{
{format = color_format, blend_state = blend},
}
pipeline_ci := sdl.GPUGraphicsPipelineCreateInfo{
vertex_shader = vertex_shader,
fragment_shader = fragment_shader,
vertex_input_state = sdl.GPUVertexInputState{
vertex_buffer_descriptions = &vb_descs[0],
num_vertex_buffers = 1,
vertex_attributes = &vattrs[0],
num_vertex_attributes = 1,
},
primitive_type = .TRIANGLELIST,
rasterizer_state = sdl.GPURasterizerState{
fill_mode = .FILL,
cull_mode = .BACK,
front_face = .COUNTER_CLOCKWISE,
depth_bias_constant_factor = 0,
depth_bias_clamp = 0,
depth_bias_slope_factor = 0,
enable_depth_bias = false,
enable_depth_clip = true,
},
multisample_state = sdl.GPUMultisampleState{
sample_count = ._1,
sample_mask = 0,
enable_mask = false,
enable_alpha_to_coverage = false,
},
depth_stencil_state = sdl.GPUDepthStencilState{
compare_op = .LESS,
back_stencil_state = sdl.GPUStencilOpState{fail_op = .KEEP, pass_op = .KEEP, depth_fail_op = .KEEP, compare_op = .ALWAYS},
front_stencil_state = sdl.GPUStencilOpState{fail_op = .KEEP, pass_op = .KEEP, depth_fail_op = .KEEP, compare_op = .ALWAYS},
compare_mask = 0,
write_mask = 0,
enable_depth_test = false,
enable_depth_write = false,
enable_stencil_test = false,
},
target_info = sdl.GPUGraphicsPipelineTargetInfo{
color_target_descriptions = &color_targets[0],
num_color_targets = 1,
depth_stencil_format = .INVALID,
has_depth_stencil_target = false,
},
props = 0,
}
pipeline := sdl.CreateGPUGraphicsPipeline(device, pipeline_ci)
if pipeline == nil {
fmt.println("CreateGPUGraphicsPipeline failed:", sdl.GetError())
return
}
defer sdl.ReleaseGPUGraphicsPipeline(device, pipeline)
pipeline_create_info := sdl.GPUGraphicsPipelineCreateInfo{
primitive_type = .TRIANGLELIST,
rasterizer_state = {
fill_mode = .LINE,
cull_mode = .NONE,
}
}
models: [dynamic]RSM_Model
walk("/home/pavel/neoragnarok_backup/kro_client/data", &models)
vertices: [dynamic]Vertex
indices: [dynamic]u16
vbuf, ibuf := loadModel(device, &models[modelIndex], &vertices, &indices)
defer sdl.ReleaseGPUBuffer(device, ibuf)
defer sdl.ReleaseGPUBuffer(device, vbuf)
init_yaw := rotor_from_axis_angle(Vec3{0, 1, 0}, 0.9)
init_right := rotate_vec3(init_yaw, Vec3{1, 0, 0})
init_pitch := rotor_from_axis_angle(init_right, -0.45)
camera := Camera{
center = Vec3{0, 0.5, 0},
distance = 8,
orientation = rotor_normalize(rotor_mul(init_pitch, init_yaw)),
fov_deg = 60,
}
cubes := []Cube_Instance{
{pos = Vec3{0, 0, 0}, scale = 1.0, color = Vec3{0.95, 0.45, 0.20}},
{pos = Vec3{2, 0.5, -1}, scale = 0.8, color = Vec3{0.20, 0.70, 0.95}},
{pos = Vec3{-2, -0.2, 1.5}, scale = 1.2, color = Vec3{0.85, 0.85, 0.30}},
{pos = Vec3{1.0, 1.4, 2.0}, scale = 0.6, color = Vec3{0.40, 0.95, 0.60}},
}
running := true
last := time.tick_now()
left_down := false
middle_down := false
debug := Debug_State{}
for running {
now := time.tick_now()
dt := f32(time.duration_seconds(time.tick_diff(last, now)))
last = now
event: sdl.Event
for sdl.PollEvent(&event) {
#partial switch event.type {
case .QUIT:
running = false
case .MOUSE_BUTTON_DOWN:
switch event.button.button {
case sdl.BUTTON_LEFT:
left_down = true
case sdl.BUTTON_MIDDLE:
middle_down = true
}
case .MOUSE_BUTTON_UP:
switch event.button.button {
case sdl.BUTTON_LEFT:
left_down = false
case sdl.BUTTON_MIDDLE:
middle_down = false
}
case .MOUSE_WHEEL:
camera.distance = math.clamp(camera.distance - event.wheel.y * 0.7, 1.5, 80.0)
case .KEY_DOWN:
if event.key.key == sdl.K_ESCAPE {
running = false
} else if event.key.scancode == sdl.Scancode.F1 && !event.key.repeat {
debug.enabled = !debug.enabled
fmt.println("debug:", debug.enabled)
} else if event.key.key == sdl.K_N {
modelIndex += 1
sdl.ReleaseGPUBuffer(device, ibuf)
sdl.ReleaseGPUBuffer(device, vbuf)
vbuf, ibuf = loadModel(device, &models[modelIndex], &vertices, &indices)
}
}
}
x_rel: f32 = 0
y_rel: f32 = 0
_ = sdl.GetRelativeMouseState(&x_rel, &y_rel)
if left_down {
yaw_r := rotor_from_axis_angle(Vec3{0, 1, 0}, -x_rel * 0.006)
q1 := rotor_normalize(rotor_mul(yaw_r, camera.orientation))
right_axis := vec3_normalize(rotate_vec3(q1, Vec3{1, 0, 0}))
pitch_r := rotor_from_axis_angle(right_axis, -y_rel * 0.006)
q2 := rotor_normalize(rotor_mul(pitch_r, q1))
fwd2 := camera_forward(Camera{center = camera.center, distance = camera.distance, orientation = q2, fov_deg = camera.fov_deg})
if math.abs(vec3_dot(fwd2, Vec3{0, 1, 0})) < 0.98 {
camera.orientation = q2
} else {
camera.orientation = q1
}
}
if middle_down {
right := camera_right(camera)
up := camera_up(camera)
pan_speed := 0.008 * camera.distance
camera.center = vec3_add(camera.center, vec3_scale(right, -x_rel * pan_speed))
camera.center = vec3_add(camera.center, vec3_scale(up, y_rel * pan_speed))
}
keys := sdl.GetKeyboardState(nil)
forward := camera_forward(camera)
right := camera_right(camera)
up := camera_up(camera)
move_speed := camera.distance * dt * 1.4
if keys[sdl.Scancode.W] {
camera.center = vec3_add(camera.center, vec3_scale(forward, move_speed))
}
if keys[sdl.Scancode.S] {
camera.center = vec3_add(camera.center, vec3_scale(forward, -move_speed))
}
if keys[sdl.Scancode.A] {
camera.center = vec3_add(camera.center, vec3_scale(right, -move_speed))
}
if keys[sdl.Scancode.D] {
camera.center = vec3_add(camera.center, vec3_scale(right, move_speed))
}
if debug.enabled {
debug.accum += dt
if debug.accum >= 0.2 {
debug.accum = 0
eye := camera_position(camera)
fmt.println("cam eye:", eye,
"center:", camera.center,
"fwd:", forward,
"right:", right,
"up:", up)
}
}
w: c.int = 0
h: c.int = 0
sdl.GetWindowSize(window, &w, &h)
if w <= 0 || h <= 0 {
continue
}
cmd := sdl.AcquireGPUCommandBuffer(device)
if cmd == nil {
fmt.println("AcquireGPUCommandBuffer failed:", sdl.GetError())
break
}
swap_tex: ^sdl.GPUTexture
swap_w: sdl.Uint32 = 0
swap_h: sdl.Uint32 = 0
if !sdl.WaitAndAcquireGPUSwapchainTexture(cmd, window, &swap_tex, &swap_w, &swap_h) {
fmt.println("WaitAndAcquireGPUSwapchainTexture failed:", sdl.GetError())
_ = sdl.CancelGPUCommandBuffer(cmd)
break
}
if swap_tex == nil {
if !sdl.SubmitGPUCommandBuffer(cmd) {
fmt.println("SubmitGPUCommandBuffer failed:", sdl.GetError())
break
}
continue
}
clear := sdl.FColor{0.08, 0.09, 0.12, 1.0}
cti := sdl.GPUColorTargetInfo{
texture = swap_tex,
mip_level = 0,
layer_or_depth_plane = 0,
clear_color = clear,
load_op = .CLEAR,
store_op = .STORE,
resolve_texture = nil,
resolve_mip_level = 0,
resolve_layer = 0,
cycle = false,
cycle_resolve_texture = false,
}
rp := sdl.BeginGPURenderPass(cmd, &cti, 1, nil)
if rp == nil {
fmt.println("BeginGPURenderPass failed:", sdl.GetError())
_ = sdl.CancelGPUCommandBuffer(cmd)
break
}
vp := sdl.GPUViewport{x = 0, y = 0, w = f32(swap_w), h = f32(swap_h), min_depth = 0, max_depth = 1}
sdl.SetGPUViewport(rp, vp)
sdl.BindGPUGraphicsPipeline(rp, pipeline)
vb_binding := sdl.GPUBufferBinding{buffer = vbuf, offset = 0}
sdl.BindGPUVertexBuffers(rp, 0, &vb_binding, 1)
ib_binding := sdl.GPUBufferBinding{buffer = ibuf, offset = 0}
sdl.BindGPUIndexBuffer(rp, ib_binding, ._16BIT)
aspect := f32(swap_w) / f32(swap_h)
proj := linalg.matrix4_perspective(camera.fov_deg, aspect, 0.1, 300.0)
view := linalg.matrix4_look_at(camera_position(camera), camera.center, camera_up(camera))
vp_mat := proj * view
for node in models[modelIndex].nodes {
rotation := linalg.quaternion_angle_axis(node.rotation_angle, node.rotation_axis)
if (len(node.rotation_keyframes) > 0) {
q := node.rotation_keyframes[0].quaternion
rotation = quaternion(imag=q[0], jmag=q[1], kmag=q[2], real=q[3])
}
rotation = linalg.quaternion_normalize(rotation)
r := linalg.matrix3_from_quaternion(rotation)
s := matrix[3,3]f32{
node.scale[0], 0, 0 ,
0, node.scale[1], 0 ,
0, 0, node.scale[2],
}
a3 := r * node.offset_matrix * s
t := node.translation1 + node.translation2
// 4x4 affine matrix
model := matrix[4,4]f32{
a3[0][0], a3[0][1], a3[0][2], t[0],
a3[1][0], a3[1][1], a3[1][2], t[1],
a3[2][0], a3[2][1], a3[2][2], t[2],
0, 0, 0, 1 ,
}
// append(vertices, Vertex{
// pos = linalg.matrix3_from_quaternion(rotation) * node.offset_matrix * (node.scale * pos) + node.translation1 + node.translation2
// // uv = uv,
// })
// model := mat4_mul(node., mat4_scale_uniform(1))
pc := Push_Constants{}
pc.mvp = vp_mat * model
pc.color = [4]f32{1, 0, 0, 1.0}
sdl.PushGPUVertexUniformData(cmd, 0, &pc, u32(size_of(Push_Constants)))
sdl.PushGPUFragmentUniformData(cmd, 0, &pc, u32(size_of(Push_Constants)))
sdl.DrawGPUIndexedPrimitives(rp, u32(len(indices)), 1, 0, 0, 0)
}
sdl.EndGPURenderPass(rp)
if !sdl.SubmitGPUCommandBuffer(cmd) {
fmt.println("SubmitGPUCommandBuffer failed:", sdl.GetError())
break
}
}
_ = sdl.WaitForGPUIdle(device)
}
walk :: proc(dir: string, models: ^[dynamic]RSM_Model) {
f, _ := os.open(dir)
entries, ok := os.read_dir(f, 0, context.allocator)
for entry in entries {
path, _ := filepath.join({dir, entry.name})
if entry.type == .Directory {
walk(path, models)
}
if strings.contains(entry.name, ".rsm") {
data, _ := os.read_entire_file(path, context.allocator)
parsed, err := parse_rsm(data)
if err != nil {
fmt.printfln("%v", err)
} else {
append_elem(models, parsed)
}
}
}
}

View file

@ -2,16 +2,11 @@
layout(location = 0) out vec4 out_color;
layout(location = 0) in vec2 uv;
layout(set = 2, binding = 0) uniform sampler2D tex_sampler;
layout(set = 3, binding = 0) uniform FragmentUniforms {
mat4 u_mvp;
vec4 u_color;
};
void main() {
vec4 c = texture(tex_sampler, uv);
float tol = 0.02;
if (distance(c.rgb, vec3(1.0, 0.0, 1.0)) < tol || distance(c.rgb, vec3(0.0, 0.0, 0.0)) < tol) {
discard;
}
out_color = c;
out_color = u_color;
}

Binary file not shown.

View file

@ -7,11 +7,6 @@ layout(set = 1, binding = 0) uniform VertexUniforms {
vec4 u_color;
};
layout(location=1) in vec2 uv;
layout(location=0) out vec2 out_uv;
void main() {
gl_Position = u_mvp * vec4(a_pos, 1.0);
out_uv = uv;
}

Binary file not shown.

View file

@ -1,114 +0,0 @@
package format
import "core:strings"
Version :: struct {
major: u8,
minor: u8,
}
version_ge :: #force_inline proc(v: Version, major, minor: int) -> bool {
return int(v.major) > major || (int(v.major) == major && int(v.minor) >= minor)
}
version_below :: #force_inline proc(v: Version, major, minor: int) -> bool {
return !version_ge(v, major, minor)
}
normalize_path :: proc(p: string) -> string {
// Byte-preserving normalization for legacy CP949/EUC-KR path bytes.
// Only ASCII transforms are applied.
b := transmute([]u8)p
out := make([]u8, len(b), context.allocator)
for i in 0..<len(b) {
ch := b[i]
if ch == '\\' {
out[i] = '/'
} else if ch >= 'A' && ch <= 'Z' {
out[i] = ch + ('a' - 'A')
} else {
out[i] = ch
}
}
return string(out)
}
latin1_bytes_to_utf8_string :: proc(src: []u8, allocator := context.allocator) -> string {
// 0x00..0x7F => 1 byte UTF-8, 0x80..0xFF => 2 bytes UTF-8
out := make([dynamic]u8, 0, len(src) * 2, allocator)
for b in src {
if b < 0x80 {
append(&out, b)
} else {
append(&out, 0xC0 | (b >> 6))
append(&out, 0x80 | (b & 0x3F))
}
}
return string(out[:])
}
read_u8 :: proc(r: ^RSM_Reader) -> (u8, bool) {
if r.at+1 > len(r.data) { return 0, false }
v := r.data[r.at]
r.at += 1
return v, true
}
read_u16 :: proc(r: ^RSM_Reader) -> (u16, bool) {
if r.at+2 > len(r.data) { return 0, false }
v := u16(r.data[r.at]) | (u16(r.data[r.at+1]) << 8)
r.at += 2
return v, true
}
read_u32 :: proc(r: ^RSM_Reader) -> (u32, bool) {
if r.at+4 > len(r.data) { return 0, false }
v := u32(r.data[r.at]) | (u32(r.data[r.at+1]) << 8) | (u32(r.data[r.at+2]) << 16) | (u32(r.data[r.at+3]) << 24)
r.at += 4
return v, true
}
read_i32 :: proc(r: ^RSM_Reader) -> (i32, bool) {
v, ok := read_u32(r)
return i32(v), ok
}
read_f32 :: proc(r: ^RSM_Reader) -> (f32, bool) {
bits, ok := read_u32(r)
if !ok { return 0, false }
return transmute(f32)bits, true
}
read_fixed_string_clean :: proc(r: ^RSM_Reader, n: int) -> (string, bool) {
s, ok := read_fixed_string(r, n)
if !ok { return "", false }
return strings.trim_space(s), true
}
read_fixed_string :: proc(r: ^RSM_Reader, n: int) -> (string, bool) {
if r.at+n > len(r.data) { return "", false }
buf := r.data[r.at:r.at+n]
r.at += n
end := 0
for end < len(buf) && buf[end] != 0 {
end += 1
}
return latin1_bytes_to_utf8_string(buf[:end]), true
}
read_model_string :: proc(r: ^RSM_Reader, version: Version, fixed_len: int) -> (string, bool) {
if version_ge(version, 2, 2) {
n, ok := read_u32(r)
if !ok || int(n) < 0 || r.at+int(n) > len(r.data) { return "", false }
s := latin1_bytes_to_utf8_string(r.data[r.at:r.at+int(n)])
r.at += int(n)
return s, true
}
return read_fixed_string(r, fixed_len)
}
read_vec3 :: proc(r: ^RSM_Reader) -> ([3]f32, bool) {
x, ok1 := read_f32(r); y, ok2 := read_f32(r); z, ok3 := read_f32(r)
return [3]f32{x,y,z}, ok1 && ok2 && ok3
}

View file

@ -1,385 +0,0 @@
package format
import "core:strings"
RSW_Parse_Error :: enum {
None,
UnexpectedEOF,
InvalidSignature,
}
RSW_WaterSettings :: struct {
water_level: f32,
water_type: i32,
wave_height: f32,
wave_speed: f32,
wave_pitch: f32,
texture_cycling_interval: u32,
}
RSW_LightSettings :: struct {
light_longitude: i32,
light_latitude: i32,
diffuse_color: [3]f32,
ambient_color: [3]f32,
shadow_map_alpha: f32,
}
RSW_Transform :: struct {
position: [3]f32,
rotation_deg: [3]f32,
scale: [3]f32,
}
RSW_Object :: struct {
name: string,
model_name: string,
node_name: string,
transform: RSW_Transform,
}
RSW_LightSource :: struct {
name: string,
position: [3]f32,
color: [3]f32,
range: f32,
}
RSW_SoundSource :: struct {
name: string,
sound_file: string,
position: [3]f32,
volume: f32,
width: u32,
height: u32,
range: f32,
cycle: f32,
}
RSW_EffectSource :: struct {
name: string,
position: [3]f32,
effect_type: u32,
emit_speed: f32,
param0: f32,
param1: f32,
param2: f32,
param3: f32,
}
RSW_Data :: struct {
version: Version,
build_version: u32,
ground_file: string,
gat_file: string,
water: RSW_WaterSettings,
light: RSW_LightSettings,
objects: [dynamic]RSW_Object,
light_sources: [dynamic]RSW_LightSource,
sound_sources: [dynamic]RSW_SoundSource,
effect_sources: [dynamic]RSW_EffectSource,
}
read_vec3f :: proc(r: ^RSM_Reader) -> ([3]f32, bool) {
x, ok1 := read_f32(r)
y, ok2 := read_f32(r)
z, ok3 := read_f32(r)
return [3]f32{x,y,z}, ok1 && ok2 && ok3
}
parse_rsw :: proc(data: []u8) -> (RSW_Data, RSW_Parse_Error) {
out := RSW_Data{
objects = make([dynamic]RSW_Object),
light_sources = make([dynamic]RSW_LightSource),
sound_sources = make([dynamic]RSW_SoundSource),
effect_sources = make([dynamic]RSW_EffectSource),
}
r := RSM_Reader{data = data}
sig, ok := read_fixed_string(&r, 4)
resources_amount: u32
rtype: i32
if !ok || sig != "GRSW" {
return out, .InvalidSignature
}
maj, ok1 := read_u8(&r)
min, ok2 := read_u8(&r)
if !(ok1 && ok2) { return out, .UnexpectedEOF }
out.version = Version{maj, min}
if version_ge(out.version, 2, 5) {
out.build_version, ok = read_u32(&r)
if !ok { return out, .UnexpectedEOF }
}
if version_ge(out.version, 2, 2) {
_, ok = read_u8(&r)
if !ok { return out, .UnexpectedEOF }
}
_, ok = read_fixed_string_clean(&r, 40)
if !ok { return out, .UnexpectedEOF }
out.ground_file, ok = read_fixed_string_clean(&r, 40)
if !ok { return out, .UnexpectedEOF }
out.gat_file, ok = read_fixed_string_clean(&r, 40)
if !ok { return out, .UnexpectedEOF }
if version_ge(out.version, 1, 4) {
_, ok = read_fixed_string_clean(&r, 40)
if !ok { return out, .UnexpectedEOF }
}
if !version_ge(out.version, 2, 6) {
if version_ge(out.version, 1, 3) {
out.water.water_level, ok = read_f32(&r); if !ok { return out, .UnexpectedEOF }
}
if version_ge(out.version, 1, 8) {
out.water.water_type, ok = read_i32(&r); if !ok { return out, .UnexpectedEOF }
out.water.wave_height, ok = read_f32(&r); if !ok { return out, .UnexpectedEOF }
out.water.wave_speed, ok = read_f32(&r); if !ok { return out, .UnexpectedEOF }
out.water.wave_pitch, ok = read_f32(&r); if !ok { return out, .UnexpectedEOF }
}
if version_ge(out.version, 1, 9) {
out.water.texture_cycling_interval, ok = read_u32(&r); if !ok { return out, .UnexpectedEOF }
}
}
if version_ge(out.version, 1, 5) {
out.light.light_longitude, ok = read_i32(&r); if !ok { return out, .UnexpectedEOF }
out.light.light_latitude, ok = read_i32(&r); if !ok { return out, .UnexpectedEOF }
out.light.diffuse_color, ok = read_vec3f(&r); if !ok { return out, .UnexpectedEOF }
out.light.ambient_color, ok = read_vec3f(&r); if !ok { return out, .UnexpectedEOF }
}
if version_ge(out.version, 1, 7) {
out.light.shadow_map_alpha, ok = read_f32(&r); if !ok { return out, .UnexpectedEOF }
}
if version_ge(out.version, 1, 6) {
for _ in 0..<4 {
_, ok = read_i32(&r)
if !ok { return out, .UnexpectedEOF }
}
}
resources_amount, ok = read_u32(&r)
if !ok { return out, .UnexpectedEOF }
for i := 0; i < int(resources_amount); i += 1 {
rtype, ok = read_i32(&r)
if !ok { return out, .UnexpectedEOF }
switch rtype {
case 1:
obj := RSW_Object{}
if version_ge(out.version, 1, 3) {
obj.name, ok = read_fixed_string_clean(&r, 40); if !ok { return out, .UnexpectedEOF }
_, ok = read_i32(&r); if !ok { return out, .UnexpectedEOF }
_, ok = read_f32(&r); if !ok { return out, .UnexpectedEOF }
_, ok = read_i32(&r); if !ok { return out, .UnexpectedEOF }
}
if version_ge(out.version, 2, 6) && out.build_version >= 186 {
_, ok = read_u8(&r)
if !ok { return out, .UnexpectedEOF }
}
obj.model_name, ok = read_fixed_string_clean(&r, 80); if !ok { return out, .UnexpectedEOF }
obj.node_name, ok = read_fixed_string_clean(&r, 80); if !ok { return out, .UnexpectedEOF }
obj.model_name = normalize_path(obj.model_name)
obj.transform.position, ok = read_vec3f(&r); if !ok { return out, .UnexpectedEOF }
obj.transform.rotation_deg, ok = read_vec3f(&r); if !ok { return out, .UnexpectedEOF }
obj.transform.scale, ok = read_vec3f(&r); if !ok { return out, .UnexpectedEOF }
obj.transform.position[1] = -obj.transform.position[1]
append(&out.objects, obj)
case 2:
l := RSW_LightSource{}
l.name, ok = read_fixed_string_clean(&r, 80); if !ok { return out, .UnexpectedEOF }
l.position, ok = read_vec3f(&r); if !ok { return out, .UnexpectedEOF }
l.position[1] = -l.position[1]
l.color, ok = read_vec3f(&r); if !ok { return out, .UnexpectedEOF }
l.range, ok = read_f32(&r); if !ok { return out, .UnexpectedEOF }
append(&out.light_sources, l)
case 3:
s := RSW_SoundSource{}
s.name, ok = read_fixed_string_clean(&r, 80); if !ok { return out, .UnexpectedEOF }
s.sound_file, ok = read_fixed_string_clean(&r, 80); if !ok { return out, .UnexpectedEOF }
s.position, ok = read_vec3f(&r); if !ok { return out, .UnexpectedEOF }
s.position[1] = -s.position[1]
s.volume, ok = read_f32(&r); if !ok { return out, .UnexpectedEOF }
s.width, ok = read_u32(&r); if !ok { return out, .UnexpectedEOF }
s.height, ok = read_u32(&r); if !ok { return out, .UnexpectedEOF }
s.range, ok = read_f32(&r); if !ok { return out, .UnexpectedEOF }
if version_ge(out.version, 2, 0) {
s.cycle, ok = read_f32(&r); if !ok { return out, .UnexpectedEOF }
} else {
s.cycle = 4.0
}
append(&out.sound_sources, s)
case 4:
e := RSW_EffectSource{}
e.name, ok = read_fixed_string_clean(&r, 80); if !ok { return out, .UnexpectedEOF }
e.position, ok = read_vec3f(&r); if !ok { return out, .UnexpectedEOF }
e.position[1] = -e.position[1]
e.effect_type, ok = read_u32(&r); if !ok { return out, .UnexpectedEOF }
e.emit_speed, ok = read_f32(&r); if !ok { return out, .UnexpectedEOF }
e.param0, ok = read_f32(&r); if !ok { return out, .UnexpectedEOF }
e.param1, ok = read_f32(&r); if !ok { return out, .UnexpectedEOF }
e.param2, ok = read_f32(&r); if !ok { return out, .UnexpectedEOF }
e.param3, ok = read_f32(&r); if !ok { return out, .UnexpectedEOF }
append(&out.effect_sources, e)
case:
return out, .UnexpectedEOF
}
}
return out, .None
}
GND_Info :: struct { version: Version, width, height: i32, zoom: f32 }
GAT_Info :: struct { version: Version, width, height: i32 }
GND_Surface :: struct {
u: [4]f32,
v: [4]f32,
texture_index: i16,
light_map_index: i16,
color_bgra: [4]u8,
}
GND_Tile :: struct {
h_sw, h_se, h_nw, h_ne: f32,
top_surface_index: i32,
north_surface_index: i32,
east_surface_index: i32,
}
GND_Data :: struct {
info: GND_Info,
textures: [dynamic]string,
surfaces: [dynamic]GND_Surface,
tiles: [dynamic]GND_Tile,
}
parse_gnd_info :: proc(data: []u8) -> (GND_Info, RSW_Parse_Error) {
r := RSM_Reader{data = data}
sig, ok := read_fixed_string(&r, 4)
if !ok || sig != "GRGN" { return GND_Info{}, .InvalidSignature }
maj, ok1 := read_u8(&r)
min, ok2 := read_u8(&r)
if !(ok1 && ok2) { return GND_Info{}, .UnexpectedEOF }
w, ok3 := read_i32(&r)
h, ok4 := read_i32(&r)
z, ok5 := read_f32(&r)
if !(ok3 && ok4 && ok5) { return GND_Info{}, .UnexpectedEOF }
return GND_Info{version = Version{maj, min}, width = w, height = h, zoom = z}, .None
}
parse_gnd :: proc(data: []u8) -> (GND_Data, RSW_Parse_Error) {
out := GND_Data{
textures = make([dynamic]string),
surfaces = make([dynamic]GND_Surface),
tiles = make([dynamic]GND_Tile),
}
info, err := parse_gnd_info(data)
if err != .None {
return out, err
}
out.info = info
r := RSM_Reader{data = data, at = 4 + 2 + 4 + 4 + 4}
texture_count, ok_tc := read_i32(&r)
if !ok_tc { return out, .UnexpectedEOF }
texture_name_length, ok_tnl := read_i32(&r)
if !ok_tnl { return out, .UnexpectedEOF }
for _ in 0..<texture_count {
t, ok_t := read_fixed_string_clean(&r, int(texture_name_length))
if !ok_t { return out, .UnexpectedEOF }
append(&out.textures, normalize_path(t))
}
light_map_count, ok_lmc := read_i32(&r); if !ok_lmc { return out, .UnexpectedEOF }
light_map_width, ok_lmw := read_i32(&r); if !ok_lmw { return out, .UnexpectedEOF }
light_map_height, ok_lmh := read_i32(&r); if !ok_lmh { return out, .UnexpectedEOF }
_, ok_lmcpg := read_i32(&r) // light_map_cells_per_grid
if !ok_lmcpg { return out, .UnexpectedEOF }
if version_ge(info.version, 1, 7) {
skip := int(light_map_count * light_map_width * light_map_height * 4)
if r.at+skip > len(r.data) { return out, .UnexpectedEOF }
r.at += skip
} else {
skip := int(light_map_count * 16)
if r.at+skip > len(r.data) { return out, .UnexpectedEOF }
r.at += skip
}
surface_count, ok_sc := read_i32(&r)
if !ok_sc { return out, .UnexpectedEOF }
for _ in 0..<surface_count {
s := GND_Surface{}
for i in 0..<4 {
u, ok_sc_u := read_f32(&r)
if !ok_sc_u { return out, .UnexpectedEOF }
s.u[i] = u
}
for i in 0..<4 {
v, ok_sc_v := read_f32(&r)
if !ok_sc_v { return out, .UnexpectedEOF }
s.v[i] = v
}
ti, ok_ti := read_u16(&r); if !ok_ti { return out, .UnexpectedEOF }
li, ok_li := read_u16(&r); if !ok_li { return out, .UnexpectedEOF }
s.texture_index = i16(ti)
s.light_map_index = i16(li)
for i in 0..<4 {
c, ok_c := read_u8(&r)
if !ok_c { return out, .UnexpectedEOF }
s.color_bgra[i] = c
}
append(&out.surfaces, s)
}
tile_count := int(info.width * info.height)
for _ in 0..<tile_count {
t := GND_Tile{}
h_sw, ok_hsw := read_f32(&r); if !ok_hsw { return out, .UnexpectedEOF }
h_se, ok_hse := read_f32(&r); if !ok_hse { return out, .UnexpectedEOF }
h_nw, ok_hnw := read_f32(&r); if !ok_hnw { return out, .UnexpectedEOF }
h_ne, ok_hne := read_f32(&r); if !ok_hne { return out, .UnexpectedEOF }
t.h_sw = h_sw
t.h_se = h_se
t.h_nw = h_nw
t.h_ne = h_ne
if version_ge(info.version, 1, 7) {
top_surface_index, ok_tsi := read_i32(&r); if !ok_tsi { return out, .UnexpectedEOF }
north_surface_index, ok_nsi := read_i32(&r); if !ok_nsi { return out, .UnexpectedEOF }
east_surface_index, ok_esi := read_i32(&r); if !ok_esi { return out, .UnexpectedEOF }
t.top_surface_index = top_surface_index
t.north_surface_index = north_surface_index
t.east_surface_index = east_surface_index
} else {
top16, ok_top16 := read_u16(&r); if !ok_top16 { return out, .UnexpectedEOF }
north16, ok_north16 := read_u16(&r); if !ok_north16 { return out, .UnexpectedEOF }
east16, ok_east16 := read_u16(&r); if !ok_east16 { return out, .UnexpectedEOF }
t.top_surface_index = i32(i16(top16))
t.north_surface_index = i32(i16(north16))
t.east_surface_index = i32(i16(east16))
}
append(&out.tiles, t)
}
return out, .None
}
parse_gat_info :: proc(data: []u8) -> (GAT_Info, RSW_Parse_Error) {
r := RSM_Reader{data = data}
sig, ok := read_fixed_string(&r, 4)
if !ok || sig != "GRAT" { return GAT_Info{}, .InvalidSignature }
maj, ok1 := read_u8(&r)
min, ok2 := read_u8(&r)
if !(ok1 && ok2) { return GAT_Info{}, .UnexpectedEOF }
w, ok3 := read_i32(&r)
h, ok4 := read_i32(&r)
if !(ok3 && ok4) { return GAT_Info{}, .UnexpectedEOF }
return GAT_Info{version = Version{maj, min}, width = w, height = h}, .None
}

View file

@ -1,282 +0,0 @@
package format
import "core:strings"
import "core:fmt"
RSM_ScaleKeyframe :: struct { frame: i32, scale: [3]f32, _reserved: f32 }
RSM_RotationKeyframe :: struct { frame: i32, quaternion: [4]f32 }
RSM_TranslationKeyframe :: struct { frame: i32, translation: [3]f32, _reserved: f32 }
RSM_TextureFrame :: struct { frame: i32, operation_value: f32 }
RSM_TextureKeyframe :: struct { operation_type: u32, texture_frames: [dynamic]RSM_TextureFrame }
RSM_TexturesKeyframe :: struct { texture_index: u32, texture_keyframes: [dynamic]RSM_TextureKeyframe }
RSM_Face :: struct {
length: u32,
vertex_position_indices: [3]u16,
texture_coordinate_indices:[3]u16,
texture_index: u16,
padding: u16,
two_sided: i32,
smooth_group: i32,
smooth_group_extra: [dynamic]i32,
}
RSM_TextureCoordinate :: struct { color: u32, coordinates: [2]f32 }
RSM_Node :: struct {
node_name: string,
parent_node_name: string,
texture_indices: [dynamic]u32,
texture_names: [dynamic]string,
offset_matrix: matrix[3,3] f32,
translation1: [3]f32,
translation2: [3]f32,
rotation_angle: f32,
rotation_axis: [3]f32,
scale: [3]f32,
vertex_positions: [dynamic][3]f32,
texture_coordinates: [dynamic]RSM_TextureCoordinate,
faces: [dynamic]RSM_Face,
scale_keyframes: [dynamic]RSM_ScaleKeyframe,
rotation_keyframes: [dynamic]RSM_RotationKeyframe,
translation_keyframes: [dynamic]RSM_TranslationKeyframe,
textures_keyframes: [dynamic]RSM_TexturesKeyframe,
}
RSM_Model :: struct {
version: Version,
animation_length:u32,
shade_type: u32,
alpha: u8,
frames_per_second:f32,
texture_names: [dynamic]string,
root_node_names: [dynamic]string,
nodes: [dynamic]RSM_Node,
}
RSM_Parse_Error :: enum {
None,
UnexpectedEOF,
InvalidSignature,
InvalidUtf8,
}
RSM_Reader :: struct {
data: []u8,
at: int,
}
parse_rsm :: proc(data: []u8) -> (RSM_Model, RSM_Parse_Error) {
model := RSM_Model{}
r := RSM_Reader{data = data}
ok: bool
sig: string
alpha: u8
fps: f32
texture_count, root_count, node_count: u32
count, vp_count, tc_count, face_count, s_count, r_count, t_count, tk_count, key_count, frame_count: u32
name, root: string
idx: u32
v: [3]f32
sg: i32
sig, ok = read_fixed_string(&r, 4)
if !ok || sig != "GRSM" {
fmt.printfln("signature is %v")
return model, .InvalidSignature
}
major, ok1 := read_u8(&r)
minor, ok2 := read_u8(&r)
if !(ok1 && ok2) { return model, .UnexpectedEOF }
model.version = Version{major = major, minor = minor}
animation_length, ok3 := read_u32(&r)
shade_type, ok4 := read_u32(&r)
if !(ok3 && ok4) { return model, .UnexpectedEOF }
model.animation_length = animation_length
model.shade_type = shade_type
if version_ge(model.version, 1, 4) {
alpha, ok = read_u8(&r)
if !ok { return model, .UnexpectedEOF }
model.alpha = alpha
}
if version_below(model.version, 2, 2) {
if r.at+16 > len(r.data) { return model, .UnexpectedEOF }
r.at += 16
}
if version_ge(model.version, 2, 2) {
fps, ok = read_f32(&r)
if !ok { return model, .UnexpectedEOF }
model.frames_per_second = fps
}
if version_below(model.version, 2, 3) {
texture_count, ok = read_u32(&r)
if !ok { return model, .UnexpectedEOF }
for _ in 0..<int(texture_count) {
name, ok = read_model_string(&r, model.version, 40)
if !ok { return model, .UnexpectedEOF }
append(&model.texture_names, normalize_path(name))
}
}
if version_below(model.version, 2, 2) {
root, ok = read_model_string(&r, model.version, 40)
if !ok { return model, .UnexpectedEOF }
append(&model.root_node_names, normalize_path(root))
} else {
root_count, ok = read_u32(&r)
if !ok { return model, .UnexpectedEOF }
for _ in 0..<int(root_count) {
root, ok = read_model_string(&r, model.version, 40)
if !ok { return model, .UnexpectedEOF }
append(&model.root_node_names, normalize_path(root))
}
}
node_count, ok = read_u32(&r)
if !ok { return model, .UnexpectedEOF }
for _ in 0..<int(node_count) {
node := RSM_Node{}
node.node_name, ok = read_model_string(&r, model.version, 40); if !ok { return model, .UnexpectedEOF }
node.parent_node_name, ok = read_model_string(&r, model.version, 40); if !ok { return model, .UnexpectedEOF }
if version_below(model.version, 2, 3) {
count, ok = read_u32(&r); if !ok { return model, .UnexpectedEOF }
for _ in 0..<int(count) {
idx, ok = read_u32(&r); if !ok { return model, .UnexpectedEOF }
append(&node.texture_indices, idx)
}
} else {
count, ok = read_u32(&r); if !ok { return model, .UnexpectedEOF }
for _ in 0..<int(count) {
name, ok = read_model_string(&r, model.version, 40); if !ok { return model, .UnexpectedEOF }
append(&node.texture_names, strings.to_lower(name))
}
}
for x in 0..<3 {
for y in 0..<3 {
node.offset_matrix[x][y], ok = read_f32(&r)
if !ok { return model, .UnexpectedEOF }
}
}
if version_below(model.version, 2, 2) {
node.translation1, ok = read_vec3(&r); if !ok { return model, .UnexpectedEOF }
}
node.translation2, ok = read_vec3(&r); if !ok { return model, .UnexpectedEOF }
if version_below(model.version, 2, 2) {
node.rotation_angle, ok = read_f32(&r); if !ok { return model, .UnexpectedEOF }
node.rotation_axis, ok = read_vec3(&r); if !ok { return model, .UnexpectedEOF }
node.scale, ok = read_vec3(&r); if !ok { return model, .UnexpectedEOF }
if node.rotation_angle == 0 {
node.rotation_axis = {1,0,0}
}
} else {
node.scale = {1,1,1}
node.rotation_angle = 0
node.rotation_axis = {1,0,0}
}
vp_count, ok = read_u32(&r); if !ok { return model, .UnexpectedEOF }
for _ in 0..<int(vp_count) {
v, ok = read_vec3(&r); if !ok { return model, .UnexpectedEOF }
append(&node.vertex_positions, v)
}
tc_count, ok = read_u32(&r); if !ok { return model, .UnexpectedEOF }
for _ in 0..<int(tc_count) {
tc := RSM_TextureCoordinate{}
if version_ge(model.version, 1, 2) {
tc.color, ok = read_u32(&r); if !ok { return model, .UnexpectedEOF }
}
u, ok1 := read_f32(&r); v, ok2 := read_f32(&r)
if !(ok1 && ok2) { return model, .UnexpectedEOF }
tc.coordinates = [2]f32{u, v}
append(&node.texture_coordinates, tc)
}
face_count, ok = read_u32(&r); if !ok { return model, .UnexpectedEOF }
for _ in 0..<int(face_count) {
f := RSM_Face{}
if version_ge(model.version, 2, 2) {
f.length, ok = read_u32(&r); if !ok { return model, .UnexpectedEOF }
}
for i in 0..<3 { f.vertex_position_indices[i], ok = read_u16(&r); if !ok { return model, .UnexpectedEOF } }
for i in 0..<3 { f.texture_coordinate_indices[i], ok = read_u16(&r); if !ok { return model, .UnexpectedEOF } }
f.texture_index, ok = read_u16(&r); if !ok { return model, .UnexpectedEOF }
f.padding, ok = read_u16(&r); if !ok { return model, .UnexpectedEOF }
f.two_sided, ok = read_i32(&r); if !ok { return model, .UnexpectedEOF }
f.smooth_group, ok = read_i32(&r); if !ok { return model, .UnexpectedEOF }
if version_ge(model.version, 2, 2) {
extra_count := (int(f.length) - 24)
if extra_count > 0 {
for _ in 0..<extra_count/4 {
sg, ok = read_i32(&r); if !ok { return model, .UnexpectedEOF }
append(&f.smooth_group_extra, sg)
}
}
}
append(&node.faces, f)
}
if version_ge(model.version, 1, 6) {
s_count, ok = read_u32(&r); if !ok { return model, .UnexpectedEOF }
for _ in 0..<int(s_count) {
sk := RSM_ScaleKeyframe{}
sk.frame, ok = read_i32(&r); if !ok { return model, .UnexpectedEOF }
sk.scale, ok = read_vec3(&r); if !ok { return model, .UnexpectedEOF }
sk._reserved, ok = read_f32(&r); if !ok { return model, .UnexpectedEOF }
append(&node.scale_keyframes, sk)
}
}
r_count, ok = read_u32(&r); if !ok { return model, .UnexpectedEOF }
for _ in 0..<int(r_count) {
rk := RSM_RotationKeyframe{}
rk.frame, ok = read_i32(&r); if !ok { return model, .UnexpectedEOF }
for i in 0..<4 { rk.quaternion[i], ok = read_f32(&r); if !ok { return model, .UnexpectedEOF } }
append(&node.rotation_keyframes, rk)
}
if version_ge(model.version, 2, 2) {
t_count, ok = read_u32(&r); if !ok { return model, .UnexpectedEOF }
for _ in 0..<int(t_count) {
tk := RSM_TranslationKeyframe{}
tk.frame, ok = read_i32(&r); if !ok { return model, .UnexpectedEOF }
tk.translation, ok = read_vec3(&r); if !ok { return model, .UnexpectedEOF }
tk._reserved, ok = read_f32(&r); if !ok { return model, .UnexpectedEOF }
append(&node.translation_keyframes, tk)
}
}
if version_ge(model.version, 2, 3) {
tk_count, ok = read_u32(&r); if !ok { return model, .UnexpectedEOF }
for _ in 0..<int(tk_count) {
tks := RSM_TexturesKeyframe{}
tks.texture_index, ok = read_u32(&r); if !ok { return model, .UnexpectedEOF }
key_count, ok = read_u32(&r); if !ok { return model, .UnexpectedEOF }
for _ in 0..<int(key_count) {
tk := RSM_TextureKeyframe{}
tk.operation_type, ok = read_u32(&r); if !ok { return model, .UnexpectedEOF }
frame_count, ok = read_u32(&r); if !ok { return model, .UnexpectedEOF }
for _ in 0..<int(frame_count) {
tf := RSM_TextureFrame{}
tf.frame, ok = read_i32(&r); if !ok { return model, .UnexpectedEOF }
tf.operation_value, ok = read_f32(&r); if !ok { return model, .UnexpectedEOF }
append(&tk.texture_frames, tf)
}
append(&tks.texture_keyframes, tk)
}
append(&node.textures_keyframes, tks)
}
}
append(&model.nodes, node)
}
return model, .None
}

View file

@ -1,875 +0,0 @@
package gndview
import sdl "vendor:sdl3"
import "core:os"
import "core:strings"
import "core:path/filepath"
import "core:fmt"
import stbi "vendor:stb/image"
import "core:time"
import "core:mem"
import "core:math"
import "../format"
import "../shared"
import "core:math/linalg"
import "core:c"
TextureHolder :: struct {
texture: ^sdl.GPUTexture,
pixels: [^]byte,
transferBuffer: ^sdl.GPUTransferBuffer,
w: i32,
h: i32,
path: string,
}
RsmNode :: struct {
start: int,
end: int,
texture: int,
node: ^format.RSM_Node,
textures: []sdl.GPUTexture,
modelIndex: int,
vbufIndex: int,
ibufIndex: int,
transform: matrix[4,4]f32,
}
GndNode :: struct {
start: int,
end: int,
texture: int,
node: ^format.GND_Tile,
textures: []sdl.GPUTexture,
}
Push_Constants :: struct {
mvp: matrix[4,4]f32,
color: [4]f32,
}
loadTexture :: proc(path: string, textures: ^[dynamic]TextureHolder) -> int {
for i := 0; i < len(textures); i+=1 {
if textures[i].path == path {
return i
}
}
index := len(textures)
c_path := strings.clone_to_cstring(path)
defer delete(c_path)
img_size: [2]i32
pixels := stbi.load(c_path, &img_size.x, &img_size.y, nil, 4);
assert(pixels != nil)
append(textures, TextureHolder{
pixels = pixels,
w = img_size.x,
h = img_size.y,
path = path,
})
return index
}
uploadTextures :: proc(device: ^sdl.GPUDevice, textures: ^[dynamic]TextureHolder) {
setup_cmd := sdl.AcquireGPUCommandBuffer(device)
if setup_cmd == nil {
fmt.println("AcquireGPUCommandBuffer failed:", sdl.GetError())
return
}
fmt.println("copying textures to transfer buffer")
for &texture in textures {
texture.texture = sdl.CreateGPUTexture(device, sdl.GPUTextureCreateInfo{
format = .R8G8B8A8_UNORM,
usage = {.SAMPLER},
width = u32(texture.w),
height = u32(texture.h),
layer_count_or_depth = 1,
num_levels = 1,
}); assert(texture.texture != nil)
tb_tex := sdl.CreateGPUTransferBuffer(device, sdl.GPUTransferBufferCreateInfo{
usage = .UPLOAD,
size = u32(texture.w * texture.h * 4),
props = 0,
}); assert(tb_tex != nil)
tex_mapped := sdl.MapGPUTransferBuffer(device, tb_tex, false); assert(tex_mapped != nil)
mem.copy_non_overlapping(tex_mapped, texture.pixels, int(texture.w * texture.h * 4))
sdl.UnmapGPUTransferBuffer(device, tb_tex)
texture.transferBuffer = tb_tex
}
cp := sdl.BeginGPUCopyPass(setup_cmd)
if cp == nil {
fmt.println("BeginGPUCopyPass failed:", sdl.GetError())
return
}
for &texture in textures {
fmt.println("transferring textures")
fmt.println("tex ptr:", texture, "w/h:", texture.w, texture.h)
sdl.UploadToGPUTexture(cp,
{
transfer_buffer = texture.transferBuffer,
offset = 0,
pixels_per_row = u32(texture.w),
rows_per_layer = u32(texture.h),
},
{texture = texture.texture, w = u32(texture.w), h = u32(texture.h), d = 1},
false,
)
sdl.ReleaseGPUTransferBuffer(device, texture.transferBuffer)
fmt.println("transferred textures")
}
sdl.EndGPUCopyPass(cp)
if !sdl.SubmitGPUCommandBuffer(setup_cmd) {
fmt.println("SubmitGPUCommandBuffer (setup) failed:", sdl.GetError())
return
}
}
loadGndModel :: proc(device: ^sdl.GPUDevice, model: ^format.GND_Data, vertices: ^[dynamic]shared.Vertex, indices: ^[dynamic]u32, nodes: ^[dynamic]GndNode, textures: ^[dynamic]TextureHolder) -> (vbuf: ^sdl.GPUBuffer, ibuf: ^sdl.GPUBuffer) {
clear(vertices)
clear(indices)
clear(nodes)
fmt.println("version ", model.info.version)
textureMap : [dynamic]int
defer delete(textureMap)
fmt.printfln("loading model texture %v", model.textures)
for textureName in model.textures {
textureName, _ := strings.replace_all(textureName, "\\", "/")
full_path, err := filepath.join({"/home/pavel/neoragnarok_backup/kro_client/data/texture", textureName}); assert(err == nil)
full_path, err = filepath.clean(full_path); assert(err == nil)
append(&textureMap, loadTexture(full_path, textures))
}
width := model.info.width
height := model.info.height
for node_idx := 0; node_idx < len(model.tiles); node_idx+= 1 {
x := i32(node_idx) % width
y := i32(node_idx) / width
node := &model.tiles[node_idx]
// fmt.println("h_ne", node.h_ne)
// fmt.println("h_nw", node.h_nw)
// fmt.println("h_se", node.h_se)
// fmt.println("h_sw", node.h_sw)
// fmt.println("east_surface_index", node.east_surface_index)
// fmt.println("north_surface_index", node.north_surface_index)
// fmt.println("top_surface_index", node.top_surface_index)
surface := model.surfaces[0]
if node.top_surface_index != -1 {
surface = model.surfaces[node.top_surface_index]
}
fmt.println(surface.u, surface.v)
n := GndNode{
start = len(indices),
node = node,
texture = textureMap[0],
}
if surface.texture_index != -1 {
n.texture = textureMap[surface.texture_index]
}
// fmt.println("top_surface_index", surface.u)
offset := len(vertices)
pos := shared.Vec2{f32(x) - f32(model.info.width)/2.0, f32(y) - f32(model.info.height)/2.0}
// node.h_ne = 0
// node.h_nw = 0
// node.h_se = 0
// node.h_sw = 0
append(vertices, shared.Vertex{
pos = {pos.x * model.info.zoom,-node.h_nw, -pos.y * model.info.zoom},
uv = {surface.u[2], surface.v[2]},
})
append(vertices, shared.Vertex{
pos = {(pos.x+1)* model.info.zoom,-node.h_ne,-pos.y* model.info.zoom},
uv = {surface.u[3], surface.v[3]},
})
append(vertices, shared.Vertex{
pos = {pos.x* model.info.zoom,-node.h_sw,-(pos.y+1)* model.info.zoom},
uv = {surface.u[1], surface.v[1]},
})
append(vertices, shared.Vertex{
pos = {(pos.x+1)* model.info.zoom,-node.h_se,-(pos.y+1)* model.info.zoom},
uv = {surface.u[0], surface.v[0]},
})
append(indices, u32(offset))
append(indices, u32(offset+1))
append(indices, u32(offset+2))
append(indices, u32(offset+1))
append(indices, u32(offset+3))
append(indices, u32(offset+2))
n.end = len(indices)
append(nodes, n)
}
vbuf_info := sdl.GPUBufferCreateInfo{
usage = sdl.GPUBufferUsageFlags{.VERTEX},
size = u32(len(vertices) * size_of(vertices[0])),
props = 0,
}
vbuf = sdl.CreateGPUBuffer(device, vbuf_info)
if vbuf == nil {
fmt.println("CreateGPUBuffer failed:", sdl.GetError())
return
}
ibuf_info := sdl.GPUBufferCreateInfo{
usage = sdl.GPUBufferUsageFlags{.INDEX},
size = u32(len(indices) * size_of(indices[0])),
props = 0,
}
ibuf = sdl.CreateGPUBuffer(device, ibuf_info)
if ibuf == nil {
fmt.println("CreateGPUBuffer failed:", sdl.GetError())
return
}
{
setup_cmd := sdl.AcquireGPUCommandBuffer(device)
if setup_cmd == nil {
fmt.println("AcquireGPUCommandBuffer failed:", sdl.GetError())
return
}
vert_byte_count := len(vertices) * size_of(vertices[0])
ind_byte_count := len(indices) * size_of(indices[0])
tb_info := sdl.GPUTransferBufferCreateInfo{
usage = .UPLOAD,
size = u32(vert_byte_count + ind_byte_count),
props = 0,
}
tb := sdl.CreateGPUTransferBuffer(device, tb_info)
if tb == nil {
fmt.println("CreateGPUTransferBuffer failed:", sdl.GetError())
return
}
defer sdl.ReleaseGPUTransferBuffer(device, tb)
mapped := transmute([^]byte)sdl.MapGPUTransferBuffer(device, tb, false)
if mapped == nil {
fmt.println("MapGPUTransferBuffer failed:", sdl.GetError())
return
}
mem.copy_non_overlapping(mapped, raw_data(vertices[:]), vert_byte_count)
mem.copy_non_overlapping(mapped[vert_byte_count:], raw_data(indices[:]), ind_byte_count)
sdl.UnmapGPUTransferBuffer(device, tb)
cp := sdl.BeginGPUCopyPass(setup_cmd)
if cp == nil {
fmt.println("BeginGPUCopyPass failed:", sdl.GetError())
return
}
vert_src := sdl.GPUTransferBufferLocation{transfer_buffer = tb, offset = 0}
vert_dst := sdl.GPUBufferRegion{buffer = vbuf, offset = 0, size = u32(vert_byte_count)}
sdl.UploadToGPUBuffer(cp, vert_src, vert_dst, false)
ind_src := sdl.GPUTransferBufferLocation{transfer_buffer = tb, offset = u32(vert_byte_count)}
ind_dst := sdl.GPUBufferRegion{buffer = ibuf, offset = 0, size = u32(ind_byte_count)}
sdl.UploadToGPUBuffer(cp, ind_src, ind_dst, false)
sdl.EndGPUCopyPass(cp)
if !sdl.SubmitGPUCommandBuffer(setup_cmd) {
fmt.println("SubmitGPUCommandBuffer (setup) failed:", sdl.GetError())
return
}
}
return vbuf, ibuf
}
walk :: proc(dir: string, models: ^[dynamic]format.GND_Data) {
f, _ := os.open(dir)
entries, ok := os.read_dir(f, 0, context.allocator)
for entry in entries {
path, _ := filepath.join({dir, entry.name})
if entry.type == .Directory {
walk(path, models)
}
if strings.contains(entry.name, ".gnd") {
data, _ := os.read_entire_file(path, context.allocator)
parsed, err := format.parse_gnd(data)
if err != nil {
fmt.printfln("%v", err)
} else {
append_elem(models, parsed)
}
}
}
}
loadRsw :: proc() -> format.RSW_Data {
prontera := "/home/pavel/neoragnarok_backup/kro_client/data/aldebaran.rsw"
data, _ := os.read_entire_file(prontera, context.allocator)
parsed, err := format.parse_rsw(data)
if err != nil {
fmt.printfln("%v", err)
panic("aaa")
}
return parsed
}
loadRsmModel :: proc(device: ^sdl.GPUDevice, model: ^format.RSM_Model, modelIndex: int, nodes: ^[dynamic]RsmNode, textures: ^[dynamic]TextureHolder, vbufs: ^[dynamic]^sdl.GPUBuffer, ibufs: ^[dynamic]^sdl.GPUBuffer, transform: matrix[4,4]f32) {
vertices: [dynamic]shared.Vertex
indices: [dynamic]u16
vbufIndex := len(vbufs)
ibufIndex := len(ibufs)
fmt.printfln("version %v", model.version)
textureMap : [dynamic]int
defer delete(textureMap)
fmt.printfln("loading model texture %v", model.texture_names[:])
for textureName in model.texture_names {
textureName, _ := strings.replace_all(textureName, "\\", "/")
full_path, err := filepath.join({"/home/pavel/neoragnarok_backup/kro_client/data/texture", textureName}); assert(err == nil)
full_path, err = filepath.clean(full_path); assert(err == nil)
append(&textureMap, loadTexture(full_path, textures))
}
for node_idx := 0; node_idx < len(model.nodes); node_idx+= 1 {
textureOffset := (0)
node := &model.nodes[node_idx]
fmt.println("offset_matrix", node.offset_matrix)
fmt.println("node.translation1", node.translation1)
fmt.println("node.translation2", node.translation2)
fmt.println("node.scale", node.scale)
fmt.println("node.rotation_angle", node.rotation_angle)
fmt.println("node.rotation_axis", node.rotation_axis)
// fmt.println("node.scale_keyframes[:]", node.scale_keyframes[:])
// fmt.println("node.texture_names[:]", node.texture_names[:])
if model.version.major >= 2 && model.version.minor > 2 {
textureOffset = (len(textures))
}
for textureName in node.texture_names {
fmt.printfln("loading texture %s", textureName)
full_path, err := filepath.join({"/home/pavel/neoragnarok_backup/kro_client/data/texture", textureName}); assert(err == nil)
full_path, err = filepath.clean(full_path); assert(err == nil)
full_path, _ = strings.replace_all(full_path, "\\", "/")
append(&textureMap, loadTexture(full_path, textures))
}
for face in node.faces {
n := RsmNode{
start = len(indices),
node = node,
texture = textureMap[int(face.texture_index) + textureOffset],
modelIndex = modelIndex,
vbufIndex = vbufIndex,
ibufIndex = ibufIndex,
transform = transform,
}
for i in 0..<3 {
v_idx := face.vertex_position_indices[i]
t_idx := face.texture_coordinate_indices[i]
pos := node.vertex_positions[v_idx]
uv := node.texture_coordinates[t_idx].coordinates
append(&vertices, shared.Vertex{
pos = pos,
uv = uv,
})
append(&indices, u16(len(vertices)-1))
}
n.end = len(indices)
append(nodes, n)
}
}
vbuf_info := sdl.GPUBufferCreateInfo{
usage = sdl.GPUBufferUsageFlags{.VERTEX},
size = u32(len(vertices) * size_of(vertices[0])),
props = 0,
}
vbuf := sdl.CreateGPUBuffer(device, vbuf_info); assert(vbuf != nil)
append(vbufs, vbuf)
ibuf_info := sdl.GPUBufferCreateInfo{
usage = sdl.GPUBufferUsageFlags{.INDEX},
size = u32(len(indices) * size_of(indices[0])),
props = 0,
}
ibuf := sdl.CreateGPUBuffer(device, ibuf_info); assert(ibuf != nil)
append(ibufs, ibuf)
{
setup_cmd := sdl.AcquireGPUCommandBuffer(device)
if setup_cmd == nil {
fmt.println("AcquireGPUCommandBuffer failed:", sdl.GetError())
return
}
vert_byte_count := len(vertices) * size_of(vertices[0])
ind_byte_count := len(indices) * size_of(indices[0])
tb_info := sdl.GPUTransferBufferCreateInfo{
usage = .UPLOAD,
size = u32(vert_byte_count + ind_byte_count),
props = 0,
}
tb := sdl.CreateGPUTransferBuffer(device, tb_info)
if tb == nil {
fmt.println("CreateGPUTransferBuffer failed:", sdl.GetError())
panic("fail")
}
defer sdl.ReleaseGPUTransferBuffer(device, tb)
mapped := transmute([^]byte)sdl.MapGPUTransferBuffer(device, tb, false)
if mapped == nil {
fmt.println("MapGPUTransferBuffer failed:", sdl.GetError())
panic("fail")
}
mem.copy_non_overlapping(mapped, raw_data(vertices[:]), vert_byte_count)
mem.copy_non_overlapping(mapped[vert_byte_count:], raw_data(indices[:]), ind_byte_count)
sdl.UnmapGPUTransferBuffer(device, tb)
cp := sdl.BeginGPUCopyPass(setup_cmd)
if cp == nil {
fmt.println("BeginGPUCopyPass failed:", sdl.GetError())
panic("fail")
}
vert_src := sdl.GPUTransferBufferLocation{transfer_buffer = tb, offset = 0}
vert_dst := sdl.GPUBufferRegion{buffer = vbuf, offset = 0, size = u32(vert_byte_count)}
sdl.UploadToGPUBuffer(cp, vert_src, vert_dst, false)
ind_src := sdl.GPUTransferBufferLocation{transfer_buffer = tb, offset = u32(vert_byte_count)}
ind_dst := sdl.GPUBufferRegion{buffer = ibuf, offset = 0, size = u32(ind_byte_count)}
sdl.UploadToGPUBuffer(cp, ind_src, ind_dst, false)
sdl.EndGPUCopyPass(cp)
if !sdl.SubmitGPUCommandBuffer(setup_cmd) {
fmt.println("SubmitGPUCommandBuffer (setup) failed:", sdl.GetError())
return
}
}
}
runGame :: proc(window: ^sdl.Window, device: ^sdl.GPUDevice, pipeline: ^sdl.GPUGraphicsPipeline) {
sampler := sdl.CreateGPUSampler(device, {})
modelIndex := 0
models: [dynamic]format.RSM_Model
rsw := loadRsw()
rsmVbufs: [dynamic]^sdl.GPUBuffer
rsmIbufs: [dynamic]^sdl.GPUBuffer
textures: [dynamic]TextureHolder
defer for texture in textures do sdl.ReleaseGPUTexture(device, texture.texture)
rsmNodes: [dynamic]RsmNode
for object in rsw.objects {
modelPath, err1 := filepath.join({"/home/pavel/neoragnarok_backup/kro_client/data/model", object.model_name}); assert(err1 ==nil)
fmt.println(modelPath)
data, err := os.read_entire_file(modelPath, context.allocator)
if err != nil {
fmt.println(err)
panic("failed to read rsm")
}
fmt.println(object.model_name)
parsed, err2 := format.parse_rsm(data)
if err2 != nil {
fmt.println(err2)
panic("failed to parse rsm")
}
modelIndex := len(models)
append_elem(&models, parsed)
T := linalg.matrix4_translate(object.transform.position)
S := linalg.matrix4_scale(object.transform.scale)
Rx := linalg.matrix4_rotate_f32(object.transform.rotation_deg[0], {1,0,0})
Ry := linalg.matrix4_rotate_f32(object.transform.rotation_deg[1], {0,1,0})
Rz := linalg.matrix4_rotate_f32(object.transform.rotation_deg[2], {0,0,1})
R := Rz * Ry * Rx
M := T * R * S
loadRsmModel(device, &parsed, modelIndex, &rsmNodes, &textures, &rsmVbufs, &rsmIbufs, M)
}
gnd, err1 := filepath.join({"/home/pavel/neoragnarok_backup/kro_client/data", rsw.ground_file}); assert(err1 ==nil)
data, _ := os.read_entire_file(gnd, context.allocator)
gndModel, err := format.parse_gnd(data); assert(err == nil)
gndVertices: [dynamic]shared.Vertex
gndIndices: [dynamic]u32
gndNodes: [dynamic]GndNode
gndVbuf, gndIbuf := loadGndModel(device, &gndModel, &gndVertices, &gndIndices, &gndNodes, &textures)
defer sdl.ReleaseGPUBuffer(device, gndIbuf)
defer sdl.ReleaseGPUBuffer(device, gndVbuf)
uploadTextures(device, &textures)
init_yaw := shared.rotor_from_axis_angle(shared.Vec3{0, 1, 0}, 0.9)
init_right := shared.rotate_vec3(init_yaw, shared.Vec3{1, 0, 0})
init_pitch := shared.rotor_from_axis_angle(init_right, -0.45)
camera := shared.Camera{
center = shared.Vec3{0, 0.5, 0},
distance = 8,
orientation = shared.rotor_normalize(shared.rotor_mul(init_pitch, init_yaw)),
fov_deg = 60,
}
start := time.tick_now()
last := start
left_down := false
middle_down := false
running := true
depthTex : ^sdl.GPUTexture
dti : sdl.GPUDepthStencilTargetInfo
for running {
now := time.tick_now()
elapsed_ms := time.duration_milliseconds(time.tick_diff(start, now))
dt_ms := time.duration_milliseconds(time.tick_diff(last, now))
dt := f32(time.duration_seconds(time.tick_diff(last, now)))
last = now
event: sdl.Event
for sdl.PollEvent(&event) {
#partial switch event.type {
case .QUIT:
running = false
case .MOUSE_BUTTON_DOWN:
switch event.button.button {
case sdl.BUTTON_LEFT:
left_down = true
case sdl.BUTTON_MIDDLE:
middle_down = true
}
case .MOUSE_BUTTON_UP:
switch event.button.button {
case sdl.BUTTON_LEFT:
left_down = false
case sdl.BUTTON_MIDDLE:
middle_down = false
}
case .MOUSE_WHEEL:
camera.distance = math.clamp(camera.distance - event.wheel.y * 0.7, 1.5, 80.0)
// case .KEY_DOWN:
// if event.key.key == sdl.K_ESCAPE {
// running = false
// } else if event.key.key == sdl.K_N {
// modelIndex += 1
// sdl.ReleaseGPUBuffer(device, gndIbuf)
// sdl.ReleaseGPUBuffer(device, gndVbuf)
// for texture in gndTextures do sdl.ReleaseGPUTexture(device, texture.texture)
// gndVbuf, gndIbuf, gndTextures = loadGndModel(device, &gndModel, &gndVertices, &gndIndices, &gndNodes)
// }
}
}
x_rel: f32 = 0
y_rel: f32 = 0
_ = sdl.GetRelativeMouseState(&x_rel, &y_rel)
if left_down {
yaw_r := shared.rotor_from_axis_angle(shared.Vec3{0, 1, 0}, -x_rel * 0.006)
q1 := shared.rotor_normalize(shared.rotor_mul(yaw_r, camera.orientation))
right_axis := shared.vec3_normalize(shared.rotate_vec3(q1, shared.Vec3{1, 0, 0}))
pitch_r := shared.rotor_from_axis_angle(right_axis, -y_rel * 0.006)
q2 := shared.rotor_normalize(shared.rotor_mul(pitch_r, q1))
fwd2 := shared.camera_forward(shared.Camera{center = camera.center, distance = camera.distance, orientation = q2, fov_deg = camera.fov_deg})
if math.abs(shared.vec3_dot(fwd2, shared.Vec3{0, 1, 0})) < 0.98 {
camera.orientation = q2
} else {
camera.orientation = q1
}
}
if middle_down {
right := shared.camera_right(camera)
up := shared.camera_up(camera)
pan_speed := 0.008 * camera.distance
camera.center = camera.center + right * -x_rel * pan_speed
camera.center = camera.center + up * y_rel * pan_speed
}
keys := sdl.GetKeyboardState(nil)
forward := shared.camera_forward(camera)
right := shared.camera_right(camera)
up := shared.camera_up(camera)
move_speed := camera.distance * dt * 1.4
if keys[sdl.Scancode.W] {
camera.center = camera.center + forward * move_speed
}
if keys[sdl.Scancode.S] {
camera.center = camera.center + forward * -move_speed
}
if keys[sdl.Scancode.A] {
camera.center = camera.center + right * -move_speed
}
if keys[sdl.Scancode.D] {
camera.center = camera.center + right * move_speed
}
w: c.int = 0
h: c.int = 0
sdl.GetWindowSize(window, &w, &h)
if w <= 0 || h <= 0 {
continue
}
cmd := sdl.AcquireGPUCommandBuffer(device)
if cmd == nil {
fmt.println("AcquireGPUCommandBuffer failed:", sdl.GetError())
break
}
swap_tex: ^sdl.GPUTexture
swap_w: sdl.Uint32 = 0
swap_h: sdl.Uint32 = 0
if !sdl.WaitAndAcquireGPUSwapchainTexture(cmd, window, &swap_tex, &swap_w, &swap_h) {
fmt.println("WaitAndAcquireGPUSwapchainTexture failed:", sdl.GetError())
_ = sdl.CancelGPUCommandBuffer(cmd)
break
}
if swap_tex == nil {
if !sdl.SubmitGPUCommandBuffer(cmd) {
fmt.println("SubmitGPUCommandBuffer failed:", sdl.GetError())
break
}
continue
}
if depthTex == nil {
depthTex = sdl.CreateGPUTexture(device, {
format = .D32_FLOAT,
usage = {.DEPTH_STENCIL_TARGET},
width = swap_w,
height = swap_h,
layer_count_or_depth = 1,
num_levels = 1,
})
dti = sdl.GPUDepthStencilTargetInfo{
texture = depthTex,
clear_depth = 1.0,
load_op = .CLEAR,
store_op = .STORE,
}
}
clear := sdl.FColor{0.08, 0.09, 0.12, 1.0}
cti := sdl.GPUColorTargetInfo{
texture = swap_tex,
mip_level = 0,
layer_or_depth_plane = 0,
clear_color = clear,
load_op = .CLEAR,
store_op = .STORE,
resolve_texture = nil,
resolve_mip_level = 0,
resolve_layer = 0,
cycle = false,
cycle_resolve_texture = false,
}
rp := sdl.BeginGPURenderPass(cmd, &cti, 1, &dti)
if rp == nil {
fmt.println("BeginGPURenderPass failed:", sdl.GetError())
_ = sdl.CancelGPUCommandBuffer(cmd)
break
}
vp := sdl.GPUViewport{x = 0, y = 0, w = f32(swap_w), h = f32(swap_h), min_depth = 0, max_depth = 1}
sdl.SetGPUViewport(rp, vp)
sdl.BindGPUGraphicsPipeline(rp, pipeline)
vb_binding := sdl.GPUBufferBinding{buffer = gndVbuf, offset = 0}
sdl.BindGPUVertexBuffers(rp, 0, &vb_binding, 1)
ib_binding := sdl.GPUBufferBinding{buffer = gndIbuf, offset = 0}
sdl.BindGPUIndexBuffer(rp, ib_binding, ._32BIT)
aspect := f32(swap_w) / f32(swap_h)
proj := linalg.matrix4_perspective(linalg.to_radians(camera.fov_deg), aspect, 0.5, 2000.0)
view := linalg.matrix4_look_at(shared.camera_position(camera), camera.center, shared.camera_up(camera))
vp_mat := proj * view
for mesh in gndNodes {
node := mesh.node
pc := Push_Constants{}
pc.mvp = vp_mat * linalg.MATRIX4F32_IDENTITY
pc.color = [4]f32{1, 0, 0, 1.0}
sdl.BindGPUFragmentSamplers(rp, 0, &(sdl.GPUTextureSamplerBinding{
texture = textures[mesh.texture].texture,
sampler = sampler,
}), 1)
sdl.PushGPUVertexUniformData(cmd, 0, &pc, u32(size_of(Push_Constants)))
sdl.DrawGPUIndexedPrimitives(rp, u32(mesh.end-mesh.start), 1, u32(mesh.start), 0, 0)
}
renderModel(&models, elapsed_ms, rsmNodes, &textures, sampler, cmd, vp_mat, rp, &rsmVbufs, &rsmIbufs)
sdl.EndGPURenderPass(rp)
if !sdl.SubmitGPUCommandBuffer(cmd) {
fmt.println("SubmitGPUCommandBuffer failed:", sdl.GetError())
break
}
}
}
renderModel :: proc(models: ^[dynamic]format.RSM_Model, elapsed_ms: f64, nodes: [dynamic]RsmNode, textures: ^[dynamic]TextureHolder, sampler: ^sdl.GPUSampler, cmd: ^sdl.GPUCommandBuffer, vp_mat: matrix[4,4]f32, rp: ^sdl.GPURenderPass, vbufs: ^[dynamic]^sdl.GPUBuffer, ibufs: ^[dynamic]^sdl.GPUBuffer) {
for mesh in nodes {
vb_binding := sdl.GPUBufferBinding{buffer = vbufs[mesh.vbufIndex], offset = 0}
sdl.BindGPUVertexBuffers(rp, 0, &vb_binding, 1)
ib_binding := sdl.GPUBufferBinding{buffer = ibufs[mesh.ibufIndex], offset = 0}
sdl.BindGPUIndexBuffer(rp, ib_binding, ._16BIT)
model := &models[mesh.modelIndex]
animation_length := model.animation_length
fps := model.frames_per_second
frame_delay : f32
total_frames : u32
if fps == 0 {
total_seconds := f32(animation_length) / 1000
total_frames = u32(total_seconds * 30)
frame_delay = 1000 / f32(30)
} else {
total_frames = animation_length
frame_delay = 1000 / fps
}
current_frame := u32(f32(elapsed_ms) / frame_delay) % total_frames
node := mesh.node
rotation := linalg.quaternion_angle_axis(node.rotation_angle, node.rotation_axis)
if len(node.rotation_keyframes) > 0 {
keyframe_idx := 0
for i := 0; i < len(node.rotation_keyframes); i+= 1 {
keyframe := node.rotation_keyframes[i]
if u32(keyframe.frame) <= current_frame {
keyframe_idx = i
} else do break
}
q := node.rotation_keyframes[keyframe_idx].quaternion
rotation = quaternion(imag=q[0], jmag=q[1], kmag=q[2], real=q[3])
}
rotation = linalg.quaternion_normalize(rotation)
scale := node.scale
if len(node.scale_keyframes) > 0 {
keyframe_idx := 0
for i := 0; i < len(node.scale_keyframes); i+= 1 {
keyframe := node.scale_keyframes[i]
if u32(keyframe.frame) <= current_frame {
keyframe_idx = i
} else do break
}
scale = node.scale_keyframes[keyframe_idx].scale
}
translation := [3]f32{0,0,0}
if(len(node.translation_keyframes) > 0) {
keyframe_idx := 0
for i := 0; i < len(node.translation_keyframes); i+= 1 {
keyframe := node.translation_keyframes[i]
if u32(keyframe.frame) <= current_frame {
keyframe_idx = i
} else do break
}
translation = node.translation_keyframes[keyframe_idx].translation
}
r := linalg.matrix3_from_quaternion(rotation)
s := matrix[3,3]f32{
scale[0], 0, 0 ,
0, scale[1], 0 ,
0, 0, scale[2],
}
a3 := r * node.offset_matrix * s
t := node.translation1 + node.translation2 + translation
// 4x4 affine matrix
model_mat := matrix[4,4]f32{
a3[0][0], a3[0][1], a3[0][2], t[0],
a3[1][0], a3[1][1], a3[1][2], t[1],
a3[2][0], a3[2][1], a3[2][2], t[2],
0, 0, 0, 1 ,
}
pc := Push_Constants{}
pc.mvp = vp_mat * mesh.transform * model_mat
pc.color = [4]f32{1, 0, 0, 1.0}
sdl.BindGPUFragmentSamplers(rp, 0, &(sdl.GPUTextureSamplerBinding{
texture = textures[mesh.texture].texture,
sampler = sampler,
}), 1)
sdl.PushGPUVertexUniformData(cmd, 0, &pc, u32(size_of(Push_Constants)))
sdl.DrawGPUIndexedPrimitives(rp, u32(mesh.end-mesh.start), 1, u32(mesh.start), 0, 0)
}
}

View file

@ -1,12 +1,201 @@
package main
import "core:c"
import "core:fmt"
import "core:math"
import "core:mem"
import "core:os"
import sdl "vendor:sdl3"
import "rsmview"
import "gndview"
import "shared"
import "core:time"
import sdl "vendor:sdl3"
Vec3 :: [3]f32
Mat4 :: [16]f32 // column-major
Camera :: struct {
center: Vec3,
distance: f32,
orientation: Rotor,
fov_deg: f32,
}
Debug_State :: struct {
enabled: bool,
accum: f32,
}
Rotor :: struct {
s, x, y, z: f32,
}
Cube_Instance :: struct {
pos: Vec3,
scale: f32,
color: Vec3,
}
Vertex :: struct {
pos: Vec3,
}
Push_Constants :: struct {
mvp: Mat4,
color: [4]f32,
}
mat4_identity :: proc() -> Mat4 {
return Mat4{1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1}
}
mat4_mul :: proc(a, b: Mat4) -> Mat4 {
r := Mat4{}
for c in 0 ..< 4 {
for row in 0 ..< 4 {
r[c*4+row] =
a[0*4+row] * b[c*4+0] +
a[1*4+row] * b[c*4+1] +
a[2*4+row] * b[c*4+2] +
a[3*4+row] * b[c*4+3]
}
}
return r
}
vec3_sub :: proc(a, b: Vec3) -> Vec3 {
return Vec3{a[0] - b[0], a[1] - b[1], a[2] - b[2]}
}
vec3_add :: proc(a, b: Vec3) -> Vec3 {
return Vec3{a[0] + b[0], a[1] + b[1], a[2] + b[2]}
}
vec3_scale :: proc(v: Vec3, s: f32) -> Vec3 {
return Vec3{v[0] * s, v[1] * s, v[2] * s}
}
vec3_dot :: proc(a, b: Vec3) -> f32 {
return a[0] * b[0] + a[1] * b[1] + a[2] * b[2]
}
vec3_cross :: proc(a, b: Vec3) -> Vec3 {
return Vec3{
a[1] * b[2] - a[2] * b[1],
a[2] * b[0] - a[0] * b[2],
a[0] * b[1] - a[1] * b[0],
}
}
vec3_normalize :: proc(v: Vec3) -> Vec3 {
len2 := vec3_dot(v, v)
if len2 <= 0.000001 {
return Vec3{0, 0, 0}
}
inv := 1.0 / math.sqrt(len2)
return vec3_scale(v, inv)
}
rotor_identity :: proc() -> Rotor {
return Rotor{1, 0, 0, 0}
}
rotor_normalize :: proc(r: Rotor) -> Rotor {
len2 := r.s*r.s + r.x*r.x + r.y*r.y + r.z*r.z
if len2 <= 0.000001 {
return rotor_identity()
}
inv := 1.0 / math.sqrt(len2)
return Rotor{r.s * inv, r.x * inv, r.y * inv, r.z * inv}
}
rotor_mul :: proc(a, b: Rotor) -> Rotor {
return Rotor{
a.s*b.s - a.x*b.x - a.y*b.y - a.z*b.z,
a.s*b.x + a.x*b.s + a.y*b.z - a.z*b.y,
a.s*b.y - a.x*b.z + a.y*b.s + a.z*b.x,
a.s*b.z + a.x*b.y - a.y*b.x + a.z*b.s,
}
}
rotor_from_axis_angle :: proc(axis: Vec3, angle: f32) -> Rotor {
a := vec3_normalize(axis)
h := angle * 0.5
c := math.cos(h)
s := math.sin(h)
return Rotor{c, a[0] * s, a[1] * s, a[2] * s}
}
rotate_vec3 :: proc(r: Rotor, v: Vec3) -> Vec3 {
u := Vec3{r.x, r.y, r.z}
t := vec3_scale(vec3_cross(u, v), 2.0)
return vec3_add(v, vec3_add(vec3_scale(t, r.s), vec3_cross(u, t)))
}
mat4_translate :: proc(p: Vec3) -> Mat4 {
m := mat4_identity()
m[12] = p[0]
m[13] = p[1]
m[14] = p[2]
return m
}
mat4_scale_uniform :: proc(s: f32) -> Mat4 {
m := mat4_identity()
m[0] = s
m[5] = s
m[10] = s
return m
}
mat4_perspective :: proc(fov_deg, aspect, z_near, z_far: f32) -> Mat4 {
f := 1.0 / math.tan((fov_deg * 0.5) * (math.PI / 180.0))
m := Mat4{}
m[0] = f / aspect
m[5] = f
m[10] = (z_far + z_near) / (z_near - z_far)
m[11] = -1
m[14] = (2.0 * z_far * z_near) / (z_near - z_far)
return m
}
mat4_look_at :: proc(eye, target, up: Vec3) -> Mat4 {
fwd := vec3_normalize(vec3_sub(target, eye))
right := vec3_normalize(vec3_cross(fwd, up))
real_up := vec3_cross(right, fwd)
m := mat4_identity()
m[0] = right[0]
m[1] = real_up[0]
m[2] = -fwd[0]
m[4] = right[1]
m[5] = real_up[1]
m[6] = -fwd[1]
m[8] = right[2]
m[9] = real_up[2]
m[10] = -fwd[2]
m[12] = -vec3_dot(right, eye)
m[13] = -vec3_dot(real_up, eye)
m[14] = vec3_dot(fwd, eye)
return m
}
camera_forward :: proc(c: Camera) -> Vec3 {
return vec3_normalize(rotate_vec3(c.orientation, Vec3{0, 0, -1}))
}
camera_right :: proc(c: Camera) -> Vec3 {
return vec3_normalize(rotate_vec3(c.orientation, Vec3{1, 0, 0}))
}
camera_up :: proc(c: Camera) -> Vec3 {
return vec3_normalize(rotate_vec3(c.orientation, Vec3{0, 1, 0}))
}
camera_position :: proc(c: Camera) -> Vec3 {
return vec3_sub(c.center, vec3_scale(camera_forward(c), c.distance))
}
read_file_or_fail :: proc(path: string) -> []u8 {
file_bytes, err := os.read_entire_file(path, context.allocator)
@ -17,7 +206,7 @@ read_file_or_fail :: proc(path: string) -> []u8 {
return file_bytes
}
create_gpu_shader :: proc(device: ^sdl.GPUDevice, path: string, stage: sdl.GPUShaderStage, num_samplers: u32) -> ^sdl.GPUShader {
create_gpu_shader :: proc(device: ^sdl.GPUDevice, path: string, stage: sdl.GPUShaderStage) -> ^sdl.GPUShader {
bytes := read_file_or_fail(path)
if len(bytes) == 0 {
return nil
@ -29,7 +218,7 @@ create_gpu_shader :: proc(device: ^sdl.GPUDevice, path: string, stage: sdl.GPUSh
entrypoint = cstring("main"),
format = sdl.GPUShaderFormat{.SPIRV},
stage = stage,
num_samplers = num_samplers,
num_samplers = 0,
num_storage_textures = 0,
num_storage_buffers = 0,
num_uniform_buffers = 1,
@ -44,21 +233,39 @@ create_gpu_shader :: proc(device: ^sdl.GPUDevice, path: string, stage: sdl.GPUSh
return shader
}
copy_data_to_gpu :: proc(
device: ^sdl.GPUDevice,
cmd: ^sdl.GPUCommandBuffer,
vertices: []shared.Vertex,
vert_buf: ^sdl.GPUBuffer,
indices: []u16,
ind_buf: ^sdl.GPUBuffer
) -> bool {
return true
}
copy_vertices_to_gpu :: proc(device: ^sdl.GPUDevice, cmd: ^sdl.GPUCommandBuffer, vertices: []Vertex, dst: ^sdl.GPUBuffer) -> bool {
byte_count := len(vertices) * size_of(Vertex)
tb_info := sdl.GPUTransferBufferCreateInfo{
usage = .UPLOAD,
size = u32(byte_count),
props = 0,
}
tb := sdl.CreateGPUTransferBuffer(device, tb_info)
if tb == nil {
fmt.println("CreateGPUTransferBuffer failed:", sdl.GetError())
return false
}
defer sdl.ReleaseGPUTransferBuffer(device, tb)
GPU_Vertex :: struct {
position: [3]f32,
uv: [2]f32,
mapped := sdl.MapGPUTransferBuffer(device, tb, false)
if mapped == nil {
fmt.println("MapGPUTransferBuffer failed:", sdl.GetError())
return false
}
mem.copy_non_overlapping(mapped, raw_data(vertices), byte_count)
sdl.UnmapGPUTransferBuffer(device, tb)
cp := sdl.BeginGPUCopyPass(cmd)
if cp == nil {
fmt.println("BeginGPUCopyPass failed:", sdl.GetError())
return false
}
src := sdl.GPUTransferBufferLocation{transfer_buffer = tb, offset = 0}
dst_region := sdl.GPUBufferRegion{buffer = dst, offset = 0, size = u32(byte_count)}
sdl.UploadToGPUBuffer(cp, src, dst_region, false)
sdl.EndGPUCopyPass(cp)
return true
}
main :: proc() {
@ -95,13 +302,13 @@ main :: proc() {
return
}
vertex_shader := create_gpu_shader(device, "shaders/cubes.vert.spv", .VERTEX, 0)
vertex_shader := create_gpu_shader(device, "shaders/cubes.vert.spv", .VERTEX)
if vertex_shader == nil {
return
}
defer sdl.ReleaseGPUShader(device, vertex_shader)
fragment_shader := create_gpu_shader(device, "shaders/cubes.frag.spv", .FRAGMENT, 1)
fragment_shader := create_gpu_shader(device, "shaders/cubes.frag.spv", .FRAGMENT)
if fragment_shader == nil {
return
}
@ -113,12 +320,11 @@ main :: proc() {
return
}
vb_descs := []sdl.GPUVertexBufferDescription{
{slot = 0, pitch = size_of(shared.Vertex), input_rate = .VERTEX, instance_step_rate = 0},
vb_descs := [1]sdl.GPUVertexBufferDescription{
{slot = 0, pitch = size_of(Vertex), input_rate = .VERTEX, instance_step_rate = 0},
}
vattrs := []sdl.GPUVertexAttribute{
{location = 0, buffer_slot = 0, format = .FLOAT3, offset = u32(offset_of(shared.Vertex, pos))},
{location = 1, buffer_slot = 0, format = .FLOAT2, offset = u32(offset_of(shared.Vertex, uv))},
vattrs := [1]sdl.GPUVertexAttribute{
{location = 0, buffer_slot = 0, format = .FLOAT3, offset = 0},
}
blend := sdl.GPUColorTargetBlendState{
@ -136,15 +342,15 @@ main :: proc() {
{format = color_format, blend_state = blend},
}
pipeline_ci := sdl.GPUGraphicsPipelineCreateInfo{
vertex_shader = vertex_shader,
fragment_shader = fragment_shader,
vertex_input_state = sdl.GPUVertexInputState{
vertex_buffer_descriptions = &vb_descs[0],
num_vertex_buffers = 1,
vertex_attributes = &vattrs[0],
num_vertex_attributes = 2,
},
pipeline_ci := sdl.GPUGraphicsPipelineCreateInfo{
vertex_shader = vertex_shader,
fragment_shader = fragment_shader,
vertex_input_state = sdl.GPUVertexInputState{
vertex_buffer_descriptions = &vb_descs[0],
num_vertex_buffers = 1,
vertex_attributes = &vattrs[0],
num_vertex_attributes = 1,
},
primitive_type = .TRIANGLELIST,
rasterizer_state = sdl.GPURasterizerState{
fill_mode = .FILL,
@ -168,15 +374,15 @@ main :: proc() {
front_stencil_state = sdl.GPUStencilOpState{fail_op = .KEEP, pass_op = .KEEP, depth_fail_op = .KEEP, compare_op = .ALWAYS},
compare_mask = 0,
write_mask = 0,
enable_depth_test = true,
enable_depth_write = true,
enable_stencil_test = true,
enable_depth_test = false,
enable_depth_write = false,
enable_stencil_test = false,
},
target_info = sdl.GPUGraphicsPipelineTargetInfo{
color_target_descriptions = &color_targets[0],
num_color_targets = 1,
depth_stencil_format = .D32_FLOAT,
has_depth_stencil_target = true,
depth_stencil_format = .INVALID,
has_depth_stencil_target = false,
},
props = 0,
}
@ -188,16 +394,249 @@ main :: proc() {
}
defer sdl.ReleaseGPUGraphicsPipeline(device, pipeline)
pipeline_create_info := sdl.GPUGraphicsPipelineCreateInfo{
primitive_type = .TRIANGLELIST,
rasterizer_state = {
fill_mode = .LINE,
cull_mode = .FRONT,
vertices := [36]Vertex{
{pos = Vec3{-0.5, -0.5, -0.5}}, {pos = Vec3{0.5, 0.5, -0.5}}, {pos = Vec3{0.5, -0.5, -0.5}},
{pos = Vec3{-0.5, -0.5, -0.5}}, {pos = Vec3{-0.5, 0.5, -0.5}}, {pos = Vec3{0.5, 0.5, -0.5}},
{pos = Vec3{-0.5, -0.5, 0.5}}, {pos = Vec3{0.5, -0.5, 0.5}}, {pos = Vec3{0.5, 0.5, 0.5}},
{pos = Vec3{-0.5, -0.5, 0.5}}, {pos = Vec3{0.5, 0.5, 0.5}}, {pos = Vec3{-0.5, 0.5, 0.5}},
{pos = Vec3{-0.5, 0.5, 0.5}}, {pos = Vec3{-0.5, 0.5, -0.5}}, {pos = Vec3{-0.5, -0.5, -0.5}},
{pos = Vec3{-0.5, -0.5, -0.5}}, {pos = Vec3{-0.5, -0.5, 0.5}}, {pos = Vec3{-0.5, 0.5, 0.5}},
{pos = Vec3{0.5, 0.5, 0.5}}, {pos = Vec3{0.5, -0.5, -0.5}}, {pos = Vec3{0.5, 0.5, -0.5}},
{pos = Vec3{0.5, -0.5, -0.5}}, {pos = Vec3{0.5, 0.5, 0.5}}, {pos = Vec3{0.5, -0.5, 0.5}},
{pos = Vec3{-0.5, -0.5, -0.5}}, {pos = Vec3{0.5, -0.5, -0.5}}, {pos = Vec3{0.5, -0.5, 0.5}},
{pos = Vec3{-0.5, -0.5, -0.5}}, {pos = Vec3{0.5, -0.5, 0.5}}, {pos = Vec3{-0.5, -0.5, 0.5}},
{pos = Vec3{-0.5, 0.5, -0.5}}, {pos = Vec3{0.5, 0.5, 0.5}}, {pos = Vec3{0.5, 0.5, -0.5}},
{pos = Vec3{-0.5, 0.5, -0.5}}, {pos = Vec3{-0.5, 0.5, 0.5}}, {pos = Vec3{0.5, 0.5, 0.5}},
}
vbuf_info := sdl.GPUBufferCreateInfo{
usage = sdl.GPUBufferUsageFlags{.VERTEX},
size = u32(len(vertices) * size_of(Vertex)),
props = 0,
}
vbuf := sdl.CreateGPUBuffer(device, vbuf_info)
if vbuf == nil {
fmt.println("CreateGPUBuffer failed:", sdl.GetError())
return
}
defer sdl.ReleaseGPUBuffer(device, vbuf)
{
setup_cmd := sdl.AcquireGPUCommandBuffer(device)
if setup_cmd == nil {
fmt.println("AcquireGPUCommandBuffer failed:", sdl.GetError())
return
}
if !copy_vertices_to_gpu(device, setup_cmd, vertices[:], vbuf) {
_ = sdl.CancelGPUCommandBuffer(setup_cmd)
return
}
if !sdl.SubmitGPUCommandBuffer(setup_cmd) {
fmt.println("SubmitGPUCommandBuffer (setup) failed:", sdl.GetError())
return
}
}
init_yaw := rotor_from_axis_angle(Vec3{0, 1, 0}, 0.9)
init_right := rotate_vec3(init_yaw, Vec3{1, 0, 0})
init_pitch := rotor_from_axis_angle(init_right, -0.45)
camera := Camera{
center = Vec3{0, 0.5, 0},
distance = 8,
orientation = rotor_normalize(rotor_mul(init_pitch, init_yaw)),
fov_deg = 60,
}
cubes := []Cube_Instance{
{pos = Vec3{0, 0, 0}, scale = 1.0, color = Vec3{0.95, 0.45, 0.20}},
{pos = Vec3{2, 0.5, -1}, scale = 0.8, color = Vec3{0.20, 0.70, 0.95}},
{pos = Vec3{-2, -0.2, 1.5}, scale = 1.2, color = Vec3{0.85, 0.85, 0.30}},
{pos = Vec3{1.0, 1.4, 2.0}, scale = 0.6, color = Vec3{0.40, 0.95, 0.60}},
}
running := true
last := time.tick_now()
left_down := false
middle_down := false
debug := Debug_State{}
for running {
now := time.tick_now()
dt := f32(time.duration_seconds(time.tick_diff(last, now)))
last = now
event: sdl.Event
for sdl.PollEvent(&event) {
#partial switch event.type {
case .QUIT:
running = false
case .MOUSE_BUTTON_DOWN:
switch event.button.button {
case sdl.BUTTON_LEFT:
left_down = true
case sdl.BUTTON_MIDDLE:
middle_down = true
}
case .MOUSE_BUTTON_UP:
switch event.button.button {
case sdl.BUTTON_LEFT:
left_down = false
case sdl.BUTTON_MIDDLE:
middle_down = false
}
case .MOUSE_WHEEL:
camera.distance = math.clamp(camera.distance - event.wheel.y * 0.7, 1.5, 80.0)
case .KEY_DOWN:
if event.key.key == sdl.K_ESCAPE {
running = false
} else if event.key.scancode == sdl.Scancode.F1 && !event.key.repeat {
debug.enabled = !debug.enabled
fmt.println("debug:", debug.enabled)
}
}
}
x_rel: f32 = 0
y_rel: f32 = 0
_ = sdl.GetRelativeMouseState(&x_rel, &y_rel)
if left_down {
yaw_r := rotor_from_axis_angle(Vec3{0, 1, 0}, -x_rel * 0.006)
q1 := rotor_normalize(rotor_mul(yaw_r, camera.orientation))
right_axis := vec3_normalize(rotate_vec3(q1, Vec3{1, 0, 0}))
pitch_r := rotor_from_axis_angle(right_axis, -y_rel * 0.006)
q2 := rotor_normalize(rotor_mul(pitch_r, q1))
fwd2 := camera_forward(Camera{center = camera.center, distance = camera.distance, orientation = q2, fov_deg = camera.fov_deg})
if math.abs(vec3_dot(fwd2, Vec3{0, 1, 0})) < 0.98 {
camera.orientation = q2
} else {
camera.orientation = q1
}
}
if middle_down {
right := camera_right(camera)
up := camera_up(camera)
pan_speed := 0.008 * camera.distance
camera.center = vec3_add(camera.center, vec3_scale(right, -x_rel * pan_speed))
camera.center = vec3_add(camera.center, vec3_scale(up, y_rel * pan_speed))
}
keys := sdl.GetKeyboardState(nil)
forward := camera_forward(camera)
right := camera_right(camera)
up := camera_up(camera)
move_speed := camera.distance * dt * 1.4
if keys[sdl.Scancode.W] {
camera.center = vec3_add(camera.center, vec3_scale(forward, move_speed))
}
if keys[sdl.Scancode.S] {
camera.center = vec3_add(camera.center, vec3_scale(forward, -move_speed))
}
if keys[sdl.Scancode.A] {
camera.center = vec3_add(camera.center, vec3_scale(right, -move_speed))
}
if keys[sdl.Scancode.D] {
camera.center = vec3_add(camera.center, vec3_scale(right, move_speed))
}
if debug.enabled {
debug.accum += dt
if debug.accum >= 0.2 {
debug.accum = 0
eye := camera_position(camera)
fmt.println("cam eye:", eye,
"center:", camera.center,
"fwd:", forward,
"right:", right,
"up:", up)
}
}
w: c.int = 0
h: c.int = 0
sdl.GetWindowSize(window, &w, &h)
if w <= 0 || h <= 0 {
continue
}
cmd := sdl.AcquireGPUCommandBuffer(device)
if cmd == nil {
fmt.println("AcquireGPUCommandBuffer failed:", sdl.GetError())
break
}
swap_tex: ^sdl.GPUTexture
swap_w: sdl.Uint32 = 0
swap_h: sdl.Uint32 = 0
if !sdl.WaitAndAcquireGPUSwapchainTexture(cmd, window, &swap_tex, &swap_w, &swap_h) {
fmt.println("WaitAndAcquireGPUSwapchainTexture failed:", sdl.GetError())
_ = sdl.CancelGPUCommandBuffer(cmd)
break
}
if swap_tex == nil {
if !sdl.SubmitGPUCommandBuffer(cmd) {
fmt.println("SubmitGPUCommandBuffer failed:", sdl.GetError())
break
}
continue
}
clear := sdl.FColor{0.08, 0.09, 0.12, 1.0}
cti := sdl.GPUColorTargetInfo{
texture = swap_tex,
mip_level = 0,
layer_or_depth_plane = 0,
clear_color = clear,
load_op = .CLEAR,
store_op = .STORE,
resolve_texture = nil,
resolve_mip_level = 0,
resolve_layer = 0,
cycle = false,
cycle_resolve_texture = false,
}
rp := sdl.BeginGPURenderPass(cmd, &cti, 1, nil)
if rp == nil {
fmt.println("BeginGPURenderPass failed:", sdl.GetError())
_ = sdl.CancelGPUCommandBuffer(cmd)
break
}
vp := sdl.GPUViewport{x = 0, y = 0, w = f32(swap_w), h = f32(swap_h), min_depth = 0, max_depth = 1}
sdl.SetGPUViewport(rp, vp)
sdl.BindGPUGraphicsPipeline(rp, pipeline)
vb_binding := sdl.GPUBufferBinding{buffer = vbuf, offset = 0}
sdl.BindGPUVertexBuffers(rp, 0, &vb_binding, 1)
aspect := f32(swap_w) / f32(swap_h)
proj := mat4_perspective(camera.fov_deg, aspect, 0.1, 300.0)
view := mat4_look_at(camera_position(camera), camera.center, camera_up(camera))
vp_mat := mat4_mul(proj, view)
for cube in cubes {
model := mat4_mul(mat4_translate(cube.pos), mat4_scale_uniform(cube.scale))
pc := Push_Constants{}
pc.mvp = mat4_mul(vp_mat, model)
pc.color = [4]f32{cube.color[0], cube.color[1], cube.color[2], 1.0}
sdl.PushGPUVertexUniformData(cmd, 0, &pc, u32(size_of(Push_Constants)))
sdl.PushGPUFragmentUniformData(cmd, 0, &pc, u32(size_of(Push_Constants)))
sdl.DrawGPUPrimitives(rp, 36, 1, 0, 0)
}
sdl.EndGPURenderPass(rp)
if !sdl.SubmitGPUCommandBuffer(cmd) {
fmt.println("SubmitGPUCommandBuffer failed:", sdl.GetError())
break
}
}
gndview.runGame(window, device, pipeline)
_ = sdl.WaitForGPUIdle(device)
}
}

View file

@ -1,646 +0,0 @@
package rsmview
import sdl "vendor:sdl3"
import "core:os"
import "core:strings"
import "core:path/filepath"
import "core:fmt"
import stbi "vendor:stb/image"
import "core:time"
import "core:mem"
import "core:math"
import "../format"
import "../shared"
import "core:math/linalg"
import "core:c"
TextureHolder :: struct {
texture: ^sdl.GPUTexture,
pixels: [^]byte,
transferBuffer: ^sdl.GPUTransferBuffer,
w: i32,
h: i32,
}
Node :: struct {
start: int,
end: int,
texture: u16,
node: ^format.RSM_Node,
textures: []sdl.GPUTexture,
}
Push_Constants :: struct {
mvp: matrix[4,4]f32,
color: [4]f32,
}
last_path_component :: proc(s: string) -> string {
last_slash := strings.last_index_byte(s, '/')
last_backslash := strings.last_index_byte(s, '\\')
last := max(last_slash, last_backslash)
if last < 0 do return s
return s[last+1:]
}
find_texture_by_basename :: proc(root, wanted_name: string) -> (string, bool) {
wanted_lower := strings.to_lower(last_path_component(wanted_name), context.temp_allocator)
return find_texture_by_basename_walk(root, wanted_lower)
}
find_texture_by_basename_walk :: proc(dir, wanted_lower: string) -> (string, bool) {
f, err := os.open(dir)
if err != nil do return "", false
defer os.close(f)
entries, _ := os.read_dir(f, 0, context.temp_allocator)
for entry in entries {
path, join_err := filepath.join({dir, entry.name})
if join_err != nil do continue
if entry.type == .Directory {
if resolved, found := find_texture_by_basename_walk(path, wanted_lower); found {
return resolved, true
}
continue
}
name_lower := strings.to_lower(entry.name, context.temp_allocator)
if name_lower == wanted_lower {
return path, true
}
}
return "", false
}
loadModel :: proc(device: ^sdl.GPUDevice, model: ^format.RSM_Model, vertices: ^[dynamic]shared.Vertex, indices: ^[dynamic]u16, nodes: ^[dynamic]Node) -> (vbuf: ^sdl.GPUBuffer, ibuf: ^sdl.GPUBuffer, textures: [dynamic]TextureHolder) {
clear(vertices)
clear(indices)
clear(nodes)
fmt.println("version %v", model.version)
version := 0
if model.version.major > 2 && model.version.minor > 2 {
version = 1
}
fmt.printfln("loading model texture %v", model.texture_names[:])
for textureName in model.texture_names {
textureName, _ := strings.replace_all(textureName, "\\", "/")
full_path, err := filepath.join({"/home/pavel/neoragnarok_backup/kro_client/data/texture/", textureName}); assert(err == nil)
full_path, err = filepath.clean(full_path); assert(err == nil)
c_path := strings.clone_to_cstring(full_path)
defer delete(c_path)
img_size: [2]i32
pixels := stbi.load(c_path, &img_size.x, &img_size.y, nil, 4);
if pixels == nil {
if fallback_path, found := find_texture_by_basename("/home/pavel/neoragnarok_backup/kro_client/data/texture", textureName); found {
fmt.printfln("fallback texture match %q -> %q", textureName, fallback_path)
c_path := strings.clone_to_cstring(fallback_path)
defer delete(c_path)
pixels = stbi.load(c_path, &img_size.x, &img_size.y, nil, 4)
}
}
if pixels == nil {
fmt.printfln("missing model texture: %q", textureName)
continue
}
texture := sdl.CreateGPUTexture(device, sdl.GPUTextureCreateInfo{
format = .R8G8B8A8_UNORM,
usage = {.SAMPLER},
width = u32(img_size.x),
height = u32(img_size.y),
layer_count_or_depth = 1,
num_levels = 1,
}); assert(texture != nil)
texture_byte_size := img_size.x * img_size.y * 4
append(&textures, TextureHolder{
texture = texture,
pixels = pixels,
w = img_size.x,
h = img_size.y,
})
}
for node_idx := 0; node_idx < len(model.nodes); node_idx+= 1 {
textureOffset := u16(0)
node := &model.nodes[node_idx]
fmt.println("offset_matrix", node.offset_matrix)
fmt.println("node.translation1", node.translation1)
fmt.println("node.translation2", node.translation2)
fmt.println("node.scale", node.scale)
fmt.println("node.rotation_angle", node.rotation_angle)
fmt.println("node.rotation_axis", node.rotation_axis)
// fmt.println("node.scale_keyframes[:]", node.scale_keyframes[:])
// fmt.println("node.texture_names[:]", node.texture_names[:])
if model.version.major >= 2 && model.version.minor > 2 {
textureOffset = u16(len(textures))
}
fmt.printfln("texture offset %v", textureOffset)
fmt.printfln("loading node texture %v", node.texture_names[:])
for textureName in node.texture_names {
fmt.printfln("loading texture %s", textureName)
full_path, err := filepath.join({"/home/pavel/neoragnarok_backup/kro_client/data/texture/", textureName}); assert(err == nil)
full_path, err = filepath.clean(full_path); assert(err == nil)
full_path, _ = strings.replace_all(full_path, "\\", "/")
c_path := strings.clone_to_cstring(full_path)
defer delete(c_path)
img_size: [2]i32
pixels := stbi.load(c_path, &img_size.x, &img_size.y, nil, 4)
fmt.printfln("texture has size %v", img_size)
texture := sdl.CreateGPUTexture(device, sdl.GPUTextureCreateInfo{
format = .R8G8B8A8_UNORM,
usage = {.SAMPLER},
width = u32(img_size.x),
height = u32(img_size.y),
layer_count_or_depth = 1,
num_levels = 1,
}); assert(texture != nil)
texture_byte_size := img_size.x * img_size.y * 4
append(&textures, TextureHolder{
texture = texture,
pixels = pixels,
w = img_size.x,
h = img_size.y,
})
}
for face in node.faces {
n := Node{
start = len(indices),
node = node,
texture = face.texture_index + textureOffset,
}
for i in 0..<3 {
v_idx := face.vertex_position_indices[i]
t_idx := face.texture_coordinate_indices[i]
pos := node.vertex_positions[v_idx]
uv := node.texture_coordinates[t_idx].coordinates
append(vertices, shared.Vertex{
pos = pos,
uv = uv,
})
append(indices, u16(len(vertices)-1))
}
n.end = len(indices)
append(nodes, n)
}
}
vbuf_info := sdl.GPUBufferCreateInfo{
usage = sdl.GPUBufferUsageFlags{.VERTEX},
size = u32(len(vertices) * size_of(vertices[0])),
props = 0,
}
vbuf = sdl.CreateGPUBuffer(device, vbuf_info)
if vbuf == nil {
fmt.println("CreateGPUBuffer failed:", sdl.GetError())
return
}
ibuf_info := sdl.GPUBufferCreateInfo{
usage = sdl.GPUBufferUsageFlags{.INDEX},
size = u32(len(indices) * size_of(indices[0])),
props = 0,
}
ibuf = sdl.CreateGPUBuffer(device, ibuf_info)
if ibuf == nil {
fmt.println("CreateGPUBuffer failed:", sdl.GetError())
return
}
{
setup_cmd := sdl.AcquireGPUCommandBuffer(device)
if setup_cmd == nil {
fmt.println("AcquireGPUCommandBuffer failed:", sdl.GetError())
return
}
fmt.println("copying textures to transfer buffer")
for &texture in textures {
tb_tex := sdl.CreateGPUTransferBuffer(device, sdl.GPUTransferBufferCreateInfo{
usage = .UPLOAD,
size = u32(texture.w * texture.h * 4),
props = 0,
}); assert(tb_tex != nil)
tex_mapped := sdl.MapGPUTransferBuffer(device, tb_tex, false); assert(tex_mapped != nil)
mem.copy_non_overlapping(tex_mapped, texture.pixels, int(texture.w * texture.h * 4))
sdl.UnmapGPUTransferBuffer(device, tb_tex)
texture.transferBuffer = tb_tex
}
fmt.println("copying textures to transfer buffer")
vert_byte_count := len(vertices) * size_of(vertices[0])
ind_byte_count := len(indices) * size_of(indices[0])
tb_info := sdl.GPUTransferBufferCreateInfo{
usage = .UPLOAD,
size = u32(vert_byte_count + ind_byte_count),
props = 0,
}
tb := sdl.CreateGPUTransferBuffer(device, tb_info)
if tb == nil {
fmt.println("CreateGPUTransferBuffer failed:", sdl.GetError())
return
}
defer sdl.ReleaseGPUTransferBuffer(device, tb)
mapped := transmute([^]byte)sdl.MapGPUTransferBuffer(device, tb, false)
if mapped == nil {
fmt.println("MapGPUTransferBuffer failed:", sdl.GetError())
return
}
mem.copy_non_overlapping(mapped, raw_data(vertices[:]), vert_byte_count)
mem.copy_non_overlapping(mapped[vert_byte_count:], raw_data(indices[:]), ind_byte_count)
sdl.UnmapGPUTransferBuffer(device, tb)
cp := sdl.BeginGPUCopyPass(setup_cmd)
if cp == nil {
fmt.println("BeginGPUCopyPass failed:", sdl.GetError())
return
}
vert_src := sdl.GPUTransferBufferLocation{transfer_buffer = tb, offset = 0}
vert_dst := sdl.GPUBufferRegion{buffer = vbuf, offset = 0, size = u32(vert_byte_count)}
sdl.UploadToGPUBuffer(cp, vert_src, vert_dst, false)
ind_src := sdl.GPUTransferBufferLocation{transfer_buffer = tb, offset = u32(vert_byte_count)}
ind_dst := sdl.GPUBufferRegion{buffer = ibuf, offset = 0, size = u32(ind_byte_count)}
sdl.UploadToGPUBuffer(cp, ind_src, ind_dst, false)
fmt.println("transferring textures")
for texture in textures {
fmt.println("tex ptr:", texture, "w/h:", texture.w, texture.h)
sdl.UploadToGPUTexture(cp,
{
transfer_buffer = texture.transferBuffer,
offset = 0,
pixels_per_row = u32(texture.w),
rows_per_layer = u32(texture.h),
},
{texture = texture.texture, w = u32(texture.w), h = u32(texture.h), d = 1},
false,
)
}
fmt.println("transferred textures")
sdl.EndGPUCopyPass(cp)
if !sdl.SubmitGPUCommandBuffer(setup_cmd) {
fmt.println("SubmitGPUCommandBuffer (setup) failed:", sdl.GetError())
return
}
}
return vbuf, ibuf, textures
}
walk :: proc(dir: string, models: ^[dynamic]format.RSM_Model) {
f, _ := os.open(dir)
entries, ok := os.read_dir(f, 0, context.allocator)
for entry in entries {
path, _ := filepath.join({dir, entry.name})
if entry.type == .Directory {
walk(path, models)
}
if strings.contains(entry.name, ".rsm") {
data, _ := os.read_entire_file(path, context.allocator)
parsed, err := format.parse_rsm(data)
if err != nil {
fmt.printfln("%v", err)
} else {
for node in parsed.nodes {
if parsed.version.major == 2 && parsed.version.minor == 3{
append_elem(models, parsed)
break
}
}
}
}
}
}
runGame :: proc(window: ^sdl.Window, device: ^sdl.GPUDevice, pipeline: ^sdl.GPUGraphicsPipeline) {\
sampler := sdl.CreateGPUSampler(device, {})
modelIndex := 0
models: [dynamic]format.RSM_Model
walk("/home/pavel/neoragnarok_backup/kro_client/data", &models)
vertices: [dynamic]shared.Vertex
indices: [dynamic]u16
nodes: [dynamic]Node
vbuf, ibuf, textures := loadModel(device, &models[modelIndex], &vertices, &indices, &nodes)
defer sdl.ReleaseGPUBuffer(device, ibuf)
defer sdl.ReleaseGPUBuffer(device, vbuf)
defer for texture in textures do sdl.ReleaseGPUTexture(device, texture.texture)
init_yaw := shared.rotor_from_axis_angle(shared.Vec3{0, 1, 0}, 0.9)
init_right := shared.rotate_vec3(init_yaw, shared.Vec3{1, 0, 0})
init_pitch := shared.rotor_from_axis_angle(init_right, -0.45)
camera := shared.Camera{
center = shared.Vec3{0, 0.5, 0},
distance = 8,
orientation = shared.rotor_normalize(shared.rotor_mul(init_pitch, init_yaw)),
fov_deg = 60,
}
start := time.tick_now()
last := start
left_down := false
middle_down := false
running := true
for running {
now := time.tick_now()
elapsed_ms := time.duration_milliseconds(time.tick_diff(start, now))
dt_ms := time.duration_milliseconds(time.tick_diff(last, now))
dt := f32(time.duration_seconds(time.tick_diff(last, now)))
last = now
event: sdl.Event
for sdl.PollEvent(&event) {
#partial switch event.type {
case .QUIT:
running = false
case .MOUSE_BUTTON_DOWN:
switch event.button.button {
case sdl.BUTTON_LEFT:
left_down = true
case sdl.BUTTON_MIDDLE:
middle_down = true
}
case .MOUSE_BUTTON_UP:
switch event.button.button {
case sdl.BUTTON_LEFT:
left_down = false
case sdl.BUTTON_MIDDLE:
middle_down = false
}
case .MOUSE_WHEEL:
camera.distance = math.clamp(camera.distance - event.wheel.y * 0.7, 1.5, 80.0)
case .KEY_DOWN:
if event.key.key == sdl.K_ESCAPE {
running = false
} else if event.key.key == sdl.K_N {
modelIndex += 1
sdl.ReleaseGPUBuffer(device, ibuf)
sdl.ReleaseGPUBuffer(device, vbuf)
for texture in textures do sdl.ReleaseGPUTexture(device, texture.texture)
vbuf, ibuf, textures = loadModel(device, &models[modelIndex], &vertices, &indices, &nodes)
}
}
}
x_rel: f32 = 0
y_rel: f32 = 0
_ = sdl.GetRelativeMouseState(&x_rel, &y_rel)
if left_down {
yaw_r := shared.rotor_from_axis_angle(shared.Vec3{0, 1, 0}, -x_rel * 0.006)
q1 := shared.rotor_normalize(shared.rotor_mul(yaw_r, camera.orientation))
right_axis := shared.vec3_normalize(shared.rotate_vec3(q1, shared.Vec3{1, 0, 0}))
pitch_r := shared.rotor_from_axis_angle(right_axis, -y_rel * 0.006)
q2 := shared.rotor_normalize(shared.rotor_mul(pitch_r, q1))
fwd2 := shared.camera_forward(shared.Camera{center = camera.center, distance = camera.distance, orientation = q2, fov_deg = camera.fov_deg})
if math.abs(shared.vec3_dot(fwd2, shared.Vec3{0, 1, 0})) < 0.98 {
camera.orientation = q2
} else {
camera.orientation = q1
}
}
if middle_down {
right := shared.camera_right(camera)
up := shared.camera_up(camera)
pan_speed := 0.008 * camera.distance
camera.center = camera.center + right * -x_rel * pan_speed
camera.center = camera.center + up * y_rel * pan_speed
}
keys := sdl.GetKeyboardState(nil)
forward := shared.camera_forward(camera)
right := shared.camera_right(camera)
up := shared.camera_up(camera)
move_speed := camera.distance * dt * 1.4
if keys[sdl.Scancode.W] {
camera.center = camera.center + forward * move_speed
}
if keys[sdl.Scancode.S] {
camera.center = camera.center + forward * -move_speed
}
if keys[sdl.Scancode.A] {
camera.center = camera.center + right * -move_speed
}
if keys[sdl.Scancode.D] {
camera.center = camera.center + right * move_speed
}
w: c.int = 0
h: c.int = 0
sdl.GetWindowSize(window, &w, &h)
if w <= 0 || h <= 0 {
continue
}
cmd := sdl.AcquireGPUCommandBuffer(device)
if cmd == nil {
fmt.println("AcquireGPUCommandBuffer failed:", sdl.GetError())
break
}
swap_tex: ^sdl.GPUTexture
swap_w: sdl.Uint32 = 0
swap_h: sdl.Uint32 = 0
if !sdl.WaitAndAcquireGPUSwapchainTexture(cmd, window, &swap_tex, &swap_w, &swap_h) {
fmt.println("WaitAndAcquireGPUSwapchainTexture failed:", sdl.GetError())
_ = sdl.CancelGPUCommandBuffer(cmd)
break
}
if swap_tex == nil {
if !sdl.SubmitGPUCommandBuffer(cmd) {
fmt.println("SubmitGPUCommandBuffer failed:", sdl.GetError())
break
}
continue
}
clear := sdl.FColor{0.08, 0.09, 0.12, 1.0}
cti := sdl.GPUColorTargetInfo{
texture = swap_tex,
mip_level = 0,
layer_or_depth_plane = 0,
clear_color = clear,
load_op = .CLEAR,
store_op = .STORE,
resolve_texture = nil,
resolve_mip_level = 0,
resolve_layer = 0,
cycle = false,
cycle_resolve_texture = false,
}
rp := sdl.BeginGPURenderPass(cmd, &cti, 1, nil)
if rp == nil {
fmt.println("BeginGPURenderPass failed:", sdl.GetError())
_ = sdl.CancelGPUCommandBuffer(cmd)
break
}
vp := sdl.GPUViewport{x = 0, y = 0, w = f32(swap_w), h = f32(swap_h), min_depth = 0, max_depth = 1}
sdl.SetGPUViewport(rp, vp)
sdl.BindGPUGraphicsPipeline(rp, pipeline)
vb_binding := sdl.GPUBufferBinding{buffer = vbuf, offset = 0}
sdl.BindGPUVertexBuffers(rp, 0, &vb_binding, 1)
ib_binding := sdl.GPUBufferBinding{buffer = ibuf, offset = 0}
sdl.BindGPUIndexBuffer(rp, ib_binding, ._16BIT)
aspect := f32(swap_w) / f32(swap_h)
proj := linalg.matrix4_perspective(linalg.to_radians(camera.fov_deg), aspect, 0.1, 300.0)
view := linalg.matrix4_look_at(shared.camera_position(camera), camera.center, shared.camera_up(camera))
vp_mat := proj * view
renderModel(&models[modelIndex], elapsed_ms, nodes, &textures, sampler, cmd, vp_mat, rp)
sdl.EndGPURenderPass(rp)
if !sdl.SubmitGPUCommandBuffer(cmd) {
fmt.println("SubmitGPUCommandBuffer failed:", sdl.GetError())
break
}
}
}
renderModel :: proc(model: ^format.RSM_Model, elapsed_ms: f64, nodes: [dynamic]Node, textures: ^[dynamic]TextureHolder, sampler: ^sdl.GPUSampler, cmd: ^sdl.GPUCommandBuffer, vp_mat: matrix[4,4]f32, rp: ^sdl.GPURenderPass) {
animation_length := model.animation_length
fps := model.frames_per_second
frame_delay : f32
total_frames : u32
if fps == 0 {
total_seconds := f32(animation_length) / 1000
total_frames = u32(total_seconds * 30)
frame_delay = 1000 / f32(30)
} else {
total_frames = animation_length
frame_delay = 1000 / fps
}
current_frame := u32(f32(elapsed_ms) / frame_delay) % total_frames
for mesh in nodes {
node := mesh.node
rotation := linalg.quaternion_angle_axis(node.rotation_angle, node.rotation_axis)
if len(node.rotation_keyframes) > 0 {
keyframe_idx := 0
for i := 0; i < len(node.rotation_keyframes); i+= 1 {
keyframe := node.rotation_keyframes[i]
if u32(keyframe.frame) <= current_frame {
keyframe_idx = i
} else do break
}
q := node.rotation_keyframes[keyframe_idx].quaternion
rotation = quaternion(imag=q[0], jmag=q[1], kmag=q[2], real=q[3])
}
rotation = linalg.quaternion_normalize(rotation)
scale := node.scale
if len(node.scale_keyframes) > 0 {
keyframe_idx := 0
for i := 0; i < len(node.scale_keyframes); i+= 1 {
keyframe := node.scale_keyframes[i]
if u32(keyframe.frame) <= current_frame {
keyframe_idx = i
} else do break
}
scale = node.scale_keyframes[keyframe_idx].scale
}
translation := [3]f32{0,0,0}
if(len(node.translation_keyframes) > 0) {
keyframe_idx := 0
for i := 0; i < len(node.translation_keyframes); i+= 1 {
keyframe := node.translation_keyframes[i]
if u32(keyframe.frame) <= current_frame {
keyframe_idx = i
} else do break
}
translation = node.translation_keyframes[keyframe_idx].translation
}
r := linalg.matrix3_from_quaternion(rotation)
s := matrix[3,3]f32{
scale[0], 0, 0 ,
0, scale[1], 0 ,
0, 0, scale[2],
}
a3 := r * node.offset_matrix * s
t := node.translation1 + node.translation2 + translation
// 4x4 affine matrix
model := matrix[4,4]f32{
a3[0][0], a3[0][1], a3[0][2], t[0],
a3[1][0], a3[1][1], a3[1][2], t[1],
a3[2][0], a3[2][1], a3[2][2], t[2],
0, 0, 0, 1 ,
}
pc := Push_Constants{}
pc.mvp = vp_mat * model
pc.color = [4]f32{1, 0, 0, 1.0}
sdl.BindGPUFragmentSamplers(rp, 0, &(sdl.GPUTextureSamplerBinding{
texture = textures[mesh.texture].texture,
sampler = sampler,
}), 1)
sdl.PushGPUVertexUniformData(cmd, 0, &pc, u32(size_of(Push_Constants)))
sdl.DrawGPUIndexedPrimitives(rp, u32(mesh.end-mesh.start), 1, u32(mesh.start), 0, 0)
}
}

View file

@ -1,98 +0,0 @@
package shared
import "core:math"
Vec3 :: [3]f32
Vec2 :: [2]f32
Camera :: struct {
center: Vec3,
distance: f32,
orientation: Rotor,
fov_deg: f32,
}
Rotor :: struct {
s, x, y, z: f32,
}
Vertex :: struct {
pos: Vec3,
uv: Vec2,
}
vec3_dot :: proc(a, b: Vec3) -> f32 {
return a[0] * b[0] + a[1] * b[1] + a[2] * b[2]
}
vec3_cross :: proc(a, b: Vec3) -> Vec3 {
return Vec3{
a[1] * b[2] - a[2] * b[1],
a[2] * b[0] - a[0] * b[2],
a[0] * b[1] - a[1] * b[0],
}
}
vec3_normalize :: proc(v: Vec3) -> Vec3 {
len2 := vec3_dot(v, v)
if len2 <= 0.000001 {
return Vec3{0, 0, 0}
}
inv := 1.0 / math.sqrt(len2)
return v * inv
}
rotor_identity :: proc() -> Rotor {
return Rotor{1, 0, 0, 0}
}
rotor_normalize :: proc(r: Rotor) -> Rotor {
len2 := r.s*r.s + r.x*r.x + r.y*r.y + r.z*r.z
if len2 <= 0.000001 {
return rotor_identity()
}
inv := 1.0 / math.sqrt(len2)
return Rotor{r.s * inv, r.x * inv, r.y * inv, r.z * inv}
}
rotor_mul :: proc(a, b: Rotor) -> Rotor {
return Rotor{
a.s*b.s - a.x*b.x - a.y*b.y - a.z*b.z,
a.s*b.x + a.x*b.s + a.y*b.z - a.z*b.y,
a.s*b.y - a.x*b.z + a.y*b.s + a.z*b.x,
a.s*b.z + a.x*b.y - a.y*b.x + a.z*b.s,
}
}
rotor_from_axis_angle :: proc(axis: Vec3, angle: f32) -> Rotor {
a := vec3_normalize(axis)
h := angle * 0.5
c := math.cos(h)
s := math.sin(h)
return Rotor{c, a[0] * s, a[1] * s, a[2] * s}
}
rotate_vec3 :: proc(r: Rotor, v: Vec3) -> Vec3 {
u := Vec3{r.x, r.y, r.z}
t := vec3_cross(u, v) * 2.0
return v + t * r.s + vec3_cross(u, t)
}
camera_forward :: proc(c: Camera) -> Vec3 {
return vec3_normalize(rotate_vec3(c.orientation, Vec3{0, 0, -1}))
}
camera_right :: proc(c: Camera) -> Vec3 {
return vec3_normalize(rotate_vec3(c.orientation, Vec3{1, 0, 0}))
}
camera_up :: proc(c: Camera) -> Vec3 {
return vec3_normalize(rotate_vec3(c.orientation, Vec3{0, 1, 0}))
}
camera_position :: proc(c: Camera) -> Vec3 {
return (c.center - camera_forward(c) * c.distance)
}