diff --git a/src/handmade.cpp b/src/handmade.cpp index ac4e70b..f8ab87d 100644 --- a/src/handmade.cpp +++ b/src/handmade.cpp @@ -2,22 +2,41 @@ #define PI32 3.141592653589f +internal void renderPlayer(GameOffscreenBuffer *buffer, int playerX, int playerY) { + uint8 *endOfBuffer = (uint8 *)buffer->memory + buffer->pitch*buffer->height; + uint32 color = 0xFFFFFFFF; + int top = playerY; + int bottom = playerY + 10; + for (int x = playerX; x < playerX + 10; x++) { + uint8 *pixel = ((uint8 *)buffer->memory + x * buffer->bytesPerPixel + top*buffer->pitch); + for (int y = top; y < bottom; y++) { + if ((pixel >= buffer->memory) && (pixel + 4 < endOfBuffer)) { + *(uint32 *)pixel = color; + } + pixel += buffer->pitch; + } + } + +} + internal void outputSineSound(GameSoundOutputBuffer *soundBuffer, GameState *state) { int16 toneVolume = 3000; int wavePeriod = soundBuffer->samplesPerSecond/state->toneHz; int16 *sampleOut = soundBuffer->samples; for (int sampleIndex = 0; sampleIndex < soundBuffer->sampleCount; sampleIndex++) { + state->tSine += 2.0f * PI32 / (real32)wavePeriod; +#if 0 int16 sampleValue = (int16)(sin(state->tSine) * (real32)toneVolume); +#else + int16 sampleValue = 0; +#endif *sampleOut++ = sampleValue; *sampleOut++ = sampleValue; - state->tSine += 2.0f * PI32 / (real32)wavePeriod; } } internal void renderWeirdGradient(GameOffscreenBuffer *buffer, int xOffset, int yOffset) { - int bytesPerPixel = 4; - int pitch = buffer->width*bytesPerPixel; uint8 *row = (uint8 *)buffer->memory; for (int y = 0; y < buffer->height; y++) { uint32 *pixel = (uint32*)row; @@ -26,7 +45,7 @@ internal void renderWeirdGradient(GameOffscreenBuffer *buffer, int xOffset, int uint8 green = (uint8)(y + yOffset); *pixel++ = (green << 8) | blue; } - row += pitch; + row += buffer->pitch; } } @@ -46,11 +65,16 @@ extern "C" GAME_UPDATE_AND_RENDER(gameUpdateAndRender) { state->toneHz = 440; state->tSine = 0; + state->playerY = 100; + state->playerX = 100; + state->blueOffset = 0; + state->greenOffset = 0; memory->isInitialised = true; } for (int controllerIndex = 0; controllerIndex < ArrayCount(input->controllers); controllerIndex++) { GameControllerInput *controllerInput = &input->controllers[controllerIndex]; + /* if (controllerInput->isAnalog) { state->toneHz = 440 + (int)(128.0f*controllerInput->stickAvgX); state->greenOffset -= (int)(20.0f*controllerInput->stickAvgX); @@ -69,9 +93,18 @@ extern "C" GAME_UPDATE_AND_RENDER(gameUpdateAndRender) { state->blueOffset -= 10; } } + */ + + if (controllerInput->btnDown.endedDown) { + state->playerY -= 30; + } + + state->playerX += (int)(4.0f*controllerInput->stickAvgX); + state->playerY -= (int)(4.0f*controllerInput->stickAvgY); } renderWeirdGradient(videoBuf, state->greenOffset, state->blueOffset); + renderPlayer(videoBuf, state->playerX, state->playerY); } extern "C" GAME_GET_SOUND_SAMPLES(gameGetSoundSamples) { diff --git a/src/handmade.h b/src/handmade.h index ba3d45a..5004118 100644 --- a/src/handmade.h +++ b/src/handmade.h @@ -97,6 +97,8 @@ struct GameOffscreenBuffer { void *memory; int width; int height; + int bytesPerPixel; + int pitch; // Bytes per row }; struct GameButtonState { @@ -151,6 +153,8 @@ struct GameState { int greenOffset; int blueOffset; real32 tSine; + int playerY; + int playerX; }; // === Game to platform services === diff --git a/src/win32_handmade.cpp b/src/win32_handmade.cpp index a06fec6..a91aeee 100644 --- a/src/win32_handmade.cpp +++ b/src/win32_handmade.cpp @@ -104,6 +104,7 @@ internal void resizeDIBSection(Win32OffscreenBuffer *buffer, int width, int heig buffer->width = width; buffer->height = height; buffer->bytesPerPixel = 4; + buffer->pitch = buffer->width*buffer->bytesPerPixel; buffer->info.bmiHeader.biSize = sizeof(buffer->info.bmiHeader); buffer->info.bmiHeader.biWidth = buffer->width; @@ -359,7 +360,56 @@ internal void win32FillSoundBuffer(Win32SoundOutput *soundOutput, DWORD byteToLo } } -internal void win32ProcessPendingMessages(GameControllerInput *keyboardController) { +internal void win32BeginRecordingInput(Win32State *win32State, int inputRecordingIndex) { + win32State->inputRecordingIndex = inputRecordingIndex; + char *filename = "recording.hmi"; + win32State->recordingHandle = CreateFileA(filename, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, NULL, NULL); + DWORD bytesToWrite = (DWORD)win32State->gameMemoryTotalSize; + Assert(bytesToWrite < 0xFFFFFFFF); + DWORD bytesWritten; + WriteFile(win32State->recordingHandle, win32State->gameMemoryBlock, bytesToWrite, &bytesWritten, NULL); +} + +internal void win32EndRecordingInput(Win32State *win32State) { + CloseHandle(win32State->recordingHandle); + win32State->inputRecordingIndex = 0; +} + +internal void win32RecordInput(Win32State *win32State, GameInput *newInput) { + DWORD bytesWritten; + WriteFile(win32State->recordingHandle, newInput, sizeof(*newInput), &bytesWritten, NULL); +} + +internal void win32BeginInputPlayback(Win32State *win32State, int inputPlayingIndex) { + win32State->inputPlayingIndex = inputPlayingIndex; + char *filename = "recording.hmi"; + win32State->playbackHandle = CreateFileA(filename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, NULL, NULL); + DWORD bytesToRead = (DWORD)win32State->gameMemoryTotalSize; + Assert(bytesToRead < 0xFFFFFFFF); + DWORD bytesRead; + ReadFile(win32State->playbackHandle, win32State->gameMemoryBlock, bytesToRead, &bytesRead, NULL); +} + +internal void win32EndInputPlayback(Win32State *win32State) { + CloseHandle(win32State->playbackHandle); + win32State->inputPlayingIndex = 0; +} + +internal void win32PlaybackInput(Win32State *win32State, GameInput *newInput) { + DWORD bytesRead; + if (ReadFile(win32State->playbackHandle, newInput, sizeof(*newInput), &bytesRead, NULL)) { + if (bytesRead == 0) { + // Restart + int playingIndex = win32State->inputPlayingIndex; + win32EndInputPlayback(win32State); + win32BeginInputPlayback(win32State, playingIndex); + } else { + // there's still input, read next input + } + } +} + +internal void win32ProcessPendingMessages(Win32State *win32State, GameControllerInput *keyboardController) { MSG message; while (PeekMessageA(&message, NULL, NULL, NULL, PM_REMOVE)) { if (message.message == WM_QUIT) { @@ -397,6 +447,15 @@ internal void win32ProcessPendingMessages(GameControllerInput *keyboardControlle } else if (VKCode == VK_RIGHT) { win32ProcessKeyboardKeypress(&keyboardController->btnRight, isDown); } else if (VKCode == VK_SPACE) { + } else if (VKCode == 'L') { + if (isDown) { + if (win32State->inputRecordingIndex == 0) { + win32BeginRecordingInput(win32State, 1); + } else { + win32EndRecordingInput(win32State); + win32BeginInputPlayback(win32State, 1); + } + } } } @@ -430,12 +489,11 @@ inline real32 win32GetSecondsElapsed(LARGE_INTEGER start, LARGE_INTEGER end) { #define monitorRefreshHz 60 #define gameUpdateHz (monitorRefreshHz / 2) -internal void win32DebugDrawVertical(Win32OffscreenBuffer *screenBuffer, int x, int top, int bottom, int32 color) { - int pitch = screenBuffer->width*screenBuffer->bytesPerPixel; - uint8 *pixel = (uint8 *)screenBuffer->memory + top * pitch + x*screenBuffer->bytesPerPixel; +internal void win32DebugDrawVertical(Win32OffscreenBuffer *buffer, int x, int top, int bottom, int32 color) { + uint8 *pixel = (uint8 *)buffer->memory + top * buffer->pitch + x*buffer->bytesPerPixel; for (int y = top; y < bottom; y++) { *(uint32 *)pixel = color; - pixel += pitch; + pixel += buffer->pitch; } } @@ -445,26 +503,27 @@ inline void win32DrawSoundBufferMarker(Win32OffscreenBuffer *buffer, Win32SoundO win32DebugDrawVertical(buffer, x, top, bottom, color); } -internal void win32DebugSyncDisplay(Win32OffscreenBuffer *screenBuffer, int markerCount, Win32DebugTimeMarker *markers, Win32SoundOutput *soundOutput, real32 targetSecondsPerFrame) { +internal void win32DebugSyncDisplay(Win32OffscreenBuffer *buffer, int markerCount, Win32DebugTimeMarker *markers, Win32SoundOutput *soundOutput, real32 targetSecondsPerFrame) { int padX = 16; int padY = 16; int top = padY; - int bottom = screenBuffer->height - padY; - int renderWidth = screenBuffer->width - 2 *padX; + int bottom = buffer->height - padY; + int renderWidth = buffer->width - 2 *padX; real32 pxPerSoundBufferEntry = (real32)renderWidth / (real32)soundOutput->secondaryBufferSize; - int pitch = screenBuffer->width*screenBuffer->bytesPerPixel; +#if 0 for (int markerIndex = 0; markerIndex < markerCount; markerIndex++) { Win32DebugTimeMarker *thisMarker = &markers[markerIndex]; real32 alpha = ((real32)(markerIndex + 1) / (real32)markerCount); int x = padX + (int)(pxPerSoundBufferEntry * (real32)thisMarker->writeCursor); - uint8 *pixel = (uint8 *)screenBuffer->memory + top * pitch + x*screenBuffer->bytesPerPixel; + uint8 *pixel = (uint8 *)buffer->memory + top * buffer->pitch + x*buffer->bytesPerPixel; uint32 newPixel = (uint32)((real32)alpha * 0xFFFFFFFF + (1.0f - alpha) * (*(uint32 *)pixel)); - win32DrawSoundBufferMarker(screenBuffer, soundOutput, pxPerSoundBufferEntry, padX, top, bottom, thisMarker->playCursor, newPixel); - win32DrawSoundBufferMarker(screenBuffer, soundOutput, pxPerSoundBufferEntry, padX, top, bottom, thisMarker->writeCursor, 0x00FF0000); + win32DrawSoundBufferMarker(buffer, soundOutput, pxPerSoundBufferEntry, padX, top, bottom, thisMarker->playCursor, newPixel); + win32DrawSoundBufferMarker(buffer, soundOutput, pxPerSoundBufferEntry, padX, top, bottom, thisMarker->writeCursor, 0x00FF0000); } +#endif } void catStrings(size_t sourceACount, char *sourceA, size_t sourceBCount, char *sourceB, size_t destCount, char *dest) { @@ -536,6 +595,8 @@ int APIENTRY WinMain(HINSTANCE instance, HINSTANCE prevInstance, PSTR commandLin RegisterHotKey(window, HH_CTRLW, MOD_CONTROL, 'W'); globalRunning = true; + Win32State win32State = {}; + GameInput input[2] = {}; GameInput *oldInput = &input[0]; GameInput *newInput = &input[1]; @@ -568,8 +629,9 @@ int APIENTRY WinMain(HINSTANCE instance, HINSTANCE prevInstance, PSTR commandLin gameMemory.debugReadEntireFile = debugReadEntireFile; gameMemory.debug_printf = debug_printf; - uint64 totalSize = gameMemory.permanentStorageSize + gameMemory.transientStorageSize; - gameMemory.permanentStorage = VirtualAlloc(baseAddress, totalSize, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); + win32State.gameMemoryTotalSize = gameMemory.permanentStorageSize + gameMemory.transientStorageSize; + win32State.gameMemoryBlock = VirtualAlloc(baseAddress, win32State.gameMemoryTotalSize, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); + gameMemory.permanentStorage = win32State.gameMemoryBlock; gameMemory.transientStorage = ((uint8 *)gameMemory.permanentStorage + gameMemory.permanentStorageSize); win32InitSound(window, soundOutput.samplesPerSecond, soundOutput.secondaryBufferSize); @@ -602,7 +664,7 @@ int APIENTRY WinMain(HINSTANCE instance, HINSTANCE prevInstance, PSTR commandLin newKeyboardController->buttons[buttonIndex].endedDown = oldKeyboardController->buttons[buttonIndex].endedDown; } - win32ProcessPendingMessages(newKeyboardController); + win32ProcessPendingMessages(&win32State, newKeyboardController); XINPUT_STATE controllerState; int maxControllerCount = XUSER_MAX_COUNT; @@ -670,6 +732,16 @@ int APIENTRY WinMain(HINSTANCE instance, HINSTANCE prevInstance, PSTR commandLin videoBuffer.memory = globalBackBuffer.memory; videoBuffer.width = globalBackBuffer.width; videoBuffer.height = globalBackBuffer.height; + videoBuffer.pitch = globalBackBuffer.pitch; + videoBuffer.bytesPerPixel = globalBackBuffer.bytesPerPixel; + + if (win32State.inputRecordingIndex) { + win32RecordInput(&win32State, newInput); + } + if (win32State.inputPlayingIndex) { + win32PlaybackInput(&win32State, newInput); + } + game.updateAndRender(&gameMemory, &videoBuffer, newInput); DWORD playCursor = 0; diff --git a/src/win32_handmade.h b/src/win32_handmade.h index 977f7b5..5a9e16f 100644 --- a/src/win32_handmade.h +++ b/src/win32_handmade.h @@ -6,6 +6,7 @@ struct Win32OffscreenBuffer { int width; int height; int bytesPerPixel; + int pitch; }; struct Win32WindowDimensions { @@ -27,3 +28,17 @@ struct Win32DebugTimeMarker { DWORD playCursor; DWORD writeCursor; }; + +struct Win32RecordedInput { + int inputCount; + GameInput *inputStream; +}; + +struct Win32State { + HANDLE recordingHandle; + uint32 inputRecordingIndex; + HANDLE playbackHandle; + uint32 inputPlayingIndex; + uint64 gameMemoryTotalSize; + void *gameMemoryBlock; +};