karl2d.odin 16 KB

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