1
2
3
4
5
6
7
8
9
10
11
12
13package hellope
14
15import core_img "core:image"
16import "core:image/png"
17import "core:log"
18import "core:math"
19import "core:os"
20import "core:strings"
21import "vendor:sdl2"
22import sdl_img "vendor:sdl2/image"
23
24_ :: sdl_img
25_ :: strings
26
27
28USE_SDL2_IMAGE :: #config(USE_SDL2_IMAGE, false)
29
30WINDOW_TITLE :: "Hellope World!"
31WINDOW_X := i32(400)
32WINDOW_Y := i32(400)
33WINDOW_WIDTH := i32(800)
34WINDOW_HEIGHT := i32(600)
35WINDOW_FLAGS :: sdl2.WindowFlags{.SHOWN}
36
37
38
39
40
41CENTER_WINDOW :: true
42
43
44
45
46ODIN_LOGO_PATH :: "logo-slim.png"
47
48Texture_Asset :: struct {
49 tex: ^sdl2.Texture,
50 w: i32,
51 h: i32,
52
53 scale: f32,
54 pivot: struct {
55 x: f32,
56 y: f32,
57 },
58}
59
60Surface :: struct {
61 surf: ^sdl2.Surface,
62
63
64
65
66 img: ^core_img.Image,
67}
68
69CTX :: struct {
70 window: ^sdl2.Window,
71 surface: ^sdl2.Surface,
72 renderer: ^sdl2.Renderer,
73 textures: [dynamic]Texture_Asset,
74
75 should_close: bool,
76 app_start: f64,
77
78 frame_start: f64,
79 frame_end: f64,
80 frame_elapsed: f64,
81
82}
83
84ctx := CTX{}
85
86init_sdl :: proc() -> (ok: bool) {
87 if sdl_res := sdl2.Init(sdl2.INIT_VIDEO); sdl_res < 0 {
88 log.errorf("sdl2.init returned %v.", sdl_res)
89 return false
90 }
91
92 when USE_SDL2_IMAGE {
93 img_init_flags := sdl_img.INIT_PNG
94 img_res := sdl_img.InitFlags(sdl_img.Init(img_init_flags))
95 if img_init_flags != img_res {
96 log.errorf("sdl2_image.init returned %v.", img_res)
97 }
98 }
99
100 if CENTER_WINDOW {
101
102
103
104 bounds := sdl2.Rect{}
105 if e := sdl2.GetDisplayBounds(0, &bounds); e != 0 {
106 log.errorf("Unable to get desktop bounds.")
107 return false
108 }
109
110 WINDOW_X = ((bounds.w - bounds.x) / 2) - (WINDOW_WIDTH / 2) + bounds.x
111 WINDOW_Y = ((bounds.h - bounds.y) / 2) - (WINDOW_HEIGHT / 2) + bounds.y
112 }
113
114 ctx.window = sdl2.CreateWindow(WINDOW_TITLE, WINDOW_X, WINDOW_Y, WINDOW_WIDTH, WINDOW_HEIGHT, WINDOW_FLAGS)
115 if ctx.window == nil {
116 log.errorf("sdl2.CreateWindow failed.")
117 return false
118 }
119
120 ctx.renderer = sdl2.CreateRenderer(ctx.window, -1, {.ACCELERATED, .PRESENTVSYNC})
121 if ctx.renderer == nil {
122 log.errorf("sdl2.CreateRenderer failed.")
123 return false
124 }
125
126 return true
127}
128
129when USE_SDL2_IMAGE {
130 load_surface_from_image_file :: proc(image_path: string) -> (surface: ^Surface) {
131 path := strings.clone_to_cstring(image_path, context.temp_allocator)
132
133 surface = new(Surface)
134 surface.surf = sdl_img.Load(path)
135 if surface.surf == nil {
136 log.errorf("Couldn't load %v.", ODIN_LOGO_PATH)
137 }
138 return
139 }
140} else {
141 load_surface_from_image_file :: proc(image_path: string) -> (surface: ^Surface) {
142
143
144
145
146 res_img, res_error := png.load(image_path)
147 if res_error != nil {
148 log.errorf("Couldn't load %v.", ODIN_LOGO_PATH)
149 return nil
150 }
151
152 surface = new(Surface)
153 surface.img = res_img
154
155
156
157
158 rmask := u32(0x000000ff)
159 gmask := u32(0x0000ff00)
160 bmask := u32(0x00ff0000)
161 amask := u32(0xff000000) if res_img.channels == 4 else u32(0x0)
162
163 depth := i32(res_img.depth) * i32(res_img.channels)
164 pitch := i32(res_img.width) * i32(res_img.channels)
165
166 surface.surf = sdl2.CreateRGBSurfaceFrom(
167 raw_data(res_img.pixels.buf),
168 i32(res_img.width), i32(res_img.height), depth, pitch,
169 rmask, gmask, bmask, amask,
170 )
171
172 return
173 }
174}
175
176destroy_surface :: proc(surface: ^Surface) {
177 assert(surface != nil)
178
179 when !USE_SDL2_IMAGE {
180 png.destroy(surface.img)
181 }
182 sdl2.FreeSurface(surface.surf)
183 free(surface)
184}
185
186init_resources :: proc() -> (ok: bool) {
187 logo_surface: ^Surface
188
189 if logo_surface = load_surface_from_image_file(ODIN_LOGO_PATH); logo_surface == nil {
190 log.errorf("Couldn't load image %v.", ODIN_LOGO_PATH)
191 }
192
193 tex := sdl2.CreateTextureFromSurface(ctx.renderer, logo_surface.surf)
194 if tex == nil {
195 log.errorf("Couldn't convert image to texture.")
196 return false
197 }
198 odin_logo := Texture_Asset{
199 tex = tex,
200 w = logo_surface.surf.w,
201 h = logo_surface.surf.h,
202
203
204
205
206 scale = 1.00,
207 pivot = { 0.5, 0.5, },
208 }
209
210 destroy_surface(logo_surface)
211 append(&ctx.textures, odin_logo)
212
213 return true
214}
215
216draw :: proc() {
217 sdl2.SetRenderDrawColor(ctx.renderer, 255, 142, 27, 0xff)
218 sdl2.RenderClear(ctx.renderer)
219
220 tex := ctx.textures[0]
221 r := sdl2.Rect{
222 x = (WINDOW_WIDTH / 2) - i32(f32(tex.w) * tex.scale * tex.pivot.x),
223 y = (WINDOW_HEIGHT / 2) - i32(f32(tex.h) * tex.scale * tex.pivot.y),
224 w = i32(f32(tex.w) * tex.scale),
225 h = i32(f32(tex.h) * tex.scale),
226 }
227 sdl2.RenderCopy(ctx.renderer, tex.tex, nil, &r)
228 sdl2.RenderPresent(ctx.renderer)
229}
230
231cleanup :: proc() {
232 defer delete(ctx.textures)
233 sdl2.DestroyWindow(ctx.window)
234 sdl2.Quit()
235}
236
237process_input :: proc() {
238 e: sdl2.Event
239
240 for sdl2.PollEvent(&e) {
241 #partial switch(e.type) {
242 case .QUIT:
243 ctx.should_close = true
244 case .KEYDOWN:
245 #partial switch(e.key.keysym.sym) {
246 case .ESCAPE:
247 ctx.should_close = true
248 }
249 }
250 }
251}
252
253update :: proc() {
254 breathe_speed := math.PI * 0.35
255 time_running := ctx.frame_start - ctx.app_start
256 angle := breathe_speed * time_running
257
258 ctx.textures[0].scale = 0.75 + f32(math.sin(angle)) * 0.5
259 ctx.textures[0].pivot = { 0.5 + f32(math.cos(angle)), 0.5 + f32(math.sin(angle)) }
260}
261
262loop :: proc() {
263 ctx.frame_start = ctx.app_start
264 ctx.frame_elapsed = 0.001
265
266 for !ctx.should_close {
267 process_input()
268 update()
269 draw()
270
271 ctx.frame_end = f64(sdl2.GetPerformanceCounter()) / f64(sdl2.GetPerformanceFrequency())
272 ctx.frame_elapsed = ctx.frame_end - ctx.frame_start
273 ctx.frame_start = ctx.frame_end
274 }
275}
276
277main :: proc() {
278 context.logger = log.create_console_logger()
279
280 if res := init_sdl(); !res {
281 log.errorf("Initialization failed.")
282 os.exit(1)
283 }
284
285 if res := init_resources(); !res {
286 log.errorf("Couldn't initialize resources.")
287 os.exit(1)
288 }
289 defer cleanup()
290
291
292
293
294 ctx.app_start = f64(sdl2.GetPerformanceCounter()) / f64(sdl2.GetPerformanceFrequency())
295
296 loop()
297
298 now := f64(sdl2.GetPerformanceCounter()) / f64(sdl2.GetPerformanceFrequency())
299 elapsed := now - ctx.app_start
300 log.infof("Finished in %v seconds!\n", elapsed)
301}