2016-06-04 12:42:22 +00:00
|
|
|
#define STBI_FAILURE_USERMSG
|
|
|
|
#define STB_IMAGE_IMPLEMENTATION
|
|
|
|
#include <STB/stb_image.h>
|
|
|
|
|
2016-06-28 17:17:03 +00:00
|
|
|
#define SBTT_STATIC
|
|
|
|
#define STB_TRUETYPE_IMPLEMENTATION
|
|
|
|
#include <STB/stb_truetype.h>
|
|
|
|
|
2016-06-28 06:00:03 +00:00
|
|
|
#include "Dengine/Platform.h"
|
|
|
|
#include "Dengine/AssetManager.h"
|
2016-07-09 10:46:04 +00:00
|
|
|
#include "Dengine/Debug.h"
|
2016-06-28 06:00:03 +00:00
|
|
|
|
2016-07-07 16:45:37 +00:00
|
|
|
//#define WT_RENDER_FONT_FILE
|
2016-07-07 12:30:06 +00:00
|
|
|
#ifdef WT_RENDER_FONT_FILE
|
|
|
|
#define STB_IMAGE_WRITE_IMPLEMENTATION
|
|
|
|
#include <STB/stb_image_write.h>
|
|
|
|
#endif
|
|
|
|
|
2016-06-29 08:23:51 +00:00
|
|
|
Texture *asset_getTexture(AssetManager *assetManager, const enum TexList type)
|
2016-06-08 07:29:16 +00:00
|
|
|
{
|
2016-06-18 09:12:09 +00:00
|
|
|
if (type < texlist_count)
|
2016-06-29 08:23:51 +00:00
|
|
|
return &assetManager->textures[type];
|
2016-06-08 07:29:16 +00:00
|
|
|
|
2016-07-09 10:46:04 +00:00
|
|
|
#ifdef DENGINE_DEBUG
|
2016-07-08 06:09:18 +00:00
|
|
|
ASSERT(INVALID_CODE_PATH);
|
|
|
|
#endif
|
|
|
|
|
2016-06-17 16:01:43 +00:00
|
|
|
return NULL;
|
2016-06-08 07:29:16 +00:00
|
|
|
}
|
2016-06-04 12:42:22 +00:00
|
|
|
|
2016-06-29 08:23:51 +00:00
|
|
|
TexAtlas *asset_getTextureAtlas(AssetManager *assetManager, const enum TexList type)
|
2016-06-25 11:23:15 +00:00
|
|
|
{
|
|
|
|
if (type < texlist_count)
|
2016-06-29 08:23:51 +00:00
|
|
|
return &assetManager->texAtlas[type];
|
2016-06-25 11:23:15 +00:00
|
|
|
|
2016-07-09 10:46:04 +00:00
|
|
|
#ifdef DENGINE_DEBUG
|
2016-07-08 06:09:18 +00:00
|
|
|
ASSERT(INVALID_CODE_PATH);
|
|
|
|
#endif
|
2016-06-25 11:23:15 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2016-07-18 17:47:32 +00:00
|
|
|
Animation *asset_getAnim(AssetManager *assetManager, i32 type)
|
|
|
|
{
|
|
|
|
if (type < animlist_count)
|
|
|
|
return &assetManager->anims[type];
|
|
|
|
|
|
|
|
#ifdef DENGINE_DEBUG
|
|
|
|
ASSERT(INVALID_CODE_PATH);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2016-06-29 08:23:51 +00:00
|
|
|
const i32 asset_loadTextureImage(AssetManager *assetManager,
|
|
|
|
const char *const path, const enum TexList type)
|
2016-06-08 07:29:16 +00:00
|
|
|
{
|
|
|
|
/* Open the texture image */
|
|
|
|
i32 imgWidth, imgHeight, bytesPerPixel;
|
|
|
|
stbi_set_flip_vertically_on_load(TRUE);
|
2016-06-17 14:40:40 +00:00
|
|
|
u8 *image =
|
2016-06-17 16:01:43 +00:00
|
|
|
stbi_load(path, &imgWidth, &imgHeight, &bytesPerPixel, 0);
|
2016-06-08 07:29:16 +00:00
|
|
|
|
2016-07-12 08:11:31 +00:00
|
|
|
#ifdef DENGINE_DEBUG
|
|
|
|
if (imgWidth != imgHeight)
|
|
|
|
{
|
|
|
|
printf(
|
|
|
|
"worldTraveller_gameInit() warning: Sprite sheet is not square: "
|
|
|
|
"%dx%dpx\n", imgWidth, imgHeight);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2016-06-08 07:29:16 +00:00
|
|
|
if (!image)
|
|
|
|
{
|
2016-06-17 16:01:43 +00:00
|
|
|
printf("stdbi_load() failed: %s\n", stbi_failure_reason());
|
2016-06-08 07:29:16 +00:00
|
|
|
return -1;
|
2016-06-04 12:42:22 +00:00
|
|
|
}
|
|
|
|
|
2016-07-16 07:15:03 +00:00
|
|
|
Texture tex = texture_gen(CAST(GLuint)(imgWidth), CAST(GLuint)(imgHeight),
|
|
|
|
CAST(GLint)(bytesPerPixel), image);
|
2016-06-17 14:40:40 +00:00
|
|
|
glCheckError();
|
2016-06-08 07:29:16 +00:00
|
|
|
stbi_image_free(image);
|
|
|
|
|
2016-06-29 08:23:51 +00:00
|
|
|
assetManager->textures[type] = tex;
|
2016-06-08 07:29:16 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-06-29 08:23:51 +00:00
|
|
|
Shader *asset_getShader(AssetManager *assetManager, const enum ShaderList type)
|
2016-06-08 07:29:16 +00:00
|
|
|
{
|
2016-06-18 09:12:09 +00:00
|
|
|
if (type < shaderlist_count)
|
2016-06-29 08:23:51 +00:00
|
|
|
return &assetManager->shaders[type];
|
2016-06-08 07:29:16 +00:00
|
|
|
|
2016-07-09 10:46:04 +00:00
|
|
|
#ifdef DENGINE_DEBUG
|
2016-07-08 06:09:18 +00:00
|
|
|
ASSERT(INVALID_CODE_PATH);
|
|
|
|
#endif
|
2016-06-18 09:12:09 +00:00
|
|
|
return NULL;
|
2016-06-08 07:29:16 +00:00
|
|
|
}
|
|
|
|
|
2016-07-19 11:19:26 +00:00
|
|
|
INTERNAL GLuint createShaderFromPath(MemoryArena *arena, const char *const path, GLuint shadertype)
|
2016-06-08 07:29:16 +00:00
|
|
|
{
|
2016-06-28 17:17:03 +00:00
|
|
|
PlatformFileRead file = {0};
|
2016-06-08 07:29:16 +00:00
|
|
|
|
2016-07-19 11:19:26 +00:00
|
|
|
i32 status = platform_readFileToBuffer(arena, path, &file);
|
2016-06-17 16:01:43 +00:00
|
|
|
if (status)
|
|
|
|
return status;
|
2016-06-08 07:29:16 +00:00
|
|
|
|
2016-06-17 16:01:43 +00:00
|
|
|
const GLchar *source = CAST(char *)file.buffer;
|
2016-06-08 07:29:16 +00:00
|
|
|
|
|
|
|
GLuint result = glCreateShader(shadertype);
|
|
|
|
glShaderSource(result, 1, &source, NULL);
|
|
|
|
glCompileShader(result);
|
|
|
|
|
|
|
|
GLint success;
|
|
|
|
GLchar infoLog[512];
|
|
|
|
glGetShaderiv(result, GL_COMPILE_STATUS, &success);
|
|
|
|
if (!success)
|
|
|
|
{
|
|
|
|
glGetShaderInfoLog(result, 512, NULL, infoLog);
|
2016-06-17 16:01:43 +00:00
|
|
|
printf("glCompileShader() failed: %s\n", infoLog);
|
2016-06-04 12:42:22 +00:00
|
|
|
}
|
|
|
|
|
2016-07-19 11:19:26 +00:00
|
|
|
platform_closeFileRead(arena, &file);
|
2016-06-17 16:01:43 +00:00
|
|
|
|
2016-06-08 07:29:16 +00:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2016-07-16 07:15:03 +00:00
|
|
|
INTERNAL i32 shaderLoadProgram(Shader *const shader, const GLuint vertexShader,
|
|
|
|
const GLuint fragmentShader)
|
|
|
|
{
|
|
|
|
shader->id = glCreateProgram();
|
|
|
|
glAttachShader(shader->id, vertexShader);
|
|
|
|
glAttachShader(shader->id, fragmentShader);
|
|
|
|
glLinkProgram(shader->id);
|
|
|
|
|
|
|
|
glDeleteShader(fragmentShader);
|
|
|
|
glDeleteShader(vertexShader);
|
|
|
|
|
|
|
|
GLint success;
|
|
|
|
GLchar infoLog[512];
|
|
|
|
glGetProgramiv(shader->id, GL_LINK_STATUS, &success);
|
|
|
|
if (!success)
|
|
|
|
{
|
|
|
|
glGetProgramInfoLog(shader->id, 512, NULL, infoLog);
|
|
|
|
printf("glLinkProgram failed: %s\n", infoLog);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-07-19 11:19:26 +00:00
|
|
|
const i32 asset_loadShaderFiles(AssetManager *assetManager, MemoryArena *arena,
|
2016-06-29 08:23:51 +00:00
|
|
|
const char *const vertexPath,
|
2016-06-17 16:01:43 +00:00
|
|
|
const char *const fragmentPath,
|
2016-06-18 09:12:09 +00:00
|
|
|
const enum ShaderList type)
|
2016-06-08 07:29:16 +00:00
|
|
|
{
|
2016-07-19 11:19:26 +00:00
|
|
|
GLuint vertexShader = createShaderFromPath(arena, vertexPath, GL_VERTEX_SHADER);
|
2016-06-17 14:40:40 +00:00
|
|
|
GLuint fragmentShader =
|
2016-07-19 11:19:26 +00:00
|
|
|
createShaderFromPath(arena, fragmentPath, GL_FRAGMENT_SHADER);
|
2016-06-08 07:29:16 +00:00
|
|
|
|
|
|
|
Shader shader;
|
2016-07-16 07:15:03 +00:00
|
|
|
i32 result = shaderLoadProgram(&shader, vertexShader, fragmentShader);
|
2016-06-08 07:29:16 +00:00
|
|
|
if (result)
|
|
|
|
return result;
|
|
|
|
|
2016-06-29 08:23:51 +00:00
|
|
|
assetManager->shaders[type] = shader;
|
2016-06-08 07:29:16 +00:00
|
|
|
return 0;
|
|
|
|
}
|
2016-06-28 17:17:03 +00:00
|
|
|
|
2016-06-29 08:23:51 +00:00
|
|
|
/* Individual glyph bitmap generated from STB used for creating a font sheet */
|
|
|
|
typedef struct GlyphBitmap
|
|
|
|
{
|
2016-07-17 13:45:59 +00:00
|
|
|
v2 dimensions;
|
2016-06-29 08:23:51 +00:00
|
|
|
u32 *pixels;
|
|
|
|
i32 codepoint;
|
|
|
|
} GlyphBitmap;
|
|
|
|
|
2016-07-19 11:19:26 +00:00
|
|
|
const i32 asset_loadTTFont(AssetManager *assetManager, MemoryArena *arena,
|
|
|
|
const char *filePath)
|
2016-06-28 17:17:03 +00:00
|
|
|
{
|
|
|
|
PlatformFileRead fontFileRead = {0};
|
2016-07-19 11:19:26 +00:00
|
|
|
platform_readFileToBuffer(arena, filePath, &fontFileRead);
|
2016-06-28 17:17:03 +00:00
|
|
|
|
|
|
|
stbtt_fontinfo fontInfo = {0};
|
|
|
|
stbtt_InitFont(&fontInfo, fontFileRead.buffer,
|
|
|
|
stbtt_GetFontOffsetForIndex(fontFileRead.buffer, 0));
|
|
|
|
|
2016-06-29 14:14:07 +00:00
|
|
|
/* Initialise Assetmanager Font */
|
2016-06-29 10:44:35 +00:00
|
|
|
Font *font = &assetManager->font;
|
|
|
|
font->codepointRange = V2i(32, 127);
|
2016-07-17 13:45:59 +00:00
|
|
|
v2 codepointRange = font->codepointRange;
|
|
|
|
const i32 numGlyphs = CAST(i32)(codepointRange.y - codepointRange.x);
|
2016-06-28 17:17:03 +00:00
|
|
|
|
2016-07-19 11:19:26 +00:00
|
|
|
GlyphBitmap *glyphBitmaps =
|
|
|
|
PLATFORM_MEM_ALLOC(arena, numGlyphs, GlyphBitmap);
|
2016-07-17 13:45:59 +00:00
|
|
|
v2 largestGlyphDimension = V2(0, 0);
|
2016-06-28 17:17:03 +00:00
|
|
|
|
2016-07-15 16:34:44 +00:00
|
|
|
const f32 targetFontHeight = 15.0f;
|
2016-06-28 17:17:03 +00:00
|
|
|
f32 scaleY = stbtt_ScaleForPixelHeight(&fontInfo, targetFontHeight);
|
|
|
|
|
2016-06-29 14:14:07 +00:00
|
|
|
i32 ascent, descent, lineGap;
|
|
|
|
stbtt_GetFontVMetrics(&fontInfo, &ascent, &descent, &lineGap);
|
|
|
|
|
|
|
|
ascent = CAST(i32)(ascent * scaleY);
|
|
|
|
descent = CAST(i32)(descent * scaleY);
|
|
|
|
lineGap = CAST(i32)(lineGap * scaleY);
|
|
|
|
|
|
|
|
font->metrics = CAST(FontMetrics){ascent, descent, lineGap};
|
|
|
|
|
2016-07-19 11:19:26 +00:00
|
|
|
font->charMetrics = PLATFORM_MEM_ALLOC(arena, numGlyphs, CharMetrics);
|
2016-06-29 14:14:07 +00:00
|
|
|
|
2016-06-28 17:17:03 +00:00
|
|
|
/* Use STB_TrueType to generate a series of bitmap characters */
|
2016-06-29 14:14:07 +00:00
|
|
|
i32 glyphIndex = 0;
|
2016-07-17 13:45:59 +00:00
|
|
|
for (i32 codepoint = CAST(i32) codepointRange.x;
|
|
|
|
codepoint < CAST(i32) codepointRange.y; codepoint++)
|
2016-06-28 17:17:03 +00:00
|
|
|
{
|
|
|
|
// NOTE(doyle): ScaleX if not specified, is then calculated based on the
|
|
|
|
// ScaleY component
|
|
|
|
i32 width, height, xOffset, yOffset;
|
2016-06-29 08:23:51 +00:00
|
|
|
u8 *const monoBitmap =
|
2016-06-28 17:17:03 +00:00
|
|
|
stbtt_GetCodepointBitmap(&fontInfo, 0, scaleY, codepoint, &width,
|
|
|
|
&height, &xOffset, &yOffset);
|
|
|
|
|
|
|
|
u8 *source = monoBitmap;
|
2016-07-19 11:19:26 +00:00
|
|
|
u32 *colorBitmap = PLATFORM_MEM_ALLOC(arena, width * height, u32);
|
2016-06-28 17:17:03 +00:00
|
|
|
u32 *dest = colorBitmap;
|
|
|
|
|
|
|
|
// NOTE(doyle): STB generates 1 byte per pixel bitmaps, we use 4bpp, so
|
|
|
|
// duplicate the alpha byte (essentially) for all RGBA components
|
|
|
|
for (i32 y = 0; y < height; y++)
|
|
|
|
{
|
|
|
|
for (i32 x = 0; x < width; x++)
|
|
|
|
{
|
|
|
|
u8 monoByte = *source++;
|
|
|
|
*dest++ = (monoByte << 24) | (monoByte << 16) |
|
|
|
|
(monoByte << 8) | (monoByte << 0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-06-29 14:14:07 +00:00
|
|
|
/* Get individual character metrics */
|
|
|
|
i32 advance, leftSideBearing;
|
|
|
|
stbtt_GetCodepointHMetrics(&fontInfo, codepoint, &advance,
|
|
|
|
&leftSideBearing);
|
|
|
|
|
|
|
|
advance = CAST(i32)(advance * scaleY);
|
|
|
|
leftSideBearing = CAST(i32)(leftSideBearing * scaleY);
|
|
|
|
|
|
|
|
font->charMetrics[glyphIndex] =
|
|
|
|
CAST(CharMetrics){advance, leftSideBearing, NULL,
|
|
|
|
V2i(xOffset, yOffset), V2i(width, height)};
|
|
|
|
|
|
|
|
/* Store bitmap into intermediate storage */
|
2016-06-28 17:17:03 +00:00
|
|
|
stbtt_FreeBitmap(monoBitmap, NULL);
|
2016-06-29 14:14:07 +00:00
|
|
|
|
|
|
|
// TODO(doyle): Dimensions is used twice in font->trueSize and this
|
2016-06-28 17:17:03 +00:00
|
|
|
glyphBitmaps[glyphIndex].dimensions = V2i(width, height);
|
2016-06-29 08:23:51 +00:00
|
|
|
glyphBitmaps[glyphIndex].codepoint = codepoint;
|
|
|
|
glyphBitmaps[glyphIndex++].pixels = colorBitmap;
|
2016-06-28 17:17:03 +00:00
|
|
|
|
2016-07-17 13:45:59 +00:00
|
|
|
if (height > CAST(f32)largestGlyphDimension.h)
|
|
|
|
largestGlyphDimension.h = CAST(f32)height;
|
|
|
|
if (width > CAST(f32)largestGlyphDimension.w)
|
|
|
|
largestGlyphDimension.w = CAST(f32)width;
|
2016-06-28 17:17:03 +00:00
|
|
|
|
2016-07-09 10:46:04 +00:00
|
|
|
#ifdef DENGINE_DEBUG
|
2016-06-28 17:17:03 +00:00
|
|
|
if ((largestGlyphDimension.h - CAST(i32)targetFontHeight) >= 50)
|
|
|
|
{
|
|
|
|
printf(
|
|
|
|
"asset_loadTTFont() warning: The loaded font file has a glyph "
|
|
|
|
"considerably larger than our target .. font packing is "
|
|
|
|
"unoptimal\n");
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* NOTE(doyle): Use rasterised TTF bitmap-characters combine them all into
|
|
|
|
* one bitmap as an atlas. We determine how many glyphs we can fit per row
|
2016-07-07 12:44:22 +00:00
|
|
|
* by determining the largest glyph size we have.
|
2016-06-28 17:17:03 +00:00
|
|
|
*
|
|
|
|
* For the amount of glyphs we fit per row, we iterate through them and
|
|
|
|
* write each row of the glyph adjacent to the next until we finish writing
|
|
|
|
* all pixels for the glyphs, then move onto the next set of glyphs.
|
|
|
|
*/
|
2016-07-07 12:44:22 +00:00
|
|
|
font->maxSize = largestGlyphDimension;
|
2016-07-17 13:45:59 +00:00
|
|
|
i32 glyphsPerRow = (MAX_TEXTURE_SIZE / CAST(i32)font->maxSize.w);
|
2016-06-28 17:17:03 +00:00
|
|
|
|
2016-07-09 10:46:04 +00:00
|
|
|
#ifdef DENGINE_DEBUG
|
2016-07-17 13:45:59 +00:00
|
|
|
i32 glyphsPerCol = MAX_TEXTURE_SIZE / CAST(i32)font->maxSize.h;
|
2016-06-28 17:17:03 +00:00
|
|
|
if ((glyphsPerRow * glyphsPerCol) <= numGlyphs)
|
|
|
|
{
|
|
|
|
printf(
|
|
|
|
"asset_loadTTFont() warning: The target font height creates a "
|
|
|
|
"glyph sheet that exceeds the available space!");
|
2016-07-08 06:09:18 +00:00
|
|
|
|
|
|
|
ASSERT(INVALID_CODE_PATH);
|
2016-06-28 17:17:03 +00:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2016-07-14 13:15:42 +00:00
|
|
|
i32 bitmapSize = SQUARED(TARGET_TEXTURE_SIZE) * TARGET_BYTES_PER_PIXEL;
|
2016-07-19 11:19:26 +00:00
|
|
|
u32 *fontBitmap = PLATFORM_MEM_ALLOC(arena, bitmapSize, u32);
|
2016-06-29 08:23:51 +00:00
|
|
|
const i32 pitch = MAX_TEXTURE_SIZE * TARGET_BYTES_PER_PIXEL;
|
2016-06-28 17:17:03 +00:00
|
|
|
|
|
|
|
// Check value to determine when a row of glyphs is completely printed
|
|
|
|
i32 verticalPixelsBlitted = 0;
|
|
|
|
|
|
|
|
i32 startingGlyphIndex = 0;
|
|
|
|
i32 glyphsRemaining = numGlyphs;
|
2016-07-15 08:34:23 +00:00
|
|
|
i32 glyphsOnCurrRow =
|
|
|
|
(glyphsPerRow < glyphsRemaining) ? glyphsPerRow : glyphsRemaining;
|
2016-06-28 17:17:03 +00:00
|
|
|
|
2016-07-07 12:30:06 +00:00
|
|
|
// TODO(doyle): We copy over the bitmap direct to the font sheet, should we
|
|
|
|
// align the baselines up so we don't need to do baseline adjusting at
|
|
|
|
// render?
|
2016-06-29 08:23:51 +00:00
|
|
|
i32 atlasIndex = 0;
|
|
|
|
for (i32 row = 0; row < MAX_TEXTURE_SIZE; row++)
|
2016-06-28 17:17:03 +00:00
|
|
|
{
|
|
|
|
u32 *destRow = fontBitmap + (row * MAX_TEXTURE_SIZE);
|
|
|
|
for (i32 glyphIndex = 0; glyphIndex < glyphsOnCurrRow;
|
|
|
|
glyphIndex++)
|
|
|
|
{
|
|
|
|
i32 activeGlyphIndex = startingGlyphIndex + glyphIndex;
|
|
|
|
|
|
|
|
GlyphBitmap activeGlyph = glyphBitmaps[activeGlyphIndex];
|
2016-06-29 08:23:51 +00:00
|
|
|
|
|
|
|
/* Store the location of glyph into atlas */
|
|
|
|
if (verticalPixelsBlitted == 0)
|
|
|
|
{
|
|
|
|
TexAtlas *fontAtlas = &assetManager->texAtlas[texlist_font];
|
2016-07-09 10:46:04 +00:00
|
|
|
#ifdef DENGINE_DEBUG
|
2016-06-29 08:23:51 +00:00
|
|
|
ASSERT(activeGlyph.codepoint < ARRAY_COUNT(fontAtlas->texRect));
|
|
|
|
#endif
|
|
|
|
|
2016-06-29 14:14:07 +00:00
|
|
|
v2 origin =
|
|
|
|
V2(CAST(f32)(glyphIndex * font->maxSize.w), CAST(f32) row);
|
|
|
|
#if 1
|
|
|
|
fontAtlas->texRect[atlasIndex++] =
|
2016-07-16 07:15:03 +00:00
|
|
|
math_getRect(origin, V2(CAST(f32) font->maxSize.w,
|
|
|
|
CAST(f32) font->maxSize.h));
|
2016-06-29 14:14:07 +00:00
|
|
|
#else
|
|
|
|
v2i fontSize =
|
|
|
|
font->charMetrics[activeGlyph.codepoint - 32].trueSize;
|
2016-07-16 07:15:03 +00:00
|
|
|
fontAtlas->texRect[atlasIndex++] = math_getRect(
|
|
|
|
origin, V2(CAST(f32) fontSize.x, CAST(f32) fontSize.y));
|
2016-06-29 14:14:07 +00:00
|
|
|
#endif
|
2016-06-29 08:23:51 +00:00
|
|
|
}
|
2016-06-28 17:17:03 +00:00
|
|
|
|
|
|
|
/* Copy over exactly one row of pixels */
|
2016-07-17 13:45:59 +00:00
|
|
|
i32 numPixelsToPad = CAST(i32)font->maxSize.w;
|
2016-06-28 17:17:03 +00:00
|
|
|
if (verticalPixelsBlitted < activeGlyph.dimensions.h)
|
|
|
|
{
|
|
|
|
const i32 srcPitch =
|
2016-07-17 13:45:59 +00:00
|
|
|
CAST(i32) activeGlyph.dimensions.w * verticalPixelsBlitted;
|
2016-06-28 17:17:03 +00:00
|
|
|
const u32 *src = activeGlyph.pixels + srcPitch;
|
|
|
|
|
2016-07-17 13:45:59 +00:00
|
|
|
const i32 numPixelsToCopy = CAST(i32)activeGlyph.dimensions.w;
|
2016-06-28 17:17:03 +00:00
|
|
|
|
|
|
|
for (i32 count = 0; count < numPixelsToCopy; count++)
|
|
|
|
*destRow++ = *src++;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* NOTE(doyle): If the glyph is smaller than largest glyph
|
|
|
|
* available size, don't advance src pointer any further
|
|
|
|
* (NULL/mixes up rows), instead just advance the final bitmap
|
|
|
|
* pointer by the remaining distance
|
|
|
|
*/
|
2016-07-17 13:45:59 +00:00
|
|
|
numPixelsToPad =
|
|
|
|
CAST(i32)(font->maxSize.w - activeGlyph.dimensions.w);
|
2016-06-28 17:17:03 +00:00
|
|
|
}
|
|
|
|
destRow += numPixelsToPad;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* A row of glyphs has been fully formed on the atlas */
|
2016-06-29 14:14:07 +00:00
|
|
|
if (verticalPixelsBlitted++ >= font->maxSize.h)
|
2016-06-28 17:17:03 +00:00
|
|
|
{
|
|
|
|
verticalPixelsBlitted = 0;
|
|
|
|
startingGlyphIndex += glyphsPerRow;
|
|
|
|
|
|
|
|
glyphsRemaining -= glyphsPerRow;
|
|
|
|
|
|
|
|
if (glyphsRemaining <= 0)
|
|
|
|
break;
|
|
|
|
else if (glyphsRemaining <= glyphsPerRow)
|
|
|
|
{
|
|
|
|
// NOTE(doyle): This allows us to modify the glyph iterator to
|
|
|
|
// prevent over-running of the available glyphs
|
|
|
|
glyphsOnCurrRow = glyphsRemaining;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-07-16 07:15:03 +00:00
|
|
|
Texture tex = texture_gen(MAX_TEXTURE_SIZE, MAX_TEXTURE_SIZE, 4,
|
|
|
|
CAST(u8 *) fontBitmap);
|
2016-06-29 08:23:51 +00:00
|
|
|
assetManager->textures[texlist_font] = tex;
|
2016-06-29 10:44:35 +00:00
|
|
|
|
2016-07-07 12:30:06 +00:00
|
|
|
#ifdef WT_RENDER_FONT_FILE
|
|
|
|
/* save out a 4 channel image */
|
|
|
|
stbi_write_png("out.png", MAX_TEXTURE_SIZE, MAX_TEXTURE_SIZE, 4, fontBitmap,
|
|
|
|
MAX_TEXTURE_SIZE * 4);
|
|
|
|
#endif
|
2016-07-19 11:19:26 +00:00
|
|
|
PLATFORM_MEM_FREE(arena, fontBitmap, bitmapSize);
|
2016-07-07 12:30:06 +00:00
|
|
|
|
2016-06-29 10:44:35 +00:00
|
|
|
font->tex = &assetManager->textures[texlist_font];
|
|
|
|
font->atlas = &assetManager->texAtlas[texlist_font];
|
2016-06-28 17:17:03 +00:00
|
|
|
|
|
|
|
for (i32 i = 0; i < numGlyphs; i++)
|
2016-07-09 11:42:36 +00:00
|
|
|
{
|
2016-07-17 13:45:59 +00:00
|
|
|
i32 glyphBitmapSizeInBytes = CAST(i32) glyphBitmaps[i].dimensions.w *
|
|
|
|
CAST(i32) glyphBitmaps[i].dimensions.h *
|
|
|
|
sizeof(u32);
|
2016-07-19 11:19:26 +00:00
|
|
|
PLATFORM_MEM_FREE(arena, glyphBitmaps[i].pixels, glyphBitmapSizeInBytes);
|
2016-07-09 11:42:36 +00:00
|
|
|
}
|
2016-06-28 17:17:03 +00:00
|
|
|
|
2016-07-19 11:19:26 +00:00
|
|
|
PLATFORM_MEM_FREE(arena, glyphBitmaps, numGlyphs * sizeof(GlyphBitmap));
|
|
|
|
platform_closeFileRead(arena, &fontFileRead);
|
2016-06-28 17:17:03 +00:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2016-07-18 17:47:32 +00:00
|
|
|
|
2016-07-19 11:19:26 +00:00
|
|
|
void asset_addAnimation(AssetManager *assetManager, MemoryArena *arena,
|
|
|
|
i32 texId, i32 animId, i32 *frameIndex, i32 numFrames,
|
|
|
|
f32 frameDuration)
|
2016-07-18 17:47:32 +00:00
|
|
|
{
|
|
|
|
#ifdef DENGINE_DEBUG
|
2016-07-19 03:32:31 +00:00
|
|
|
ASSERT(assetManager && frameIndex)
|
|
|
|
ASSERT(!assetManager->anims[animId].frameIndex);
|
2016-07-18 17:47:32 +00:00
|
|
|
#endif
|
|
|
|
|
|
|
|
Animation anim = {0};
|
|
|
|
anim.atlas = asset_getTextureAtlas(assetManager, texId);
|
|
|
|
|
2016-07-19 11:19:26 +00:00
|
|
|
anim.frameIndex = PLATFORM_MEM_ALLOC(arena, numFrames, i32);
|
2016-07-19 03:32:31 +00:00
|
|
|
for (i32 i = 0; i < numFrames; i++) anim.frameIndex[i] = frameIndex[i];
|
2016-07-18 17:47:32 +00:00
|
|
|
|
|
|
|
anim.numFrames = numFrames;
|
|
|
|
anim.frameDuration = frameDuration;
|
|
|
|
|
|
|
|
assetManager->anims[animId] = anim;
|
|
|
|
}
|