#include "core.h" #include "handmade.h" #include "sdl_handmade.h" #include "handmade_intrinsics.h" #include /* 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; } } */