Day 13 complete

This commit is contained in:
Edward R. Gonzalez 2023-09-17 21:20:11 -04:00
parent ad5288f9e8
commit abe3066071
9 changed files with 619 additions and 262 deletions

96
docs/Day 013.md Normal file
View 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;
};
```

View File

@ -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,13 +101,132 @@ 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
if ( ! wave_switch )
output_sound( sound_buffer, sine_wave_sample_value );
else
output_sound( sound_buffer, square_wave_sample_value ); 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 );

View File

@ -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

View File

@ -0,0 +1,10 @@
#pragma once
template< class Type >
void swap( Type& a, Type& b )
{
Type
temp = a;
a = b;
b = temp;
}

View File

@ -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,33 +510,13 @@ 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;
MSG window_msg_info;
{
window_class.style = CS_Horizontal_Redraw | CS_Vertical_Redraw; window_class.style = CS_Horizontal_Redraw | CS_Vertical_Redraw;
window_class.lpfnWndProc = main_window_callback; window_class.lpfnWndProc = main_window_callback;
// window_class.cbClsExtra = ; // window_class.cbClsExtra = ;
@ -547,7 +534,7 @@ WinMain(
return 0; return 0;
} }
HWND window_handle = CreateWindowExW( window_handle = CreateWindowExW(
0, 0,
window_class.lpszClassName, window_class.lpszClassName,
L"Handmade Hero", L"Handmade Hero",
@ -563,13 +550,13 @@ WinMain(
// TODO : Diagnostic Logging // TODO : Diagnostic Logging
return 0; 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; sound_output.IsPlaying = 0;
DS_SecondaryBuffer_SamplesPerSecond = 48000; DS_SecondaryBuffer_SamplesPerSecond = 48000;
DS_SecondaryBuffer_BytesPerSample = sizeof(s16) * 2; DS_SecondaryBuffer_BytesPerSample = sizeof(s16) * 2;
@ -580,53 +567,69 @@ WinMain(
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
bool wave_switch = false;
sound_output.RunningSampleIndex = 0; 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; sound_output.LatencySampleCount = DS_SecondaryBuffer_SamplesPerSecond / 15;
ds_clear_sound_buffer( & sound_output ); // ds_clear_sound_buffer( & sound_output );
DS_SecondaryBuffer->Play( 0, 0, DSBPLAY_LOOPING ); DS_SecondaryBuffer->Play( 0, 0, DSBPLAY_LOOPING );
}
// Graphics & Input Test // Timing
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;
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,102 +649,131 @@ 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 else
{ {
// NOTE: Controller is not available 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
{
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;
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->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... // Pain...
b32 sound_is_valid = false; b32 sound_is_valid = false;
@ -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"

View File

@ -38,3 +38,6 @@
#define do_once_end \ #define do_once_end \
} \ } \
while(0); while(0);
#define array_count( array ) ( sizeof( array ) / sizeof( ( array )[0] ) )

View File

@ -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"

View File

@ -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.