diff --git a/data/test_background.bmp b/data/background.bmp similarity index 100% rename from data/test_background.bmp rename to data/background.bmp diff --git a/data/hero_head_front.bmp b/data/hero_head_front.bmp new file mode 100644 index 0000000..e7bd369 Binary files /dev/null and b/data/hero_head_front.bmp differ diff --git a/data/test.bmp b/data/test.bmp new file mode 100644 index 0000000..73f4167 Binary files /dev/null and b/data/test.bmp differ diff --git a/handmade.c b/handmade.c index 7e67557..dd0040d 100644 --- a/handmade.c +++ b/handmade.c @@ -84,6 +84,39 @@ static void DrawRectangle(game_offscreen_buffer *Buffer, F32 RealMinX, F32 RealM } } +static void DrawBitmap(game_offscreen_buffer *Buffer, loaded_bitmap *Bitmap, F32 RelX, F32 RelY) +{ + S32 MinX = RoundF32toS32(RelX); + S32 MinY = RoundF32toS32(RelY); + S32 MaxX = RoundF32toS32(RelX + (F32)Bitmap->Width); + S32 MaxY = RoundF32toS32(RelY + (F32)Bitmap->Height); + + S32 Width = Buffer->Width; + S32 Height = Buffer->Height; + + if(MinX < 0) + MinX = 0; + if(MinY < 0) + MinY = 0; + + if(MaxX > Width) + MaxX = Width; + if(MaxY > Height) + MaxY = Height; + + U32 *SourceRow = Bitmap->Pixels + Bitmap->Width*(Bitmap->Height - 1); + U8 *DestRow = ((U8 *)Buffer->Memory + MinX*Buffer->BytesPerPixel + MinY*Buffer->Pitch); + for(S32 Y = MinY; Y < MaxY; Y++) { + U32 *Dest = (U32 *)DestRow; + U32 *Source = SourceRow; + for(S32 X = MinX; X < MaxX; X++) { + *Dest++ = *Source++; + } + DestRow += Buffer->Pitch; + SourceRow -= Bitmap->Width; + } +} + #pragma pack(push, 1) typedef struct bitmap_header { U16 FileType; @@ -96,19 +129,47 @@ typedef struct bitmap_header { S32 Height; U16 Planes; U16 BitsPerPixel; + U32 Compression; + U32 SizeOfBitmap; + S32 HorzResolution; + S32 VertResolution; + U32 ColorsUsed; + U32 ColorsImportant; + + U32 RedMask; + U32 GreenMask; + U32 BlueMask; } bitmap_header; #pragma pack(pop) -static U32 *LoadBMP(thread_context *Thread, debug_read_file_result (*DEBUGPlatformReadEntireFile)(thread_context *Thread, const char *), char *FileName) +static loaded_bitmap DEBUGLoadBMP(thread_context *Thread, debug_read_file_result (*DEBUGPlatformReadEntireFile)(thread_context *Thread, const char *), char *FileName) { - U32 *Result = 0; + loaded_bitmap Result; + memset(&Result, 0, sizeof(Result)); + + // NOTE: Byte order in memory is AA BB GG RR, bottom up. + // Littel-endian: 0xRRGGBBAA debug_read_file_result ReadResult = DEBUGPlatformReadEntireFile(Thread, FileName); if(ReadResult.ContentsSize > 0) { bitmap_header *Header = (bitmap_header *)ReadResult.Contents; U32 *Pixels = (U32 *)((U8 *)ReadResult.Contents + Header->BitmapOffset); - Result = Pixels; + Result.Pixels = Pixels; + Result.Width = Header->Width; + Result.Height = Header->Height; + + // TODO: Is this too outdated? As of 2026, this loads fine on little-endian. + // Maybe my BMP uses a newer format. + /* + U32 *SourceDest = Pixels; + for(S32 Y = 0; Y < Header->Height; Y++) { + for(S32 X = 0; X < Header->Width; X++) { + *SourceDest = (*SourceDest >> 8) | (*SourceDest << 24); + SourceDest++; + } + } + */ } return Result; @@ -130,7 +191,10 @@ void UpdateAndRender(thread_context *Thread, game_memory *Memory, game_input *In GameState = (game_state *)Memory->PermanentStorage; if(!Memory->IsInitialized) { - GameState->PixelPointer = LoadBMP(Thread, Memory->DEBUGPlatformReadEntireFile, "data/test_background.bmp"); + GameState->Backdrop = DEBUGLoadBMP(Thread, Memory->DEBUGPlatformReadEntireFile, "data/background.bmp"); + GameState->HeroHead = DEBUGLoadBMP(Thread, Memory->DEBUGPlatformReadEntireFile, "data/hero_head_front.bmp");; + GameState->HeroCape = DEBUGLoadBMP(Thread, Memory->DEBUGPlatformReadEntireFile, "data/hero_cape_front.bmp");; + GameState->HeroTorso = DEBUGLoadBMP(Thread, Memory->DEBUGPlatformReadEntireFile, "data/hero_torso_front.bmp");; GameState->PlayerP.AbsTileX = 2; GameState->PlayerP.AbsTileY = 3; @@ -329,10 +393,13 @@ void UpdateAndRender(thread_context *Thread, game_memory *Memory, game_input *In } } + DrawRectangle(Buffer, 0.0f, 0.0f, (F32)Buffer->Width, (F32)Buffer->Height, 0.0f, 1.0f, 0.0f); + + DrawBitmap(Buffer, &GameState->Backdrop, 0.0f, 0.0f); + F32 ScreenCenterX = 0.5f * (F32)Buffer->Width; F32 ScreenCenterY = 0.5f * (F32)Buffer->Height; - DrawRectangle(Buffer, 0.0f, 0.0f, (F32)Buffer->Width, (F32)Buffer->Height, 0.0f, 1.0f, 0.0f); for(S32 RelRow = -10; RelRow < 10; RelRow++) { for(S32 RelColumn = -20; RelColumn < 20; RelColumn++) { U32 Column = GameState->PlayerP.AbsTileX + RelColumn; @@ -340,7 +407,7 @@ void UpdateAndRender(thread_context *Thread, game_memory *Memory, game_input *In U32 TileID = GetTileValue(TileMap, Column, Row, GameState->PlayerP.AbsTileZ); - if(TileID > 0) { + if(TileID > 1) { F32 Gray = 0.5f; if(TileID == 2) { Gray = 1.0f; @@ -371,17 +438,8 @@ void UpdateAndRender(thread_context *Thread, game_memory *Memory, game_input *In F32 PlayerB = 0.0f; F32 PlayerLeft = ScreenCenterX - 0.5f * MetersToPixels*PlayerWidth; F32 PlayerTop = ScreenCenterY - MetersToPixels*PlayerHeight; - DrawRectangle(Buffer, PlayerLeft, PlayerTop, PlayerLeft + MetersToPixels*PlayerWidth, PlayerTop + MetersToPixels*PlayerHeight, PlayerR, PlayerG, PlayerB); - -#if 0 - U32 *Source = GameState->PixelPointer; - U32 *Dest = (U32 *)Buffer->Memory; - for(S32 Y = 0; Y < Buffer->Height; Y++) { - for(S32 X = 0; X < Buffer->Width; X++) { - *Dest++ = *Source++; - } - } -#endif + //DrawRectangle(Buffer, PlayerLeft, PlayerTop, PlayerLeft + MetersToPixels*PlayerWidth, PlayerTop + MetersToPixels*PlayerHeight, PlayerR, PlayerG, PlayerB); + DrawBitmap(Buffer, &GameState->HeroHead, PlayerLeft, PlayerTop); GameOutputSound(GameState, SoundBuffer, 400); } diff --git a/handmade.h b/handmade.h index 1f204d4..64c6ed9 100644 --- a/handmade.h +++ b/handmade.h @@ -165,12 +165,23 @@ typedef struct world { tile_map *TileMap; } world; +typedef struct loaded_bitmap { + S32 Width; + S32 Height; + U32 *Pixels; +} loaded_bitmap; + typedef struct game_state { memory_arena WorldArena; world *World; tile_map_position PlayerP; - U32 *PixelPointer; + + loaded_bitmap Backdrop; + + loaded_bitmap HeroHead; + loaded_bitmap HeroCape; + loaded_bitmap HeroTorso; } game_state; #endif diff --git a/sdl_handmade.c b/sdl_handmade.c index e67d4b2..785706e 100644 --- a/sdl_handmade.c +++ b/sdl_handmade.c @@ -557,7 +557,7 @@ S32 main(S32 argc, char *argv[]) SDL_GetCurrentDisplayMode(0, &display_mode); SDL_Renderer *Renderer = SDL_CreateRenderer(Window, -1, SDL_RENDERER_PRESENTVSYNC); - SDL_SetWindowPosition(Window, (display_mode.w - RESOLUTION_WIDTH), (display_mode.h - RESOLUTION_HEIGHT)); + SDL_SetWindowPosition(Window, (display_mode.w - RESOLUTION_WIDTH-20), (display_mode.h - RESOLUTION_HEIGHT-20)); /* NOTE: For future reference, the custom window bar should use * SDL_SetWindowHitTest to define areas that will be used to