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:
2023-09-30 10:05:37 -04:00
parent 465339743a
commit dc43db117c
13 changed files with 881 additions and 589 deletions

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 {};
EngineActions engine_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 );
}
if ( state->InputPlayingIndex )
{
play_input( state, input, platform_api );
// Input recording and playback for game state
if ( memory->input_recording_index )
{
record_input( memory, input, platform_api );
}
if ( memory->input_playback_index )
{
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