Files
handmadehero/handmade.c
T
2026-04-14 18:33:43 -07:00

414 lines
14 KiB
C

#include "core.h"
#include "handmade.h"
#include "sdl_handmade.h"
#include "handmade_intrinsics.h"
#include <string.h> /* memset */
static void GameOutputSound(game_state *GameState,
game_sound_output_buffer *SoundBuffer,
S32 ToneHz)
{
/*
S16 ToneVolume, *SampleOut;
S32 WavePeriod, SampleIndex;
ToneVolume = 0;
WavePeriod = SoundBuffer->SamplesPerSecond / ToneHz;
SampleOut = SoundBuffer->Samples;
for(SampleIndex = 0;
SampleIndex < SoundBuffer->SampleCount;
SampleIndex++)
{
F32 SineValue;
S16 SampleValue;
SineValue = sinf(GameState->tSine);
SampleValue = (S16)(SineValue * ToneVolume);
*SampleOut++ = SampleValue;
*SampleOut++ = SampleValue;
GameState->tSine += 2.0f * (F32)PI * 1.0f / (F32)WavePeriod;
if(GameState->tSine > 2.0f*(F32)PI) {
GameState->tSine -= 2.0f*(F32)PI;
}
}
*/
}
static void ClearBackground(game_offscreen_buffer *Buffer)
{
S32 Width, Height, X;
Width = Buffer->Width;
Height = Buffer->Height;
for(X = 0; X < Width * Height; X++) {
U32 *Pixel = &((U32 *)Buffer->Memory)[X];
*Pixel = 0xFFFFFF;
}
}
static void DrawRectangle(game_offscreen_buffer *Buffer,
F32 RealMinX, F32 RealMinY,
F32 RealMaxX, F32 RealMaxY,
F32 R, F32 G, F32 B)
{
S32 MinX = RoundF32toS32(RealMinX);
S32 MinY = RoundF32toS32(RealMinY);
S32 MaxX = RoundF32toS32(RealMaxX);
S32 MaxY = RoundF32toS32(RealMaxY);
S32 Width = Buffer->Width;
S32 Height = Buffer->Height;
U8 *Row;
S32 Y, X;
U32 Color;
if(MinX < 0)
MinX = 0;
if(MinY < 0)
MinY = 0;
if(MaxX > Width)
MaxX = Width;
if(MaxY > Height)
MaxY = Height;
Color = (U32)((RoundF32toS32(R * 255.0f) << 16) |
(RoundF32toS32(G * 255.0f) << 8) |
(RoundF32toS32(B * 255.0f) << 0));
Row = ((U8 *)Buffer->Memory + MinX*Buffer->BytesPerPixel +
MinY*Buffer->Pitch);
for(Y = MinY; Y < MaxY; Y++) {
U32 *Pixel = (U32 *)Row;
for(X = MinX; X < MaxX; X++) {
*(U32 *)Pixel = Color;
Pixel++;
}
Row += Buffer->Pitch;
}
}
static tile_chunk *GetTileChunk(world *World,
S32 TileChunkX, S32 TileChunkY)
{
tile_chunk *TileChunk = NULL;
if((TileChunkX >= 0) && (TileChunkX < World->TileChunkCountX) &&
(TileChunkY >= 0) && (TileChunkY < World->TileChunkCountY))
{
TileChunk = &World->TileChunks[TileChunkY *
World->TileChunkCountX +
TileChunkX];
}
return TileChunk;
}
static U32 GetTileValueUnchecked(world *World, tile_chunk *TileChunk,
U32 TileX, U32 TileY)
{
U32 TileChunkValue;
ASSERT(TileChunk);
ASSERT(TileX < World->ChunkDim);
ASSERT(TileY < World->ChunkDim);
TileChunkValue = TileChunk->Tiles[TileY * World->ChunkDim + TileX];
return TileChunkValue;
}
static U32 GetTileChunkValue(world *World, tile_chunk *TileChunk,
U32 TestTileX, U32 TestTileY)
{
U32 TileChunkValue = 0;
if(TileChunk) {
TileChunkValue = GetTileValueUnchecked(World,
TileChunk,
TestTileX, TestTileY);
}
return TileChunkValue;
}
static void CannonicalizeCoord(world *World, U32 *Tile, F32 *TileRel)
{
/* NOTE: The world is assumed to be toroidal topology. If you step off
one end, you come back on the other. */
S32 Offset = FloorF32toS32(*TileRel / World->TileSideInMeters);
*Tile += Offset;
*TileRel -= (F32)Offset * World->TileSideInMeters;
ASSERT(*TileRel >= 0);
ASSERT(*TileRel < World->TileSideInMeters);
}
static cannonical_position RecannonicalizePosition(world *World,
cannonical_position Pos)
{
cannonical_position Result = Pos;
CannonicalizeCoord(World, &Result.AbsTileX, &Result.TileRelX);
CannonicalizeCoord(World, &Result.AbsTileY, &Result.TileRelY);
return Result;
}
static tile_chunk_position GetChunkPositionFor(world *World,
U32 AbsTileX, U32 AbsTileY)
{
tile_chunk_position Result;
Result.TileChunkX = AbsTileX >> World->ChunkShift;
Result.TileChunkY = AbsTileY >> World->ChunkShift;
Result.RelTileX = AbsTileX & World->ChunkMask;
Result.RelTileY = AbsTileY & World->ChunkMask;
return Result;
}
static U32 GetTileValue(world *World, U32 AbsTileX, U32 AbsTileY)
{
tile_chunk_position ChunkPos = GetChunkPositionFor(World,
AbsTileX,
AbsTileY);
tile_chunk *TileMap = GetTileChunk(World,
ChunkPos.TileChunkX,
ChunkPos.TileChunkY);
U32 TileChunkValue = GetTileChunkValue(World, TileMap,
ChunkPos.RelTileX,
ChunkPos.RelTileY);
return TileChunkValue;
}
static B32 IsWorldPointEmpty(world *World, cannonical_position CanPos)
{
B32 Empty;
U32 TileChunkValue = GetTileValue(World,
CanPos.AbsTileX,
CanPos.AbsTileY);
Empty = (TileChunkValue == 0);
return Empty;
}
void UpdateAndRender(thread_context *Thread, game_memory *Memory,
game_input *Input, game_offscreen_buffer *Buffer,
game_sound_output_buffer *SoundBuffer)
{
#define TILEMAP_COUNT_X 256
#define TILEMAP_COUNT_Y 256
game_state *GameState;
U32 ControllerIndex;
U32 TempTiles[TILEMAP_COUNT_Y][TILEMAP_COUNT_X] = {
{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
{1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1},
{1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1},
{1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1},
{1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1},
{1, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1},
{1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1},
{1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1},
{1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1},
{1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1},
{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1},
{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1},
{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1},
{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1},
{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1},
{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1},
{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1},
{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
};
F32 PlayerR, PlayerG, PlayerB,
PlayerWidth, PlayerHeight,
PlayerLeft, PlayerTop;
F32 CenterX, CenterY;
tile_chunk TileChunk;
world World;
ASSERT((&Input->Controllers[0].u.buttons.Terminator -
&Input->Controllers[0].u.Buttons[0]) ==
ARRAY_SIZE(Input->Controllers[0].u.Buttons));
ASSERT(sizeof(game_state) <= Memory->PermanentStorageSize);
memset(&World, 0, sizeof(World));
/* NOTE: This is set to using 256x256 tile chunks. */
World.ChunkShift = 8;
World.ChunkMask = 0xFF;
World.ChunkDim = 256;
World.TileChunkCountX = 1;
World.TileChunkCountY = 1;
TileChunk.Tiles = (U32 *)TempTiles;
World.TileChunks = &TileChunk;
World.TileSideInMeters = 1.4f;
World.TileSideInPixels = 60;
World.MetersToPixels =
(F32)World.TileSideInPixels / World.TileSideInMeters;
PlayerHeight = 1.4f;
PlayerWidth = 0.75f * PlayerHeight;
CenterX = 0.5f * (F32)Buffer->Width;
CenterY = 0.5f * (F32)Buffer->Height;
GameState = (game_state *)Memory->PermanentStorage;
if(!Memory->IsInitialized) {
GameState->PlayerP.AbsTileX = 3;
GameState->PlayerP.AbsTileY = 3;
GameState->PlayerP.TileRelX = 5.0f;
GameState->PlayerP.TileRelY = 5.0f;
Memory->IsInitialized = TRUE;
}
for(ControllerIndex = 0;
ControllerIndex < ARRAY_SIZE(Input->Controllers);
ControllerIndex++)
{
game_controller_input *Controller =
GetController(Input, ControllerIndex);
if(Controller->IsAnalog) {
} else {
F32 dPlayerX = 0.0f;
F32 dPlayerY = 0.0f;
cannonical_position NewPlayerP, PlayerLeft, PlayerRight;
if(Controller->u.buttons.MoveUp.EndedDown) {
dPlayerY = 1.0f;
}
if(Controller->u.buttons.MoveDown.EndedDown) {
dPlayerY = -1.0f;
}
if(Controller->u.buttons.MoveLeft.EndedDown) {
dPlayerX = -1.0f;
}
if(Controller->u.buttons.MoveRight.EndedDown) {
dPlayerX = 1.0f;
}
dPlayerX *= 5.0f;
dPlayerY *= 5.0f;
NewPlayerP = GameState->PlayerP;
NewPlayerP.TileRelX += Input->dtForFrame*dPlayerX;
NewPlayerP.TileRelY += Input->dtForFrame*dPlayerY;
NewPlayerP = RecannonicalizePosition(&World, NewPlayerP);
PlayerLeft = NewPlayerP;
PlayerLeft.TileRelX -= 0.5f*PlayerWidth;
PlayerLeft = RecannonicalizePosition(&World, PlayerLeft);
PlayerRight = NewPlayerP;
PlayerRight.TileRelX += 0.5f*PlayerWidth;
PlayerRight = RecannonicalizePosition(&World, PlayerRight);
if(IsWorldPointEmpty(&World, NewPlayerP) &&
IsWorldPointEmpty(&World, PlayerLeft) &&
IsWorldPointEmpty(&World, PlayerRight))
{
GameState->PlayerP = NewPlayerP;
}
}
}
DrawRectangle(Buffer, 0.0f, 0.0f,
(F32)Buffer->Width, (F32)Buffer->Height,
0.0f, 1.0f, 0.0f);
{
S32 RelRow, RelColumn;
for(RelRow = -10; RelRow < 10; RelRow++) {
for(RelColumn = -20; RelColumn < 20; RelColumn++) {
U32 Column = GameState->PlayerP.AbsTileX + RelColumn;
U32 Row = GameState->PlayerP.AbsTileY + RelRow;
U32 TileID = GetTileValue(&World, Column, Row);
F32 Gray = 0.5f;
if(TileID) {
Gray = 1.0f;
}
if((Column == GameState->PlayerP.AbsTileX) &&
(Row == GameState->PlayerP.AbsTileY))
{
Gray = 0.0f;
}
{
F32 MinX, MinY, MaxX, MaxY;
MinX = CenterX +
(F32)RelColumn * (F32)World.TileSideInPixels;
MinY = CenterY -
(F32)RelRow * (F32)World.TileSideInPixels;
MaxX = MinX + (F32)World.TileSideInPixels;
MaxY = MinY - (F32)World.TileSideInPixels;
DrawRectangle(Buffer,
MinX, MaxY, MaxX, MinY,
Gray, Gray, Gray);
}
}
}
}
PlayerR = 1.0f;
PlayerG = 0.0f;
PlayerB = 0.0f;
PlayerLeft = CenterX + World.MetersToPixels*GameState->PlayerP.TileRelX -
0.5f*World.MetersToPixels*PlayerWidth;
PlayerTop = CenterY - World.MetersToPixels*GameState->PlayerP.TileRelY -
World.MetersToPixels*PlayerHeight;
DrawRectangle(Buffer,
PlayerLeft, PlayerTop,
PlayerLeft + World.MetersToPixels*PlayerWidth,
PlayerTop + World.MetersToPixels*PlayerHeight,
PlayerR, PlayerG, PlayerB);
GameOutputSound(GameState, SoundBuffer, 400);
}
/*
static void RenderWeirdGradient(game_offscreen_buffer *Buffer, S32 XOffset,
S32 YOffset)
{
S32 Width, Height, Y;
U8 *Row;
Width = Buffer->Width;
Height = Buffer->Height;
Row = (U8 *)Buffer->Memory;
for(Y = 0; Y < Height; Y++) {
U32 *Pixel = (U32 *)Row;
S32 X;
Pixel = (U32 *)Row;
for(X = 0; X < Width; X++) {
U8 Blue, Green;
Blue = (U8)(X + XOffset);
Green = (U8)(Y + YOffset);
*Pixel = (Green << 8) | Blue;
Pixel++;
}
Row += Buffer->Pitch;
}
}
*/