mirror of
				https://github.com/Ed94/HandmadeHero.git
				synced 2025-10-31 06:50:54 -07:00 
			
		
		
		
	Day 13 complete
This commit is contained in:
		
							
								
								
									
										96
									
								
								docs/Day 013.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										96
									
								
								docs/Day 013.md
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,96 @@ | |||||||
|  | # Day 13 | ||||||
|  |  | ||||||
|  | Started to lifting input, not happy about my abstraction to gamepad state in hindsight. Which is kinda lead by how Casey is doing it. | ||||||
|  |  | ||||||
|  | The reality of how input is actually done in the games; that I've worked on; is we don't abstract the gamepad. | ||||||
|  | We just make a layout per device we decide to support (Dualsense/shock, xinput, nintendo, etc) and then just see what device is mapped to what player. This allows us to have a tailored layout to the device. Doing otherwise usually leads to some discrepancy in the UX for that layout for the user. | ||||||
|  |  | ||||||
|  | Casey has no concept of a player nor a device beyond xinput for this engine (at least up to this point), which is most likely fine for the scope of this project. | ||||||
|  |  | ||||||
|  | For the purposes of my version of Handmade I will make 3 devices that could be mapped to a `Controller` struct: `Keyboard & Mouse`, `Dualsense`, and `XInput`.   | ||||||
|  | This is to retain implementation Casey is already making while also natively supporting the Dualsense controller from sony which I personally perfer to use. | ||||||
|  |  | ||||||
|  | From the 3 layer abstraction (platform/engine/game):   | ||||||
|  | Platform will deal with the raw polling (and most likely down the line have a thread dedicated to running it as fast as possible).   | ||||||
|  | It will provide the engine layer the pad states in the following struct: | ||||||
|  |  | ||||||
|  | ```cpp | ||||||
|  | struct InputState | ||||||
|  | { | ||||||
|  |     ControllerState Controllers[4]; | ||||||
|  | }; | ||||||
|  | ``` | ||||||
|  |  | ||||||
|  | There can only be four controllers available at a time and are equivalnet to the input aspect of a local player in Unreal. | ||||||
|  | (Sort of)   | ||||||
|  | Each controller can be assigned the following: | ||||||
|  |  | ||||||
|  | ```cpp | ||||||
|  | struct ControllerState | ||||||
|  | { | ||||||
|  |     KeyboardState*     Keyboard; | ||||||
|  |     MousesState*       Mouse; | ||||||
|  |     XInputPadState*    XPad; | ||||||
|  |     DualsensePadState* DSPad; | ||||||
|  | }; | ||||||
|  | ``` | ||||||
|  |  | ||||||
|  | If any are null that means the controller has no assignment.   | ||||||
|  | Each pad state is the latest state but the platform keeps a record of the previous frame's state to compare against. | ||||||
|  |  | ||||||
|  | Exmaple for Dualsense: | ||||||
|  |  | ||||||
|  | ```cpp | ||||||
|  | struct DualsensePadState | ||||||
|  | { | ||||||
|  |     struct | ||||||
|  |     { | ||||||
|  |         AnalogStick Left; | ||||||
|  |         AnalogStick Right; | ||||||
|  |     } Stick; | ||||||
|  |  | ||||||
|  |     AnalogAxis L2; | ||||||
|  |     AnalogAxis R2; | ||||||
|  |  | ||||||
|  |     union { | ||||||
|  |         DigitalBtn Btns[14]; | ||||||
|  |         struct { | ||||||
|  |             struct { | ||||||
|  |                 DigitalBtn Up; | ||||||
|  |                 DigitalBtn Down; | ||||||
|  |                 DigitalBtn Left; | ||||||
|  |                 DigitalBtn Right; | ||||||
|  |             } DPad; | ||||||
|  |             DigitalBtn X; | ||||||
|  |             DigitalBtn Circle; | ||||||
|  |             DigitalBtn Square; | ||||||
|  |             DigitalBtn Triangle; | ||||||
|  |             DigitalBtn Share; | ||||||
|  |             DigitalBtn Options; | ||||||
|  |             DigitalBtn L1; | ||||||
|  |             DigitalBtn R1; | ||||||
|  |         }; | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     b32 using_analog() | ||||||
|  |     { | ||||||
|  |     return true; | ||||||
|  |     }; | ||||||
|  | }; | ||||||
|  | ``` | ||||||
|  |  | ||||||
|  | The game layer handles mapping an input state of a controller to an action binding.   | ||||||
|  | Possible action binding when we get to that point: | ||||||
|  |  | ||||||
|  | ```cpp | ||||||
|  | struct ActionBinding | ||||||
|  | { | ||||||
|  |     String       InternalName; | ||||||
|  |     StringCached LocalizedName; | ||||||
|  |  | ||||||
|  |     DigitalBtnBinding* BtnBinds; | ||||||
|  |     AxisBinding*       AxisBinds; | ||||||
|  |     StickBinding*      StickBinds; | ||||||
|  |     MouseBinding*      MouseBinds; | ||||||
|  | }; | ||||||
|  | ``` | ||||||
| @@ -1,17 +1,21 @@ | |||||||
| #include "engine.h" | #include "engine.h" | ||||||
|  | #include "win32.h" | ||||||
|  |  | ||||||
| NS_ENGINE_BEGIN | NS_ENGINE_BEGIN | ||||||
|  |  | ||||||
|  |  | ||||||
| using GetSoundSampleValueFn = s16( SoundBuffer* sound_buffer ); | using GetSoundSampleValueFn = s16( SoundBuffer* sound_buffer ); | ||||||
|  |  | ||||||
| global s32 SoundWavePeriod = 250; | global s32 SoundTest_ToneVolume = 3000; | ||||||
|  | global s32 SoundTest_WavePeriod = 0; | ||||||
|  | global s32 SoundTest_WaveToneHz = 262; | ||||||
|  |  | ||||||
|  |  | ||||||
| internal s16 | internal s16 | ||||||
| square_wave_sample_value( SoundBuffer* sound_buffer ) | square_wave_sample_value( SoundBuffer* sound_buffer ) | ||||||
| { | { | ||||||
| 	s16 sample_value = (sound_buffer->RunningSampleIndex /  (sound_buffer->WavePeriod /2)) % 2 ? | 	s16 sample_value = (sound_buffer->RunningSampleIndex /  (SoundTest_WavePeriod /2)) % 2 ? | ||||||
| 		sound_buffer->ToneVolume : - sound_buffer->ToneVolume; | 		SoundTest_ToneVolume : - SoundTest_ToneVolume; | ||||||
|  |  | ||||||
| 	return sample_value; | 	return sample_value; | ||||||
| } | } | ||||||
| @@ -21,10 +25,11 @@ sine_wave_sample_value( SoundBuffer* sound_buffer ) | |||||||
| { | { | ||||||
| 	local_persist f32 time = 0.f; | 	local_persist f32 time = 0.f; | ||||||
|  |  | ||||||
|  | 	// time =  TAU * (f32)sound_buffer->RunningSampleIndex / (f32)SoundTest_WavePeriod; | ||||||
| 	f32 sine_value   = sinf( time ); | 	f32 sine_value   = sinf( time ); | ||||||
| 	s16 sample_value = scast(u16, sine_value * sound_buffer->ToneVolume); | 	s16 sample_value = scast(u16, sine_value * SoundTest_ToneVolume); | ||||||
|  |  | ||||||
| 	time += TAU * 1.0f / scast(f32, sound_buffer->WavePeriod ); | 	time += TAU * 1.0f / scast(f32, SoundTest_WavePeriod ); | ||||||
| 	return sample_value; | 	return sample_value; | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -35,7 +40,11 @@ output_sound( SoundBuffer* sound_buffer, GetSoundSampleValueFn* get_sample_value | |||||||
| 	for ( u32 sample_index = 0; sample_index < sound_buffer->NumSamples; ++ sample_index ) | 	for ( u32 sample_index = 0; sample_index < sound_buffer->NumSamples; ++ sample_index ) | ||||||
| 	{ | 	{ | ||||||
| 		s16 sample_value = get_sample_value( sound_buffer ); | 		s16 sample_value = get_sample_value( sound_buffer ); | ||||||
| 		++ sound_buffer->RunningSampleIndex; | 		sound_buffer->RunningSampleIndex++; | ||||||
|  |  | ||||||
|  | 		// char ms_timing_debug[256] {}; | ||||||
|  | 		// wsprintfA( ms_timing_debug, "sample_value: %d\n", sample_value ); | ||||||
|  | 		// OutputDebugStringA( ms_timing_debug ); | ||||||
|  |  | ||||||
| 		*sample_out = sample_value; | 		*sample_out = sample_value; | ||||||
| 		++ sample_out; | 		++ sample_out; | ||||||
| @@ -92,14 +101,133 @@ render_weird_graident(OffscreenBuffer* buffer, u32 x_offset, u32 y_offset ) | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| internal | b32 input_using_analog() | ||||||
| void update_and_render( OffscreenBuffer* back_buffer, SoundBuffer* sound_buffer |  | ||||||
| 	// Temp (for feature parity) |  | ||||||
| 	, u32 x_offset, u32 y_offset |  | ||||||
| ) |  | ||||||
| { | { | ||||||
|  | 	return false; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // TODO : I rather expose the back_buffer and sound_buffer using getters for access in any function. | ||||||
|  | internal void | ||||||
|  | update_and_render( InputState* input, OffscreenBuffer* back_buffer, SoundBuffer* sound_buffer ) | ||||||
|  | { | ||||||
|  | 	// Graphics & Input Test | ||||||
|  | 	local_persist u32 x_offset = 0; | ||||||
|  | 	local_persist u32 y_offset = 0; | ||||||
|  |  | ||||||
|  | 	// Wave Sound Test | ||||||
|  | 	local_persist bool wave_switch = false; | ||||||
|  |  | ||||||
|  | #if 0 | ||||||
|  | 	if ( input_using_analog() ) | ||||||
|  | 	{ | ||||||
|  | 		// TODO(Ed) : Use analog movement tuning | ||||||
|  | 	} | ||||||
|  | 	else | ||||||
|  | 	{ | ||||||
|  | 		// TODO(Ed) : Use digital movement tuning | ||||||
|  | 	} | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | #if 1 | ||||||
|  | 	do_once_start | ||||||
|  | 	{ | ||||||
|  | 		SoundTest_WavePeriod = sound_buffer->SamplesPerSecond / SoundTest_WaveToneHz; | ||||||
|  | 	} | ||||||
|  | 	do_once_end | ||||||
|  |  | ||||||
|  | 	ControllerState* controller = & input->Controllers[0]; | ||||||
|  |  | ||||||
|  | 	if ( controller->DSPad ) | ||||||
|  | 	{ | ||||||
|  | 		DualsensePadState* pad = controller->DSPad; | ||||||
|  |  | ||||||
|  | 		x_offset += pad->DPad.Right.State; | ||||||
|  | 		x_offset -= pad->DPad.Left.State; | ||||||
|  | 		y_offset += pad->DPad.Down.State; | ||||||
|  | 		y_offset -= pad->DPad.Up.State; | ||||||
|  |  | ||||||
|  | 		x_offset += pad->Stick.Left.X.End; | ||||||
|  | 		y_offset += pad->Stick.Left.Y.End; | ||||||
|  |  | ||||||
|  | 		if ( pad->Triangle.State ) | ||||||
|  | 		{ | ||||||
|  | 			SoundTest_ToneVolume += 10; | ||||||
|  | 		} | ||||||
|  | 		if ( pad->Circle.State ) | ||||||
|  | 		{ | ||||||
|  | 			SoundTest_ToneVolume -= 10; | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		if ( pad->Square.State ) | ||||||
|  | 		{ | ||||||
|  | 			SoundTest_WaveToneHz  += 1; | ||||||
|  | 			SoundTest_WavePeriod  = sound_buffer->SamplesPerSecond / SoundTest_WaveToneHz; | ||||||
|  | 		} | ||||||
|  | 		if ( pad->X.State ) | ||||||
|  | 		{ | ||||||
|  | 			SoundTest_WaveToneHz  -= 1; | ||||||
|  | 			SoundTest_WavePeriod  = sound_buffer->SamplesPerSecond / SoundTest_WaveToneHz; | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		if ( pad->Options.State ) | ||||||
|  | 		{ | ||||||
|  | 			wave_switch ^= true; | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		if ( pad->Share.State ) | ||||||
|  | 		{ | ||||||
|  | 			// TODO(Ed) : Add rumble test | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	else if ( controller->XPad ) | ||||||
|  | 	{ | ||||||
|  | 		XInputPadState* pad = controller->XPad; | ||||||
|  |  | ||||||
|  | 		x_offset += pad->DPad.Right.State; | ||||||
|  | 		x_offset -= pad->DPad.Left.State; | ||||||
|  | 		y_offset += pad->DPad.Down.State; | ||||||
|  | 		y_offset -= pad->DPad.Up.State; | ||||||
|  |  | ||||||
|  | 		x_offset += pad->Stick.Left.X.End; | ||||||
|  | 		y_offset += pad->Stick.Left.Y.End; | ||||||
|  |  | ||||||
|  | 		if ( pad->Y.State ) | ||||||
|  | 		{ | ||||||
|  | 			SoundTest_ToneVolume += 10; | ||||||
|  | 		} | ||||||
|  | 		if ( pad->B.State ) | ||||||
|  | 		{ | ||||||
|  | 			SoundTest_ToneVolume -= 10; | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		if ( pad->X.State ) | ||||||
|  | 		{ | ||||||
|  | 			SoundTest_WaveToneHz  += 1; | ||||||
|  | 			SoundTest_WavePeriod  = sound_buffer->SamplesPerSecond / SoundTest_WaveToneHz; | ||||||
|  | 		} | ||||||
|  | 		if ( pad->A.State ) | ||||||
|  | 		{ | ||||||
|  | 			SoundTest_WaveToneHz  -= 1; | ||||||
|  | 			SoundTest_WavePeriod  = sound_buffer->SamplesPerSecond / SoundTest_WaveToneHz; | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		if ( pad->Start.State ) | ||||||
|  | 		{ | ||||||
|  | 			wave_switch ^= true; | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		if ( pad->Back.State ) | ||||||
|  | 		{ | ||||||
|  | 			// TODO(Ed) : Add rumble test | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | #endif | ||||||
|  |  | ||||||
| 	// TODO(Ed) : Allow sample offsets here for more robust platform options | 	// TODO(Ed) : Allow sample offsets here for more robust platform options | ||||||
| 	output_sound( sound_buffer, square_wave_sample_value ); | 	if ( ! wave_switch ) | ||||||
|  | 		output_sound( sound_buffer, sine_wave_sample_value ); | ||||||
|  | 	else | ||||||
|  | 		output_sound( sound_buffer, square_wave_sample_value ); | ||||||
|  |  | ||||||
| 	render_weird_graident( back_buffer, x_offset, y_offset ); | 	render_weird_graident( back_buffer, x_offset, y_offset ); | ||||||
| } | } | ||||||
|   | |||||||
							
								
								
									
										135
									
								
								project/engine.h
									
									
									
									
									
								
							
							
						
						
									
										135
									
								
								project/engine.h
									
									
									
									
									
								
							| @@ -20,22 +20,143 @@ struct OffscreenBuffer | |||||||
| 	u32        BytesPerPixel; | 	u32        BytesPerPixel; | ||||||
| }; | }; | ||||||
|  |  | ||||||
|  | // TODO : Will be gutting this once we have other stuff lifted. | ||||||
| struct SoundBuffer | struct SoundBuffer | ||||||
| { | { | ||||||
| 	s16* Samples; | 	s16* Samples; | ||||||
| 	u32  RunningSampleIndex; | 	u32  RunningSampleIndex; | ||||||
| 	s32  SamplesPerSecond; | 	s32  SamplesPerSecond; | ||||||
| 	s32  NumSamples; | 	s32  NumSamples; | ||||||
| 	s32  ToneVolume; |  | ||||||
| 	s32  WaveToneHz; |  | ||||||
| 	s32  WavePeriod; |  | ||||||
| }; | }; | ||||||
|  |  | ||||||
|  | struct DigitalBtn | ||||||
|  | { | ||||||
|  | 	s32 HalfTransitions; | ||||||
|  | 	b32 State; | ||||||
|  | }; | ||||||
|  | #define DigitalBtn_Up   0 | ||||||
|  | #define DigitalBtn_Down 1 | ||||||
|  |  | ||||||
|  | struct AnalogAxis | ||||||
|  | { | ||||||
|  | 	f32 Start; | ||||||
|  | 	f32 End; | ||||||
|  | 	f32 Min; | ||||||
|  | 	f32 Max; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | struct AnalogStick | ||||||
|  | { | ||||||
|  | 	AnalogAxis X; | ||||||
|  | 	AnalogAxis Y; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | struct KeyboardState | ||||||
|  | { | ||||||
|  | 	DigitalBtn W; | ||||||
|  | 	DigitalBtn A; | ||||||
|  | 	DigitalBtn S; | ||||||
|  | 	DigitalBtn D; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | struct MousesState | ||||||
|  | { | ||||||
|  | 	DigitalBtn Left; | ||||||
|  | 	DigitalBtn Middle; | ||||||
|  | 	DigitalBtn Right; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | struct XInputPadState | ||||||
|  | { | ||||||
|  | 	struct | ||||||
|  | 	{ | ||||||
|  | 		AnalogStick Left; | ||||||
|  | 		AnalogStick Right; | ||||||
|  | 	} Stick; | ||||||
|  |  | ||||||
|  | 	AnalogAxis LeftTrigger; | ||||||
|  | 	AnalogAxis RightTrigger; | ||||||
|  |  | ||||||
|  | 	union { | ||||||
|  | 		DigitalBtn Btns[14]; | ||||||
|  | 		struct { | ||||||
|  | 			struct { | ||||||
|  | 				DigitalBtn Up; | ||||||
|  | 				DigitalBtn Down; | ||||||
|  | 				DigitalBtn Left; | ||||||
|  | 				DigitalBtn Right; | ||||||
|  | 			} DPad; | ||||||
|  | 			DigitalBtn A; | ||||||
|  | 			DigitalBtn B; | ||||||
|  | 			DigitalBtn X; | ||||||
|  | 			DigitalBtn Y; | ||||||
|  | 			DigitalBtn Back; | ||||||
|  | 			DigitalBtn Start; | ||||||
|  | 			DigitalBtn LeftShoulder; | ||||||
|  | 			DigitalBtn RightShoulder; | ||||||
|  | 		}; | ||||||
|  | 	}; | ||||||
|  |  | ||||||
|  | 	b32 using_analog() | ||||||
|  | 	{ | ||||||
|  | 		return true; | ||||||
|  | 	}; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | struct DualsensePadState | ||||||
|  | { | ||||||
|  | 	struct | ||||||
|  | 	{ | ||||||
|  | 		AnalogStick Left; | ||||||
|  | 		AnalogStick Right; | ||||||
|  | 	} Stick; | ||||||
|  |  | ||||||
|  | 	AnalogAxis L2; | ||||||
|  | 	AnalogAxis R2; | ||||||
|  |  | ||||||
|  | 	union { | ||||||
|  | 		DigitalBtn Btns[14]; | ||||||
|  | 		struct { | ||||||
|  | 			struct { | ||||||
|  | 				DigitalBtn Up; | ||||||
|  | 				DigitalBtn Down; | ||||||
|  | 				DigitalBtn Left; | ||||||
|  | 				DigitalBtn Right; | ||||||
|  | 			} DPad; | ||||||
|  | 			DigitalBtn X; | ||||||
|  | 			DigitalBtn Circle; | ||||||
|  | 			DigitalBtn Square; | ||||||
|  | 			DigitalBtn Triangle; | ||||||
|  | 			DigitalBtn Share; | ||||||
|  | 			DigitalBtn Options; | ||||||
|  | 			DigitalBtn L1; | ||||||
|  | 			DigitalBtn R1; | ||||||
|  | 		}; | ||||||
|  | 	}; | ||||||
|  |  | ||||||
|  | 	b32 using_analog() | ||||||
|  | 	{ | ||||||
|  | 		return true; | ||||||
|  | 	}; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | struct ControllerState | ||||||
|  | { | ||||||
|  | 	KeyboardState*     Keyboard; | ||||||
|  | 	MousesState*       Mouse; | ||||||
|  | 	XInputPadState*    XPad; | ||||||
|  | 	DualsensePadState* DSPad; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | struct InputState | ||||||
|  | { | ||||||
|  | 	ControllerState Controllers[4]; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | b32 input_using_analog(); | ||||||
|  |  | ||||||
| // Needs a contextual reference to four things: | // Needs a contextual reference to four things: | ||||||
| // Timing, Input, Bitmap Buffer, Sound Buffer | // Timing, Input, Bitmap Buffer, Sound Buffer | ||||||
| void update_and_render( OffscreenBuffer* back_buffer, SoundBuffer* sound_buffer | void update_and_render( InputState* input, OffscreenBuffer* back_buffer, SoundBuffer* sound_buffer ); | ||||||
| 	// Temp (for feature parity) |  | ||||||
| 	, u32 x_offset, u32 y_offset |  | ||||||
| ); |  | ||||||
|  |  | ||||||
| NS_ENGINE_END | NS_ENGINE_END | ||||||
|   | |||||||
							
								
								
									
										10
									
								
								project/platform/generics.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								project/platform/generics.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,10 @@ | |||||||
|  | #pragma once | ||||||
|  |  | ||||||
|  | template< class Type > | ||||||
|  | void swap( Type& a, Type& b ) | ||||||
|  | { | ||||||
|  | 	Type | ||||||
|  | 	temp = a; | ||||||
|  | 	a    = b; | ||||||
|  | 	b    = temp; | ||||||
|  | } | ||||||
| @@ -89,8 +89,16 @@ struct WinDimensions | |||||||
| 	u32 Height; | 	u32 Height; | ||||||
| }; | }; | ||||||
|  |  | ||||||
| HRESULT WINAPI | // TODO : This will def need to be looked over. | ||||||
| DirectSoundCreate(LPGUID lpGuid, LPDIRECTSOUND* ppDS, LPUNKNOWN  pUnkOuter ); | struct SoundOutput | ||||||
|  | { | ||||||
|  | 	DWORD IsPlaying; | ||||||
|  | 	u32   RunningSampleIndex; | ||||||
|  | 	s32   LatencySampleCount; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  |  | ||||||
|  | HRESULT WINAPI DirectSoundCreate(LPGUID lpGuid, LPDIRECTSOUND* ppDS, LPUNKNOWN  pUnkOuter ); | ||||||
|  |  | ||||||
| using DirectSoundCreateFn = HRESULT WINAPI (LPGUID lpGuid, LPDIRECTSOUND* ppDS, LPUNKNOWN  pUnkOuter ); | using DirectSoundCreateFn = HRESULT WINAPI (LPGUID lpGuid, LPDIRECTSOUND* ppDS, LPUNKNOWN  pUnkOuter ); | ||||||
| global DirectSoundCreateFn* direct_sound_create; | global DirectSoundCreateFn* direct_sound_create; | ||||||
| @@ -177,16 +185,6 @@ init_sound(HWND window_handle, s32 samples_per_second, s32 buffer_size ) | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| struct SoundOutput |  | ||||||
| { |  | ||||||
| 	DWORD IsPlaying; |  | ||||||
| 	u32   RunningSampleIndex; |  | ||||||
| 	s32   WaveToneHz; |  | ||||||
| 	s32   WavePeriod; |  | ||||||
| 	s32   ToneVolume; |  | ||||||
| 	s32   LatencySampleCount; |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| internal void | internal void | ||||||
| ds_clear_sound_buffer( SoundOutput* sound_output ) | ds_clear_sound_buffer( SoundOutput* sound_output ) | ||||||
| { | { | ||||||
| @@ -494,6 +492,15 @@ main_window_callback( | |||||||
|  |  | ||||||
| NS_WIN32_END | NS_WIN32_END | ||||||
|  |  | ||||||
|  | internal void | ||||||
|  | input_process_digital_btn( engine::DigitalBtn* old_state, engine::DigitalBtn* new_state, u32 raw_btns, u32 btn_flag ) | ||||||
|  | { | ||||||
|  | #define had_transition() ( old_state->State == new_state->State ) | ||||||
|  | 	new_state->State           = (raw_btns & btn_flag); | ||||||
|  | 	new_state->HalfTransitions = had_transition() ? 1 : 0; | ||||||
|  | #undef had_transition | ||||||
|  | } | ||||||
|  |  | ||||||
| int CALLBACK | int CALLBACK | ||||||
| WinMain( | WinMain( | ||||||
| 	HINSTANCE instance, | 	HINSTANCE instance, | ||||||
| @@ -503,130 +510,126 @@ WinMain( | |||||||
| ) | ) | ||||||
| { | { | ||||||
| 	using namespace win32; | 	using namespace win32; | ||||||
| 	// xinput_load_library_bindings(); |  | ||||||
|  |  | ||||||
| 	using JSL_DeviceHandle = int; |  | ||||||
| 	u32 jsl_num_devices = JslConnectDevices(); |  | ||||||
|  |  | ||||||
| 	JSL_DeviceHandle device_handles[4] {}; |  | ||||||
| 	u32 jsl_getconnected_found = JslGetConnectedDeviceHandles( device_handles, jsl_num_devices ); |  | ||||||
| 	{ |  | ||||||
| 		if ( jsl_getconnected_found != jsl_num_devices ) |  | ||||||
| 		{ |  | ||||||
| 			OutputDebugStringA( "Error: JSLGetConnectedDeviceHandles didn't find as many as were stated with JslConnectDevices\n"); |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		if ( jsl_num_devices > 0 ) |  | ||||||
| 		{ |  | ||||||
| 			OutputDebugStringA( "JSL Connected Devices:\n" ); |  | ||||||
| 			for ( u32 jsl_device_index = 0; jsl_device_index < jsl_num_devices; ++ jsl_device_index ) |  | ||||||
| 			{ |  | ||||||
| 				JslSetLightColour( device_handles[ jsl_device_index ], (255 << 8) ); |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	// MessageBox( 0, L"First message!", L"Handmade Hero", MB_Ok_Btn | MB_Icon_Information ); | 	// MessageBox( 0, L"First message!", L"Handmade Hero", MB_Ok_Btn | MB_Icon_Information ); | ||||||
|  |  | ||||||
| 	WNDCLASSW | 	WNDCLASSW window_class {}; | ||||||
| 	window_class {}; | 	HWND window_handle = nullptr; | ||||||
| 	window_class.style = CS_Horizontal_Redraw | CS_Vertical_Redraw; | 	MSG window_msg_info; | ||||||
| 	window_class.lpfnWndProc = main_window_callback; |  | ||||||
| 	// window_class.cbClsExtra  = ; |  | ||||||
| 	// window_class.cbWndExtra  = ; |  | ||||||
| 	window_class.hInstance   = instance; |  | ||||||
| 	// window_class.hIcon = ; |  | ||||||
| 	// window_class.hCursor = ; |  | ||||||
| 	// window_class.hbrBackground = ; |  | ||||||
| 	window_class.lpszMenuName  = L"Handmade Hero!"; |  | ||||||
| 	window_class.lpszClassName = L"HandmadeHeroWindowClass"; |  | ||||||
|  |  | ||||||
| 	if ( ! RegisterClassW( & window_class ) ) |  | ||||||
| 	{ | 	{ | ||||||
| 		// TODO : Diagnostic Logging | 		window_class.style = CS_Horizontal_Redraw | CS_Vertical_Redraw; | ||||||
| 		return 0; | 		window_class.lpfnWndProc = main_window_callback; | ||||||
|  | 		// window_class.cbClsExtra  = ; | ||||||
|  | 		// window_class.cbWndExtra  = ; | ||||||
|  | 		window_class.hInstance   = instance; | ||||||
|  | 		// window_class.hIcon = ; | ||||||
|  | 		// window_class.hCursor = ; | ||||||
|  | 		// window_class.hbrBackground = ; | ||||||
|  | 		window_class.lpszMenuName  = L"Handmade Hero!"; | ||||||
|  | 		window_class.lpszClassName = L"HandmadeHeroWindowClass"; | ||||||
|  |  | ||||||
|  | 		if ( ! RegisterClassW( & window_class ) ) | ||||||
|  | 		{ | ||||||
|  | 			// TODO : Diagnostic Logging | ||||||
|  | 			return 0; | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		window_handle = CreateWindowExW( | ||||||
|  | 			0, | ||||||
|  | 			window_class.lpszClassName, | ||||||
|  | 			L"Handmade Hero", | ||||||
|  | 			WS_Overlapped_Window | WS_Initially_Visible, | ||||||
|  | 			CW_Use_Default, CW_Use_Default, // x, y | ||||||
|  | 			CW_Use_Default, CW_Use_Default, // width, height | ||||||
|  | 			0, 0,                         // parent, menu | ||||||
|  | 			instance, 0                   // instance, param | ||||||
|  | 		); | ||||||
|  |  | ||||||
|  | 		if ( ! window_handle ) | ||||||
|  | 		{ | ||||||
|  | 			// TODO : Diagnostic Logging | ||||||
|  | 			return 0; | ||||||
|  | 		} | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	HWND window_handle = CreateWindowExW( |  | ||||||
| 		0, |  | ||||||
| 		window_class.lpszClassName, |  | ||||||
| 		L"Handmade Hero", |  | ||||||
| 		WS_Overlapped_Window | WS_Initially_Visible, |  | ||||||
| 		CW_Use_Default, CW_Use_Default, // x, y |  | ||||||
| 		CW_Use_Default, CW_Use_Default, // width, height |  | ||||||
| 		0, 0,                         // parent, menu |  | ||||||
| 		instance, 0                   // instance, param |  | ||||||
| 	); |  | ||||||
|  |  | ||||||
| 	if ( ! window_handle ) |  | ||||||
| 	{ |  | ||||||
| 		// TODO : Diagnostic Logging |  | ||||||
| 		return 0; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	Running = true; |  | ||||||
|  |  | ||||||
| 	WinDimensions dimensions = get_window_dimensions( window_handle ); | 	WinDimensions dimensions = get_window_dimensions( window_handle ); | ||||||
| 	resize_dib_section( &BackBuffer, 1280, 720 ); | 	resize_dib_section( &BackBuffer, 1280, 720 ); | ||||||
|  |  | ||||||
| 	SoundOutput sound_output; | 	SoundOutput sound_output; | ||||||
| 	sound_output.IsPlaying              = 0; | 	{ | ||||||
| 	DS_SecondaryBuffer_SamplesPerSecond = 48000; | 		sound_output.IsPlaying              = 0; | ||||||
| 	DS_SecondaryBuffer_BytesPerSample   = sizeof(s16) * 2; | 		DS_SecondaryBuffer_SamplesPerSecond = 48000; | ||||||
|  | 		DS_SecondaryBuffer_BytesPerSample   = sizeof(s16) * 2; | ||||||
|  |  | ||||||
| 	DS_SecondaryBuffer_Size = DS_SecondaryBuffer_SamplesPerSecond * DS_SecondaryBuffer_BytesPerSample; | 		DS_SecondaryBuffer_Size = DS_SecondaryBuffer_SamplesPerSecond * DS_SecondaryBuffer_BytesPerSample; | ||||||
| 	init_sound( window_handle, DS_SecondaryBuffer_SamplesPerSecond, DS_SecondaryBuffer_Size ); | 		init_sound( window_handle, DS_SecondaryBuffer_SamplesPerSecond, DS_SecondaryBuffer_Size ); | ||||||
|  |  | ||||||
| 	SoundBufferSamples = rcast( s16*, VirtualAlloc( 0, 48000 * 2 * sizeof(s16) | 		SoundBufferSamples = rcast( s16*, VirtualAlloc( 0, 48000 * 2 * sizeof(s16) | ||||||
| 		, MEM_Commit_Zeroed | MEM_Reserve, Page_Read_Write )); | 			, MEM_Commit_Zeroed | MEM_Reserve, Page_Read_Write )); | ||||||
|  |  | ||||||
| 	// Wave Sound Test | 		sound_output.RunningSampleIndex = 0; | ||||||
| 	bool wave_switch = false; | 		sound_output.LatencySampleCount = DS_SecondaryBuffer_SamplesPerSecond / 15; | ||||||
| 	sound_output.RunningSampleIndex = 0; | 		// ds_clear_sound_buffer( & sound_output ); | ||||||
| 	sound_output.WaveToneHz 	    = 262; | 		DS_SecondaryBuffer->Play( 0, 0, DSBPLAY_LOOPING ); | ||||||
| 	sound_output.WavePeriod         = DS_SecondaryBuffer_SamplesPerSecond / sound_output.WaveToneHz; | 	} | ||||||
| 	sound_output.ToneVolume         = 3000; |  | ||||||
| 	sound_output.LatencySampleCount = DS_SecondaryBuffer_SamplesPerSecond / 15; |  | ||||||
| 	ds_clear_sound_buffer( & sound_output ); |  | ||||||
| 	DS_SecondaryBuffer->Play( 0, 0, DSBPLAY_LOOPING ); |  | ||||||
|  |  | ||||||
| 	// Graphics & Input Test |  | ||||||
| 	u32 x_offset = 0; |  | ||||||
| 	u32 y_offset = 0; |  | ||||||
|  |  | ||||||
| 	// Controller State |  | ||||||
| 	bool xinput_detected = false; |  | ||||||
|  |  | ||||||
| 	b32 dpad_up        = false; |  | ||||||
| 	b32 dpad_down      = false; |  | ||||||
| 	b32 dpad_left      = false; |  | ||||||
| 	b32 dpad_right     = false; |  | ||||||
| 	b32 start          = false; |  | ||||||
| 	b32 back           = false; |  | ||||||
| 	b32 left_shoulder  = false; |  | ||||||
| 	b32 right_shoulder = false; |  | ||||||
| 	b32 btn_a          = false; |  | ||||||
| 	b32 btn_b          = false; |  | ||||||
| 	b32 btn_x          = false; |  | ||||||
| 	b32 btn_y          = false; |  | ||||||
| 	u16 stick_left_x  = 0; |  | ||||||
| 	u16 stick_left_y  = 0; |  | ||||||
| 	u16 stick_right_x = 0; |  | ||||||
| 	u16 stick_right_y = 0; |  | ||||||
|  |  | ||||||
| 	// TODO : Add sine wave test |  | ||||||
|  |  | ||||||
| 	// Windows |  | ||||||
| 	MSG window_msg_info; |  | ||||||
|  |  | ||||||
|  | 	// Timing | ||||||
| 	u64 perf_counter_frequency; | 	u64 perf_counter_frequency; | ||||||
| 	QueryPerformanceFrequency( rcast(LARGE_INTEGER*, & perf_counter_frequency) ); |  | ||||||
|  |  | ||||||
| 	u64 last_frame_time; | 	u64 last_frame_time; | ||||||
|  | 	QueryPerformanceFrequency( rcast(LARGE_INTEGER*, & perf_counter_frequency) ); | ||||||
| 	QueryPerformanceCounter( rcast(LARGE_INTEGER*, & last_frame_time) ); | 	QueryPerformanceCounter( rcast(LARGE_INTEGER*, & last_frame_time) ); | ||||||
|  |  | ||||||
| 	u64 last_cycle_time = __rdtsc(); | 	u64 last_cycle_time = __rdtsc(); | ||||||
|  |  | ||||||
|  | 	// Input shitshow | ||||||
|  | 	constexpr u32 Max_Controllers = 4; | ||||||
|  | 	// Max controllers for the platform layer and thus for all other layers is 4. (Sanity and xinput limit) | ||||||
|  |  | ||||||
|  | 	engine::InputState input {}; | ||||||
|  |  | ||||||
|  | 	using EngineXInputPadStates = engine::XInputPadState[ Max_Controllers ]; | ||||||
|  | 	EngineXInputPadStates xpad_states[2]; | ||||||
|  | 	EngineXInputPadStates* old_xpads = & xpad_states[0]; | ||||||
|  | 	EngineXInputPadStates* new_xpads = & xpad_states[1]; | ||||||
|  |  | ||||||
|  | 	using EngineDSPadStates = engine::DualsensePadState[Max_Controllers]; | ||||||
|  | 	EngineDSPadStates ds_pad_states[2]; | ||||||
|  | 	EngineDSPadStates* old_ds_pads = & ds_pad_states[0]; | ||||||
|  | 	EngineDSPadStates* new_ds_pads = & ds_pad_states[1]; | ||||||
|  |  | ||||||
|  | 	using JSL_DeviceHandle = int; | ||||||
|  | 	u32 jsl_num_devices | ||||||
|  | 			// = JslConnectDevices(); | ||||||
|  | 			= 0; | ||||||
|  | 	JSL_DeviceHandle jsl_device_handles[4] {}; | ||||||
|  | 	{ | ||||||
|  | 		xinput_load_library_bindings(); | ||||||
|  |  | ||||||
|  | 		u32 jsl_getconnected_found = JslGetConnectedDeviceHandles( jsl_device_handles, jsl_num_devices ); | ||||||
|  | 		{ | ||||||
|  | 			if ( jsl_getconnected_found != jsl_num_devices ) | ||||||
|  | 			{ | ||||||
|  | 				OutputDebugStringA( "Error: JSLGetConnectedDeviceHandles didn't find as many as were stated with JslConnectDevices\n"); | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			if ( jsl_num_devices > 0 ) | ||||||
|  | 			{ | ||||||
|  | 				OutputDebugStringA( "JSL Connected Devices:\n" ); | ||||||
|  | 				for ( u32 jsl_device_index = 0; jsl_device_index < jsl_num_devices; ++ jsl_device_index ) | ||||||
|  | 				{ | ||||||
|  | 					JslSetLightColour( jsl_device_handles[ jsl_device_index ], (255 << 8) ); | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		if ( jsl_num_devices > 4 ) | ||||||
|  | 		{ | ||||||
|  | 			jsl_num_devices = 4; | ||||||
|  | 			MessageBoxA( window_handle, "More than 4 JSL devices found, this engine will only support the first four found.", "Warning", MB_ICONEXCLAMATION ); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	Running = true; | ||||||
| 	while( Running ) | 	while( Running ) | ||||||
| 	{ | 	{ | ||||||
| 		// Window Management | 		// Window Management | ||||||
| @@ -646,103 +649,132 @@ WinMain( | |||||||
|  |  | ||||||
| 		// Input | 		// Input | ||||||
| 		{ | 		{ | ||||||
|  | 			// Swapping at the beginning of the input frame instead of the end. | ||||||
|  | 			swap( old_xpads,   new_xpads ); | ||||||
|  | 			swap( old_ds_pads, new_ds_pads ); | ||||||
|  |  | ||||||
| 			// XInput Polling | 			// XInput Polling | ||||||
| 			// TODO(Ed) : Should we poll this more frequently? | 			// TODO(Ed) : Should we poll this more frequently? | ||||||
| 			for ( DWORD controller_index = 0; controller_index < XUSER_MAX_COUNT; ++ controller_index ) | 			for ( DWORD controller_index = 0; controller_index < Max_Controllers; ++ controller_index ) | ||||||
| 			{ | 			{ | ||||||
| 				XINPUT_STATE controller_state; | 				XINPUT_STATE controller_state; | ||||||
| 				xinput_detected = xinput_get_state( controller_index, & controller_state ) == XI_PluggedIn; | 				b32 xinput_detected = xinput_get_state( controller_index, & controller_state ) == XI_PluggedIn; | ||||||
| 				if ( xinput_detected ) | 				if ( xinput_detected ) | ||||||
| 				{ | 				{ | ||||||
| 					XINPUT_GAMEPAD* pad = & controller_state.Gamepad; | 					XINPUT_GAMEPAD*       xpad = & controller_state.Gamepad; | ||||||
|  | 					engine::XInputPadState* old_xpad = old_xpads[ controller_index ]; | ||||||
|  | 					engine::XInputPadState* new_xpad = new_xpads[ controller_index ]; | ||||||
|  |  | ||||||
| 					dpad_up        = pad->wButtons & XINPUT_GAMEPAD_DPAD_UP; | 					input_process_digital_btn( & old_xpad->DPad.Up,    & new_xpad->DPad.Up, xpad->wButtons, XINPUT_GAMEPAD_DPAD_UP ); | ||||||
| 					dpad_down      = pad->wButtons & XINPUT_GAMEPAD_DPAD_DOWN; | 					input_process_digital_btn( & old_xpad->DPad.Down,  & new_xpad->DPad.Down, xpad->wButtons, XINPUT_GAMEPAD_DPAD_DOWN ); | ||||||
| 					dpad_left      = pad->wButtons & XINPUT_GAMEPAD_DPAD_LEFT; | 					input_process_digital_btn( & old_xpad->DPad.Left,  & new_xpad->DPad.Left, xpad->wButtons, XINPUT_GAMEPAD_DPAD_LEFT ); | ||||||
| 					dpad_right     = pad->wButtons & XINPUT_GAMEPAD_DPAD_RIGHT; | 					input_process_digital_btn( & old_xpad->DPad.Right, & new_xpad->DPad.Right, xpad->wButtons, XINPUT_GAMEPAD_DPAD_RIGHT ); | ||||||
| 					start          = pad->wButtons & XINPUT_GAMEPAD_START; |  | ||||||
| 					back           = pad->wButtons & XINPUT_GAMEPAD_BACK; |  | ||||||
| 					left_shoulder  = pad->wButtons & XINPUT_GAMEPAD_LEFT_SHOULDER; |  | ||||||
| 					right_shoulder = pad->wButtons & XINPUT_GAMEPAD_RIGHT_SHOULDER; |  | ||||||
| 					btn_a          = pad->wButtons & XINPUT_GAMEPAD_A; |  | ||||||
| 					btn_b          = pad->wButtons & XINPUT_GAMEPAD_B; |  | ||||||
| 					btn_x          = pad->wButtons & XINPUT_GAMEPAD_X; |  | ||||||
| 					btn_y          = pad->wButtons & XINPUT_GAMEPAD_Y; |  | ||||||
|  |  | ||||||
| 					stick_left_x  = pad->sThumbLX; | 					input_process_digital_btn( & old_xpad->Y, & new_xpad->Y, xpad->wButtons, XINPUT_GAMEPAD_Y ); | ||||||
| 					stick_left_y  = pad->sThumbLY; | 					input_process_digital_btn( & old_xpad->A, & new_xpad->A, xpad->wButtons, XINPUT_GAMEPAD_A ); | ||||||
| 					stick_right_x = pad->sThumbRX; | 					input_process_digital_btn( & old_xpad->B, & new_xpad->B, xpad->wButtons, XINPUT_GAMEPAD_B ); | ||||||
| 					stick_right_y = pad->sThumbRY; | 					input_process_digital_btn( & old_xpad->X, & new_xpad->X, xpad->wButtons, XINPUT_GAMEPAD_X ); | ||||||
|  |  | ||||||
|  | 					input_process_digital_btn( & old_xpad->Back,  & new_xpad->Back,  xpad->wButtons, XINPUT_GAMEPAD_BACK ); | ||||||
|  | 					input_process_digital_btn( & old_xpad->Start, & new_xpad->Start, xpad->wButtons, XINPUT_GAMEPAD_START ); | ||||||
|  |  | ||||||
|  | 					input_process_digital_btn( & old_xpad->LeftShoulder,  & new_xpad->LeftShoulder,  xpad->wButtons, XINPUT_GAMEPAD_LEFT_SHOULDER ); | ||||||
|  | 					input_process_digital_btn( & old_xpad->RightShoulder, & new_xpad->RightShoulder, xpad->wButtons, XINPUT_GAMEPAD_RIGHT_SHOULDER ); | ||||||
|  |  | ||||||
|  | 					new_xpad->Stick.Left.X.Start = old_xpad->Stick.Left.X.End; | ||||||
|  | 					new_xpad->Stick.Left.Y.Start = old_xpad->Stick.Left.Y.End; | ||||||
|  |  | ||||||
|  | 					// TODO(Ed) : Compress this into a proc | ||||||
|  | 					f32 X; | ||||||
|  | 					if ( xpad->sThumbLX < 0 ) | ||||||
|  | 					{ | ||||||
|  | 						X = scast(f32, xpad->sThumbLX) / scast(f32, -S16_MIN); | ||||||
|  | 					} | ||||||
|  | 					else | ||||||
|  | 					{ | ||||||
|  | 						X = scast(f32, xpad->sThumbLX) / scast(f32, S16_MAX); | ||||||
|  | 					} | ||||||
|  |  | ||||||
|  | 					// TODO(Ed) : Min/Max macros!!! | ||||||
|  | 					new_xpad->Stick.Left.X.Min = new_xpad->Stick.Left.X.Max = new_xpad->Stick.Left.X.End = X; | ||||||
|  |  | ||||||
|  | 					f32 Y; | ||||||
|  | 					if ( xpad->sThumbLY < 0 ) | ||||||
|  | 					{ | ||||||
|  | 						Y = scast(f32, xpad->sThumbLY) / scast(f32, -S16_MIN); | ||||||
|  | 					} | ||||||
|  | 					else | ||||||
|  | 					{ | ||||||
|  | 						Y = scast(f32, xpad->sThumbLY) / scast(f32, S16_MAX); | ||||||
|  | 					} | ||||||
|  |  | ||||||
|  | 					// TODO(Ed) : Min/Max macros!!! | ||||||
|  | 					new_xpad->Stick.Left.Y.Min = new_xpad->Stick.Left.Y.Max = new_xpad->Stick.Left.Y.End = Y; | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  | 					// epad->Stick.Left.X.End  = xpad->sThumbLX; | ||||||
|  | 					// epad->Stick.Left.Y.End  = xpad->sThumbLY; | ||||||
|  | 					// epad->Stick.Right.X.End = xpad->sThumbRX; | ||||||
|  | 					// epad->Stick.Right.X.End = xpad->sThumbRY; | ||||||
|  |  | ||||||
|  | 					// TODO(Ed) : Dead zone processing!!!!!!!!!!!!!!! | ||||||
|  | 					// XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE | ||||||
|  | 					// XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE | ||||||
|  |  | ||||||
|  | 					// S16_MAX | ||||||
|  | 					// S16_MIN | ||||||
|  |  | ||||||
|  | 					input.Controllers[ controller_index ].XPad = new_xpad; | ||||||
| 				} | 				} | ||||||
| 				else | 				else | ||||||
| 				{ | 				{ | ||||||
| 					// NOTE: Controller is not available | 					input.Controllers[ controller_index ].XPad = nullptr; | ||||||
| 				} | 				} | ||||||
| 			} | 			} | ||||||
|  |  | ||||||
| 			// JSL Input Polling | 			// JSL Input Polling | ||||||
| 			for ( u32 jsl_device_index = 0; jsl_device_index < jsl_num_devices; ++ jsl_device_index ) | 			for ( u32 jsl_device_index = 0; jsl_device_index < jsl_num_devices; ++ jsl_device_index ) | ||||||
| 			{ | 			{ | ||||||
| 				if ( ! JslStillConnected( device_handles[ jsl_device_index ] ) ) | 				if ( ! JslStillConnected( jsl_device_handles[ jsl_device_index ] ) ) | ||||||
| 				{ | 				{ | ||||||
| 					OutputDebugStringA( "Error: JSLStillConnected returned false\n" ); | 					OutputDebugStringA( "Error: JSLStillConnected returned false\n" ); | ||||||
| 					continue; | 					continue; | ||||||
| 				} | 				} | ||||||
|  |  | ||||||
| 				JOY_SHOCK_STATE state = JslGetSimpleState( device_handles[ jsl_device_index ] ); | 				// TODO : Won't support more than 4 for now... (or prob ever) | ||||||
| 				dpad_up        = state.buttons & JSMASK_UP; | 				if ( jsl_device_index > 4 ) | ||||||
| 				dpad_down      = state.buttons & JSMASK_DOWN; | 					break; | ||||||
| 				dpad_left      = state.buttons & JSMASK_LEFT; |  | ||||||
| 				dpad_right     = state.buttons & JSMASK_RIGHT; |  | ||||||
| 				start          = state.buttons & JSMASK_PLUS; |  | ||||||
| 				back           = state.buttons & JSMASK_MINUS; |  | ||||||
| 				left_shoulder  = state.buttons & JSMASK_L; |  | ||||||
| 				right_shoulder = state.buttons & JSMASK_R; |  | ||||||
| 				btn_a          = state.buttons & JSMASK_S; |  | ||||||
| 				btn_b          = state.buttons & JSMASK_E; |  | ||||||
| 				btn_x          = state.buttons & JSMASK_W; |  | ||||||
| 				btn_y          = state.buttons & JSMASK_N; |  | ||||||
|  |  | ||||||
| 				stick_left_x  = state.stickLX; | 				JOY_SHOCK_STATE       state = JslGetSimpleState( jsl_device_handles[ jsl_device_index ] ); | ||||||
| 				stick_left_y  = state.stickLY; | 				engine::DualsensePadState* old_ds_pad  = old_ds_pads[ jsl_device_index ]; | ||||||
| 				stick_right_x = state.stickRX; | 				engine::DualsensePadState* new_ds_pad  = new_ds_pads[ jsl_device_index ]; | ||||||
| 				stick_right_y = state.stickRY; |  | ||||||
| 			} |  | ||||||
|  |  | ||||||
| 			x_offset += dpad_right; | 				input_process_digital_btn( & old_ds_pad->DPad.Up,    & new_ds_pad->DPad.Up,    state.buttons, JSMASK_UP ); | ||||||
| 			x_offset -= dpad_left; | 				input_process_digital_btn( & old_ds_pad->DPad.Down,  & new_ds_pad->DPad.Down,  state.buttons, JSMASK_DOWN ); | ||||||
| 			y_offset += dpad_up; | 				input_process_digital_btn( & old_ds_pad->DPad.Left,  & new_ds_pad->DPad.Left,  state.buttons, JSMASK_LEFT ); | ||||||
| 			y_offset -= dpad_down; | 				input_process_digital_btn( & old_ds_pad->DPad.Right, & new_ds_pad->DPad.Right, state.buttons, JSMASK_RIGHT ); | ||||||
|  |  | ||||||
| 			if ( start ) | 				input_process_digital_btn( & old_ds_pad->Triangle, & new_ds_pad->Triangle, state.buttons, JSMASK_N ); | ||||||
| 			{ | 				input_process_digital_btn( & old_ds_pad->X,        & new_ds_pad->X,        state.buttons, JSMASK_S ); | ||||||
| 				if ( xinput_detected ) | 				input_process_digital_btn( & old_ds_pad->Square,   & new_ds_pad->Square,   state.buttons, JSMASK_W ); | ||||||
| 				{ | 				input_process_digital_btn( & old_ds_pad->Circle,   & new_ds_pad->Circle,   state.buttons, JSMASK_E ); | ||||||
| 					XINPUT_VIBRATION vibration; |  | ||||||
| 					vibration.wLeftMotorSpeed  = 30000; | 				input_process_digital_btn( & old_ds_pad->Share,   & new_ds_pad->Share,   state.buttons, JSMASK_SHARE ); | ||||||
| 					xinput_set_state( 0, & vibration ); | 				input_process_digital_btn( & old_ds_pad->Options, & new_ds_pad->Options, state.buttons, JSMASK_OPTIONS ); | ||||||
| 				} |  | ||||||
| 				else | 				input_process_digital_btn( & old_ds_pad->L1, & new_ds_pad->L1, state.buttons, JSMASK_L ); | ||||||
| 				{ | 				input_process_digital_btn( & old_ds_pad->R1, & new_ds_pad->R1, state.buttons, JSMASK_R ); | ||||||
| 					JslSetRumble( 0, 1, 0 ); |  | ||||||
| 				} | 				// epad->Stick.Left.X.End  = state.stickLX; | ||||||
| 			} | 				// epad->Stick.Left.Y.End  = state.stickLY; | ||||||
| 			else | 				// epad->Stick.Right.X.End = state.stickRX; | ||||||
| 			{ | 				// epad->Stick.Right.X.End = state.stickRY; | ||||||
| 				if ( xinput_detected ) |  | ||||||
| 				{ | 				input.Controllers[ jsl_device_index ].DSPad = new_ds_pad; | ||||||
| 					XINPUT_VIBRATION vibration; |  | ||||||
| 					vibration.wLeftMotorSpeed  = 0; |  | ||||||
| 					xinput_set_state( 0, & vibration ); |  | ||||||
| 				} |  | ||||||
| 				else |  | ||||||
| 				{ |  | ||||||
| 					JslSetRumble( 0, 0, 0 ); |  | ||||||
| 				} |  | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
|  |  | ||||||
| 		// Pain... | 		// Pain... | ||||||
| 		b32 sound_is_valid = false; | 		b32 sound_is_valid = false; | ||||||
| 		DWORD ds_play_cursor; | 		DWORD ds_play_cursor; | ||||||
| @@ -751,17 +783,11 @@ WinMain( | |||||||
| 		DWORD bytes_to_write; | 		DWORD bytes_to_write; | ||||||
| 		if ( SUCCEEDED( DS_SecondaryBuffer->GetCurrentPosition( & ds_play_cursor, & ds_write_cursor ) )) | 		if ( SUCCEEDED( DS_SecondaryBuffer->GetCurrentPosition( & ds_play_cursor, & ds_write_cursor ) )) | ||||||
| 		{ | 		{ | ||||||
| 			DWORD target_cursor = (ds_play_cursor + sound_output.LatencySampleCount * DS_SecondaryBuffer_BytesPerSample) % DS_SecondaryBuffer_Size; |  | ||||||
|  |  | ||||||
| 			byte_to_lock = (sound_output.RunningSampleIndex * DS_SecondaryBuffer_BytesPerSample) % DS_SecondaryBuffer_Size; | 			byte_to_lock        = (sound_output.RunningSampleIndex * DS_SecondaryBuffer_BytesPerSample) % DS_SecondaryBuffer_Size; | ||||||
| 			bytes_to_write; | 			DWORD target_cursor = (ds_play_cursor + (sound_output.LatencySampleCount * DS_SecondaryBuffer_BytesPerSample)) % DS_SecondaryBuffer_Size; | ||||||
|  |  | ||||||
| 			if ( byte_to_lock == target_cursor ) | 			if ( byte_to_lock > target_cursor) | ||||||
| 			{ |  | ||||||
| 				// We are in the middle of playing. Wait for the write cursor to catch up. |  | ||||||
| 				bytes_to_write = 0; |  | ||||||
| 			} |  | ||||||
| 			else if ( byte_to_lock > target_cursor) |  | ||||||
| 			{ | 			{ | ||||||
| 				// Infront of play cursor |--play--byte_to_write-->--| | 				// Infront of play cursor |--play--byte_to_write-->--| | ||||||
| 				bytes_to_write =  DS_SecondaryBuffer_Size - byte_to_lock; | 				bytes_to_write =  DS_SecondaryBuffer_Size - byte_to_lock; | ||||||
| @@ -778,15 +804,12 @@ WinMain( | |||||||
|  |  | ||||||
| 		// s16 samples[ 48000 * 2 ]; | 		// s16 samples[ 48000 * 2 ]; | ||||||
| 		engine::SoundBuffer sound_buffer {}; | 		engine::SoundBuffer sound_buffer {}; | ||||||
| 		sound_buffer.NumSamples         = DS_SecondaryBuffer_SamplesPerSecond / 30; | 		sound_buffer.NumSamples         = bytes_to_write / DS_SecondaryBuffer_BytesPerSample; | ||||||
| 		sound_buffer.RunningSampleIndex = sound_output.RunningSampleIndex; | 		sound_buffer.RunningSampleIndex = sound_output.RunningSampleIndex; | ||||||
| 		sound_buffer.SamplesPerSecond   = DS_SecondaryBuffer_SamplesPerSecond; | 		sound_buffer.SamplesPerSecond   = DS_SecondaryBuffer_SamplesPerSecond; | ||||||
| 		sound_buffer.ToneVolume         = sound_output.ToneVolume; |  | ||||||
| 		sound_buffer.Samples            = SoundBufferSamples; | 		sound_buffer.Samples            = SoundBufferSamples; | ||||||
| 		sound_buffer.WavePeriod = sound_output.WavePeriod; |  | ||||||
| 		sound_buffer.WaveToneHz = sound_output.WaveToneHz; |  | ||||||
|  |  | ||||||
| 		engine::update_and_render( rcast(engine::OffscreenBuffer*, & BackBuffer.Memory), & sound_buffer, x_offset, y_offset ); | 		engine::update_and_render( & input, rcast(engine::OffscreenBuffer*, & BackBuffer.Memory), & sound_buffer ); | ||||||
|  |  | ||||||
| 		// Rendering | 		// Rendering | ||||||
| 		{ | 		{ | ||||||
| @@ -799,29 +822,6 @@ WinMain( | |||||||
|  |  | ||||||
| 		// Audio | 		// Audio | ||||||
| 		do { | 		do { | ||||||
| 			if ( btn_y ) |  | ||||||
| 			{ |  | ||||||
| 				sound_output.ToneVolume += 10; |  | ||||||
| 			} |  | ||||||
| 			if ( btn_b ) |  | ||||||
| 			{ |  | ||||||
| 				sound_output.ToneVolume -= 10; |  | ||||||
| 			} |  | ||||||
| 			if ( btn_x ) |  | ||||||
| 			{ |  | ||||||
| 				sound_output.WaveToneHz += 1; |  | ||||||
| 				sound_output.WavePeriod  = DS_SecondaryBuffer_SamplesPerSecond / sound_output.WaveToneHz; |  | ||||||
| 			} |  | ||||||
| 			if ( btn_a ) |  | ||||||
| 			{ |  | ||||||
| 				sound_output.WaveToneHz -= 1; |  | ||||||
| 				sound_output.WavePeriod  = DS_SecondaryBuffer_SamplesPerSecond / sound_output.WaveToneHz; |  | ||||||
| 			} |  | ||||||
| 			if ( back ) |  | ||||||
| 			{ |  | ||||||
| 				wave_switch ^= true; |  | ||||||
| 			} |  | ||||||
|  |  | ||||||
| 			DWORD ds_status = 0; | 			DWORD ds_status = 0; | ||||||
| 			if ( SUCCEEDED( DS_SecondaryBuffer->GetStatus( & ds_status ) ) ) | 			if ( SUCCEEDED( DS_SecondaryBuffer->GetStatus( & ds_status ) ) ) | ||||||
| 			{ | 			{ | ||||||
| @@ -854,9 +854,9 @@ WinMain( | |||||||
| 		u32 ms_per_frame        = MS_PER_SECOND * frame_time_elapsed / perf_counter_frequency; | 		u32 ms_per_frame        = MS_PER_SECOND * frame_time_elapsed / perf_counter_frequency; | ||||||
| 		u32 fps                 = perf_counter_frequency / frame_time_elapsed; | 		u32 fps                 = perf_counter_frequency / frame_time_elapsed; | ||||||
|  |  | ||||||
| 		char ms_timing_debug[256] {}; | 		// char ms_timing_debug[256] {}; | ||||||
| 		wsprintfA( ms_timing_debug, "%d ms\n" "FPS: %d\n" "mega cycles: %d\n", ms_per_frame, fps, mega_cycles_elapsed ); | 		// wsprintfA( ms_timing_debug, "%d ms\n" "FPS: %d\n" "mega cycles: %d\n", ms_per_frame, fps, mega_cycles_elapsed ); | ||||||
| 		OutputDebugStringA( ms_timing_debug ); | 		// OutputDebugStringA( ms_timing_debug ); | ||||||
|  |  | ||||||
| 		last_cycle_time = end_cycle_count; | 		last_cycle_time = end_cycle_count; | ||||||
| 		last_frame_time = frame_cycle_time_end; | 		last_frame_time = frame_cycle_time_end; | ||||||
| @@ -864,15 +864,11 @@ WinMain( | |||||||
|  |  | ||||||
| 	if ( jsl_num_devices > 0 ) | 	if ( jsl_num_devices > 0 ) | ||||||
| 	{ | 	{ | ||||||
| 		OutputDebugStringA( "JSL Connected Devices:\n" ); |  | ||||||
| 		for ( u32 jsl_device_index = 0; jsl_device_index < jsl_num_devices; ++ jsl_device_index ) | 		for ( u32 jsl_device_index = 0; jsl_device_index < jsl_num_devices; ++ jsl_device_index ) | ||||||
| 		{ | 		{ | ||||||
| 			JslSetLightColour( device_handles[ jsl_device_index ], 0 ); | 			JslSetLightColour( jsl_device_handles[ jsl_device_index ], 0 ); | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	return 0; | 	return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| // Engine layer translation unit. |  | ||||||
| // #include "engine.cpp" |  | ||||||
|   | |||||||
| @@ -37,4 +37,7 @@ | |||||||
|  |  | ||||||
| #define do_once_end        \ | #define do_once_end        \ | ||||||
| 	}                      \ | 	}                      \ | ||||||
| 	while(0); | 	while(0); | ||||||
|  |  | ||||||
|  |  | ||||||
|  | #define array_count( array ) ( sizeof( array ) / sizeof( ( array )[0] ) ) | ||||||
|   | |||||||
| @@ -6,6 +6,7 @@ | |||||||
| #pragma once | #pragma once | ||||||
| #include "grime.h" | #include "grime.h" | ||||||
| #include "macros.h" | #include "macros.h" | ||||||
|  | #include "generics.h" | ||||||
| #include "math_constants.h" | #include "math_constants.h" | ||||||
| #include "types.h" | #include "types.h" | ||||||
|  |  | ||||||
|   | |||||||
| @@ -4,29 +4,31 @@ | |||||||
|  |  | ||||||
| #define U8_MIN 0u | #define U8_MIN 0u | ||||||
| #define U8_MAX 0xffu | #define U8_MAX 0xffu | ||||||
| #define I8_MIN ( -0x7f - 1 ) | #define S8_MIN ( -0x7f - 1 ) | ||||||
| #define I8_MAX 0x7f | #define S8_MAX 0x7f | ||||||
|  |  | ||||||
| #define U16_MIN 0u | #define U16_MIN 0u | ||||||
| #define U16_MAX 0xffffu | #define U16_MAX 0xffffu | ||||||
| #define I16_MIN ( -0x7fff - 1 ) | #define S16_MIN ( -0x7fff - 1 ) | ||||||
| #define I16_MAX 0x7fff | #define S16_MAX 0x7fff | ||||||
|  |  | ||||||
| #define U32_MIN 0u | #define U32_MIN 0u | ||||||
| #define U32_MAX 0xffffffffu | #define U32_MAX 0xffffffffu | ||||||
| #define I32_MIN ( -0x7fffffff - 1 ) | #define S32_MIN ( -0x7fffffff - 1 ) | ||||||
| #define I32_MAX 0x7fffffff | #define S32_MAX 0x7fffffff | ||||||
|  |  | ||||||
| #define U64_MIN 0ull | #define U64_MIN 0ull | ||||||
| #define U64_MAX 0xffffffffffffffffull | #define U64_MAX 0xffffffffffffffffull | ||||||
| #define I64_MIN ( -0x7fffffffffffffffll - 1 ) | #define S64_MIN ( -0x7fffffffffffffffll - 1 ) | ||||||
| #define I64_MAX 0x7fffffffffffffffll | #define S64_MAX 0x7fffffffffffffffll | ||||||
|  |  | ||||||
|  | // Word size is the same as uw or size_t. This engine will not run on some weird compiler that doesn't | ||||||
|  | // Match the largest object to the word size of the architecture. | ||||||
| #if defined( ARCH_64_BIT ) | #if defined( ARCH_64_BIT ) | ||||||
| #	define USIZE_MIN GEN_U64_MIN | #	define UWORD_MIN U64_MIN | ||||||
| #	define USIZE_MAX GEN_U64_MAX | #	define UWORD_MAX U64_MAX | ||||||
| #	define ISIZE_MIN GEN_I64_MIN | #	define SWORD_MIN S64_MIN | ||||||
| #	define ISIZE_MAX GEN_I64_MAX | #	define SWORD_MAX S64_MAX | ||||||
| #else | #else | ||||||
| #	error Unknown architecture size. This library only supports 64 bit architectures. | #	error Unknown architecture size. This library only supports 64 bit architectures. | ||||||
| #endif | #endif | ||||||
|   | |||||||
										
											Binary file not shown.
										
									
								
							
		Reference in New Issue
	
	Block a user