text:
#include "Matrix.h"
#include "ShaderVarType.h"
#include "PhxString.h"
#include "Vec2.h"
#include "Vec3.h"
#include "Vec4.h"

ShaderVarType ShaderVarType_FromStr (cstr s) {
  for (ShaderVarType i = ShaderVarType_BEGIN; i <= ShaderVarType_END; ++i)
    if (StrEqual(s, ShaderVarType_GetGLSLName(i)))
      return i;
  return ShaderVarType_None;
}

cstr ShaderVarType_GetGLSLName (ShaderVarType self) {
  switch (self) {
    case ShaderVarType_Float:   return "float";
    case ShaderVarType_Float2:  return "vec2";
    case ShaderVarType_Float3:  return "vec3";
    case ShaderVarType_Float4:  return "vec4";
    case ShaderVarType_Int:     return "int";
    case ShaderVarType_Int2:    return "ivec2";
    case ShaderVarType_Int3:    return "ivec3";
    case ShaderVarType_Int4:    return "ivec4";
    case ShaderVarType_Matrix:  return "mat4";
    case ShaderVarType_Tex1D:   return "sampler1D";
    case ShaderVarType_Tex2D:   return "sampler2D";
    case ShaderVarType_Tex3D:   return "sampler3D";
    case ShaderVarType_TexCube: return "samplerCube";
  }
  return 0;
}

cstr ShaderVarType_GetName (ShaderVarType self) {
  switch (self) {
    case ShaderVarType_Float:   return "float";
    case ShaderVarType_Float2:  return "float2";
    case ShaderVarType_Float3:  return "float3";
    case ShaderVarType_Float4:  return "float4";
    case ShaderVarType_Int:     return "int";
    case ShaderVarType_Int2:    return "int2";
    case ShaderVarType_Int3:    return "int3";
    case ShaderVarType_Int4:    return "int4";
    case ShaderVarType_Matrix:  return "Matrix";
    case ShaderVarType_Tex1D:   return "Tex1D";
    case ShaderVarType_Tex2D:   return "Tex2D";
    case ShaderVarType_Tex3D:   return "Tex3D";
    case ShaderVarType_TexCube: return "TexCube";
  }
  return 0;
}

int ShaderVarType_GetSize (ShaderVarType self) {
  switch (self) {
    case ShaderVarType_Float:   return sizeof(float);
    case ShaderVarType_Float2:  return sizeof(Vec2f);
    case ShaderVarType_Float3:  return sizeof(Vec3f);
    case ShaderVarType_Float4:  return sizeof(Vec4f);
    case ShaderVarType_Int:     return sizeof(int);
    case ShaderVarType_Int2:    return sizeof(Vec2i);
    case ShaderVarType_Int3:    return sizeof(Vec3i);
    case ShaderVarType_Int4:    return sizeof(Vec4i);
    case ShaderVarType_Matrix:  return sizeof(Matrix*);
    case ShaderVarType_Tex1D:   return sizeof(Tex1D*);
    case ShaderVarType_Tex2D:   return sizeof(Tex2D*);
    case ShaderVarType_Tex3D:   return sizeof(Tex3D*);
    case ShaderVarType_TexCube: return sizeof(TexCube*);
  }
  return 0;
}


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

void BlendMode_Pop() {
  RenderState_PopBlendMode();
}

void BlendMode_Push(BlendMode blendMode) {
  RenderState_PushBlendMode(blendMode);
}

void BlendMode_PushAdditive() {
  RenderState_PushBlendMode(BlendMode_Additive);
}

void BlendMode_PushAlpha() {
  RenderState_PushBlendMode(BlendMode_Alpha);
}

void BlendMode_PushDisabled() {
  RenderState_PushBlendMode(BlendMode_Disabled);
}

void BlendMode_PushPreMultAlpha() {
  RenderState_PushBlendMode(BlendMode_PreMultAlpha);
}

repo_name: JoshParnell/libphx

repo_language: C

mime_type: text/x-c++


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

struct Timer {
  uint64 value;
};

static double frequency = 0;

Timer* Timer_Create () {
  static bool init = false;
  if (!init) {
    init = true;
    frequency = (double)SDL_GetPerformanceFrequency();
  }

  Timer* self = MemNew(Timer);
  self->value = SDL_GetPerformanceCounter();
  return self;
}

void Timer_Free (Timer* self) {
  MemFree(self);
}

double Timer_GetAndReset (Timer* self) {
  uint64 now = SDL_GetPerformanceCounter();
  double elapsed = (double)(now - self->value) / frequency;
  self->value = now;
  return elapsed;
}

double Timer_GetElapsed (Timer* self) {
  uint64 now = SDL_GetPerformanceCounter();
  return (double)(now - self->value) / frequency;
}

void Timer_Reset (Timer* self) {
  self->value = SDL_GetPerformanceCounter();
}


text:
#include "ClipRect.h"
#include "OpenGL.h"
#include "PhxMath.h"
#include "Vec2.h"
#include "Viewport.h"

#define MAX_STACK_DEPTH 128

struct ClipRectTransform {
  float tx, ty;
  float sx, sy;
};

static ClipRectTransform transform[MAX_STACK_DEPTH];
static int transformIndex = -1;

struct ClipRect {
  float x, y;
  float sx, sy;
  bool enabled;
};

static ClipRect rect[MAX_STACK_DEPTH];
static int rectIndex = -1;

inline static void TransformRect (float* x, float* y, float* sx, float* sy) {
  if (transformIndex >= 0) {
    ClipRectTransform* curr = transform + transformIndex;
    *x = curr->sx * (*x) + curr->tx;
    *y = curr->sy * (*y) + curr->ty;
    *sx = curr->sx * (*sx);
    *sy = curr->sy * (*sy);
  }
}

void ClipRect_Activate (ClipRect* self) {
  if (self && self->enabled) {
    Vec2i vpSize;
    Viewport_GetSize(&vpSize);
    GLCALL(glEnable(GL_SCISSOR_TEST))
    float x = self->x;
    float y = self->y;
    float sx = self->sx;
    float sy = self->sy;
    TransformRect(&x, &y, &sx, &sy);
    GLCALL(glScissor(
      (int)x,
      vpSize.y - (int)(y + sy),
      (int)sx,
      (int)sy))
  } else {
    GLCALL(glDisable(GL_SCISSOR_TEST))
  }
}

void ClipRect_Push (float x, float y, float sx, float sy) {
  if (rectIndex + 1 >= MAX_STACK_DEPTH)
    Fatal("ClipRect_Push: Maximum stack depth exceeded");
  rectIndex++;
  ClipRect* curr = rect + rectIndex;
  curr->x = x;
  curr->y = y;
  curr->sx = sx;
  curr->sy = sy;
  curr->enabled = true;
  ClipRect_Activate(curr);
}

void ClipRect_PushCombined (float x, float y, float sx, float sy) {
  ClipRect* curr = rect + rectIndex;
  if (rectIndex >= 0 && curr->enabled) {
    float maxX = x + sx;
    float maxY = y + sy;
    x = Max(x, curr->x);
    y = Max(y, curr->y);

    ClipRect_Push(x, y,
      Min(maxX, curr->x + curr->sx) - x,
      Min(maxY, curr->y + curr->sy) - y
   );
  } else {
    ClipRect_Push(x, y, sx, sy);
  }
}

void ClipRect_PushDisabled () {
  if (rectIndex + 1 >= MAX_STACK_DEPTH)
    Fatal("ClipRect_Push: Maximum stack depth exceeded");
  rectIndex++;
  ClipRect* curr = rect + rectIndex;
  curr->enabled = false;
  ClipRect_Activate(curr);
}

void ClipRect_PushTransform (float tx, float ty, float sx, float sy) {
  if (transformIndex + 1 >= MAX_STACK_DEPTH)
    Fatal("ClipRect_PushTransform: Maximum stack depth exceeded");
  transformIndex++;
  ClipRectTransform* curr = transform + transformIndex;
  curr->tx = tx;
  curr->ty = ty;
  curr->sx = sx;
  curr->sy = sy;
  if (rectIndex >= 0)
    ClipRect_Activate(rect + rectIndex);
}

void ClipRect_Pop () {
  if (rectIndex < 0)
    Fatal("ClipRect_Pop: Attempting to pop an empty stack");
  rectIndex--;
  ClipRect_Activate(rectIndex >= 0 ? rect + rectIndex : 0);
}

void ClipRect_PopTransform () {
  if (transformIndex < 0)
    Fatal("ClipRect_PopTransform: Attempting to pop an empty stack");
  transformIndex--;
  if (rectIndex >= 0)
    ClipRect_Activate(rect + rectIndex);
}


text:
static void HmGui_InitWidget (HmGuiWidget* e, uint32 type) {
  e->parent = self.group;
  e->next = 0;
  e->prev = self.group ? self.group->tail : 0;

  if (e->parent) {
    e->parent->children++;
    e->hash = Hash_FNV64_Incremental(
      e->parent->hash,
      &e->parent->children,
      sizeof(e->parent->children));
    (e->next ? e->next->prev : e->parent->tail) = e;
    (e->prev ? e->prev->next : e->parent->head) = e;
  } else {
    e->hash = Hash_FNV64_Init();
  }

  e->type = type;
  e->pos = Vec2f_Create(0, 0);
  e->size = Vec2f_Create(0, 0);
  e->minSize = Vec2f_Create(0, 0);
  e->align = Vec2f_Create(0, 0);
  e->stretch = Vec2f_Create(0, 0);

  self.last = e;
}

static void HmGui_BeginGroup (uint32 layout) {
  HmGuiGroup* e = MemNew(HmGuiGroup);
  HmGui_InitWidget(e, Widget_Group);
  e->head = 0;
  e->tail = 0;
  e->layout = layout;
  e->children = 0;
  e->focusStyle = FocusStyle_None;
  e->paddingLower = Vec2f_Create(0, 0);
  e->paddingUpper = Vec2f_Create(0, 0);
  e->offset = Vec2f_Create(0, 0);
  e->maxSize = Vec2f_Create(1e30f, 1e30f);
  e->spacing = self.style->spacing;
  e->frameOpacity = 0.0f;
  e->clip = false;
  e->expand = true;
  for (int i = 0; i < FocusType_SIZE; ++i) e->focusable[i] = false;
  e->storeSize = false;
  self.group = e;

  switch (layout) {
    case Layout_Stack:
      e->stretch = Vec2f_Create(1, 1);
      break;
    case Layout_Vertical:
      e->stretch = Vec2f_Create(1, 0);
      break;
    case Layout_Horizontal:
      e->stretch = Vec2f_Create(0, 1);
      break;
  }
}

static void HmGui_FreeText (HmGuiText* e) {
  StrFree(e->text);
  MemFree(e);
}

static void HmGui_FreeGroup (HmGuiGroup* g) {
  HmGuiWidget* e = g->head;
  while (e) {
    HmGuiWidget* next = e->next;
    switch (e->type) {
      case Widget_Group: HmGui_FreeGroup((HmGuiGroup*)e); break;
      case Widget_Text: HmGui_FreeText((HmGuiText*)e); break;
      default: MemFree(e); break;
    }
    e = next;
  }
  MemFree(g);
}

/* -------------------------------------------------------------------------- */

static HmGuiData* HmGui_GetData (HmGuiGroup* g) {
  HmGuiData* data = (HmGuiData*)HashMap_GetRaw(self.data, g->hash);
  if (!data) {
    data = MemNew(HmGuiData);
    data->offset = Vec2f_Create(0, 0);
    data->minSize = Vec2f_Create(0, 0);
    data->size = Vec2f_Create(0, 0);
    HashMap_SetRaw(self.data, g->hash, data);
  }
  return data;
}

/* -------------------------------------------------------------------------- */

static void HmGui_PushClipRect (HmGuiGroup* g) {
  HmGuiClipRect* rect = MemNew(HmGuiClipRect);
  rect->prev = self.clipRect;
  rect->lower = g->pos;
  rect->upper = Vec2f_Add(g->pos, g->size);
  if (rect->prev) {
    rect->lower.x = Max(rect->lower.x, rect->prev->lower.x);
    rect->lower.y = Max(rect->lower.y, rect->prev->lower.y);
    rect->upper.x = Min(rect->upper.x, rect->prev->upper.x);
    rect->upper.y = Min(rect->upper.y, rect->prev->upper.y);
  }
  self.clipRect = rect;
}

static void HmGui_PopClipRect () {
  HmGuiClipRect* rect = self.clipRect;
  self.clipRect = rect->prev;
  MemFree(rect);
}

/* -------------------------------------------------------------------------- */

static void HmGui_ComputeSize (HmGuiGroup* g) {
  for (HmGuiWidget* e = g->head; e; e = e->next)
    if (e->type == Widget_Group)
      HmGui_ComputeSize((HmGuiGroup*)e);

  g->minSize = Vec2f_Create(0, 0);

  for (HmGuiWidget* e = g->head; e; e = e->next) {
    switch (g->layout) {
      case Layout_Stack:
        g->minSize.x = Max(g->minSize.x, e->minSize.x);
        g->minSize.y = Max(g->minSize.y, e->minSize.y);
        break;
      case Layout_Vertical:
        g->minSize.x  = Max(g->minSize.x, e->minSize.x);
        g->minSize.y += e->minSize.y;
        if (e != g->head) g->minSize.y += g->spacing;
        break;
      case Layout_Horizontal:
        g->minSize.x += e->minSize.x;
        g->minSize.y  = Max(g->minSize.y, e->minSize.y);
        if (e != g->head) g->minSize.x += g->spacing;
        break;
    }
  }

  g->minSize.x += g->paddingLower.x + g->paddingUpper.x;
  g->minSize.y += g->paddingLower.y + g->paddingUpper.y;

  if (g->storeSize) {
    HmGuiData* data = HmGui_GetData(g);
    data->minSize = g->minSize;
  }

  g->minSize.x = Min(g->minSize.x, g->maxSize.x);
  g->minSize.y = Min(g->minSize.y, g->maxSize.y);
}

static void HmGui_LayoutWidget (HmGuiWidget* e, Vec2f pos, float sx, float sy) {
  e->pos = pos;
  e->size = e->minSize;
  e->size.x += e->stretch.x * (sx - e->minSize.x);
  e->size.y += e->stretch.y * (sy - e->minSize.y);
  e->pos.x += e->align.x * (sx - e->size.x);
  e->pos.y += e->align.y * (sy - e->size.y);
}

static void HmGui_LayoutGroup (HmGuiGroup* g) {
  Vec2f pos = g->pos;
  Vec2f size = g->size;
  float extra = 0;
  float totalStretch = 0;

  pos.x += g->paddingLower.x + g->offset.x;
  pos.y += g->paddingLower.y + g->offset.y;
  size.x -= g->paddingLower.x + g->paddingUpper.x;
  size.y -= g->paddingLower.y + g->paddingUpper.y;

  if (g->expand) {
    if (g->layout == Layout_Vertical) {
      extra = g->size.y - g->minSize.y;
      for (HmGuiWidget* e = g->head; e; e = e->next)
        totalStretch += e->stretch.y;
    } else if (g->layout == Layout_Horizontal) {
      extra = g->size.x - g->minSize.x;
      for (HmGuiWidget* e = g->head; e; e = e->next)
        totalStretch += e->stretch.x;
    }

    if (totalStretch > 0)
      extra /= totalStretch;
  }

  float s;
  for (HmGuiWidget* e = g->head; e; e = e->next) {
    switch (g->layout) {
      case Layout_None:
        HmGui_LayoutWidget(e, e->pos, size.x, size.y);
        break;
      case Layout_Stack:
        HmGui_LayoutWidget(e, pos, size.x, size.y);
        break;
      case Layout_Vertical:
        s = e->minSize.y;
        if (extra > 0) s += e->stretch.y * extra;
        HmGui_LayoutWidget(e, pos, size.x, s);
        pos.y += e->size.y + g->spacing;
        break;
      case Layout_Horizontal:
        s = e->minSize.x;
        if (extra > 0) s += e->stretch.x * extra;
        HmGui_LayoutWidget(e, pos, s, size.y);
        pos.x += e->size.x + g->spacing;
        break;
    }

    if (e->type == Widget_Group)
      HmGui_LayoutGroup((HmGuiGroup*)e);
  }

  if (g->storeSize) {
    HmGuiData* data = HmGui_GetData(g);
    data->size = g->size;
  }
}

inline static bool IsClipped (HmGuiGroup* g, Vec2f p) {
  return
    p.x < g->pos.x ||
    p.y < g->pos.y ||
    g->pos.x + g->size.x < p.x ||
    g->pos.y + g->size.y < p.y;
}

static void HmGui_CheckFocus (HmGuiGroup* g) {
  if (g->clip && IsClipped(g, self.focusPos))
    return;

  for (HmGuiWidget* e = g->tail; e; e = e->prev)
    if (e->type == Widget_Group)
      HmGui_CheckFocus((HmGuiGroup*)e);

  for (int i = 0; i < FocusType_SIZE; ++i) {
    if (self.focus[i] == 0 && g->focusable[i]) {
      if (g->pos.x <= self.focusPos.x &&
          g->pos.y <= self.focusPos.y &&
          self.focusPos.x <= g->pos.x + g->size.x &&
          self.focusPos.y <= g->pos.y + g->size.y)
      {
        self.focus[i] = g->hash;
      }
    }
  }
}

/* -------------------------------------------------------------------------- */

static void HmGui_DrawText (HmGuiText* e) {
#if HMGUI_DRAW_GROUP_FRAMES
  Draw_Color(0.5f, 0.2f, 0.2f, 0.5f);
  Draw_Border(1.0f, e->pos.x, e->pos.y, e->size.x, e->size.y);
#endif

  UIRenderer_Text(e->font, e->text, e->pos.x, e->pos.y + e->minSize.y, UNPACK4(e->color));
}

static void HmGui_DrawRect (HmGuiRect* e) {
  UIRenderer_Rect(e->pos.x, e->pos.y, e->size.x, e->size.y, UNPACK4(e->color), false);
}

static void HmGui_DrawImage (HmGuiImage* e) {
  UIRenderer_Image(e->image, e->pos.x, e->pos.y, e->size.x, e->size.y);
}

static void HmGui_DrawGroup (HmGuiGroup* g) {
#if HMGUI_DRAW_GROUP_FRAMES
  Draw_Color(0.2f, 0.2f, 0.2f, 0.5f);
  Draw_Border(2.0f, g->pos.x, g->pos.y, g->size.x, g->size.y);
#endif

  UIRenderer_BeginLayer(g->pos.x, g->pos.y, g->size.x, g->size.y, g->clip);

  for (HmGuiWidget* e = g->tail; e; e = e->prev) {
    switch (e->type) {
      case Widget_Group: HmGui_DrawGroup((HmGuiGroup*)e); break;
      case Widget_Text: HmGui_DrawText((HmGuiText*)e); break;
      case Widget_Rect: HmGui_DrawRect((HmGuiRect*)e); break;
      case Widget_Image: HmGui_DrawImage((HmGuiImage*)e); break;
    }
  }

  if (g->focusable[FocusType_Mouse]) {
    bool focus = self.focus[FocusType_Mouse] == g->hash;
    if (g->focusStyle == FocusStyle_None) {
      UIRenderer_Panel(g->pos.x, g->pos.y, g->size.x, g->size.y, 0.1f, 0.12f, 0.13f, 1.0f, 8.0f, g->frameOpacity);
    }

    else if (g->focusStyle == FocusStyle_Fill) {
      if (focus)
        UIRenderer_Panel(g->pos.x, g->pos.y, g->size.x, g->size.y, 0.1f, 0.5f, 1.0f, 1.0f, 0.0f, 1.0f);
      else
        UIRenderer_Panel(g->pos.x, g->pos.y, g->size.x, g->size.y, 0.15f, 0.15f, 0.15f, 0.8f, 0.0f, g->frameOpacity);
    }

    else if (g->focusStyle == FocusStyle_Outline) {
      if (focus) {
        UIRenderer_Rect(g->pos.x, g->pos.y, g->size.x, g->size.y, 0.1f, 0.5f, 1.0f, 1.0f, true);
      }
    }

    else if (g->focusStyle == FocusStyle_Underline) {
      UIRenderer_Rect(g->pos.x, g->pos.y, g->size.x, g->size.y, 0.3f, 0.3f, 0.3f, focus ? 0.5f : g->frameOpacity, false);
    }
  }

  UIRenderer_EndLayer();
}

/* -------------------------------------------------------------------------- */


text:
#include "Gamepad.h"
#include "GamepadAxis.h"
#include "GamepadButton.h"
#include "LinkedList.h"
#include "PhxMath.h"
#include "PhxMemory.h"
#include "SDL.h"
#include "TimeStamp.h"

struct Gamepad {
  LinkedListCell(gamepadList, Gamepad);
  SDL_GameController* handle;
  TimeStamp lastActive;
  double axisState[GamepadAxis_SIZE];
  double axisLast[GamepadAxis_SIZE];
  double deadzone[GamepadAxis_SIZE];
  bool buttonState[GamepadButton_SIZE];
  bool buttonLast[GamepadButton_SIZE];
};

static LinkedList(gamepadList, Gamepad) = 0;

static void Gamepad_UpdateState (Gamepad* self) {
  TimeStamp now = TimeStamp_Get();
  for (GamepadAxis i = GamepadAxis_BEGIN; i <= GamepadAxis_END; ++i) {
    double state = Gamepad_GetAxis(self, i);
    if (self->axisState[i] != state)
      self->lastActive = now;
    self->axisLast[i] = self->axisState[i];
    self->axisState[i] = state;
  }

  for (GamepadButton i = GamepadButton_BEGIN; i <= GamepadButton_END; ++i) {
    bool state = Gamepad_GetButton(self, i);
    if (self->buttonState[i] != state)
      self->lastActive = now;
    self->buttonLast[i] = self->buttonState[i];
    self->buttonState[i] = state;
  }
}

bool Gamepad_CanOpen (int index) {
  return SDL_IsGameController(index) == SDL_TRUE;
}

Gamepad* Gamepad_Open (int index) {
  SDL_GameController* handle = SDL_GameControllerOpen(index);
  if (!handle)
    return 0;
  Gamepad* self = MemNewZero(Gamepad);
  self->handle = handle;
  self->lastActive = TimeStamp_Get();
  LinkedList_Insert(gamepadList, self);
  Gamepad_UpdateState(self);
  return self;
}

void Gamepad_Close (Gamepad* self) {
  LinkedList_Remove(gamepadList, self);
  SDL_GameControllerClose(self->handle);
  MemFree(self);
}

int Gamepad_AddMappings (cstr file) {
  return SDL_GameControllerAddMappingsFromFile(file);
}

double Gamepad_GetAxis (Gamepad* self, GamepadAxis axis) {
  double value = (double)SDL_GameControllerGetAxis(
    self->handle, (SDL_GameControllerAxis)axis) / 32767.0;
  double deadzone = self->deadzone[axis];
  if (value >  deadzone) return (value - deadzone) / (1.0 - deadzone);
  if (value < -deadzone) return (value + deadzone) / (1.0 - deadzone);
  return 0.0;
}

double Gamepad_GetAxisDelta (Gamepad* self, GamepadAxis axis) {
  return self->axisState[axis] - self->axisLast[axis];
}

bool Gamepad_GetButton (Gamepad* self, GamepadButton button) {
  return SDL_GameControllerGetButton(self->handle, (SDL_GameControllerButton)button) == 1;
}

double Gamepad_GetButtonPressed (Gamepad* self, GamepadButton button) {
  return (self->buttonState[button] && !self->buttonLast[button]) ? 1.0 : 0.0;
}

double Gamepad_GetButtonReleased (Gamepad* self, GamepadButton button) {
  return (!self->buttonState[button] && self->buttonLast[button]) ? 1.0 : 0.0;
}

double Gamepad_GetIdleTime (Gamepad* self) {
  return TimeStamp_GetElapsed(self->lastActive);
}

int Gamepad_GetID (Gamepad* self) {
  SDL_Joystick* joystick = SDL_GameControllerGetJoystick(self->handle);
  if (!joystick)
    return -1;
  return SDL_JoystickInstanceID(joystick);
}

cstr Gamepad_GetName (Gamepad* self) {
  return SDL_GameControllerName(self->handle);
}

bool Gamepad_IsConnected (Gamepad* self) {
  return SDL_GameControllerGetAttached(self->handle) == SDL_TRUE;
}

void Gamepad_SetDeadzone (Gamepad* self, GamepadAxis axis, double deadzone) {
  self->deadzone[axis] = deadzone;
}

void Gamepad_Update () {
  LinkedList_ForEach(gamepadList, Gamepad, self)
    Gamepad_UpdateState(self);
}


text:
#include "ArrayList.h"
#include "Mesh.h"
#include "PhxString.h"
#include "Vertex.h"

#include <errno.h>

/* TODO : See if it's cleaner to use sscanf */
/* TODO : Should the Mesh API have a mechanism for checking degenerate polygons? */

/* ROBUSTNESS : Support \ line continuations */
/* ROBUSTNESS : Fatal when there are extra elements on a line */

/* NOTE :
 *   - Fatal() on any error
 *   - Standard supports polygonal and free form objects
 *   - Coordinate system is right handed
 *   - Rational curves and surfaces have a w-coord in geometric vertices
 *   - Vertex normals supercede smoothing groups
 *   - Each vertex type can appear in multiple spacially disjoint groups
 *   - Negative index values indicate a relative index
 *   - Group, display, and render statements are stateful (remembered until changed)
 *   - Not validating mesh integrity. If desired, this should be done in a common place for all meshes.
 *
 *   SUPPORTED
 *   - #          comment
 *   - v          geometric vertice, 3-4 floats
 *   - vt         texture vertices, 1-3 floats, defaults to 0
 *   - vn         vertex normals
 *   - f          triangles / vertices
 *                  1-3 integers separated by '/' iv[/ivt/ivn],
 *                  3-4(?) sets of indices (triangle or quadrilateral)
 *                  "//" when skipping ivt
 *                  no spaces
 *                  slashes must be consistent across entire line
 *                  "There may be more than one series of geometric vertex/texture vertex/vertex normal numbers on a line." ???
 *
 *   UNSUPPORTED, NON-FAILING
 *   - p          point
 *   - l          line
 *   - g          grouping
 *                  may have multiple space delimited groups
 *                  letters, numbers, ???
 *                  defaults to "default"
 *   - s          smoothing group
 *                0 or "off" disable smoothing and are supported
 *                other values fail
 *   - o          object name
 *   - maplib
 *   - usemap
 *   - usemtl     material name
 *   - mtllib     material library
 *
 *   UNSUPPORTED, FAILING
 *   - vp         parameter space vertices
 *   - cstype     curve or surface type
 *   - deg        degree
 *   - bmat       basis matrix
 *   - step       step size
 *   - fo         face outline, deprecated in 2.11
 *
 *   - curv       curve
 *   - curve2     2D curve
 *   - surf       surface
 *
 *   - param      parameter values
 *   - trim       outer trimming loop
 *   - hole       inner trimming loop
 *   - scrv       special curve
 *   - sp         special point
 *   - end        end statement
 *
 *   - con        connect
 *
 *   - mg         merging group
 *
 *   - bevel      bevel interpolation
 *   - c_interp   color interoplation
 *   - d_interp   dissolve interpolation
 *   - lod        level of detail ***
 *   - shadow_obj shadow casting
 *   - trace_obj  ray tracing
 *   - ctech      curve approximation technique
 *   - stech      surface approximation technique
 *
 *   - bsp        superseded in 3.0
 *   - bzp        superseded in 3.0
 *   - cdc        superseded in 3.0
 *   - cdp        superseded in 3.0
 *   - res        superseded in 3.0
 */

struct ParseState {
  const char* cursor;
  const char* endOfData;
  const char* lineStart;
  int32 lineNumber;
};

static void Obj_Fatal (cstr message, ParseState* s) {
  int32 len = 0;
  const char* ch = s->lineStart;
  while (ch < s->endOfData && *ch != '\r' && *ch != '\n') {
    ch++;
    len += 1;
  }

  char* line = (char*) MemAlloc(len + 1);
  MemCpy(line, s->lineStart, len);
  line[len] = 0;

  Fatal("%s Line %i\n%s", message, s->lineNumber, line);
}

static bool ConsumeRestOfLine (ParseState* s) {
  const char* oldPosition = s->cursor;

  while (s->cursor < s->endOfData && *s->cursor != '\r' && *s->cursor != '\n')
    s->cursor++;

  int32 cr = 0;
  int32 nl = 0;
  while (s->cursor < s->endOfData && (*s->cursor == '\r' || *s->cursor == '\n')) {
    if (*s->cursor == '\r') {
      if (cr == 1) {
        cr = nl = 0;
        s->lineNumber++;
      }
      cr++;
    }
    if (*s->cursor == '\n') {
      if (nl == 1) {
        cr = nl = 0;
        s->lineNumber++;
      }
      nl++;
    }
    s->cursor++;
  }

  return s->cursor != oldPosition;
}

static bool ConsumeWhitespace (ParseState* s) {
  const char* oldPosition = s->cursor;

  while (s->cursor < s->endOfData && (*s->cursor == ' ' || *s->cursor == '\t'))
    s->cursor++;

  return s->cursor != oldPosition;
}

static bool ConsumeToken (char* token, int32 tokenLen, ParseState* s) {
  int32 i = 0;

  while (s->cursor < s->endOfData && i < tokenLen - 1
     && *s->cursor != ' '  && *s->cursor != '\t'
     && *s->cursor != '\r' && *s->cursor != '\n') {
    token[i++] = *s->cursor;
    s->cursor++;
  }
  token[i] = 0;

  return i != 0;
}

static bool ConsumeFloat (float* value, ParseState* s) {
  char* afterFloat;
  float f = strtof(s->cursor, &afterFloat);

  if (errno == ERANGE)
    Obj_Fatal("Parsed float in .obj data is out of range.", s);

  if (afterFloat != s->cursor) {
    s->cursor = afterFloat;
    *value = f;
    return true;
  }

  return false;
}

static bool ConsumeInt (int32* value, ParseState* s) {
  char* afterInt;
  int32 i = strtol(s->cursor, &afterInt, 10);

  if (errno == ERANGE)
    Obj_Fatal("Parsed int in .obj data is out of range.", s);

  if (afterInt != s->cursor) {
    s->cursor = afterInt;
    *value = i;
    return true;
  }

  return false;
}

static bool ConsumeCharacter (char character, ParseState* s) {
  if (s->cursor < s->endOfData && *s->cursor == character) {
    s->cursor++;
    return true;
  }

  return false;
}

Mesh* Mesh_FromObj (cstr bytes) {
  int bytesSize = StrLen(bytes);

  ParseState s = { 0 };
  s.cursor    = bytes;
  s.endOfData = s.cursor + bytesSize; //One past last

  Mesh* mesh = Mesh_Create();
  int32 vertexCount = 0;
  int32 indexCount  = 0;
  int32 faceCount   = 0;

  ArrayList(Vec3f, positions); ArrayList_Init(positions);
  ArrayList(Vec2f, uvs);       ArrayList_Init(uvs);
  ArrayList(Vec3f, normals);   ArrayList_Init(normals);

  /* OPTIMIZE : These heuristics could be made better by analyzing more meshes */
  /* NOTE : This makes no real difference right now. Bottleneck is elsewhere. */
  ArrayList_Reserve(positions, (int32) (.008f * bytesSize));
  ArrayList_Reserve(uvs,       (int32) (.008f * bytesSize));
  ArrayList_Reserve(normals,   (int32) (.008f * bytesSize));
  Mesh_ReserveIndexData(mesh,  (int32) (.050f * bytesSize));
  Mesh_ReserveVertexData(mesh, (int32) (.050f * bytesSize));

  while (true) {
    s.lineStart = s.cursor;
    s.lineNumber++;

    #define MAX_TOKEN_LEN 16
    char token[MAX_TOKEN_LEN];

    ConsumeWhitespace(&s);
    ConsumeToken(token, MAX_TOKEN_LEN, &s);
    ConsumeWhitespace(&s);

    if (StrEqual(token, "")) {
      //End of file
      if (s.cursor >= s.endOfData)
        break;

      //Blank line
    }
    //Geometric Vertex
    else if (StrEqual(token, "v")) {
      if (positions_size == INT32_MAX)
        Obj_Fatal(".obj data contains more vertex positions than will fit in an ArrayList.", &s);

      Vec3f p;
      if (!(ConsumeFloat(&p.x, &s) && ConsumeFloat(&p.y, &s) && ConsumeFloat(&p.z, &s)))
        Obj_Fatal("Failed to parse geometric vertex from .obj data.", &s);

      ArrayList_Append(positions, p);
    }
    //Texture Vertex
    else if (StrEqual(token, "vt")) {
      if (uvs_size == INT32_MAX)
        Obj_Fatal(".obj data contains more UVs than will fit in an ArrayList.", &s);

      Vec2f uv;
      if (!(ConsumeFloat(&uv.x, &s) && ConsumeFloat(&uv.y, &s)))
        Obj_Fatal("Failed to parse texture vertex from .obj data.", &s);

      ArrayList_Append(uvs, uv);
    }
    //Vertex Normal
    else if (StrEqual(token, "vn")) {
      if (normals_size == INT32_MAX)
        Obj_Fatal(".obj data contains more normals than will fit in an ArrayList.", &s);

      Vec3f n;
      if (!(ConsumeFloat(&n.x, &s) && ConsumeFloat(&n.y, &s) && ConsumeFloat(&n.z, &s)))
        Obj_Fatal("Failed to parse vertex normal from .obj data.", &s);

      ArrayList_Append(normals, n);
    }
    //Face
    else if (StrEqual(token, "f")) {
      struct VertexIndices
      {
        int32 iP;
        int32 iN;
        int32 iUV;
      };

      int32 vertexIndicesCount = 0;
      VertexIndices vertexIndices[4] = {};

      while (s.cursor < s.endOfData && *s.cursor != '\r' && *s.cursor != '\n') {
        VertexIndices* face = &vertexIndices[vertexIndicesCount];

        face->iUV = INT32_MIN;
        face->iN  = INT32_MIN;

        if (!ConsumeInt(&face->iP, &s))
          Obj_Fatal("Failed to parse face vertex index from .obj data.", &s);

        if (ConsumeCharacter('/', &s)) {
          ConsumeInt(&face->iUV, &s);

          if (ConsumeCharacter('/', &s)) {
            ConsumeInt(&face->iN, &s);
          }
        }
        vertexIndicesCount++;

        ConsumeWhitespace(&s);
      }

      for (int32 i = 0; i < vertexIndicesCount; i++) {
        if (vertexCount == INT32_MAX)
          Obj_Fatal(".obj data contains more vertex indices than will fit in an ArrayList.", &s);

        VertexIndices* face = &vertexIndices[i];
        Vertex vertex = {};

        face->iP += (face->iP < 0) ? positions_size : -1;
        if (face->iP < 0 || face->iP >= positions_size)
          Obj_Fatal("Face vertex index is out of range in .obj data", &s);
        vertex.p = positions_data[face->iP];

        if (face->iN != INT32_MIN) {
          face->iN += (face->iN < 0) ? normals_size : -1;
          if (face->iN < 0 || face->iN >= normals_size)
            Obj_Fatal("Face normal index is out of range in .obj data", &s);
          vertex.n = normals_data[face->iN];
        }

        if (face->iUV != INT32_MIN) {
          face->iUV += (face->iUV < 0) ? uvs_size : -1;
          if (face->iUV < 0 || face->iUV >= uvs_size)
            Obj_Fatal("Face UV index is out of range in .obj data", &s);
          vertex.uv = uvs_data[face->iUV];
        }

        vertexCount++;
        Mesh_AddVertexRaw(mesh, &vertex);
      }

      if (indexCount >= INT32_MAX - vertexIndicesCount)
        Obj_Fatal(".obj data contains more vertex indices than will fit in an ArrayList", &s);

      /* NOTE : Ensure the mesh doesn't have any degenerate polygons (i.e. where
       *        one of the vertex positions is equal to one or more other
       *        positions). */
      Vertex* vertices = Mesh_GetVertexData(mesh);
      int32 verticesLen = Mesh_GetVertexCount(mesh);
      for (int32 i = 0; i < vertexIndicesCount; i++) {
        for (int32 j = i + 1; j < vertexIndicesCount; j++) {
          Vec3f p1 = vertices[verticesLen - vertexIndicesCount + i].p;
          Vec3f p2 = vertices[verticesLen - vertexIndicesCount + j].p;
          if (Vec3f_Equal(p1, p2))
            Obj_Fatal(".obj data contains a degenerate polygon.", &s);
        }
      }

      if (vertexIndicesCount == 3) {
        faceCount  += 1;
        indexCount += vertexIndicesCount;
        Mesh_AddTri(mesh, vertexCount - 3, vertexCount - 2, vertexCount - 1);
      }
      else if (vertexIndicesCount == 4) {
        faceCount  += 2;
        indexCount += vertexIndicesCount;
        Mesh_AddQuad(mesh, vertexCount - 4, vertexCount - 3, vertexCount - 2, vertexCount - 1);
      }
      else {
        Obj_Fatal(".obj data has an unexpected number of vertices in a face", &s);
      }
    }
    //Unsupported
    else {
      //Non-fatal
      if (StrEqual(token, "#"     )
       || StrEqual(token, "f"     )
       || StrEqual(token, "s"     )
       || StrEqual(token, "p"     )
       || StrEqual(token, "l"     )
       || StrEqual(token, "g"     )
       || StrEqual(token, "o"     )
       || StrEqual(token, "maplib")
       || StrEqual(token, "usemap")
       || StrEqual(token, "usemtl")
       || StrEqual(token, "mtllib")) {
        //Do nothing
      }
      //Fatal
      else {
        Obj_Fatal("Unsupported token in .obj data.", &s);
      }
    }

    ConsumeRestOfLine(&s);
  }

  ArrayList_Free(positions);
  ArrayList_Free(uvs);
  ArrayList_Free(normals);

  return mesh;
}


text:
#include "ArrayList.h"
#include "Audio.h"
#include "FMODError.h"
#include "MemPool.h"
#include "PhxMath.h"
#include "Sound.h"
#include "SoundDef.h"
#include "StrMap.h"
#include "Vec3.h"
#include "fmod/fmod.h"

#define AUDIO_CHANNELS 1024
#define SOUNDPOOL_BLOCK_SIZE 128

struct Audio {
  FMOD_SYSTEM*      handle;
  StrMap*           descMap;
  MemPool*          soundPool;
  ArrayList(Sound*, playingSounds);
  ArrayList(Sound*, freeingSounds);

  Vec3f const* autoPos;
  Vec3f const* autoVel;
  Vec3f const* autoFwd;
  Vec3f const* autoUp;
} static self;

void Audio_Init () {
    /* Initialize Debugging. */ {
      FMOD_DEBUG_FLAGS flags = 0;
      NCHECK(flags |= FMOD_DEBUG_LEVEL_NONE);
      CHECK1(flags |= FMOD_DEBUG_LEVEL_ERROR);
      CHECK1(flags |= FMOD_DEBUG_LEVEL_WARNING);
      CHECK2(flags |= FMOD_DEBUG_LEVEL_LOG);

      FMOD_RESULT res;
      res = FMOD_Debug_Initialize(flags, FMOD_DEBUG_MODE_FILE, 0, "log/fmod.txt");
      if (res != FMOD_OK && res != FMOD_ERR_UNSUPPORTED)
        FMODCALL(res);
    }

  /* Initialize FMOD. */ {
    FMODCALL(FMOD_System_Create(&self.handle));

    uint version;
    FMODCALL(FMOD_System_GetVersion(self.handle, &version));
    if (version < FMOD_VERSION)
      Fatal("Audio_Create: FMOD library link/compile version mismatch");

    /* NOTE : The fake HRTF mentioned in FMOD_INIT_CHANNEL_LOWPASS and
     *        FMOD_ADVANCEDSETTINGS has been removed from FMOD.
     *        http://www.fmod.org/questions/question/hrtf-does-not-appear-to-work/ */

    FMOD_INITFLAGS flags = 0;
    flags |= FMOD_INIT_NORMAL;
    flags |= FMOD_INIT_3D_RIGHTHANDED;
    flags |= FMOD_INIT_CHANNEL_DISTANCEFILTER;
    CHECK2(flags |= FMOD_INIT_PROFILE_ENABLE);
    FMODCALL(FMOD_System_Init(self.handle, AUDIO_CHANNELS, flags, 0));
  }

  /* Initialize audio instance data. */ {
    self.descMap = StrMap_Create(128);
    self.soundPool = MemPool_Create(sizeof(Sound), SOUNDPOOL_BLOCK_SIZE);
  }
}

void Audio_Free () {
  FMODCALL(FMOD_System_Release(self.handle));
  StrMap_Free(self.descMap);
  MemPool_Free(self.soundPool);
  ArrayList_Free(self.playingSounds);
  ArrayList_Free(self.freeingSounds);
}

void Audio_AttachListenerPos (Vec3f const* pos, Vec3f const* vel, Vec3f const* fwd, Vec3f const* up) {
  self.autoPos = pos;
  self.autoVel = vel;
  self.autoFwd = fwd;
  self.autoUp  = up;
  Audio_SetListenerPos(pos, vel, fwd, up);
}

void Audio_Set3DSettings (float doppler, float scale, float rolloff) {
  FMODCALL(FMOD_System_Set3DSettings(self.handle, doppler, scale, rolloff));
}

void Audio_SetListenerPos (
  Vec3f const* pos,
  Vec3f const* vel,
  Vec3f const* fwd,
  Vec3f const* up)
{
  Assert(sizeof(*pos) == sizeof(FMOD_VECTOR));
  Assert(!fwd || Approx(Vec3f_Length(*fwd), 1));
  Assert(!up || Approx(Vec3f_Length(*up), 1));
  Assert(!fwd || !up || Approx(Vec3f_Dot(*fwd, *up), 0));

  FMODCALL(FMOD_System_Set3DListenerAttributes(
    self.handle,
    0,
    (FMOD_VECTOR*) pos,
    (FMOD_VECTOR*) vel,
    (FMOD_VECTOR*) fwd,
    (FMOD_VECTOR*) up
  ));
}

void Audio_Update () {
  FMODCALL(FMOD_System_Update(self.handle));
  Audio_SetListenerPos(self.autoPos, self.autoVel, self.autoFwd, self.autoUp);

  ArrayList_ForEachI(self.playingSounds, i) {
    Sound* sound = ArrayList_Get(self.playingSounds, i);
    /* TODO : Refine the API to make this less awkward */
    if (!Sound_IsFreed(sound) && Sound_IsPlaying(sound)) {
      Sound_Update(sound);
    } else {
      ArrayList_RemoveAtFast(self.playingSounds, i--);
    }
  }

  ArrayList_ForEachI(self.freeingSounds, i) {
    Sound* sound = ArrayList_Get(self.freeingSounds, i);
    Audio_DeallocSound(sound);
  }
  ArrayList_Clear(self.freeingSounds);
}

int32 Audio_GetLoadedCount () {
  uint32 size = StrMap_GetSize(self.descMap);
  Assert(size <= INT32_MAX);
  return (int32) size;
}

int32 Audio_GetPlayingCount () {
  return ArrayList_GetSize (self.playingSounds);
}

int32 Audio_GetTotalCount () {
  uint32 size = MemPool_GetSize(self.soundPool);
  Assert(size <= INT32_MAX);
  return (int32) size;
}

void* Audio_GetHandle () {
  return self.handle;
}

SoundDesc* Audio_AllocSoundDesc (cstr name) {
  SoundDesc* desc = (SoundDesc*) StrMap_Get(self.descMap, name);
  if (!desc) {
    desc = MemNewZero(SoundDesc);
    StrMap_Set(self.descMap, name, desc);
  }
  return desc;
}

void Audio_DeallocSoundDesc (SoundDesc* desc) {
  StrMap_Remove(self.descMap, desc->name);
  MemFree(desc);
}

Sound* Audio_AllocSound () {
  return (Sound*) MemPool_Alloc(self.soundPool);
}

void Audio_DeallocSound (Sound* sound) {
  MemPool_Dealloc(self.soundPool, sound);
}

void Audio_SoundStateChanged (Sound* sound) {
  if (Sound_IsFreed(sound)) {
    ArrayList_Append(self.freeingSounds, sound);
  } else if (Sound_IsPlaying(sound)) {
    ArrayList_Append(self.playingSounds, sound);

    CHECK1(
      if (ArrayList_GetSize(self.playingSounds) == AUDIO_CHANNELS + 1)
        Warn("Audio: Exceeded the number of available sound channels (%i)", AUDIO_CHANNELS);
    )
  }
}


text:
#include "Error.h"
#include <stdio.h>

void Error_Print (Error e) {
  printf("ERROR: ");
  if (e & Error_Stack)        printf("Stack ");
  if (e & Error_Heap)         printf("Heap ");
  if (e & Error_Buffer)       printf("Buffer ");
  if (e & Error_Path)         printf("Path ");
  if (e & Error_Index)        printf("Index ");
  if (e & Error_Vertex)       printf("Vertex ");
  if (e & Error_VertPos)      printf("Vertex Position ");
  if (e & Error_VertNorm)     printf("Vertex Normal ");
  if (e & Error_VertUV)       printf("Vertex UV ");

  if (e & Error_Input)        printf("Input ");
  if (e & Error_Intermediate) printf("Intermediate Value ");
  if (e & Error_Output)       printf("Output ");

  if (e & Error_Null)         printf("NULL");
  if (e & Error_Invalid)      printf("Invalid");
  if (e & Error_Overflow)     printf("Overflow");
  if (e & Error_Underflow)    printf("Underflow");
  if (e & Error_Empty)        printf("Empty");
  if (e & Error_NaN)          printf("NaN");
  if (e & Error_Degenerate)   printf("Degenerate");
  if (e & Error_BadCount)     printf("Incorrect Count");

  if (e == Error_None)        printf("None!");
  printf("\n");
};


text:
#include "BlendMode.h"
#include "Button.h"
#include "ClipRect.h"
#include "Draw.h"
#include "Font.h"
#include "Hash.h"
#include "HashMap.h"
#include "HmGui.h"
#include "Input.h"
#include "PhxMemory.h"
#include "PhxString.h"
#include "Profiler.h"
#include "RenderState.h"
#include "Tex2D.h"
#include "UIRenderer.h"

#define HMGUI_DRAW_GROUP_FRAMES 0
#define HMGUI_ENABLE_DRAGGING 1

const uint32 Layout_None = 0;
const uint32 Layout_Stack = 1;
const uint32 Layout_Vertical = 2;
const uint32 Layout_Horizontal = 3;

const uint32 Widget_Group = 0;
const uint32 Widget_Text = 1;
const uint32 Widget_Rect = 2;
const uint32 Widget_Image = 3;

const uint32 FocusStyle_None = 0;
const uint32 FocusStyle_Fill = 1;
const uint32 FocusStyle_Outline = 2;
const uint32 FocusStyle_Underline = 3;

const int FocusType_Mouse = 0;
const int FocusType_Scroll = 1;
const int FocusType_SIZE = 2;

struct HmGuiWidget {
  struct HmGuiGroup* parent;
  HmGuiWidget* next;
  HmGuiWidget* prev;
  uint64 hash;
  uint32 type;
  Vec2f pos;
  Vec2f size;
  Vec2f minSize;
  Vec2f align;
  Vec2f stretch;
};

struct HmGuiGroup : public HmGuiWidget {
  HmGuiWidget* head;
  HmGuiWidget* tail;
  uint32 layout;
  uint32 children;
  uint32 focusStyle;
  Vec2f paddingLower;
  Vec2f paddingUpper;
  Vec2f offset;
  Vec2f maxSize;
  Vec2f totalStretch;
  float spacing;
  float frameOpacity;
  bool clip;
  bool expand;
  bool focusable[FocusType_SIZE];
  bool storeSize;
};

struct HmGuiText : public HmGuiWidget {
  Font* font;
  cstr text;
  Vec4f color;
};

struct HmGuiRect : public HmGuiWidget {
  Vec4f color;
};

struct HmGuiImage : public HmGuiWidget {
  Tex2D* image;
};

struct HmGuiClipRect {
  HmGuiClipRect* prev;
  Vec2f lower;
  Vec2f upper;
};

struct HmGuiData {
  Vec2f offset;
  Vec2f minSize;
  Vec2f size;
};

struct HmGuiStyle {
  HmGuiStyle* prev;
  Font* font;
  float spacing;
  Vec4f colorPrimary;
  Vec4f colorFrame;
  Vec4f colorText;
};

struct HmGui {
  HmGuiGroup* group;
  HmGuiGroup* root;
  HmGuiWidget* last;
  HmGuiStyle* style;
  HmGuiClipRect* clipRect;
  HashMap* data;

  uint64 focus[FocusType_SIZE];
  Vec2f focusPos;
  bool activate;
} static self = { 0 };

static bool init = false;

#include "HmGuiInternal.h"

void HmGui_Begin (float sx, float sy) {
  if (!init) {
    init = true;
    self.group = 0;
    self.root = 0;

    self.style = MemNew(HmGuiStyle);
    self.style->prev = 0;
    self.style->font = Font_Load("Rajdhani", 14);
    self.style->spacing = 6;

    self.style->colorPrimary = Vec4f_Create(0.1f, 0.5f, 1.0f, 1.0f);
    self.style->colorFrame = Vec4f_Create(0.1f, 0.1f, 0.1f, 0.5f);
    self.style->colorText = Vec4f_Create(1, 1, 1, 1);

    self.clipRect = 0;
    self.data = HashMap_Create(0, 128);

    for (int i = 0; i < FocusType_SIZE; ++i) self.focus[i] = 0;
    self.activate = false;
  }

  if (self.root) {
    HmGui_FreeGroup(self.root);
    self.root = 0;
  }
  self.last = 0;
  self.activate = Input_GetPressed(Button_Mouse_Left);

  HmGui_BeginGroup(Layout_None);
  self.group->clip = true;
  self.group->pos = Vec2f_Create(0, 0);
  self.group->size = Vec2f_Create(sx, sy);
  self.root = self.group;
}

void HmGui_End () {
  FRAME_BEGIN;
  HmGui_EndGroup();
  HmGui_ComputeSize(self.root);
  HmGui_LayoutGroup(self.root);

  for (int i = 0; i < FocusType_SIZE; ++i) self.focus[i] = 0;
  Vec2i mouse; Input_GetMousePosition(&mouse);
  self.focusPos = Vec2f_Create(mouse.x, mouse.y);
  HmGui_CheckFocus(self.root);
  FRAME_END;
}

void HmGui_Draw () {
  FRAME_BEGIN;
  RenderState_PushBlendMode(BlendMode_Alpha);
  UIRenderer_Begin();
  HmGui_DrawGroup(self.root);
  UIRenderer_End();
  RenderState_PopBlendMode();
  UIRenderer_Draw();
  FRAME_END;
}

/* -------------------------------------------------------------------------- */

void HmGui_BeginGroupX () {
  HmGui_BeginGroup(Layout_Horizontal);
}

void HmGui_BeginGroupY () {
  HmGui_BeginGroup(Layout_Vertical);
}

void HmGui_BeginGroupStack () {
  HmGui_BeginGroup(Layout_Stack);
}

void HmGui_EndGroup () {
  self.last = self.group;
  self.group = self.group->parent;
}

void HmGui_BeginScroll (float maxSize) {
  HmGui_BeginGroupX();
  HmGui_SetStretch(1, 1);
  self.group->clip = true;
  HmGui_SetSpacing(2);
  HmGui_BeginGroupY();
  HmGui_SetPadding(6, 6);
  HmGui_SetStretch(1, 1);
  self.group->expand = false;
  self.group->storeSize = true;
  self.group->maxSize.y = maxSize;

  HmGuiData* data = HmGui_GetData(self.group);
  self.group->offset.y = -data->offset.y;
}

void HmGui_EndScroll () {
  HmGuiData* data = HmGui_GetData(self.group);
  if (HmGui_GroupHasFocus(FocusType_Scroll)) {
    Vec2i scroll; Input_GetMouseScroll(&scroll);
    data->offset.y -= 10.0f * scroll.y;
  }

  float maxScroll = Max(0.0f, data->minSize.y - data->size.y);
  data->offset.y = Clamp(data->offset.y, 0.0f, maxScroll);

  HmGui_EndGroup();

  HmGui_BeginGroupY();
  HmGui_SetStretch(0, 1);
  HmGui_SetSpacing(0);
  if (maxScroll > 0) {
    float handleSize = data->size.y * (data->size.y / data->minSize.y);
    float handlePos = Lerp(0.0f, data->size.y - handleSize, data->offset.y / maxScroll);
    HmGui_Rect(4.0f, handlePos, 0.0f, 0.0f, 0.0f, 0.0f);
    HmGui_Rect(4.0f, handleSize, UNPACK4(self.style->colorFrame));
  } else {
    HmGui_Rect(4.0f, 16.0f, 0.0f, 0.0f, 0.0f, 0.0f);
  }
  HmGui_EndGroup();
  HmGui_EndGroup();
}

void HmGui_BeginWindow (cstr title) {
  HmGui_BeginGroupStack();
  HmGui_SetStretch(0, 0);
  self.group->focusStyle = FocusStyle_None;
  self.group->frameOpacity = 0.95f;
#if HMGUI_ENABLE_DRAGGING
  HmGuiData* data = HmGui_GetData(self.group);
  if (HmGui_GroupHasFocus(FocusType_Mouse)) {
    if (Input_GetDown(Button_Mouse_Left)) {
      Vec2i md; Input_GetMouseDelta(&md);
      data->offset.x += md.x;
      data->offset.y += md.y;
    }
  }

  self.group->pos.x += data->offset.x;
  self.group->pos.y += data->offset.y;
#endif

  HmGui_BeginGroupY();
  self.group->clip = true;
  HmGui_SetPadding(8, 8);
  HmGui_SetStretch(1, 1);
  // HmGui_TextColored(title, 1.0f, 1.0f, 1.0f, 0.3f);
  // HmGui_SetAlign(0.5f, 0.0f);
}

void HmGui_EndWindow () {
  HmGui_EndGroup();
  HmGui_EndGroup();
}

/* -------------------------------------------------------------------------- */

bool HmGui_Button (cstr label) {
  HmGui_BeginGroupStack();
  self.group->focusStyle = FocusStyle_Fill;
  self.group->frameOpacity = 0.5f;
  bool focus = HmGui_GroupHasFocus(FocusType_Mouse);
  HmGui_SetPadding(8, 8);
  HmGui_Text(label);
  HmGui_SetAlign(0.5f, 0.5f);
  HmGui_EndGroup();
  return focus && self.activate;
}

bool HmGui_Checkbox (cstr label, bool value) {
  HmGui_BeginGroupX();
  self.group->focusStyle = FocusStyle_Underline;
  if (HmGui_GroupHasFocus(FocusType_Mouse) && self.activate)
    value = !value;
  HmGui_SetPadding(4, 4);
  HmGui_SetSpacing(8);
  HmGui_SetStretch(1, 0);

  HmGui_Text(label);
  HmGui_SetAlign(0.0f, 0.5f);
  HmGui_SetStretch(1, 0);

  HmGui_BeginGroupStack();
  HmGui_Rect(16, 16, UNPACK4(self.style->colorFrame));
  if (value) {
    HmGui_Rect(10, 10, UNPACK4(self.style->colorPrimary));
    HmGui_SetAlign(0.5f, 0.5f);
  }
  HmGui_EndGroup();
  HmGui_SetStretch(0, 0);
  HmGui_EndGroup();
  return value;
}

float HmGui_Slider (float lower, float upper, float value) {
  HmGui_BeginGroupStack();
  HmGui_Rect(0, 2, 0.5f, 0.5f, 0.5f, 1.0f);
  HmGui_SetAlign(0.5f, 0.5f);
  HmGui_SetStretch(1, 0);
  HmGui_EndGroup();
  return 0.0f;
}

/* -------------------------------------------------------------------------- */

void HmGui_Image (Tex2D* image) {
  HmGuiImage* e = MemNew(HmGuiImage);
  HmGui_InitWidget(e, Widget_Image);
  e->image = image;
  e->stretch = Vec2f_Create(1, 1);
}

void HmGui_Rect (float sx, float sy, float r, float g, float b, float a) {
  HmGuiRect* e = MemNew(HmGuiRect);
  HmGui_InitWidget(e, Widget_Rect);
  e->color = Vec4f_Create(r, g, b, a);
  e->minSize = Vec2f_Create(sx, sy);
}

void HmGui_Text (cstr text) {
  HmGui_TextEx(self.style->font, text, UNPACK4(self.style->colorText));
}

void HmGui_TextColored (cstr text, float r, float g, float b, float a) {
  HmGui_TextEx(self.style->font, text, r, g, b, a);
}

void HmGui_TextEx (Font* font, cstr text, float r, float g, float b, float a) {
  HmGuiText* e = MemNew(HmGuiText);
  HmGui_InitWidget(e, Widget_Text);
  e->font = font;
  e->text = StrDup(text);
  e->color = Vec4f_Create(r, g, b, a);
  Vec2i size; Font_GetSize2(e->font, &size, e->text);
  e->minSize = Vec2f_Create(size.x, size.y);
  HmGui_SetAlign(0.0f, 1.0f);
}

/* -------------------------------------------------------------------------- */

void HmGui_SetAlign (float ax, float ay) {
  self.last->align = Vec2f_Create(ax, ay);
}

void HmGui_SetPadding (float px, float py) {
  self.group->paddingLower = Vec2f_Create(px, py);
  self.group->paddingUpper = Vec2f_Create(px, py);
}

void HmGui_SetPaddingEx (float left, float top, float right, float bottom) {
  self.group->paddingLower = Vec2f_Create(left, top);
  self.group->paddingUpper = Vec2f_Create(right, bottom);
}

void HmGui_SetPaddingLeft (float padding) {
  self.group->paddingLower.x = padding;
}

void HmGui_SetPaddingTop (float padding) {
  self.group->paddingLower.y = padding;
}

void HmGui_SetPaddingRight (float padding) {
  self.group->paddingUpper.x = padding;
}

void HmGui_SetPaddingBottom (float padding) {
  self.group->paddingUpper.y = padding;
}

void HmGui_SetSpacing (float spacing) {
  self.group->spacing = spacing;
}

void HmGui_SetStretch (float x, float y) {
  self.last->stretch = Vec2f_Create(x, y);
}

bool HmGui_GroupHasFocus (int type) {
  self.group->focusable[type] = true;
  return self.focus[type] == self.group->hash;
}

/* -------------------------------------------------------------------------- */

void HmGui_PushStyle () {
  HmGuiStyle* style = MemNew(HmGuiStyle);
  *style = *self.style;
  style->prev = self.style;
  self.style = style;
}

void HmGui_PushFont (Font* font) {
  HmGui_PushStyle();
  self.style->font = font;
}

void HmGui_PushTextColor (float r, float g, float b, float a) {
  HmGui_PushStyle();
  self.style->colorText = Vec4f_Create(r, g, b, a);
}

void HmGui_PopStyle (int depth) {
  for (int i = 0; i < depth; ++i) {
    HmGuiStyle* style = self.style;
    self.style = style->prev;
    MemFree(style);
  }
}


text:
#include "PhxMemory.h"

void* Memory_Alloc (size_t size) {
  return malloc(size);
}

void* Memory_Calloc (size_t n, size_t size) {
  return calloc(n, size);
}

void Memory_Free (void* ptr) {
  free(ptr);
}

void Memory_MemCopy (void* dst, void const* src, size_t size) {
  memcpy(dst, src, size);
}

void Memory_MemMove (void* dst, void const* src, size_t size) {
  memmove(dst, src, size);
}

void* Memory_Realloc (void* ptr, size_t newSize) {
  return realloc(ptr, newSize);
}


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

const MouseButton MouseButton_Left   = SDL_BUTTON_LEFT;
const MouseButton MouseButton_Middle = SDL_BUTTON_MIDDLE;
const MouseButton MouseButton_Right  = SDL_BUTTON_RIGHT;
const MouseButton MouseButton_X1     = SDL_BUTTON_X1;
const MouseButton MouseButton_X2     = SDL_BUTTON_X2;


text:
#include "ArrayList.h"
#include "CollisionShape.h"
#include "Mesh.h"
#include "Vertex.h"

struct Cache {
  ArrayList(CollisionShape, shapes);
} static cache;

CollisionShape* CollisionShape_GetCached (int index) {
  return ArrayList_GetPtr(cache.shapes, index);
}

CollisionShape* CollisionShape_Create (CollisionShape shape) {
  Assert(shape.scale > 0);
  switch (shape.type) {
    default:
      Fatal("CollisionShape_CreateShape: Unhandled case for CollisionShapeType %i.", shape.type);
      break;

    case CollisionShapeType_Sphere: {
      Assert(shape.sphere.radius > 0);
      ArrayList_ForEachI(cache.shapes, i) {
        CollisionShape* cached = ArrayList_GetPtr(cache.shapes, i);
        if (cached->type == shape.type &&
            cached->scale == shape.scale &&
            cached->sphere.radius == shape.sphere.radius)
          return cached;
      }

      shape.base.handle = new btSphereShape(shape.sphere.radius);
      shape.base.handle->setLocalScaling(btVector3(REPEAT3(shape.scale)));
      break;
    }

    case CollisionShapeType_Box: {
      Assert(shape.box.halfExtents.x > 0);
      Assert(shape.box.halfExtents.y > 0);
      Assert(shape.box.halfExtents.z > 0);
      ArrayList_ForEachI(cache.shapes, i) {
        CollisionShape* cached = ArrayList_GetPtr(cache.shapes, i);
        if (cached->type == shape.type &&
            cached->scale == shape.scale &&
            Vec3f_Equal(cached->box.halfExtents, shape.box.halfExtents))
          return cached;
      }

      shape.base.handle = new btBoxShape(Vec3f_ToBullet(&shape.box.halfExtents));
      shape.base.handle->setLocalScaling(btVector3(REPEAT3(shape.scale)));
      break;
    }

    case CollisionShapeType_Hull: {
      Assert(shape.hull.mesh != 0);
      btConvexHullShape* hullHandle = 0;
      ArrayList_ForEachI(cache.shapes, i) {
        CollisionShape* cached = ArrayList_GetPtr(cache.shapes, i);
        if (cached->type == shape.type &&
            cached->hull.mesh == shape.hull.mesh) {
          hullHandle = cached->hull.hullHandle;
          break;
        }
      }

      if (!hullHandle) {
        Vertex* vFirst = Mesh_GetVertex(shape.hull.mesh, 0);
        int     vCount = Mesh_GetVertexCount(shape.hull.mesh);
        Assert(vCount != 0);
        /* TODO : Don't leak these when we actually start removing shapes from the cache. */
        hullHandle = new btConvexHullShape(&(vFirst->p.x), vCount, sizeof(Vertex));
        hullHandle->optimizeConvexHull();
      }

      shape.hull.hullHandle = hullHandle;
      shape.base.handle = new btUniformScalingShape(hullHandle, shape.scale);
      break;
    }

    case CollisionShapeType_Compound: {
      shape.base.handle = new btCompoundShape(true, 4);
      shape.base.handle->setLocalScaling(btVector3(REPEAT3(shape.scale)));
      break;
    }
  }

  shape.iShape = ArrayList_GetSize(cache.shapes);
  ArrayList_Append(cache.shapes, shape);
  return ArrayList_GetLastPtr(cache.shapes);
}

void CollisionShape_Free (CollisionShape*) {
  /* TODO: Actually free shapes. */
}

CollisionShape* CollisionShape_CreateBox (Vec3f* halfExtents) {
  CollisionShape shapeDef = {};
  shapeDef.scale           = 1.0f;
  shapeDef.type            = CollisionShapeType_Box;
  shapeDef.box.halfExtents = *halfExtents;
  return CollisionShape_Create(shapeDef);
}

CollisionShape* CollisionShape_CreateBoxFromMesh (Mesh* mesh) {
  Box3f bounds = {}; Mesh_GetBound(mesh, &bounds);

  CollisionShape shapeDef = {};
  shapeDef.scale             = 1.0f;
  shapeDef.type              = CollisionShapeType_Box;
  shapeDef.box.halfExtents.x = Max(Abs(bounds.upper.x), Abs(bounds.lower.x));
  shapeDef.box.halfExtents.y = Max(Abs(bounds.upper.y), Abs(bounds.lower.y));
  shapeDef.box.halfExtents.z = Max(Abs(bounds.upper.z), Abs(bounds.lower.z));
  return CollisionShape_Create(shapeDef);
}

CollisionShape* CollisionShape_CreateSphere (float radius) {
  CollisionShape shapeDef = {};
  shapeDef.scale         = 1.0f;
  shapeDef.type          = CollisionShapeType_Sphere;
  shapeDef.sphere.radius = radius;
  return CollisionShape_Create(shapeDef);
}

CollisionShape* CollisionShape_CreateSphereFromMesh (Mesh* mesh) {
  CollisionShape shapeDef = {};
  shapeDef.scale         = 1.0f;
  shapeDef.type          = CollisionShapeType_Sphere;
  shapeDef.sphere.radius = Mesh_GetRadius(mesh);
  return CollisionShape_Create(shapeDef);
}

CollisionShape* CollisionShape_CreateHullFromMesh (Mesh* mesh) {
  CollisionShape shapeDef = {};
  shapeDef.scale     = 1.0f;
  shapeDef.type      = CollisionShapeType_Hull;
  shapeDef.hull.mesh = mesh;
  return CollisionShape_Create(shapeDef);
}


text:
/* TODO : Currently disabled due to matrix transition. There's a lot of matrix
 *        code in here that will be a hassle to rewrite. */

#if 0
#include "Matrix.h"
#include "PhxMemory.h"
#include "Tex2D.h"
#include "VR.h"
#include "openvr/openvr.h"

#if WINDOWS
  #pragma comment(lib, "openvr_api.lib")
#endif

/* TODO : Make conditionally available. */

const VRDeviceType VRDeviceType_Invalid = vr::TrackedDeviceClass_Invalid;
const VRDeviceType VRDeviceType_HMD = vr::TrackedDeviceClass_HMD;
const VRDeviceType VRDeviceType_Controller = vr::TrackedDeviceClass_Controller;
const VRDeviceType VRDeviceType_Tracker = vr::TrackedDeviceClass_TrackingReference;
const VRDeviceType VRDeviceType_Other = vr::TrackedDeviceClass_Other;

static vr::IVRSystem* vrSystem = 0;
static vr::TrackedDevicePose_t poses[vr::k_unMaxTrackedDeviceCount];

typedef vr::HmdMatrix34_t Matrix3x4;
typedef vr::HmdMatrix44_t Matrix4x4;
typedef vr::VRControllerState_t ControllerState;

bool VR_Exists () {
  static bool checked = false;
  static bool present;
  if (checked)
    return present;
  else {
    present = vr::VR_IsHmdPresent();
    checked = true;
    return present;
  }
}

bool VR_Init () {
  vr::HmdError err;
  vrSystem = vr::VR_Init(&err, vr::VRApplication_Scene);
  if (vrSystem)
    return true;
  else
    return false;
}

void VR_Free () {
  if (vrSystem)
    vr::VR_Shutdown();
}

Vec2i VR_GetResolution () {
  Vec2i res;
  vrSystem->GetRecommendedRenderTargetSize((uint32*)&res.x, (uint32*)&res.y);
  return res;
}

Matrix VR_GetProjection (int eye, double z0, double z1) {
  Matrix4x4 matrix = vrSystem->GetProjectionMatrix(
    eye == 0 ? vr::Eye_Left : vr::Eye_Right,
    (float)z0, (float)z1, vr::API_OpenGL);

  return Matrix_Create(
    matrix.m[0][0], matrix.m[1][0], matrix.m[2][0], matrix.m[3][0],
    matrix.m[0][1], matrix.m[1][1], matrix.m[2][1], matrix.m[3][1],
    matrix.m[0][2], matrix.m[1][2], matrix.m[2][2], matrix.m[3][2],
    matrix.m[0][3], matrix.m[1][3], matrix.m[2][3], matrix.m[3][3]);
}

Matrix VR_GetEyeTransform (int eye) {
  Matrix3x4 matrix = vrSystem->GetEyeToHeadTransform(
    eye == 0 ? vr::Eye_Left : vr::Eye_Right);

  return Matrix_Create(
    matrix.m[0][0], matrix.m[1][0], matrix.m[2][0], 0,
    matrix.m[0][1], matrix.m[1][1], matrix.m[2][1], 0,
    matrix.m[0][2], matrix.m[1][2], matrix.m[2][2], 0,
    matrix.m[0][3], matrix.m[1][3], matrix.m[2][3], 1);
}

int VR_GetMaxAxis () {
  return vr::k_unControllerStateAxisCount;
}

int VR_GetMaxButton () {
  return vr::k_EButton_Max;
}

int VR_GetMaxDevice () {
  return vr::k_unMaxTrackedDeviceCount;
}

inline static cstr GetStringProperty (int index, vr::TrackedDeviceProperty prop) {
  vr::TrackedPropertyError error;
  uint32 buffSize = vrSystem->GetStringTrackedDeviceProperty(
    index, prop, 0, 0, &error);
  char* result = (char*)MemAlloc(buffSize);
  vrSystem->GetStringTrackedDeviceProperty(index, prop, result, buffSize, &error);
  return result;
}

cstr VR_GetDeviceName (int index) {
  return GetStringProperty(index, vr::Prop_TrackingSystemName_String);
}

Matrix VR_GetDeviceTransform (int index) {
  Matrix3x4 matrix = poses[index].mDeviceToAbsoluteTracking;
  return Matrix_Create(
    matrix.m[0][0], matrix.m[1][0], matrix.m[2][0], 0,
    matrix.m[0][1], matrix.m[1][1], matrix.m[2][1], 0,
    matrix.m[0][2], matrix.m[1][2], matrix.m[2][2], 0,
    matrix.m[0][3], matrix.m[1][3], matrix.m[2][3], 1);
}

VRDeviceType VR_GetDeviceType (int index) {
  return (VRDeviceType)vrSystem->GetTrackedDeviceClass(index);
}

bool VR_IsDeviceConnected (int index) {
  return vrSystem->IsTrackedDeviceConnected(index);
}

struct VRController {
  int index;
  ControllerState state;
};

VRController* VR_OpenController (int index) {
  VRController* self = MemNew(VRController);
  self->index = index;
  vrSystem->GetControllerState(self->index, &self->state, sizeof(ControllerState));
  return self;
}

void VR_CloseController (VRController* self) {
  MemFree(self);
}

Vec2d VR_GetAxis (VRController* self, int axis) {
  vrSystem->GetControllerState(self->index, &self->state, sizeof(ControllerState));
  Vec2d result;
  result.x = self->state.rAxis[axis].x;
  result.y = self->state.rAxis[axis].y;
  return result;
}

bool VR_GetButton (VRController* self, int button) {
  vrSystem->GetControllerState(self->index, &self->state, sizeof(ControllerState));
  return (self->state.ulButtonPressed & vr::ButtonMaskFromId((vr::EVRButtonId)button)) != 0;
}

void VR_Vibrate (VRController* self, int axis, int microseconds) {
  vrSystem->TriggerHapticPulse(self->index, axis, (unsigned short) microseconds);
}

void VR_GetTransforms () {
  vr::IVRCompositor* comp = vr::VRCompositor();
  comp->WaitGetPoses(poses, vr::k_unMaxTrackedDeviceCount, 0, 0);
}

void VR_Render (int eye, Tex2D* texture) {
  vr::IVRCompositor* comp = vr::VRCompositor();
  comp->CompositorBringToFront();
  vr::Texture_t vrt;
  vrt.handle = (void*)(size_t)Tex2D_GetHandle(texture);
  vrt.eType = vr::API_OpenGL;
  vrt.eColorSpace = vr::ColorSpace_Gamma;
  comp->Submit(eye == 0 ? vr::Eye_Left : vr::Eye_Right, &vrt);
}

#endif


text:
#include "PhxTime.h"
#include <limits.h>
#include <time.h>

inline static Time Time_Convert (struct tm* t) {
  Time result;
  result.second = t->tm_sec;
  result.minute = t->tm_min;
  result.hour = t->tm_hour;
  result.dayOfWeek = t->tm_wday + 1;
  result.dayOfMonth = t->tm_mday;
  result.dayOfYear = t->tm_yday + 1;
  result.month = t->tm_mon + 1;
  result.year = t->tm_year + 1900;
  return result;
}

Time Time_GetLocal () {
  time_t t = time(0);
  return Time_Convert(localtime(&t));
}

Time Time_GetUTC () {
  time_t t = time(0);
  return Time_Convert(gmtime(&t));
}

uint Time_GetRaw () {
  return (uint)(time(0) % UINT_MAX);
}


text:
#include "ArrayList.h"
#include "Lua.h"
#include "PhxMemory.h"
#include "PhxSignal.h"
#include "PhxString.h"
#include "Profiler.h"
#include "Resource.h"

cstr const kErrorHandler =
  "function __error_handler__ (e)"
  "  return debug.traceback(e, 1)"
  "end"
;

static bool initialized = false;
static Lua* activeInstance = 0;
static Signal cSignal = 0;

static void Lua_BacktraceHook (Lua *self, lua_Debug*) {
  lua_sethook(self, 0, 0, 0);
  luaL_where(self, 0);
  lua_pushstring(self, StrAdd("Received Signal: ", Signal_ToString(cSignal)));
  lua_error(self);
}

static void Lua_SignalHandler (Signal sig) {
  if (!activeInstance) return;
  if (sig == Signal_Abrt || sig == Signal_Segv) {
    /* NOTE : The implementation of abort() causes the program to forcefully
     *        exit as soon as the signal handler returns. Thus Lua_BacktraceHook
     *        will never get a chance to be called and we have to dump a trace
     *        now. */
    Lua_Backtrace();
  } else {
    cSignal = sig;
    lua_sethook(activeInstance, Lua_BacktraceHook, LUA_MASKCALL | LUA_MASKRET | LUA_MASKCOUNT, 1);
    Signal_IgnoreDefault();
  }
}

static void Lua_PCall (Lua* self, int args, int rets, int errorHandler) {
  Lua* prev = activeInstance;
  activeInstance = self;
  int result = lua_pcall(self, args, rets, errorHandler);
  if (result != 0) {
    if (result == LUA_ERRMEM)
      Fatal("Lua_PCall: Lua returned a memory allocation error");
    else if (result == LUA_ERRERR)
      Fatal("Lua_PCall: Lua errored while attempting to run the error handler");
    else if (result == LUA_ERRRUN) {
      cstr error = lua_tostring(self, -1);
      Fatal("Lua_PCall: Lua returned error message: %s", error);
    } else {
      Fatal("Lua_PCall: Lua returned an invalid error code (corruption?)");
    }
  }
  activeInstance = prev;
}

/* -- Utility CFunctions to be injected into global state ------------------- */

static int Lua_CallBarrier (Lua* self) {
  int args = lua_gettop(self) - 1;
  lua_call(self, args, LUA_MULTRET);
  return lua_gettop(self);
}

/* -------------------------------------------------------------------------- */

static void Lua_InitExtensions (Lua* self) {
  Lua_SetFn(self, "Call", Lua_CallBarrier);
  LuaScheduler_Init(self);
  LuaScheduler_Register(self);
}

Lua* Lua_Create () {
  if (!initialized) {
    initialized = true;
    Signal_AddHandlerAll(Lua_SignalHandler);
  }

  Lua* self = luaL_newstate();
  luaL_openlibs(self);
  Lua_InitExtensions(self);
  if (luaL_dostring(self, kErrorHandler))
    Fatal("Lua_Create: failed to load error handler");
  return self;
}

Lua* Lua_CreateThread (Lua* self) {
  return lua_newthread(self);
}

void Lua_Free (Lua* self) {
  lua_close(self);
}

Lua* Lua_GetActive () {
  return activeInstance;
}

void Lua_DoFile (Lua* self, cstr name) {
  Lua_LoadFile(self, name);
  Lua_PCall(self, 0, 0, 0);
}

void Lua_DoString (Lua* self, cstr code) {
  Lua_LoadString(self, code);
  Lua_PCall(self, 0, 0, 0);
}

void Lua_LoadFile (Lua* self, cstr name) {
  cstr path = Resource_GetPath(ResourceType_Script, name);
  if (luaL_loadfile(self, path))
    Fatal("Lua_LoadFile: failed to load <%s>:\n%s", path, lua_tostring(self, -1));
}

void Lua_LoadString (Lua* self, cstr code) {
  if (luaL_loadstring(self, code))
    Fatal("Lua_LoadString: failed to load string:\n%s", lua_tostring(self, -1));
}

void Lua_Call (Lua* self, int args, int rets, int errorHandler) {
  Lua_PCall(self, args, rets, errorHandler);
}

void Lua_PushGlobal (Lua* self, cstr name) {
  lua_getfield(self, LUA_GLOBALSINDEX, name);
  if (lua_isnil(self, lua_gettop(self)))
    Fatal("Lua_PushGlobal: failed to find global key <%s>", name);
}

void Lua_PushNumber (Lua* self, double value) {
  lua_pushnumber(self, value);
}

void Lua_PushPtr (Lua* self, void* value) {
  lua_pushlightuserdata(self, value);
}

void Lua_PushStr (Lua* self, cstr value) {
  lua_pushstring(self, value);
}

void Lua_PushThread (Lua* self, Lua* thread) {
  lua_pushthread(thread);
  lua_xmove(thread, self, 1);
}

void Lua_SetBool (Lua* self, cstr name, bool value) {
  lua_pushboolean(self, value);
  lua_setglobal(self, name);
}

void Lua_SetFn (Lua* self, cstr name, LuaFn fn) {
  lua_register(self, name, fn);
}

void Lua_SetNumber (Lua* self, cstr name, double value) {
  lua_pushnumber(self, value);
  lua_setglobal(self, name);
}

void Lua_SetPtr (Lua* self, cstr name, void* value) {
  lua_pushlightuserdata(self, value);
  lua_setglobal(self, name);
}

void Lua_SetStr (Lua* self, cstr name, cstr value) {
  lua_pushstring(self, value);
  lua_setglobal(self, name);
}

void Lua_TransferStack (Lua* src, Lua* dst, int count) {
  lua_xmove(src, dst, count);
}

LuaRef Lua_GetRef (Lua* self) {
  return luaL_ref(self, LUA_REGISTRYINDEX);
}

void Lua_ReleaseRef (Lua* self, LuaRef ref) {
  luaL_unref(self, LUA_REGISTRYINDEX, ref);
}

void Lua_PushRef (Lua* self, LuaRef ref) {
  lua_rawgeti(self, LUA_REGISTRYINDEX, ref);
}

void Lua_GCFull (Lua* self) {
  lua_gc(self, LUA_GCCOLLECT, 0);
}

void Lua_GCSetActive (Lua* self, bool active) {
  if (active)
    lua_gc(self, LUA_GCRESTART, 0);
  else
    lua_gc(self, LUA_GCSTOP, 0);
}

void Lua_GCStep (Lua* self) {
  lua_gc(self, LUA_GCSTEP, 0);
}

int Lua_GetMemory (Lua* self) {
  return lua_gc(self, LUA_GCCOUNT, 0) * 1024 +
         lua_gc(self, LUA_GCCOUNTB, 0);
}

inline static cstr Lua_ToString (Lua* self, cstr name) {
  /* NOTE : We leak strings allocated by StrDup/Format in a couple places here
   *        to simplify the code. We're in the middle of crashing so it doesn't
   *        matter. */

  /* NOTE : LUA_TCDATA and a couple other extensions are defined in lj_obj.h.
   *        Pulling that in requires pulling in a bunch of stuff. */
  #define LUA_TCDATA 10

  int  type     = lua_type(self, -1);
  cstr typeName = lua_typename(self, type);
  cstr strValue = 0;
  bool isNull   = false;

  if (luaL_callmeta(self, -1, "__tostring")) {
    strValue = StrDup(lua_tostring(self, -1));
    lua_pop(self, 1);
  } else {
    switch (type) {
      default: Fatal("Lua_ToString: Unexpected type %i", type);

      case LUA_TNIL:           strValue = "nil"; isNull = true;                        break;
      case LUA_TBOOLEAN:       strValue = lua_toboolean (self, -1) ? "True" : "False"; break;
      case LUA_TNUMBER:        strValue = lua_tostring  (self, -1);                    break;
      case LUA_TSTRING:        strValue = lua_tostring  (self, -1);                    break;
      case LUA_TLIGHTUSERDATA: strValue = StrFormat("0x%p", lua_touserdata(self, -1)); break;
      case LUA_TTABLE:         strValue = StrFormat("0x%p", lua_topointer (self, -1)); break;
      case LUA_TFUNCTION:      strValue = StrFormat("0x%p", lua_topointer (self, -1)); break;
      case LUA_TUSERDATA:      strValue = StrFormat("0x%p", lua_touserdata(self, -1)); break;
      case LUA_TTHREAD:        strValue = StrFormat("0x%p", lua_topointer (self, -1)); break;
      case LUA_TCDATA:         strValue = StrFormat("0x%p", lua_topointer (self, -1)); break;
    }
  }

  cstr pre = isNull ? "" : "";
  cstr app = isNull ? ""    : "";
  return StrFormat("%s      %-10s %-16s = %s%s", pre, typeName, name, strValue, app);
}

void Lua_Backtrace () {
  Lua* self = activeInstance;
  if (!self) return;

  /* NOTE : This is inefficient, but meh it's happening during a crash... */
  ArrayList(cstr, stack); ArrayList_Init(stack);
  for (int iStack = 0; true; iStack++) {
    lua_Debug ar = {};
    int result = lua_getstack(self, iStack, &ar);
    if (result == 0) break;

    result = lua_getinfo(self, "nSluf", &ar);
    if (result == 0)
      Fatal("Lua_GetStack: lua_getinfo failed.");

    int variablesPrinted = 0;

    { //File, line, and function
      cstr  funcName = ar.name;
      cstr  fileName = ar.source;
      int32 line     = ar.currentline;
      if (fileName[0] != '@')      { fileName = "<string>"; line = -1; }
      if (fileName[0] == '@')        fileName = fileName + 1;
      if (StrEqual(ar.what, "C"))    fileName = "<native>";
      if (StrEqual(ar.what, "main")) funcName = "<main>";
      if (!funcName)                 funcName = "<null>";

      cstr stackFrame = (line > 0)
        ? StrFormat("  %s(%i): %s", fileName, line, funcName)
        : StrFormat("  %s: %s", fileName, funcName);
      ArrayList_Append(stack, stackFrame);
    }

    { //Upvalues
      for (int iUp = 1; true; iUp++) {
        cstr name = lua_getupvalue(self, -1, iUp);
        if (!name) break;

        if (iUp == 1)
          ArrayList_Append(stack, StrDup("    [Upvalues]"));

        cstr upValue = Lua_ToString(self, name);
        ArrayList_Append(stack, upValue);
        lua_pop(self, 1);
        variablesPrinted++;
      }
    }

    { //Locals
      for (int iLocal = 1; true; iLocal++) {
        cstr name = lua_getlocal(self, &ar, iLocal);
        if (!name) break;

        if (iLocal == 1)
          ArrayList_Append(stack, StrDup("    [Locals]"));

        cstr local = Lua_ToString(self, name);
        ArrayList_Append(stack, local);
        lua_pop(self, 1);
        variablesPrinted++;
      }
    }

    if (variablesPrinted > 0)
      ArrayList_Append(stack, StrDup(""));
    lua_pop(self, 1);
  }

  Warn("Lua Backtrace:");
  ArrayList_ForEach(stack, cstr, stackFrame) {
    Warn(*stackFrame);
    StrFree(*stackFrame);
  }
  ArrayList_Free(stack);
}

/* TODO : Add backtrace to lua errors in PCall */


text:
#include "OpenGL.h"
#include "TexWrapMode.h"

const TexWrapMode TexWrapMode_Clamp        = GL_CLAMP_TO_EDGE;
const TexWrapMode TexWrapMode_MirrorClamp  = GL_MIRROR_CLAMP_TO_EDGE;
const TexWrapMode TexWrapMode_MirrorRepeat = GL_MIRRORED_REPEAT;
const TexWrapMode TexWrapMode_Repeat       = GL_REPEAT;


text:
#include "Array.h"
#include "Button.h"
#include "DeviceType.h"

#include <stdio.h>

const DeviceType DeviceType_Null     = 0;
const DeviceType DeviceType_Mouse    = 1;
const DeviceType DeviceType_Keyboard = 2;
const DeviceType DeviceType_Gamepad  = 3;

DeviceType DeviceType_FromButton (Button button) {
  return Button_ToDeviceType(button);
}

cstr DeviceType_ToString (DeviceType deviceType) {
  switch (deviceType) {
    default:
      static char buffer[512];
      snprintf(buffer, (size_t) Array_GetSize(buffer), "Unknown (%i)", deviceType);
      return buffer;

    case DeviceType_Null:     return "DeviceType_Null";     break;
    case DeviceType_Mouse:    return "DeviceType_Mouse";    break;
    case DeviceType_Keyboard: return "DeviceType_Keyboard"; break;
    case DeviceType_Gamepad:  return "DeviceType_Gamepad";  break;
  }
}


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

const HatDir HatDir_Centered  = SDL_HAT_CENTERED;
const HatDir HatDir_Up        = SDL_HAT_UP;
const HatDir HatDir_Right     = SDL_HAT_RIGHT;
const HatDir HatDir_Down      = SDL_HAT_DOWN;
const HatDir HatDir_Left      = SDL_HAT_LEFT;
const HatDir HatDir_RightUp   = SDL_HAT_RIGHTUP;
const HatDir HatDir_RightDown = SDL_HAT_RIGHTDOWN;
const HatDir HatDir_LeftUp    = SDL_HAT_LEFTUP;
const HatDir HatDir_LeftDown  = SDL_HAT_LEFTDOWN;


text:
if (WIN32)
  set (PLATFORM "win")
  set (WINDOWS TRUE)
elseif (UNIX AND NOT APPLE)
  set (PLATFORM "linux")
  set (LINUX TRUE)
else ()
  message (FATAL_ERROR "Unsupported Platform")
endif ()

if ("${CMAKE_SIZEOF_VOID_P}" EQUAL "4")
  set (ARCH "32")
elseif ("${CMAKE_SIZEOF_VOID_P}" EQUAL "8")
  set (ARCH "64")
else ()
  message (FATAL_ERROR "Unsupported CPU Architecture")
endif ()

set (PLATARCH "${PLATFORM}${ARCH}")

function (phx_configure_output_dir target)
  set_target_properties (${target} PROPERTIES
    RUNTIME_OUTPUT_DIRECTORY "${CMAKE_SOURCE_DIR}/bin"
    LIBRARY_OUTPUT_DIRECTORY "${CMAKE_SOURCE_DIR}/bin"
    ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_SOURCE_DIR}/bin")

  foreach (config ${CMAKE_CONFIGURATION_TYPES})
    string (TOUPPER ${config} config)
    set_target_properties (${target} PROPERTIES
      RUNTIME_OUTPUT_DIRECTORY_${config} "${CMAKE_SOURCE_DIR}/bin"
      LIBRARY_OUTPUT_DIRECTORY_${config} "${CMAKE_SOURCE_DIR}/bin"
      ARCHIVE_OUTPUT_DIRECTORY_${config} "${CMAKE_SOURCE_DIR}/bin")
  endforeach (config)
endfunction ()

function (phx_configure_target_properties target)
  if (WINDOWS)
    target_compile_definitions (${target} PRIVATE _CRT_SECURE_NO_DEPRECATE)
    target_compile_definitions (${target} PRIVATE WIN32_LEAN_AND_MEAN)
    target_compile_definitions (${target} PRIVATE WINDOWS=1)

    target_compile_options (${target} PRIVATE "/MP")         # Multithreaded Build
    target_compile_options (${target} PRIVATE "/MD")         # Dynamic C Runtime
    target_compile_options (${target} PRIVATE "/EHs-c-")     # No exception handling
    target_compile_options (${target} PRIVATE "/fp:fast")    # No strict FP
    target_compile_options (${target} PRIVATE "/GL")         # Whole Program Optimization
    target_compile_options (${target} PRIVATE "/GS-")        # No Buffer Security Checks
    target_compile_options (${target} PRIVATE "/GR-")        # No RTTI
    target_compile_options (${target} PRIVATE "/arch:SSE2")  # Assume SSE2+
  elseif (LINUX)
    target_compile_definitions (${target} PRIVATE UNIX=1)

    target_compile_options (${target} PRIVATE "-Wall")            # All error checking
    target_compile_options (${target} PRIVATE "-fno-exceptions")  # No exception handling
    target_compile_options (${target} PRIVATE "-ffast-math")      # No strict FP
    target_compile_options (${target} PRIVATE "-fpic")            # PIC since this is shared

    target_compile_options (${target} PRIVATE "-Wno-unused-variable")
    target_compile_options (${target} PRIVATE "-Wno-unknown-pragmas")

    # Aggressive optimization, assuming SSE4+
    target_compile_options (${target} PRIVATE "-O3")
    target_compile_options (${target} PRIVATE "-msse")
    target_compile_options (${target} PRIVATE "-msse2")
    target_compile_options (${target} PRIVATE "-msse3")
    target_compile_options (${target} PRIVATE "-msse4")

    # :(
    target_compile_options (${target} PRIVATE "-std=c++11")

  endif ()
endfunction ()


text:
local libphx = require('ffi.libphx').lib

function onDef_Ray_t (t, mt)
  mt.__index.getPoint = function (self, t) return Vec3f(
    self.px + t * self.dirx,
    self.py + t * self.diry,
    self.pz + t * self.dirz)
  end

  mt.__index.getDirection = function (self) return Vec3f(self.dirx, self.diry, self.dirz) end
  mt.__index.getOrigin    = function (self) return Vec3f(self.px, self.py, self.pz) end

  mt.__tostring = function (self)
    return string.format(
      '(origin: (%f, %f, %f), dir: (%f, %f, %f), t: [%f, %f])',
      self.px, self.py, self.pz,
      self.dirx, self.diry, self.dirz,
      self.tMin, self.tMax)
  end
end


text:
local libphx = require('ffi.libphx').lib

local function defineVec2_t (t, mt)
   local Vec2 = t

  function mt.__add (a, b)
    return (Vec2(a.x + b.x, a.y + b.y))
  end

  function mt.__div (a, b)
    return (Vec2(a.x / b.x, a.y / b.y))
  end

  function mt.__mul (a, b)
    return (Vec2(a.x * b.x, a.y * b.y))
  end

  function mt.__pow (a, b)
    return (Vec2(a.x ^ b.x, a.y ^ b.y))
  end

  function mt.__sub (a, b)
    return (Vec2(a.x - b.x, a.y - b.y))
  end

  function mt.__unm (v)
    return (Vec2(-v.x, -v.y))
  end

  function mt.__index.iadd (a, b)
    a.x = a.x + b.x
    a.y = a.y + b.y
  end

  function mt.__index.idiv (a, b)
    a.x = a.x / b.x
    a.y = a.y / b.y
  end

  function mt.__index.idivs (a, s)
    a.x = a.x / s
    a.y = a.y / s
  end

  function mt.__index.divs (a, s)
    return (Vec2(a.x / s, a.y / s))
  end

  function mt.__index.imadds (a, b, s)
    a.x = a.x + s * b.x
    a.y = a.y + s * b.y
  end

  function mt.__index.imul (a, b)
    a.x = a.x * b.x
    a.y = a.y * b.y
  end

  function mt.__index.imuls (a, s)
    a.x = a.x * s
    a.y = a.y * s
  end

  function mt.__index.muls (a, s)
    return (Vec2(a.x * s, a.y * s))
  end

  function mt.__index.isub (a, b)
    a.x = a.x - b.x
    a.y = a.y - b.y
  end

  function mt.__index.abs (v)
    return (Vec2(math.abs(v.x), math.abs(v.y)))
  end

  function mt.__index.clamp (v, lower, upper)
    return (Vec2(
      math.max(lower.x, math.min(upper.x, v.x)),
      math.max(lower.y, math.min(upper.y, v.y))))
  end

  function mt.__index.iclampLength (v, clamp)
    local lenSq = v:lengthSquared()
    if lenSq > clamp*clamp then
      v:iscale(clamp / sqrt(lenSq))
    end
  end

  function mt.__index.clampLength (v, clamp)
    local result = v:clone()
    local lenSq = v:lengthSquared()
    if lenSq > clamp*clamp then
      result:iscale(clamp / sqrt(lenSq))
    end
    return result
  end

  function mt.__index.distance (a, b)
    local dx = a.x - b.x
    local dy = a.y - b.y
    return (sqrt(dx*dx + dy*dy))
  end

  function mt.__index.distanceSquared (a, b)
    local dx = a.x - b.x
    local dy = a.y - b.y
    return dx*dx + dy*dy
  end

  function mt.__index.dot (a, b)
    return a.x*b.x + a.y*b.y
  end

  function mt.__index.approximatelyEqual (a, b)
    return abs(a.x - b.x) < 1e-3
       and abs(a.y - b.y) < 1e-3
  end

  function mt.__index.equal (a, b)
    return a.x == b.x
       and a.y == b.y
  end

  function mt.__index.isZero (v)
    return v.x == 0 and v.y == 0
  end

  function mt.__index.length (v)
    return (sqrt(v.x * v.x + v.y * v.y))
  end

  function mt.__index.lengthSquared (v)
    return (v.x * v.x + v.y * v.y)
  end

  function mt.__index.ilerp (a, b, t)
    a.x = a.x + (b.x - a.x) * t
    a.y = a.y + (b.y - a.y) * t
  end

  function mt.__index.lerp (a, b, t)
    return (Vec2(
      a.x + (b.x - a.x) * t,
      a.y + (b.y - a.y) * t))
  end

  function mt.__index.lerpv (a, b, t)
    return (Vec2(
      a.x + (b.x - a.x) * t.x,
      a.y + (b.y - a.y) * t.y))
  end

  function mt.__index.inormalize (v)
    local l = sqrt(v.x*v.x + v.y*v.y)
    assert(l > 1e-12, 'Attempting to normalize vector with near-zero length')
    v.x = v.x / l
    v.y = v.y / l
  end

  function mt.__index.normalize (v)
    local l = sqrt(v.x*v.x + v.y*v.y)
    assert(l > 1e-12, 'Attempting to normalize vector with near-zero length')
    return (Vec2(v.x / l, v.y / l))
  end

  function mt.__index.iscale (v, s)
    v.x = v.x * s
    v.y = v.y * s
  end

  function mt.__index.scale (v, s)
    return (Vec2(s*v.x, s*v.y))
  end
end

local function defineVec2i_t (t, mt)
  function mt.__index.toVec2f (v)
    return Vec2f(v.x, v.y)
  end

  function mt.__index.toVec2d (v)
    return Vec2d(v.x, v.y)
  end

  function mt.__index.toVec3i (v)
    return Vec3i(v.x, v.y, 0.0)
  end

  function mt.__index.toVec4i (v)
    return Vec4i(v.x, v.y, 0.0, 0.0)
  end

  function mt.__tostring (v)
    return string.format('(%i, %i)', v.x, v.y)
  end
end

local function defineVec2f_t (t, mt)
  function mt.__index.toVec2i (v)
    return Vec2i(v.x, v.y)
  end

  function mt.__index.toVec2d (v)
    return Vec2d(v.x, v.y)
  end

  function mt.__index.toVec3f (v)
    return Vec3f(v.x, v.y, 0.0)
  end

  function mt.__index.toVec4f (v)
    return Vec4f(v.x, v.y, 0.0, 0.0)
  end

  function mt.__tostring (v)
    return string.format('(%.4f, %.4f)', v.x, v.y)
  end
end

local function defineVec2d_t (t, mt)
  function mt.__index.toVec2i (v)
    return Vec2i(v.x, v.y)
  end

  function mt.__index.toVec2f (v)
    return Vec2f(v.x, v.y)
  end

  function mt.__index.toVec3d (v)
    return Vec3d(v.x, v.y, 0.0)
  end

  function mt.__index.toVec4d (v)
    return Vec4d(v.x, v.y, 0.0, 0.0)
  end

  function mt.__tostring (v)
    return string.format('(%.4f, %.4f)', v.x, v.y)
  end
end

onDef_Vec2i_t = function (t, mt) defineVec2_t(t, mt) defineVec2i_t(t, mt) end
onDef_Vec2f_t = function (t, mt) defineVec2_t(t, mt) defineVec2f_t(t, mt) end
onDef_Vec2d_t = function (t, mt) defineVec2_t(t, mt) defineVec2d_t(t, mt) end


text:
local libphx = require('ffi.libphx').lib
local memory

function onDef_Profiler (t, mt)
  t.BeginMemoryProfile = function ()
    GC.Collect()
    GC.Stop()
    memory = GC.GetMemory()
  end

  t.EndMemoryProfile = function ()
    local dMemory = GC.GetMemory() - memory
    GC.Start()
    return dMemory
  end

  t.TimeGPU = function (name, fn)
    Draw.Flush()
    local begin = TimeStamp.Get()
    fn()
    Draw.Flush()
    local duration = TimeStamp.GetElapsedMs(begin)
    printf('%s : %.2f ms', name, duration)
  end
end


text:
-- TODO : Ensure mt exists in genffi
local libphx = require('ffi.libphx').lib

function onDef_File (t, mt)
  t.Read = function (path)
    local s = libphx.File_ReadCstr(path)
    if s ~= nil then return ffi.string(s) else return nil
    end
  end
end


text:
local libphx = require('ffi.libphx').lib

function onDef_Math (t, mt)
  t.Tau = 6.283185307179586232
  t.Pi  = 3.141592653589793116
  t.Pi2 = 1.570796326794896558
  t.Pi4 = 0.785398163397448279
  t.Pi6 = 0.52359877559829881566
  t.Infinity = math.huge

  t.Float32MaxInt = 16777216 -- 2^24
  t.Float64MaxInt = 9007199254740992 -- 2^53

  t.ToRadians = rad
  t.ToDegrees = deg

  t.Approx = function (a, b)
    return abs(a - b) < 1e-4
  end

  t.Avg = function (a, b)
    return (a + b) / 2.0
  end

  t.DivAndMod = function (x, divisor)
    local div = floor(x / divisor)
    local mod = x - div*divisor
    return div, mod
  end

  -- Exponential moving average
  t.EMA = function (last, current, dt, period)
    local factor = exp(-dt / period)
    return factor * last + (1.0 - factor) * current
  end

  t.GeomAvg = function (a, b)
    return (sqrt(a * b))
  end

  t.Lerp = function (a, b, t)
    return a + (b - a) * t
  end

  t.LerpSnap = function (a, b, t)
    local r = a + (b - a) * t
    if abs(a - r) < 1e-3 then r = a end
    if abs(b - r) < 1e-3 then r = b end
    return r
  end

  -- Compute projectile impact time and point given position and velocity of
  -- source / destination + closing speed of projectile. Assumes projectile
  -- inherits the velocity of source (in addition to the closing speed).
  -- Returns impact_time, impact_point or nil if no solution exists
  t.Impact = function (pSrc, pDst, vSrc, vDst, speed)
    local dp = pDst - pSrc
    local dv = vDst - vSrc
    local a = dv:lengthSquared() - speed * speed
    local b = 2.0 * dv:dot(dp)
    local c = dp:lengthSquared()
    local d = b * b - 4.0 * a * c
    if d <= 0 then return nil end
    d = sqrt(d)
    local t = max((-b - d) / (2.0 * a), (-b + d) / 2.0 * a)
    return t, pDst + dv:scale(t)
  end

  t.InverseLerp = function (a, b, v)
    return (v - a) / (b - a)
  end

  t.OrthoVector = function (e1)
    local absY = abs(e1.y)
    if absY < 0.5 then
      return Vec3f(e1.z, 0, -e1.x):normalize()
    else
      return Vec3f(e1.y, -e1.x, 0):normalize()
    end
  end

  t.OrthoBasis = function (e1)
    local e2 = Math.OrthoVector(e1)
    local e3 = e1:cross(e2):normalize()
    return e2, e3
  end

  t.IsInfinite = function (x)
    return x == math.huge or x == -math.huge
  end

  t.IsNaN = function (x)
    return x ~= x
  end

  t.IsOk = function (x)
    return not Math.IsInfinite(x) and not Math.IsNaN(x)
  end

  t.AbsMax = function (a, b)
    return abs(b) > abs(a) and b or a
  end

  -- Smoothstep 3rd order ('Smoothstep')
  -- 3x^2 - 2x^3
  t.SmoothStep3 = function (x)
    return x * x * (3 - 2 * x)
  end

  -- Smoothstep 5th order ('Smootherstep')
  -- 6x^5 - 15x^4 + 10x^3
  t.SmoothStep5 = function (x)
    return x * x * x * (10 + x * (-15 + 6 * x))
  end

  -- Smoothstep 7th order ('Smootheststep')
  -- -20x^7 + 70x^6 - 84x^5 + 35x^4
  t.SmoothStep7 = function (x)
    return x * x * x * x * (35 + x * (-84 + x * (70 - 20 * x)))
  end

  t.Round = function (x)
    return floor(x + 0.5)
  end

  t.Saturate = function (x)
    return max(0.0, min(1.0, x))
  end

  t.Sign = function (x)
    return (x >= 0 and 1) or -1
  end

  t.Sign0 = function (x)
    return (x > 0 and 1) or (x < 0 and -1) or 0
  end

  t.Snap = function (x, a, b, epsilon)
    if abs(x - a) < epsilon then return a end
    if abs(x - b) < epsilon then return b end
    return x
  end

  t.Spherical = function (radius, pitch, yaw)
    return (Vec3f(
      radius * cos(pitch) * cos(yaw),
      radius * sin(pitch),
      radius * cos(pitch) * sin(yaw)))
  end

  -- WARNING : Integers only! Floats will wrap incorrectly.
  t.Wrap = function (x, min, max)
    -- NOTE : Lua's integer mod function wraps negative back to positive,
    --        hence the following simple formula works:
    --           wrapped = offset % range + min
    return (x - min) % (max - min + 1) + min
  end
end


text:
local libphx = require('ffi.libphx').lib

function onDef_Mesh_t (t, mt)
  mt.__index.getBound  = function (self) local b = Box3f() libphx.Mesh_GetBound(self, b) return b end
  mt.__index.getCenter = function (self) local v = Vec3f() libphx.Mesh_GetCenter(self, v) return v end
end


text:
local libphx = require('ffi.libphx').lib

function onDef_Directory (t, mt)
  local function walkDirectory (path, root, fn, recurse)
    local dir = t.Open(path)
    if dir == nil then return end

    local subdirs = {}
    while true do
      local name = dir:getNext()
      if name == nil then break end
      name = ffi.string(name)
      local fullpath = format('%s/%s', path, name)
      if File.IsDir(fullpath) then
        local root = #root > 0 and (root .. '/' .. name) or name
        subdirs[#subdirs + 1] = { path = fullpath, root = root }
      else
        fn(root, name, fullpath)
      end
    end
    dir:close()

    if recurse then
      for i = 1, #subdirs do
        local subdir = subdirs[i]
        walkDirectory(subdir.path, subdir.root, fn)
      end
    end
  end

  -- Collects all files in a directory tree for which the given filter function
  -- returns true. The filter is called with arguments filterFn(root, filename,
  -- fullpath). Note that files are returned as full (relative) paths.
  t.Filter = function (path, filterFn, recurse)
    local recurse = recurse or true
    local elems = List()
    t.Foreach(path, function (root, filename, fullpath)
      if filterFn(root, filename, fullpath) then elems:add(fullpath) end
    end, recurse)
    return elems
  end

  -- Visit each file in the directory tree, calling fn(root, filename, fullpath)
  -- for each file found
  t.ForEach = function (path, fn, recurse)
    local recurse = recurse or true
    if path:sub(#path, #path) == '/' then path = path:sub(1, #path - 1) end
    walkDirectory(path, '', fn, recurse)
  end

  -- Return a List of all elements in the given directory
  t.List = function (path)
    local dir = t.Open(path)
    if dir == nil then error('Failed to open directory at ' .. path) end
    local elems = List()
    while true do
      local elem = dir:getNext()
      if elem == nil then break end
      elems:add(ffi.string(elem))
    end
    dir:close()
    return elems
  end
end


text:
local libphx = require('ffi.libphx').lib

function onDef_Input (t, mt)
  t.GetMouseDelta    = function () local v = Vec2i() libphx.Input_GetMouseDelta   (v) return v end
  t.GetMousePosition = function () local v = Vec2i() libphx.Input_GetMousePosition(v) return v end
  t.GetMouseScroll   = function () local v = Vec2i() libphx.Input_GetMouseScroll  (v) return v end
  t.SetMousePosition = function () local v = Vec2i() libphx.Input_SetMousePosition(v) return v end
  t.SetMouseScroll   = function () local v = Vec2i() libphx.Input_SetMouseScroll  (v) return v end
end


text:
local libphx = require('ffi.libphx').lib

function onDef_Matrix_t (t, mt)
  function mt.__add (a, b)
    return (libphx.Matrix_Sum(a, b))
  end

  function mt.__mul (a, b)
    return (libphx.Matrix_Product(a, b))
  end

  function mt.__index.mulBox     (m, box) local out = Box3f() libphx.Matrix_MulBox    (m, out, box)                 return out end
  function mt.__index.mulDir     (m, dir) local out = Vec3f() libphx.Matrix_MulDir    (m, out, dir.x, dir.y, dir.z) return out end
  function mt.__index.mulPoint   (m, p)   local out = Vec3f() libphx.Matrix_MulPoint  (m, out, p.x, p.y, p.z)       return out end
  function mt.__index.mulVec     (m, v)   local out = Vec4f() libphx.Matrix_MulVec    (m, out, v.x, v.y, v.z, v.w)  return out end
  function mt.__index.getRight   (m)      local out = Vec3f() libphx.Matrix_GetRight  (m, out)                      return out end
  function mt.__index.getUp      (m)      local out = Vec3f() libphx.Matrix_GetUp     (m, out)                      return out end
  function mt.__index.getForward (m)      local out = Vec3f() libphx.Matrix_GetForward(m, out)                      return out end
  function mt.__index.getPos     (m)      local out = Vec3f() libphx.Matrix_GetPos    (m, out)                      return out end
  function mt.__index.toQuat     (m)      local out = Quat()  libphx.Matrix_ToQuat    (m, out)                      return out end
end


text:
local libphx = require('ffi.libphx').lib

function onDef_Tex2D_t (t, mt)
  mt.__index.getSize       = function (self)        local v = Vec2i() libphx.Tex2D_GetSize(self, v)             return v end
  mt.__index.getSizeLevel  = function (self, level) local v = Vec2i() libphx.Tex2D_GetSizeLevel(self, v, level) return v end
end


text:
local libphx = require('ffi.libphx').lib

function onDef_Viewport (t, mt)
  t.GetSize = function () local v = Vec2i() libphx.Viewport_GetSize(v) return v end
end


text:
local libphx = require('ffi.libphx').lib

function onDef_Physics_t (t, mt)
  mt.__index.rayCast    = function (self, ray)                   local r = RayCastResult()   libphx.Physics_RayCast(self, ray, r)                   return r end
  mt.__index.sphereCast = function (self, sphere)                local r = ShapeCastResult() libphx.Physics_SphereCast(self, sphere, r)             return r end
  mt.__index.boxCast    = function (self, pos, rot, halfExtents) local r = ShapeCastResult() libphx.Physics_BoxCast(self, pos, rot, halfExtents, r) return r end
end


text:
local libphx = require('ffi.libphx').lib

local function defineBox3 (box3, mt)
  function mt.__tostring (b)
    return format(
      '[(%.2f, %.2f, %.2f) -> (%.2f, %.2f, %.2f)]',
      b.lowerx, b.lowery, b.lowerz,
      b.upperx, b.uppery, b.upperz)
  end
end

onDef_Box3i_t = defineBox3
onDef_Box3f_t = defineBox3
onDef_Box3d_t = defineBox3


text:
local libphx = require('ffi.libphx').lib

local function defineVec3 (t, mt)
  local Vec3 = mt.__call

  function t.Identity ()
    return (Vec3(0, 0, 0))
  end
end

local function defineVec3_t (t, mt)
  local Vec3 = t

  function mt.__add (a, b)
    return (Vec3(a.x + b.x, a.y + b.y, a.z + b.z))
  end

  function mt.__div (a, b)
    return (Vec3(a.x / b.x, a.y / b.y, a.z / b.z))
  end

  function mt.__mul (a, b)
    return (Vec3(a.x * b.x, a.y * b.y, a.z * b.z))
  end

  function mt.__pow (a, b)
    return (Vec3(a.x ^ b.x, a.y ^ b.y, a.z ^ b.z))
  end

  function mt.__sub (a, b)
    return (Vec3(a.x - b.x, a.y - b.y, a.z - b.z))
  end

  function mt.__unm (v)
    return (Vec3(-v.x, -v.y, -v.z))
  end

  function mt.__index.iadd (a, b)
    a.x = a.x + b.x
    a.y = a.y + b.y
    a.z = a.z + b.z
  end

  function mt.__index.idiv (a, b)
    a.x = a.x / b.x
    a.y = a.y / b.y
    a.z = a.z / b.z
  end

  function mt.__index.idivs (a, s)
    a.x = a.x / s
    a.y = a.y / s
    a.z = a.z / s
  end

  function mt.__index.divs (a, s)
    return (Vec3(a.x / s, a.y / s, a.z / s))
  end

  function mt.__index.imadds (a, b, s)
    a.x = a.x + s * b.x
    a.y = a.y + s * b.y
    a.z = a.z + s * b.z
  end

  function mt.__index.imul (a, b)
    a.x = a.x * b.x
    a.y = a.y * b.y
    a.z = a.z * b.z
  end

  function mt.__index.imuls (a, s)
    a.x = a.x * s
    a.y = a.y * s
    a.z = a.z * s
  end

  function mt.__index.muls (a, s)
    return (Vec3(a.x * s, a.y * s, a.z * s))
  end

  function mt.__index.isub (a, b)
    a.x = a.x - b.x
    a.y = a.y - b.y
    a.z = a.z - b.z
  end

  function mt.__index.abs (v)
    return (Vec3(math.abs(v.x), math.abs(v.y), math.abs(v.z)))
  end

  function mt.__index.clamp (v, lower, upper)
    return (Vec3(
      math.max(lower.x, math.min(upper.x, v.x)),
      math.max(lower.y, math.min(upper.y, v.y)),
      math.max(lower.z, math.min(upper.z, v.z))))
  end

  function mt.__index.iclampLength (v, clamp)
    local lenSq = v:lengthSquared()
    if lenSq > clamp*clamp then
      v:iscale(clamp / sqrt(lenSq))
    end
  end

  function mt.__index.clampLength (v, clamp)
    local result = v:clone()
    local lenSq = v:lengthSquared()
    if lenSq > clamp*clamp then
      result:iscale(clamp / sqrt(lenSq))
    end
    return result
  end

  function mt.__index.icross (a, b, out)
    out.x = b.z * a.y - b.y * a.z
    out.y = b.x * a.z - b.z * a.x
    out.z = b.y * a.x - b.x * a.y
  end

  function mt.__index.cross (a, b)
    return (Vec3(
      b.z * a.y - b.y * a.z,
      b.x * a.z - b.z * a.x,
      b.y * a.x - b.x * a.y))
  end

  function mt.__index.distance (a, b)
    local dx = a.x - b.x
    local dy = a.y - b.y
    local dz = a.z - b.z
    return (math.sqrt(dx*dx + dy*dy + dz*dz))
  end

  function mt.__index.distanceSquared (a, b)
    local dx = a.x - b.x
    local dy = a.y - b.y
    local dz = a.z - b.z
    return dx*dx + dy*dy + dz*dz
  end

  function mt.__index.dot (a, b)
    return a.x*b.x + a.y*b.y + a.z*b.z
  end

  function mt.__index.approximatelyEqual (a, b)
    return abs(a.x - b.x) < 1e-3
       and abs(a.y - b.y) < 1e-3
       and abs(a.z - b.z) < 1e-3
  end

  function mt.__index.equal (a, b)
    return a.x == b.x
       and a.y == b.y
       and a.z == b.z
  end

  function mt.__index.iinverse (v)
    v:iscale(-1)
  end

  function mt.__index.inverse (v)
    return v:scale(-1)
  end

  function mt.__index.isZero (v)
    return v.x == 0 and v.y == 0 and v.z == 0
  end

  function mt.__index.length (v)
    return (math.sqrt(v.x*v.x + v.y*v.y + v.z*v.z))
  end

  function mt.__index.lengthSquared (v)
    return (v.x*v.x + v.y*v.y + v.z*v.z)
  end

  function mt.__index.ilerp (a, b, t)
    a.x = a.x + (b.x - a.x) * t
    a.y = a.y + (b.y - a.y) * t
    a.z = a.z + (b.z - a.z) * t
  end

  function mt.__index.lerp (a, b, t)
    return (Vec3(
      a.x + (b.x - a.x) * t,
      a.y + (b.y - a.y) * t,
      a.z + (b.z - a.z) * t))
  end

  function mt.__index.lerpv (a, b, tv)
    return (Vec3(
      a.x + (b.x - a.x) * tv.x,
      a.y + (b.y - a.y) * tv.y,
      a.z + (b.z - a.z) * tv.z))
  end

  function mt.__index.max (a, b)
    return (Vec3(
      math.max(a.x, b.x),
      math.max(a.y, b.y),
      math.max(a.z, b.z)))
  end

  function mt.__index.maxComponent (v)
    return (math.max(v.x, math.max(v.y, v.z)))
  end

  function mt.__index.min (a, b)
    return (Vec3(
      math.min(a.x, b.x),
      math.min(a.y, b.y),
      math.min(a.z, b.z)))
  end

  function mt.__index.minComponent (v)
    return (math.min(v.x, math.min(v.y, v.z)))
  end

  function mt.__index.inormalize (v)
    local l = math.sqrt(v.x*v.x + v.y*v.y + v.z*v.z)
    assert(l > 1e-12, 'Attempting to normalize vector with near-zero length')
    v.x = v.x / l
    v.y = v.y / l
    v.z = v.z / l
  end

  function mt.__index.normalize (v)
    local l = math.sqrt(v.x*v.x + v.y*v.y + v.z*v.z)
    assert(l > 1e-12, 'Attempting to normalize vector with near-zero length')
    return (Vec3(v.x / l, v.y / l, v.z / l))
  end

  function mt.__index.pNormalize (v, p)
    local l = (math.abs(v.x)^p + math.abs(v.y)^p + math.abs(v.z)^p)^(1.0/p)
    assert(l > 1e-12, 'Attempting to normalize vector with near-zero length')
    return (Vec3(v.x / l, v.y / l, v.z / l))
  end

  -- Assumes b is normalized
  function mt.__index.project (a, b)
    local d = a.x*b.x + a.y*b.y + a.z*b.z
    return (Vec3(d*b.x, d*b.y, d*b.z))
  end

  -- Assumes b is normalized
  function mt.__index.reject (a, b)
    local d = a.x*b.x + a.y*b.y + a.z*b.z
    return (Vec3(a.x - d*b.x, a.y - d*b.y, a.z - d*b.z))
  end

  function mt.__index.iscale (v, s)
    v.x = v.x * s
    v.y = v.y * s
    v.z = v.z * s
  end

  function mt.__index.scale (v, s)
    return (Vec3(s*v.x, s*v.y, s*v.z))
  end

  function mt.__index.set (v, x, y, z)
    v.x = x
    v.y = y
    v.z = z
  end

  function mt.__index.setv (v, other)
    v.x = other.x
    v.y = other.y
    v.z = other.z
  end

  function mt.__index.sqrt (v)
    return (Vec3(math.sqrt(v.x), math.sqrt(v.y), math.sqrt(v.z)))
  end
end

local function defineVec3i_t (t, mt)
  function mt.__index.toVec3f (v)
    return Vec3f(v.x, v.y, v.z)
  end

  function mt.__index.toVec3d (v)
    return Vec3d(v.x, v.y, v.z)
  end

  function mt.__index.toVec2i (v)
    return Vec2i(v.x, v.y)
  end

  function mt.__index.toVec4i (v)
    return Vec4i(v.x, v.y, v.z, 0.0)
  end

  function mt.__tostring (v)
    return string.format('(%i, %i, %i)', v.x, v.y, v.z)
  end
end

local function defineVec3f_t (t, mt)
  function mt.__index.toVec3i (v)
    return Vec3i(v.x, v.y, v.z)
  end

  function mt.__index.toVec3d (v)
    return Vec3d(v.x, v.y, v.z)
  end

  function mt.__index.toVec2f (v)
    return Vec2f(v.x, v.y)
  end

  function mt.__index.toVec4f (v)
    return Vec4f(v.x, v.y, v.z, 0.0)
  end

  function mt.__tostring (v)
    return string.format('(%.4f, %.4f, %.4f)', v.x, v.y, v.z)
  end
end

local function defineVec3d_t (t, mt)
  function mt.__index.toVec3i (v)
    return Vec3i(v.x, v.y, v.z)
  end

  function mt.__index.toVec3f (v)
    return Vec3f(v.x, v.y, v.z)
  end

  function mt.__index.toVec2d (v)
    return Vec2d(v.x, v.y)
  end

  function mt.__index.toVec4d (v)
    return Vec4d(v.x, v.y, v.z, 0.0)
  end

  function mt.__tostring (v)
    return string.format('(%.4f, %.4f, %.4f)', v.x, v.y, v.z)
  end
end

onDef_Vec3i = defineVec3
onDef_Vec3f = defineVec3
onDef_Vec3d = defineVec3

onDef_Vec3i_t = function (t, mt) defineVec3_t(t, mt) defineVec3i_t(t, mt) end
onDef_Vec3f_t = function (t, mt) defineVec3_t(t, mt) defineVec3f_t(t, mt) end
onDef_Vec3d_t = function (t, mt) defineVec3_t(t, mt) defineVec3d_t(t, mt) end


text:
local libphx = require('ffi.libphx').lib

function onDef_RNG_t (t, mt)
  mt.__index.choose    = function (self, table) return table[libphx.RNG_GetInt(self, 1, #table)] end
  mt.__index.getAxis2  = function (self)       local v = Vec2f() libphx.RNG_GetAxis2(self, v)      return v end
  mt.__index.getAxis3  = function (self)       local v = Vec3f() libphx.RNG_GetAxis3(self, v)      return v end
  mt.__index.getDir2   = function (self)       local v = Vec2f() libphx.RNG_GetDir2(self, v)       return v end
  mt.__index.getDir3   = function (self)       local v = Vec3f() libphx.RNG_GetDir3(self, v)       return v end
  mt.__index.getDisc   = function (self)       local v = Vec2f() libphx.RNG_GetDisc(self, v)       return v end
  mt.__index.getSphere = function (self)       local v = Vec3f() libphx.RNG_GetSphere(self, v)     return v end
  mt.__index.getVec2   = function (self, l, u) local v = Vec2f() libphx.RNG_GetVec2(self, v, l, u) return v end
  mt.__index.getVec3   = function (self, l, u) local v = Vec3f() libphx.RNG_GetVec3(self, v, l, u) return v end
  mt.__index.getVec4   = function (self, l, u) local v = Vec4f() libphx.RNG_GetVec4(self, v, l, u) return v end
  mt.__index.getQuat   = function (self)       local q = Quat()  libphx.RNG_GetQuat(self, q)       return q end
end


text:
local libphx = require('ffi.libphx').lib

function onDef_Font_t (t, mt)
  mt.__index.getSize = function (self, text)
    local v = Vec4i()
    libphx.Font_GetSize(self, v, text)
    return v
  end
end


text:
local libphx = require('ffi.libphx').lib

function onDef_Window_t (t, mt)
  mt.__index.getPosition = function (self) local v = Vec2i() libphx.Window_GetPosition(self, v) return v end
  mt.__index.getSize     = function (self) local v = Vec2i() libphx.Window_GetSize(self, v)     return v end
end