4coder/test_data/lots_of_files/test_asset_builder.cpp

1143 lines
36 KiB
C++

/* ========================================================================
$File: $
$Date: $
$Revision: $
$Creator: Casey Muratori $
$Notice: (C) Copyright 2015 by Molly Rocket, Inc. All Rights Reserved. $
======================================================================== */
#include "test_asset_builder.h"
#pragma pack(push, 1)
struct bitmap_header
{
uint16 FileType;
uint32 FileSize;
uint16 Reserved1;
uint16 Reserved2;
uint32 BitmapOffset;
uint32 Size;
int32 Width;
int32 Height;
uint16 Planes;
uint16 BitsPerPixel;
uint32 Compression;
uint32 SizeOfBitmap;
int32 HorzResolution;
int32 VertResolution;
uint32 ColorsUsed;
uint32 ColorsImportant;
uint32 RedMask;
uint32 GreenMask;
uint32 BlueMask;
};
struct WAVE_header
{
uint32 RIFFID;
uint32 Size;
uint32 WAVEID;
};
#define RIFF_CODE(a, b, c, d) (((uint32)(a) << 0) | ((uint32)(b) << 8) | ((uint32)(c) << 16) | ((uint32)(d) << 24))
enum
{
WAVE_ChunkID_fmt = RIFF_CODE('f', 'm', 't', ' '),
WAVE_ChunkID_data = RIFF_CODE('d', 'a', 't', 'a'),
WAVE_ChunkID_RIFF = RIFF_CODE('R', 'I', 'F', 'F'),
WAVE_ChunkID_WAVE = RIFF_CODE('W', 'A', 'V', 'E'),
};
struct WAVE_chunk
{
uint32 ID;
uint32 Size;
};
struct WAVE_fmt
{
uint16 wFormatTag;
uint16 nChannels;
uint32 nSamplesPerSec;
uint32 nAvgBytesPerSec;
uint16 nBlockAlign;
uint16 wBitsPerSample;
uint16 cbSize;
uint16 wValidBitsPerSample;
uint32 dwChannelMask;
uint8 SubFormat[16];
};
#pragma pack(pop)
struct entire_file
{
u32 ContentsSize;
void *Contents;
};
entire_file
ReadEntireFile(char *FileName)
{
entire_file Result = {};
FILE *In = fopen(FileName, "rb");
if(In)
{
fseek(In, 0, SEEK_END);
Result.ContentsSize = ftell(In);
fseek(In, 0, SEEK_SET);
Result.Contents = malloc(Result.ContentsSize);
fread(Result.Contents, Result.ContentsSize, 1, In);
fclose(In);
}
else
{
printf("ERROR: Cannot open file %s.\n", FileName);
}
return(Result);
}
internal loaded_bitmap
LoadBMP(char *FileName)
{
loaded_bitmap Result = {};
entire_file ReadResult = ReadEntireFile(FileName);
if(ReadResult.ContentsSize != 0)
{
Result.Free = ReadResult.Contents;
bitmap_header *Header = (bitmap_header *)ReadResult.Contents;
uint32 *Pixels = (uint32 *)((uint8 *)ReadResult.Contents + Header->BitmapOffset);
Result.Memory = Pixels;
Result.Width = Header->Width;
Result.Height = Header->Height;
Assert(Result.Height >= 0);
Assert(Header->Compression == 3);
// NOTE(casey): If you are using this generically for some reason,
// please remember that BMP files CAN GO IN EITHER DIRECTION and
// the height will be negative for top-down.
// (Also, there can be compression, etc., etc... DON'T think this
// is complete BMP loading code because it isn't!!)
// NOTE(casey): Byte order in memory is determined by the Header itself,
// so we have to read out the masks and convert the pixels ourselves.
uint32 RedMask = Header->RedMask;
uint32 GreenMask = Header->GreenMask;
uint32 BlueMask = Header->BlueMask;
uint32 AlphaMask = ~(RedMask | GreenMask | BlueMask);
bit_scan_result RedScan = FindLeastSignificantSetBit(RedMask);
bit_scan_result GreenScan = FindLeastSignificantSetBit(GreenMask);
bit_scan_result BlueScan = FindLeastSignificantSetBit(BlueMask);
bit_scan_result AlphaScan = FindLeastSignificantSetBit(AlphaMask);
Assert(RedScan.Found);
Assert(GreenScan.Found);
Assert(BlueScan.Found);
Assert(AlphaScan.Found);
int32 RedShiftDown = (int32)RedScan.Index;
int32 GreenShiftDown = (int32)GreenScan.Index;
int32 BlueShiftDown = (int32)BlueScan.Index;
int32 AlphaShiftDown = (int32)AlphaScan.Index;
uint32 *SourceDest = Pixels;
for(int32 Y = 0;
Y < Header->Height;
++Y)
{
for(int32 X = 0;
X < Header->Width;
++X)
{
uint32 C = *SourceDest;
v4 Texel = {(real32)((C & RedMask) >> RedShiftDown),
(real32)((C & GreenMask) >> GreenShiftDown),
(real32)((C & BlueMask) >> BlueShiftDown),
(real32)((C & AlphaMask) >> AlphaShiftDown)};
Texel = SRGB255ToLinear1(Texel);
#if 1
Texel.rgb *= Texel.a;
#endif
Texel = Linear1ToSRGB255(Texel);
*SourceDest++ = (((uint32)(Texel.a + 0.5f) << 24) |
((uint32)(Texel.r + 0.5f) << 16) |
((uint32)(Texel.g + 0.5f) << 8) |
((uint32)(Texel.b + 0.5f) << 0));
}
}
}
Result.Pitch = Result.Width*BITMAP_BYTES_PER_PIXEL;
#if 0
Result.Memory = (uint8 *)Result.Memory + Result.Pitch*(Result.Height - 1);
Result.Pitch = -Result.Pitch;
#endif
return(Result);
}
internal loaded_font *
LoadFont(char *FileName, char *FontName, int PixelHeight)
{
loaded_font *Font = (loaded_font *)malloc(sizeof(loaded_font));
AddFontResourceExA(FileName, FR_PRIVATE, 0);
Font->Win32Handle = CreateFontA(PixelHeight, 0, 0, 0,
FW_NORMAL, // NOTE(casey): Weight
FALSE, // NOTE(casey): Italic
FALSE, // NOTE(casey): Underline
FALSE, // NOTE(casey): Strikeout
DEFAULT_CHARSET,
OUT_DEFAULT_PRECIS,
CLIP_DEFAULT_PRECIS,
ANTIALIASED_QUALITY,
DEFAULT_PITCH|FF_DONTCARE,
FontName);
SelectObject(GlobalFontDeviceContext, Font->Win32Handle);
GetTextMetrics(GlobalFontDeviceContext, &Font->TextMetric);
Font->MinCodePoint = INT_MAX;
Font->MaxCodePoint = 0;
// NOTE(casey): 5k characters should be more than enough for _anybody_!
Font->MaxGlyphCount = 5000;
Font->GlyphCount = 0;
u32 GlyphIndexFromCodePointSize = ONE_PAST_MAX_FONT_CODEPOINT*sizeof(u32);
Font->GlyphIndexFromCodePoint = (u32 *)malloc(GlyphIndexFromCodePointSize);
memset(Font->GlyphIndexFromCodePoint, 0, GlyphIndexFromCodePointSize);
Font->Glyphs = (hha_font_glyph *)malloc(sizeof(hha_font_glyph)*Font->MaxGlyphCount);
size_t HorizontalAdvanceSize = sizeof(r32)*Font->MaxGlyphCount*Font->MaxGlyphCount;
Font->HorizontalAdvance = (r32 *)malloc(HorizontalAdvanceSize);
memset(Font->HorizontalAdvance, 0, HorizontalAdvanceSize);
Font->OnePastHighestCodepoint = 0;
// NOTE(casey): Reserve space for the null glyph
Font->GlyphCount = 1;
Font->Glyphs[0].UnicodeCodePoint = 0;
Font->Glyphs[0].BitmapID.Value = 0;
return(Font);
}
internal void
FinalizeFontKerning(loaded_font *Font)
{
SelectObject(GlobalFontDeviceContext, Font->Win32Handle);
DWORD KerningPairCount = GetKerningPairsW(GlobalFontDeviceContext, 0, 0);
KERNINGPAIR *KerningPairs = (KERNINGPAIR *)malloc(KerningPairCount*sizeof(KERNINGPAIR));
GetKerningPairsW(GlobalFontDeviceContext, KerningPairCount, KerningPairs);
for(DWORD KerningPairIndex = 0;
KerningPairIndex < KerningPairCount;
++KerningPairIndex)
{
KERNINGPAIR *Pair = KerningPairs + KerningPairIndex;
if((Pair->wFirst < ONE_PAST_MAX_FONT_CODEPOINT) &&
(Pair->wSecond < ONE_PAST_MAX_FONT_CODEPOINT))
{
u32 First = Font->GlyphIndexFromCodePoint[Pair->wFirst];
u32 Second = Font->GlyphIndexFromCodePoint[Pair->wSecond];
if((First != 0) && (Second != 0))
{
Font->HorizontalAdvance[First*Font->MaxGlyphCount + Second] += (r32)Pair->iKernAmount;
}
}
}
free(KerningPairs);
}
internal void
FreeFont(loaded_font *Font)
{
if(Font)
{
DeleteObject(Font->Win32Handle);
free(Font->Glyphs);
free(Font->HorizontalAdvance);
free(Font->GlyphIndexFromCodePoint);
free(Font);
}
}
internal void
InitializeFontDC(void)
{
GlobalFontDeviceContext = CreateCompatibleDC(GetDC(0));
BITMAPINFO Info = {};
Info.bmiHeader.biSize = sizeof(Info.bmiHeader);
Info.bmiHeader.biWidth = MAX_FONT_WIDTH;
Info.bmiHeader.biHeight = MAX_FONT_HEIGHT;
Info.bmiHeader.biPlanes = 1;
Info.bmiHeader.biBitCount = 32;
Info.bmiHeader.biCompression = BI_RGB;
Info.bmiHeader.biSizeImage = 0;
Info.bmiHeader.biXPelsPerMeter = 0;
Info.bmiHeader.biYPelsPerMeter = 0;
Info.bmiHeader.biClrUsed = 0;
Info.bmiHeader.biClrImportant = 0;
HBITMAP Bitmap = CreateDIBSection(GlobalFontDeviceContext, &Info, DIB_RGB_COLORS, &GlobalFontBits, 0, 0);
SelectObject(GlobalFontDeviceContext, Bitmap);
SetBkColor(GlobalFontDeviceContext, RGB(0, 0, 0));
}
internal loaded_bitmap
LoadGlyphBitmap(loaded_font *Font, u32 CodePoint, hha_asset *Asset)
{
loaded_bitmap Result = {};
u32 GlyphIndex = Font->GlyphIndexFromCodePoint[CodePoint];
#if USE_FONTS_FROM_WINDOWS
SelectObject(GlobalFontDeviceContext, Font->Win32Handle);
memset(GlobalFontBits, 0x00, MAX_FONT_WIDTH*MAX_FONT_HEIGHT*sizeof(u32));
wchar_t CheesePoint = (wchar_t)CodePoint;
SIZE Size;
GetTextExtentPoint32W(GlobalFontDeviceContext, &CheesePoint, 1, &Size);
int PreStepX = 128;
int BoundWidth = Size.cx + 2*PreStepX;
if(BoundWidth > MAX_FONT_WIDTH)
{
BoundWidth = MAX_FONT_WIDTH;
}
int BoundHeight = Size.cy;
if(BoundHeight > MAX_FONT_HEIGHT)
{
BoundHeight = MAX_FONT_HEIGHT;
}
// PatBlt(DeviceContext, 0, 0, Width, Height, BLACKNESS);
// SetBkMode(DeviceContext, TRANSPARENT);
SetTextColor(GlobalFontDeviceContext, RGB(255, 255, 255));
TextOutW(GlobalFontDeviceContext, PreStepX, 0, &CheesePoint, 1);
s32 MinX = 10000;
s32 MinY = 10000;
s32 MaxX = -10000;
s32 MaxY = -10000;
u32 *Row = (u32 *)GlobalFontBits + (MAX_FONT_HEIGHT - 1)*MAX_FONT_WIDTH;
for(s32 Y = 0;
Y < BoundHeight;
++Y)
{
u32 *Pixel = Row;
for(s32 X = 0;
X < BoundWidth;
++X)
{
#if 0
COLORREF RefPixel = GetPixel(GlobalFontDeviceContext, X, Y);
Assert(RefPixel == *Pixel);
#endif
if(*Pixel != 0)
{
if(MinX > X)
{
MinX = X;
}
if(MinY > Y)
{
MinY = Y;
}
if(MaxX < X)
{
MaxX = X;
}
if(MaxY < Y)
{
MaxY = Y;
}
}
++Pixel;
}
Row -= MAX_FONT_WIDTH;
}
r32 KerningChange = 0;
if(MinX <= MaxX)
{
int Width = (MaxX - MinX) + 1;
int Height = (MaxY - MinY) + 1;
Result.Width = Width + 2;
Result.Height = Height + 2;
Result.Pitch = Result.Width*BITMAP_BYTES_PER_PIXEL;
Result.Memory = malloc(Result.Height*Result.Pitch);
Result.Free = Result.Memory;
memset(Result.Memory, 0, Result.Height*Result.Pitch);
u8 *DestRow = (u8 *)Result.Memory + (Result.Height - 1 - 1)*Result.Pitch;
u32 *SourceRow = (u32 *)GlobalFontBits + (MAX_FONT_HEIGHT - 1 - MinY)*MAX_FONT_WIDTH;
for(s32 Y = MinY;
Y <= MaxY;
++Y)
{
u32 *Source = (u32 *)SourceRow + MinX;
u32 *Dest = (u32 *)DestRow + 1;
for(s32 X = MinX;
X <= MaxX;
++X)
{
#if 0
COLORREF Pixel = GetPixel(GlobalFontDeviceContext, X, Y);
Assert(Pixel == *Source);
#else
u32 Pixel = *Source;
#endif
r32 Gray = (r32)(Pixel & 0xFF);
v4 Texel = {255.0f, 255.0f, 255.0f, Gray};
Texel = SRGB255ToLinear1(Texel);
Texel.rgb *= Texel.a;
Texel = Linear1ToSRGB255(Texel);
*Dest++ = (((uint32)(Texel.a + 0.5f) << 24) |
((uint32)(Texel.r + 0.5f) << 16) |
((uint32)(Texel.g + 0.5f) << 8) |
((uint32)(Texel.b + 0.5f) << 0));
++Source;
}
DestRow -= Result.Pitch;
SourceRow -= MAX_FONT_WIDTH;
}
Asset->Bitmap.AlignPercentage[0] = (1.0f) / (r32)Result.Width;
Asset->Bitmap.AlignPercentage[1] = (1.0f + (MaxY - (BoundHeight - Font->TextMetric.tmDescent))) / (r32)Result.Height;
KerningChange = (r32)(MinX - PreStepX);
}
#if 0
ABC ThisABC;
GetCharABCWidthsW(GlobalFontDeviceContext, CodePoint, CodePoint, &ThisABC);
r32 CharAdvance = (r32)(ThisABC.abcA + ThisABC.abcB + ThisABC.abcC);
#else
INT ThisWidth;
GetCharWidth32W(GlobalFontDeviceContext, CodePoint, CodePoint, &ThisWidth);
r32 CharAdvance = (r32)ThisWidth;
#endif
for(u32 OtherGlyphIndex = 0;
OtherGlyphIndex < Font->MaxGlyphCount;
++OtherGlyphIndex)
{
Font->HorizontalAdvance[GlyphIndex*Font->MaxGlyphCount + OtherGlyphIndex] += CharAdvance - KerningChange;
if(OtherGlyphIndex != 0)
{
Font->HorizontalAdvance[OtherGlyphIndex*Font->MaxGlyphCount + GlyphIndex] += KerningChange;
}
}
#else
entire_file TTFFile = ReadEntireFile(FileName);
if(TTFFile.ContentsSize != 0)
{
stbtt_fontinfo Font;
stbtt_InitFont(&Font, (u8 *)TTFFile.Contents, stbtt_GetFontOffsetForIndex((u8 *)TTFFile.Contents, 0));
int Width, Height, XOffset, YOffset;
u8 *MonoBitmap = stbtt_GetCodepointBitmap(&Font, 0, stbtt_ScaleForPixelHeight(&Font, 128.0f),
CodePoint, &Width, &Height, &XOffset, &YOffset);
Result.Width = Width;
Result.Height = Height;
Result.Pitch = Result.Width*BITMAP_BYTES_PER_PIXEL;
Result.Memory = malloc(Height*Result.Pitch);
Result.Free = Result.Memory;
u8 *Source = MonoBitmap;
u8 *DestRow = (u8 *)Result.Memory + (Height - 1)*Result.Pitch;
for(s32 Y = 0;
Y < Height;
++Y)
{
u32 *Dest = (u32 *)DestRow;
for(s32 X = 0;
X < Width;
++X)
{
u8 Gray = *Source++;
u8 Alpha = 0xFF;
*Dest++ = ((Alpha << 24) |
(Gray << 16) |
(Gray << 8) |
(Gray << 0));
}
DestRow -= Result.Pitch;
}
stbtt_FreeBitmap(MonoBitmap, 0);
free(TTFFile.Contents);
}
#endif
return(Result);
}
struct riff_iterator
{
uint8 *At;
uint8 *Stop;
};
inline riff_iterator
ParseChunkAt(void *At, void *Stop)
{
riff_iterator Iter;
Iter.At = (uint8 *)At;
Iter.Stop = (uint8 *)Stop;
return(Iter);
}
inline riff_iterator
NextChunk(riff_iterator Iter)
{
WAVE_chunk *Chunk = (WAVE_chunk *)Iter.At;
uint32 Size = (Chunk->Size + 1) & ~1;
Iter.At += sizeof(WAVE_chunk) + Size;
return(Iter);
}
inline bool32
IsValid(riff_iterator Iter)
{
bool32 Result = (Iter.At < Iter.Stop);
return(Result);
}
inline void *
GetChunkData(riff_iterator Iter)
{
void *Result = (Iter.At + sizeof(WAVE_chunk));
return(Result);
}
inline uint32
GetType(riff_iterator Iter)
{
WAVE_chunk *Chunk = (WAVE_chunk *)Iter.At;
uint32 Result = Chunk->ID;
return(Result);
}
inline uint32
GetChunkDataSize(riff_iterator Iter)
{
WAVE_chunk *Chunk = (WAVE_chunk *)Iter.At;
uint32 Result = Chunk->Size;
return(Result);
}
struct loaded_sound
{
uint32 SampleCount; // NOTE(casey): This is the sample count divided by 8
uint32 ChannelCount;
int16 *Samples[2];
void *Free;
};
internal loaded_sound
LoadWAV(char *FileName, u32 SectionFirstSampleIndex, u32 SectionSampleCount)
{
loaded_sound Result = {};
entire_file ReadResult = ReadEntireFile(FileName);
if(ReadResult.ContentsSize != 0)
{
Result.Free = ReadResult.Contents;
WAVE_header *Header = (WAVE_header *)ReadResult.Contents;
Assert(Header->RIFFID == WAVE_ChunkID_RIFF);
Assert(Header->WAVEID == WAVE_ChunkID_WAVE);
uint32 ChannelCount = 0;
uint32 SampleDataSize = 0;
int16 *SampleData = 0;
for(riff_iterator Iter = ParseChunkAt(Header + 1, (uint8 *)(Header + 1) + Header->Size - 4);
IsValid(Iter);
Iter = NextChunk(Iter))
{
switch(GetType(Iter))
{
case WAVE_ChunkID_fmt:
{
WAVE_fmt *fmt = (WAVE_fmt *)GetChunkData(Iter);
Assert(fmt->wFormatTag == 1); // NOTE(casey): Only support PCM
Assert(fmt->nSamplesPerSec == 48000);
Assert(fmt->wBitsPerSample == 16);
Assert(fmt->nBlockAlign == (sizeof(int16)*fmt->nChannels));
ChannelCount = fmt->nChannels;
} break;
case WAVE_ChunkID_data:
{
SampleData = (int16 *)GetChunkData(Iter);
SampleDataSize = GetChunkDataSize(Iter);
} break;
}
}
Assert(ChannelCount && SampleData);
Result.ChannelCount = ChannelCount;
u32 SampleCount = SampleDataSize / (ChannelCount*sizeof(int16));
if(ChannelCount == 1)
{
Result.Samples[0] = SampleData;
Result.Samples[1] = 0;
}
else if(ChannelCount == 2)
{
Result.Samples[0] = SampleData;
Result.Samples[1] = SampleData + SampleCount;
#if 0
for(uint32 SampleIndex = 0;
SampleIndex < SampleCount;
++SampleIndex)
{
SampleData[2*SampleIndex + 0] = (int16)SampleIndex;
SampleData[2*SampleIndex + 1] = (int16)SampleIndex;
}
#endif
for(uint32 SampleIndex = 0;
SampleIndex < SampleCount;
++SampleIndex)
{
int16 Source = SampleData[2*SampleIndex];
SampleData[2*SampleIndex] = SampleData[SampleIndex];
SampleData[SampleIndex] = Source;
}
}
else
{
Assert(!"Invalid channel count in WAV file");
}
// TODO(casey): Load right channels!
b32 AtEnd = true;
Result.ChannelCount = 1;
if(SectionSampleCount)
{
Assert((SectionFirstSampleIndex + SectionSampleCount) <= SampleCount);
AtEnd = ((SectionFirstSampleIndex + SectionSampleCount) == SampleCount);
SampleCount = SectionSampleCount;
for(uint32 ChannelIndex = 0;
ChannelIndex < Result.ChannelCount;
++ChannelIndex)
{
Result.Samples[ChannelIndex] += SectionFirstSampleIndex;
}
}
if(AtEnd)
{
for(uint32 ChannelIndex = 0;
ChannelIndex < Result.ChannelCount;
++ChannelIndex)
{
for(u32 SampleIndex = SampleCount;
SampleIndex < (SampleCount + 8);
++SampleIndex)
{
Result.Samples[ChannelIndex][SampleIndex] = 0;
}
}
}
Result.SampleCount = SampleCount;
}
return(Result);
}
internal void
BeginAssetType(game_assets *Assets, asset_type_id TypeID)
{
Assert(Assets->DEBUGAssetType == 0);
Assets->DEBUGAssetType = Assets->AssetTypes + TypeID;
Assets->DEBUGAssetType->TypeID = TypeID;
Assets->DEBUGAssetType->FirstAssetIndex = Assets->AssetCount;
Assets->DEBUGAssetType->OnePastLastAssetIndex = Assets->DEBUGAssetType->FirstAssetIndex;
}
struct added_asset
{
u32 ID;
hha_asset *HHA;
asset_source *Source;
};
internal added_asset
AddAsset(game_assets *Assets)
{
Assert(Assets->DEBUGAssetType);
Assert(Assets->DEBUGAssetType->OnePastLastAssetIndex < ArrayCount(Assets->Assets));
u32 Index = Assets->DEBUGAssetType->OnePastLastAssetIndex++;
asset_source *Source = Assets->AssetSources + Index;
hha_asset *HHA = Assets->Assets + Index;
HHA->FirstTagIndex = Assets->TagCount;
HHA->OnePastLastTagIndex = HHA->FirstTagIndex;
Assets->AssetIndex = Index;
added_asset Result;
Result.ID = Index;
Result.HHA = HHA;
Result.Source = Source;
return(Result);
}
internal bitmap_id
AddBitmapAsset(game_assets *Assets, char *FileName, r32 AlignPercentageX = 0.5f, r32 AlignPercentageY = 0.5f)
{
added_asset Asset = AddAsset(Assets);
Asset.HHA->Bitmap.AlignPercentage[0] = AlignPercentageX;
Asset.HHA->Bitmap.AlignPercentage[1] = AlignPercentageY;
Asset.Source->Type = AssetType_Bitmap;
Asset.Source->Bitmap.FileName = FileName;
bitmap_id Result = {Asset.ID};
return(Result);
}
internal bitmap_id
AddCharacterAsset(game_assets *Assets, loaded_font *Font, u32 Codepoint)
{
added_asset Asset = AddAsset(Assets);
Asset.HHA->Bitmap.AlignPercentage[0] = 0.0f; // NOTE(casey): Set later by extraction
Asset.HHA->Bitmap.AlignPercentage[1] = 0.0f; // NOTE(casey): Set later by extraction
Asset.Source->Type = AssetType_FontGlyph;
Asset.Source->Glyph.Font = Font;
Asset.Source->Glyph.Codepoint = Codepoint;
bitmap_id Result = {Asset.ID};
Assert(Font->GlyphCount < Font->MaxGlyphCount);
u32 GlyphIndex = Font->GlyphCount++;
hha_font_glyph *Glyph = Font->Glyphs + GlyphIndex;
Glyph->UnicodeCodePoint = Codepoint;
Glyph->BitmapID = Result;
Font->GlyphIndexFromCodePoint[Codepoint] = GlyphIndex;
if(Font->OnePastHighestCodepoint <= Codepoint)
{
Font->OnePastHighestCodepoint = Codepoint + 1;
}
return(Result);
}
internal sound_id
AddSoundAsset(game_assets *Assets, char *FileName, u32 FirstSampleIndex = 0, u32 SampleCount = 0)
{
added_asset Asset = AddAsset(Assets);
Asset.HHA->Sound.SampleCount = SampleCount;
Asset.HHA->Sound.Chain = HHASoundChain_None;
Asset.Source->Type = AssetType_Sound;
Asset.Source->Sound.FileName = FileName;
Asset.Source->Sound.FirstSampleIndex = FirstSampleIndex;
sound_id Result = {Asset.ID};
return(Result);
}
internal font_id
AddFontAsset(game_assets *Assets, loaded_font *Font)
{
added_asset Asset = AddAsset(Assets);
Asset.HHA->Font.OnePastHighestCodepoint = Font->OnePastHighestCodepoint;
Asset.HHA->Font.GlyphCount = Font->GlyphCount;
Asset.HHA->Font.AscenderHeight = (r32)Font->TextMetric.tmAscent;
Asset.HHA->Font.DescenderHeight = (r32)Font->TextMetric.tmDescent;
Asset.HHA->Font.ExternalLeading = (r32)Font->TextMetric.tmExternalLeading;
Asset.Source->Type = AssetType_Font;
Asset.Source->Font.Font = Font;
font_id Result = {Asset.ID};
return(Result);
}
internal void
AddTag(game_assets *Assets, asset_tag_id ID, real32 Value)
{
Assert(Assets->AssetIndex);
hha_asset *HHA = Assets->Assets + Assets->AssetIndex;
++HHA->OnePastLastTagIndex;
hha_tag *Tag = Assets->Tags + Assets->TagCount++;
Tag->ID = ID;
Tag->Value = Value;
}
internal void
EndAssetType(game_assets *Assets)
{
Assert(Assets->DEBUGAssetType);
Assets->AssetCount = Assets->DEBUGAssetType->OnePastLastAssetIndex;
Assets->DEBUGAssetType = 0;
Assets->AssetIndex = 0;
}
internal void
WriteHHA(game_assets *Assets, char *FileName)
{
FILE *Out = fopen(FileName, "wb");
if(Out)
{
hha_header Header = {};
Header.MagicValue = HHA_MAGIC_VALUE;
Header.Version = HHA_VERSION;
Header.TagCount = Assets->TagCount;
Header.AssetTypeCount = Asset_Count; // TODO(casey): Do we really want to do this? Sparseness!
Header.AssetCount = Assets->AssetCount;
u32 TagArraySize = Header.TagCount*sizeof(hha_tag);
u32 AssetTypeArraySize = Header.AssetTypeCount*sizeof(hha_asset_type);
u32 AssetArraySize = Header.AssetCount*sizeof(hha_asset);
Header.Tags = sizeof(Header);
Header.AssetTypes = Header.Tags + TagArraySize;
Header.Assets = Header.AssetTypes + AssetTypeArraySize;
fwrite(&Header, sizeof(Header), 1, Out);
fwrite(Assets->Tags, TagArraySize, 1, Out);
fwrite(Assets->AssetTypes, AssetTypeArraySize, 1, Out);
fseek(Out, AssetArraySize, SEEK_CUR);
for(u32 AssetIndex = 1;
AssetIndex < Header.AssetCount;
++AssetIndex)
{
asset_source *Source = Assets->AssetSources + AssetIndex;
hha_asset *Dest = Assets->Assets + AssetIndex;
Dest->DataOffset = ftell(Out);
if(Source->Type == AssetType_Sound)
{
loaded_sound WAV = LoadWAV(Source->Sound.FileName,
Source->Sound.FirstSampleIndex,
Dest->Sound.SampleCount);
Dest->Sound.SampleCount = WAV.SampleCount;
Dest->Sound.ChannelCount = WAV.ChannelCount;
for(u32 ChannelIndex = 0;
ChannelIndex < WAV.ChannelCount;
++ChannelIndex)
{
fwrite(WAV.Samples[ChannelIndex], Dest->Sound.SampleCount*sizeof(s16), 1, Out);
}
free(WAV.Free);
}
else if(Source->Type == AssetType_Font)
{
loaded_font *Font = Source->Font.Font;
FinalizeFontKerning(Font);
u32 GlyphsSize = Font->GlyphCount*sizeof(hha_font_glyph);
fwrite(Font->Glyphs, GlyphsSize, 1, Out);
u8 *HorizontalAdvance = (u8 *)Font->HorizontalAdvance;
for(u32 GlyphIndex = 0;
GlyphIndex < Font->GlyphCount;
++GlyphIndex)
{
u32 HorizontalAdvanceSliceSize = sizeof(r32)*Font->GlyphCount;
fwrite(HorizontalAdvance, HorizontalAdvanceSliceSize, 1, Out);
HorizontalAdvance += sizeof(r32)*Font->MaxGlyphCount;
}
}
else
{
loaded_bitmap Bitmap;
if(Source->Type == AssetType_FontGlyph)
{
Bitmap = LoadGlyphBitmap(Source->Glyph.Font, Source->Glyph.Codepoint, Dest);
}
else
{
Assert(Source->Type == AssetType_Bitmap);
Bitmap = LoadBMP(Source->Bitmap.FileName);
}
Dest->Bitmap.Dim[0] = Bitmap.Width;
Dest->Bitmap.Dim[1] = Bitmap.Height;
Assert((Bitmap.Width*4) == Bitmap.Pitch);
fwrite(Bitmap.Memory, Bitmap.Width*Bitmap.Height*4, 1, Out);
free(Bitmap.Free);
}
}
fseek(Out, (u32)Header.Assets, SEEK_SET);
fwrite(Assets->Assets, AssetArraySize, 1, Out);
fclose(Out);
}
else
{
printf("ERROR: Couldn't open file :(\n");
}
}
internal void
Initialize(game_assets *Assets)
{
Assets->TagCount = 1;
Assets->AssetCount = 1;
Assets->DEBUGAssetType = 0;
Assets->AssetIndex = 0;
Assets->AssetTypeCount = Asset_Count;
memset(Assets->AssetTypes, 0, sizeof(Assets->AssetTypes));
}
internal void
WriteFonts(void)
{
game_assets Assets_;
game_assets *Assets = &Assets_;
Initialize(Assets);
loaded_font *Fonts[] =
{
LoadFont("c:/Windows/Fonts/arial.ttf", "Arial", 128),
LoadFont("c:/Windows/Fonts/LiberationMono-Regular.ttf", "Liberation Mono", 20),
};
BeginAssetType(Assets, Asset_FontGlyph);
for(u32 FontIndex = 0;
FontIndex < ArrayCount(Fonts);
++FontIndex)
{
loaded_font *Font = Fonts[FontIndex];
AddCharacterAsset(Assets, Font, ' ');
for(u32 Character = '!';
Character <= '~';
++Character)
{
AddCharacterAsset(Assets, Font, Character);
}
// NOTE(casey): Kanji OWL!!!!!!!
AddCharacterAsset(Assets, Font, 0x5c0f);
AddCharacterAsset(Assets, Font, 0x8033);
AddCharacterAsset(Assets, Font, 0x6728);
AddCharacterAsset(Assets, Font, 0x514e);
}
EndAssetType(Assets);
// TODO(casey): This is kinda janky, because it means you have to get this
// order right always!
BeginAssetType(Assets, Asset_Font);
AddFontAsset(Assets, Fonts[0]);
AddTag(Assets, Tag_FontType, FontType_Default);
AddFontAsset(Assets, Fonts[1]);
AddTag(Assets, Tag_FontType, FontType_Debug);
EndAssetType(Assets);
WriteHHA(Assets, "testfonts.hha");
}
internal void
WriteHero(void)
{
game_assets Assets_;
game_assets *Assets = &Assets_;
Initialize(Assets);
real32 AngleRight = 0.0f*Tau32;
real32 AngleBack = 0.25f*Tau32;
real32 AngleLeft = 0.5f*Tau32;
real32 AngleFront = 0.75f*Tau32;
r32 HeroAlign[] = {0.5f, 0.156682029f};
BeginAssetType(Assets, Asset_Head);
AddBitmapAsset(Assets, "test/test_hero_right_head.bmp", HeroAlign[0], HeroAlign[1]);
AddTag(Assets, Tag_FacingDirection, AngleRight);
AddBitmapAsset(Assets, "test/test_hero_back_head.bmp", HeroAlign[0], HeroAlign[1]);
AddTag(Assets, Tag_FacingDirection, AngleBack);
AddBitmapAsset(Assets, "test/test_hero_left_head.bmp", HeroAlign[0], HeroAlign[1]);
AddTag(Assets, Tag_FacingDirection, AngleLeft);
AddBitmapAsset(Assets, "test/test_hero_front_head.bmp", HeroAlign[0], HeroAlign[1]);
AddTag(Assets, Tag_FacingDirection, AngleFront);
EndAssetType(Assets);
BeginAssetType(Assets, Asset_Cape);
AddBitmapAsset(Assets, "test/test_hero_right_cape.bmp", HeroAlign[0], HeroAlign[1]);
AddTag(Assets, Tag_FacingDirection, AngleRight);
AddBitmapAsset(Assets, "test/test_hero_back_cape.bmp", HeroAlign[0], HeroAlign[1]);
AddTag(Assets, Tag_FacingDirection, AngleBack);
AddBitmapAsset(Assets, "test/test_hero_left_cape.bmp", HeroAlign[0], HeroAlign[1]);
AddTag(Assets, Tag_FacingDirection, AngleLeft);
AddBitmapAsset(Assets, "test/test_hero_front_cape.bmp", HeroAlign[0], HeroAlign[1]);
AddTag(Assets, Tag_FacingDirection, AngleFront);
EndAssetType(Assets);
BeginAssetType(Assets, Asset_Torso);
AddBitmapAsset(Assets, "test/test_hero_right_torso.bmp", HeroAlign[0], HeroAlign[1]);
AddTag(Assets, Tag_FacingDirection, AngleRight);
AddBitmapAsset(Assets, "test/test_hero_back_torso.bmp", HeroAlign[0], HeroAlign[1]);
AddTag(Assets, Tag_FacingDirection, AngleBack);
AddBitmapAsset(Assets, "test/test_hero_left_torso.bmp", HeroAlign[0], HeroAlign[1]);
AddTag(Assets, Tag_FacingDirection, AngleLeft);
AddBitmapAsset(Assets, "test/test_hero_front_torso.bmp", HeroAlign[0], HeroAlign[1]);
AddTag(Assets, Tag_FacingDirection, AngleFront);
EndAssetType(Assets);
WriteHHA(Assets, "test1.hha");
}
internal void
WriteNonHero(void)
{
game_assets Assets_;
game_assets *Assets = &Assets_;
Initialize(Assets);
BeginAssetType(Assets, Asset_Shadow);
AddBitmapAsset(Assets, "test/test_hero_shadow.bmp", 0.5f, 0.156682029f);
EndAssetType(Assets);
BeginAssetType(Assets, Asset_Tree);
AddBitmapAsset(Assets, "test2/tree00.bmp", 0.493827164f, 0.295652181f);
EndAssetType(Assets);
BeginAssetType(Assets, Asset_Sword);
AddBitmapAsset(Assets, "test2/rock03.bmp", 0.5f, 0.65625f);
EndAssetType(Assets);
BeginAssetType(Assets, Asset_Grass);
AddBitmapAsset(Assets, "test2/grass00.bmp");
AddBitmapAsset(Assets, "test2/grass01.bmp");
EndAssetType(Assets);
BeginAssetType(Assets, Asset_Tuft);
AddBitmapAsset(Assets, "test2/tuft00.bmp");
AddBitmapAsset(Assets, "test2/tuft01.bmp");
AddBitmapAsset(Assets, "test2/tuft02.bmp");
EndAssetType(Assets);
BeginAssetType(Assets, Asset_Stone);
AddBitmapAsset(Assets, "test2/ground00.bmp");
AddBitmapAsset(Assets, "test2/ground01.bmp");
AddBitmapAsset(Assets, "test2/ground02.bmp");
AddBitmapAsset(Assets, "test2/ground03.bmp");
EndAssetType(Assets);
WriteHHA(Assets, "test2.hha");
}
internal void
WriteSounds(void)
{
game_assets Assets_;
game_assets *Assets = &Assets_;
Initialize(Assets);
BeginAssetType(Assets, Asset_Bloop);
AddSoundAsset(Assets, "test3/bloop_00.wav");
AddSoundAsset(Assets, "test3/bloop_01.wav");
AddSoundAsset(Assets, "test3/bloop_02.wav");
AddSoundAsset(Assets, "test3/bloop_03.wav");
AddSoundAsset(Assets, "test3/bloop_04.wav");
EndAssetType(Assets);
BeginAssetType(Assets, Asset_Crack);
AddSoundAsset(Assets, "test3/crack_00.wav");
EndAssetType(Assets);
BeginAssetType(Assets, Asset_Drop);
AddSoundAsset(Assets, "test3/drop_00.wav");
EndAssetType(Assets);
BeginAssetType(Assets, Asset_Glide);
AddSoundAsset(Assets, "test3/glide_00.wav");
EndAssetType(Assets);
u32 OneMusicChunk = 10*48000;
u32 TotalMusicSampleCount = 7468095;
BeginAssetType(Assets, Asset_Music);
for(u32 FirstSampleIndex = 0;
FirstSampleIndex < TotalMusicSampleCount;
FirstSampleIndex += OneMusicChunk)
{
u32 SampleCount = TotalMusicSampleCount - FirstSampleIndex;
if(SampleCount > OneMusicChunk)
{
SampleCount = OneMusicChunk;
}
sound_id ThisMusic = AddSoundAsset(Assets, "test3/music_test.wav", FirstSampleIndex, SampleCount);
if((FirstSampleIndex + OneMusicChunk) < TotalMusicSampleCount)
{
Assets->Assets[ThisMusic.Value].Sound.Chain = HHASoundChain_Advance;
}
}
EndAssetType(Assets);
BeginAssetType(Assets, Asset_Puhp);
AddSoundAsset(Assets, "test3/puhp_00.wav");
AddSoundAsset(Assets, "test3/puhp_01.wav");
EndAssetType(Assets);
WriteHHA(Assets, "test3.hha");
}
int
main(int ArgCount, char **Args)
{
InitializeFontDC();
WriteFonts();
WriteNonHero();
WriteHero();
WriteSounds();
}