Compare commits
No commits in common. "28f0d737b1c7e1b893ed7e3e336c6b94741e4ea4" and "cd40b79306c0b9f057a1c215fd0b9f4e9c5bbf61" have entirely different histories.
28f0d737b1
...
cd40b79306
19 changed files with 493 additions and 3473 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -3,4 +3,3 @@ bin/
|
|||
*.dll
|
||||
*.dylib
|
||||
*.so
|
||||
*.spv
|
||||
|
|
|
|||
211
idk/main.odin
211
idk/main.odin
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
@ -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/
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
#version 460
|
||||
|
||||
layout(location=0) out vec4 frag_color;
|
||||
|
||||
layout(location=0) in vec4 color;
|
||||
|
||||
void main() {
|
||||
frag_color = color;
|
||||
}
|
||||
|
|
@ -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.
768
main copy.odin
768
main copy.odin
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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.
|
|
@ -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.
|
|
@ -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
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
539
src/main.odin
539
src/main.odin
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
@ -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)
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue