diff --git a/docs/Day 013.md b/docs/Day 013.md new file mode 100644 index 0000000..3696862 --- /dev/null +++ b/docs/Day 013.md @@ -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; +}; +``` diff --git a/project/engine.cpp b/project/engine.cpp index dc63912..2eebe0a 100644 --- a/project/engine.cpp +++ b/project/engine.cpp @@ -1,17 +1,21 @@ #include "engine.h" +#include "win32.h" NS_ENGINE_BEGIN 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 square_wave_sample_value( SoundBuffer* sound_buffer ) { - s16 sample_value = (sound_buffer->RunningSampleIndex / (sound_buffer->WavePeriod /2)) % 2 ? - sound_buffer->ToneVolume : - sound_buffer->ToneVolume; + s16 sample_value = (sound_buffer->RunningSampleIndex / (SoundTest_WavePeriod /2)) % 2 ? + SoundTest_ToneVolume : - SoundTest_ToneVolume; return sample_value; } @@ -21,10 +25,11 @@ sine_wave_sample_value( SoundBuffer* sound_buffer ) { local_persist f32 time = 0.f; + // time = TAU * (f32)sound_buffer->RunningSampleIndex / (f32)SoundTest_WavePeriod; 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; } @@ -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 ) { 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; @@ -92,14 +101,133 @@ render_weird_graident(OffscreenBuffer* buffer, u32 x_offset, u32 y_offset ) } } -internal -void update_and_render( OffscreenBuffer* back_buffer, SoundBuffer* sound_buffer - // Temp (for feature parity) - , u32 x_offset, u32 y_offset -) +b32 input_using_analog() { + 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 - 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 ); } diff --git a/project/engine.h b/project/engine.h index f644daa..71fde35 100644 --- a/project/engine.h +++ b/project/engine.h @@ -20,22 +20,143 @@ struct OffscreenBuffer u32 BytesPerPixel; }; +// TODO : Will be gutting this once we have other stuff lifted. struct SoundBuffer { s16* Samples; u32 RunningSampleIndex; s32 SamplesPerSecond; 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: // Timing, Input, Bitmap Buffer, Sound Buffer -void update_and_render( OffscreenBuffer* back_buffer, SoundBuffer* sound_buffer - // Temp (for feature parity) - , u32 x_offset, u32 y_offset -); +void update_and_render( InputState* input, OffscreenBuffer* back_buffer, SoundBuffer* sound_buffer ); NS_ENGINE_END diff --git a/project/platform/generics.h b/project/platform/generics.h new file mode 100644 index 0000000..fa3995a --- /dev/null +++ b/project/platform/generics.h @@ -0,0 +1,10 @@ +#pragma once + +template< class Type > +void swap( Type& a, Type& b ) +{ + Type + temp = a; + a = b; + b = temp; +} diff --git a/project/platform/handmade_win32.cpp b/project/platform/handmade_win32.cpp index 21e45e3..3ef2aa6 100644 --- a/project/platform/handmade_win32.cpp +++ b/project/platform/handmade_win32.cpp @@ -89,8 +89,16 @@ struct WinDimensions u32 Height; }; -HRESULT WINAPI -DirectSoundCreate(LPGUID lpGuid, LPDIRECTSOUND* ppDS, LPUNKNOWN pUnkOuter ); +// TODO : This will def need to be looked over. +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 ); 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 ds_clear_sound_buffer( SoundOutput* sound_output ) { @@ -494,6 +492,15 @@ main_window_callback( 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 WinMain( HINSTANCE instance, @@ -503,130 +510,126 @@ WinMain( ) { 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 ); - WNDCLASSW - window_class {}; - window_class.style = CS_Horizontal_Redraw | CS_Vertical_Redraw; - 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 ) ) + WNDCLASSW window_class {}; + HWND window_handle = nullptr; + MSG window_msg_info; { - // TODO : Diagnostic Logging - return 0; + window_class.style = CS_Horizontal_Redraw | CS_Vertical_Redraw; + 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 ); resize_dib_section( &BackBuffer, 1280, 720 ); SoundOutput sound_output; - sound_output.IsPlaying = 0; - DS_SecondaryBuffer_SamplesPerSecond = 48000; - DS_SecondaryBuffer_BytesPerSample = sizeof(s16) * 2; + { + sound_output.IsPlaying = 0; + DS_SecondaryBuffer_SamplesPerSecond = 48000; + DS_SecondaryBuffer_BytesPerSample = sizeof(s16) * 2; - DS_SecondaryBuffer_Size = DS_SecondaryBuffer_SamplesPerSecond * DS_SecondaryBuffer_BytesPerSample; - init_sound( window_handle, DS_SecondaryBuffer_SamplesPerSecond, DS_SecondaryBuffer_Size ); + DS_SecondaryBuffer_Size = DS_SecondaryBuffer_SamplesPerSecond * DS_SecondaryBuffer_BytesPerSample; + init_sound( window_handle, DS_SecondaryBuffer_SamplesPerSecond, DS_SecondaryBuffer_Size ); - SoundBufferSamples = rcast( s16*, VirtualAlloc( 0, 48000 * 2 * sizeof(s16) - , MEM_Commit_Zeroed | MEM_Reserve, Page_Read_Write )); + SoundBufferSamples = rcast( s16*, VirtualAlloc( 0, 48000 * 2 * sizeof(s16) + , MEM_Commit_Zeroed | MEM_Reserve, Page_Read_Write )); - // Wave Sound Test - bool wave_switch = false; - sound_output.RunningSampleIndex = 0; - sound_output.WaveToneHz = 262; - 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; + sound_output.RunningSampleIndex = 0; + sound_output.LatencySampleCount = DS_SecondaryBuffer_SamplesPerSecond / 15; + // ds_clear_sound_buffer( & sound_output ); + DS_SecondaryBuffer->Play( 0, 0, DSBPLAY_LOOPING ); + } + // Timing u64 perf_counter_frequency; - QueryPerformanceFrequency( rcast(LARGE_INTEGER*, & perf_counter_frequency) ); - u64 last_frame_time; + QueryPerformanceFrequency( rcast(LARGE_INTEGER*, & perf_counter_frequency) ); QueryPerformanceCounter( rcast(LARGE_INTEGER*, & last_frame_time) ); 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 ) { // Window Management @@ -646,103 +649,132 @@ WinMain( // 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 // 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_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 ) { - 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; - dpad_down = pad->wButtons & XINPUT_GAMEPAD_DPAD_DOWN; - dpad_left = pad->wButtons & XINPUT_GAMEPAD_DPAD_LEFT; - dpad_right = pad->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; + input_process_digital_btn( & old_xpad->DPad.Up, & new_xpad->DPad.Up, xpad->wButtons, XINPUT_GAMEPAD_DPAD_UP ); + input_process_digital_btn( & old_xpad->DPad.Down, & new_xpad->DPad.Down, xpad->wButtons, XINPUT_GAMEPAD_DPAD_DOWN ); + input_process_digital_btn( & old_xpad->DPad.Left, & new_xpad->DPad.Left, xpad->wButtons, XINPUT_GAMEPAD_DPAD_LEFT ); + input_process_digital_btn( & old_xpad->DPad.Right, & new_xpad->DPad.Right, xpad->wButtons, XINPUT_GAMEPAD_DPAD_RIGHT ); - stick_left_x = pad->sThumbLX; - stick_left_y = pad->sThumbLY; - stick_right_x = pad->sThumbRX; - stick_right_y = pad->sThumbRY; + input_process_digital_btn( & old_xpad->Y, & new_xpad->Y, xpad->wButtons, XINPUT_GAMEPAD_Y ); + input_process_digital_btn( & old_xpad->A, & new_xpad->A, xpad->wButtons, XINPUT_GAMEPAD_A ); + input_process_digital_btn( & old_xpad->B, & new_xpad->B, xpad->wButtons, XINPUT_GAMEPAD_B ); + 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 { - // NOTE: Controller is not available + input.Controllers[ controller_index ].XPad = nullptr; } } // JSL Input Polling 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" ); continue; } - JOY_SHOCK_STATE state = JslGetSimpleState( device_handles[ jsl_device_index ] ); - dpad_up = state.buttons & JSMASK_UP; - dpad_down = state.buttons & JSMASK_DOWN; - 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; + // TODO : Won't support more than 4 for now... (or prob ever) + if ( jsl_device_index > 4 ) + break; - stick_left_x = state.stickLX; - stick_left_y = state.stickLY; - stick_right_x = state.stickRX; - stick_right_y = state.stickRY; - } + JOY_SHOCK_STATE state = JslGetSimpleState( jsl_device_handles[ jsl_device_index ] ); + engine::DualsensePadState* old_ds_pad = old_ds_pads[ jsl_device_index ]; + engine::DualsensePadState* new_ds_pad = new_ds_pads[ jsl_device_index ]; - x_offset += dpad_right; - x_offset -= dpad_left; - y_offset += dpad_up; - y_offset -= dpad_down; + input_process_digital_btn( & old_ds_pad->DPad.Up, & new_ds_pad->DPad.Up, state.buttons, JSMASK_UP ); + input_process_digital_btn( & old_ds_pad->DPad.Down, & new_ds_pad->DPad.Down, state.buttons, JSMASK_DOWN ); + input_process_digital_btn( & old_ds_pad->DPad.Left, & new_ds_pad->DPad.Left, state.buttons, JSMASK_LEFT ); + input_process_digital_btn( & old_ds_pad->DPad.Right, & new_ds_pad->DPad.Right, state.buttons, JSMASK_RIGHT ); - if ( start ) - { - if ( xinput_detected ) - { - XINPUT_VIBRATION vibration; - vibration.wLeftMotorSpeed = 30000; - xinput_set_state( 0, & vibration ); - } - else - { - JslSetRumble( 0, 1, 0 ); - } - } - else - { - if ( xinput_detected ) - { - XINPUT_VIBRATION vibration; - vibration.wLeftMotorSpeed = 0; - xinput_set_state( 0, & vibration ); - } - else - { - JslSetRumble( 0, 0, 0 ); - } + 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 ); + 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 ); + + input_process_digital_btn( & old_ds_pad->Share, & new_ds_pad->Share, state.buttons, JSMASK_SHARE ); + input_process_digital_btn( & old_ds_pad->Options, & new_ds_pad->Options, state.buttons, JSMASK_OPTIONS ); + + 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 ); + + // epad->Stick.Left.X.End = state.stickLX; + // epad->Stick.Left.Y.End = state.stickLY; + // epad->Stick.Right.X.End = state.stickRX; + // epad->Stick.Right.X.End = state.stickRY; + + input.Controllers[ jsl_device_index ].DSPad = new_ds_pad; } } - // Pain... b32 sound_is_valid = false; DWORD ds_play_cursor; @@ -751,17 +783,11 @@ WinMain( DWORD bytes_to_write; 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; - bytes_to_write; + byte_to_lock = (sound_output.RunningSampleIndex * DS_SecondaryBuffer_BytesPerSample) % DS_SecondaryBuffer_Size; + DWORD target_cursor = (ds_play_cursor + (sound_output.LatencySampleCount * DS_SecondaryBuffer_BytesPerSample)) % DS_SecondaryBuffer_Size; - 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) + if ( byte_to_lock > target_cursor) { // Infront of play cursor |--play--byte_to_write-->--| bytes_to_write = DS_SecondaryBuffer_Size - byte_to_lock; @@ -778,15 +804,12 @@ WinMain( // s16 samples[ 48000 * 2 ]; 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.SamplesPerSecond = DS_SecondaryBuffer_SamplesPerSecond; - sound_buffer.ToneVolume = sound_output.ToneVolume; 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 { @@ -799,29 +822,6 @@ WinMain( // Audio 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; 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 fps = perf_counter_frequency / frame_time_elapsed; - 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 ); - OutputDebugStringA( ms_timing_debug ); + // 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 ); + // OutputDebugStringA( ms_timing_debug ); last_cycle_time = end_cycle_count; last_frame_time = frame_cycle_time_end; @@ -864,15 +864,11 @@ WinMain( 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 ], 0 ); + JslSetLightColour( jsl_device_handles[ jsl_device_index ], 0 ); } } return 0; } - -// Engine layer translation unit. -// #include "engine.cpp" diff --git a/project/platform/macros.h b/project/platform/macros.h index 7669ce4..dd10ee0 100644 --- a/project/platform/macros.h +++ b/project/platform/macros.h @@ -37,4 +37,7 @@ #define do_once_end \ } \ - while(0); \ No newline at end of file + while(0); + + +#define array_count( array ) ( sizeof( array ) / sizeof( ( array )[0] ) ) diff --git a/project/platform/platform.h b/project/platform/platform.h index 1ef2ba6..8cc58bd 100644 --- a/project/platform/platform.h +++ b/project/platform/platform.h @@ -6,6 +6,7 @@ #pragma once #include "grime.h" #include "macros.h" +#include "generics.h" #include "math_constants.h" #include "types.h" diff --git a/project/platform/types.h b/project/platform/types.h index 80956b9..e7874d4 100644 --- a/project/platform/types.h +++ b/project/platform/types.h @@ -4,29 +4,31 @@ #define U8_MIN 0u #define U8_MAX 0xffu -#define I8_MIN ( -0x7f - 1 ) -#define I8_MAX 0x7f +#define S8_MIN ( -0x7f - 1 ) +#define S8_MAX 0x7f #define U16_MIN 0u #define U16_MAX 0xffffu -#define I16_MIN ( -0x7fff - 1 ) -#define I16_MAX 0x7fff +#define S16_MIN ( -0x7fff - 1 ) +#define S16_MAX 0x7fff #define U32_MIN 0u #define U32_MAX 0xffffffffu -#define I32_MIN ( -0x7fffffff - 1 ) -#define I32_MAX 0x7fffffff +#define S32_MIN ( -0x7fffffff - 1 ) +#define S32_MAX 0x7fffffff #define U64_MIN 0ull #define U64_MAX 0xffffffffffffffffull -#define I64_MIN ( -0x7fffffffffffffffll - 1 ) -#define I64_MAX 0x7fffffffffffffffll +#define S64_MIN ( -0x7fffffffffffffffll - 1 ) +#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 ) -# define USIZE_MIN GEN_U64_MIN -# define USIZE_MAX GEN_U64_MAX -# define ISIZE_MIN GEN_I64_MIN -# define ISIZE_MAX GEN_I64_MAX +# define UWORD_MIN U64_MIN +# define UWORD_MAX U64_MAX +# define SWORD_MIN S64_MIN +# define SWORD_MAX S64_MAX #else # error Unknown architecture size. This library only supports 64 bit architectures. #endif diff --git a/scripts/handmade.rdbg b/scripts/handmade.rdbg index 877b5a8..649867a 100644 Binary files a/scripts/handmade.rdbg and b/scripts/handmade.rdbg differ