Day 25 Part 1

I'm splitting this implementation into parts since so much already happened...

I fully updated the code the the latest convention I want to use for the project.
Engine & Game replay should work.
This commit is contained in:
Edward R. Gonzalez 2023-09-30 10:05:37 -04:00
parent 465339743a
commit dc43db117c
13 changed files with 881 additions and 589 deletions

View File

@ -14,22 +14,22 @@
"label": "Static Data"
},
{
"line": 640,
"line": 655,
"column": 0,
"label": "Timing"
},
{
"line": 1471,
"line": 1575,
"column": 4,
"label": "Main Loop : Audio Processing"
},
{
"line": 1590,
"line": 1694,
"column": 2,
"label": "Main Loop : Timing Update"
},
{
"line": 1674,
"line": 1778,
"column": 0,
"label": "Main Loop : End"
}

12
docs/Day 025.md Normal file
View File

@ -0,0 +1,12 @@
# Day 25
Him doing replay save states at the platform layer is causing issues for me...
I'm going to most likely end up doing two categories of save states, one for engine level and one for platform level.
The engine level can just store it in the memory struct, and the game memory can just stay where it is.
I want to use the same "save state" slots for both engine and game since there is no need different memory buckets for that. They will at worst case be ofcourse the size of engine's memory block.
So if there is 4, we are looking at 5x the actual memory size. This is fine for now since the engine block is 2 gigs.
Since engine manages the memory of game and editor (when editor exists), restoring its state will also restore the game and editor state.

View File

@ -8,6 +8,8 @@
#include "dependencies/gen.hpp"
#undef ccast
#undef pcast
#undef rcast
#undef scast
#undef do_once
#undef do_once_start
#undef do_once_end

View File

@ -5,73 +5,57 @@
NS_ENGINE_BEGIN
#define pressed( btn ) (btn.EndedDown && btn.HalfTransitions == 1)
#define pressed( btn ) (btn.ended_down && btn.half_transitions > 0)
// Used to determine if analog input is at move threshold
constexpr f32 analog__move_threshold = 0.5f;
struct EngineActions
{
b32 move_up = false;
b32 move_down = false;
b32 move_left = false;
b32 move_right = false;
b32 move_up;
b32 move_down;
b32 move_left;
b32 move_right;
b32 loop_mode = false;
b32 loop_mode_engine;
b32 loop_mode_game;
b32 raise_volume = false;
b32 lower_volume = false;
b32 raise_tone_hz = false;
b32 lower_tone_hz = false;
b32 raise_volume;
b32 lower_volume;
b32 raise_tone_hz;
b32 lower_tone_hz;
b32 toggle_wave_tone = false;
b32 toggle_wave_tone;
#if Build_Development
b32 pause_renderer = false;
b32 pause_renderer;
#endif
};
struct PlayerActions
{
s32 player_x_move_digital = 0;
s32 player_y_move_digital = 0;
f32 player_x_move_analog = 0;
f32 player_y_move_analog = 0;
b32 jump = false;
};
struct EngineState
{
s32 WaveToneHz;
s32 ToneVolume;
s32 XOffset;
s32 YOffset;
s32 wave_tone_hz;
s32 tone_volume;
s32 x_offset;
s32 y_offset;
b32 RendererPaused;
b32 renderer_paused;
f32 SampleWaveSineTime;
b32 SampleWaveSwitch;
s32 InputRecordingIndex;
s32 InputPlayingIndex;
platform::File ActiveInputRecordingFile;
platform::File ActivePlaybackFile;
f32 sample_wave_sine_time;
b32 sample_wave_switch;
hh::Memory game_memory;
};
using GetSoundSampleValueFn = s16( EngineState* state, AudioBuffer* sound_buffer );
internal s16
square_wave_sample_value( EngineState* state, AudioBuffer* sound_buffer )
{
s32 wave_period = sound_buffer->SamplesPerSecond / state->WaveToneHz;
s32 wave_period = sound_buffer->samples_per_second / state->wave_tone_hz;
s32 sample_value = (sound_buffer->RunningSampleIndex / (wave_period / 2) ) % 2 ?
state->ToneVolume : - state->ToneVolume;
s32 sample_value = (sound_buffer->running_sample_index / (wave_period / 2) ) % 2 ?
state->tone_volume : - state->tone_volume;
return scast(s16, sample_value);
}
@ -79,13 +63,13 @@ square_wave_sample_value( EngineState* state, AudioBuffer* sound_buffer )
internal s16
sine_wave_sample_value( EngineState* state, AudioBuffer* sound_buffer )
{
f32& time = state->SampleWaveSineTime;
f32& time = state->sample_wave_sine_time;
s32 wave_period = sound_buffer->SamplesPerSecond / state->WaveToneHz;
s32 wave_period = sound_buffer->samples_per_second / state->wave_tone_hz;
// time = TAU * (f32)sound_buffer->RunningSampleIndex / (f32)SoundTest_WavePeriod;
f32 sine_value = sinf( time );
s16 sample_value = scast(s16, sine_value * scast(f32, state->ToneVolume));
s16 sample_value = scast(s16, sine_value * scast(f32, state->tone_volume));
time += TAU * 1.0f / scast(f32, wave_period );
if ( time > TAU )
@ -98,11 +82,11 @@ sine_wave_sample_value( EngineState* state, AudioBuffer* sound_buffer )
internal void
output_sound( EngineState* state, AudioBuffer* sound_buffer, GetSoundSampleValueFn* get_sample_value )
{
s16* sample_out = sound_buffer->Samples;
for ( s32 sample_index = 0; sample_index < sound_buffer->NumSamples; ++ sample_index )
s16* sample_out = sound_buffer->samples;
for ( s32 sample_index = 0; sample_index < sound_buffer->num_samples; ++ sample_index )
{
s16 sample_value = get_sample_value( state, sound_buffer );
sound_buffer->RunningSampleIndex++;
sound_buffer->running_sample_index++;
// char ms_timing_debug[256] {};
// wsprintfA( ms_timing_debug, "sample_value: %d\n", sample_value );
@ -128,14 +112,14 @@ render_weird_graident(OffscreenBuffer* buffer, u32 x_offset, u32 y_offset )
u8 Alpha;
};
u8* row = rcast( u8*, buffer->Memory);
u8* row = rcast( u8*, buffer->memory);
local_persist float wildcard = 0;
for ( u32 y = 0; y < buffer->Height; ++ y )
for ( u32 y = 0; y < buffer->height; ++ y )
{
// u8* pixel = rcast(u8*, row);
// Pixel* pixel = rcast( Pixel*, row );
u32* pixel = rcast(u32*, row);
for ( u32 x = 0; x < buffer->Width; ++ x )
for ( u32 x = 0; x < buffer->width; ++ x )
{
/* Pixel in memory:
-----------------------------------------------
@ -150,25 +134,25 @@ render_weird_graident(OffscreenBuffer* buffer, u32 x_offset, u32 y_offset )
u8 green = scast(u8, y + y_offset - u8(wildcard) % 128);
u8 red = scast(u8, wildcard) % 256 - x * 0.4f;
#else
u8 red = scast(u8, y + x_offset);
u8 green = scast(u8, x + y_offset);
u8 blue = scast(u8, x + y - x_offset - y_offset);
u8 red = scast(u8, y + y_offset);
u8 green = scast(u8, x + x_offset);
u8 blue = scast(u8, x + y_offset) - scast(u8, y + y_offset);
// blue *= 2;
#endif
*pixel++ = u32(red/2 << 16) | u32(green/6 << 8) | blue/2 << 0;
*pixel++ = u32(red/2 << 16) | u32(green/6 << 0) | blue/2 << 0;
}
wildcard += 0.5375f;
row += buffer->Pitch;
row += buffer->pitch;
}
}
internal void
render_player( OffscreenBuffer* buffer, s32 pos_x, s32 pos_y )
{
u8* end_of_buffer = rcast(u8*, buffer->Memory)
- buffer->BytesPerPixel * buffer->Width
+ buffer->Pitch * buffer->Height;
u8* end_of_buffer = rcast(u8*, buffer->memory)
- buffer->bytes_per_pixel * buffer->width
+ buffer->pitch * buffer->height;
s32 top = pos_y;
s32 bottom = pos_y + 10;
@ -178,198 +162,286 @@ render_player( OffscreenBuffer* buffer, s32 pos_x, s32 pos_y )
for ( s32 coord_x = pos_x; coord_x < (pos_x+ 10); ++ coord_x )
{
u8*
pixel_byte = rcast(u8*, buffer->Memory);
pixel_byte += coord_x * buffer->BytesPerPixel;
pixel_byte += top * buffer->Pitch;
pixel_byte = rcast(u8*, buffer->memory);
pixel_byte += coord_x * buffer->bytes_per_pixel;
pixel_byte += top * buffer->pitch;
for ( s32 coord_y = top; coord_y < bottom; ++ coord_y )
{
if ( pixel_byte < buffer->Memory || pixel_byte >= end_of_buffer )
if ( pixel_byte < buffer->memory || pixel_byte >= end_of_buffer )
continue;
s32* pixel = rcast(s32*, pixel_byte);
*pixel = color;
pixel_byte += buffer->Pitch;
pixel_byte += buffer->pitch;
}
}
}
using SnapshotFn = void ( s32 slot, Memory* memory, platform::ModuleAPI* platform_api );
internal
void begin_recording_input( EngineState* state, InputState* input, platform::ModuleAPI* platform_api )
void load_engine_snapshot( s32 slot, Memory* memory, platform::ModuleAPI* platform_api )
{
platform_api->memory_copy( memory->persistent, memory->total_size(), memory->snapshots[ slot ].memory );
}
internal
void load_game_snapshot( s32 slot, Memory* memory, platform::ModuleAPI* platform_api )
{
EngineState* state = rcast( EngineState*, memory->persistent );
void* persistent_slot = memory->snapshots[ slot ].memory;
void* transient_slot = rcast( Byte*, memory->snapshots[ slot ].memory ) + state->game_memory.persistent_size;
platform_api->memory_copy( state->game_memory.persistent, state->game_memory.persistent_size, persistent_slot );
platform_api->memory_copy( state->game_memory.transient, state->game_memory.transient_size, transient_slot );
}
internal
void take_engine_snapshot( s32 slot, Memory* memory, platform::ModuleAPI* platform_api )
{
platform_api->memory_copy( memory->snapshots[ slot ].memory, memory->total_size(), memory->persistent );
}
internal
void take_game_snapshot( s32 slot, Memory* memory, platform::ModuleAPI* platform_api )
{
EngineState* state = rcast( EngineState*, memory->persistent );
void* persistent_slot = memory->snapshots[ slot ].memory;
void* transient_slot = rcast( Byte*, memory->snapshots[ slot ].memory ) + state->game_memory.persistent_size;
platform_api->memory_copy( persistent_slot, state->game_memory.persistent_size, state->game_memory.persistent );
platform_api->memory_copy( transient_slot, state->game_memory.transient_size, state->game_memory.transient );
}
#define Snapshot_New_Way 1
internal
void begin_recording_input( Memory* memory, InputState* input, platform::ModuleAPI* platform_api )
{
Str file_name = str_ascii("test_input.hmi");
StrPath file_path = {};
file_path.concat( platform_api->path_scratch, file_name );
state->ActiveInputRecordingFile.Path = file_path;
state->InputRecordingIndex = 1;
memory->active_recording_file.path = file_path;
memory->input_recording_index = 1;
// TODO(Ed) : If game persist memory is larger than 4 gb, this will need to be done in chunks...
#if Snapshot_New_Way
platform_api->file_delete( memory->active_recording_file.path );
#else
platform_api->file_write_content( & state->ActiveInputRecordingFile, scast(u32, state->game_memory.PersistentSize), state->game_memory.Persistent );
#endif
}
internal
void end_recording_input( EngineState* state, InputState* input, platform::ModuleAPI* platform_api )
void end_recording_input( Memory* memory, InputState* input, platform::ModuleAPI* platform_api )
{
platform_api->file_close( & state->ActiveInputRecordingFile );
state->InputRecordingIndex = 0;
memory->input_recording_index = 0;
platform_api->file_close( & memory->active_recording_file );
}
internal
void begin_playback_input( EngineState* state, InputState* input, platform::ModuleAPI* platform_api )
void begin_playback_input( Memory* memory, InputState* input, platform::ModuleAPI* platform_api )
{
Str file_name = str_ascii("test_input.hmi");
StrPath file_path = {};
file_path.concat( platform_api->path_scratch, file_name );
if ( platform_api->file_check_exists( file_path ) )
{
state->ActivePlaybackFile.Path = file_path;
state->InputPlayingIndex = 1;
memory->active_playback_file.path = file_path;
memory->input_playback_index = 1;
}
#if Snapshot_New_Way
#else
if ( state->ActiveInputRecordingFile.OpaqueHandle == nullptr )
{
platform_api->file_read_stream( & state->ActivePlaybackFile, scast(u32, state->game_memory.PersistentSize), state->game_memory.Persistent );
}
#endif
}
internal
void end_playback_input( EngineState* state, InputState* input, platform::ModuleAPI* platform_api )
void end_playback_input( Memory* memory, InputState* input, platform::ModuleAPI* platform_api )
{
memory->input_playback_index = 0;
#if Snapshot_New_Way
#else
platform_api->file_rewind( & state->ActivePlaybackFile );
platform_api->file_read_stream( & state->ActivePlaybackFile, scast(u32, state->game_memory.PersistentSize), state->game_memory.Persistent );
platform_api->file_close( & state->ActivePlaybackFile );
#endif
state->InputPlayingIndex = 0;
platform_api->file_close( & memory->active_playback_file );
}
InputStateSnapshot input_state_snapshot( InputState* input )
{
InputStateSnapshot snapshot = {};
for ( s32 idx = 0; idx < array_count( snapshot.Controllers ); ++ idx )
for ( s32 idx = 0; idx < array_count( snapshot.controllers ); ++ idx )
{
ControllerState* controller = & input->Controllers[idx];
ControllerState* controller = & input->controllers[idx];
if ( controller == nullptr )
continue;
if ( controller->DSPad )
snapshot.Controllers[idx].DSPad = *controller->DSPad;
if ( controller->ds_pad )
snapshot.controllers[idx].ds_pad = *controller->ds_pad;
if ( controller->XPad )
snapshot.Controllers[idx].XPad = *controller->XPad;
if ( controller->xpad )
snapshot.controllers[idx].xpad = *controller->xpad;
if ( controller->Keyboard )
if ( controller->keyboard )
{
snapshot.Controllers[idx].Keyboard = *controller->Keyboard;
snapshot.controllers[idx].keyboard = *controller->keyboard;
}
if ( controller->Mouse )
snapshot.Controllers[idx].Mouse = *controller->Mouse;
if ( controller->mouse )
snapshot.controllers[idx].mouse = *controller->mouse;
}
return snapshot;
}
internal
void record_input( EngineState* state, InputState* input, platform::ModuleAPI* platform_api )
void record_input( Memory* memory, InputState* input, platform::ModuleAPI* platform_api )
{
InputStateSnapshot snapshot = input_state_snapshot( input );
if ( platform_api->file_write_stream( & state->ActiveInputRecordingFile, sizeof(snapshot), &snapshot ) == 0 )
if ( platform_api->file_write_stream( & memory->active_recording_file, sizeof(snapshot), &snapshot ) == 0 )
{
// TODO(Ed) : Logging
}
}
internal
void play_input( EngineState* state, InputState* input, platform::ModuleAPI* platform_api )
void play_input( SnapshotFn* load_snapshot, Memory* memory, InputState* input, platform::ModuleAPI* platform_api )
{
InputStateSnapshot new_input;
if ( platform_api->file_read_stream( & state->ActivePlaybackFile, sizeof(InputStateSnapshot), & new_input ) == 0 )
if ( platform_api->file_read_stream( & memory->active_playback_file, sizeof(InputStateSnapshot), & new_input ) == 0 )
{
end_playback_input( state, input, platform_api );
begin_playback_input( state, input, platform_api );
end_playback_input( memory, input, platform_api );
load_snapshot( memory->active_snapshot_slot, memory, platform_api );
begin_playback_input( memory, input, platform_api );
return;
}
for ( s32 idx = 0; idx < array_count( new_input.Controllers ); ++ idx )
for ( s32 idx = 0; idx < array_count( new_input.controllers ); ++ idx )
{
ControllerState* controller = & input->Controllers[idx];
ControllerState* controller = & input->controllers[idx];
if ( controller == nullptr )
continue;
if ( controller->DSPad )
*controller->DSPad = new_input.Controllers[idx].DSPad;
if ( controller->ds_pad )
*controller->ds_pad = new_input.controllers[idx].ds_pad;
if ( controller->XPad )
*controller->XPad = new_input.Controllers[idx].XPad;
if ( controller->xpad )
*controller->xpad = new_input.controllers[idx].xpad;
if ( controller->Keyboard )
if ( controller->keyboard )
{
*controller->Keyboard = new_input.Controllers[idx].Keyboard;
*controller->keyboard = new_input.controllers[idx].keyboard;
}
if ( controller->Mouse )
*controller->Mouse = new_input.Controllers[idx].Mouse;
if ( controller->mouse )
*controller->mouse = new_input.controllers[idx].mouse;
}
}
void process_loop_mode( SnapshotFn* take_snapshot, SnapshotFn* load_snapshot
, Memory* memory, EngineState* state, InputState* input, platform::ModuleAPI* platform_api )
{
if ( memory->input_recording_index == 0 && memory->input_playback_index == 0 )
{
take_snapshot( 0, memory, platform_api );
memory->active_snapshot_slot = 0;
begin_recording_input( memory, input, platform_api );
}
else if ( memory->input_playback_index )
{
end_playback_input( memory, input, platform_api );
load_snapshot( memory->active_snapshot_slot, memory, platform_api );
}
else if ( memory->input_recording_index )
{
end_recording_input( memory, input, platform_api );
load_snapshot( memory->active_snapshot_slot, memory, platform_api );
begin_playback_input( memory, input, platform_api );
}
}
internal
void input_poll_engine_actions( InputState* input, EngineActions* actions )
{
ControllerState* controller = & input->Controllers[0];
KeyboardState* keyboard = controller->Keyboard;
ControllerState* controller = & input->controllers[0];
KeyboardState* keyboard = controller->keyboard;
actions->move_right |= keyboard->D.EndedDown;
actions->move_left |= keyboard->A.EndedDown;
actions->move_up |= keyboard->W.EndedDown;
actions->move_down |= keyboard->S.EndedDown;
// actions->move_right |= keyboard->D.EndedDown;
// actions->move_left |= keyboard->A.EndedDown;
// actions->move_up |= keyboard->W.EndedDown;
// actions->move_down |= keyboard->S.EndedDown;
actions->raise_volume |= keyboard->Up.EndedDown;
actions->lower_volume |= keyboard->Down.EndedDown;
actions->raise_volume |= keyboard->up.ended_down;
actions->lower_volume |= keyboard->down.ended_down;
actions->raise_tone_hz |= keyboard->Right.EndedDown;
actions->lower_tone_hz |= keyboard->Left.EndedDown;
actions->raise_tone_hz |= keyboard->right.ended_down;
actions->lower_tone_hz |= keyboard->left.ended_down;
#if Build_Development
actions->pause_renderer |= pressed( keyboard->Pause );
actions->pause_renderer |= pressed( keyboard->pause );
#endif
actions->toggle_wave_tone |= pressed( keyboard->Q );
actions->loop_mode |= pressed( keyboard->L );
actions->loop_mode_game |= pressed( keyboard->L ) && ! keyboard->right_shift.ended_down;
actions->loop_mode_engine |= pressed( keyboard->L ) && keyboard->right_shift.ended_down;
MousesState* mouse = controller->mouse;
actions->move_right = (mouse->horizontal_wheel.end > 0.f) * 20;
actions->move_left = (mouse->horizontal_wheel.end < 0.f) * 20;
actions->move_up = (mouse->vertical_wheel.end > 0.f) * 10;
actions->move_down = (mouse->vertical_wheel.end < 0.f) * 10;
}
internal
void input_poll_player_actions( InputState* input, PlayerActions* actions )
void input_poll_player_actions( InputState* input, hh::PlayerActions* actions )
{
ControllerState* controller = & input->Controllers[0];
ControllerState* controller = & input->controllers[0];
if ( controller->DSPad )
if ( controller->ds_pad )
{
DualsensePadState* pad = controller->DSPad;
DualsensePadState* pad = controller->ds_pad;
actions->jump |= pressed( pad->X );
actions->jump |= pressed( pad->cross );
actions->player_x_move_analog += pad->Stick.Left.X.End;
actions->player_y_move_analog += pad->Stick.Left.Y.End;
actions->player_x_move_analog += pad->stick.left.X.end;
actions->player_y_move_analog += pad->stick.left.Y.end;
}
if ( controller->XPad )
if ( controller->xpad )
{
XInputPadState* pad = controller->XPad;
XInputPadState* pad = controller->xpad;
actions->jump |= pressed( pad->A );
actions->player_x_move_analog += pad->Stick.Left.X.End;
actions->player_y_move_analog += pad->Stick.Left.Y.End;
actions->player_x_move_analog += pad->stick.left.X.end;
actions->player_y_move_analog += pad->stick.left.Y.end;
}
if ( controller->Keyboard )
if ( controller->keyboard )
{
KeyboardState* keyboard = controller->Keyboard;
actions->jump |= pressed( keyboard->Space );
KeyboardState* keyboard = controller->keyboard;
actions->jump |= pressed( keyboard->space );
actions->player_x_move_digital += keyboard->D.EndedDown - keyboard->A.EndedDown;
actions->player_y_move_digital += keyboard->W.EndedDown - keyboard->S.EndedDown;
actions->player_x_move_digital += keyboard->D.ended_down - keyboard->A.ended_down;
actions->player_y_move_digital += keyboard->W.ended_down - keyboard->S.ended_down;
}
if ( controller->mouse )
{
MousesState* mouse = controller->mouse;
}
}
@ -382,37 +454,44 @@ void on_module_reload( Memory* memory, platform::ModuleAPI* platfom_api )
Engine_API
void startup( Memory* memory, platform::ModuleAPI* platform_api )
{
EngineState* state = rcast( EngineState*, memory->Persistent );
assert( sizeof(EngineState) <= memory->PersistentSize );
memory->active_snapshot_slot = -1;
memory->input_recording_index = 0;
memory->input_playback_index = 0;
memory->active_recording_file = {};
memory->active_playback_file = {};
memory->engine_loop_active = false;
memory->game_loop_active = false;
state->ToneVolume = 1000;
for ( s32 slot = 0; slot < memory->Num_Snapshot_Slots; ++ slot )
{
// TODO(Ed) : Specify default file paths for saving slots ?
}
state->XOffset = 0;
state->YOffset = 0;
EngineState* state = rcast( EngineState*, memory->persistent );
assert( sizeof(EngineState) <= memory->persistent_size );
state->SampleWaveSwitch = false;
state->WaveToneHz = 60;
state->SampleWaveSineTime = 0.f;
state->tone_volume = 1000;
state->RendererPaused = false;
state->x_offset = 0;
state->y_offset = 0;
state->InputRecordingIndex = 0;
state->InputPlayingIndex = 0;
state->sample_wave_switch = false;
state->wave_tone_hz = 60;
state->sample_wave_sine_time = 0.f;
state->ActiveInputRecordingFile = {};
state->ActivePlaybackFile = {};
state->renderer_paused = false;
state->game_memory.persistent_size = memory->persistent_size / 2;
state->game_memory.persistent = rcast(Byte*, memory->persistent) + state->game_memory.persistent_size;
state->game_memory.transient_size = memory->transient_size / 2;
state->game_memory.transient = rcast(Byte*, memory->transient) + state->game_memory.transient_size;
state->game_memory.PersistentSize = memory->PersistentSize / 2;
state->game_memory.Persistent = rcast(Byte*, memory->Persistent) + state->game_memory.PersistentSize;
state->game_memory.TransientSize = memory->TransientSize / 2;
state->game_memory.Transient = rcast(Byte*, memory->Transient) + state->game_memory.TransientSize;
hh::PlayerState* player = rcast( hh::PlayerState*, state->game_memory.persistent );
assert( sizeof(hh::PlayerState) <= state->game_memory.persistent_size );
hh::PlayerState* player = rcast( hh::PlayerState*, state->game_memory.Persistent );
assert( sizeof(hh::PlayerState) <= state->game_memory.PersistentSize );
player->Pos_X = 100;
player->Pos_Y = 100;
player->MidJump = false;
player->pos_x = 100;
player->pos_y = 100;
player->mid_jump = false;
player->jump_time = 0.f;
}
Engine_API
@ -422,137 +501,169 @@ void shutdown( Memory* memory, platform::ModuleAPI* platform_api )
Engine_API
// TODO : I rather expose the back_buffer and sound_buffer using getters for access in any function.
void update_and_render( InputState* input, OffscreenBuffer* back_buffer, Memory* memory, platform::ModuleAPI* platform_api )
void update_and_render( InputState* input, OffscreenBuffer* back_buffer, Memory* memory, platform::ModuleAPI* platform_api, ThreadContext* thread )
{
EngineState* state = rcast( EngineState*, memory->Persistent );
assert( sizeof(EngineState) <= memory->PersistentSize );
EngineState* state = rcast( EngineState*, memory->persistent );
assert( sizeof(EngineState) <= memory->persistent_size );
ControllerState* controller = & input->Controllers[0];
ControllerState* controller = & input->controllers[0];
EngineActions engine_actions {};
PlayerActions player_actions {};
hh::PlayerActions player_actions {};
input_poll_engine_actions( input, & engine_actions );
// Ease of use: Allow user to press L key without shift if engine loop recording is active.
engine_actions.loop_mode_engine |= engine_actions.loop_mode_game && memory->engine_loop_active;
if ( engine_actions.loop_mode_engine && ! memory->game_loop_active )
{
state->XOffset += 3 * engine_actions.move_right;
state->XOffset -= 3 * engine_actions.move_left;
state->YOffset += 3 * engine_actions.move_down;
state->YOffset -= 3 * engine_actions.move_up;
process_loop_mode( & take_engine_snapshot, & load_engine_snapshot, memory, state, input, platform_api );
memory->engine_loop_active = memory->input_playback_index || memory->input_recording_index;
}
// Input recording and playback for engine state
if ( memory->engine_loop_active )
{
if ( memory->input_recording_index )
{
record_input( memory, input, platform_api );
}
if ( memory->input_playback_index )
{
play_input( & load_engine_snapshot, memory, input, platform_api );
}
}
// Process Engine Actions
{
state->x_offset += 3 * engine_actions.move_right;
state->x_offset -= 3 * engine_actions.move_left;
state->y_offset += 3 * engine_actions.move_down;
state->y_offset -= 3 * engine_actions.move_up;
if ( engine_actions.raise_volume )
{
state->ToneVolume += 10;
state->tone_volume += 10;
}
if ( engine_actions.lower_volume )
{
state->ToneVolume -= 10;
if ( state->ToneVolume <= 0 )
state->ToneVolume = 0;
state->tone_volume -= 10;
if ( state->tone_volume <= 0 )
state->tone_volume = 0;
}
if ( engine_actions.raise_tone_hz )
{
state->WaveToneHz += 1;
state->wave_tone_hz += 1;
}
if ( engine_actions.lower_tone_hz )
{
state->WaveToneHz -= 1;
if ( state->WaveToneHz <= 0 )
state->WaveToneHz = 1;
state->wave_tone_hz -= 1;
if ( state->wave_tone_hz <= 0 )
state->wave_tone_hz = 1;
}
if ( engine_actions.toggle_wave_tone )
{
state->SampleWaveSwitch ^= true;
state->sample_wave_switch ^= true;
}
if ( engine_actions.loop_mode )
if ( engine_actions.loop_mode_game && ! memory->engine_loop_active )
{
if ( state->InputRecordingIndex == 0 && state->InputPlayingIndex == 0 )
{
begin_recording_input( state, input, platform_api );
}
else if ( state->InputPlayingIndex )
{
end_playback_input( state, input, platform_api );
}
else if ( state->InputRecordingIndex )
{
end_recording_input( state, input, platform_api );
begin_playback_input( state, input, platform_api );
}
process_loop_mode( & take_game_snapshot, & load_game_snapshot, memory, state, input, platform_api );
memory->game_loop_active = memory->input_playback_index || memory->input_recording_index;
}
#if Build_Development
if ( engine_actions.pause_renderer )
{
if ( state->RendererPaused )
if ( state->renderer_paused )
{
platform_api->debug_set_pause_rendering(false);
state->RendererPaused = false;
state->renderer_paused = false;
}
else
{
platform_api->debug_set_pause_rendering(true);
state->RendererPaused = true;
state->renderer_paused = true;
}
}
#endif
}
if ( state->InputRecordingIndex )
if ( ! memory->engine_loop_active )
{
record_input( state, input, platform_api );
// Input recording and playback for game state
if ( memory->input_recording_index )
{
record_input( memory, input, platform_api );
}
if ( state->InputPlayingIndex )
if ( memory->input_playback_index )
{
play_input( state, input, platform_api );
play_input( & load_game_snapshot, memory, input, platform_api );
}
}
hh::PlayerState* player = rcast( hh::PlayerState*, state->game_memory.Persistent );
assert( sizeof(hh::PlayerState) <= state->game_memory.PersistentSize );
hh::PlayerState* player = rcast( hh::PlayerState*, state->game_memory.persistent );
assert( sizeof(hh::PlayerState) <= state->game_memory.persistent_size );
input_poll_player_actions( input, & player_actions );
{
player->Pos_X += player_actions.player_x_move_digital * 5;
player->Pos_Y -= player_actions.player_y_move_digital * 5;
player->Pos_X += scast(u32, player_actions.player_x_move_analog * 5);
player->Pos_Y -= scast(u32, player_actions.player_y_move_analog * 5) - scast(u32, sinf( player->JumpTime * TAU ) * 10);
player->pos_x += player_actions.player_x_move_digital * 5;
player->pos_y -= player_actions.player_y_move_digital * 5;
player->pos_x += scast(u32, player_actions.player_x_move_analog * 5);
player->pos_y -= scast(u32, player_actions.player_y_move_analog * 5) - scast(u32, sinf( player->jump_time * TAU ) * 10);
if ( player->JumpTime > 0.f )
if ( player->jump_time > 0.f )
{
player->JumpTime -= 0.025f;
player->jump_time -= 0.025f;
}
else
{
player->JumpTime = 0.f;
player->MidJump = false;
player->jump_time = 0.f;
player->mid_jump = false;
}
if ( ! player->MidJump && player_actions.jump )
if ( ! player->mid_jump && player_actions.jump )
{
player->JumpTime = 1.f;
player->MidJump = true;
player->jump_time = 1.f;
player->mid_jump = true;
}
}
render_weird_graident( back_buffer, 0, 0 );
render_player( back_buffer, player->Pos_X, player->Pos_Y );
render_weird_graident( back_buffer, state->x_offset, state->y_offset );
render_player( back_buffer, player->pos_x, player->pos_y );
if ( state->InputRecordingIndex )
render_player( back_buffer, player->Pos_X + 20, player->Pos_Y - 20 );
if ( memory->input_recording_index )
render_player( back_buffer, player->pos_x + 20, player->pos_y - 20 );
render_player( back_buffer, (s32)input->controllers[0].mouse->X.end, (s32)input->controllers[0].mouse->Y.end );
// Mouse buttons test
{
if ( input->controllers[0].mouse->left.ended_down == true )
render_player( back_buffer, 5, 5 );
if ( input->controllers[0].mouse->middle.ended_down == true )
render_player( back_buffer, 5, 20 );
if ( input->controllers[0].mouse->right.ended_down == true )
render_player( back_buffer, 5, 35 );
}
}
Engine_API
void update_audio( AudioBuffer* audio_buffer, Memory* memory, platform::ModuleAPI* platform_api )
void update_audio( AudioBuffer* audio_buffer, Memory* memory, platform::ModuleAPI* platform_api, ThreadContext* thread )
{
EngineState* state = rcast( EngineState*, memory->Persistent );
EngineState* state = rcast( EngineState*, memory->persistent );
do_once_start
do_once_end
// TODO(Ed) : Allow sample offsets here for more robust platform options
if ( ! state->SampleWaveSwitch )
if ( ! state->sample_wave_switch )
output_sound( state, audio_buffer, sine_wave_sample_value );
else
output_sound( state, audio_buffer, square_wave_sample_value );

View File

@ -14,7 +14,18 @@ NS_ENGINE_BEGIN
struct Clocks
{
// TODO(Ed) : Clock values...
f32 SecondsElapsed;
f32 seconds_elapsed;
};
struct ThreadContext
{
u32 placeholder;
};
struct MemorySnapshot
{
Str file_path;
void* memory;
};
struct Memory
@ -22,51 +33,75 @@ struct Memory
// All memory for the engine is required to be zero initialized.
// Wiped on shutdown
void* Persistent;
u64 PersistentSize;
void* persistent;
u64 persistent_size;
// Wiped on a per-frame basis
// void* Frame;
// u64 FrameSize;
// Wiped whenever the engine wants to?
void* Transient;
u64 TransientSize;
void* transient;
u64 transient_size;
// TODO(Ed) : Move this crap to state & replay archive definitions?
static constexpr
s32 Num_Snapshot_Slots = 4;
// Abuse RAM to store snapshots of the Engine or Game state.
MemorySnapshot snapshots[ Num_Snapshot_Slots ];
s32 active_snapshot_slot;
// Recording and playback info is the same for either engine or game.
s32 input_recording_index;
s32 input_playback_index;
platform::File active_recording_file;
platform::File active_playback_file;
// Engine-wide recording & playback loop.
s32 engine_loop_active;
s32 game_loop_active;
u64 total_size()
{
return persistent_size + transient_size;
}
};
struct OffscreenBuffer
{
void* Memory; // Lets use directly mess with the "pixel's memory buffer"
u32 Width;
u32 Height;
u32 Pitch;
u32 BytesPerPixel;
void* memory; // Lets use directly mess with the "pixel's memory buffer"
u32 width;
u32 height;
u32 pitch;
u32 bytes_per_pixel;
};
// TODO : Will be gutting this once we have other stuff lifted.
struct AudioBuffer
{
s16* Samples;
u32 RunningSampleIndex;
s32 SamplesPerSecond;
s32 NumSamples;
s16* samples;
u32 running_sample_index;
s32 samples_per_second;
s32 num_samples;
};
struct DigitalBtn
{
s32 HalfTransitions;
b32 EndedDown;
s32 half_transitions;
b32 ended_down;
};
struct AnalogAxis
{
f32 Start;
f32 End;
f32 Min;
f32 Max;
f32 start;
f32 end;
f32 min;
f32 max;
// Platform doesn't provide this, we process in the engine layer.
f32 Average;
f32 average;
};
struct AnalogStick
@ -77,62 +112,70 @@ struct AnalogStick
union KeyboardState
{
DigitalBtn Keys[12];
DigitalBtn keys[12];
struct {
DigitalBtn Row_1;
DigitalBtn row_1;
DigitalBtn Q;
DigitalBtn E;
DigitalBtn W;
DigitalBtn A;
DigitalBtn S;
DigitalBtn D;
DigitalBtn K;
DigitalBtn L;
DigitalBtn Escape;
DigitalBtn Backspace;
DigitalBtn Up;
DigitalBtn Down;
DigitalBtn Left;
DigitalBtn Right;
DigitalBtn Space;
DigitalBtn Pause;
DigitalBtn escape;
DigitalBtn backspace;
DigitalBtn up;
DigitalBtn down;
DigitalBtn left;
DigitalBtn right;
DigitalBtn space;
DigitalBtn pause;
DigitalBtn right_shift;
DigitalBtn left_shift;
};
};
struct MousesState
{
DigitalBtn Left;
DigitalBtn Middle;
DigitalBtn Right;
DigitalBtn left;
DigitalBtn middle;
DigitalBtn right;
AnalogAxis X;
AnalogAxis Y;
AnalogAxis vertical_wheel;
AnalogAxis horizontal_wheel;
};
struct XInputPadState
{
struct
{
AnalogStick Left;
AnalogStick Right;
} Stick;
AnalogStick left;
AnalogStick right;
} stick;
AnalogAxis LeftTrigger;
AnalogAxis RightTrigger;
AnalogAxis left_trigger;
AnalogAxis right_trigger;
union {
DigitalBtn Btns[14];
DigitalBtn btns[14];
struct {
struct {
DigitalBtn Up;
DigitalBtn Down;
DigitalBtn Left;
DigitalBtn Right;
} DPad;
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;
DigitalBtn back;
DigitalBtn start;
DigitalBtn left_shoulder;
DigitalBtn right_shoulder;
};
};
};
@ -141,28 +184,28 @@ struct DualsensePadState
{
struct
{
AnalogStick Left;
AnalogStick Right;
} Stick;
AnalogStick left;
AnalogStick right;
} stick;
AnalogAxis L2;
AnalogAxis R2;
union {
DigitalBtn Btns[14];
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 up;
DigitalBtn down;
DigitalBtn left;
DigitalBtn right;
} dpad;
DigitalBtn cross;
DigitalBtn circle;
DigitalBtn square;
DigitalBtn triangle;
DigitalBtn share;
DigitalBtn options;
DigitalBtn L1;
DigitalBtn R1;
};
@ -171,39 +214,39 @@ struct DualsensePadState
struct ControllerState
{
KeyboardState* Keyboard;
MousesState* Mouse;
XInputPadState* XPad;
DualsensePadState* DSPad;
KeyboardState* keyboard;
MousesState* mouse;
XInputPadState* xpad;
DualsensePadState* ds_pad;
};
struct ControllerStateSnapshot
{
KeyboardState Keyboard;
MousesState Mouse;
XInputPadState XPad;
DualsensePadState DSPad;
KeyboardState keyboard;
MousesState mouse;
XInputPadState xpad;
DualsensePadState ds_pad;
};
struct InputState
{
ControllerState Controllers[4];
ControllerState controllers[4];
};
struct InputStateSnapshot
{
ControllerStateSnapshot Controllers[4];
ControllerStateSnapshot controllers[4];
};
using InputBindCallback = void( void* );
using InputBindCallback_DigitalBtn = void( engine::DigitalBtn* Button );
using InputBindCallback_AnalogAxis = void( engine::AnalogAxis* Axis );
using InputBindCallback_AnalogStick = void( engine::AnalogStick* Stick );
using InputBindCallback_DigitalBtn = void( engine::DigitalBtn* button );
using InputBindCallback_AnalogAxis = void( engine::AnalogAxis* axis );
using InputBindCallback_AnalogStick = void( engine::AnalogStick* stick );
struct InputMode
{
InputBindCallback* Binds;
s32 NumBinds;
InputBindCallback* binds;
s32 num_binds;
};
void input_mode_pop( InputMode* mode );
@ -212,8 +255,8 @@ void input_mode_pop( InputMode* mode );
#if 0
struct RecordedInput
{
s32 Num;
InputState* Stream;
s32 num;
InputState* stream;
};
#endif

View File

@ -16,12 +16,12 @@ using ShutdownFn = void( Memory* memory, platform::ModuleAPI* platform_api
// Needs a contextual reference to four things:
// Timing, Input, Bitmap Buffer
using UpdateAndRenderFn = void ( InputState* input, OffscreenBuffer* back_buffer, Memory* memory, platform::ModuleAPI* platform_api );
using UpdateAndRenderFn = void ( InputState* input, OffscreenBuffer* back_buffer, Memory* memory, platform::ModuleAPI* platform_api, ThreadContext* thread );
// Audio timing is complicated, processing samples must be done at a different period from the rest of the engine's usual update.
// IMPORTANT: This has very tight timing, and cannot be more than a millisecond in execution.
// TODO(Ed) : Reduce timing pressure on performance by measuring it or pinging its time.
using UpdateAudioFn = void ( AudioBuffer* audio_buffer, Memory* memory, platform::ModuleAPI* platform_api );
using UpdateAudioFn = void ( AudioBuffer* audio_buffer, Memory* memory, platform::ModuleAPI* platform_api, ThreadContext* thread );
struct ModuleAPI
{

View File

@ -10,8 +10,8 @@ Str const symbol_startup = str_ascii("?startup@engine@@YAXPEAUMemory@1@PEAUModu
constexpr
Str const symbol_shutdown = str_ascii("?shutdown@engine@@YAXPEAUMemory@1@PEAUModuleAPI@platform@@@Z");
constexpr
Str const symbol_update_and_render = str_ascii("?update_and_render@engine@@YAXPEAUInputState@1@PEAUOffscreenBuffer@1@PEAUMemory@1@PEAUModuleAPI@platform@@@Z");
Str const symbol_update_and_render = str_ascii("?update_and_render@engine@@YAXPEAUInputState@1@PEAUOffscreenBuffer@1@PEAUMemory@1@PEAUModuleAPI@platform@@PEAUThreadContext@1@@Z");
constexpr
Str const symbol_update_audio = str_ascii("?update_audio@engine@@YAXPEAUAudioBuffer@1@PEAUMemory@1@PEAUModuleAPI@platform@@@Z");
Str const symbol_update_audio = str_ascii("?update_audio@engine@@YAXPEAUAudioBuffer@1@PEAUMemory@1@PEAUModuleAPI@platform@@PEAUThreadContext@1@@Z");
NS_ENGINE_END

View File

@ -11,22 +11,27 @@ struct Memory
{
// Subscection of engine memory for the game to use.
void* Persistent;
u64 PersistentSize;
void* persistent;
u64 persistent_size;
// void* Frame;
// u64 FrameSize;
void* Transient;
u64 TransientSize;
void* transient;
u64 transient_size;
u64 total_size()
{
return persistent_size + transient_size;
}
};
// We want a 'binding' to have multiple binds to active it (most likely)
struct Actionable
{
char const* Name;
engine::InputBindCallback* Binds;
s32 NumBinds;
char const* name;
engine::InputBindCallback* binds;
s32 num_binds;
};
struct ActionableMode
@ -58,18 +63,28 @@ struct ActionableMode
struct Player
{
// So far just has an assigned controller.
engine::ControllerState* Controller;
engine::ControllerState* controller;
// Possilby some other stuff in the future.
};
struct PlayerState
{
s32 Pos_X;
s32 Pos_Y;
s32 pos_x;
s32 pos_y;
b32 MidJump;
f32 JumpTime;
b32 mid_jump;
f32 jump_time;
};
struct PlayerActions
{
s32 player_x_move_digital;
s32 player_y_move_digital;
f32 player_x_move_analog;
f32 player_y_move_analog;
b32 jump = false;
};
NS_HANDMADE_END

View File

@ -11,6 +11,7 @@
#pragma warning( disable: 4820 ) // Support auto-adding padding to structs
#pragma warning( disable: 4711 ) // Support automatic inline expansion
#pragma warning( disable: 4710 ) // Support automatic inline expansion
#pragma warning( disable: 4805 ) // Support comparisons of s32 to bool.
#endif
#ifdef __clang__

View File

@ -10,19 +10,19 @@
// Casting
#define ccast( Type, Value ) ( const_cast< Type >( (Value) ) )
#define pcast( Type, Value ) ( * reinterpret_cast< Type* >( & ( Value ) ) )
#define rcast( Type, Value ) reinterpret_cast< Type >( Value )
#define scast( Type, Value ) static_cast< Type >( Value )
#define ccast( type, value ) ( const_cast< type >( (value) ) )
#define pcast( type, value ) ( * reinterpret_cast< type* >( & ( value ) ) )
#define rcast( type, value ) reinterpret_cast< type >( value )
#define scast( type, value ) static_cast< type >( value )
#define do_once() \
do \
{ \
local_persist \
bool Done = false; \
if ( Done ) \
bool done = false; \
if ( done ) \
return; \
Done = true; \
done = true; \
} \
while(0)
@ -30,10 +30,10 @@
do \
{ \
local_persist \
bool Done = false; \
if ( Done ) \
bool done = false; \
if ( done ) \
break; \
Done = true;
done = true;
#define do_once_end \
} \

View File

@ -34,10 +34,10 @@ using DebugSetPauseRenderingFn = void (b32 value);
struct File
{
void* OpaqueHandle;
Str Path;
void* Data;
u32 Size;
void* opaque_handle;
Str path;
void* data;
u32 size;
};
#pragma region Settings Exposure
@ -57,7 +57,7 @@ using SetEngineFrameTargetFn = void ( u32 rate_in_hz );
struct BinaryModule
{
void* OpaqueHandle;
void* opaque_handle;
};
using LoadBinaryModuleFn = BinaryModule ( char const* module_path );
@ -78,6 +78,8 @@ using FileWriteContentFn = u32 ( File* file, u32 content_size, void* content_mem
using FileWriteStreamFn = u32 ( File* file, u32 content_size, void* content_memory );
using FileRewindFn = void ( File* file );
using MemoryCopyFn = void( void* dest, u64 src_size, void* src );
struct ModuleAPI
{
Str path_root;
@ -106,6 +108,8 @@ struct ModuleAPI
FileRewindFn* file_rewind; // Rewinds the file stream to the beginning
FileWriteContentFn* file_write_content; // Writes content to file (overwrites)
FileWriteStreamFn* file_write_stream; // Appends content to file
MemoryCopyFn* memory_copy;
};
#pragma endregion Settings Exposure

View File

@ -12,32 +12,32 @@ u32 str_length( char const* str );
// Length tracked raw strings.
struct Str
{
u32 Len;
char* Data;
u32 len;
char* ptr;
void append( u32 src_len, char const* src ) {
str_append( Len, Data, src_len, src );
str_append( len, ptr, src_len, src );
}
void append( Str const src ) {
str_append( Len, Data, src.Len, src.Data );
str_append( len, ptr, src.len, src.ptr );
}
void concat( u32 dest_size, Str* dest, Str const str_a, Str const str_b )
{
str_concat( dest_size, dest->Data
, str_a.Len, str_a.Data
, str_b.Len, str_b.Data );
dest->Len = str_a.Len + str_b.Len;
str_concat( dest_size, dest->ptr
, str_a.len, str_a.ptr
, str_b.len, str_b.ptr );
dest->len = str_a.len + str_b.len;
}
operator char*() const {
return Data;
return ptr;
}
char& operator []( u32 idx ) {
return Data[idx];
return ptr[idx];
}
char const& operator []( u32 idx ) const {
return Data[idx];
return ptr[idx];
}
};
@ -47,35 +47,35 @@ struct StrFixed
{
constexpr static u32 Capacity = capacity;
u32 Len;
char Data[capacity];
u32 len;
char ptr[capacity];
void append( u32 src_len, char const* src ) {
str_append( Len, Data, src_len, src );
str_append( len, data, src_len, src );
}
void append( Str const src ) {
str_append( Len, Data, src.Len, src.Data );
str_append( len, data, src.Len, src.data );
}
void concat( Str const str_a, Str const str_b )
{
str_concat( Capacity, Data
, str_a.Len, str_a.Data
, str_b.Len, str_b.Data );
Len = str_a.Len + str_b.Len;
str_concat( Capacity, ptr
, str_a.len, str_a.ptr
, str_b.len, str_b.ptr );
len = str_a.len + str_b.len;
}
operator char*() { return Data;}
operator char const*() { return Data; }
operator Str() { return { Len, Data }; }
operator Str const() const { return { Len, Data }; }
operator char*() { return ptr;}
operator char const*() { return ptr; }
operator Str() { return { len, ptr }; }
operator Str const() const { return { len, ptr }; }
char& operator []( u32 idx ) {
assert( idx < Capacity );
return Data[idx];
return ptr[idx];
}
char const& operator []( u32 idx ) const {
assert( idx < Capacity );
return Data[idx];
return ptr[idx];
}
};

File diff suppressed because it is too large Load Diff