karl2d.odin 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690
  1. package karl2d
  2. import win32 "core:sys/windows"
  3. import "base:runtime"
  4. import "core:mem"
  5. import "core:log"
  6. import "core:math"
  7. import "core:math/linalg"
  8. import "core:slice"
  9. import "core:image"
  10. import "core:image/bmp"
  11. import "core:image/png"
  12. import "core:image/tga"
  13. _ :: bmp
  14. _ :: png
  15. _ :: tga
  16. // Opens a window and initializes some internal state. The internal state will use `allocator` for
  17. // all dynamically allocated memory. The return value can be ignored unless you need to later call
  18. // `set_state`.
  19. init :: proc(window_width: int, window_height: int, window_title: string,
  20. allocator := context.allocator, loc := #caller_location) -> ^State {
  21. win32.SetProcessDPIAware()
  22. s = new(State, allocator, loc)
  23. s.allocator = allocator
  24. s.custom_context = context
  25. CLASS_NAME :: "karl2d"
  26. instance := win32.HINSTANCE(win32.GetModuleHandleW(nil))
  27. s.run = true
  28. s.width = window_width
  29. s.height = window_height
  30. cls := win32.WNDCLASSW {
  31. lpfnWndProc = window_proc,
  32. lpszClassName = CLASS_NAME,
  33. hInstance = instance,
  34. hCursor = win32.LoadCursorA(nil, win32.IDC_ARROW),
  35. }
  36. window_proc :: proc "stdcall" (hwnd: win32.HWND, msg: win32.UINT, wparam: win32.WPARAM, lparam: win32.LPARAM) -> win32.LRESULT {
  37. context = s.custom_context
  38. switch msg {
  39. case win32.WM_DESTROY:
  40. win32.PostQuitMessage(0)
  41. s.run = false
  42. case win32.WM_CLOSE:
  43. s.run = false
  44. case win32.WM_KEYDOWN:
  45. key := VK_MAP[wparam]
  46. s.keys_went_down[key] = true
  47. s.keys_is_held[key] = true
  48. case win32.WM_KEYUP:
  49. key := VK_MAP[wparam]
  50. s.keys_is_held[key] = false
  51. s.keys_went_up[key] = true
  52. }
  53. return win32.DefWindowProcW(hwnd, msg, wparam, lparam)
  54. }
  55. win32.RegisterClassW(&cls)
  56. r: win32.RECT
  57. r.right = i32(window_width)
  58. r.bottom = i32(window_height)
  59. style := win32.WS_OVERLAPPEDWINDOW | win32.WS_VISIBLE
  60. win32.AdjustWindowRect(&r, style, false)
  61. hwnd := win32.CreateWindowW(CLASS_NAME,
  62. win32.utf8_to_wstring(window_title),
  63. style,
  64. 100, 10, r.right - r.left, r.bottom - r.top,
  65. nil, nil, instance, nil)
  66. s.window = hwnd
  67. assert(hwnd != nil, "Failed creating window")
  68. s.rb = BACKEND_D3D11
  69. rb_alloc_error: runtime.Allocator_Error
  70. s.rb_state, rb_alloc_error = mem.alloc(s.rb.state_size())
  71. log.assertf(rb_alloc_error == nil, "Failed allocating memory for rendering backend: %v", rb_alloc_error)
  72. s.proj_matrix = make_default_projection(window_width, window_height)
  73. s.view_matrix = 1
  74. s.rb.init(s.rb_state, uintptr(hwnd), window_width, window_height, allocator, loc)
  75. white_rect: [16*16*4]u8
  76. slice.fill(white_rect[:], 255)
  77. s.shape_drawing_texture = s.rb.load_texture(white_rect[:], 16, 16)
  78. return s
  79. }
  80. // Closes the window and cleans up the internal state.
  81. shutdown :: proc() {
  82. s.rb.destroy_texture(s.shape_drawing_texture)
  83. s.rb.shutdown()
  84. win32.DestroyWindow(s.window)
  85. a := s.allocator
  86. free(s.rb_state, a)
  87. free(s, a)
  88. s = nil
  89. }
  90. // Clear the backbuffer with supplied color.
  91. clear :: proc(color: Color) {
  92. s.rb.clear(color)
  93. }
  94. // Present the backbuffer. Call at end of frame to make everything you've drawn appear on the screen.
  95. present :: proc() {
  96. s.rb.present()
  97. }
  98. // Call at start or end of frame to process all events that have arrived to the window.
  99. //
  100. // WARNING: Not calling this will make your program impossible to interact with.
  101. process_events :: proc() {
  102. s.keys_went_up = {}
  103. s.keys_went_down = {}
  104. msg: win32.MSG
  105. for win32.PeekMessageW(&msg, nil, 0, 0, win32.PM_REMOVE) {
  106. win32.TranslateMessage(&msg)
  107. win32.DispatchMessageW(&msg)
  108. }
  109. }
  110. /* Flushes the current batch. This sends off everything to the GPU that has been queued in the
  111. current batch. Normally, you do not need to do this manually. It is done automatically when these
  112. procedures run:
  113. present
  114. set_camera
  115. set_shader
  116. TODO: complete this list and motivate why it needs to happen on those procs (or do that in the
  117. docs for those procs).
  118. */
  119. draw_current_batch :: proc() {
  120. s.rb.draw_current_batch()
  121. }
  122. // Can be used to restore the internal state using the pointer returned by `init`. Useful after
  123. // reloading the library (for example, when doing code hot reload).
  124. set_internal_state :: proc(state: ^State) {
  125. s = state
  126. s.rb.set_internal_state(s.rb_state)
  127. }
  128. get_screen_width :: proc() -> int {
  129. return s.rb.get_swapchain_width()
  130. }
  131. get_screen_height :: proc() -> int {
  132. return s.rb.get_swapchain_height()
  133. }
  134. key_went_down :: proc(key: Keyboard_Key) -> bool {
  135. return s.keys_went_down[key]
  136. }
  137. key_went_up :: proc(key: Keyboard_Key) -> bool {
  138. return s.keys_went_up[key]
  139. }
  140. key_is_held :: proc(key: Keyboard_Key) -> bool {
  141. return s.keys_is_held[key]
  142. }
  143. // Returns true if the user has tried to close the window.
  144. window_should_close :: proc() -> bool {
  145. return !s.run
  146. }
  147. set_window_position :: proc(x: int, y: int) {
  148. // TODO: Does x, y respect monitor DPI?
  149. win32.SetWindowPos(
  150. s.window,
  151. {},
  152. i32(x),
  153. i32(y),
  154. 0,
  155. 0,
  156. win32.SWP_NOACTIVATE | win32.SWP_NOZORDER | win32.SWP_NOSIZE,
  157. )
  158. }
  159. set_window_size :: proc(width: int, height: int) {
  160. panic("Not implemented")
  161. }
  162. set_camera :: proc(camera: Maybe(Camera)) {
  163. if camera == s.batch_camera {
  164. return
  165. }
  166. s.rb.draw_current_batch()
  167. s.batch_camera = camera
  168. s.proj_matrix = make_default_projection(s.width, s.height)
  169. if c, c_ok := camera.?; c_ok {
  170. origin_trans := linalg.matrix4_translate(vec3_from_vec2(-c.origin))
  171. translate := linalg.matrix4_translate(vec3_from_vec2(c.target))
  172. rot := linalg.matrix4_rotate_f32(c.rotation * math.RAD_PER_DEG, {0, 0, 1})
  173. camera_matrix := translate * rot * origin_trans
  174. s.view_matrix = linalg.inverse(camera_matrix)
  175. s.proj_matrix[0, 0] *= c.zoom
  176. s.proj_matrix[1, 1] *= c.zoom
  177. } else {
  178. s.view_matrix = 1
  179. }
  180. s.rb.set_view_projection_matrix(s.proj_matrix * s.view_matrix)
  181. }
  182. load_texture_from_file :: proc(filename: string) -> Texture {
  183. img, img_err := image.load_from_file(filename, options = {.alpha_add_if_missing}, allocator = context.temp_allocator)
  184. if img_err != nil {
  185. log.errorf("Error loading texture %v: %v", filename, img_err)
  186. return {}
  187. }
  188. backend_tex := s.rb.load_texture(img.pixels.buf[:], img.width, img.height)
  189. return {
  190. handle = backend_tex,
  191. width = img.width,
  192. height = img.height,
  193. }
  194. }
  195. destroy_texture :: proc(tex: Texture) {
  196. s.rb.destroy_texture(tex.handle)
  197. }
  198. draw_rect :: proc(r: Rect, c: Color) {
  199. if s.batch_texture != TEXTURE_NONE && s.batch_texture != s.shape_drawing_texture {
  200. s.rb.draw_current_batch()
  201. }
  202. s.batch_texture = s.shape_drawing_texture
  203. _tmp_set_batch_texture(s.batch_texture)
  204. s.rb.batch_vertex({r.x, r.y}, {0, 0}, c)
  205. s.rb.batch_vertex({r.x + r.w, r.y}, {1, 0}, c)
  206. s.rb.batch_vertex({r.x + r.w, r.y + r.h}, {1, 1}, c)
  207. s.rb.batch_vertex({r.x, r.y}, {0, 0}, c)
  208. s.rb.batch_vertex({r.x + r.w, r.y + r.h}, {1, 1}, c)
  209. s.rb.batch_vertex({r.x, r.y + r.h}, {0, 1}, c)
  210. }
  211. draw_rect_outline :: proc(r: Rect, thickness: f32, color: Color) {
  212. t := thickness
  213. // Based on DrawRectangleLinesEx from Raylib
  214. top := Rect {
  215. r.x,
  216. r.y,
  217. r.w,
  218. t,
  219. }
  220. bottom := Rect {
  221. r.x,
  222. r.y + r.h - t,
  223. r.w,
  224. t,
  225. }
  226. left := Rect {
  227. r.x,
  228. r.y + t,
  229. t,
  230. r.h - t * 2,
  231. }
  232. right := Rect {
  233. r.x + r.w - t,
  234. r.y + t,
  235. t,
  236. r.h - t * 2,
  237. }
  238. draw_rect(top, color)
  239. draw_rect(bottom, color)
  240. draw_rect(left, color)
  241. draw_rect(right, color)
  242. }
  243. draw_texture :: proc(tex: Texture, pos: Vec2, tint := WHITE) {
  244. draw_texture_ex(
  245. tex,
  246. {0, 0, f32(tex.width), f32(tex.height)},
  247. {pos.x, pos.y, f32(tex.width), f32(tex.height)},
  248. {},
  249. 0,
  250. tint,
  251. )
  252. }
  253. draw_texture_rect :: proc(tex: Texture, rect: Rect, pos: Vec2, tint := WHITE) {
  254. draw_texture_ex(
  255. tex,
  256. rect,
  257. {pos.x, pos.y, rect.w, rect.h},
  258. {},
  259. 0,
  260. tint,
  261. )
  262. }
  263. draw_texture_ex :: proc(tex: Texture, src: Rect, dst: Rect, origin: Vec2, rotation: f32, tint := WHITE) {
  264. if tex.width == 0 || tex.height == 0 {
  265. return
  266. }
  267. if s.batch_texture != TEXTURE_NONE && s.batch_texture != tex.handle {
  268. s.rb.draw_current_batch()
  269. }
  270. r := dst
  271. r.x -= origin.x
  272. r.y -= origin.y
  273. s.batch_texture = tex.handle
  274. _tmp_set_batch_texture(tex.handle)
  275. tl, tr, bl, br: Vec2
  276. // Rotation adapted from Raylib's "DrawTexturePro"
  277. if rotation == 0 {
  278. x := dst.x - origin.x
  279. y := dst.y - origin.y
  280. tl = { x, y }
  281. tr = { x + dst.w, y }
  282. bl = { x, y + dst.h }
  283. br = { x + dst.w, y + dst.h }
  284. } else {
  285. sin_rot := math.sin(rotation * math.RAD_PER_DEG)
  286. cos_rot := math.cos(rotation * math.RAD_PER_DEG)
  287. x := dst.x
  288. y := dst.y
  289. dx := -origin.x
  290. dy := -origin.y
  291. tl = {
  292. x + dx * cos_rot - dy * sin_rot,
  293. y + dx * sin_rot + dy * cos_rot,
  294. }
  295. tr = {
  296. x + (dx + dst.w) * cos_rot - dy * sin_rot,
  297. y + (dx + dst.w) * sin_rot + dy * cos_rot,
  298. }
  299. bl = {
  300. x + dx * cos_rot - (dy + dst.h) * sin_rot,
  301. y + dx * sin_rot + (dy + dst.h) * cos_rot,
  302. }
  303. br = {
  304. x + (dx + dst.w) * cos_rot - (dy + dst.h) * sin_rot,
  305. y + (dx + dst.w) * sin_rot + (dy + dst.h) * cos_rot,
  306. }
  307. }
  308. ts := Vec2{f32(tex.width), f32(tex.height)}
  309. up := Vec2{src.x, src.y} / ts
  310. us := Vec2{src.w, src.h} / ts
  311. c := tint
  312. s.rb.batch_vertex(tl, up, c)
  313. s.rb.batch_vertex(tr, up + {us.x, 0}, c)
  314. s.rb.batch_vertex(br, up + us, c)
  315. s.rb.batch_vertex(tl, up, c)
  316. s.rb.batch_vertex(br, up + us, c)
  317. s.rb.batch_vertex(bl, up + {0, us.y}, c)
  318. }
  319. Rendering_Backend :: struct {
  320. state_size: proc() -> int,
  321. init: proc(state: rawptr, window_handle: uintptr, swapchain_width, swapchain_height: int,
  322. allocator := context.allocator, loc := #caller_location),
  323. shutdown: proc(),
  324. clear: proc(color: Color),
  325. present: proc(),
  326. draw_current_batch: proc(),
  327. set_internal_state: proc(state: rawptr),
  328. load_texture: proc(data: []u8, width: int, height: int) -> Texture_Handle,
  329. destroy_texture: proc(handle: Texture_Handle),
  330. get_swapchain_width: proc() -> int,
  331. get_swapchain_height: proc() -> int,
  332. set_view_projection_matrix: proc(m: Mat4),
  333. batch_vertex: proc(v: Vec2, uv: Vec2, color: Color),
  334. }
  335. State :: struct {
  336. allocator: runtime.Allocator,
  337. custom_context: runtime.Context,
  338. rb: Rendering_Backend,
  339. rb_state: rawptr,
  340. keys_went_down: #sparse [Keyboard_Key]bool,
  341. keys_went_up: #sparse [Keyboard_Key]bool,
  342. keys_is_held: #sparse [Keyboard_Key]bool,
  343. window: win32.HWND,
  344. width: int,
  345. height: int,
  346. run: bool,
  347. shape_drawing_texture: Texture_Handle,
  348. batch_camera: Maybe(Camera),
  349. batch_texture: Texture_Handle,
  350. view_matrix: Mat4,
  351. proj_matrix: Mat4,
  352. }
  353. VK_MAP := [255]Keyboard_Key {
  354. win32.VK_A = .A,
  355. win32.VK_B = .B,
  356. win32.VK_C = .C,
  357. win32.VK_D = .D,
  358. win32.VK_E = .E,
  359. win32.VK_F = .F,
  360. win32.VK_G = .G,
  361. win32.VK_H = .H,
  362. win32.VK_I = .I,
  363. win32.VK_J = .J,
  364. win32.VK_K = .K,
  365. win32.VK_L = .L,
  366. win32.VK_M = .M,
  367. win32.VK_N = .N,
  368. win32.VK_O = .O,
  369. win32.VK_P = .P,
  370. win32.VK_Q = .Q,
  371. win32.VK_R = .R,
  372. win32.VK_S = .S,
  373. win32.VK_T = .T,
  374. win32.VK_U = .U,
  375. win32.VK_V = .V,
  376. win32.VK_W = .W,
  377. win32.VK_X = .X,
  378. win32.VK_Y = .Y,
  379. win32.VK_Z = .Z,
  380. win32.VK_LEFT = .Left,
  381. win32.VK_RIGHT = .Right,
  382. win32.VK_UP = .Up,
  383. win32.VK_DOWN = .Down,
  384. }
  385. @(private="file")
  386. s: ^State
  387. // --------------------------------
  388. // old, non migrated API below
  389. get_default_shader: proc() -> Shader_Handle : _get_default_shader
  390. set_scissor_rect: proc(scissor_rect: Maybe(Rect)) : _set_scissor_rect
  391. set_shader: proc(shader: Shader_Handle) : _set_shader
  392. //set_vertex_value :: _set_vertex_value
  393. draw_circle: proc(center: Vec2, radius: f32, color: Color) : _draw_circle
  394. draw_line: proc(start: Vec2, end: Vec2, thickness: f32, color: Color) : _draw_line
  395. load_shader: proc(shader_source: string, layout_formats: []Shader_Input_Format = {}) -> Shader_Handle : _load_shader
  396. destroy_shader: proc(shader: Shader_Handle) : _destroy_shader
  397. get_shader_constant_location: proc(shader: Shader_Handle, name: string) -> Shader_Constant_Location : _get_shader_constant_location
  398. set_shader_constant :: _set_shader_constant
  399. set_shader_constant_mat4: proc(shader: Shader_Handle, loc: Shader_Constant_Location, val: matrix[4,4]f32) : _set_shader_constant_mat4
  400. set_shader_constant_f32: proc(shader: Shader_Handle, loc: Shader_Constant_Location, val: f32) : _set_shader_constant_f32
  401. set_shader_constant_vec2: proc(shader: Shader_Handle, loc: Shader_Constant_Location, val: Vec2) : _set_shader_constant_vec2
  402. Shader_Input_Format :: enum {
  403. Unknown,
  404. RGBA32_Float,
  405. RGBA8_Norm,
  406. RGBA8_Norm_SRGB,
  407. RG32_Float,
  408. R32_Float,
  409. }
  410. // WARNING: Not proper text rendering yet... No font support etc
  411. draw_text: proc(text: string, pos: Vec2, font_size: f32, color: Color) : _draw_text
  412. screen_to_world: proc(pos: Vec2, camera: Camera) -> Vec2 : _screen_to_world
  413. mouse_button_went_down: proc(button: Mouse_Button) -> bool : _mouse_button_pressed
  414. mouse_button_went_up: proc(button: Mouse_Button) -> bool : _mouse_button_released
  415. mouse_button_is_held: proc(button: Mouse_Button) -> bool : _mouse_button_held
  416. get_mouse_wheel_delta: proc() -> f32 : _mouse_wheel_delta
  417. get_mouse_position: proc() -> Vec2 : _mouse_position
  418. Color :: [4]u8
  419. Vec2 :: [2]f32
  420. Mat4 :: matrix[4,4]f32
  421. Vec2i :: [2]int
  422. Rect :: struct {
  423. x, y: f32,
  424. w, h: f32,
  425. }
  426. Texture :: struct {
  427. handle: Texture_Handle,
  428. width: int,
  429. height: int,
  430. }
  431. Camera :: struct {
  432. target: Vec2,
  433. origin: Vec2,
  434. rotation: f32,
  435. zoom: f32,
  436. }
  437. // Support for up to 255 mouse buttons. Cast an int to type `Mouse_Button` to use things outside the
  438. // options presented here.
  439. Mouse_Button :: enum {
  440. Left,
  441. Right,
  442. Middle,
  443. Max = 255,
  444. }
  445. // TODO: These are just copied from raylib, we probably want a list of our own "default colors"
  446. WHITE :: Color { 255, 255, 255, 255 }
  447. BLACK :: Color { 0, 0, 0, 255 }
  448. GRAY :: Color{ 130, 130, 130, 255 }
  449. RED :: Color { 230, 41, 55, 255 }
  450. YELLOW :: Color { 253, 249, 0, 255 }
  451. BLUE :: Color { 0, 121, 241, 255 }
  452. MAGENTA :: Color { 255, 0, 255, 255 }
  453. DARKGRAY :: Color{ 80, 80, 80, 255 }
  454. GREEN :: Color{ 0, 228, 48, 255 }
  455. Shader_Handle :: distinct Handle
  456. SHADER_NONE :: Shader_Handle {}
  457. // Based on Raylib / GLFW
  458. Keyboard_Key :: enum {
  459. None = 0,
  460. // Alphanumeric keys
  461. Apostrophe = 39,
  462. Comma = 44,
  463. Minus = 45,
  464. Period = 46,
  465. Slash = 47,
  466. Zero = 48,
  467. One = 49,
  468. Two = 50,
  469. Three = 51,
  470. Four = 52,
  471. Five = 53,
  472. Six = 54,
  473. Seven = 55,
  474. Eight = 56,
  475. Nine = 57,
  476. Semicolon = 59,
  477. Equal = 61,
  478. A = 65,
  479. B = 66,
  480. C = 67,
  481. D = 68,
  482. E = 69,
  483. F = 70,
  484. G = 71,
  485. H = 72,
  486. I = 73,
  487. J = 74,
  488. K = 75,
  489. L = 76,
  490. M = 77,
  491. N = 78,
  492. O = 79,
  493. P = 80,
  494. Q = 81,
  495. R = 82,
  496. S = 83,
  497. T = 84,
  498. U = 85,
  499. V = 86,
  500. W = 87,
  501. X = 88,
  502. Y = 89,
  503. Z = 90,
  504. Left_Bracket = 91,
  505. Backslash = 92,
  506. Right_Bracket = 93,
  507. Grave = 96,
  508. // Function keys
  509. Space = 32,
  510. Escape = 256,
  511. Enter = 257,
  512. Tab = 258,
  513. Backspace = 259,
  514. Insert = 260,
  515. Delete = 261,
  516. Right = 262,
  517. Left = 263,
  518. Down = 264,
  519. Up = 265,
  520. Page_Up = 266,
  521. Page_Down = 267,
  522. Home = 268,
  523. End = 269,
  524. Caps_Lock = 280,
  525. Scroll_Lock = 281,
  526. Num_Lock = 282,
  527. Print_Screen = 283,
  528. Pause = 284,
  529. F1 = 290,
  530. F2 = 291,
  531. F3 = 292,
  532. F4 = 293,
  533. F5 = 294,
  534. F6 = 295,
  535. F7 = 296,
  536. F8 = 297,
  537. F9 = 298,
  538. F10 = 299,
  539. F11 = 300,
  540. F12 = 301,
  541. Left_Shift = 340,
  542. Left_Control = 341,
  543. Left_Alt = 342,
  544. Left_Super = 343,
  545. Right_Shift = 344,
  546. Right_Control = 345,
  547. Right_Alt = 346,
  548. Right_Super = 347,
  549. Menu = 348,
  550. // Keypad keys
  551. KP_0 = 320,
  552. KP_1 = 321,
  553. KP_2 = 322,
  554. KP_3 = 323,
  555. KP_4 = 324,
  556. KP_5 = 325,
  557. KP_6 = 326,
  558. KP_7 = 327,
  559. KP_8 = 328,
  560. KP_9 = 329,
  561. KP_Decimal = 330,
  562. KP_Divide = 331,
  563. KP_Multiply = 332,
  564. KP_Subtract = 333,
  565. KP_Add = 334,
  566. KP_Enter = 335,
  567. KP_Equal = 336,
  568. }