abstracting to engine package
This commit is contained in:
@@ -0,0 +1,56 @@
|
|||||||
|
package engine
|
||||||
|
|
||||||
|
import "core:fmt"
|
||||||
|
import sdl "vendor:sdl3"
|
||||||
|
|
||||||
|
App :: struct {
|
||||||
|
window: ^sdl.Window,
|
||||||
|
renderer: ^sdl.Renderer,
|
||||||
|
}
|
||||||
|
|
||||||
|
init :: proc(app: ^App, title: cstring, width, height: i32) -> bool {
|
||||||
|
if !sdl.Init({.VIDEO}) {
|
||||||
|
fmt.eprintfln("SDL_Init failed: %s", sdl.GetError())
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if !sdl.CreateWindowAndRenderer(title, width, height, {}, &app.window, &app.renderer) {
|
||||||
|
fmt.eprintfln("CreateWindowAndRenderer failed: %s", sdl.GetError())
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
shutdown :: proc(app: ^App) {
|
||||||
|
if app.renderer != nil do sdl.DestroyRenderer(app.renderer)
|
||||||
|
if app.window != nil do sdl.DestroyWindow(app.window)
|
||||||
|
|
||||||
|
sdl.Quit()
|
||||||
|
app^ = {}
|
||||||
|
}
|
||||||
|
|
||||||
|
pump_events :: proc() -> bool {
|
||||||
|
event: sdl.Event
|
||||||
|
|
||||||
|
for sdl.PollEvent(&event) {
|
||||||
|
if event.type == .QUIT {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
begin_frame :: proc(app: ^App, r: u8 = 30, g: u8 = 30, b: u8 = 40, a: u8 = 255) {
|
||||||
|
sdl.SetRenderDrawColor(app.renderer, r, g, b, a)
|
||||||
|
sdl.RenderClear(app.renderer)
|
||||||
|
}
|
||||||
|
|
||||||
|
end_frame :: proc(app: ^App) {
|
||||||
|
sdl.RenderPresent(app.renderer)
|
||||||
|
}
|
||||||
|
|
||||||
|
renderer :: proc(app: ^App) -> ^sdl.Renderer {
|
||||||
|
return app.renderer
|
||||||
|
}
|
||||||
@@ -0,0 +1,108 @@
|
|||||||
|
package engine
|
||||||
|
|
||||||
|
import "core:encoding/json"
|
||||||
|
import "core:fmt"
|
||||||
|
import "core:os"
|
||||||
|
import "core:path/filepath"
|
||||||
|
import "core:strings"
|
||||||
|
import sdl "vendor:sdl3"
|
||||||
|
|
||||||
|
Frame_Def :: struct {
|
||||||
|
rect: [4]int,
|
||||||
|
source_size: [2]int,
|
||||||
|
trim_offset: [2]int,
|
||||||
|
}
|
||||||
|
|
||||||
|
Clip_Def :: struct {
|
||||||
|
loop: bool,
|
||||||
|
fps: f32,
|
||||||
|
frames: []Frame_Def,
|
||||||
|
}
|
||||||
|
|
||||||
|
Char_Def :: struct {
|
||||||
|
version: int,
|
||||||
|
id: string,
|
||||||
|
atlas: string,
|
||||||
|
pixels_per_unit: f32,
|
||||||
|
pivot: [2]f32,
|
||||||
|
clips: map[string]Clip_Def,
|
||||||
|
}
|
||||||
|
|
||||||
|
Character_Data :: struct {
|
||||||
|
def: Char_Def,
|
||||||
|
texture: ^sdl.Texture,
|
||||||
|
}
|
||||||
|
|
||||||
|
load_character_data :: proc(
|
||||||
|
renderer: ^sdl.Renderer,
|
||||||
|
character_json_path: string,
|
||||||
|
) -> (
|
||||||
|
Character_Data,
|
||||||
|
bool,
|
||||||
|
) {
|
||||||
|
file_data, read_err := os.read_entire_file(character_json_path, context.allocator)
|
||||||
|
if read_err != nil {
|
||||||
|
fmt.eprintfln("failed to read %s: %v", character_json_path, read_err)
|
||||||
|
return {}, false
|
||||||
|
}
|
||||||
|
|
||||||
|
defer delete(file_data)
|
||||||
|
|
||||||
|
out: Character_Data
|
||||||
|
if err := json.unmarshal(file_data, &out.def); err != nil {
|
||||||
|
fmt.eprintfln("json unmarshal failed: %v", err)
|
||||||
|
return {}, false
|
||||||
|
}
|
||||||
|
|
||||||
|
// filepath.dir returns a substring of the path — do NOT delete it
|
||||||
|
dir := filepath.dir(character_json_path)
|
||||||
|
|
||||||
|
atlas_path, join_err := filepath.join({dir, out.def.atlas})
|
||||||
|
if join_err != nil {
|
||||||
|
fmt.eprintfln("atlas join failed: %v", join_err)
|
||||||
|
return {}, false
|
||||||
|
}
|
||||||
|
|
||||||
|
defer delete(atlas_path)
|
||||||
|
|
||||||
|
c_path := strings.clone_to_cstring(atlas_path)
|
||||||
|
defer delete(c_path)
|
||||||
|
|
||||||
|
surface := sdl.LoadPNG(c_path)
|
||||||
|
if surface == nil {
|
||||||
|
fmt.eprintfln("LoadPNG failed: %s", sdl.GetError())
|
||||||
|
return {}, false
|
||||||
|
}
|
||||||
|
defer sdl.DestroySurface(surface)
|
||||||
|
|
||||||
|
out.texture = sdl.CreateTextureFromSurface(renderer, surface)
|
||||||
|
if out.texture == nil {
|
||||||
|
fmt.eprintfln("CreateTextureFromSurface failed: %s", sdl.GetError())
|
||||||
|
return {}, false
|
||||||
|
}
|
||||||
|
sdl.SetTextureScaleMode(out.texture, .NEAREST)
|
||||||
|
return out, true
|
||||||
|
}
|
||||||
|
|
||||||
|
destroy_character_data :: proc(data: ^Character_Data) {
|
||||||
|
if data.texture != nil {
|
||||||
|
sdl.DestroyTexture(data.texture)
|
||||||
|
}
|
||||||
|
data^ = {}
|
||||||
|
}
|
||||||
|
|
||||||
|
character_frame_rect :: proc(
|
||||||
|
data: ^Character_Data,
|
||||||
|
clip_name: string,
|
||||||
|
frame_index: int,
|
||||||
|
) -> (
|
||||||
|
rect: [4]int,
|
||||||
|
ok: bool,
|
||||||
|
) {
|
||||||
|
clip, found := data.def.clips[clip_name]
|
||||||
|
if !found || frame_index < 0 || frame_index >= len(clip.frames) {
|
||||||
|
return {}, false
|
||||||
|
}
|
||||||
|
|
||||||
|
return clip.frames[frame_index].rect, true
|
||||||
|
}
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
package engine
|
||||||
|
|
||||||
|
import sdl "vendor:sdl3"
|
||||||
|
|
||||||
|
Vec2 :: [2]f32
|
||||||
|
|
||||||
|
Sprite :: struct {
|
||||||
|
data: ^Character_Data,
|
||||||
|
position: Vec2,
|
||||||
|
clip: string,
|
||||||
|
frame: int,
|
||||||
|
}
|
||||||
|
|
||||||
|
spawn_sprite :: proc(
|
||||||
|
data: ^Character_Data,
|
||||||
|
position: Vec2,
|
||||||
|
clip: string = "idle",
|
||||||
|
frame: int = 0,
|
||||||
|
) -> Sprite {
|
||||||
|
return Sprite{data = data, position = position, clip = clip, frame = frame}
|
||||||
|
}
|
||||||
|
|
||||||
|
// stubbed for later
|
||||||
|
update_sprite :: proc(sprite: ^Sprite, dt: f32) {
|
||||||
|
_ = sprite
|
||||||
|
_ = dt
|
||||||
|
}
|
||||||
|
|
||||||
|
draw_sprite :: proc(renderer: ^sdl.Renderer, sprite: ^Sprite) {
|
||||||
|
assert(sprite.data != nil)
|
||||||
|
rect, ok := character_frame_rect(sprite.data, sprite.clip, sprite.frame)
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
w := f32(rect[2])
|
||||||
|
h := f32(rect[3])
|
||||||
|
dst := sdl.FRect {
|
||||||
|
x = sprite.position.x - w * 0.5,
|
||||||
|
y = sprite.position.y - h,
|
||||||
|
w = w,
|
||||||
|
h = h,
|
||||||
|
}
|
||||||
|
src := sdl.FRect {
|
||||||
|
x = f32(rect[0]),
|
||||||
|
y = f32(rect[1]),
|
||||||
|
w = w,
|
||||||
|
h = h,
|
||||||
|
}
|
||||||
|
sdl.RenderTexture(renderer, sprite.data.texture, &src, &dst)
|
||||||
|
}
|
||||||
+15
-103
@@ -1,117 +1,29 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import "core:encoding/json"
|
import eng "pkg:engine"
|
||||||
import "core:fmt"
|
|
||||||
import "core:os"
|
|
||||||
import "core:text/regex/virtual_machine"
|
|
||||||
import sdl "vendor:sdl3"
|
|
||||||
|
|
||||||
Frame_Def :: struct {
|
|
||||||
rect: [4]int,
|
|
||||||
source_size: [2]int,
|
|
||||||
trim_offset: [2]int,
|
|
||||||
}
|
|
||||||
|
|
||||||
Clip_Def :: struct {
|
|
||||||
loop: bool,
|
|
||||||
fps: f32,
|
|
||||||
frames: []Frame_Def,
|
|
||||||
}
|
|
||||||
|
|
||||||
Char_Def :: struct {
|
|
||||||
version: int,
|
|
||||||
id: string,
|
|
||||||
atlas: string,
|
|
||||||
pixels_per_unit: f32,
|
|
||||||
pivot: [2]f32,
|
|
||||||
clips: map[string]Clip_Def,
|
|
||||||
}
|
|
||||||
|
|
||||||
main :: proc() {
|
main :: proc() {
|
||||||
if !sdl.Init({.VIDEO}) {
|
app: eng.App
|
||||||
fmt.eprintfln("SDL_Init failed: %s", sdl.GetError())
|
if !eng.init(&app, "toad example", 800, 600) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
defer sdl.Quit()
|
defer eng.shutdown(&app)
|
||||||
|
|
||||||
window: ^sdl.Window
|
data, ok := eng.load_character_data(
|
||||||
renderer: ^sdl.Renderer
|
app.renderer,
|
||||||
|
"assets_baked/characters/toad/toad.char.json",
|
||||||
if !sdl.CreateWindowAndRenderer("toad", 800, 600, {}, &window, &renderer) {
|
)
|
||||||
fmt.eprintfln("CreateWindowAndRenderer failed: %s", sdl.GetError())
|
if !ok {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
defer eng.destroy_character_data(&data)
|
||||||
|
|
||||||
defer sdl.DestroyRenderer(renderer)
|
toad := eng.spawn_sprite(&data, {400, 500}, "idle", 0)
|
||||||
defer sdl.DestroyWindow(window)
|
|
||||||
|
|
||||||
surface := sdl.LoadPNG("assets_baked/characters/toad/toad.atlas.png")
|
for eng.pump_events() {
|
||||||
if surface == nil {
|
eng.begin_frame(&app)
|
||||||
fmt.eprintfln("LoadPNG failed: %s", sdl.GetError())
|
eng.draw_sprite(app.renderer, &toad)
|
||||||
return
|
eng.end_frame(&app)
|
||||||
}
|
|
||||||
defer sdl.DestroySurface(surface)
|
|
||||||
|
|
||||||
texture := sdl.CreateTextureFromSurface(renderer, surface)
|
|
||||||
if texture == nil {
|
|
||||||
fmt.eprintfln("CreateTextureFromSurface failed: %s", sdl.GetError())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
defer sdl.DestroyTexture(texture)
|
|
||||||
|
|
||||||
sdl.SetTextureScaleMode(texture, .NEAREST)
|
|
||||||
|
|
||||||
char_path := "assets_baked/characters/toad/toad.char.json"
|
|
||||||
data, read_err := os.read_entire_file(char_path, context.allocator)
|
|
||||||
if read_err != nil {
|
|
||||||
fmt.eprintfln("failed to read %s: %v", char_path, read_err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
defer delete(data)
|
|
||||||
|
|
||||||
char_def: Char_Def
|
|
||||||
if err := json.unmarshal(data, &char_def); err != nil {
|
|
||||||
fmt.eprintfln("json unmarshal failed: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
idle, ok := char_def.clips["idle"]
|
|
||||||
if !ok || len(idle.frames) == 0 {
|
|
||||||
fmt.eprintfln("missing idle frames")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Atlas crop for idle frame 0 — used every frame as `src`
|
|
||||||
r := idle.frames[0].rect // [x, y, w, h]
|
|
||||||
src := sdl.FRect {
|
|
||||||
x = f32(r[0]),
|
|
||||||
y = f32(r[1]),
|
|
||||||
w = f32(r[2]),
|
|
||||||
h = f32(r[3]),
|
|
||||||
}
|
|
||||||
|
|
||||||
running := true
|
|
||||||
|
|
||||||
for running {
|
|
||||||
event: sdl.Event
|
|
||||||
for sdl.PollEvent(&event) {
|
|
||||||
if event.type == .QUIT {
|
|
||||||
running = false
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sdl.SetRenderDrawColor(renderer, 30, 30, 40, 255)
|
|
||||||
sdl.RenderClear(renderer)
|
|
||||||
|
|
||||||
dst := sdl.FRect {
|
|
||||||
x = 400 - src.w * 0.5,
|
|
||||||
y = 500 - src.h,
|
|
||||||
w = src.w,
|
|
||||||
h = src.h,
|
|
||||||
}
|
|
||||||
|
|
||||||
sdl.RenderTexture(renderer, texture, &src, &dst)
|
|
||||||
|
|
||||||
sdl.RenderPresent(renderer)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://raw.githubusercontent.com/DanielGavin/ols/master/misc/ols.schema.json",
|
||||||
|
"collections": [
|
||||||
|
{ "name": "pkg", "path": "." }
|
||||||
|
],
|
||||||
|
"enable_document_symbols": true,
|
||||||
|
"enable_hover": true,
|
||||||
|
"enable_snippets": true,
|
||||||
|
"profiles": [
|
||||||
|
{
|
||||||
|
"name": "default",
|
||||||
|
"checker_path": ["examples/toad", "engine", "assetbake"]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user