text:
cmake_minimum_required (VERSION 3.0.2)
project (LibPHX)
set (CMAKE_BUILD_TYPE Release)
include (script/build/Shared.cmake)

# ------------------------------------------------------------------------------

file (GLOB HEADERS "include/*.h")
file (GLOB SOURCES "src/*.cpp")

add_library (phx SHARED ${SOURCES} ${HEADERS})
phx_configure_output_dir (phx)
phx_configure_target_properties (phx)

target_compile_definitions (phx PRIVATE LIBPHX_BUILDING=1)
target_include_directories (phx PUBLIC "include")
target_include_directories (phx PUBLIC "ext/include")
target_include_directories (phx PUBLIC "ext/include/bullet")
target_link_directories (phx PUBLIC "ext/lib/${PLATARCH}")

set_target_properties (phx PROPERTIES
  PREFIX "lib"
  OUTPUT_NAME_DEBUG "phx${ARCH}d"
  OUTPUT_NAME_RELEASE "phx${ARCH}r"
  OUTPUT_NAME_RELWITHDEBINFO "phx${ARCH}"
  OUTPUT_NAME_MINSIZEREL "phx${ARCH}rm")

# ------------------------------------------------------------------------------

if (WINDOWS)

  # register an external shared library
  function (add_extlib name lib dll)
    add_library (${name} SHARED IMPORTED GLOBAL)
    set_property (TARGET ${name} PROPERTY IMPORTED_IMPLIB "${CMAKE_CURRENT_SOURCE_DIR}/ext/lib/${PLATARCH}/${lib}")
    set_property (TARGET ${name} PROPERTY IMPORTED_LOCATION "${CMAKE_CURRENT_SOURCE_DIR}/ext/lib/${PLATARCH}/${dll}")
  endfunction ()

  # link target to an external library; also add a post-build step to
  # copy the library DLL into the bin folder
  function (target_link_extlib target lib)
    target_link_libraries (${target} ${lib})
    add_custom_command (TARGET ${target} POST_BUILD
      COMMAND ${CMAKE_COMMAND} -E copy_if_different
      $<TARGET_FILE:${lib}> $<TARGET_FILE_DIR:${target}>
    )
  endfunction ()

  add_extlib (glew glew32.lib glew32.dll)
  add_extlib (lz4 liblz4.lib liblz4.dll)
  add_extlib (lua51 lua51.lib lua51.dll)
  add_extlib (sdl2 SDL2.lib SDL2.dll)

  if (ARCH EQUAL "32")
    add_extlib (fmod fmodL_vc.lib fmodL.dll)
    add_extlib (fmodstudio fmodstudioL_vc.lib fmodstudioL.dll)
  else ()
    add_extlib (fmod fmodL64_vc.lib fmodL64.dll)
    add_extlib (fmodstudio fmodstudioL64_vc.lib fmodstudioL64.dll)
  endif ()

elseif (LINUX)

  # TODO

endif ()

# ------------------------------------------------------------------------------

if (WINDOWS)

  target_link_libraries (phx
    opengl32.lib
    user32.lib winmm.lib Ws2_32.lib
    freetype.lib
    BulletCollision.lib BulletDynamics.lib LinearMath.lib)

  target_link_extlib (phx glew)
  target_link_extlib (phx sdl2)
  target_link_extlib (phx lz4)
  target_link_extlib (phx lua51)
  target_link_extlib (phx fmod)
  target_link_extlib (phx fmodstudio)

elseif (LINUX)

  target_link_libraries (phx
    GL
    GLEW
    dl
    SDL2
    freetype
    luajit-5.1
    lz4
    fmod fmodstudio
    BulletCollision BulletDynamics)

  # Add ext/lib/{$PLATARCH} to runtime library search path
  target_link_options (phx PRIVATE "-Wl,-rpath,../ext/lib/${PLATARCH}")

endif ()

repo_name: JoshParnell/libphx

repo_language: C

mime_type: text/x-c++


text:
# LibPHX

LibPHX is a lightweight, (mostly) C-based game engine developed for the cancelled space simulation game Limit Theory. LibPHX focuses on implementing the core features necessary to build performant 3D games without including the kitchen sink.

Rather than acting as a framework like most modern game engines, the LibPHX philosophy is to provide a game engine *as a library* -- that is, control flow is in the hands of the user, not the engine. LibPHX is designed specifically to be controlled from Lua scripts using the LuaJIT runtime. The engine's C interface, combined with LuaJIT's FFI technology, allows for scripts to make zero-overhead calls into the engine, thus allowing *the majority* of game logic and control to remain in script. Keeping control flow in the hands of Lua opens up a world of fast iteration and fully-moddable game logic, while achieving maximal performance by delegating heavy computation to engine-side functions.

LibPHX is now abandoned and mostly undocumented, however you can see examples of its usage in https://github.com/JoshParnell/ltheory.


text:
#include "GamepadAxis.h"
#include "SDL.h"

const GamepadAxis GamepadAxis_BEGIN    = SDL_CONTROLLER_AXIS_LEFTX;
const GamepadAxis GamepadAxis_LeftX    = SDL_CONTROLLER_AXIS_LEFTX;
const GamepadAxis GamepadAxis_LeftY    = SDL_CONTROLLER_AXIS_LEFTY;
const GamepadAxis GamepadAxis_RightX   = SDL_CONTROLLER_AXIS_RIGHTX;
const GamepadAxis GamepadAxis_RightY   = SDL_CONTROLLER_AXIS_RIGHTY;
const GamepadAxis GamepadAxis_LTrigger = SDL_CONTROLLER_AXIS_TRIGGERLEFT;
const GamepadAxis GamepadAxis_RTrigger = SDL_CONTROLLER_AXIS_TRIGGERRIGHT;
const GamepadAxis GamepadAxis_END      = SDL_CONTROLLER_AXIS_TRIGGERRIGHT;


text:
#include "DataFormat.h"
#include "PhxMemory.h"
#include "Mesh.h"
#include "PixelFormat.h"
#include "SDF.h"
#include "Tex3D.h"
#include "Vec3.h"

struct Cell {
  float value;
  Vec3f normal;
};

struct SDF {
  Vec3i size;
  Cell* data;
};

SDF* SDF_Create (int sx, int sy, int sz) {
  SDF* self = MemNew(SDF);
  self->size = Vec3i_Create(sx, sy, sz);
  self->data = MemNewArray(Cell, sx * sy * sz);
  MemZero(self->data, sizeof(Cell) * sx * sy * sz);
  return self;
}

SDF* SDF_FromTex3D (Tex3D* tex) {
  SDF* self = MemNew(SDF);
  Tex3D_GetSize(tex, &self->size);
  self->data = MemNewArray(Cell, self->size.x * self->size.y * self->size.z);
  Tex3D_GetData(tex, self->data, PixelFormat_RGBA, DataFormat_Float);
  return self;
}

void SDF_Free (SDF* self) {
  MemFree(self->data);
  MemFree(self);
}

/* Triangulate using Surface Nets. */
Mesh* SDF_ToMesh (SDF* self) {
  Mesh* mesh = Mesh_Create();
  Vec3i const cells =  { self->size.x - 1, self->size.y - 1, self->size.z - 1 };
  Vec3f const cellsF = { (float)cells.x, (float)cells.y, (float)cells.z };
  Vec3i const stride = { 1, self->size.x, self->size.x * self->size.y };
  Vec3i const cellStride = { 1, cells.x, cells.x * cells.y };
  int* indices = MemNewArray(int, cells.x * cells.y * cells.z);

  Vec3f const vp[8] = {
    { 0, 0, 0 }, { 1, 0, 0 }, { 0, 1, 0 }, { 1, 1, 0 },
    { 0, 0, 1 }, { 1, 0, 1 }, { 0, 1, 1 }, { 1, 1, 1 },
  };

  int const edgeTable[12][2] = {
    { 0, 1 }, { 2, 3 }, { 4, 5 }, { 6, 7 },
    { 0, 2 }, { 1, 3 }, { 4, 6 }, { 5, 7 },
    { 0, 4 }, { 1, 5 }, { 2, 6 }, { 3, 7 },
  };

  for (int z = 0; z < cells.z; ++z) {
    float z0 = (float)z / (float)cells.z;

    for (int y = 0; y < cells.y; ++y) {
      float y0 = (float)y / (float)cells.y;

      for (int x = 0; x < cells.x; ++x) {
        float x0 = (float)x / (float)cells.x;
        Vec3i cell = { x, y, z };
        int cellIndex = Vec3i_Dots(cellStride, x, y, z);

        Cell const* base = self->data + Vec3i_Dots(stride, x, y, z);
        Cell const* v[8] = {
          base,
          base + stride.x,
          base + stride.y,
          base + stride.x + stride.y,
          base + stride.z,
          base + stride.z + stride.x,
          base + stride.z + stride.y,
          base + stride.z + stride.y + stride.x,
        };

        int mask = 0;
        mask |= v[0]->value > 0 ? 0x01 : 0x00;
        mask |= v[1]->value > 0 ? 0x02 : 0x00;
        mask |= v[2]->value > 0 ? 0x04 : 0x00;
        mask |= v[3]->value > 0 ? 0x08 : 0x00;
        mask |= v[4]->value > 0 ? 0x10 : 0x00;
        mask |= v[5]->value > 0 ? 0x20 : 0x00;
        mask |= v[6]->value > 0 ? 0x40 : 0x00;
        mask |= v[7]->value > 0 ? 0x80 : 0x00;

        if (mask == 0x00 || mask == 0xFF) {
          indices[cellIndex] = -1;
          continue;
        }

        float tw = 0.0f;
        Vec3f offset = { 0, 0, 0 };
        Vec3f n = { 0, 0, 0 };

        /* Generate vertex. */
        for (int i = 0; i < 12; ++i) {
          int i0 = edgeTable[i][0];
          int i1 = edgeTable[i][1];
          Cell const* v0 = v[i0];
          Cell const* v1 = v[i1];
          if ((v0->value > 0) == (v1->value > 0))
            continue;

          float t = Saturate(v0->value / (v0->value - v1->value));
          Vec3f_IAdd(&offset, Vec3f_Lerp(vp[i0], vp[i1], t));
          Vec3f_IAdd(&n, Vec3f_Lerp(v0->normal, v1->normal, t));
          tw += 1.0f;
        }

        Vec3f_IDivs(&offset, tw);
        n = Vec3f_SNormalize(n);
        Vec3f p = Vec3f_Add(Vec3f_Create(x0, y0, z0), Vec3f_Div(offset, cellsF));
        p = Vec3f_Subs(Vec3f_Muls(p, 2.0f), 1.0f);

        indices[cellIndex] = Mesh_GetVertexCount(mesh);
        Mesh_AddVertex(mesh, p.x, p.y, p.z, n.x, n.y, n.z, 1, 0);

        /* Generate faces. */
        for (int i = 0; i < 3; ++i) {
          int j = (i + 1) % 3;
          int k = (i + 2) % 3;
          if ((&cell.x)[j] == 0 || (&cell.x)[k] == 0)
            continue;

          int du = (&cellStride.x)[j];
          int dv = (&cellStride.x)[k];

          int i0 = indices[cellIndex];
          int i1 = indices[cellIndex - du];
          int i2 = indices[cellIndex - du - dv];
          int i3 = indices[cellIndex - dv];

          if (i1 < 0 || i2 < 0 || i3 < 0)
            continue;

          if (v[0]->value > 0)
            Mesh_AddQuad(mesh, i0, i3, i2, i1);
          else
            Mesh_AddQuad(mesh, i0, i1, i2, i3);
        }
      }
    }
  }

  MemFree(indices);
  return mesh;
}

void SDF_Clear (SDF* self, float value) {
  uint64 size = self->size.x * self->size.y * self->size.z;
  Cell* pCell = self->data;
  for (uint64 i = 0; i < size; ++i)
    (*pCell++).value = value;
}

void SDF_ComputeNormals (SDF* self) {
  Vec3i const stride = { 1, self->size.x, self->size.x * self->size.y };
  for (int z = 1; z < self->size.z - 1; ++z)
  for (int y = 1; y < self->size.y - 1; ++y)
  for (int x = 1; x < self->size.x - 1; ++x) {
    Cell* cell = self->data + x * stride.x + y * stride.y + z * stride.z;
    Cell const* x0 = cell - stride.x; Cell const* x1 = cell + stride.x;
    Cell const* y0 = cell - stride.y; Cell const* y1 = cell + stride.y;
    Cell const* z0 = cell - stride.z; Cell const* z1 = cell + stride.z;
    cell->normal = Vec3f_SNormalize(Vec3f_Create(
      x1->value - x0->value,
      y1->value - y0->value,
      z1->value - z0->value));
  }
}

void SDF_Set (SDF* self, int x, int y, int z, float value) {
  self->data[x + self->size.x * (y + self->size.y * z)].value = value;
}

void SDF_SetNormal (SDF* self, int x, int y, int z, Vec3f const* normal) {
  self->data[x + self->size.x * (y + self->size.y * z)].normal = *normal;
}


text:
#include "Directory.h"
#include "File.h"
#include "PhxMemory.h"
#include "PhxString.h"

#include <stdio.h>

#if WINDOWS
  #include <windows.h>
  #include <direct.h>
  #include "windirent.h"
  #define ChangeDir _chdir
  #define GetCWD _getcwd
  #define RemoveDir _rmdir
#elif POSIX
  #include <dirent.h>
  #include <unistd.h>
  #include <sys/stat.h>
  #define ChangeDir chdir
  #define GetCWD getcwd
  #define RemoveDir rmdir
#endif

struct Directory {
  DIR* handle;
};

Directory* Directory_Open (cstr path) {
  DIR* dir = opendir(path);
  if (!dir)
    return 0;
  Directory* self = MemNew(Directory);
  self->handle = dir;
  return self;
}

void Directory_Close (Directory* self) {
  closedir(self->handle);
  MemFree(self);
}

cstr Directory_GetNext (Directory* self) {
  for (;;) {
    dirent* ent = readdir(self->handle);
    if (!ent)
      return 0;
    if (StrEqual(ent->d_name, ".") || StrEqual(ent->d_name, ".."))
      continue;
    return ent->d_name;
  }
}

bool Directory_Change (cstr cwd) {
  return ChangeDir(cwd) == 0;
}

bool Directory_Create (cstr path) {
#if WINDOWS
  _mkdir(path);
#elif POSIX
  mkdir(path, 0775);
#endif
  return File_IsDir(path);
}

cstr Directory_GetCurrent () {
  static char buffer[FILENAME_MAX];
  if (GetCWD(buffer, sizeof(buffer)) != 0)
    return 0;
  buffer[sizeof(buffer) - 1] = 0;
  return buffer;
}

bool Directory_Remove (cstr path) {
  return RemoveDir(path) == 0;
}


text:
#include "GamepadButton.h"
#include "SDL.h"

const GamepadButton GamepadButton_BEGIN   = SDL_CONTROLLER_BUTTON_A;
const GamepadButton GamepadButton_A       = SDL_CONTROLLER_BUTTON_A;
const GamepadButton GamepadButton_B       = SDL_CONTROLLER_BUTTON_B;
const GamepadButton GamepadButton_X       = SDL_CONTROLLER_BUTTON_X;
const GamepadButton GamepadButton_Y       = SDL_CONTROLLER_BUTTON_Y;
const GamepadButton GamepadButton_Back    = SDL_CONTROLLER_BUTTON_BACK;
const GamepadButton GamepadButton_Guide   = SDL_CONTROLLER_BUTTON_GUIDE;
const GamepadButton GamepadButton_Start   = SDL_CONTROLLER_BUTTON_START;
const GamepadButton GamepadButton_LStick  = SDL_CONTROLLER_BUTTON_LEFTSTICK;
const GamepadButton GamepadButton_RStick  = SDL_CONTROLLER_BUTTON_RIGHTSTICK;
const GamepadButton GamepadButton_LBumper = SDL_CONTROLLER_BUTTON_LEFTSHOULDER;
const GamepadButton GamepadButton_RBumper = SDL_CONTROLLER_BUTTON_RIGHTSHOULDER;
const GamepadButton GamepadButton_Up      = SDL_CONTROLLER_BUTTON_DPAD_UP;
const GamepadButton GamepadButton_Down    = SDL_CONTROLLER_BUTTON_DPAD_DOWN;
const GamepadButton GamepadButton_Left    = SDL_CONTROLLER_BUTTON_DPAD_LEFT;
const GamepadButton GamepadButton_Right   = SDL_CONTROLLER_BUTTON_DPAD_RIGHT;
const GamepadButton GamepadButton_END     = SDL_CONTROLLER_BUTTON_DPAD_RIGHT;


text:
#include "BlendMode.h"
#include "ClipRect.h"
#include "Draw.h"
#include "Font.h"
#include "MemPool.h"
#include "RenderState.h"
#include "Shader.h"
#include "Tex2D.h"
#include "UIRenderer.h"
#include "Vec2.h"
#include "Vec4.h"
#include "Viewport.h"

struct UIRendererImage {
  UIRendererImage* next;
  Tex2D* image;
  Vec2f pos;
  Vec2f size;
};

struct UIRendererPanel {
  UIRendererPanel* next;
  Vec2f pos;
  Vec2f size;
  Vec4f color;
  float bevel;
  float innerAlpha;
};

struct UIRendererRect {
  UIRendererRect* next;
  Vec2f pos;
  Vec2f size;
  Vec4f color;
  bool outline;
};

struct UIRendererText {
  UIRendererText* next;
  Font* font;
  cstr text;
  Vec2f pos;
  Vec4f color;
};

struct UIRendererLayer {
  UIRendererLayer* parent;
  UIRendererLayer* next;
  UIRendererLayer* children;

  UIRendererImage* imageList;
  UIRendererPanel* panelList;
  UIRendererRect* rectList;
  UIRendererText* textList;

  Vec2f pos;
  Vec2f size;
  bool clip;
};

struct UIRenderer {
  UIRendererLayer* root;
  UIRendererLayer* layer;

  MemPool* layerPool;
  MemPool* imagePool;
  MemPool* panelPool;
  MemPool* rectPool;
  MemPool* textPool;
} static self = { 0 };

static void UIRenderer_Init () {
  static bool init = false;
  if (init) return;
  init = true;

  self.root = 0;
  self.layer = 0;

  self.layerPool = MemPool_CreateAuto(sizeof(UIRendererLayer));
  self.imagePool = MemPool_CreateAuto(sizeof(UIRendererImage));
  self.panelPool = MemPool_CreateAuto(sizeof(UIRendererPanel));
  self.rectPool = MemPool_CreateAuto(sizeof(UIRendererRect));
  self.textPool = MemPool_CreateAuto(sizeof(UIRendererText));
}

void UIRenderer_Begin () {
  UIRenderer_Init();

  self.root = 0;
  self.layer = 0;

  MemPool_Clear(self.layerPool);
  MemPool_Clear(self.imagePool);
  MemPool_Clear(self.panelPool);
  MemPool_Clear(self.rectPool);
  MemPool_Clear(self.textPool);

  Vec2i vp; Viewport_GetSize(&vp);
  UIRenderer_BeginLayer(0, 0, vp.x, vp.y, true);
  self.root = self.layer;
}

void UIRenderer_End () {
  UIRenderer_EndLayer();
}

static void UIRenderer_DrawLayer (UIRendererLayer const* self) {
  if (self->clip)
    ClipRect_PushCombined(self->pos.x, self->pos.y, self->size.x, self->size.y);

  if (self->panelList) {
    static Shader* shader = 0;
    if (!shader)
      shader = Shader_Load("vertex/ui", "fragment/ui/panel");

    const float pad = 64.0f;
    Shader_Start(shader);
    Shader_SetFloat("padding", pad);

    for (UIRendererPanel const* e = self->panelList; e; e = e->next) {
      float x = e->pos.x - pad;
      float y = e->pos.y - pad;
      float sx = e->size.x + 2.0f * pad;
      float sy = e->size.y + 2.0f * pad;

      Shader_SetFloat("innerAlpha", e->innerAlpha);
      Shader_SetFloat("bevel", e->bevel);
      Shader_SetFloat2("size", sx, sy);
      Shader_SetFloat4("color", UNPACK4(e->color));
      Draw_Rect(x, y, sx, sy);
    }

    Shader_Stop(shader);
  }

  for (UIRendererImage const* e = self->imageList; e; e = e->next) {
    Tex2D_Draw(e->image, UNPACK2(e->pos), UNPACK2(e->size));
  }

  for (UIRendererRect const* e = self->rectList; e; e = e->next) {
    Draw_Color(UNPACK4(e->color));
    if (e->outline)
      Draw_Border(1.0f, UNPACK2(e->pos), UNPACK2(e->size));
    else
      Draw_Rect(UNPACK2(e->pos), UNPACK2(e->size));
  }

  for (UIRendererText const* e = self->textList; e; e = e->next)
    Font_Draw(e->font, e->text, UNPACK2(e->pos), UNPACK4(e->color));

  for (UIRendererLayer const* e = self->children; e; e = e->next)
    UIRenderer_DrawLayer(e);

  if (self->clip)
    ClipRect_Pop();
}

void UIRenderer_Draw () {
  RenderState_PushBlendMode(BlendMode_Alpha);
  UIRenderer_DrawLayer(self.root);
  RenderState_PopBlendMode();
}

void UIRenderer_BeginLayer (float x, float y, float sx, float sy, bool clip) {
  UIRendererLayer* layer = (UIRendererLayer*)MemPool_Alloc(self.layerPool);
  layer->parent = self.layer;
  layer->next = 0;
  layer->children = 0;

  layer->pos = Vec2f_Create(x, y);
  layer->size = Vec2f_Create(sx, sy);
  layer->clip = clip;

  layer->imageList = 0;
  layer->panelList = 0;
  layer->rectList = 0;
  layer->textList = 0;
  self.layer = layer;
}

void UIRenderer_EndLayer () {
  if (self.layer->parent) {
    self.layer->next = self.layer->parent->children;
    self.layer->parent->children = self.layer;
  }
  self.layer = self.layer->parent;
}

void UIRenderer_Image (
  Tex2D* image,
  float x, float y,
  float sx, float sy)
{
  UIRendererImage* e = (UIRendererImage*)MemPool_Alloc(self.imagePool);
  e->next = self.layer->imageList;
  e->image = image;
  e->pos = Vec2f_Create(x, y);
  e->size = Vec2f_Create(sx, sy);
  self.layer->imageList = e;
}

void UIRenderer_Panel (
  float x, float y,
  float sx, float sy,
  float r, float g, float b, float a,
  float bevel,
  float innerAlpha)
{
  UIRendererPanel* e = (UIRendererPanel*)MemPool_Alloc(self.panelPool);
  e->next = self.layer->panelList;
  e->pos = Vec2f_Create(x, y);
  e->size = Vec2f_Create(sx, sy);
  e->color = Vec4f_Create(r, g, b, a);
  e->bevel = bevel;
  e->innerAlpha = innerAlpha;
  self.layer->panelList = e;
}

void UIRenderer_Rect (
  float x, float y,
  float sx, float sy,
  float r, float g, float b, float a,
  bool outline)
{
  UIRendererRect* e = (UIRendererRect*)MemPool_Alloc(self.rectPool);
  e->next = self.layer->rectList;
  e->pos = Vec2f_Create(x, y);
  e->size = Vec2f_Create(sx, sy);
  e->color = Vec4f_Create(r, g, b, a);
  e->outline = outline;
  self.layer->rectList = e;
}

void UIRenderer_Text (
  Font* font, cstr text,
  float x, float y,
  float r, float g, float b, float a)
{
  UIRendererText* e = (UIRendererText*)MemPool_Alloc(self.textPool);
  e->next = self.layer->textList;
  e->font = font;
  e->text = text;
  e->pos = Vec2f_Create(x, y);
  e->color = Vec4f_Create(r, g, b, a);
  self.layer->textList = e;
}


text:
#include "CubeFace.h"
#include "DataFormat.h"
#include "Draw.h"
#include "PhxMemory.h"
#include "PixelFormat.h"
#include "RenderTarget.h"
#include "RNG.h"
#include "Shader.h"
#include "Tex2D.h"
#include "TexCube.h"
#include "TexFilter.h"
#include "TexFormat.h"
#include "Vec2.h"

/* TODO : Make this cleaner and faster. */
/* TODO : This is very specific functionality (GGX mips). Where does this kind
 *        of functionality really belong? Not PHX core but not Lua.. */

TexCube* TexCube_GenIRMap (TexCube* self, int sampleCount) {
  int size = TexCube_GetSize(self);
  TexFormat format = TexCube_GetFormat(self);
  TexCube* result = TexCube_Create(size, format);

  /* Copy 0th-level data directly, as it should be identical. Doing so in
   * shader may result in subtle differences. */ {
    int components = TexFormat_Components(format);
    PixelFormat pf =
      components == 4 ? PixelFormat_RGBA :
      components == 3 ? PixelFormat_RGB  :
      components == 2 ? PixelFormat_RG   :
                        PixelFormat_Red;
    DataFormat df = DataFormat_Float;

    void* buffer = MemAlloc(size * size * sizeof(float) * components);
    for (int i = 0; i < 6; ++i) {
      TexCube_GetData(self, buffer, CubeFace_Get(i), 0, pf, df);
      TexCube_SetData(result, buffer, CubeFace_Get(i), 0, pf, df);
    }
    TexCube_GenMipmap(result);
    MemFree(buffer);
  }

  static Shader* shader = 0;
  if (!shader)
    shader = Shader_Load("vertex/identity", "fragment/compute/irmap");

  CubeFace const face[6] = {
    CubeFace_PX, CubeFace_NX, CubeFace_PY,
    CubeFace_NY, CubeFace_PZ, CubeFace_NZ,
  };

  Vec3f const look[6] = {
    {  1,  0,  0 }, { -1,  0,  0 }, {  0,  1,  0 },
    {  0, -1,  0 }, {  0,  0,  1 }, {  0,  0, -1 },
  };

  Vec3f const up[6] = {
    {  0,  1,  0 }, {  0,  1,  0 }, {  0,  0, -1 },
    {  0,  0,  1 }, {  0,  1,  0 }, {  0,  1,  0 },
  };

  RNG* rng = RNG_FromTime();

  int levels = 0;
  for (int i = size; i > 0; i /= 2)
    levels++;

  Shader_Start(shader);
  int level = 0;
  while (size > 1) {
    size /= 2;
    level += 1;

    double ggxWidth = (double)level / (double)levels;
    ggxWidth *= ggxWidth;

    Vec2f* sampleBuffer = MemNewArray(Vec2f, sampleCount);
    Tex2D* sampleTex = Tex2D_Create(sampleCount, 1, TexFormat_RG16F);
    /* Populate the sample buffer with a fresh set of weighted directions */ {
      for (int i = 0; i < sampleCount; ++i) {
        double e1 = RNG_GetUniform(rng);
        double e2 = RNG_GetUniform(rng);
        double pitch = Atan(ggxWidth * Sqrt(e1), Sqrt(1.0 - e1));
        double yaw = Tau * e2;
        sampleBuffer[i] = Vec2f_Create((float)pitch, (float)yaw);
      }
      Tex2D_SetData(sampleTex, sampleBuffer, PixelFormat_RG, DataFormat_Float);
    }

    float angle = (float)level / (float)(levels - 1);
    angle = angle * angle;

    Shader_SetFloat("angle", angle);
    Shader_SetTexCube("src", self);
    Shader_SetTex2D("sampleBuffer", sampleTex);
    Shader_SetInt("samples", sampleCount);

    for (int i = 0; i < 6; ++i) {
      CubeFace thisFace = face[i];
      Vec3f thisLook = look[i];
      Vec3f thisUp = up[i];
      RenderTarget_Push(size, size);
      RenderTarget_BindTexCubeLevel(result, thisFace, level);
      Shader_SetFloat3("cubeLook", thisLook.x, thisLook.y, thisLook.z);
      Shader_SetFloat3("cubeUp", thisUp.x, thisUp.y, thisUp.z);
      Draw_Rect(-1, -1, 2, 2);
      RenderTarget_Pop();
    }

    MemFree(sampleBuffer);
    Tex2D_Free(sampleTex);
  }

  RNG_Free(rng);
  Shader_Stop(shader);
  TexCube_SetMagFilter(result, TexFilter_Linear);
  TexCube_SetMinFilter(result, TexFilter_LinearMipLinear);
  return result;
}


text:
#include "Intersect.h"
#include "Plane.h"
#include "Polygon.h"

PointClassification Plane_ClassifyPoint (Plane* plane, Vec3f* p) {
  float magnitude = Abs(1.0f - Vec3f_Length(plane->n));
  Assert(magnitude < .01f); UNUSED(magnitude);

  float dist = Vec3f_Dot(plane->n, *p) - plane->d;
  if (dist > PLANE_THICKNESS_EPSILON)
    return PointClassification_InFront;
  else if (dist < -PLANE_THICKNESS_EPSILON)
    return PointClassification_Behind;
  else
    return PointClassification_Coplanar;
}

PolygonClassification Plane_ClassifyPolygon (Plane* plane, Polygon* polygon) {
  int32 numInFront = 0;
  int32 numBehind  = 0;

  for (int32 i = 0; i < ArrayList_GetSize(polygon->vertices); i++) {
    Vec3f vertex = ArrayList_Get(polygon->vertices, i);
    PointClassification classification = Plane_ClassifyPoint(plane, &vertex);
    switch (classification) {
      default: Fatal("Plane_ClassifyPolygon: Unhandled case: %i", classification);

      case PointClassification_InFront:
        numInFront++;
        break;

      case PointClassification_Behind:
        numBehind++;
        break;

      case PointClassification_Coplanar:
        //Doesn't count for either
        break;
    }

    /* TODO : This early out may not make as much sense if the BSP stops cutting triangles. */
    if (numInFront != 0 && numBehind != 0)
      return PolygonClassification_Straddling;
  }

  if (numInFront != 0)
    return PolygonClassification_InFront;
  if (numBehind != 0)
    return PolygonClassification_Behind;
  return PolygonClassification_Coplanar;
}

Error Plane_Validate (Plane* plane) {
  Error e = Error_None;

  e |= Float_Validate(plane->d);
  e |= Vec3f_Validate(plane->n);

  return e;
}

void Plane_FromPolygon (Polygon* polygon, Plane* plane) {
  Polygon_ToPlane(polygon, plane);
}

void Plane_FromPolygonFast (Polygon* polygon, Plane* plane) {
  Polygon_ToPlaneFast(polygon, plane);
}


text:
#include "Intersect.h"
#include "LineSegment.h"
#include "Ray.h"

void Ray_GetPoint (Ray const* self, float t, Vec3f* out) {
  *out = Vec3f_Add(self->p, Vec3f_Muls(self->dir, t));
}

bool Ray_IntersectPlane (Ray const* self, Plane const* plane, Vec3f* pHit) {
  return Intersect_RayPlane(self, plane, pHit);
}

bool Ray_IntersectTriangle_Barycentric (Ray const* self, Triangle const* tri, float tEpsilon, float* tHit) {
  return Intersect_RayTriangle_Barycentric(self, tri, tEpsilon, tHit);
}

bool Ray_IntersectTriangle_Moller1 (Ray const* self, Triangle const* tri, float* tHit) {
  return Intersect_RayTriangle_Moller1(self, tri, tHit);
}

bool Ray_IntersectTriangle_Moller2 (Ray const* self, Triangle const* tri, float* tHit) {
  return Intersect_RayTriangle_Moller2(self, tri, tHit);
}

void Ray_ToLineSegment (Ray const* self, LineSegment* lineSegment) {
  Ray_GetPoint(self, self->tMin, &lineSegment->p0);
  Ray_GetPoint(self, self->tMax, &lineSegment->p1);
}

void Ray_FromLineSegment (LineSegment const* lineSegment, Ray* self) {
  LineSegment_ToRay(lineSegment, self);
}


text:
#include "Array.h"
#include "LineSegment.h"
#include "Ray.h"

void LineSegment_ToRay (LineSegment const* self, Ray* out) {
  out->p = self->p0;
  out->dir = Vec3f_Sub(self->p1, self->p0);
  out->tMin = 0.0f;
  out->tMax = 1.0f;
}

void LineSegment_FromRay (Ray const* ray, LineSegment* out) {
  Ray_ToLineSegment(ray, out);
}

cstr LineSegment_ToString (LineSegment* self) {
  static char buffer[512];
  snprintf(buffer, (size_t) Array_GetSize(buffer),
    "p0:%s p1:%s",
    Vec3f_ToString(&self->p0),
    Vec3f_ToString(&self->p1)
  );
  return buffer;
}


text:
#include "Bit.h"

uint32 Bit_And32 (uint32 x, uint32 y) {
  return x & y;
}

uint32 Bit_Or32 (uint32 x, uint32 y) {
  return x | y;
}

uint32 Bit_Xor32 (uint32 x, uint32 y) {
  return x ^ y;
}

bool Bit_Has32 (uint32 x, uint32 y) {
  return (x & y) == y;
}

uint64 Bit_And64 (uint64 x, uint64 y) {
  return x & y;
}

uint64 Bit_Or64 (uint64 x, uint64 y) {
  return x | y;
}

uint64 Bit_Xor64 (uint64 x, uint64 y) {
  return x ^ y;
}

bool Bit_Has64 (uint64 x, uint64 y) {
  return (x & y) == y;
}


text:
#include "Joystick.h"
#include "PhxMath.h"
#include "PhxMemory.h"
#include "PhxString.h"
#include "SDL.h"
#include "TimeStamp.h"

/* TODO : Use a linked-list instead of a freelist. This is lazy. */

struct Joystick {
  SDL_Joystick* handle;
  cstr guid;
  int axes;
  int balls;
  int buttons;
  int hats;
  bool* buttonStates;
  bool* axisAlive;
  double* axisStates;
  TimeStamp lastUsed;
};

static const int kMaxOpen = 64;
static int kOpen = 0;
static Joystick* freeList[kMaxOpen] = { 0 };

static char* ConvertGUID (SDL_JoystickGUID id) {
  static char buf[64];
  SDL_JoystickGetGUIDString(id, buf, sizeof(buf));
  return buf;
}

static void Joystick_UpdateSingle (Joystick* self) {
  bool changed = false;
  for (int i = 0; i < self->axes; ++i) {
    double state = Joystick_GetAxis(self, i);
    double delta = Abs(state - self->axisStates[i]);
    if (delta > 0.1) {
      changed = true;
      self->axisAlive[i] = true;
    }
    self->axisStates[i] = state;
  }

  for (int i = 0; i < self->buttons; ++i) {
    bool state = Joystick_ButtonDown(self, i);
    if (self->buttonStates[i] != state)
      changed = true;
    self->buttonStates[i] = state;
  }

  if (changed)
    self->lastUsed = TimeStamp_Get();
}

int Joystick_GetCount () {
  return SDL_NumJoysticks();
}

Joystick* Joystick_Open (int index) {
  Joystick* self = MemNew(Joystick);
  if (kOpen == kMaxOpen)
    Fatal("Cannot open any more gamepad connections.");

  for (int i = 0; i < kMaxOpen; ++i) {
    if (!freeList[i]) {
      freeList[i] = self;
      kOpen++;
      break;
    }
  }

  self->handle = SDL_JoystickOpen(index);
  self->guid = StrDup(ConvertGUID(SDL_JoystickGetGUID(self->handle)));
  self->axes = SDL_JoystickNumAxes(self->handle);
  self->balls = SDL_JoystickNumBalls(self->handle);
  self->buttons = SDL_JoystickNumButtons(self->handle);
  self->hats = SDL_JoystickNumHats(self->handle);
  self->buttonStates = MemNewArray(bool, self->buttons);
  self->axisAlive = MemNewArray(bool, self->axes);
  MemZero(self->axisAlive, sizeof(bool) * self->axes);
  self->axisStates = MemNewArray(double, self->axes);
  self->lastUsed = TimeStamp_Get();
  Joystick_UpdateSingle(self);
  return self;
}

void Joystick_Close (Joystick* self) {
  kOpen--;
  for (int i = 0; i < kMaxOpen; ++i) {
    if (freeList[i] == self) {
      freeList[i] = 0;
      break;
    }
  }

  SDL_JoystickClose(self->handle);
  MemFree(self->guid);
  MemFree(self->buttonStates);
  MemFree(self->axisStates);
  MemFree(self);
}

cstr Joystick_GetGUID (Joystick* self) {
  return self->guid;
}

cstr Joystick_GetGUIDByIndex (int index) {
  return (cstr)ConvertGUID(SDL_JoystickGetDeviceGUID(index));
}

cstr Joystick_GetName (Joystick* self) {
  return SDL_JoystickName(self->handle);
}

cstr Joystick_GetNameByIndex (int index) {
  return SDL_JoystickNameForIndex(index);
}

int Joystick_GetAxisCount (Joystick* self) {
  return self->axes;
}

int Joystick_GetBallCount (Joystick* self) {
  return self->balls;
}

int Joystick_GetButtonCount (Joystick* self) {
  return self->buttons;
}

int Joystick_GetHatCount (Joystick* self) {
  return self->hats;
}

double Joystick_GetIdleTime (Joystick* self) {
  return TimeStamp_GetElapsed(self->lastUsed);
}

double Joystick_GetAxis (Joystick* self, int index) {
  return SDL_JoystickGetAxis(self->handle, index) / 32768.0;
}

bool Joystick_GetAxisAlive (Joystick* self, int index) {
  return self->axisAlive[index];
}

double Joystick_GetAxisDelta (Joystick* self, int index) {
  return SDL_JoystickGetAxis(self->handle, index) / 32768.0 - self->axisStates[index];
}

HatDir Joystick_GetHat (Joystick* self, int index) {
  return (HatDir)SDL_JoystickGetHat(self->handle, index);
}

bool Joystick_ButtonDown (Joystick* self, int index) {
  return SDL_JoystickGetButton(self->handle, index) > 0;
}

bool Joystick_ButtonPressed (Joystick* self, int index) {
  return SDL_JoystickGetButton(self->handle, index) > 0 &&
         !self->buttonStates[index];
}

bool Joystick_ButtonReleased (Joystick* self, int index) {
  return SDL_JoystickGetButton(self->handle, index) == 0 &&
         self->buttonStates[index];
}

void Joystick_Update () {
  for (int i = 0; i < kMaxOpen; ++i)
    if (freeList[i])
      Joystick_UpdateSingle(freeList[i]);
}


text:
#include "Intersect.h"
#include "Plane.h"
#include "Triangle.h"

inline static bool PointsInPlane (
  Plane const* plane,
  Triangle const* tri)
{
  for (int i = 0; i < 3; ++i) {
    float dist = Vec3f_Dot(plane->n, tri->vertices[i]) - plane->d;
    if (Abs(dist) >= PLANE_THICKNESS_EPSILON)
      return false;
  }

  return true;
};

/* TODO : OPTIMIZE */
void Triangle_ToPlane (Triangle const* tri, Plane* plane) {
  Vec3f const* v = tri->vertices;
  Vec3f e1 = Vec3f_Sub(v[1], v[0]);
  Vec3f e2 = Vec3f_Sub(v[2], v[0]);
  Vec3f n  = Vec3f_Cross(e1, e2);
  n = Vec3f_Normalize(n);

  Vec3f centroid = v[0];
  centroid = Vec3f_Add(centroid, v[1]);
  centroid = Vec3f_Add(centroid, v[2]);
  centroid = Vec3f_Divs(centroid, 3.0f);

  plane->n = n;
  plane->d = Vec3f_Dot(centroid, n);

  Assert(Plane_Validate(plane) == Error_None);
  CHECK2(Assert(PointsInPlane(plane, tri)));
}

/* NOTE: Doesn't normalize n and uses v[0] as the center. */
void Triangle_ToPlaneFast (Triangle const* triangle, Plane* plane) {
  Vec3f const* v = triangle->vertices;
  Vec3f e1 = Vec3f_Sub(v[1], v[0]);
  Vec3f e2 = Vec3f_Sub(v[2], v[0]);
  Vec3f n  = Vec3f_Cross(e1, e2);

  plane->n = n;
  plane->d = Vec3f_Dot(v[0], n);

  Assert(Plane_Validate(plane) == Error_None);
  CHECK2(Assert(PointsInPlane(plane, triangle)));
}

float Triangle_GetArea (Triangle const* tri) {
  Vec3f e1 = Vec3f_Sub(tri->vertices[1], tri->vertices[0]);
  Vec3f e2 = Vec3f_Sub(tri->vertices[2], tri->vertices[1]);
  return 0.5f * Vec3f_Length(Vec3f_Cross(e1, e2));
}

Error Triangle_Validate (Triangle const* tri) {
  Vec3f const* v = tri->vertices;

  //NaN or Inf
  for (int32 i = 0; i < 3; i++) {
    Error e = Vec3f_Validate(v[i]);
    if (e != Error_None) return Error_VertPos | e;
  }

  //Degenerate
  bool eq01 = Vec3f_Equal(v[0], v[1]);
  bool eq12 = Vec3f_Equal(v[1], v[2]);
  bool eq20 = Vec3f_Equal(v[2], v[0]);
  if (eq01 || eq12 || eq20) return Error_VertPos | Error_Degenerate;

  //Sliver
  /* TODO : The triangle should also be considered a sliver if any angle is
   *       small enough that at PLANE_THICKNESS_EPSILON from the vertex the
   *       distance between the two edges is less than PLANE_THICKNESS_EPSILON.
   *       That means 2*tan^-1(1/2) = 53.13 degrees. Yikes! Maybe each time
   *       we cut a polygon we check the newly created angles, and if any are
   *       less than this we send the triangle to both sides? */
  float e01 = Vec3f_Length(Vec3f_Sub(v[0], v[1]));
  float e12 = Vec3f_Length(Vec3f_Sub(v[1], v[2]));
  float e20 = Vec3f_Length(Vec3f_Sub(v[2], v[0]));
  float shortest = Min(Min(e01, e12), e20);
  if (shortest < 0.75f*PLANE_THICKNESS_EPSILON) return Error_VertPos | Error_Underflow;

  //Phew!
  return Error_None;
}


text:
#include "SDL.h"
#include "TimeStamp.h"

TimeStamp TimeStamp_Get () {
  return SDL_GetPerformanceCounter();
}

double TimeStamp_GetDifference (TimeStamp start, TimeStamp end) {
  double freq = (double)SDL_GetPerformanceFrequency();
  return (double)(end - start) / freq;
}

double TimeStamp_GetElapsed (TimeStamp then) {
  double freq = (double)SDL_GetPerformanceFrequency();
  return (double)(SDL_GetPerformanceCounter() - then) / freq;
}

double TimeStamp_GetElapsedMs (TimeStamp then) {
  double freq = (double)SDL_GetPerformanceFrequency();
  return (double)(1000 * (SDL_GetPerformanceCounter() - then)) / freq;
}

TimeStamp TimeStamp_GetFuture (double seconds) {
  double freq = (double)SDL_GetPerformanceFrequency();
  return SDL_GetPerformanceCounter() + (TimeStamp)(freq * seconds);
}

TimeStamp TimeStamp_GetRelative (TimeStamp start, double seconds) {
  double freq = (double)SDL_GetPerformanceFrequency();
  return start + (TimeStamp)(freq * seconds);
}

/* TODO : Keep the division in uint64, only use double for remainder. */
double TimeStamp_ToDouble (TimeStamp self) {
  return (double)self / (double)SDL_GetPerformanceFrequency();
}


text:
#include "BlendMode.h"
#include "CullFace.h"
#include "OpenGL.h"
#include "RenderState.h"

void OpenGL_Init () {
  static bool init = false;
  if (!init) {
    init = true;
    glewInit();
  }

  GLCALL(glDisable(GL_MULTISAMPLE))
  GLCALL(glDisable(GL_CULL_FACE));
  GLCALL(glCullFace(GL_BACK))

  GLCALL(glPixelStorei(GL_PACK_ALIGNMENT, 1))
  GLCALL(glPixelStorei(GL_UNPACK_ALIGNMENT, 1))
  GLCALL(glDepthFunc(GL_LEQUAL))

  GLCALL(glEnable(GL_BLEND))
  GLCALL(glBlendFunc(GL_ONE, GL_ZERO))

  GLCALL(glEnable(GL_TEXTURE_CUBE_MAP_SEAMLESS))
  GLCALL(glDisable(GL_POINT_SMOOTH))
  GLCALL(glDisable(GL_LINE_SMOOTH))
  GLCALL(glHint(GL_POINT_SMOOTH_HINT, GL_FASTEST))
  GLCALL(glHint(GL_LINE_SMOOTH_HINT, GL_FASTEST))
  GLCALL(glLineWidth(2))

  GLCALL(glMatrixMode(GL_PROJECTION))
  GLCALL(glLoadIdentity())
  GLCALL(glMatrixMode(GL_MODELVIEW))
  GLCALL(glLoadIdentity())

  // GLCALL(glDepthRange(-1, 1))

  RenderState_PushAllDefaults();
}

void OpenGL_CheckError (cstr file, int line) {
  GLenum errorID = glGetError();
  cstr error = 0;
  switch (errorID) {
    case GL_NO_ERROR: return;
    case GL_INVALID_ENUM:
      error = "GL_INVALID_ENUM"; break;
    case GL_INVALID_VALUE:
      error = "GL_INVALID_VALUE"; break;
    case GL_INVALID_OPERATION:
      error = "GL_INVALID_OPERATION"; break;
    case GL_INVALID_FRAMEBUFFER_OPERATION:
      error = "GL_INVALID_FRAMEBUFFER_OPERATION"; break;
    case GL_OUT_OF_MEMORY:
      error = "GL_OUT_OF_MEMORY"; break;
    default:
      Fatal("OpenGL_CheckError: glGetError returned illegal error code %u at %s:%d", errorID, file, line);
      break;
  }
  Fatal("OpenGL_CheckError: %s at %s:%d", error, file, line);
}


text:
#include "PhxMemory.h"
#include "Metric.h"

static int32 valueCurr[Metric_SIZE + 1] = { 0 };

int32 Metric_Get (Metric self) {
  return valueCurr[self];
}

cstr Metric_GetName (Metric self) {
  switch (self) {
    case Metric_DrawCalls:  return "Draw Calls";
    case Metric_Immediate:  return "Draw Calls (Immediate)";
    case Metric_PolysDrawn: return "Polys";
    case Metric_TrisDrawn:  return "Tris";
    case Metric_VertsDrawn: return "Vertices";
    case Metric_Flush:      return "Pipeline Flushes";
    case Metric_FBOSwap:    return "Framebuffer Swaps";
  }
  return 0;
}

void Metric_AddDraw (int32 polys, int32 tris, int32 verts) {
  valueCurr[Metric_DrawCalls]  += 1;
  valueCurr[Metric_PolysDrawn] += polys;
  valueCurr[Metric_TrisDrawn]  += tris;
  valueCurr[Metric_VertsDrawn] += verts;
}

void Metric_AddDrawImm (int32 polys, int32 tris, int32 verts) {
  valueCurr[Metric_Immediate]  += 1;
  valueCurr[Metric_PolysDrawn] += polys;
  valueCurr[Metric_TrisDrawn]  += tris;
  valueCurr[Metric_VertsDrawn] += verts;
}

void Metric_Inc (Metric self) {
  valueCurr[self] += 1;
}

void Metric_Mod (Metric self, int32 delta) {
  valueCurr[self] += delta;
}

void Metric_Reset () {
  MemZero(valueCurr, sizeof(valueCurr));
}


text:
#include "Array.h"
#include "Button.h"
#include "Device.h"
#include "DeviceType.h"
#include "InputEvent.h"
#include "State.h"

#include <stdio.h>

cstr InputEvent_ToString (InputEvent* ie) {
  static char buffer[512];
  snprintf(buffer, (size_t) Array_GetSize(buffer),
    "Event %p\n"
    "\tTimestamp: %i\n"
    "\tDevice:    %s\n"
    "\tButton:    %s\n"
    "\tValue:     %.2f\n"
    "\tState:     %s",
    ie,
    ie->timestamp,
    Device_ToString(&ie->device),
    Button_ToString(ie->button),
    ie->value,
    State_ToString(ie->state)
  );
  return buffer;
}


text:
#include "Bytes.h"
#include "PhxMemory.h"
#include "Socket.h"
#include "SocketType.h"
#include "PhxString.h"

#include <stdio.h>
#include <string.h>

#if WINDOWS
  #define _WINSOCK_DEPRECATED_NO_WARNINGS 1
  #include <winsock2.h>

  #define SOCK_CLOSE closesocket
  #define SOCK_ERROR SOCKET_ERROR
  #define SOCK_INVALID INVALID_SOCKET
  #define SOCK_LASTERROR (WSAGetLastError())
  #define SOCK_WOULDBLOCK WSAEWOULDBLOCK

  typedef SOCKET sock_t;
  typedef int socklen_t;
  static WSADATA wsa;
#elif POSIX
  #include <errno.h>
  #include <fcntl.h>
  #include <unistd.h>
  #include <sys/types.h>
  #include <sys/socket.h>
  #include <netinet/in.h>
  #include <arpa/inet.h>

  #define SOCK_CLOSE close
  #define SOCK_ERROR -1
  #define SOCK_INVALID -1
  #define SOCK_LASTERROR errno

  #define SOCK_WOULDBLOCK EWOULDBLOCK
  typedef int sock_t;
#endif

#define BUF_SIZE 2048

struct Socket {
  SocketType type;
  sock_t sock;
  sockaddr_in addrSend;
  sockaddr_in addrRecv;
  char buffer[BUF_SIZE];
};

inline static void Socket_Cleanup (sock_t self) {
  if (self != SOCK_INVALID)
    SOCK_CLOSE(self);
}

inline static int Socket_Receive (sock_t self, void* buf, int len) {
  #if POSIX
    return read(self, buf, len);
  #elif WINDOWS
    return recv(self, (char*)buf, len, 0);
  #endif
}

inline static int Socket_Send (sock_t self, void const* buf, int len) {
  #if POSIX
    return write(self, buf, len);
  #elif WINDOWS
    return send(self, (char const*)buf, len, 0);
  #endif
}

inline static bool Socket_SetNonblocking (sock_t self) {
  #if POSIX
    return fcntl(self, F_SETFL, fcntl(self, F_GETFL, 0) | O_NONBLOCK) >= 0;
  #elif WINDOWS
    ulong opt = 1;
    return ioctlsocket(self, FIONBIO, &opt) != SOCKET_ERROR;
  #endif
}

Socket* Socket_Create (SocketType type) {
  if (type != SocketType_UDP && type != SocketType_TCP)
    Fatal("Socket_Create: socket type must be either SocketType_TCP or SocketType_UDP");

  #if WINDOWS
    static bool init = false;
    if (!init) {
      init = true;
      if (WSAStartup(MAKEWORD(2, 2), &wsa) != 0)
        Fatal("Socket_Create: failed to initialize Winsock");
    }
  #endif

  Socket* self = MemNew(Socket);
  self->type = type;
  self->sock = socket(AF_INET, type == SocketType_UDP ? SOCK_DGRAM : SOCK_STREAM, 0);
  if (self->sock == SOCK_INVALID)
    Fatal("Socket_Create: failed to open socket");

  #if POSIX
    int opt = 1;
    if (setsockopt(self->sock, SOL_SOCKET, SO_REUSEADDR, (char*)&opt, sizeof(opt)))
      Fatal("Socket_Create: failed to set socket to reusable");
  #endif

  if (!Socket_SetNonblocking(self->sock))
    Fatal("Socket_Create: failed to set socket to non-blocking");
  return self;
}

void Socket_Free (Socket* self) {
  Socket_Cleanup(self->sock);
  MemFree(self);
}

Socket* Socket_Accept (Socket* self) {
  if (self->type != SocketType_TCP)
    Fatal("Socket_Accept: can only accept connections on TCP sockets");

  sock_t sock = accept(self->sock, 0, 0);
  if (sock == SOCK_INVALID)
    return 0;

  Socket* con = MemNew(Socket);
  con->type = SocketType_TCP;
  con->sock = sock;
  if (!Socket_SetNonblocking(con->sock))
    Fatal("Socket_Accept: failed to set socket to non-blocking");
  return con;
}

void Socket_Bind (Socket* self, int port) {
  sockaddr_in addr;
  memset(&addr, 0, sizeof(addr));
  addr.sin_family = AF_INET;
  addr.sin_port = htons((uint16)port);
  addr.sin_addr.s_addr = htonl(INADDR_ANY);
  if (bind(self->sock, (sockaddr*)&addr, sizeof(addr)) == SOCK_ERROR)
    Fatal("Socket_Bind: failed to bind socket");
}

void Socket_Listen (Socket* self) {
  if (self->type != SocketType_TCP)
    Fatal("Socket_Listen: can only listen for connections on TCP sockets");
  if (listen(self->sock, 1) == SOCK_ERROR)
    Fatal("Socket_Listen: failed to listen");
}

cstr Socket_Read (Socket* self) {
  int bytes = Socket_Receive(self->sock, self->buffer, sizeof(self->buffer));
  if (bytes == SOCK_ERROR) {
    if (SOCK_LASTERROR == SOCK_WOULDBLOCK)
      return 0;
    Fatal("Socket_Read: failed to read from socket");
  }

  if (bytes == 0)
    return 0;

  self->buffer[bytes] = 0;
  return self->buffer;
}

Bytes* Socket_ReadBytes (Socket* self) {
  int bytes = Socket_Receive(self->sock, self->buffer, sizeof(self->buffer));
  if (bytes == SOCK_ERROR) {
    if (SOCK_LASTERROR == SOCK_WOULDBLOCK)
      return 0;
    Fatal("Socket_ReadRaw: failed to read from socket");
  }

  if (bytes == 0)
    return 0;

  Bytes* data = Bytes_Create(bytes);
  Bytes_Write(data, self->buffer, bytes);
  return data;
}

int Socket_ReceiveFrom (Socket* self, void* data, size_t len) {
  MemZero(data, len);
  socklen_t addrSize;
#if POSIX
  int bytes = recvfrom(self->sock, data, len, 0, (sockaddr*)&self->addrRecv, &addrSize);
#else
  int bytes = recvfrom(self->sock, (char*)data, (int)len, 0, (sockaddr*)&self->addrRecv, &addrSize);
#endif

  if (bytes == SOCK_ERROR) {
    if (SOCK_LASTERROR == SOCK_WOULDBLOCK)
      return 0;
    return -1;
  }

  return bytes;
}

cstr Socket_GetAddress (Socket* self) {
  return StrFormat("%s:%d",
    inet_ntoa(self->addrRecv.sin_addr),
    ntohs(self->addrRecv.sin_port));
}

void Socket_SetAddress (Socket* self, cstr addr) {
  cstr colon = StrFind(addr, ":");
  if (!colon)
    Fatal("Socket_SetReceiver: address must be in format a.b.c.d:port format");
  cstr ip = StrSubStr(addr, colon);
  cstr port = StrSubStr(colon + 1, addr + strlen(addr));
  self->addrSend.sin_family = AF_INET;
  self->addrSend.sin_port = htons((uint16)strtol(port, 0, 0));

#if POSIX
  if (inet_aton(ip, &self->addrSend.sin_addr) == 0)
#else
  self->addrSend.sin_addr.S_un.S_addr = inet_addr(ip);
  if (self->addrSend.sin_addr.S_un.S_addr == INADDR_NONE)
#endif
    Fatal("Socket_SetReceiver: failed to interpret network address");

  StrFree(ip);
  StrFree(port);
}

int Socket_SendTo (Socket* self, void const* data, size_t len) {
#if POSIX
  int bytes = sendto(self->sock, data, len, 0, (sockaddr*)&self->addrSend, sizeof(self->addrSend));
#else
  int bytes = sendto(self->sock, (cstr)data, (int)len, 0, (sockaddr*)&self->addrSend, sizeof(self->addrSend));
#endif
  if (bytes == SOCK_ERROR)
    return -1;
  return bytes;
}

void Socket_Write (Socket* self, cstr msg) {
  if (Socket_Send(self->sock, msg, StrLen(msg)) == SOCK_ERROR)
    Fatal("Socket_Write: failed to write to socket");
}

void Socket_WriteBytes (Socket* self, Bytes* msg) {
  if (Socket_Send(self->sock, Bytes_GetData(msg), Bytes_GetSize(msg)) == SOCK_ERROR)
    Fatal("Socket_WriteRaw: failed to write to socket");
}


text:
#include "Hash.h"
#include "HashMap.h"
#include "PhxMemory.h"

#include <stdlib.h>
#include <string.h>

struct Node {
  uint64 hash;
  void*  value;
};

struct HashMap {
  Node* elems;
  uint32 size;
  uint32 capacity;
  uint32 mask;
  uint32 keySize;
  uint32 maxProbe;
};

inline static uint64 Hash (void const* key, uint32 len) {
  return Hash_XX64(key, len, 0x0ULL);
}

HashMap* HashMap_Create (uint32 keySize, uint32 capacity) {
  uint32 logCapacity = 0;
  while (capacity > 1) {
    capacity /= 2;
    logCapacity++;
  }
  capacity = 1 << logCapacity;

  HashMap* self = MemNew(HashMap);
  self->elems = MemNewArrayZero(Node, capacity);
  self->size = 0;
  self->capacity = capacity;
  self->mask = (1 << logCapacity) - 1;
  self->keySize = keySize;
  self->maxProbe = logCapacity;
  return self;
}

void HashMap_Free (HashMap* self) {
  MemFree(self->elems);
  MemFree(self);
}

void HashMap_Foreach (HashMap* self, ValueForeach fn, void* userData) {
  for (uint32 i = 0; i < self->capacity; ++i) {
    Node* node = self->elems + i;
    if (node->value)
      fn(node->value, userData);
  }
}

void* HashMap_Get (HashMap* self, void const* key) {
  return HashMap_GetRaw(self, Hash(key, self->keySize));
}

void* HashMap_GetRaw (HashMap* self, uint64 hash) {
  uint32 index = 0;
  Node* node = self->elems + ((hash + index) & self->mask);
  while (node->value && index < self->maxProbe) {
    if (node->hash == hash) return node->value;
    index++;
    node = self->elems + ((hash + index) & self->mask);
  }
  return 0;
}

/* TODO : Removal in open-addressing is non-trivial! */
#if 0
void* HashMap_Remove (HashMap* self, void const* key) {
  Node* node = HashMap_GetNode(self, key);
  Node* head = node;
  Node* last = 0;

  while (node && node->key) {
    if (memcmp(node->key, key, self->keySize) == 0) {
      void* value = node->value;
      MemFree(node->key);
      if (last) {
        last->next = node->next;
        MemFree(node);
      } else if (node->next)
        *head = *node->next;
      return value;
    }
    last = node;
    node = node->next;
  }

  return 0;
}
#endif

void HashMap_Resize (HashMap* self, uint32 capacity) {
  HashMap* other = HashMap_Create(self->keySize, capacity);
  for (uint32 i = 0; i < self->capacity; ++i) {
    Node* node = self->elems + i;
    if (node->value)
      HashMap_SetRaw(other, node->hash, node->value);
  }

  MemFree(self->elems);
  *self = *other;
}

void HashMap_Set (HashMap* self, void const* key, void* value) {
  HashMap_SetRaw(self, Hash(key, self->keySize), value);
}

void HashMap_SetRaw (HashMap* self, uint64 hash, void* value) {
  uint32 index = 0;
  Node* node = self->elems + ((hash + index) & self->mask);
  while (node->value && index < self->maxProbe) {
    if (node->hash == hash) { node->value = value; return; }
    index++;
    node = self->elems + ((hash + index) & self->mask);
  }

  if (index >= self->maxProbe) {
    HashMap_Resize(self, self->capacity * 2);
    HashMap_SetRaw(self, hash, value);
  } else {
    node->hash = hash;
    node->value = value;
    self->size++;
  }
}


text:
#include "Audio.h"
#include "Bytes.h"
#include "FMODError.h"
#include "PhxMemory.h"
#include "PhxMath.h"
#include "Sound.h"
#include "SoundDesc.h"
#include "SoundDef.h"
#include "Vec3.h"
#include "fmod/fmod.h"

static void Sound_SetState(Sound*, SoundState);

static FMOD_RESULT F_CALLBACK Sound_Callback (
  FMOD_CHANNELCONTROL*              channel,
  FMOD_CHANNELCONTROL_TYPE          controlType,
  FMOD_CHANNELCONTROL_CALLBACK_TYPE callbackType,
  void*, void*)
{
  if (callbackType == FMOD_CHANNELCONTROL_CALLBACK_END) {
    Assert(controlType == FMOD_CHANNELCONTROL_CHANNEL);
    Sound* self;
    FMODCALL(FMOD_Channel_GetUserData((FMOD_CHANNEL*) channel, (void**) &self));
    Sound_SetState(self, SoundState_Finished);
  }

  return FMOD_OK;
}

inline static void Sound_EnsureLoadedImpl (Sound* self, cstr func) {
  if (self->state == SoundState_Loading) {
    SoundDesc_FinishLoad(self->desc, func);
    FMODCALL(FMOD_System_PlaySound((FMOD_SYSTEM*) Audio_GetHandle(), self->desc->handle, 0, true, &self->handle));
    FMODCALL(FMOD_Channel_SetUserData(self->handle, self));
    FMODCALL(FMOD_Channel_SetCallback(self->handle, Sound_Callback));
    Sound_SetState(self, SoundState_Paused);

    if (Sound_Get3D(self)) {
      Vec3f zero = { 0, 0, 0 };
      Sound_Set3DPos(self, &zero, &zero);
    }
  }
}
#define Sound_EnsureLoaded(...) Sound_EnsureLoadedImpl(__VA_ARGS__, __func__)

inline static void Sound_EnsureNotFreedImpl (Sound* self, cstr func) {
  if (self->state == SoundState_Freed) {
    cstr name = (self->desc->_refCount > 0) ? self->desc->name : "<SoundDesc has been freed>";
    Fatal("%s: Sound has been freed.\n  Name: %s", func, name);
  }
}
#define Sound_EnsureNotFreed(...) Sound_EnsureNotFreedImpl(__VA_ARGS__, __func__)

inline static void Sound_EnsureStateImpl (Sound* self, cstr func) {
  Sound_EnsureLoadedImpl(self, func);
  Sound_EnsureNotFreedImpl(self, func);
}
#define Sound_EnsureState(...) Sound_EnsureStateImpl(__VA_ARGS__, __func__)

static void Sound_SetState (Sound* self, SoundState nextState) {
  if (nextState == self->state) return;

  switch (nextState) {
    default: Fatal("Sound_SetState: Unhandled case: %i", nextState);

    case SoundState_Loading:
      Assert(self->state == SoundState_Null);
      break;

    case SoundState_Playing:
      FMODCALL(FMOD_Channel_SetPaused(self->handle, false));
      break;

    case SoundState_Paused:
      FMODCALL(FMOD_Channel_SetPaused(self->handle, true));
      break;

    case SoundState_Finished:
      FMODCALL(FMOD_Channel_Stop(self->handle));
      break;

    case SoundState_Freed:
      /* NOTE : Deallocation is deferred to Audio_Update for performance. */
      break;
  }

  self->state = nextState;
  Audio_SoundStateChanged(self);

  if (self->freeOnFinish && self->state == SoundState_Finished)
    Sound_Free(self);
}

static Sound* Sound_Create (cstr name, bool immediate, bool isLooped, bool is3D) {
  SoundDesc* desc = SoundDesc_Load(name, immediate, isLooped, is3D);
  Sound* self = Audio_AllocSound();
  self->desc = desc;
  Sound_SetState(self, SoundState_Loading);
  return self;
}

Sound* Sound_Load (cstr name, bool isLooped, bool is3D) {
  Sound* self = Sound_Create(name, true, isLooped, is3D);
  Sound_EnsureLoaded(self);
  return self;
}

Sound* Sound_LoadAsync (cstr name, bool isLooped, bool is3D) {
  Sound* self = Sound_Create(name, false, isLooped, is3D);
  return self;
}

Sound* Sound_Clone (Sound* self) {
  Sound_EnsureState(self);
  Sound* clone = Audio_AllocSound();
  *clone = *self;
  SoundDesc_Acquire(self->desc);
  clone->handle = 0;
  clone->state = SoundState_Null;
  Sound_SetState(clone, SoundState_Loading);
  return clone;
}

void Sound_ToFile (Sound* self, cstr name) {
  Sound_EnsureState(self);
  SoundDesc_ToFile(self->desc, name);
}

void Sound_Acquire (Sound* self) {
  Sound_EnsureState(self);
  RefCounted_Acquire(self->desc);
}

void Sound_Free (Sound* self) {
  Sound_EnsureState(self);
  Sound_SetState(self, SoundState_Finished);
  Sound_SetState(self, SoundState_Freed);
  SoundDesc_Free(self->desc);
}

void Sound_Play (Sound* self) {
  Sound_EnsureState(self);
  Sound_SetState(self, SoundState_Playing);
}

void Sound_Pause (Sound* self) {
  Sound_EnsureState(self);
  Sound_SetState(self, SoundState_Paused);
}

void Sound_Rewind (Sound* self) {
  Sound_EnsureState(self);
  FMODCALL(FMOD_Channel_SetPosition(self->handle, 0, FMOD_TIMEUNIT_PCM));
}

bool Sound_Get3D (Sound* self) {
  Sound_EnsureState(self);
  FMOD_MODE mode;
  FMODCALL(FMOD_Channel_GetMode(self->handle, &mode));
  return (mode & FMOD_3D) == FMOD_3D;
}

float Sound_GetDuration (Sound* self) {
  Sound_EnsureState(self);
  return SoundDesc_GetDuration(self->desc);
}

bool Sound_GetLooped (Sound* self) {
  Sound_EnsureState(self);
  FMOD_MODE mode;
  FMODCALL(FMOD_Channel_GetMode(self->handle, &mode));
  return (mode & FMOD_LOOP_NORMAL) == FMOD_LOOP_NORMAL;
}

cstr Sound_GetName (Sound* self) {
  Sound_EnsureNotFreed(self);
  return SoundDesc_GetName(self->desc);
}

cstr Sound_GetPath (Sound* self) {
  Sound_EnsureNotFreed(self);
  return SoundDesc_GetPath(self->desc);
}

bool Sound_IsFinished (Sound* self) {
  return self->state == SoundState_Finished;
}

bool Sound_IsPlaying (Sound* self) {
  return self->state == SoundState_Playing;
}

void Sound_Attach3DPos (Sound* self, Vec3f const* pos, Vec3f const* vel) {
  //EnsureState happens in Set3DPos already
  Sound_Set3DPos(self, pos, vel);
  self->autoPos = pos;
  self->autoVel = vel;
}

void Sound_Set3DLevel (Sound* self, float level) {
  Sound_EnsureState(self);
  FMODCALL(FMOD_Channel_Set3DLevel(self->handle, level));
}

void Sound_Set3DPos (Sound* self, Vec3f const* pos, Vec3f const* vel) {
  Sound_EnsureState(self);
  Assert(sizeof(*pos) == sizeof(FMOD_VECTOR));
  FMODCALL(FMOD_Channel_Set3DAttributes(
    self->handle, (FMOD_VECTOR*) pos, (FMOD_VECTOR*) vel, 0
  ));
}

void Sound_SetFreeOnFinish (Sound* self, bool freeOnFinish) {
  self->freeOnFinish = freeOnFinish;
}

void Sound_SetPan (Sound* self, float pan) {
  Sound_EnsureState(self);
  FMODCALL(FMOD_Channel_SetPan(self->handle, pan));
}

void Sound_SetPitch (Sound* self, float pitch) {
  Sound_EnsureState(self);
  FMODCALL(FMOD_Channel_SetPitch(self->handle, pitch));
}

void Sound_SetPlayPos (Sound* self, float seconds) {
  /* NOTE : Currently this has only millisecond accuracy. */
  Sound_EnsureState(self);
  Assert(seconds >= 0.0f);
  unsigned int ms = (unsigned int) Round(seconds * 1000.0f);
  FMODCALL(FMOD_Channel_SetPosition(self->handle, ms, FMOD_TIMEUNIT_MS));
}

void Sound_SetVolume (Sound* self, float volume) {
  Sound_EnsureState(self);
  FMODCALL(FMOD_Channel_SetVolume(self->handle, volume));
}

Sound* Sound_LoadPlay (cstr name, bool isLooped, bool is3D) {
  Sound* self = Sound_Load(name, isLooped, is3D);
  Sound_Play(self);
  return self;
}

Sound* Sound_LoadPlayAttached (cstr name, bool isLooped, bool is3D, Vec3f const* pos, Vec3f const* vel) {
  Sound* self = Sound_Load(name, isLooped, is3D);
  Sound_Attach3DPos(self, pos, vel);
  Sound_Play(self);
  return self;
}

void Sound_LoadPlayFree (cstr name, bool isLooped, bool is3D) {
  Sound* self = Sound_Load(name, isLooped, is3D);
  Sound_SetFreeOnFinish(self, true);
  Sound_Play(self);
}

void Sound_LoadPlayFreeAttached (cstr name, bool isLooped, bool is3D, Vec3f const* pos, Vec3f const* vel) {
  Sound* self = Sound_Load(name, isLooped, is3D);
  Sound_Attach3DPos(self, pos, vel);
  Sound_SetFreeOnFinish(self, true);
  Sound_Play(self);
}

Sound* Sound_ClonePlay (Sound* self) {
  Sound* clone = Sound_Clone(self);
  Sound_Play(clone);
  return clone;
}

Sound* Sound_ClonePlayAttached (Sound* self, Vec3f const* pos, Vec3f const* vel) {
  Sound* clone = Sound_Clone(self);
  Sound_Attach3DPos(clone, pos, vel);
  Sound_Play(clone);
  return clone;
}

void Sound_ClonePlayFree (Sound* self) {
  Sound* clone = Sound_Clone(self);
  Sound_SetFreeOnFinish(clone, true);
  Sound_Play(clone);
}

void Sound_ClonePlayFreeAttached (Sound* self, Vec3f const* pos, Vec3f const* vel) {
  Sound* clone = Sound_Clone(self);
  Sound_Attach3DPos(clone, pos, vel);
  Sound_SetFreeOnFinish(clone, true);
  Sound_Play(clone);
}

void Sound_Update (Sound* self) {
  if (self->state == SoundState_Loading) return;

  if (Sound_Get3D(self))
    Sound_Set3DPos(self, self->autoPos, self->autoVel);
}

bool Sound_IsFreed (Sound* self) {
  return self->state == SoundState_Freed;
}

/* NOTE : We start the sound instance in the paused state so that we can change
 *        position, pitch, etc. via the Sound API *before* samples start getting
 *        mixed. */

/* NOTE : By default, 3D channels are set to the *current position* of the
 *        listener! That's confusing and almost never what we want, so we reset
 *        the position immediately for consistency. */

/* NOTE : Generally self->state is updated through callbacks triggered by
 *        FMOD_System_Update and thus is slightly stale at all times. This means
 *        a sound could have finished earlier in the frame and we won't know
 *        until the beginning of the next frame when Audio_Update is called. */

/* OPTIMIZE : Sometimes we do redundant work:
 *            - When loading a sound synchronously or pausing a currently
 *            loading sound we call FMOD_Channel_SetPaused on an already paused
 *            sound.
 *            - When freeing a loading sound we block to finish the load just to
 *            free the sound. */


text:
#include "BlendMode.h"
#include "DataFormat.h"
#include "Draw.h"
#include "Font.h"
#include "HashMap.h"
#include "PhxMemory.h"
#include "OpenGL.h"
#include "Profiler.h"
#include "RenderState.h"
#include "RefCounted.h"
#include "Resource.h"
#include "PhxMath.h"
#include "Shader.h"
#include "Tex2D.h"
#include "PixelFormat.h"
#include "TexFormat.h"
#include "Vec2.h"
#include "Vec4.h"

#include "ft2build.h"
#include FT_FREETYPE_H
#include FT_GLYPH_H
#include FT_BITMAP_H

/* TODO : Re-implement UTF-8 support */
/* TODO : Atlas instead of individual textures. */

/* NOTE : Gamma of 1.8 recommended by FreeType */
const float kGamma = 1.8f;
const float kRcpGamma = 1.0f / kGamma;

struct Glyph {
  int index;
  Tex2D* tex;
  int x0, y0, x1, y1;
  int sx, sy;
  int advance;
};

struct Font {
  RefCounted;
  FT_Face handle;
  HashMap* glyphs;
  Glyph* glyphsAscii[256];
};

static FT_Library ft = 0;

static Glyph* Font_GetGlyph (Font* self, uint32 codepoint) {
  if (codepoint < 256 && self->glyphsAscii[codepoint])
    return self->glyphsAscii[codepoint];

  Glyph* g = (Glyph*)HashMap_Get(self->glyphs, &codepoint);
  if (g)
    return g;

  FT_Face face = self->handle;
  int glyph = FT_Get_Char_Index(face, codepoint);
  if (glyph == 0)
    return 0;
  if (FT_Load_Glyph(face, glyph, FT_LOAD_FORCE_AUTOHINT | FT_LOAD_RENDER))
    return 0;

  FT_Bitmap const* bitmap = &face->glyph->bitmap;
  uchar const* pBitmap = bitmap->buffer;

  /* Create a new glyph and fill out metrics. */ {
    g = MemNew(Glyph);
    g->index = glyph;
    g->x0 = face->glyph->bitmap_left;
    g->y0 = -face->glyph->bitmap_top;
    g->sx = bitmap->width;
    g->sy = bitmap->rows;
    g->x1 = g->x0 + g->sx;
    g->y1 = g->y0 + g->sy;
    g->advance = face->glyph->advance.x >> 6;
  }

  Vec4f* buffer = MemNewArray(Vec4f, g->sx * g->sy);

  /* Copy rendered bitmap into buffer. */ {
    Vec4f* pBuffer = buffer;
    for (uint dy = 0; dy < bitmap->rows; ++dy) {
      for (uint dx = 0; dx < bitmap->width; ++dx) {
        float a = Pow((float)(pBitmap[dx]) / 255.0f, kRcpGamma);
        *pBuffer++ = Vec4f_Create(1.0f, 1.0f, 1.0f, a);
      }
      pBitmap += bitmap->pitch;
    }
  }

  /* Upload to texture. */ {
    g->tex = Tex2D_Create(g->sx, g->sy, TexFormat_RGBA8);
    Tex2D_SetData(g->tex, buffer, PixelFormat_RGBA, DataFormat_Float);
  }

  MemFree(buffer);

  /* Add to glyph cache. */
  if (codepoint < 256)
    self->glyphsAscii[codepoint] = g;
  else
    HashMap_Set(self->glyphs, &codepoint, g);
  return g;
}

inline static int Font_GetKerning (Font* self, int a, int b) {
  FT_Vector kern;
  FT_Get_Kerning(self->handle, a, b, FT_KERNING_DEFAULT, &kern);
  return kern.x >> 6;
}

Font* Font_Load (cstr name, int size) {
  if (!ft)
    FT_Init_FreeType(&ft);

  cstr path = Resource_GetPath(ResourceType_Font, name);
  Font* self = MemNew(Font);
  RefCounted_Init(self);

  if (FT_New_Face(ft, path, 0, &self->handle))
    Fatal("Font_Load: Failed to load font <%s> at <%s>", name, path);
  FT_Set_Pixel_Sizes(self->handle, 0, size);

  MemZero(self->glyphsAscii, sizeof(self->glyphsAscii));
  self->glyphs = HashMap_Create(sizeof(uint32), 16);
  return self;
}

void Font_Acquire (Font* self) {
  RefCounted_Acquire(self);
}

void Font_Free (Font* self) {
  RefCounted_Free(self) {
    /* TODO : Free glyphs! */
    FT_Done_Face(self->handle);
    MemFree(self);
  }
}

void Font_Draw (
  Font* self, cstr text,
  float x, float y,
  float r, float g, float b, float a)
{
  FRAME_BEGIN;
  int glyphLast = 0;
  uint32 codepoint = *text++;
  x = Floor(x);
  y = Floor(y);
  RenderState_PushBlendMode(BlendMode_Alpha);
  Draw_Color(r, g, b, a);

  while (codepoint) {
    Glyph* glyph = Font_GetGlyph(self, codepoint);
    if (glyph) {
      if (glyphLast)
        x += Font_GetKerning(self, glyphLast, glyph->index);
      float x0 = (float)(x + glyph->x0);
      float y0 = (float)(y + glyph->y0);
      float x1 = (float)(x + glyph->x1);
      float y1 = (float)(y + glyph->y1);
      Tex2D_DrawEx(glyph->tex, x0, y0, x1, y1, 0, 0, 1, 1);
      x += glyph->advance;
      glyphLast = glyph->index;
    } else {
      glyphLast = 0;
    }
    codepoint = *text++;
  }

  Draw_Color(1, 1, 1, 1);
  RenderState_PopBlendMode();
  FRAME_END;
}

void Font_DrawShaded (Font* self, cstr text, float x, float y) {
  FRAME_BEGIN;
  int glyphLast = 0;
  uint32 codepoint = *text++;
  x = Floor(x);
  y = Floor(y);

  while (codepoint) {
    Glyph* glyph = Font_GetGlyph(self, codepoint);
    if (glyph) {
      if (glyphLast)
        x += Font_GetKerning(self, glyphLast, glyph->index);
      float x0 = (float)(x + glyph->x0);
      float y0 = (float)(y + glyph->y0);
      float x1 = (float)(x + glyph->x1);
      float y1 = (float)(y + glyph->y1);
      Shader_SetTex2D("glyph", glyph->tex);
      Tex2D_DrawEx(glyph->tex, x0, y0, x1, y1, 0, 0, 1, 1);
      x += glyph->advance;
      glyphLast = glyph->index;
    } else {
      glyphLast = 0;
    }
    codepoint = *text++;
  }

  FRAME_END;
}

int Font_GetLineHeight (Font* self) {
  return self->handle->size->metrics.height >> 6;
}

void Font_GetSize (Font* self, Vec4i* out, cstr text) {
  FRAME_BEGIN;
  int x = 0, y = 0;
  Vec2i lower = { INT_MAX, INT_MAX };
  Vec2i upper = { INT_MIN, INT_MIN };

  int glyphLast = 0;
  uint32 codepoint = *text++;
  if (!codepoint) {
    *out = Vec4i_Create(0, 0, 0, 0);
    return;
  }

  while (codepoint) {
    Glyph* glyph = Font_GetGlyph(self, codepoint);
    if (glyph) {
      if (glyphLast)
        x += Font_GetKerning(self, glyphLast, glyph->index);
      lower.x = Min(lower.x, x + glyph->x0);
      lower.y = Min(lower.y, y + glyph->y0);
      upper.x = Max(upper.x, x + glyph->x1);
      upper.y = Max(upper.y, y + glyph->y1);
      x += glyph->advance;
      glyphLast = glyph->index;
    } else {
      glyphLast = 0;
    }
    codepoint = *text++;
  }

  *out = Vec4i_Create(lower.x, lower.y, upper.x - lower.x, upper.y - lower.y);
  FRAME_END;
}

/* NOTE : The height returned here is the maximal *ascender* height for the
 *        string. This allows easy centering of text while still allowing
 *        descending characters to look correct.
 *
 *        To correctly center text, first compute bounds via this function,
 *        then draw it at:
 *
 *           pos.x - (size.x - bound.x) / 2
 *           pos.y - (size.y + bound.y) / 2
 */

void Font_GetSize2 (Font* self, Vec2i* out, cstr text) {
  FRAME_BEGIN;
  out->x = 0;
  out->y = 0;

  int glyphLast = 0;
  uint32 codepoint = *text++;
  while (codepoint) {
    Glyph* glyph = Font_GetGlyph(self, codepoint);
    if (glyph) {
      if (glyphLast)
        out->x += Font_GetKerning(self, glyphLast, glyph->index);
      out->x += glyph->advance;
      out->y = Max(out->y, -glyph->y0 + 1);
      glyphLast = glyph->index;
    } else {
      glyphLast = 0;
    }
    codepoint = *text++;
  }

  FRAME_END;
}


text:
#include "PhxMemory.h"
#include "SDL.h"
#include "ThreadPool.h"

struct ThreadData {
  SDL_Thread* handle;
  ThreadPoolFn fn;
  int index;
  int threads;
  void* data;
};

static int ThreadPool_Dispatch (void* data) {
  ThreadData* td = (ThreadData*)data;
  return td->fn(td->index, td->threads, td->data);
};

struct ThreadPool {
  int threads;
  ThreadData* thread;
};

ThreadPool* ThreadPool_Create (int threads) {
  ThreadPool* self = MemNew(ThreadPool);
  self->threads = threads;
  self->thread = MemNewArray(ThreadData, threads);
  for (int i = 0; i < threads; ++i) {
    ThreadData* td = self->thread + i;
    td->handle = 0;
    td->fn = 0;
    td->index = i;
    td->threads = threads;
    td->data = 0;
  }
  return self;
}

void ThreadPool_Free (ThreadPool* self) {
  for (int i = 0; i < self->threads; ++i)
    if (self->thread[i].handle)
      Fatal("ThreadPool_Free: Attempting to free pool with active threads");
  MemFree(self->thread);
  MemFree(self);
}

void ThreadPool_Launch (ThreadPool* self, ThreadPoolFn fn, void* data) {
  for (int i = 0; i < self->threads; ++i) {
    ThreadData* td = self->thread + i;
    td->fn = fn;
    td->data = data;
    td->handle = SDL_CreateThread(ThreadPool_Dispatch, "PHX_ThreadPool", (void*)td);
    if (!td->handle)
      Fatal("ThreadPool_Launch: Failed to start new thread");
  }
}

void ThreadPool_Wait (ThreadPool* self) {
  for (int i = 0; i < self->threads; ++i) {
    ThreadData* td = self->thread + i;
    if (td->handle) {
      int ret;
      SDL_WaitThread(td->handle, &ret);
      td->handle = 0;
    }
  }
}


text:
#include "Array.h"
#include "State.h"

#include <stdio.h>

const State State_Null     = 0 << 0;
const State State_Changed  = 1 << 0;
const State State_Pressed  = 1 << 1;
const State State_Down     = 1 << 2;
const State State_Released = 1 << 3;

cstr State_ToString (State state) {
  static char buffer[512];

  if (state == State_Null)
    return "State_Null";

  State states[] = {
    State_Changed,
    State_Pressed,
    State_Down,
    State_Released,
  };
  cstr names[] = {
    "State_Changed",
    "State_Pressed",
    "State_Down",
    "State_Released",
  };

  char* start = buffer;
  const char* sep = "";
  int len = 0;
  for (int i = 0; i < Array_GetSize(states); i++) {
    if (HAS_FLAG(state, states[i])) {
      len += snprintf(start + len, Array_GetSize(buffer) - len, "%s%s", sep, names[i]);
      sep = " | ";
      state &= ~states[i];
    }
  }

  if (state != 0)
    len += snprintf(start + len, Array_GetSize(buffer) - len, "%sUnknown (%i)", sep, state);
  return buffer;
}

/* TODO : Maybe we should have genffi output a ToString function for enums, flags, and structs */