Refactors, lots and lots of it... plus coodinate space
This commit is contained in:
		| @@ -64,16 +64,22 @@ startup :: proc( live_mem : virtual.Arena, snapshot_mem : []u8, host_logger : ^ | ||||
| 	input      = & input_data[1] | ||||
| 	input_prev = & input_data[0] | ||||
|  | ||||
| 	rl.SetConfigFlags( { rl.ConfigFlag.WINDOW_RESIZABLE, rl.ConfigFlag.WINDOW_TOPMOST } ) | ||||
|  | ||||
| 	// Rough setup of window with rl stuff | ||||
| 	screen_width  = 1920 | ||||
| 	screen_height = 1080 | ||||
| 	window_width  : i32 = 1000 | ||||
| 	window_height : i32 = 600 | ||||
| 	win_title     : cstring = "Sectr Prototype" | ||||
| 	rl.InitWindow( screen_width, screen_height, win_title ) | ||||
| 	rl.InitWindow( window_width, window_height, win_title ) | ||||
| 	log( "Raylib initialized and window opened" ) | ||||
|  | ||||
| 	window := & state.app_window | ||||
| 	window.extent.x = f32(window_width)  * 0.5 | ||||
| 	window.extent.y = f32(window_height) * 0.5 | ||||
|  | ||||
| 	// We do not support non-uniform DPI. | ||||
| 	screen_dpi_scale = rl.GetWindowScaleDPI().x | ||||
| 	screen_dpc       = os_default_dpc * screen_dpi_scale | ||||
| 	window.dpi_scale = rl.GetWindowScaleDPI().x | ||||
| 	window.dpc       = os_default_dpc * window.dpi_scale | ||||
|  | ||||
| 	// Determining current monitor and setting the target frametime based on it.. | ||||
| 	monitor_id         = rl.GetCurrentMonitor() | ||||
| @@ -102,7 +108,7 @@ startup :: proc( live_mem : virtual.Arena, snapshot_mem : []u8, host_logger : ^ | ||||
| 			using project.workspace | ||||
| 			cam = { | ||||
| 				target = { 0, 0 }, | ||||
| 				offset = { f32(screen_width) / 2, f32(screen_height) / 2 }, | ||||
| 				offset = transmute(Vec2) window.extent, | ||||
| 				rotation = 0, | ||||
| 				zoom = 1.0, | ||||
| 			} | ||||
| @@ -116,8 +122,8 @@ startup :: proc( live_mem : virtual.Arena, snapshot_mem : []u8, host_logger : ^ | ||||
|  | ||||
| 			frame_1.color  = Color_BG_TextBox | ||||
| 			// Frame is getting interpreted as points (It doesn't have to be, I'm just doing it...) | ||||
| 			frame_1.width  = 400 | ||||
| 			frame_1.height = 250 | ||||
| 			box_set_size( & frame_1, { 400, 200 } ) | ||||
| 			// frame_1.position = { 1000, 1000 } | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|   | ||||
							
								
								
									
										51
									
								
								code/collision.odin
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								code/collision.odin
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,51 @@ | ||||
| package sectr | ||||
|  | ||||
| import "core:math/linalg" | ||||
|  | ||||
|  | ||||
| // Not sure if I should in the future not do the radius check, | ||||
| // As it maybe be better off as a general proc used in an iteration... | ||||
| box_is_within_view :: proc( box : ^ Box2 ) -> b32 | ||||
| { | ||||
| 	state := get_state(); using state | ||||
| 	screen_extent := app_window.extent | ||||
|  | ||||
| 	screen_bounds_radius := max(screen_extent.x, screen_extent.y) | ||||
| 	box_bounds_radius    := max(box.extent.x,    box.extent.y) | ||||
|  | ||||
| 	cam                 := project.workspace.cam | ||||
| 	cam_box_distance    := linalg.distance(cam.target, box.position) | ||||
| 	acceptable_distance := box_bounds_radius + screen_bounds_radius | ||||
|  | ||||
| 	if cam_box_distance > acceptable_distance { | ||||
| 		return false | ||||
| 	} | ||||
|  | ||||
| 	screen_bounds := view_get_bounds() | ||||
| 	box_bounds    := box_get_bounds( box ) | ||||
|  | ||||
| 	within_bounds : b32 = false | ||||
|  | ||||
| 	when false { | ||||
| 		within_x : b32 = box_bounds.top_left.x > screen_bounds.top_left.x | ||||
| 		within_x &= box_bounds.top_left.x < screen_bounds.bottom_right.x | ||||
|  | ||||
| 		within_y : b32 = box_bounds.top_left.y > screen_bounds.top_left.y | ||||
|  | ||||
| 		state         := get_state(); using state | ||||
| 		screen_extent := state.app_window.extent | ||||
| 		cam           := & project.workspace.cam | ||||
| 		within_x_bounds : b32 = pos.x >= -screen_extent.x && pos.x <= screen_extent.x | ||||
| 		within_y_bounds : b32 = pos.y >= -screen_extent.y && pos.y <= screen_extent.y | ||||
| 	} | ||||
| 	return within_bounds | ||||
| } | ||||
|  | ||||
| is_within_screenspace :: proc( pos : Vec2 ) -> b32 { | ||||
| 	state         := get_state(); using state | ||||
| 	screen_extent := state.app_window.extent | ||||
| 	cam           := & project.workspace.cam | ||||
| 	within_x_bounds : b32 = pos.x >= -screen_extent.x && pos.x <= screen_extent.x | ||||
| 	within_y_bounds : b32 = pos.y >= -screen_extent.y && pos.y <= screen_extent.y | ||||
| 	return within_x_bounds && within_y_bounds | ||||
| } | ||||
							
								
								
									
										35
									
								
								code/entity_box2.odin
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								code/entity_box2.odin
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,35 @@ | ||||
| package sectr | ||||
|  | ||||
| import "core:encoding/json" | ||||
|  | ||||
| import rl "vendor:raylib" | ||||
|  | ||||
| Box2 :: struct { | ||||
| 	position : Vec2, | ||||
| 	extent   : Extents2, | ||||
| 	color    : Color | ||||
| } | ||||
|  | ||||
| box_size :: proc( box : ^ Box2 ) -> AreaSize { | ||||
| 	return transmute(AreaSize) box.extent * 2.0 | ||||
| } | ||||
|  | ||||
| box_get_bounds :: proc( box : ^ Box2 ) -> Bounds2 { | ||||
| 	top_left     := box.position + Vec2 { -box.extent.x,  box.extent.y } | ||||
| 	bottom_right := box.position + Vec2 {  box.extent.x, -box.extent.y } | ||||
| 	return { top_left, bottom_right } | ||||
| } | ||||
|  | ||||
| box_set_size :: proc( box : ^ Box2, size : AreaSize ) { | ||||
| 	box.extent = transmute(Extents2) size * 0.5 | ||||
| } | ||||
|  | ||||
| get_rl_rect :: proc ( box : ^ Box2 ) -> rl.Rectangle { | ||||
| 	rect : rl.Rectangle = { | ||||
| 		x = box.position.x - box.extent.x, | ||||
| 		y = box.position.y - box.extent.y, | ||||
| 		width  = box.extent.x * 2.0, | ||||
| 		height = box.extent.y * 2.0, | ||||
| 	} | ||||
| 	return rect | ||||
| } | ||||
| @@ -26,11 +26,6 @@ Memory :: struct { | ||||
| } | ||||
|  | ||||
| save_snapshot :: proc( snapshot : [^]u8 ) { | ||||
| 	state := get_state()  | ||||
|  | ||||
| 	// state.font_rec_mono_semicasual_reg | ||||
| 	// state.default_font | ||||
|  | ||||
| 	live_ptr := cast( ^ rawptr ) memory.live.curr_block.base | ||||
| 	mem.copy_non_overlapping( & snapshot[0], live_ptr, memory_chunk_size ) | ||||
| } | ||||
| @@ -40,6 +35,12 @@ load_snapshot :: proc( snapshot : [^]u8 ) { | ||||
| 	mem.copy_non_overlapping( live_ptr, snapshot, memory_chunk_size ) | ||||
| } | ||||
|  | ||||
| AppConfig :: struct { | ||||
| 	resolution_width  : uint, | ||||
| 	resolution_height : uint, | ||||
| 	refresh_rate      : uint | ||||
| } | ||||
|  | ||||
| State :: struct { | ||||
| 	input_data : [2] InputState, | ||||
| 	input_prev : ^   InputState, | ||||
| @@ -49,10 +50,8 @@ State :: struct { | ||||
|  | ||||
| 	project : Project, | ||||
|  | ||||
| 	screen_width       : i32, | ||||
| 	screen_height      : i32, | ||||
| 	screen_dpi_scale   : f32, | ||||
| 	screen_dpc         : f32,  // Pixels per cm | ||||
| 	config     : AppConfig, | ||||
| 	app_window : AppWindow, | ||||
|  | ||||
| 	monitor_id         : i32, | ||||
| 	monitor_refresh_hz : i32, | ||||
| @@ -64,10 +63,16 @@ State :: struct { | ||||
| 	default_font                 : Font, | ||||
| } | ||||
|  | ||||
| get_state :: proc() -> (^ State) { | ||||
| get_state :: proc "contextless" () -> ^ State { | ||||
| 	return cast( ^ State ) raw_data( memory.persistent.backing.data ) | ||||
| } | ||||
|  | ||||
| AppWindow :: struct { | ||||
| 	extent    : Extents2, // Window half-size | ||||
| 	dpi_scale : f32,      // Dots per inch scale (provided by raylib via glfw) | ||||
| 	dpc       : f32,      // Dots per centimetre | ||||
| } | ||||
|  | ||||
| Project :: struct { | ||||
| 	path : string, | ||||
| 	name : string, | ||||
| @@ -80,7 +85,7 @@ Workspace :: struct { | ||||
| 	name : string, | ||||
|  | ||||
| 	cam     : Camera, | ||||
| 	frame_1 : Frame | ||||
| 	frame_1 : Box2 | ||||
| } | ||||
|  | ||||
| DebugData :: struct { | ||||
| @@ -89,6 +94,8 @@ DebugData :: struct { | ||||
|  | ||||
| 	draw_debug_text_y : f32, | ||||
|  | ||||
| 	mouse_vis : b32, | ||||
| 	mouse_pos : Vec2, | ||||
| 	cursor_locked     : b32, | ||||
| 	cursor_unlock_pos : Vec2, // Raylib changes the mose position on lock, we want restore the position the user would be in on screen | ||||
| 	mouse_vis         : b32, | ||||
| 	last_mouse_pos    : Vec2, | ||||
| } | ||||
|   | ||||
| @@ -1,27 +0,0 @@ | ||||
| package sectr | ||||
|  | ||||
| Frame :: struct { | ||||
| 	position      : Vec2, | ||||
| 	width, height : f32, | ||||
| 	color         : Color | ||||
| } | ||||
|  | ||||
| get_bounds :: proc( frame : ^ Frame ) -> Bounds2 { | ||||
| 	half_width  := frame.width  / 2 | ||||
| 	half_height := frame.height / 2 | ||||
| 	bottom_left := Vec2 { -half_width, -half_height } | ||||
| 	top_right   := Vec2 {  half_width,  half_height } | ||||
| 	return { bottom_left, top_right } | ||||
| } | ||||
|  | ||||
| get_rect :: proc ( frame : ^ Frame ) -> Rectangle { | ||||
| 	half_width  := frame.width  / 2 | ||||
| 	half_height := frame.height / 2 | ||||
| 	rect : Rectangle = { | ||||
| 		x = frame.position.x - half_width, | ||||
| 		y = frame.position.y - half_height, | ||||
| 		width  = frame.width, | ||||
| 		height = frame.height, | ||||
| 	} | ||||
| 	return rect | ||||
| } | ||||
| @@ -40,5 +40,9 @@ OS_Type                 :: type_of(ODIN_OS) | ||||
|  | ||||
| import rl "vendor:raylib" | ||||
|  | ||||
| Font      :: rl.Font | ||||
| Rectangle :: rl.Rectangle | ||||
| Font :: rl.Font | ||||
|  | ||||
| get_bounds :: proc { | ||||
| 	box_get_bounds, | ||||
| 	view_get_bounds, | ||||
| } | ||||
|   | ||||
| @@ -1,6 +1,8 @@ | ||||
| // TODO(Ed) : This if its gets larget can be moved to its own package | ||||
| package sectr | ||||
|  | ||||
| import "base:runtime" | ||||
|  | ||||
| AnalogAxis  :: f32 | ||||
| AnalogStick :: struct { | ||||
| 	X, Y : f32 | ||||
| @@ -267,9 +269,8 @@ MouseState :: struct { | ||||
| 			side, forward, back, extra : DigitalBtn | ||||
| 		} | ||||
| 	}, | ||||
| 	X, Y, | ||||
| 	vertical_wheel, | ||||
| 	horizontal_wheel : AnalogAxis | ||||
| 	pos, delta                       : Vec2, | ||||
| 	vertical_wheel, horizontal_wheel : AnalogAxis | ||||
| } | ||||
|  | ||||
| InputState :: struct { | ||||
| @@ -308,10 +309,6 @@ poll_input :: proc( old, new : ^ InputState ) | ||||
| 				key_id := cast(KeyboardKey) id | ||||
|  | ||||
| 				is_down := cast(b32) rl.IsKeyDown( to_raylib_key(id) ) | ||||
| 				if is_down { | ||||
| 					nothing := true | ||||
| 					nothing = false | ||||
| 				} | ||||
| 				input_process_digital_btn( entry_old, entry_new, is_down ) | ||||
| 			} | ||||
| 		} | ||||
| @@ -337,14 +334,12 @@ poll_input :: proc( old, new : ^ InputState ) | ||||
|  | ||||
| 			mouse_id := cast(MouseBtn) id | ||||
|  | ||||
| 			is_down := cast(b32) rl.IsMouseButtonPressed( to_raylib_mouse_btn(id) ) | ||||
| 			input_process_digital_btn( & old.mouse.left, & new.mouse.left, is_down ) | ||||
| 			is_down := cast(b32) rl.IsMouseButtonDown( to_raylib_mouse_btn(id) ) | ||||
| 			input_process_digital_btn( old_btn, new_btn, is_down ) | ||||
| 		} | ||||
|  | ||||
| 		mouse_pos := rl.GetMousePosition() | ||||
|  | ||||
| 		new.mouse.X = mouse_pos.x | ||||
| 		new.mouse.Y = mouse_pos.y | ||||
| 		new.mouse.pos            = rl.GetMousePosition() - transmute(Vec2) get_state().app_window.extent | ||||
| 		new.mouse.delta          = rl.GetMouseDelta() | ||||
| 		new.mouse.vertical_wheel = rl.GetMouseWheelMove() | ||||
| 	} | ||||
| } | ||||
|   | ||||
| @@ -5,9 +5,8 @@ import "core:math/linalg" | ||||
| Vec2 :: linalg.Vector2f32 | ||||
| Vec3 :: linalg.Vector3f32 | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| Vec2i :: [2]i32 | ||||
| Vec3i :: [3]i32 | ||||
|  | ||||
| when false { | ||||
| // TODO(Ed) : Evaluate if this is needed | ||||
|   | ||||
| @@ -3,8 +3,76 @@ package sectr | ||||
| import "core:encoding/json" | ||||
| import "core:fmt" | ||||
| import "core:os" | ||||
| import "core:reflect" | ||||
| import "core:runtime" | ||||
| import "core:strings" | ||||
|  | ||||
| @(private="file") | ||||
| assign_int :: proc(val: any, i: $T) -> bool { | ||||
| 	v := reflect.any_core(val) | ||||
| 	switch &dst in v { | ||||
| 	case i8:      dst = i8     (i) | ||||
| 	case i16:     dst = i16    (i) | ||||
| 	case i16le:   dst = i16le  (i) | ||||
| 	case i16be:   dst = i16be  (i) | ||||
| 	case i32:     dst = i32    (i) | ||||
| 	case i32le:   dst = i32le  (i) | ||||
| 	case i32be:   dst = i32be  (i) | ||||
| 	case i64:     dst = i64    (i) | ||||
| 	case i64le:   dst = i64le  (i) | ||||
| 	case i64be:   dst = i64be  (i) | ||||
| 	case i128:    dst = i128   (i) | ||||
| 	case i128le:  dst = i128le (i) | ||||
| 	case i128be:  dst = i128be (i) | ||||
| 	case u8:      dst = u8     (i) | ||||
| 	case u16:     dst = u16    (i) | ||||
| 	case u16le:   dst = u16le  (i) | ||||
| 	case u16be:   dst = u16be  (i) | ||||
| 	case u32:     dst = u32    (i) | ||||
| 	case u32le:   dst = u32le  (i) | ||||
| 	case u32be:   dst = u32be  (i) | ||||
| 	case u64:     dst = u64    (i) | ||||
| 	case u64le:   dst = u64le  (i) | ||||
| 	case u64be:   dst = u64be  (i) | ||||
| 	case u128:    dst = u128   (i) | ||||
| 	case u128le:  dst = u128le (i) | ||||
| 	case u128be:  dst = u128be (i) | ||||
| 	case int:     dst = int    (i) | ||||
| 	case uint:    dst = uint   (i) | ||||
| 	case uintptr: dst = uintptr(i) | ||||
| 	case: return false | ||||
| 	} | ||||
| 	return true | ||||
| } | ||||
|  | ||||
| when false { | ||||
| unmarshal_from_object :: proc( $Type: typeid, object : json.Object ) -> Type | ||||
| { | ||||
| 	result : Type | ||||
| 	type_info := type_info_of(Type) | ||||
| 	#partial switch type in type_info.variant { | ||||
| 		case runtime.Type_Info_Union: | ||||
| 			ensure( true, "This proc doesn't support raw unions" ) | ||||
| 	} | ||||
|  | ||||
| 	base_ptr := uintptr( & result ) | ||||
|  | ||||
| 	field_infos := reflect.struct_fields_zipped(Type) | ||||
| 	for field_info in field_infos | ||||
| 	{ | ||||
| 		field_type := field_info.type.id | ||||
| 		field_ptr := cast(field_type) rawptr( base_ptr + field_info.offset ) | ||||
|  | ||||
| 		#partial switch type in field_info.type.variant { | ||||
| 			case runtime.Type_Info_Integer: | ||||
| 				field_ptr = object[filed_info.name].(json.Integer) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return result | ||||
| } | ||||
| } | ||||
|  | ||||
| Serializer_Version :: 1 | ||||
| Serializer_Loading :: false | ||||
|  | ||||
| @@ -31,12 +99,14 @@ project_serialize :: proc ( project : ^ Project, archive : ^ ArchiveData, is_wri | ||||
| 	options.pretty      = true | ||||
| 	options.use_spaces  = false | ||||
|  | ||||
| 	if is_writting | ||||
| 	{ | ||||
| 		marshal_archive : struct { | ||||
| 	MarshalArchive :: struct { | ||||
| 			version : i32, | ||||
| 			project : Project | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	if is_writting | ||||
| 	{ | ||||
| 		marshal_archive : MarshalArchive | ||||
| 		marshal_archive.version = archive.version | ||||
| 		marshal_archive.project = project^ | ||||
| 		// TODO(Ed): In the future this will be more complicated, as serialization of workspaces and the code database won't be trivial | ||||
| @@ -55,12 +125,30 @@ project_serialize :: proc ( project : ^ Project, archive : ^ ArchiveData, is_wri | ||||
| 		archive_version : i32 = cast(i32) archive_json["version"].(json.Float) | ||||
| 		verify( Serializer_Version != archive_version, "Version mismatch on archive!" ) | ||||
|  | ||||
| 		project_json := archive_json["project"].(json.Object) | ||||
| 		project.name  = project_json["name"].(json.String) | ||||
| 		// Note(Ed) : This works fine for now, but eventually it will most likely break with pointers... | ||||
| 		// We'll most likely set things up so that all refs in the project & workspace are handles. | ||||
| 		marshal_archive : MarshalArchive | ||||
| 		json.unmarshal( archive.data, & marshal_archive, spec = json.Specification.MJSON, allocator = context.temp_allocator ) | ||||
| 		if marshal_archive.version == Serializer_Version { | ||||
| 			project^ = marshal_archive.project | ||||
| 		} | ||||
|  | ||||
| 		// TODO(Ed) : Make this a separate proc | ||||
| 		workspace_json        := project_json["workspace"].(json.Object) | ||||
| 		project.workspace.name = workspace_json["name"].(json.String) | ||||
| 		// Manual unmarshal | ||||
| 		when false | ||||
| 		{ | ||||
| 			project_json := archive_json["project"].(json.Object) | ||||
| 			project.name  = project_json["name"].(json.String) | ||||
|  | ||||
| 			// TODO(Ed) : Make this a separate proc | ||||
| 			workspace_json := project_json["workspace"].(json.Object) | ||||
| 			{ | ||||
| 				using project.workspace | ||||
| 				name = workspace_json["name"].(json.String) | ||||
|  | ||||
| 				// cam = unmarshal_from_object(Camera, workspace_json["camera"].(json.Object) ) | ||||
| 				frame_1 = frame_json_unmarshal( & workspace_json["frame_1"] ) | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		// DEBUG DUD | ||||
| 		options.use_spaces = false | ||||
|   | ||||
							
								
								
									
										38
									
								
								code/serialize_manual_unmarshal.odin
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								code/serialize_manual_unmarshal.odin
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,38 @@ | ||||
| package sectr | ||||
|  | ||||
| import "core:encoding/json" | ||||
| import "core:reflect" | ||||
|  | ||||
| // TODO(Ed) : Generic Unmarshling of json objects (There should be a way I believe todo this generically but the reflect library is not well documented) | ||||
|  | ||||
| vec2_json_unmarshal :: proc ( value : ^ json.Value ) -> Vec2 { | ||||
| 	json_v := value.(json.Array) | ||||
| 	return { | ||||
| 		f32(json_v[0].(json.Float)), | ||||
| 		f32(json_v[1].(json.Float)), | ||||
| 	} | ||||
| } | ||||
|  | ||||
| color_json_unmarshal :: proc ( value : ^ json.Value ) -> Color { | ||||
| 	json_color := value.(json.Array) | ||||
| 	r := u8(json_color[0].(json.Float)) | ||||
| 	g := u8(json_color[1].(json.Float)) | ||||
| 	b := u8(json_color[2].(json.Float)) | ||||
| 	a	:= u8(json_color[3].(json.Float)) | ||||
| 	return { r, g, b, a } | ||||
| } | ||||
|  | ||||
| box_json_unmarshal :: proc ( value : ^ json.Value ) -> Box2 { | ||||
| 	object     := value.(json.Object) | ||||
| 	json_pos   := object["position"].(json.Array) | ||||
|  | ||||
| 	position := Vec2 { f32(json_pos[0].(json.Float)), f32(json_pos[1].(json.Float)) } | ||||
| 	width    := f32( object["width"] .(json.Float)) | ||||
| 	height   := f32( object["height"].(json.Float)) | ||||
|  | ||||
| 	return { | ||||
| 		position = position, | ||||
| 		extent   = { width, height }, | ||||
| 		color    = color_json_unmarshal( & object["color"] ), | ||||
| 	}, | ||||
| } | ||||
							
								
								
									
										104
									
								
								code/space.odin
									
									
									
									
									
								
							
							
						
						
									
										104
									
								
								code/space.odin
									
									
									
									
									
								
							| @@ -16,42 +16,110 @@ when ODIN_OS == OS_Type.Windows { | ||||
| 	// 1 inch = 2.54 cm, 96 inch * 2.54 = 243.84 DPC | ||||
| } | ||||
|  | ||||
| f32_cm_to_pixels :: proc ( cm : f32 ) -> f32 { | ||||
| 	state := get_state(); using state | ||||
| cm_to_pixels :: proc( cm : f32 ) -> f32 { | ||||
| 	screen_dpc := get_state().app_window.dpc | ||||
| 	return cm * screen_dpc | ||||
| } | ||||
|  | ||||
| vec2_cm_to_pixels :: proc ( v : Vec2 ) -> Vec2 { | ||||
| 	state := get_state(); using state | ||||
| vec2_cm_to_pixels :: proc( v : Vec2 ) -> Vec2 { | ||||
| 	screen_dpc := get_state().app_window.dpc | ||||
| 	return v * screen_dpc | ||||
| } | ||||
|  | ||||
| points_to_pixels :: proc ( points : f32 ) -> f32 { | ||||
| 	state := get_state(); using state | ||||
| points_to_pixels :: proc( points : f32 ) -> f32 { | ||||
| 	screen_dpc := get_state().app_window.dpc | ||||
| 	cm_per_pixel := 1.0 / screen_dpc | ||||
| 	return points * DPT_DPC * cm_per_pixel | ||||
| } | ||||
|  | ||||
| pixels_to_points :: proc ( pixels : f32 ) -> f32 { | ||||
| 	state := get_state(); using state | ||||
| pixels_to_points :: proc( pixels : f32 ) -> f32 { | ||||
| 	screen_dpc := get_state().app_window.dpc | ||||
| 	cm_per_pixel := 1.0 / screen_dpc | ||||
| 	return pixels * cm_per_pixel * Points_Per_Centimetre | ||||
| } | ||||
|  | ||||
| vec2_points_to_pixels :: proc ( vpoints : Vec2 ) -> Vec2 { | ||||
| 	screen_dpc := get_state().app_window.dpc | ||||
| 	cm_per_pixel := 1.0 / screen_dpc | ||||
| 	return vpoints * DPT_DPC * cm_per_pixel | ||||
| } | ||||
|  | ||||
| Camera :: rl.Camera2D | ||||
|  | ||||
| get_half_screen :: proc() -> AreaSize { | ||||
| 	state := get_state(); using state | ||||
| 	return { | ||||
| 		f32(screen_width)  / 2, | ||||
| 		f32(screen_height) / 2, | ||||
| 	} | ||||
| } | ||||
| AreaSize :: distinct Vec2 | ||||
|  | ||||
| Bounds2 :: struct { | ||||
| 	bottom_left, top_right : Vec2 | ||||
| 	top_left, bottom_right : Vec2 | ||||
| } | ||||
|  | ||||
| AreaSize :: struct { | ||||
| 	width, height : f32 | ||||
| BoundsCorners2 :: struct { | ||||
| 	top_left, top_right, bottom_left, bottom_right : Vec2 | ||||
| } | ||||
|  | ||||
| Extents2  :: distinct Vec2 | ||||
| Extents2i :: distinct Vec2i | ||||
|  | ||||
| bounds2_radius :: proc( bounds : Bounds2 ) -> f32 { | ||||
| 	return max( bounds.bottom_right.x, bounds.top_left.y ) | ||||
| } | ||||
|  | ||||
| extent_from_size :: proc ( size : AreaSize ) -> Extents2 { | ||||
| 	return transmute(Extents2) size * 2.0 | ||||
| } | ||||
|  | ||||
| screen_size :: proc "contextless" () -> AreaSize { | ||||
| 	extent := get_state().app_window.extent | ||||
| 	return transmute(AreaSize) (extent * 2.0) | ||||
| } | ||||
|  | ||||
| screen_get_corners :: proc() -> BoundsCorners2 { | ||||
| 	state := get_state(); using state | ||||
| 	screen_extent := state.app_window.extent | ||||
| 	top_left     := Vec2 { -screen_extent.x,  screen_extent.y } | ||||
| 	top_right    := Vec2 {  screen_extent.x,  screen_extent.y } | ||||
| 	bottom_left  := Vec2 { -screen_extent.x, -screen_extent.y } | ||||
| 	bottom_right := Vec2 {  screen_extent.x, -screen_extent.y } | ||||
| 	return { top_left, top_right, bottom_left, bottom_right } | ||||
| } | ||||
|  | ||||
| view_get_bounds :: proc() -> Bounds2 { | ||||
| 	state         := get_state(); using state | ||||
| 	cam           := & project.workspace.cam | ||||
| 	screen_extent := state.app_window.extent | ||||
| 	top_left     := cam.target + Vec2 { -screen_extent.x,  screen_extent.y } | ||||
| 	bottom_right := cam.target + Vec2 {  screen_extent.x, -screen_extent.y } | ||||
| 	return { top_left, bottom_right } | ||||
| } | ||||
|  | ||||
| view_get_corners :: proc() -> BoundsCorners2 { | ||||
| 	state          := get_state(); using state | ||||
| 	cam            := & project.workspace.cam | ||||
| 	cam_zoom_ratio := 1.0 / cam.zoom | ||||
| 	screen_extent  := state.app_window.extent * cam_zoom_ratio | ||||
| 	top_left     := cam.target + Vec2 { -screen_extent.x,  screen_extent.y } | ||||
| 	top_right    := cam.target + Vec2 {  screen_extent.x,  screen_extent.y } | ||||
| 	bottom_left  := cam.target + Vec2 { -screen_extent.x, -screen_extent.y } | ||||
| 	bottom_right := cam.target + Vec2 {  screen_extent.x, -screen_extent.y } | ||||
| 	return { top_left, top_right, bottom_left, bottom_right } | ||||
| } | ||||
|  | ||||
| screen_to_render :: proc( pos : Vec2 ) -> Vec2 { | ||||
| 	screen_extent := transmute(Vec2) get_state().project.workspace.cam.offset | ||||
| 	return pos + { screen_extent.x, -screen_extent.y } | ||||
| } | ||||
|  | ||||
| world_screen_extent :: proc () -> Extents2 { | ||||
| 	state          := get_state(); using state | ||||
| 	cam_zoom_ratio := 1.0 / project.workspace.cam.zoom | ||||
| 	return app_window.extent * cam_zoom_ratio | ||||
| } | ||||
|  | ||||
| world_to_screen_pos :: proc( position : Vec2 ) -> Vec2 { | ||||
| 	return { position.x, position.y * -1 } | ||||
| } | ||||
|  | ||||
| world_to_screen_no_zoom :: proc( position : Vec2 ) -> Vec2 { | ||||
| 	state          := get_state(); using state | ||||
| 	cam_zoom_ratio := 1.0 / state.project.workspace.cam.zoom | ||||
| 	return { position.x, position.y * -1 } * cam_zoom_ratio | ||||
| } | ||||
|   | ||||
| @@ -3,18 +3,22 @@ package sectr | ||||
| import    "core:unicode/utf8" | ||||
| import rl "vendor:raylib" | ||||
|  | ||||
| debug_text :: proc( content : string, pos : Vec2, size : f32 = 16.0, color : rl.Color = rl.WHITE, font : rl.Font = {} ) | ||||
| debug_draw_text :: proc( content : string, pos : Vec2, size : f32 = 16.0, color : rl.Color = rl.WHITE, font : rl.Font = {} ) | ||||
| { | ||||
| 	state := get_state(); using state | ||||
|  | ||||
| 	if len( content ) == 0 { | ||||
| 		return | ||||
| 	} | ||||
| 	runes := utf8.string_to_runes( content, context.temp_allocator ) | ||||
|  | ||||
| 	font := font | ||||
| 	if ( font.chars == nil ) { | ||||
| 		font = get_state().default_font | ||||
| 	if ( font.glyphs == nil ) { | ||||
| 		font = default_font | ||||
| 	} | ||||
|  | ||||
| 	pos := screen_to_render(pos) | ||||
|  | ||||
| 	rl.DrawTextCodepoints( font, | ||||
| 		raw_data(runes), cast(i32) len(runes), | ||||
| 		position = transmute(rl.Vector2) pos, | ||||
| @@ -22,3 +26,74 @@ debug_text :: proc( content : string, pos : Vec2, size : f32 = 16.0, color : rl. | ||||
| 		spacing  = 0.0, | ||||
| 		tint     = color ); | ||||
| } | ||||
|  | ||||
| // Raylib's equivalent doesn't take a length for the string (making it a pain in the ass) | ||||
| // So this is a 1:1 copy except it takes Odin strings | ||||
| measure_text_size :: proc ( text : string, font : Font, font_size, spacing : f32 ) -> AreaSize | ||||
| { | ||||
| 	// This is a static var within raylib. We don't have getter access to it. | ||||
| 	@static text_line_spacing : f32 = 15 | ||||
|  | ||||
| 	text_size : AreaSize | ||||
|  | ||||
| 	if font.texture.id == 0 || len(text) == 0 { | ||||
| 		return text_size | ||||
| 	} | ||||
|  | ||||
| 	temp_byte_counter : i32 = 0 // Used to count longer text line num chars | ||||
| 	byte_counter      : i32 = 0 | ||||
|  | ||||
| 	text_width      : f32 = 0.0 | ||||
| 	temp_text_width : f32 = 0.0 // Used to counter longer text line width | ||||
|  | ||||
| 	text_height := cast(f32) font.baseSize | ||||
| 	scale_factor := font_size / text_height | ||||
|  | ||||
| 	letter : rune | ||||
| 	index  : i32 = 0 | ||||
|  | ||||
| 	for id : i32 = 0; id < i32(len(text)); { | ||||
| 		byte_counter += 1 | ||||
|  | ||||
| 		next : i32 = 0 | ||||
|  | ||||
| 		ctext := cast(cstring) ( & raw_data( text )[id] ) | ||||
| 		letter = rl.GetCodepointNext( ctext, & next ) | ||||
| 		index  = rl.GetGlyphIndex( font, letter ) | ||||
|  | ||||
| 		id += 1 | ||||
|  | ||||
| 		if letter != rune('\n') | ||||
| 		{ | ||||
| 			if font.glyphs[index].advanceX != 0 { | ||||
| 				text_width += f32(font.glyphs[index].advanceX) | ||||
| 			} | ||||
| 			else { | ||||
| 				text_width += font.recs[index].width + f32(font.glyphs[index].offsetX) | ||||
| 			} | ||||
| 		} | ||||
| 		else | ||||
| 		{ | ||||
| 			if temp_text_width < text_width { | ||||
| 				temp_text_width = text_width | ||||
| 			} | ||||
| 			byte_counter = 0 | ||||
| 			text_width   = 0 | ||||
|  | ||||
| 			text_height += text_line_spacing | ||||
|  | ||||
| 			if temp_byte_counter < byte_counter { | ||||
| 				temp_byte_counter = byte_counter | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	if temp_text_width < text_width { | ||||
| 		temp_text_width = text_width | ||||
| 	} | ||||
|  | ||||
| 	text_size.x = temp_text_width * scale_factor + f32(temp_byte_counter - 1) * spacing | ||||
| 	text_size.y = text_height * scale_factor | ||||
|  | ||||
| 	return text_size | ||||
| } | ||||
|   | ||||
| @@ -8,78 +8,114 @@ render :: proc() | ||||
| { | ||||
| 	state  := get_state(); using state | ||||
| 	replay := & memory.replay | ||||
| 	cam    := & project.workspace.cam | ||||
| 	win_extent := state.app_window.extent | ||||
|  | ||||
| 	half_screen_width  := f32(screen_width)  / 2 | ||||
| 	half_screen_height := f32(screen_height) / 2 | ||||
| 	screen_top_left : Vec2 = { | ||||
| 		-win_extent.x  + cam.target.x, | ||||
| 		-win_extent.y + cam.target.y, | ||||
| 	} | ||||
|  | ||||
| 	rl.BeginDrawing() | ||||
| 	rl.ClearBackground( Color_BG ) | ||||
| 	rl.BeginMode2D( project.workspace.cam ) | ||||
| 	// rl.BeginMode3D( project.workspace.cam ) | ||||
| 	defer { | ||||
| 		fps_msg := fmt.tprint( "FPS:", rl.GetFPS() ) | ||||
| 		debug_text( fps_msg, { -half_screen_width, -half_screen_height }, color = rl.GREEN ) | ||||
| 	render_mode_2d() | ||||
| 	{ | ||||
| 		fps_msg       := fmt.tprint( "FPS:", rl.GetFPS() ) | ||||
| 		fps_msg_width := measure_text_size( fps_msg, default_font, 16.0, 0.0 ).x | ||||
| 		fps_msg_pos   := screen_get_corners().top_right - { fps_msg_width, 0 } | ||||
| 		debug_draw_text( fps_msg, fps_msg_pos, color = rl.GREEN ) | ||||
|  | ||||
| 		rl.EndMode2D() | ||||
| 		// rl.EndMode3D() | ||||
| 		rl.EndDrawing() | ||||
| 		// Note(Ed) : Polls input as well. | ||||
| 		debug_text :: proc( format : string, args : ..any ) | ||||
| 		{ | ||||
| 			@static draw_text_scratch : [Kilobyte * 64]u8 | ||||
|  | ||||
| 			state := get_state(); using state | ||||
| 			if debug.draw_debug_text_y > 800 { | ||||
| 				debug.draw_debug_text_y = 50 | ||||
| 			} | ||||
|  | ||||
| 			cam            := & project.workspace.cam | ||||
| 			screen_corners := screen_get_corners() | ||||
|  | ||||
| 			position   := screen_corners.top_right | ||||
| 			position.x -= 300 | ||||
| 			position.y += debug.draw_debug_text_y | ||||
|  | ||||
| 			content := fmt.bprintf( draw_text_scratch[:], format, ..args ) | ||||
| 			debug_draw_text( content, position ) | ||||
|  | ||||
| 			debug.draw_debug_text_y += 16 | ||||
| 		} | ||||
|  | ||||
| 		// Debug Text | ||||
| 		{ | ||||
| 			debug_text( "Screen Width : %v", rl.GetScreenWidth () ) | ||||
| 			debug_text( "Screen Height: %v", rl.GetScreenHeight() ) | ||||
| 			if replay.mode == ReplayMode.Record { | ||||
| 				debug_text( "Recording Input") | ||||
| 			} | ||||
| 			if replay.mode == ReplayMode.Playback { | ||||
| 				debug_text( "Replaying Input") | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		if debug.mouse_vis { | ||||
| 			debug_text( "Position: %v", input.mouse.pos ) | ||||
| 			rect_pos :=  transmute(Vec2) state.app_window.extent + input.mouse.pos | ||||
|  | ||||
| 			width : f32 = 32 | ||||
| 			mouse_rect : rl.Rectangle | ||||
| 			mouse_rect.x      = rect_pos.x - width * 0.5 | ||||
| 			mouse_rect.y      = rect_pos.y - width * 0.5 | ||||
| 			mouse_rect.width  = width | ||||
| 			mouse_rect.height = width | ||||
| 			rl.DrawRectangleRec( mouse_rect, Color_White ) | ||||
| 		} | ||||
|  | ||||
| 		debug.draw_debug_text_y = 50 | ||||
| 	} | ||||
| 	rl.EndDrawing() | ||||
| } | ||||
|  | ||||
| render_mode_2d :: proc() { | ||||
| 	state  := get_state(); using state | ||||
| 	cam    := & project.workspace.cam | ||||
| 	win_extent := state.app_window.extent | ||||
|  | ||||
| 	rl.BeginMode2D( project.workspace.cam ) | ||||
|  | ||||
| 	// Frame 1 | ||||
| 	{ | ||||
| 		frame_1 := & project.workspace.frame_1 | ||||
| 		rect := get_rect( frame_1 ) | ||||
| 		rect := get_rl_rect( frame_1 ) | ||||
| 		screen_pos := world_to_screen_pos(frame_1.position) | ||||
|  | ||||
| 		rect.width  = points_to_pixels( rect.width ) | ||||
| 		rect.height = points_to_pixels( rect.height ) | ||||
| 		rect.x      = points_to_pixels( rect.x ) | ||||
| 		rect.y      = points_to_pixels( rect.y ) | ||||
| 		rect.x      = points_to_pixels( screen_pos.x ) | ||||
| 		rect.y      = points_to_pixels( screen_pos.y ) | ||||
|  | ||||
| 		rl.DrawRectangleRec( rect, frame_1.color ) | ||||
| 		// rl.DrawRectangleV( frame_1.position, { frame_1.width, frame_1.height }, frame_1.color ) | ||||
| 		// rl.DrawRectanglePro( rect, frame_1.position, 0, frame_1.color ) | ||||
| 	} | ||||
|  | ||||
| 	debug_draw_text :: proc( format : string, args : ..any ) | ||||
| 	{ | ||||
| 		@static draw_text_scratch : [Kilobyte * 64]u8 | ||||
| 		// Frame 2 | ||||
| 		when false | ||||
| 		{ | ||||
| 			frame_1 := & project.workspace.frame_1 | ||||
| 			rect := get_rl_rect( frame_1 ) | ||||
| 			screen_pos := world_to_screen_pos(frame_1.position) | ||||
|  | ||||
| 		state := get_state(); using state | ||||
| 		if debug.draw_debug_text_y > 800 { | ||||
| 			debug.draw_debug_text_y = 50 | ||||
| 			rect.width  = points_to_pixels( rect.width ) | ||||
| 			rect.height = points_to_pixels( rect.height ) | ||||
| 			rect.x      = points_to_pixels( screen_pos.x ) | ||||
| 			rect.y      = points_to_pixels( screen_pos.y ) | ||||
|  | ||||
| 			rl.DrawRectangleRec( rect, frame_1.color ) | ||||
| 			// rl.DrawRectangleV( frame_1.position, { frame_1.width, frame_1.height }, frame_1.color ) | ||||
| 			// rl.DrawRectanglePro( rect, frame_1.position, 0, frame_1.color ) | ||||
| 		} | ||||
|  | ||||
| 		content := fmt.bprintf( draw_text_scratch[:], format, ..args ) | ||||
| 		debug_text( content, { 25, debug.draw_debug_text_y } ) | ||||
|  | ||||
| 		debug.draw_debug_text_y += 16 | ||||
| 	} | ||||
|  | ||||
| 	// Debug Text | ||||
| 	{ | ||||
| 		debug_draw_text( "Screen Width : %v", rl.GetScreenWidth () ) | ||||
| 		debug_draw_text( "Screen Height: %v", rl.GetScreenHeight() ) | ||||
| 		if replay.mode == ReplayMode.Record { | ||||
| 			debug_draw_text( "Recording Input") | ||||
| 		} | ||||
| 		if replay.mode == ReplayMode.Playback { | ||||
| 			debug_draw_text( "Replaying Input") | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	if debug.mouse_vis { | ||||
| 		width : f32 = 32 | ||||
| 		pos   := debug.mouse_pos | ||||
|  | ||||
| 		debug_draw_text( "Position: %v", rl.GetMousePosition() ) | ||||
|  | ||||
| 		mouse_rect : rl.Rectangle | ||||
| 		mouse_rect.x      = pos.x - width/2 | ||||
| 		mouse_rect.y      = pos.y - width/2 | ||||
| 		mouse_rect.width  = width | ||||
| 		mouse_rect.height = width | ||||
| 		// rl.DrawRectangleRec( mouse_rect, Color_White ) | ||||
| 	} | ||||
|  | ||||
| 	debug.draw_debug_text_y = 50 | ||||
| } | ||||
| 	rl.EndMode2D() | ||||
| } | ||||
|   | ||||
| @@ -1,5 +1,6 @@ | ||||
| package sectr | ||||
|  | ||||
| import "base:runtime" | ||||
| import "core:fmt" | ||||
|  | ||||
| import rl "vendor:raylib" | ||||
| @@ -19,6 +20,7 @@ DebugActions :: struct { | ||||
| 	cam_move_left  : b32, | ||||
| 	cam_move_down  : b32, | ||||
| 	cam_move_right : b32, | ||||
| 	cam_mouse_pan  : b32, | ||||
| } | ||||
|  | ||||
| poll_debug_actions :: proc( actions : ^ DebugActions, input : ^ InputState ) | ||||
| @@ -26,6 +28,13 @@ poll_debug_actions :: proc( actions : ^ DebugActions, input : ^ InputState ) | ||||
| 	using actions | ||||
| 	using input | ||||
|  | ||||
| 	modifier_active := keyboard.right_alt.ended_down || | ||||
| 		keyboard.right_control.ended_down || | ||||
| 		keyboard.right_shift.ended_down || | ||||
| 		keyboard.left_alt.ended_down || | ||||
| 		keyboard.left_control.ended_down || | ||||
| 		keyboard.left_shift.ended_down | ||||
|  | ||||
| 	load_project = keyboard.left_control.ended_down && pressed( keyboard.O ) | ||||
| 	save_project = keyboard.left_control.ended_down && pressed( keyboard.S ) | ||||
|  | ||||
| @@ -35,10 +44,12 @@ poll_debug_actions :: proc( actions : ^ DebugActions, input : ^ InputState ) | ||||
|  | ||||
| 	show_mouse_pos = keyboard.right_alt.ended_down && pressed(keyboard.M) | ||||
|  | ||||
| 	cam_move_up    = keyboard.W.ended_down | ||||
| 	cam_move_left  = keyboard.A.ended_down | ||||
| 	cam_move_down  = keyboard.S.ended_down | ||||
| 	cam_move_right = keyboard.D.ended_down | ||||
| 	cam_move_up    = keyboard.W.ended_down && ( ! modifier_active || keyboard.left_shift.ended_down ) | ||||
| 	cam_move_left  = keyboard.A.ended_down && ( ! modifier_active || keyboard.left_shift.ended_down ) | ||||
| 	cam_move_down  = keyboard.S.ended_down && ( ! modifier_active || keyboard.left_shift.ended_down ) | ||||
| 	cam_move_right = keyboard.D.ended_down && ( ! modifier_active || keyboard.left_shift.ended_down ) | ||||
|  | ||||
| 	cam_mouse_pan = mouse.right.ended_down && ! pressed(mouse.right) | ||||
| } | ||||
|  | ||||
| update :: proc( delta_time : f64 ) -> b32 | ||||
| @@ -46,6 +57,14 @@ update :: proc( delta_time : f64 ) -> b32 | ||||
| 	state  := get_state(); using state | ||||
| 	replay := & memory.replay | ||||
|  | ||||
| 	if rl.IsWindowResized() { | ||||
| 		window := & state.app_window | ||||
| 		window.extent.x = f32(rl.GetScreenWidth())  * 0.5 | ||||
| 		window.extent.y = f32(rl.GetScreenHeight()) * 0.5 | ||||
|  | ||||
| 		project.workspace.cam.offset = transmute(Vec2) window.extent | ||||
| 	} | ||||
|  | ||||
| 	state.input, state.input_prev = swap( state.input, state.input_prev ) | ||||
| 	poll_input( state.input_prev, state.input ) | ||||
|  | ||||
| @@ -110,14 +129,12 @@ update :: proc( delta_time : f64 ) -> b32 | ||||
| 		debug.mouse_vis = !debug.mouse_vis | ||||
| 	} | ||||
|  | ||||
| 	debug.mouse_pos = { input.mouse.X, input.mouse.Y } | ||||
|  | ||||
| 	// Camera Manual Nav | ||||
| 	{ | ||||
| 		move_speed        : f32 = 200.0 | ||||
| 		zoom_sensitiviity : f32 = 3.5 | ||||
| 		digital_move_speed : f32 = 200.0 | ||||
| 		zoom_sensitiviity  : f32 = 3.5 | ||||
|  | ||||
| 		cam := & project.workspace.cam | ||||
| 		cam      := & project.workspace.cam | ||||
| 		cam.zoom *= 1 + input.mouse.vertical_wheel * zoom_sensitiviity * f32(delta_time) | ||||
| 		cam.zoom  = clamp( cam.zoom, 0.05, 10.0 ) | ||||
|  | ||||
| @@ -125,10 +142,20 @@ update :: proc( delta_time : f64 ) -> b32 | ||||
| 			- cast(f32) i32(debug_actions.cam_move_left) + cast(f32) i32(debug_actions.cam_move_right), | ||||
| 		  - cast(f32) i32(debug_actions.cam_move_up)   + cast(f32) i32(debug_actions.cam_move_down), | ||||
| 		} | ||||
| 		move_velocity *= move_speed * f32(delta_time) | ||||
| 		move_velocity *= digital_move_speed * f32(delta_time) | ||||
| 		cam.target    += move_velocity | ||||
|  | ||||
| 		if debug_actions.cam_mouse_pan | ||||
| 		{ | ||||
| 			if is_within_screenspace(input.mouse.pos) { | ||||
| 				pan_velocity := input.mouse.delta * (1/cam.zoom) | ||||
| 				cam.target   -= pan_velocity | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	debug.last_mouse_pos = input.mouse.pos | ||||
|  | ||||
| 	should_shutdown : b32 = ! cast(b32) rl.WindowShouldClose() | ||||
| 	return should_shutdown | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user