|
|
@@ -1,10 +1,31 @@ |
|
|
|
#include <cstdarg> |
|
|
|
#include <cstdio> |
|
|
|
#include <cstdlib> |
|
|
|
#include <windows.h> |
|
|
|
#include <stdint.h> |
|
|
|
#include <Xinput.h> |
|
|
|
#include <dsound.h> |
|
|
|
#include <math.h> |
|
|
|
|
|
|
|
#define internal static // for functions |
|
|
|
#define local_persist static // for static variables in a scope |
|
|
|
#define global static // for global variables |
|
|
|
|
|
|
|
typedef uint8_t uint8; |
|
|
|
typedef uint16_t uint16; |
|
|
|
typedef uint32_t uint32; |
|
|
|
typedef uint64_t uint64; |
|
|
|
|
|
|
|
typedef int8_t int8; |
|
|
|
typedef int16_t int16; |
|
|
|
typedef int32_t int32; |
|
|
|
typedef int64_t int64; |
|
|
|
|
|
|
|
typedef float real32; |
|
|
|
typedef double real64; |
|
|
|
|
|
|
|
typedef int32_t bool32; |
|
|
|
|
|
|
|
struct Win32OffscreenBuffer { |
|
|
|
BITMAPINFO info; |
|
|
|
void *memory; |
|
|
@@ -18,25 +39,46 @@ struct Win32WindowDimensions { |
|
|
|
int height; |
|
|
|
}; |
|
|
|
|
|
|
|
#define PI32 3.141592653589f |
|
|
|
|
|
|
|
global ATOM HH_CTRLW; |
|
|
|
global bool running; |
|
|
|
global Win32OffscreenBuffer globalBackBuffer; |
|
|
|
global LPDIRECTSOUNDBUFFER globalSecondaryBuffer; |
|
|
|
|
|
|
|
typedef uint8_t uint8; |
|
|
|
typedef uint16_t uint16; |
|
|
|
typedef uint32_t uint32; |
|
|
|
typedef uint64_t uint64; |
|
|
|
typedef int8_t int8; |
|
|
|
typedef int16_t int16; |
|
|
|
typedef int32_t int32; |
|
|
|
typedef int64_t int64; |
|
|
|
void debug_printf(char* format, ...) { |
|
|
|
int bufsize = strlen(format)*10; |
|
|
|
char *output = (char*)malloc(bufsize); |
|
|
|
va_list list; |
|
|
|
va_start(list, format); |
|
|
|
vsprintf_s(output, bufsize, format, list); |
|
|
|
OutputDebugStringA(output); |
|
|
|
free(output); |
|
|
|
} |
|
|
|
|
|
|
|
// XInputGetState |
|
|
|
#define X_INPUT_GET_STATE(name) DWORD WINAPI name(DWORD dwUserIndex, XINPUT_STATE *pState) |
|
|
|
typedef X_INPUT_GET_STATE(XInputGetStateFn); |
|
|
|
X_INPUT_GET_STATE(XInputGetStateStub) { return ERROR_DEVICE_NOT_CONNECTED; } |
|
|
|
global XInputGetStateFn *XInputGetStateDyn = XInputGetStateStub; |
|
|
|
#define XInputGetState XInputGetStateDyn |
|
|
|
|
|
|
|
// XInputSetState |
|
|
|
#define X_INPUT_SET_STATE(name) DWORD WINAPI name(DWORD dwUserIndex, XINPUT_VIBRATION *pVibration) |
|
|
|
typedef X_INPUT_SET_STATE(XInputSetStateFn); |
|
|
|
X_INPUT_SET_STATE(XInputSetStateStub) { return ERROR_DEVICE_NOT_CONNECTED; } |
|
|
|
global XInputSetStateFn *XInputSetStateDyn = XInputSetStateStub; |
|
|
|
#define XInputSetState XInputSetStateDyn |
|
|
|
|
|
|
|
typedef HRESULT WINAPI DirectSoundCreateFn(LPCGUID pcGuidDevice, LPDIRECTSOUND *ppDS, LPUNKNOWN pUnkOuter); |
|
|
|
|
|
|
|
internal void renderWeirdGradient(Win32OffscreenBuffer buffer, int xOffset, int yOffset) { |
|
|
|
internal void renderWeirdGradient(Win32OffscreenBuffer *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++) { |
|
|
|
int pitch = buffer->width*bytesPerPixel; |
|
|
|
uint8 *row = (uint8 *)buffer->memory; |
|
|
|
for (int y = 0; y < buffer->height; y++) { |
|
|
|
uint32 *pixel = (uint32*)row; |
|
|
|
for (int x = 0; x < buffer.width; x++) { |
|
|
|
for (int x = 0; x < buffer->width; x++) { |
|
|
|
uint8 blue = x + xOffset; |
|
|
|
uint8 green = y + yOffset; |
|
|
|
*pixel++ = (green << 8) | blue; |
|
|
@@ -54,22 +96,18 @@ internal void resizeDIBSection(Win32OffscreenBuffer *buffer, int width, int heig |
|
|
|
buffer->height = height; |
|
|
|
buffer->bytesPerPixel = 4; |
|
|
|
|
|
|
|
buffer->info = { |
|
|
|
.bmiHeader = { |
|
|
|
.biSize = sizeof(buffer->info.bmiHeader), |
|
|
|
.biWidth = buffer->width, |
|
|
|
.biHeight = -buffer->height, |
|
|
|
.biPlanes = 1, |
|
|
|
.biBitCount = 32, |
|
|
|
.biCompression = BI_RGB, |
|
|
|
}, |
|
|
|
}; |
|
|
|
buffer->info.bmiHeader.biSize = sizeof(buffer->info.bmiHeader); |
|
|
|
buffer->info.bmiHeader.biWidth = buffer->width; |
|
|
|
buffer->info.bmiHeader.biHeight = -buffer->height; |
|
|
|
buffer->info.bmiHeader.biPlanes = 1; |
|
|
|
buffer->info.bmiHeader.biBitCount = 32; |
|
|
|
buffer->info.bmiHeader.biCompression = BI_RGB; |
|
|
|
|
|
|
|
int bitmapSize = buffer->width*buffer->height*buffer->bytesPerPixel; |
|
|
|
buffer->memory = VirtualAlloc(NULL, bitmapSize, MEM_COMMIT, PAGE_READWRITE); |
|
|
|
} |
|
|
|
|
|
|
|
Win32WindowDimensions win32GetWindowDimensions(HWND window) { |
|
|
|
internal Win32WindowDimensions win32GetWindowDimensions(HWND window) { |
|
|
|
Win32WindowDimensions result; |
|
|
|
RECT clientRect; |
|
|
|
GetClientRect(window, &clientRect); |
|
|
@@ -78,19 +116,78 @@ Win32WindowDimensions win32GetWindowDimensions(HWND window) { |
|
|
|
return result; |
|
|
|
} |
|
|
|
|
|
|
|
void win32DrawBufferInWindow(Win32OffscreenBuffer buffer, HWND window) { |
|
|
|
internal void win32DrawBufferInWindow(Win32OffscreenBuffer *buffer, HWND window) { |
|
|
|
Win32WindowDimensions winDims = win32GetWindowDimensions(window); |
|
|
|
StretchDIBits( |
|
|
|
GetDC(window), |
|
|
|
0, 0, winDims.width, winDims.height, |
|
|
|
0, 0, buffer.width, buffer.height, |
|
|
|
buffer.memory, |
|
|
|
&buffer.info, |
|
|
|
0, 0, buffer->width, buffer->height, |
|
|
|
buffer->memory, |
|
|
|
&buffer->info, |
|
|
|
DIB_RGB_COLORS, |
|
|
|
SRCCOPY |
|
|
|
); |
|
|
|
} |
|
|
|
|
|
|
|
internal void win32LoadXInput() { |
|
|
|
HMODULE xInputLib = LoadLibrary("xinput1_4.dll"); |
|
|
|
if (!xInputLib) { |
|
|
|
xInputLib = LoadLibrary("xinput1_3.dll"); |
|
|
|
} |
|
|
|
|
|
|
|
if (xInputLib) { |
|
|
|
XInputGetState = (XInputGetStateFn *)GetProcAddress(xInputLib, "XInputGetState"); |
|
|
|
XInputSetState = (XInputSetStateFn *)GetProcAddress(xInputLib, "XInputSetState"); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
internal void win32InitSound(HWND window, int32 samplesPerSec, int bufferSize) { |
|
|
|
HMODULE dSoundLibrary = LoadLibraryA("dsound.dll"); |
|
|
|
if (dSoundLibrary) { |
|
|
|
DirectSoundCreateFn *DirectSoundCreate = (DirectSoundCreateFn*)GetProcAddress(dSoundLibrary, "DirectSoundCreate"); |
|
|
|
LPDIRECTSOUND DirectSound; |
|
|
|
if (DirectSoundCreate && SUCCEEDED(DirectSoundCreate(0, &DirectSound, 0))) { |
|
|
|
WAVEFORMATEX waveFormat = {}; |
|
|
|
waveFormat.wFormatTag = WAVE_FORMAT_PCM; |
|
|
|
waveFormat.nChannels = 2; |
|
|
|
waveFormat.nSamplesPerSec = samplesPerSec; |
|
|
|
waveFormat.wBitsPerSample = 16; |
|
|
|
waveFormat.nBlockAlign = (waveFormat.nChannels*waveFormat.wBitsPerSample) / 8; |
|
|
|
waveFormat.nAvgBytesPerSec = waveFormat.nSamplesPerSec*waveFormat.nBlockAlign; |
|
|
|
waveFormat.cbSize = 0; |
|
|
|
|
|
|
|
if (SUCCEEDED(DirectSound->SetCooperativeLevel(window, DSSCL_PRIORITY))) { |
|
|
|
DSBUFFERDESC bufferDescription = {}; |
|
|
|
bufferDescription.dwSize = sizeof(bufferDescription); |
|
|
|
bufferDescription.dwFlags = DSBCAPS_PRIMARYBUFFER; |
|
|
|
|
|
|
|
LPDIRECTSOUNDBUFFER primaryBuffer; |
|
|
|
if (SUCCEEDED(DirectSound->CreateSoundBuffer(&bufferDescription, &primaryBuffer, 0))) { |
|
|
|
if (SUCCEEDED(primaryBuffer->SetFormat(&waveFormat))) { |
|
|
|
OutputDebugStringA("Primary buffer format was set.\n"); |
|
|
|
} else { |
|
|
|
//No sound! (?) |
|
|
|
} |
|
|
|
} |
|
|
|
} else { |
|
|
|
// No sound! (?) |
|
|
|
} |
|
|
|
|
|
|
|
DSBUFFERDESC bufferDescription = {}; |
|
|
|
bufferDescription.dwSize = sizeof(bufferDescription); |
|
|
|
bufferDescription.dwFlags = 0; |
|
|
|
bufferDescription.dwBufferBytes = bufferSize; |
|
|
|
bufferDescription.lpwfxFormat = &waveFormat; |
|
|
|
|
|
|
|
if (SUCCEEDED(DirectSound->CreateSoundBuffer(&bufferDescription, &globalSecondaryBuffer, 0))) { |
|
|
|
OutputDebugStringA("Secondary buffer created successfully!\n"); |
|
|
|
} |
|
|
|
} else { |
|
|
|
// No sound! (?) |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
LRESULT mainWindowCallback( |
|
|
|
HWND window, |
|
|
|
UINT message, |
|
|
@@ -103,7 +200,7 @@ LRESULT mainWindowCallback( |
|
|
|
} break; |
|
|
|
|
|
|
|
case WM_DESTROY: { |
|
|
|
//OutputDebugStringA("WM_DESTROY\n"); |
|
|
|
OutputDebugStringA("WM_DESTROY\n"); |
|
|
|
} break; |
|
|
|
|
|
|
|
case WM_CLOSE: { |
|
|
@@ -117,34 +214,109 @@ LRESULT mainWindowCallback( |
|
|
|
int y = paint.rcPaint.top; |
|
|
|
int width = paint.rcPaint.right - paint.rcPaint.left; |
|
|
|
int height = paint.rcPaint.bottom - paint.rcPaint.top; |
|
|
|
win32DrawBufferInWindow(globalBackBuffer, window); |
|
|
|
win32DrawBufferInWindow(&globalBackBuffer, window); |
|
|
|
EndPaint(window, &paint); |
|
|
|
} break; |
|
|
|
|
|
|
|
case WM_ACTIVATEAPP: { |
|
|
|
running = false; |
|
|
|
} break; |
|
|
|
|
|
|
|
case WM_HOTKEY: { |
|
|
|
if (wParam == HH_CTRLW) { |
|
|
|
running = false; |
|
|
|
} |
|
|
|
} break; |
|
|
|
|
|
|
|
case WM_SYSKEYDOWN: |
|
|
|
case WM_SYSKEYUP: |
|
|
|
case WM_KEYUP: |
|
|
|
case WM_KEYDOWN: { |
|
|
|
uint32 VKCode = wParam; |
|
|
|
bool wasDown = (lParam & (1 << 30)) != 0; |
|
|
|
bool isDown = (lParam & (1 << 31)) == 0; |
|
|
|
if (wasDown != isDown) { |
|
|
|
if (VKCode == 'W') { |
|
|
|
} else if (VKCode == 'A') { |
|
|
|
} else if (VKCode == 'S') { |
|
|
|
} else if (VKCode == 'D') { |
|
|
|
} else if (VKCode == 'Q') { |
|
|
|
} else if (VKCode == 'E') { |
|
|
|
} else if (VKCode == VK_ESCAPE) { |
|
|
|
} else if (VKCode == VK_UP) { |
|
|
|
} else if (VKCode == VK_LEFT) { |
|
|
|
} else if (VKCode == VK_DOWN) { |
|
|
|
} else if (VKCode == VK_RIGHT) { |
|
|
|
} else if (VKCode == VK_SPACE) { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
bool32 altKeyWasDown = lParam & (1 << 29); |
|
|
|
if (altKeyWasDown && VKCode == VK_F4) { |
|
|
|
running = false; |
|
|
|
} |
|
|
|
} break; |
|
|
|
|
|
|
|
default: { |
|
|
|
result = DefWindowProcA(window, message, wParam, lParam); |
|
|
|
//OutputDebugStringA("default\n"); |
|
|
|
} break; |
|
|
|
} |
|
|
|
return result; |
|
|
|
} |
|
|
|
|
|
|
|
struct Win32SoundOutput { |
|
|
|
int samplesPerSecond; |
|
|
|
int toneHz; |
|
|
|
int wavePeriod; |
|
|
|
int16 toneVolume; |
|
|
|
uint32 runningSampleIndex; |
|
|
|
int bytesPerSample; |
|
|
|
int secondaryBufferSize; |
|
|
|
}; |
|
|
|
|
|
|
|
internal void win32FillSoundBuffer(Win32SoundOutput *soundOutput, DWORD byteToLock, DWORD bytesToWrite) { |
|
|
|
VOID *region1; |
|
|
|
DWORD region1Size; |
|
|
|
VOID *region2; |
|
|
|
DWORD region2Size; |
|
|
|
HRESULT lock = globalSecondaryBuffer->Lock(byteToLock, bytesToWrite, ®ion1, ®ion1Size, ®ion2, ®ion2Size, 0); |
|
|
|
if (SUCCEEDED(lock)) { |
|
|
|
DWORD region1SampleCount = region1Size/soundOutput->bytesPerSample; |
|
|
|
int16 *sampleOut = (int16*)region1; |
|
|
|
for (DWORD sampleIndex = 0; sampleIndex < region1SampleCount; sampleIndex++) { |
|
|
|
real32 t = 2.0f * PI32 * (real32)soundOutput->runningSampleIndex / (real32)soundOutput->wavePeriod; |
|
|
|
int16 sampleValue = (int16)(sin(t) * (real32)soundOutput->toneVolume); |
|
|
|
*sampleOut++ = sampleValue; |
|
|
|
*sampleOut++ = sampleValue; |
|
|
|
soundOutput->runningSampleIndex++; |
|
|
|
} |
|
|
|
DWORD region2SampleCount = region2Size/soundOutput->bytesPerSample; |
|
|
|
sampleOut = (int16*)region2; |
|
|
|
for (DWORD sampleIndex = 0; sampleIndex < region2SampleCount; sampleIndex++) { |
|
|
|
real32 t = 2.0f * PI32 * (real32)soundOutput->runningSampleIndex / (real32)soundOutput->wavePeriod; |
|
|
|
int16 sampleValue = (int16)(sin(t) * soundOutput->toneVolume); |
|
|
|
*sampleOut++ = sampleValue; |
|
|
|
*sampleOut++ = sampleValue; |
|
|
|
soundOutput->runningSampleIndex++; |
|
|
|
} |
|
|
|
globalSecondaryBuffer->Unlock(region1, region1Size, region2, region2Size); |
|
|
|
} else { |
|
|
|
OutputDebugStringA("No lock!\n"); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
int APIENTRY WinMain( |
|
|
|
HINSTANCE instance, |
|
|
|
HINSTANCE prevInstance, |
|
|
|
PSTR commandLine, |
|
|
|
int commandShow |
|
|
|
) { |
|
|
|
WNDCLASS windowClass = { |
|
|
|
.style = CS_VREDRAW | CS_HREDRAW, |
|
|
|
.lpfnWndProc = &mainWindowCallback, |
|
|
|
.hInstance = instance, |
|
|
|
.lpszClassName = "HandmadeHeroWindowClass", |
|
|
|
}; |
|
|
|
win32LoadXInput(); |
|
|
|
|
|
|
|
WNDCLASSA windowClass = {}; |
|
|
|
windowClass.style = CS_VREDRAW | CS_HREDRAW; |
|
|
|
windowClass.lpfnWndProc = &mainWindowCallback; |
|
|
|
windowClass.hInstance = instance; |
|
|
|
windowClass.lpszClassName = "HandmadeHeroWindowClass"; |
|
|
|
|
|
|
|
resizeDIBSection(&globalBackBuffer, 1280, 720); |
|
|
|
|
|
|
@@ -164,22 +336,94 @@ int APIENTRY WinMain( |
|
|
|
NULL |
|
|
|
); |
|
|
|
if (window) { |
|
|
|
HH_CTRLW = GlobalAddAtomA("HH_CTRLW"); |
|
|
|
RegisterHotKey(window, HH_CTRLW, MOD_CONTROL, 'W'); |
|
|
|
|
|
|
|
MSG message; |
|
|
|
running = true; |
|
|
|
|
|
|
|
// graphics test |
|
|
|
int xOffset = 0; |
|
|
|
int yOffset = 0; |
|
|
|
|
|
|
|
// sound test |
|
|
|
Win32SoundOutput soundOutput = {}; |
|
|
|
soundOutput.samplesPerSecond = 48000; |
|
|
|
soundOutput.toneHz = 440; // A above middle C |
|
|
|
soundOutput.wavePeriod = soundOutput.samplesPerSecond / soundOutput.toneHz; |
|
|
|
soundOutput.toneVolume = 6000; |
|
|
|
soundOutput.runningSampleIndex = 0; |
|
|
|
soundOutput.bytesPerSample = sizeof(int16)*2; |
|
|
|
soundOutput.secondaryBufferSize = soundOutput.samplesPerSecond*soundOutput.bytesPerSample; |
|
|
|
|
|
|
|
win32InitSound(window, soundOutput.samplesPerSecond, soundOutput.secondaryBufferSize); |
|
|
|
win32FillSoundBuffer(&soundOutput, 0, soundOutput.secondaryBufferSize); |
|
|
|
globalSecondaryBuffer->Play(0, 0, DSBPLAY_LOOPING); |
|
|
|
|
|
|
|
while (running) { |
|
|
|
while(PeekMessageA(&message, NULL, NULL, NULL, PM_REMOVE)) { |
|
|
|
while (PeekMessageA(&message, NULL, NULL, NULL, PM_REMOVE)) { |
|
|
|
if (message.message == WM_QUIT) { |
|
|
|
running = false; |
|
|
|
} |
|
|
|
TranslateMessage(&message); |
|
|
|
DispatchMessage(&message); |
|
|
|
} |
|
|
|
renderWeirdGradient(globalBackBuffer, xOffset, yOffset); |
|
|
|
win32DrawBufferInWindow(globalBackBuffer, window); |
|
|
|
xOffset++; |
|
|
|
yOffset++; |
|
|
|
|
|
|
|
XINPUT_STATE controllerState; |
|
|
|
for (DWORD controllerIndex = 0; controllerIndex < XUSER_MAX_COUNT; controllerIndex++) { |
|
|
|
if (XInputGetState(controllerIndex, &controllerState) == ERROR_SUCCESS) { |
|
|
|
break; |
|
|
|
} else { |
|
|
|
// controller not available |
|
|
|
} |
|
|
|
} |
|
|
|
XINPUT_GAMEPAD *Pad = &controllerState.Gamepad; |
|
|
|
bool up = (Pad->wButtons & XINPUT_GAMEPAD_DPAD_UP); |
|
|
|
bool down = (Pad->wButtons & XINPUT_GAMEPAD_DPAD_DOWN); |
|
|
|
bool left = (Pad->wButtons & XINPUT_GAMEPAD_DPAD_LEFT); |
|
|
|
bool right = (Pad->wButtons & XINPUT_GAMEPAD_DPAD_RIGHT); |
|
|
|
bool start = (Pad->wButtons & XINPUT_GAMEPAD_START); |
|
|
|
bool back = (Pad->wButtons & XINPUT_GAMEPAD_BACK); |
|
|
|
bool leftShoulder = (Pad->wButtons & XINPUT_GAMEPAD_LEFT_SHOULDER); |
|
|
|
bool rightShoulder = (Pad->wButtons & XINPUT_GAMEPAD_RIGHT_SHOULDER); |
|
|
|
bool aButton = (Pad->wButtons & XINPUT_GAMEPAD_A); |
|
|
|
bool bButton = (Pad->wButtons & XINPUT_GAMEPAD_B); |
|
|
|
bool xButton = (Pad->wButtons & XINPUT_GAMEPAD_X); |
|
|
|
bool yButton = (Pad->wButtons & XINPUT_GAMEPAD_Y); |
|
|
|
int16 stickX = Pad->sThumbLX; |
|
|
|
int16 stickY = Pad->sThumbLY; |
|
|
|
|
|
|
|
float stickYNorm = 0; |
|
|
|
float stickXNorm = 0; |
|
|
|
if (stickY != 0 && abs(stickY) > XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE) { |
|
|
|
stickYNorm = stickY / 32767.0; |
|
|
|
} |
|
|
|
if (stickX != 0 && abs(stickX) > XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE) { |
|
|
|
stickXNorm = stickX / 32767.0; |
|
|
|
} |
|
|
|
|
|
|
|
yOffset += stickY/5000; |
|
|
|
xOffset -= stickX/5000; |
|
|
|
|
|
|
|
renderWeirdGradient(&globalBackBuffer, xOffset, yOffset); |
|
|
|
|
|
|
|
// Sound test |
|
|
|
DWORD playCursor; |
|
|
|
DWORD writeCursor; |
|
|
|
if (SUCCEEDED(globalSecondaryBuffer->GetCurrentPosition(&playCursor, &writeCursor))) { |
|
|
|
DWORD byteToLock = (soundOutput.runningSampleIndex*soundOutput.bytesPerSample) % soundOutput.secondaryBufferSize; |
|
|
|
DWORD bytesToWrite; |
|
|
|
if (byteToLock == playCursor) { |
|
|
|
bytesToWrite = 0; |
|
|
|
} else if (byteToLock > playCursor) { |
|
|
|
bytesToWrite = soundOutput.secondaryBufferSize - byteToLock + playCursor; |
|
|
|
} else { |
|
|
|
bytesToWrite = playCursor - byteToLock; |
|
|
|
} |
|
|
|
win32FillSoundBuffer(&soundOutput, byteToLock, bytesToWrite); |
|
|
|
} |
|
|
|
|
|
|
|
win32DrawBufferInWindow(&globalBackBuffer, window); |
|
|
|
} |
|
|
|
} else { |
|
|
|
// failed |
|
|
|