text:
/* -------------------------------------------------------------------------- */

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

struct ImGuiClipRect {
  ImGuiClipRect* prev;
  Vec2f p1;
  Vec2f p2;
};

struct ImGuiCursor {
  ImGuiCursor* prev;
  Vec2f pos;
};

struct ImGuiLine {
  ImGuiLine* next;
  Vec4f color;
  Vec2f p1;
  Vec2f p2;
};

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

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

struct ImGuiTex2D {
  ImGuiTex2D* next;
  Tex2D* tex;
  Vec2f pos;
  Vec2f size;
};

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

struct ImGuiData {
  Vec2f size;
  Vec2f offset;
  float scroll;
};

struct ImGuiWidget {
  ImGuiWidget* prev;
  uint64 hash;
  uint32 index;
  Vec2f pos;
  Vec2f size;
};

struct ImGuiStyle {
  ImGuiStyle* prev;
  Font* font;
  Font* fontSubheading;
  Vec2f spacing;
  Vec2f padding;
  Vec2f scrollBarSize;

  Vec4f buttonColor;
  Vec4f buttonColorFocus;
  Vec4f frameColor;
  Vec4f textColor;
  Vec4f textColorFocus;
};

struct ImGuiLayer {
  ImGuiLayer* parent;
  ImGuiLayer* next;
  ImGuiLayer* children;

  Vec2f pos;
  Vec2f size;
  uint64 hash;
  uint32 index;
  bool clip;

  ImGuiTex2D* tex2DList;
  ImGuiRect* rectList;
  ImGuiPanel* panelList;
  ImGuiText* textList;
  ImGuiLine* lineList;
};

struct ImGuiLayout {
  ImGuiLayout* prev;
  Vec2f lower;
  Vec2f upper;
  Vec2f size;
  Vec2f spacing;
  int styleVars;
  bool horizontal;
};

struct ImGui {
  ImGuiLayer* layer;
  ImGuiLayer* layerLast;
  ImGuiLayout* layout;
  ImGuiWidget* widget;
  ImGuiWidget* widgetLast;
  ImGuiStyle* style;
  ImGuiClipRect* clipRect;
  ImGuiCursor* cursorStack;

  Vec2f cursor;
  Vec2f mouse;

  uint64 focus[FocusType_SIZE];
  uint64 dragging;

  bool activate;
  Vec2f forceSize;

  HashMap* data;

  /* TODO : Stack allocator. */
  MemPool* layoutPool;
  MemPool* widgetPool;
  MemPool* stylePool;
  MemPool* clipRectPool;
  MemPool* cursorPool;
  MemPool* tex2DPool;
  MemPool* panelPool;
  MemPool* rectPool;
  MemPool* textPool;
  MemPool* linePool;
} static self;

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

inline static void EmitLine (
  Vec4f const& color,
  Vec2f const& p1,
  Vec2f const& p2)
{
  ImGuiLine* e = (ImGuiLine*)MemPool_Alloc(self.linePool);
  e->color = color;
  e->p1 = p1;
  e->p2 = p2;
  e->next = self.layer->lineList;
  self.layer->lineList = e;
}

inline static void EmitPanel (
  Vec4f const& color,
  Vec2f const& pos,
  Vec2f const& size,
  float innerAlpha = 1.0f,
  float bevel = 4.0f)
{
  ImGuiPanel* e = (ImGuiPanel*)MemPool_Alloc(self.panelPool);
  e->color = color;
  e->pos = pos;
  e->size = size;
  e->innerAlpha = innerAlpha;
  e->bevel = bevel;
  e->next = self.layer->panelList;
  self.layer->panelList = e;
}

inline static void EmitRect (
  Vec4f const& color,
  Vec2f const& pos,
  Vec2f const& size,
  bool outline = false)
{
  ImGuiRect* e = (ImGuiRect*)MemPool_Alloc(self.rectPool);
  e->color = color;
  e->pos = pos;
  e->size = size;
  e->outline = outline;
  e->next = self.layer->rectList;
  self.layer->rectList = e;
}

inline static void EmitTex2D (
  Tex2D* tex,
  Vec2f const& pos,
  Vec2f const& size)
{
  ImGuiTex2D* e = (ImGuiTex2D*)MemPool_Alloc(self.tex2DPool);
  e->tex = tex;
  e->pos = pos;
  e->size = size;
  e->next = self.layer->tex2DList;
  self.layer->tex2DList = e;
}

inline static void EmitText (
  Font* font,
  Vec4f const& color,
  Vec2f const& pos,
  cstr text)
{
  ImGuiText* e = (ImGuiText*)MemPool_Alloc(self.textPool);
  e->font = font;
  e->color = color;
  e->pos = pos;
  e->text = StrDup(text);
  e->next = self.layer->textList;
  self.layer->textList = e;
}

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

inline static ImGuiData* GetData (uint64 hash) {
  ImGuiData* data = (ImGuiData*)HashMap_GetRaw(self.data, hash);
  if (!data) {
    data = MemNew(ImGuiData);
    data->size = Vec2f_Create(0, 0);
    data->offset = Vec2f_Create(0, 0);
    data->scroll = 0;
    HashMap_SetRaw(self.data, hash, data);
  }
  return data;
}

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

static void ImGui_PushDefaultStyle () {
  static Font* font = 0;
  static Font* fontSubheading = 0;
  if (!font) {
    font = Font_Load("Share", 16);
    fontSubheading = Font_Load("Iceland", 18);
  }

  ImGuiStyle* style = (ImGuiStyle*)MemPool_Alloc(self.stylePool);
  style->prev = self.style;
  style->font = font;
  style->fontSubheading = fontSubheading;
  style->spacing = Vec2f_Create(8.0f, 8.0f);
  style->padding = Vec2f_Create(8.0f, 8.0f);
  style->scrollBarSize = Vec2f_Create(4.0f, 4.0f);
  style->buttonColor = Vec4f_Create(0.1f, 0.12f, 0.15f, 1.0f);
  style->buttonColorFocus = Vec4f_Create(0.1f, 0.6f, 1.0f, 1.0f);
  style->frameColor = Vec4f_Create(0.1f, 0.12f, 0.15f, 0.95f);
  style->textColor = Vec4f_Create(1.0f, 1.0f, 1.0f, 1.0f);
  style->textColorFocus = Vec4f_Create(0.1f, 0.1f, 0.1f, 1.0f);
  self.style = style;
}

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

static void ImGui_PushClipRect (Vec2f pos, Vec2f size) {
  ImGuiClipRect* rect = MemNew(ImGuiClipRect);
  ImGuiClipRect* prev = self.clipRect;
  rect->prev = prev;
  rect->p1 = pos;
  rect->p2 = Vec2f_Add(pos, size);
  if (prev) {
    rect->p1.x = Max(rect->p1.x, prev->p1.x);
    rect->p1.y = Max(rect->p1.y, prev->p1.y);
    rect->p2.x = Min(rect->p2.x, prev->p2.x);
    rect->p2.y = Min(rect->p2.y, prev->p2.y);
  }
  self.clipRect = rect;
}

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

inline static bool IsClipped (Vec2f p) {
  if (!self.clipRect) return false;
  return
    p.x < self.clipRect->p1.x ||
    p.y < self.clipRect->p1.y ||
    self.clipRect->p2.x < p.x ||
    self.clipRect->p2.y < p.y;
}

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

inline static void Advance (Vec2f size) {
  if (self.layout->horizontal) {
    self.cursor.x += size.x;
    self.layout->spacing.x = self.style->spacing.x;
  } else {
    self.cursor.y += size.y;
    self.layout->spacing.y = self.style->spacing.y;
  }
}

inline static uint64 HashGet () {
  return Hash_FNV64_Incremental(
     self.widget->hash,
    &self.widget->index,
    sizeof(self.widget->index));
}

inline static uint64 HashNext () {
  self.widget->index++;
  return HashGet();
}

inline static uint64 HashPeekNext () {
  uint32 index = self.widget->index + 1;
  return Hash_FNV64_Incremental(self.widget->hash, &index, sizeof(index));
}

inline static void TransformPos (float& x, float& y) {
  if (x < 0.0f) x = self.layout->upper.x + x;
  if (y < 0.0f) y = self.layout->upper.y + y;
}

inline static void TransformSize (float& sx, float& sy) {
  if (sx <= 0.0f) sx = (self.layout->upper.x - self.cursor.x) + sx;
  if (sy <= 0.0f) sy = (self.layout->upper.y - self.cursor.y) + sy;
}

inline static Vec2f GetNextSize (Vec2f s) {
  TransformSize(s.x, s.y);
  return s;
}

inline static bool RectContains (Vec2f pos, Vec2f size, Vec2f p) {
  return
    pos.x <= p.x && p.x <= pos.x + size.x &&
    pos.y <= p.y && p.y <= pos.y + size.y;
}

inline static void Spacing () {
  self.cursor.x += self.layout->spacing.x;
  self.cursor.y += self.layout->spacing.y;
  self.layout->spacing.x = 0;
  self.layout->spacing.y = 0;
}

static void ImGui_PushLayout (float sx, float sy, bool horizontal) {
  TransformSize(sx, sy);
  ImGuiLayout* layout = (ImGuiLayout*)MemPool_Alloc(self.layoutPool);
  layout->prev = self.layout;
  layout->lower = self.cursor;
  layout->upper = Vec2f_Create(self.cursor.x + sx, self.cursor.y + sy);
  layout->size = Vec2f_Create(sx, sy);
  layout->styleVars = 0;
  layout->horizontal = horizontal;
  self.layout = layout;
}

static void ImGui_PopLayout () {
  ImGuiLayout* layout = self.layout;
  for (int i = 0; i < layout->styleVars; ++i)
    ImGui_PopStyle();
  self.layout = layout->prev;
  MemPool_Dealloc(self.layoutPool, layout);
}

inline static void ImGui_Pad (float mx = 1.0f, float my = 1.0f) {
  float px = mx * self.style->padding.x;
  float py = my * self.style->padding.y;
  self.cursor.x += px;
  self.cursor.y += py;
  self.layout->lower.x += px;
  self.layout->lower.y += py;
  self.layout->upper.x -= px;
  self.layout->upper.y -= py;
  self.layout->size.x -= 2.0f * px;
  self.layout->size.y -= 2.0f * py;
}

static void ImGui_Unpad (float mx = 1.0f, float my = 1.0f) {
  float px = mx * self.style->padding.x;
  float py = my * self.style->padding.y;
  self.layout->lower.x -= px;
  self.layout->lower.y -= py;
  self.layout->upper.x += px;
  self.layout->upper.y += py;
  self.layout->size.x += 2.0f * px;
  self.layout->size.y += 2.0f * py;
  self.cursor.x -= px;
  self.cursor.y -= py;
}

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

static void ImGui_BeginWidget (float sx, float sy) {
  Spacing();
  TransformSize(sx, sy);
  ImGuiWidget* widget = (ImGuiWidget*)MemPool_Alloc(self.widgetPool);

  widget->prev = self.widget;
  widget->index = 0;
  widget->pos = Vec2f_Create(self.cursor.x, self.cursor.y);
  widget->size = Vec2f_Create(sx, sy);

  /* Compute this widget's hash. */ {
    if (self.widget) {
      self.widget->index++;
      widget->hash = Hash_FNV64_Incremental(
         self.widget->hash,
        &self.widget->index,
        sizeof(self.widget->index));
    } else {
      widget->hash = Hash_FNV64_Init();
    }
  }

  self.widget = widget;
}

static void ImGui_EndWidget () {
  if (self.widgetLast)
    MemPool_Dealloc(self.widgetPool, self.widgetLast);
  self.cursor = self.widget->pos;
  self.widgetLast = self.widget;
  self.widget = self.widget->prev;
  Advance(self.widgetLast->size);
}

static bool ImGui_Focus (ImGuiWidget* widget, int focusType) {
  if (self.focus[focusType] == 0)
    if (!IsClipped(self.mouse) && RectContains(widget->pos, widget->size, self.mouse))
      self.focus[focusType] = widget->hash;
  return self.focus[focusType] == widget->hash;
}

inline static bool ImGui_FocusCurrent (int focusType) {
  return ImGui_Focus(self.widget, focusType);
}

inline static bool ImGui_FocusLast (int focusType) {
  return ImGui_Focus(self.widgetLast, focusType);
}

inline bool HasFocus (uint64 hash, int focusType) {
  return self.focus[focusType] == hash;
}

inline bool TryFocusRect (uint64 hash, int focusType, Vec2f pos, Vec2f size) {
  if (self.focus[focusType] == 0)
    if (!IsClipped(self.mouse) && RectContains(pos, size, self.mouse))
      self.focus[focusType] = hash;
  return self.focus[focusType] == hash;
}

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

static void ImGuiLayer_Free (ImGuiLayer* self) {
  ImGuiLayer* child = self->children;
  while (child) {
    ImGuiLayer* next = child->next;
    ImGuiLayer_Free(child);
    child = next;
  }

  /* TODO : Stack allocation for strs. */
  for (ImGuiText* e = self->textList; e; e = e->next)
    StrFree(e->text);
  MemFree(self);
}

static ImGuiLayer* ImGui_PushLayer (bool clip) {
  ImGuiLayer* layer = MemNew(ImGuiLayer);
  layer->parent = self.layer;
  layer->children = 0;
  layer->next = 0;

  layer->pos = self.layout->lower;
  layer->size = self.layout->size;
  layer->index = 0;
  layer->clip = clip;

  layer->tex2DList = 0;
  layer->panelList = 0;
  layer->rectList = 0;
  layer->textList = 0;
  layer->lineList = 0;

  if (self.layer) {
    layer->next = self.layer->children;
    self.layer->children = layer;
    layer->hash = HashNext();
  } else {
    layer->hash = Hash_FNV64_Init();
  }

  self.layer = layer;
  if (clip)
    ImGui_PushClipRect(self.layer->pos, self.layer->size);
  return layer;
}

static void ImGui_PopLayer () {
  if (self.layer->clip)
    ImGui_PopClipRect();
  self.layerLast = self.layer;
  self.layer = self.layer->parent;
}

static void ImGui_DrawLayer (ImGuiLayer const* self) {
  if (self->clip)
    ClipRect_PushCombined(UNPACK2(self->pos), UNPACK2(self->size));

  for (ImGuiTex2D const* e = self->tex2DList; e; e = e->next) {
    Draw_Color(1, 1, 1, 1);
    Tex2D_Draw(e->tex, UNPACK2(e->pos), UNPACK2(e->size));
  }

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

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

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

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

    Shader_Stop(shader);
  }

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

#if 0
  for (ImGuiLine const* e = self->lineList; e; e = e->next) {
    Draw_Color(UNPACK4(e->color));
    Draw_Line(UNPACK2(e->p1), UNPACK2(e->p2));
  }
#else
  if (self->lineList) {
    RenderState_PushBlendMode(BlendMode_Additive);
    static Shader* shader = 0;
    if (!shader)
      shader = Shader_Load("vertex/ui", "fragment/ui/line");
    const float pad = 64.0f;
    Shader_Start(shader);
    for (ImGuiLine const* e = self->lineList; e; e = e->next) {
      float xMin = Min(e->p1.x, e->p2.x) - pad;
      float yMin = Min(e->p1.y, e->p2.y) - pad;
      float xMax = Max(e->p1.x, e->p2.x) + pad;
      float yMax = Max(e->p1.y, e->p2.y) + pad;
      float sx = xMax - xMin;
      float sy = yMax - yMin;
      Shader_SetFloat2("origin", xMin, yMin);
      Shader_SetFloat2("size", sx, sy);
      Shader_SetFloat2("p1", e->p1.x, e->p1.y);
      Shader_SetFloat2("p2", e->p2.x, e->p2.y);
      Shader_SetFloat4("color", UNPACK4(e->color));
      Draw_Rect(xMin, yMin, sx, sy);
    }
    Shader_Stop(shader);
    RenderState_PopBlendMode();
  }
#endif

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

  for (ImGuiLayer const* e = self->children; e; e = e->next)
    ImGui_DrawLayer(e);

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


text:
#include "Array.h"
#include "Intersect.h"
#include "LineSegment.h"
#include "Plane.h"
#include "Polygon.h"
#include "Ray.h"
#include "Triangle.h"

inline static bool PointsInPlane (Plane* plane, Polygon* polygon) {
  Vec3f* v    = ArrayList_GetData(polygon->vertices);
  int32  vLen = ArrayList_GetSize(polygon->vertices);

  for (int32 i = 0; i < vLen; i++) {
    float dist = Vec3f_Dot(plane->n, v[i]) - plane->d;
    if (Abs(dist) >= PLANE_THICKNESS_EPSILON) return false;
  }

  return true;
};

void Polygon_ToPlane (Polygon* polygon, Plane* out) {
  //NOTE: Newell's method, Realtime Collision Detection p494

  //OPTIMIZE: Could store planes with each polygon in the mesh. Since we are
  //generating them it seems dumb to hand off the polygons and make the BSP
  //re-calculate planes. These can eventually be thrown away to save memory.

  //OPTIMIZE: For offline meshes (if these will exist) the vertices can be
  //ordered such that the cross product of the first 3 gives a reasonable normal.

  Vec3f* v    = ArrayList_GetData(polygon->vertices);
  int32  vLen = ArrayList_GetSize(polygon->vertices);

  Vec3d n = { 0 };
  Vec3d centroid = { 0 };

  Vec3d vCur = Vec3f_ToVec3d(v[vLen - 1]);
  for (int32 i = 0; i < vLen; i++) {
    Vec3d vPrev = vCur;
    vCur = Vec3f_ToVec3d(v[i]);

    n.x += (vPrev.y - vCur.y) * (vPrev.z + vCur.z);
    n.y += (vPrev.z - vCur.z) * (vPrev.x + vCur.x);
    n.z += (vPrev.x - vCur.x) * (vPrev.y + vCur.y);
    centroid = Vec3d_Add(centroid, vCur);
  }
  n = Vec3d_Normalize(n);
  centroid = Vec3d_Divs(centroid, (double) vLen);

  out->n = Vec3d_ToVec3f(n);
  out->d = (float) Vec3d_Dot(centroid, n);

  CHECK2(Assert(PointsInPlane(out, polygon)));
}

void Polygon_ToPlaneFast (Polygon* polygon, Plane* out) {
  //NOTE: Doesn't normalize n and uses v[0] as the center.

  Vec3f* v = ArrayList_GetPtr(polygon->vertices, 0);
  int32 vLen = ArrayList_GetSize(polygon->vertices);

  Vec3f n = { 0 };
  for (int32 i = vLen - 1, j = 0; j < vLen; i = j, j++) {
    n.x += (v[i].y - v[j].y) * (v[i].z + v[j].z);
    n.y += (v[i].z - v[j].z) * (v[i].x + v[j].x);
    n.z += (v[i].x - v[j].x) * (v[i].y + v[j].y);
  }

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

  CHECK2(Assert(PointsInPlane(out, polygon)));
}

inline static void Polygon_SplitImpl (Polygon* polygon, Plane splitPlane, Polygon* back, Polygon* front) {
  Vec3f a = ArrayList_GetLast(polygon->vertices);
  PointClassification aSide = Plane_ClassifyPoint(&splitPlane, &a);

  for (int32 j = 0; j < ArrayList_GetSize(polygon->vertices); j++) {
    Vec3f b = ArrayList_Get(polygon->vertices, j);
    PointClassification bSide = Plane_ClassifyPoint(&splitPlane, &b);

    if (bSide == PointClassification_InFront) {
      if (aSide == PointClassification_Behind) {
        Vec3f i;
        LineSegment lineSegment = {b, a};
        bool hit = Intersect_LineSegmentPlane(&lineSegment, &splitPlane, &i);
        ArrayList_Append(front->vertices, i);
        ArrayList_Append(back->vertices, i);

        Assert(hit); UNUSED(hit);
        Assert(Plane_ClassifyPoint(&splitPlane, &i) == PointClassification_Coplanar);
      }
      ArrayList_Append(front->vertices, b);
    }
    else if (bSide == PointClassification_Behind) {
      if (aSide == PointClassification_InFront) {
        Vec3f i;
        LineSegment lineSegment = {a, b};
        bool hit = Intersect_LineSegmentPlane(&lineSegment, &splitPlane, &i);
        ArrayList_Append(front->vertices, i);
        ArrayList_Append(back->vertices, i);

        Assert(hit); UNUSED(hit);
        Assert(Plane_ClassifyPoint(&splitPlane, &i) == PointClassification_Coplanar);
      }
      else if (aSide == PointClassification_Coplanar) {
        ArrayList_Append(back->vertices, a);
      }
      ArrayList_Append(back->vertices, b);
    }
    else {
      if (aSide == PointClassification_Behind) {
        ArrayList_Append(back->vertices, b);
      }
      ArrayList_Append(front->vertices, b);
    }

    a = b;
    aSide = bSide;
  }
}

void Polygon_SplitSafe (Polygon* polygon, Plane splitPlane, Polygon* back, Polygon* front) {
  Polygon_SplitImpl(polygon, splitPlane, back, front);

  Polygon* polygons[] = { front, back };
  for (int i = 0; i < Array_GetSize(polygons); ++i) {
    Polygon* polygonPart = polygons[i];
    Vec3f* v    = ArrayList_GetData(polygonPart->vertices);
    int32  vLen = ArrayList_GetSize(polygonPart->vertices);

    Vec3f  vCur = v[vLen - 1];
    for (int32 l = 0; l < vLen; ++l) {
      Vec3f vPrev = vCur;
      vCur = v[l];

      float edgeLen = Vec3f_Length(Vec3f_Sub(vCur, vPrev));
      if (edgeLen < 0.75f * PLANE_THICKNESS_EPSILON) {
        /* TODO : Stick one warning at the end of the build process. */
        //CHECK2(Warn("Polygon_SplitSafe: Split produced a degenerate or sliver polygon. Sending it to both sides."));
        ArrayList_Clear(back->vertices);
        ArrayList_Clear(front->vertices);
        ArrayList_ForEach(polygon->vertices, Vec3f, vertex) {
          ArrayList_Append(back->vertices,  *vertex);
          ArrayList_Append(front->vertices, *vertex);
        }
        return;
      }
    }
  }
}

void Polygon_Split (Polygon* polygon, Plane splitPlane, Polygon* back, Polygon* front) {
  Polygon_SplitImpl(polygon, splitPlane, back, front);

  CHECK3 (
    Polygon_Validate(back);
    Polygon_Validate(front);
  )
}

void Polygon_GetCentroid (Polygon* polygon, Vec3f* out) {
  Vec3f centroid = { 0 };

  ArrayList_ForEach(polygon->vertices, Vec3f, v) {
    Vec3f_IAdd(&centroid, *v);
  }
  Vec3f_IDivs(&centroid, (float) ArrayList_GetSize(polygon->vertices));

  *out = centroid;
}

void Polygon_ConvexToTriangles (Polygon* polygon, int32* triangles_capacity, int32* triangles_size, Triangle** triangles_data) {
  Vec3f* v = ArrayList_GetData(polygon->vertices);
  int32 vLen = ArrayList_GetSize(polygon->vertices);

  for (int32 i = 1; i < vLen - 1; i++) {
    /* TODO : Do this all at once */
    IF_UNLIKELY (*triangles_capacity == *triangles_size) {
      *triangles_capacity = *triangles_capacity ? *triangles_capacity * 2 : 1;
      *triangles_data = (Triangle*)MemRealloc(*triangles_data, *triangles_capacity * sizeof(Triangle));
    }

    Triangle* triangle = *triangles_data + *triangles_size;
    (*triangles_size)++;
    triangle->vertices[0] = v[0];
    triangle->vertices[1] = v[i];
    triangle->vertices[2] = v[i + 1];
  }
}

Error Polygon_Validate (Polygon* polygon) {
  Vec3f* v    = ArrayList_GetData(polygon->vertices);
  int32  vLen = ArrayList_GetSize(polygon->vertices);

  Vec3f vCur = v[vLen - 1];
  for (int32 i = 0; i < vLen; i++) {
    Vec3f vPrev = vCur;
    vCur = v[i];

    //NaN or Inf
    Error e = Vec3f_Validate(vCur);
    if (e != Error_None) return Error_VertPos | e;

    //Degenerate
    for (int32 j = i + 1; j < vLen; j++) {
      if (Vec3f_Equal(vCur, v[j])) return Error_VertPos | Error_Degenerate;
    }

    //Sliver
    /* TODO : See comment on slivers in Triangle_Validate */
    float edgeLen = Vec3f_Length(Vec3f_Sub(vCur, vPrev));
    if (edgeLen < 0.75f*PLANE_THICKNESS_EPSILON) return Error_VertPos | Error_Underflow;
  }

  return Error_None;
}

repo_name: JoshParnell/libphx

repo_language: C

mime_type: text/x-c++


text:
#include "PhxMemory.h"
#include "OpenGL.h"
#include "SDL.h"
#include "Vec2.h"
#include "Viewport.h"
#include "Window.h"
#include "WindowMode.h"

struct Window {
  SDL_Window* handle;
  SDL_GLContext context;
  WindowMode mode;
};

Window* Window_Create (cstr title, int x, int y, int sx, int sy, WindowMode mode) {
  Window* self = MemNew(Window);
  mode |= SDL_WINDOW_OPENGL;
  self->handle = SDL_CreateWindow(title, x, y, sx, sy, mode);
  self->context = SDL_GL_CreateContext(self->handle);
  self->mode = mode;
  if (!self->context)
    Fatal("Failed to create OpenGL context for window");
  OpenGL_Init();
  return self;
}

void Window_Free (Window* self) {
  SDL_GL_DeleteContext(self->context);
  SDL_DestroyWindow(self->handle);
  MemFree(self);
}

void Window_BeginDraw (Window* self) {
  Vec2i size;
  SDL_GL_MakeCurrent(self->handle, self->context);
  Window_GetSize(self, &size);
  Viewport_Push(0, 0, size.x, size.y, true);
}

void Window_EndDraw (Window* self) {
  Viewport_Pop();
  SDL_GL_SwapWindow(self->handle);
}

void Window_GetSize (Window* self, Vec2i* out) {
  SDL_GetWindowSize(self->handle, &out->x, &out->y);
}

void Window_GetPosition (Window* self, Vec2i* out) {
  SDL_GetWindowPosition(self->handle, &out->x, &out->y);
}

cstr Window_GetTitle (Window* self) {
  return SDL_GetWindowTitle(self->handle);
}

void Window_SetFullscreen (Window* self, bool fs) {
  SDL_SetWindowFullscreen(self->handle, fs ? WindowMode_Fullscreen : 0);
}

void Window_SetPosition (Window* self, WindowPos x, WindowPos y) {
  SDL_SetWindowPosition(self->handle, x, y);
}

void Window_SetSize (Window* self, int sx, int sy) {
  SDL_SetWindowSize(self->handle, sx, sy);
}

void Window_SetTitle (Window* self, cstr title) {
  SDL_SetWindowTitle(self->handle, title);
}

void Window_SetVsync (Window*, bool vsync) {
  SDL_GL_SetSwapInterval(vsync ? 1 : 0);
}

void Window_ToggleFullscreen (Window* self) {
  if (self->mode & WindowMode_Fullscreen)
    SDL_SetWindowFullscreen(self->handle, 0);
  else
    SDL_SetWindowFullscreen(self->handle, WindowMode_Fullscreen);
  self->mode ^= WindowMode_Fullscreen;
}

void Window_Hide (Window* self) {
  SDL_HideWindow(self->handle);
}

void Window_Show (Window* self) {
  SDL_ShowWindow(self->handle);
}


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

const TexFilter TexFilter_Point           = GL_NEAREST;
const TexFilter TexFilter_PointMipPoint   = GL_NEAREST_MIPMAP_NEAREST;
const TexFilter TexFilter_PointMipLinear  = GL_NEAREST_MIPMAP_LINEAR;
const TexFilter TexFilter_Linear          = GL_LINEAR;
const TexFilter TexFilter_LinearMipPoint  = GL_LINEAR_MIPMAP_NEAREST;
const TexFilter TexFilter_LinearMipLinear = GL_LINEAR_MIPMAP_LINEAR;


text:
#include "Array.h"
#include "ArrayList.h"
#include "Input.h"
#include "InputBindings.h"
#include "InputEvent.h"
#include "Lua.h"
#include "PhxMath.h"
#include "PhxString.h"
#include "State.h"

static const int BindCount = 4;

struct RawButton {
  Button button;
  float  value;
};

struct AggregateButton {
  State  state;
  LuaRef onPressed;
  LuaRef onDown;
  LuaRef onReleased;
};

struct AggregateAxis {
  float  value;
  bool   invert;
  LuaRef onChanged;
};

struct AggregateAxis2D {
  Vec2f  value;
  LuaRef onChanged;
};

struct InputBinding {
  cstr            name;
  RawButton       rawButtons[4][BindCount];

  float           pressThreshold;
  float           releaseThreshold;
  float           exponent;
  float           deadzone;
  float           minValue;
  float           maxValue;

  Lua*            luaInstance;
  AggregateButton buttons[4];
  AggregateAxis   axes[2];
  AggregateAxis2D axis2D;
};

struct DownBinding {
  InputBinding*    binding;
  AggregateButton* button;
};

struct InputBindings {
  //ArrayList(InputBinding, inactiveBindings);
  ArrayList(InputBinding, activeBindings);
  ArrayList(DownBinding,  downBindings);
} static self = { 0 };

void InputBindings_Init () {
  //ArrayList_Reserve(self.inactiveBindings, 64);
  ArrayList_Reserve(self.activeBindings,   64);
  ArrayList_Reserve(self.downBindings,      8);
}

void InputBindings_Free () {
  //ArrayList_Free(self.inactiveBindings);
  ArrayList_Free(self.activeBindings);
  ArrayList_Free(self.downBindings);
}

static void InputBindings_RaiseCallback (cstr event, InputBinding* binding, LuaRef callback) {
  printf("%s - %s\n", event, binding->name);
  UNUSED(callback);
  /* TODO : Decide what all we want to pass to the callbacks (values, states, ...?) */
  //if (callback) {
  //  Lua_PushRef(binding->luaInstance, callback);
  //  Lua_PushPtr(binding->luaInstance, binding);
  //  Lua_Call(binding->luaInstance, 1, 0, 0);
  //}
}

void InputBindings_UpdateBinding (InputBinding* binding) {
  Vec2f value = {};
  float* axisValues[2] = { &value.x, &value.y };

  //Update Value
  {
    for (int iAxis = 0; iAxis < Array_GetSize(binding->axes); iAxis++) {
      float* axisValue = axisValues[iAxis];
      for (int iBind = 0; iBind < BindCount; iBind++) {
        *axisValue += binding->rawButtons[2*iAxis + 0][iBind].value;
        *axisValue -= binding->rawButtons[2*iAxis + 1][iBind].value;
      }
      *axisValue = (*axisValue - binding->deadzone) / (1.0f - binding->deadzone);
      *axisValue = Pow(*axisValue, binding->exponent);
      *axisValue = Clamp(*axisValue, binding->minValue, binding->maxValue);
    }

    float len = Vec2f_Length(value);
    if (len > 1.0f)
      Vec2f_IMuls(&value, 1.0f / len);
  }

  //Axis2D
  {
    AggregateAxis2D* axis2D = &binding->axis2D;
    if (!Vec2f_Equal(value, axis2D->value)) {
      axis2D->value = value;
      InputBindings_RaiseCallback("Changed", binding, axis2D->onChanged);
    }
  }

  //Axes
  {
    for (int iAxis = 0; iAxis < Array_GetSize(binding->axes); iAxis++) {
      AggregateAxis* axis = &binding->axes[iAxis];
      if (*axisValues[iAxis] != axis->value) {
        axis->value = *axisValues[iAxis];
        InputBindings_RaiseCallback(iAxis == 0 ? "Changed X" : "Changed Y", binding, axis->onChanged);
      }
    }
  }

  //Buttons
  for (int iBtn = 0; iBtn < Array_GetSize(binding->buttons); iBtn++) {
    AggregateButton* button = &binding->buttons[iBtn];
    float axisValue = binding->axes[iBtn / 2].value;
    bool isPos = !(iBtn & 1);

    //Pressed
    if (!HAS_FLAG(button->state, State_Down)) {
      if (isPos ? axisValue > binding->pressThreshold : axisValue < -binding->pressThreshold) {
        button->state |= State_Pressed;
        button->state |= State_Down;
        InputBindings_RaiseCallback("Pressed", binding, button->onPressed);

        DownBinding downBinding = {};
        downBinding.binding     = binding;
        downBinding.button      = button;
        ArrayList_Append(self.downBindings, downBinding);
      }
    //Released
    } else {
      if (isPos ? axisValue < binding->releaseThreshold : axisValue > -binding->releaseThreshold) {
        button->state |= State_Released;
        button->state &= ~State_Down;
        InputBindings_RaiseCallback("Released", binding, button->onReleased);

        ArrayList_RemoveWhere(self.downBindings, DownBinding,
          x->binding == binding && x->button == button
        );
      }
    }
  }
}

void InputBindings_Update () {
  //Down
  ArrayList_ForEachI(self.downBindings, i) {
    DownBinding down = ArrayList_Get(self.downBindings, i);
    InputBindings_RaiseCallback("Down", down.binding, down.button->onDown);
  }

  InputEvent event = {};
  while (Input_GetNextEvent(&event)) {
    //Match
    ArrayList_ForEachReverse(self.activeBindings, InputBinding, binding) {
      for (int iBtn = 0; iBtn < Array_GetSize(binding->rawButtons); iBtn++) {
        for (int iBind = 0; iBind < Array_GetSize(binding->rawButtons[iBtn]); iBind++) {
          RawButton* button = &binding->rawButtons[iBtn][iBind];

          if (event.button == button->button) {
            button->value = event.value;
            InputBindings_UpdateBinding(binding);
          }
        }
      }
    }
  }
}

#if false
void InputBindings_Register (InputBinding* binding) {
  //Lua* lua = Lua_GetActive();
  //if (!lua)
  //  Fatal("InputBinding_Register: No Lua instance is active");

  InputBinding binding = {};
  binding.states[Idx_xPos].button = binding->xPos;
  binding.states[Idx_xNeg].button = binding->xNeg;
  binding.states[Idx_yPos].button = binding->yPos;
  binding.states[Idx_yNeg].button = binding->yNeg;
  binding.pressThreshold          = binding->pressThreshold;
  binding.releaseThreshold        = binding->releaseThreshold;
  binding.exponent                = binding->exponent;

  //registeredBinding.luaInstance = lua;
  //registeredBinding.onPressed   = Lua_GetRef(lua);
  //registeredBinding.onDown      = Lua_GetRef(lua);
  //registeredBinding.onReleased  = Lua_GetRef(lua);
  //registeredBinding.onChanged   = Lua_GetRef(lua);

  ArrayList_Append(self.activeBindings, binding);
}

void InputBindings_Unregister (InputBinding* binding) {
  ArrayList_ForEachIReverse(self.downBindings, i) {
    RegisteredBinding* downBinding = ArrayList_Get(self.downBindings, i);
    if (StrEqual(binding->name, downBinding->name)) {
      ArrayList_RemoveAt(self.downBindings, i);
    }
  }

  ArrayList_ForEachIReverse(self.activeBindings, i) {
    RegisteredBinding* binding2 = ArrayList_GetPtr(self.activeBindings, i);
    if (StrEqual(binding->name, binding2->name)) {
      //Lua_ReleaseRef(binding2->luaInstance, binding2->onPressed);
      //Lua_ReleaseRef(binding2->luaInstance, binding2->onDown);
      //Lua_ReleaseRef(binding2->luaInstance, binding2->onReleased);
      //Lua_ReleaseRef(binding2->luaInstance, binding2->onChanged);
      ArrayList_RemoveAt(self.activeBindings, i);
    }
  }
}

const float InputBindings_DefaultPressThreshold   =  0.5f;
const float InputBindings_DefaultReleaseThreshold =  0.3f;
const float InputBindings_DefaultExponent         =  1.0f;
const float InputBindings_DefaultDeadzone         =  0.2f;
const float InputBindings_DefaultMinValue         = -FLT_MAX;
const float InputBindings_DefaultMaxValue         =  FLT_MAX;

void InputBindings_RegisterAll (InputBinding* binding, int count) {
  for (int i = 0; i < count; i++)
    InputBindings_Register(&binding[i]);
}

void InputBindings_UnregisterAll (InputBinding* binding, int count) {
  for (int i = 0; i < count; i++)
    InputBindings_Unregister(&binding[i]);
}
#endif

static const int iXPos = 0;
static const int iXNeg = 1;
static const int iYPos = 2;
static const int iYNeg = 3;

static const int iX    = 0;
static const int iY    = 1;

inline static bool InputBinding_GetButtonState (InputBinding* binding, int iBtn, State state) {
  return HAS_FLAG(binding->buttons[iBtn].state, state);
}

bool InputBinding_GetPressed (InputBinding* binding) {
  return InputBinding_GetButtonState(binding, iXPos, State_Pressed);
}

bool InputBinding_GetDown (InputBinding* binding) {
  return InputBinding_GetButtonState(binding, iXPos, State_Down);
}

bool InputBinding_GetReleased (InputBinding* binding) {
  return InputBinding_GetButtonState(binding, iXPos, State_Released);
}

float InputBinding_GetValue (InputBinding* binding) {
  return binding->axes[iX].value;
}

Vec2f InputBinding_GetVecValue (InputBinding* binding) {
  return binding->axis2D.value;
}

float InputBinding_GetXValue (InputBinding* binding) {
  return binding->axes[iX].value;
}

float InputBinding_GetYValue (InputBinding* binding) {
  return binding->axes[iY].value;
}

bool InputBinding_GetXPosPressed (InputBinding* binding) {
  return InputBinding_GetButtonState(binding, iXPos, State_Pressed);
}

bool InputBinding_GetXPosDown (InputBinding* binding) {
  return InputBinding_GetButtonState(binding, iXPos, State_Down);
}

bool InputBinding_GetXPosReleased (InputBinding* binding) {
  return InputBinding_GetButtonState(binding, iXPos, State_Released);
}

bool InputBinding_GetXNegPressed (InputBinding* binding) {
  return InputBinding_GetButtonState(binding, iXNeg, State_Pressed);
}

bool InputBinding_GetXNegDown (InputBinding* binding) {
  return InputBinding_GetButtonState(binding, iXNeg, State_Down);
}

bool InputBinding_GetXNegReleased (InputBinding* binding) {
  return InputBinding_GetButtonState(binding, iXNeg, State_Released);
}

bool InputBinding_GetYPosPressed (InputBinding* binding) {
  return InputBinding_GetButtonState(binding, iYPos, State_Pressed);
}

bool InputBinding_GetYPosDown (InputBinding* binding) {
  return InputBinding_GetButtonState(binding, iYPos, State_Down);
}

bool InputBinding_GetYPosReleased (InputBinding* binding) {
  return InputBinding_GetButtonState(binding, iYPos, State_Released);
}

bool InputBinding_GetYNegPressed (InputBinding* binding) {
  return InputBinding_GetButtonState(binding, iYNeg, State_Pressed);
}

bool InputBinding_GetYNegDown (InputBinding* binding) {
  return InputBinding_GetButtonState(binding, iYNeg, State_Down);
}

bool InputBinding_GetYNegReleased (InputBinding* binding) {
  return InputBinding_GetButtonState(binding, iYNeg, State_Released);
}

InputBinding* InputBinding_SetDeadzone (InputBinding* binding, float deadzone) {
  binding->deadzone = deadzone;
  return binding;
}

InputBinding* InputBinding_SetExponent (InputBinding* binding, float exponent) {
  binding->exponent = exponent;
  return binding;
}

inline static void InputBinding_SetInvert (InputBinding* binding, int iAxis, bool invert) {
  AggregateAxis* axis = &binding->axes[iAxis];
  if (invert != axis->invert) {
    axis->invert = invert;

    for (int iBind = 0; iBind < BindCount; iBind++) {
      RawButton* btnPos = &binding->rawButtons[2*iAxis + 0][iBind];
      RawButton* btnNeg = &binding->rawButtons[2*iAxis + 1][iBind];

      Button temp    = btnPos->button;
      btnPos->button = btnNeg->button;
      btnNeg->button = temp;
    }
  }
}

InputBinding* InputBinding_SetInvertX (InputBinding* binding, bool invert) {
  InputBinding_SetInvert(binding, 0, invert);
  return binding;
}

InputBinding* InputBinding_SetInvertY (InputBinding* binding, bool invert) {
  InputBinding_SetInvert(binding, 1, invert);
  return binding;
}

InputBinding* InputBinding_SetRange (InputBinding* binding, float min, float max) {
  binding->minValue = min;
  binding->maxValue = max;
  return binding;
}

InputBinding* InputBinding_SetThresholds (InputBinding* binding, float press, float release) {
  binding->pressThreshold   = press;
  binding->releaseThreshold = release;
  return binding;
}

/* TODO : Probably easier to aggregate values to point to top level Vec2f
 *        instead of juggling indicies and shit. */
/* TODO : What happens if you change binding settings while down? */


text:
#include "Hash.h"
#include "PhxMemory.h"
#include "RNG.h"
#include "PhxMath.h"
#include "Random.h"
#include "Quat.h"
#include "PhxString.h"
#include "TimeStamp.h"
#include "Vec2.h"
#include "Vec3.h"
#include "Vec4.h"

/* TODO : Rigorous testing of Xoroshiro128 & my implementation thereof. */

struct RNG {
  uint64 seed;
  uint64 state[2];
};

inline static uint64 RNG_Next64 (RNG* self) {
	return Random_Xoroshiro128(self->state[0], self->state[1]);
}

inline static uint32 RNG_Next32 (RNG* self) {
  return (uint32)(RNG_Next64(self) & 0xFFFFFFFFU);
}

/* Seed Xoroshiro with SM64. */
inline static void RNG_Init (RNG* self) {
  uint64 seed = self->seed;
  /* TODO : Using seed as both rolling output and seed buffer is not how SM64 is
   *        intended to work and is likely causing degenerate output space.
   *        Rigorous investigation needed. */
  for (int i = 0; i < 64; ++i)
    seed = Random_SplitMix64(seed);
  self->state[0] = Random_SplitMix64(seed);
  self->state[1] = Random_SplitMix64(seed);
  for (int i = 0; i < 64; ++i)
    RNG_Next64(self);
}

RNG* RNG_Create (uint64 seed) {
  RNG* self = MemNew(RNG);
  self->seed = seed;
  RNG_Init(self);
  return self;
}

RNG* RNG_FromStr (cstr s) {
  return RNG_Create(Hash_XX64(s, StrLen(s), 0));
}

RNG* RNG_FromTime () {
  return RNG_Create(TimeStamp_Get());
}

void RNG_Free (RNG* self) {
  MemFree(self);
}

void RNG_Rewind (RNG* self) {
  RNG_Init(self);
}

bool RNG_Chance (RNG* self, double probability) {
  return RNG_GetUniform(self) < probability;
}

int32 RNG_Get31 (RNG* self) {
  uint32 i = RNG_Next32(self) & 0x7FFFFFFFU;
  return *(int32*)&i;
}

uint32 RNG_Get32 (RNG* self) {
  return RNG_Next32(self);
}

uint64 RNG_Get64 (RNG* self) {
  return RNG_Next64(self);
}

double RNG_GetAngle (RNG* self) {
  return Tau * RNG_GetUniform(self);
}

int RNG_GetInt (RNG* self, int lower, int upper) {
  double t = RNG_GetUniform(self);
  return int(Round(lower + t * (upper - lower)));
}

RNG* RNG_GetRNG (RNG* self) {
  return RNG_Create(RNG_Get64(self));
}

double RNG_GetUniform (RNG* self) {
  return ldexp((double)RNG_Next32(self), -32);
}

double RNG_GetUniformRange (RNG* self, double lower, double upper) {
  double t = ldexp((double)RNG_Next32(self), -32);
  return lower + t * (upper - lower);
}

double RNG_GetErlang (RNG* self, int k) {
  double sum = 0.0;
  for (int i = 0; i < k; ++i)
    sum += RNG_GetExp(self);
  return sum / (double)k;
}

double RNG_GetExp (RNG* self) {
  return -Log(Max(1.0 - RNG_GetUniform(self), F64_EPSILON));
}

double RNG_GetGaussian (RNG* self) {
  double angle = RNG_GetAngle(self);
  double radius = 1.0 - RNG_GetUniform(self);
  return Cos(angle) * Sqrt(-2.0 * Log(radius));
}

void RNG_GetAxis2 (RNG* self, Vec2f* out) {
  *out = Vec2f_Create(0.0f, 0.0f);
  int axis = RNG_GetInt(self, 0, 3);
  switch (axis) {
    case 0: out->x =  1.0f; return;
    case 1: out->x = -1.0f; return;
    case 2: out->y =  1.0f; return;
    case 3: out->y = -1.0f; return;
  }
}

void RNG_GetAxis3 (RNG* self, Vec3f* out) {
  *out = Vec3f_Create(0.0f, 0.0f, 0.0f);
  int axis = RNG_GetInt(self, 0, 5);
  switch (axis) {
    case 0: out->x =  1.0f; return;
    case 1: out->x = -1.0f; return;
    case 2: out->y =  1.0f; return;
    case 3: out->y = -1.0f; return;
    case 4: out->z =  1.0f; return;
    case 5: out->z = -1.0f; return;
  }
}

void RNG_GetDir2 (RNG* self, Vec2f* out) {
  double angle = RNG_GetAngle(self);
  *out = Vec2f_Create((float)Cos(angle), (float)Sin(angle));
}

void RNG_GetDir3 (RNG* self, Vec3f* out) {
  for (;;) {
    double x = 2.0 * RNG_GetUniform(self) - 1.0;
    double y = 2.0 * RNG_GetUniform(self) - 1.0;
    double z = 2.0 * RNG_GetUniform(self) - 1.0;
    double m2 = x*x + y*y + z*z;
    if (m2 <= 1.0 && m2 > 1e-6) {
      m2 = Sqrt(m2);
      out->x = (float)(x / m2);
      out->y = (float)(y / m2);
      out->z = (float)(z / m2);
      return;
    }
  }
}

/* NOTE : Disc & Sphere use rejection-sampling. For 2D and 3D unit balls,
 *        rejection is very fast. Although not profiled, I suspect the
 *        trigonometric equivalents (i.e. analytic sampling requiring no loop)
 *        are actually slower. */

void RNG_GetDisc (RNG* self, Vec2f* out) {
  for (;;) {
    double x = 2.0 * RNG_GetUniform(self) - 1.0;
    double y = 2.0 * RNG_GetUniform(self) - 1.0;
    if ((x*x + y*y) <= 1.0) {
      out->x = (float)x;
      out->y = (float)y;
      return;
    }
  }
}

double RNG_GetSign (RNG* self) {
  return RNG_GetUniform(self) > 0.5 ? 1.0 : -1.0;
}

void RNG_GetSphere (RNG* self, Vec3f* out) {
  for (;;) {
    double x = 2.0 * RNG_GetUniform(self) - 1.0;
    double y = 2.0 * RNG_GetUniform(self) - 1.0;
    double z = 2.0 * RNG_GetUniform(self) - 1.0;
    if ((x*x + y*y + z*z) <= 1.0) {
      out->x = (float)x;
      out->y = (float)y;
      out->z = (float)z;
      return;
    }
  }
}

void RNG_GetVec2 (RNG* self, Vec2f* out, double lower, double upper) {
  out->x = (float)RNG_GetUniformRange(self, lower, upper);
  out->y = (float)RNG_GetUniformRange(self, lower, upper);
}

void RNG_GetVec3 (RNG* self, Vec3f* out, double lower, double upper) {
  out->x = (float)RNG_GetUniformRange(self, lower, upper);
  out->y = (float)RNG_GetUniformRange(self, lower, upper);
  out->z = (float)RNG_GetUniformRange(self, lower, upper);
}

void RNG_GetVec4 (RNG* self, Vec4f* out, double lower, double upper) {
  out->x = (float)RNG_GetUniformRange(self, lower, upper);
  out->y = (float)RNG_GetUniformRange(self, lower, upper);
  out->z = (float)RNG_GetUniformRange(self, lower, upper);
  out->w = (float)RNG_GetUniformRange(self, lower, upper);
}

/* http://marc-b-reynolds.github.io/distribution/2017/01/27/UniformRot.html */
void RNG_GetQuat (RNG* self, Quat* out) {
  Vec2f p0, p1;
  RNG_GetDisc(self, &p0);
  RNG_GetDisc(self, &p1);
  double d0 = Vec2f_LengthSquared(p0);
  double d1 = Vec2f_LengthSquared(p1) + F64_EPSILON;
  double s = Sqrt((1.0 - d0) / d1);
  out->x = (float)p0.y;
  out->y = (float)(p1.x * s);
  out->z = (float)(p1.y * s);
  out->w = (float)p0.x;
}


text:
#include "Mesh.h"
#include "Meshes.h"
#include "Vertex.h"

inline static void Mesh_AddPlane (
  Mesh* self,
  Vec3f origin,
  Vec3f du, Vec3f dv,
  int resU, int resV)
{
  Vec3f n = Vec3f_Normalize(Vec3f_Cross(du, dv));

  for (int iu = 0; iu < resU; ++iu) {
    float u = (float)iu / (float)(resU - 1);
    for (int iv = 0; iv < resV; ++iv) {
      float v = (float)iv / (float)(resV - 1);
      Vec3f p = Vec3f_Add(origin,
        Vec3f_Add(
          Vec3f_Muls(du, u),
          Vec3f_Muls(dv, v)));

      if (iu && iv) {
        int vc = Mesh_GetVertexCount(self);
        Mesh_AddQuad(self, vc, vc - resV, vc - resV - 1, vc - 1);
      }

      Mesh_AddVertex(self, p.x, p.y, p.z, n.x, n.y, n.z, u, v);
    }
  }
}

Mesh* Mesh_Box (int res) {
  const Vec3f origin[6] = {
    { -1, -1,  1 }, { -1, -1, -1 },
    {  1, -1, -1 }, { -1, -1, -1 },
    { -1,  1, -1 }, { -1, -1, -1 },
  };

  const Vec3f du[6] = {
    { 2, 0, 0 }, { 0, 2, 0 }, { 0, 2, 0 }, { 0, 0, 2 }, { 0, 0, 2 }, { 2, 0, 0 },
  };

  const Vec3f dv[6] = {
    { 0, 2, 0 }, { 2, 0, 0 }, { 0, 0, 2 }, { 0, 2, 0 }, { 2, 0, 0 }, { 0, 0, 2 },
  };

  Mesh* self = Mesh_Create();
  for (int i = 0; i < 6; ++i)
    Mesh_AddPlane(self, origin[i], du[i], dv[i], res, res);
  return self;
}

Mesh* Mesh_BoxSphere (int res) {
  Mesh* self = Mesh_Box(res);
  int vertexCount = Mesh_GetVertexCount(self);
  Vertex* vertexData = Mesh_GetVertexData(self);
  for (int i = 0; i < vertexCount; ++i) {
    Vertex* vertex = vertexData + i;
    vertex->p = Vec3f_Normalize(vertex->p);
  }
  return self;
}

Mesh* Mesh_Plane (Vec3f origin, Vec3f du, Vec3f dv, int resU, int resV) {
  Mesh* self = Mesh_Create();
  Mesh_AddPlane(self, origin, du, dv, resU, resV);
  return self;
}


text:
#include "GUID.h"

uint64 nextID = 1;

uint64 GUID_Create () {
  return nextID++;
}

bool GUID_Exists (uint64 id) {
  return id < nextID && id != 0;
}

void GUID_Reset () {
  nextID = 1;
}


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

#include <stdio.h>

const Modifier Modifier_Null  = 0 << 0;
const Modifier Modifier_Alt   = 1 << 0;
const Modifier Modifier_Ctrl  = 1 << 1;
const Modifier Modifier_Shift = 1 << 2;

cstr Modifier_ToString (Modifier modifier) {
  static char buffer[512];

  if (modifier == Modifier_Null)
    return "Modifier_Null";

  Modifier modifiers[] = {
    Modifier_Alt,
    Modifier_Ctrl,
    Modifier_Shift,
  };
  cstr names[] = {
    "Modifier_Alt",
    "Modifier_Ctrl",
    "Modifier_Shift",
  };

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

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


text:
#include "PhxMemory.h"
#include "StrBuffer.h"
#include "PhxString.h"

/* NOTE : In order to ensure null-termination of the internal buffer, we always
 *        allocate one byte in excess of the capacity. Hence, data[capacity] is
 *        actually in-bounds, and should always be zeroed. Furthermore, any
 *        bytes beyond size (i.e., data + size and beyond) are kept zeroed for
 *        convenience. */

/* TODO : Fuzz test / validate correctness. This API has not been rigorously
 *        tested, and is low-level enough to contain subtle mistakes. */

struct StrBuffer {
  char*  data;
  uint32 size;
  uint32 capacity;
};

inline static void StrBuffer_GrowTo (StrBuffer* self, uint32 newSize) {
  IF_UNLIKELY (newSize > self->capacity) {
    while (self->capacity < newSize) self->capacity *= 2;
    self->data = (char*)MemRealloc(self->data, self->capacity + 1);
    MemSet(self->data + self->size, 0, (self->capacity + 1) - self->size);
  }
}

inline static void StrBuffer_AppendData (StrBuffer* self, void const* data, uint32 len) {
  StrBuffer_GrowTo(self, self->size + len);
  MemCpy(self->data + self->size, data, len);
  self->size += len;
}

StrBuffer* StrBuffer_Create (uint32 capacity) {
  StrBuffer* self = MemNew(StrBuffer);
  self->data = (char*)MemAllocZero(capacity + 1);
  self->size = 0;
  self->capacity = capacity;
  return self;
}

StrBuffer* StrBuffer_FromStr (char const* s) {
  uint32 len = (uint32)StrLen(s);
  StrBuffer* self = StrBuffer_Create(len);
  self->size = len;
  MemCpy(self->data, s, len);
  return self;
}

void StrBuffer_Free (StrBuffer* self) {
  MemFree(self->data);
  MemFree(self);
}

void StrBuffer_Append (StrBuffer* self, StrBuffer* other) {
  StrBuffer_AppendData(self, (void const*)other->data, other->size);
}

void StrBuffer_AppendStr (StrBuffer* self, char const* other) {
  StrBuffer_AppendData(self, (void const*)other, StrLen(other));
}

inline static int32 StrBuffer_SetImpl (StrBuffer* self, cstr format, va_list args) {
  int32 newSize = vsnprintf(self->data, self->capacity + 1, format, args);
  Assert(newSize >= 0);

  IF_LIKELY ((uint32) newSize <= self->capacity) {
    self->size = newSize;
    return 0;
  } else {
    return newSize - self->capacity;
  }
}

void StrBuffer_Set (StrBuffer* self, cstr format, ...) {
  va_list args;
  va_start(args, format);

  int32 neededSpace = StrBuffer_SetImpl(self, format, args);
  IF_UNLIKELY (neededSpace > 0) {
    StrBuffer_GrowTo(self, self->capacity + neededSpace);

    va_list args2;
    va_start(args2, format);

    neededSpace = StrBuffer_SetImpl(self, format, args);
    Assert(neededSpace == 0);

    va_end(args2);
  }

  va_end(args);
}

StrBuffer* StrBuffer_Clone (StrBuffer* other) {
  StrBuffer* self = StrBuffer_Create(other->size);
  MemCpy(self->data, other->data, other->size);
  self->size = other->size;
  return self;
}

char const* StrBuffer_GetData (StrBuffer* self) {
  return self->data;
}


text:
#include "Bytes.h"
#include "DataFormat.h"
#include "Draw.h"
#include "PhxMemory.h"
#include "Metric.h"
#include "OpenGL.h"
#include "PixelFormat.h"
#include "RefCounted.h"
#include "RenderTarget.h"
#include "Resource.h"
#include "PhxMath.h"
#include "PhxString.h"
#include "Tex2D.h"
#include "TexFormat.h"
#include "Vec2.h"
#include "Viewport.h"

extern uchar* Tex2D_LoadRaw(cstr path, int* sx, int* sy, int* components);
extern bool Tex2D_Save_Png(cstr path, int sx, int sy, int components, uchar* data);

struct Tex2D {
  RefCounted;
  uint handle;
  Vec2i size;
  TexFormat format;
};

inline static void Tex2D_Init () {
  GLCALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST))
  GLCALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST))
  GLCALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE))
  GLCALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE))
}

Tex2D* Tex2D_Create (int sx, int sy, TexFormat format) {
  if (!TexFormat_IsValid(format))
    Fatal("Tex2D_Create: Invalid texture format requested");

  Tex2D* self = MemNew(Tex2D);
  RefCounted_Init(self);

  self->size = Vec2i_Create(sx, sy);
  self->format = format;

  GLCALL(glGenTextures(1, &self->handle))
  GLCALL(glActiveTexture(GL_TEXTURE0))
  GLCALL(glBindTexture(GL_TEXTURE_2D, self->handle))
  GLCALL(glTexImage2D(GL_TEXTURE_2D, 0,
    self->format, self->size.x, self->size.y, 0,
    TexFormat_IsColor(format) ? GL_RED : GL_DEPTH_COMPONENT,
    GL_UNSIGNED_BYTE, 0))
  Tex2D_Init();
  GLCALL(glBindTexture(GL_TEXTURE_2D, 0))
  return self;
}

Tex2D* Tex2D_ScreenCapture () {
  Vec2i size; Viewport_GetSize(&size);
  Tex2D* self = Tex2D_Create(size.x, size.y, TexFormat_RGBA8);
  uint32* buf = MemNewArray(uint32, size.x * size.y);
  Metric_Inc(Metric_Flush);
  GLCALL(glReadPixels(0, 0, size.x, size.y, GL_RGBA, GL_UNSIGNED_BYTE, buf))
  for (int y = 0; y < size.y / 2; ++y)
  for (int x = 0; x < size.x; ++x)
    Swap(buf[size.x * y + x], buf[size.x * (size.y - y - 1) + x]);

  GLCALL(glBindTexture(GL_TEXTURE_2D, self->handle))
  GLCALL(glTexImage2D(GL_TEXTURE_2D, 0, TexFormat_RGBA8, size.x, size.y,
    0, GL_RGBA, GL_UNSIGNED_BYTE, buf))
  GLCALL(glBindTexture(GL_TEXTURE_2D, 0))
  return self;
}

void Tex2D_Acquire (Tex2D* self) {
  RefCounted_Acquire(self);
}

void Tex2D_Free (Tex2D* self) {
  RefCounted_Free(self) {
    GLCALL(glDeleteTextures(1, &self->handle))
    MemFree(self);
  }
}

void Tex2D_Pop (Tex2D*) {
  RenderTarget_Pop();
}

void Tex2D_Push (Tex2D* self) {
  RenderTarget_PushTex2D(self);
}

void Tex2D_PushLevel (Tex2D* self, int level) {
  RenderTarget_PushTex2DLevel(self, level);
}

void Tex2D_Clear (Tex2D* self, float r, float g, float b, float a) {
  RenderTarget_PushTex2D(self);
  Draw_Clear(r, g, b, a);
  RenderTarget_Pop();
}

Tex2D* Tex2D_Clone (Tex2D* self) {
  Tex2D* clone = Tex2D_Create(self->size.x, self->size.y, self->format);
  RenderTarget_PushTex2D(self);
  GLCALL(glBindTexture(GL_TEXTURE_2D, clone->handle))
  GLCALL(glCopyTexImage2D(GL_TEXTURE_2D, 0, self->format, 0, 0, self->size.x, self->size.y, 0))
  GLCALL(glBindTexture(GL_TEXTURE_2D, 0))
  RenderTarget_Pop();
  return clone;
}

void Tex2D_Draw (Tex2D* self, float x, float y, float sx, float sy) {
  Metric_AddDrawImm(1, 2, 4);
  GLCALL(glEnable(GL_TEXTURE_2D))
  GLCALL(glBindTexture(GL_TEXTURE_2D, self->handle))
  glBegin(GL_QUADS);
  glTexCoord2f(0, 0); glVertex2f(x, y);
  glTexCoord2f(0, 1); glVertex2f(x, y + sy);
  glTexCoord2f(1, 1); glVertex2f(x + sx, y + sy);
  glTexCoord2f(1, 0); glVertex2f(x + sx, y);
  GLCALL(glEnd())
  GLCALL(glDisable(GL_TEXTURE_2D))
}

void Tex2D_DrawEx (
  Tex2D* self,
  float x0, float y0,
  float x1, float y1,
  float u0, float v0,
  float u1, float v1)
{
  Metric_AddDrawImm(1, 2, 4);
  GLCALL(glEnable(GL_TEXTURE_2D))
  GLCALL(glBindTexture(GL_TEXTURE_2D, self->handle))
  glBegin(GL_QUADS);
  glTexCoord2f(u0, v0); glVertex2f(x0, y0);
  glTexCoord2f(u0, v1); glVertex2f(x0, y1);
  glTexCoord2f(u1, v1); glVertex2f(x1, y1);
  glTexCoord2f(u1, v0); glVertex2f(x1, y0);
  GLCALL(glEnd())
  GLCALL(glDisable(GL_TEXTURE_2D))
}

void Tex2D_GenMipmap (Tex2D* self) {
  GLCALL(glBindTexture(GL_TEXTURE_2D, self->handle))
  GLCALL(glGenerateMipmap(GL_TEXTURE_2D))
  GLCALL(glBindTexture(GL_TEXTURE_2D, 0))
}

void Tex2D_GetData (Tex2D* self, void* data, PixelFormat pf, DataFormat df) {
  Metric_Inc(Metric_Flush);
  GLCALL(glBindTexture(GL_TEXTURE_2D, self->handle))
  GLCALL(glGetTexImage(GL_TEXTURE_2D, 0, pf, df, data))
  GLCALL(glBindTexture(GL_TEXTURE_2D, 0))
}

Bytes* Tex2D_GetDataBytes (Tex2D* self, PixelFormat pf, DataFormat df) {
  int size = self->size.x * self->size.y;
  size *= DataFormat_GetSize(df);
  size *= PixelFormat_Components(pf);
  Bytes* data = Bytes_Create(size);
  Tex2D_GetData(self, Bytes_GetData(data), pf, df);
  Bytes_Rewind(data);
  return data;
}

TexFormat Tex2D_GetFormat (Tex2D* self) {
  return self->format;
}

uint Tex2D_GetHandle (Tex2D* self) {
  return self->handle;
}

void Tex2D_GetSize (Tex2D* self, Vec2i* out) {
  *out = self->size;
}

void Tex2D_GetSizeLevel (Tex2D* self, Vec2i* out, int level) {
  *out = self->size;
  for (int i = 0; i < level; ++i) {
    out->x /= 2;
    out->y /= 2;
  }
}

Tex2D* Tex2D_Load (cstr name) {
  cstr path = Resource_GetPath(ResourceType_Tex2D, name);
  int sx, sy, components = 4;
  uchar* data = Tex2D_LoadRaw(path, &sx, &sy, &components);
  Tex2D* self = Tex2D_Create(sx, sy, TexFormat_RGBA8);

  GLenum format =
    components == 4 ? GL_RGBA :
    components == 3 ? GL_RGB  :
    components == 2 ? GL_RG : GL_RED;

  GLCALL(glActiveTexture(GL_TEXTURE0))
  GLCALL(glBindTexture(GL_TEXTURE_2D, self->handle))
  GLCALL(glTexImage2D(GL_TEXTURE_2D, 0,
    GL_RGBA8, self->size.x, self->size.y, 0,
    format, GL_UNSIGNED_BYTE, data))
  Tex2D_Init();
  GLCALL(glBindTexture(GL_TEXTURE_2D, 0))

  MemFree(data);
  return self;
}

void Tex2D_SetAnisotropy (Tex2D* self, float factor) {
  GLCALL(glBindTexture(GL_TEXTURE_2D, self->handle))
  GLCALL(glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, factor))
  GLCALL(glBindTexture(GL_TEXTURE_2D, 0))
}

void Tex2D_SetData (Tex2D* self, void const* data, PixelFormat pf, DataFormat df) {
  GLCALL(glBindTexture(GL_TEXTURE_2D, self->handle))
  GLCALL(glTexImage2D(GL_TEXTURE_2D,
    0, self->format,
    self->size.x, self->size.y,
    0, pf, df, data))
  GLCALL(glBindTexture(GL_TEXTURE_2D, 0))
}

void Tex2D_SetDataBytes (Tex2D* self, Bytes* data, PixelFormat pf, DataFormat df) {
  Tex2D_SetData(self, Bytes_GetData(data), pf, df);
}

void Tex2D_SetMagFilter (Tex2D* self, TexFilter filter) {
  GLCALL(glBindTexture(GL_TEXTURE_2D, self->handle))
  GLCALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filter))
  GLCALL(glBindTexture(GL_TEXTURE_2D, 0))
}

void Tex2D_SetMinFilter (Tex2D* self, TexFilter filter) {
  GLCALL(glBindTexture(GL_TEXTURE_2D, self->handle))
  GLCALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filter))
  GLCALL(glBindTexture(GL_TEXTURE_2D, 0))
}

/* NOTE : In general, using BASE_LEVEL, MAX_LEVEL, and MIN/MAX_LOD params is
 *        dangerous due to known bugs in old Radeon & Intel drivers. See:
 *        (https://www.opengl.org/discussion_boards/showthread.php/
 *         166266-Using-GL_TEXTURE_BASE_LEVEL-with-a-comple-texture)
 *
 *        However, constraining the mip range to a single level (minLevel ==
 *        maxLevel) seems to be acceptable even on bad drivers. Thus, it is
 *        strongly advised to use this function only to constrain sampling to
 *        a single mip level. */
void Tex2D_SetMipRange (Tex2D* self, int minLevel, int maxLevel) {
  if (minLevel != maxLevel)
    Warn("Tex2D_SetMipRange: Setting mip range with min != max; this may fail"
         " on old drivers with mip-handling bugs.");
  GLCALL(glBindTexture(GL_TEXTURE_2D, self->handle))
  GLCALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, minLevel))
  GLCALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, maxLevel))
}

void Tex2D_SetTexel (Tex2D* self, int x, int y, float r, float g, float b, float a) {
  float rgba[] = { r, g, b, a };
  GLCALL(glBindTexture(GL_TEXTURE_2D, self->handle))
  GLCALL(glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, 1, 1, GL_RGBA, GL_FLOAT, rgba))
  GLCALL(glBindTexture(GL_TEXTURE_2D, 0))
}

void Tex2D_SetWrapMode (Tex2D* self, TexWrapMode mode) {
  GLCALL(glBindTexture(GL_TEXTURE_2D, self->handle))
  GLCALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, mode))
  GLCALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, mode))
  GLCALL(glBindTexture(GL_TEXTURE_2D, 0))
}

void Tex2D_Save (Tex2D* self, cstr path) {
  Metric_Inc(Metric_Flush);
  GLCALL(glBindTexture(GL_TEXTURE_2D, self->handle))
  uchar* buffer = (uchar*)MemAlloc(4 * self->size.x * self->size.y);
  GLCALL(glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, buffer))
  Tex2D_Save_Png(path, self->size.x, self->size.y, 4, buffer);
  MemFree(buffer);
  GLCALL(glBindTexture(GL_TEXTURE_2D, 0))
}


text:
#include "PhxMemory.h"
#include "Midi.h"
#include "PhxString.h"

#if WINDOWS
  #include <windows.h>
  #include <mmsystem.h>
#endif

#if LINUX
  #include <alsa/asoundlib.h>
#endif

#define MAX_QUEUED_MESSAGES 512

struct MidiDevice {
  int cursor;
  Vec2i buffer[MAX_QUEUED_MESSAGES];
  #if WINDOWS
    HMIDIIN handle;
  #endif
};

#if WINDOWS
inline static void MidiDevice_PushMessage (MidiDevice* self, int x, int y) {
  if (self->cursor < MAX_QUEUED_MESSAGES)
    self->buffer[self->cursor++] = Vec2i_Create(x, y);
}

void CALLBACK MidiDevice_Handler (
  HMIDIIN hMidiIn,
  UINT msg,
  DWORD_PTR instance,
  DWORD arg1,
  DWORD arg2)
{
  UNUSED(hMidiIn); UNUSED(arg2);
  uint8 status, byte1, byte2;
  MidiDevice* device = (MidiDevice*)instance;

  switch (msg) {
  case MIM_OPEN:
    /* Device Opened. */
    return;
  case MIM_CLOSE:
    /* Device Closed. */
    return;
  case MIM_DATA:
    status = (uint8)((arg1 >>  0) & 0x000000FF);
    byte1  = (uint8)((arg1 >>  8) & 0x000000FF);
    byte2  = (uint8)((arg1 >> 16) & 0x000000FF);

    /* CC. */
    if ((status & 0xF0) == 0xB0)
      MidiDevice_PushMessage(device, (int)byte1, (int)byte2);
    return;
  }
}

int MidiDevice_GetCount () {
  return midiInGetNumDevs();
}

MidiDevice* MidiDevice_Open (int index) {
  MidiDevice* self = MemNew(MidiDevice);
  self->cursor = 0;
  MMRESULT result;

  /* Open the port. */ {
    result = midiInOpen(
      &self->handle,
      index,
      (DWORD)(void*)MidiDevice_Handler,
      (DWORD)(void*)self,
      CALLBACK_FUNCTION);

    if (result != MMSYSERR_NOERROR)
      Fatal("MidiDevice_Open: failed to open device");
  }

  /* Reset timestamp & begin receiving. */ {
    result = midiInStart(self->handle);
    if (result != MMSYSERR_NOERROR)
      Fatal("MidiDevice_Open: failed to start device");
  }

  return self;
}

void MidiDevice_Close (MidiDevice* self) {
  midiInStop(self->handle);
  midiInClose(self->handle);
  MemFree(self);
}

cstr MidiDevice_GetNameByIndex (int index) {
  static char buffer[256] = { 0 };
  MIDIINCAPS caps;
  MMRESULT result = midiInGetDevCaps(index, &caps, sizeof(MIDIINCAPS));
  if (result != MMSYSERR_NOERROR)
    Fatal("MidiDevice_GetNameByIndex: failed to get device capabilities");
  #ifdef _UNICODE
    WideCharToMultiByte(CP_UTF8, 0, caps.szPname, -1, buffer, sizeof(buffer), 0, 0);
    buffer[sizeof(buffer) - 1] = 0;
    return buffer;
  #else
    return StrDup(caps.szPname);
  #endif
}

#else
  int MidiDevice_GetCount () {
    return 0;
  }

  MidiDevice* MidiDevice_Open (int) {
    return 0;
  }

  void MidiDevice_Close (MidiDevice*) {}

  cstr MidiDevice_GetNameByIndex (int) {
    return 0;
  }
#endif

bool MidiDevice_HasMessage (MidiDevice* self) {
  return self->cursor > 0;
}

Vec2i MidiDevice_PopMessage (MidiDevice* self) {
  if (self->cursor <= 0)
    Fatal("MidiDevice_PopMessage: device has no messages");
  self->cursor -= 1;
  return self->buffer[self->cursor];
}


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

DataFormat const DataFormat_U8    = GL_UNSIGNED_BYTE;
DataFormat const DataFormat_I8    = GL_BYTE;
DataFormat const DataFormat_U16   = GL_UNSIGNED_SHORT;
DataFormat const DataFormat_I16   = GL_SHORT;
DataFormat const DataFormat_U32   = GL_UNSIGNED_INT;
DataFormat const DataFormat_I32   = GL_INT;
DataFormat const DataFormat_Float = GL_FLOAT;

int DataFormat_GetSize (DataFormat self) {
  switch (self) {
    case DataFormat_U8:
    case DataFormat_I8:
      return 1;
    case DataFormat_U16:
    case DataFormat_I16:
      return 2;
    case DataFormat_U32:
    case DataFormat_I32:
    case DataFormat_Float:
      return 4;
  }
  return 0;
}


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

const WindowPos WindowPos_Centered = SDL_WINDOWPOS_CENTERED;
const WindowPos WindowPos_Default  = SDL_WINDOWPOS_UNDEFINED;


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

struct MemStack {
  uint32 size;
  uint32 capacity;
  void* data;
};

MemStack* MemStack_Create (uint32 capacity) {
  MemStack* self = MemNew(MemStack);
  self->size = 0;
  self->capacity = capacity;
  self->data = MemAlloc(capacity);
  return self;
};

void MemStack_Free (MemStack* self) {
  MemFree(self->data);
  MemFree(self);
}

void* MemStack_Alloc (MemStack* self, uint32 size) {
  if (self->size + size > self->capacity)
    Fatal("MemStack_Alloc: Allocation request exceeds remaining capacity");
  void* p = (char*)self->data + self->size;
  self->size += size;
  return p;
}

void MemStack_Clear (MemStack* self) {
  self->size = 0;
}

void MemStack_Dealloc (MemStack* self, uint32 size) {
  if (self->size < size)
    Fatal("MemStack_Dealloc: Attempt to dealloc more memory than is allocated");
  self->size -= size;
}

bool MemStack_CanAlloc (MemStack* self, uint32 size) {
  return self->size + size <= self->capacity;
}

uint32 MemStack_GetSize (MemStack* self) {
  return self->size;
}

uint32 MemStack_GetCapacity (MemStack* self) {
  return self->capacity;
}

uint32 MemStack_GetRemaining (MemStack* self) {
  return self->capacity - self->size;
}


text:
#include "ArrayList.h"
#include "Lua.h"
#include "TimeStamp.h"

#include <stdlib.h>

/* WARNING : Scheduler is currently global, hence the entire scheduling
 *           mechanism may be used from only a single Lua instance! We can
 *           improve this in the future. */

/* NOTE : Should qsort ever become the bottleneck here, we can exploit the
 *        temporal coherence of this queue in numerous ways. Heaps, segregated
 *        unsorted time range interval blocks, modular/radix buckets, etc. */

typedef lua_Integer LuaRef;

struct SchedulerElem {
  LuaRef fn;
  LuaRef arg;
  TimeStamp tCreated;
  TimeStamp tWake;
};

struct Scheduler {
  ArrayList(SchedulerElem, elems);
  ArrayList(SchedulerElem, addQueue);
  TimeStamp now;
  bool locked;
} static self;

/* NOTE : We sort in decreasing order of wake time, such that the element with
 *        the earliest wake time is at the end. This way, we can pop elements
 *        off the stack as we process them in O(1) without breaking order. */
static int SortByWake (void const* pa, void const* pb) {
  SchedulerElem const* a = (SchedulerElem const*)pa;
  SchedulerElem const* b = (SchedulerElem const*)pb;
  return a->tWake <  b->tWake ? 1 :
         a->tWake == b->tWake ? 0 : -1;
}

/* (fn, arg, timeUntilWake) */
static int LuaScheduler_Add (Lua* L) {
  SchedulerElem elem;

  /* Compute timestamps. */ {
    double timeToWake = lua_tonumber(L, lua_gettop(L));
    elem.tCreated = self.now;
    elem.tWake = TimeStamp_GetRelative(self.now, timeToWake);
    lua_pop(L, 1);
  }

  /* Get references to arg and fn. */ {
    elem.arg = Lua_GetRef(L);
    elem.fn = Lua_GetRef(L);
  }

  /* Queue the element. */ {
    if (self.locked) {
      ArrayList_Append(self.addQueue, elem);
    } else {
      ArrayList_Append(self.elems, elem);
    }
  }

  return 0;
}

static int LuaScheduler_Clear (Lua* L) {
  /* Release all references. */ {
    for (int i = 0; i < ArrayList_GetSize(self.elems); ++i) {
      SchedulerElem* elem = ArrayList_GetPtr(self.elems, i);
      luaL_unref(L, LUA_REGISTRYINDEX, elem->fn);
      luaL_unref(L, LUA_REGISTRYINDEX, elem->arg);
    }
  }

  ArrayList_Clear(self.elems);
  return 0;
}

static int LuaScheduler_Update (Lua* L) {
  /* Lock the element queue while we process to preserve ordering. */
  self.locked = true;

  qsort(
    ArrayList_GetData(self.elems),
    ArrayList_GetSize(self.elems),
    sizeof(SchedulerElem),
    SortByWake);

  self.now = TimeStamp_Get();

  /* Push the error handler. */
  lua_getglobal(L, "__error_handler__");
  int handler = lua_gettop(L);

  while (ArrayList_GetSize(self.elems)) {
    SchedulerElem* elem = ArrayList_GetLastPtr(self.elems);
    if (self.now < elem->tWake)
      break;

    double dt = TimeStamp_GetDifference(elem->tCreated, self.now);

    /* Call fn(dt, arg) */ {
      Lua_PushRef(L, elem->fn);
      Lua_PushNumber(L, dt);
      Lua_PushRef(L, elem->arg);
      Lua_Call(L, 2, 0, handler);
    }

    /* Release references on fn and arg. */
    Lua_ReleaseRef(L, elem->fn);
    Lua_ReleaseRef(L, elem->arg);
    ArrayList_Pop(self.elems);
  }

  /* Pop the error handler. */
  lua_pop(L, 1);

  self.locked = false;

  /* Append any elements that were added while locked. */
  while (ArrayList_GetSize(self.addQueue)) {
    SchedulerElem elem = ArrayList_PopRet(self.addQueue);
    ArrayList_Append(self.elems, elem);
  }

  return 0;
}

void LuaScheduler_Init (Lua* L) {
  UNUSED(L);
  /* TODO : Store in Lua state so that the scheduler is per-instance */
  ArrayList_Init(self.elems);
  ArrayList_Init(self.addQueue);
  self.now = TimeStamp_Get();
  self.locked = false;
}

void LuaScheduler_Register (Lua* L) {
  Lua_SetFn(L, "Schedule", LuaScheduler_Add);
  Lua_SetFn(L, "SchedulerClear", LuaScheduler_Clear);
  Lua_SetFn(L, "SchedulerUpdate", LuaScheduler_Update);
}


text:
#include "ArrayList.h"
#include "Matrix.h"
#include "PhxMemory.h"
#include "OpenGL.h"
#include "RefCounted.h"
#include "Shader.h"
#include "ShaderState.h"
#include "Tex1D.h"
#include "Tex2D.h"
#include "Tex3D.h"
#include "TexCube.h"
#include "Vec2.h"
#include "Vec3.h"
#include "Vec4.h"

const uint32 ElemType_Float   =  1;
const uint32 ElemType_Float2  =  2;
const uint32 ElemType_Float3  =  3;
const uint32 ElemType_Float4  =  4;
const uint32 ElemType_Int     =  5;
const uint32 ElemType_Matrix  =  6;
const uint32 ElemType_Tex1D   =  7;
const uint32 ElemType_Tex2D   =  8;
const uint32 ElemType_Tex3D   =  9;
const uint32 ElemType_TexCube = 10;

struct Elem {
  uint32 type;
  int32 index;
  union {
    float    asFloat;
    Vec2f    asFloat2;
    Vec3f    asFloat3;
    Vec4f    asFloat4;
    int      asInt;
    Matrix*  asMatrix;
    Tex1D*   asTex1D;
    Tex2D*   asTex2D;
    Tex3D*   asTex3D;
    TexCube* asTexCube;
  } data;
};

struct ShaderState {
  RefCounted;
  Shader* shader;
  ArrayList(Elem, elems);
};

ShaderState* ShaderState_Create (Shader* shader) {
  ShaderState* self = MemNew(ShaderState);
  RefCounted_Init(self);
  ArrayList_Init(self->elems);
  Shader_Acquire(shader);
  self->shader = shader;
  return self;
}

void ShaderState_Acquire (ShaderState* self) {
  RefCounted_Acquire(self);
}

void ShaderState_Free (ShaderState* self) {
  RefCounted_Free(self) {
    ArrayList_ForEach(self->elems, Elem, e) {
      switch (e->type) {
        case ElemType_Tex1D:
          Tex1D_Free(e->data.asTex1D);
          break;

        case ElemType_Tex2D:
          Tex2D_Free(e->data.asTex2D);
          break;

        case ElemType_Tex3D:
          Tex3D_Free(e->data.asTex3D);
          break;

        case ElemType_TexCube:
          TexCube_Free(e->data.asTexCube);
          break;
      }
    }

    Shader_Free(self->shader);
    ArrayList_Free(self->elems);
    MemFree(self);
  }
}

ShaderState* ShaderState_FromShaderLoad (cstr vertName, cstr fragName) {
  Shader* shader = Shader_Load(vertName, fragName);
  ShaderState* self = ShaderState_Create(shader);
  Shader_Free(shader);
  return self;
}

void ShaderState_SetFloat (ShaderState* self, cstr name, float x) {
  Elem elem = { ElemType_Float, Shader_GetVariable(self->shader, name) };
  elem.data.asFloat = x;
  ArrayList_Append(self->elems, elem);
}

void ShaderState_SetFloat2 (ShaderState* self, cstr name, float x, float y) {
  Elem elem = { ElemType_Float2, Shader_GetVariable(self->shader, name) };
  elem.data.asFloat2 = Vec2f_Create(x, y);
  ArrayList_Append(self->elems, elem);
}

void ShaderState_SetFloat3 (ShaderState* self, cstr name, float x, float y, float z) {
  Elem elem = { ElemType_Float3, Shader_GetVariable(self->shader, name) };
  elem.data.asFloat3 = Vec3f_Create(x, y, z);
  ArrayList_Append(self->elems, elem);
}

void ShaderState_SetFloat4 (ShaderState* self, cstr name, float x, float y, float z, float w) {
  Elem elem = { ElemType_Float4, Shader_GetVariable(self->shader, name) };
  elem.data.asFloat4 = Vec4f_Create(x, y, z, w);
  ArrayList_Append(self->elems, elem);
}

void ShaderState_SetInt (ShaderState* self, cstr name, int x) {
  Elem elem = { ElemType_Int, Shader_GetVariable(self->shader, name) };
  elem.data.asInt = x;
  ArrayList_Append(self->elems, elem);
}

void ShaderState_SetMatrix (ShaderState* self, cstr name, Matrix* x) {
  Elem elem = { ElemType_Matrix, Shader_GetVariable(self->shader, name) };
  elem.data.asMatrix = x;
  ArrayList_Append(self->elems, elem);
}

void ShaderState_SetTex1D (ShaderState* self, cstr name, Tex1D* x) {
  Tex1D_Acquire(x);
  Elem elem = { ElemType_Tex1D, Shader_GetVariable(self->shader, name) };
  elem.data.asTex1D = x;
  ArrayList_Append(self->elems, elem);
}

void ShaderState_SetTex2D (ShaderState* self, cstr name, Tex2D* x) {
  Tex2D_Acquire(x);
  Elem elem = { ElemType_Tex2D, Shader_GetVariable(self->shader, name) };
  elem.data.asTex2D = x;
  ArrayList_Append(self->elems, elem);
}

void ShaderState_SetTex3D (ShaderState* self, cstr name, Tex3D* x) {
  Tex3D_Acquire(x);
  Elem elem = { ElemType_Tex3D, Shader_GetVariable(self->shader, name) };
  elem.data.asTex3D = x;
  ArrayList_Append(self->elems, elem);
}

void ShaderState_SetTexCube (ShaderState* self, cstr name, TexCube* x) {
  TexCube_Acquire(x);
  Elem elem = { ElemType_TexCube, Shader_GetVariable(self->shader, name) };
  elem.data.asTexCube = x;
  ArrayList_Append(self->elems, elem);
}

void ShaderState_Start (ShaderState* self) {
  Shader_Start(self->shader);
  ArrayList_ForEach(self->elems, Elem, e) {
    switch (e->type) {
      case ElemType_Float:
        glUniform1f(e->index, e->data.asFloat);
        break;

      case ElemType_Float2:
        glUniform2f(e->index,
          e->data.asFloat2.x,
          e->data.asFloat2.y);
        break;

      case ElemType_Float3:
        glUniform3f(e->index,
          e->data.asFloat3.x,
          e->data.asFloat3.y,
          e->data.asFloat3.z);
        break;

      case ElemType_Float4:
        glUniform4f(e->index,
          e->data.asFloat4.x,
          e->data.asFloat4.y,
          e->data.asFloat4.z,
          e->data.asFloat4.w);
        break;

      case ElemType_Int:
        glUniform1i(e->index, e->data.asInt);
        break;

      case ElemType_Matrix:
        Shader_ISetMatrix(e->index, e->data.asMatrix);
        break;

      case ElemType_Tex1D:
        Shader_ISetTex1D(e->index, e->data.asTex1D);
        break;

      case ElemType_Tex2D:
        Shader_ISetTex2D(e->index, e->data.asTex2D);
        break;

      case ElemType_Tex3D:
        Shader_ISetTex3D(e->index, e->data.asTex3D);
        break;

      case ElemType_TexCube:
        Shader_ISetTexCube(e->index, e->data.asTexCube);
        break;

      default:
        Fatal("ShaderState_Start: Encountered invalid opcode");
    }
  }
}

void ShaderState_Stop (ShaderState* self) {
  Shader_Stop(self->shader);
}


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

void DepthTest_Pop() {
  RenderState_PopDepthTest();
}

void DepthTest_Push(bool depthTest) {
  RenderState_PushDepthTest(depthTest);
}

void DepthTest_PushDisabled() {
  RenderState_PushDepthTest(false);
}

void DepthTest_PushEnabled() {
  RenderState_PushDepthTest(true);
}


text:
#include "Common.h"
#include "PhxMath.h"

/* 2-valued sign for when product with another term will provide the zero. */
inline static double Sign2 (double x) {
  return x > 0.0 ? 1.0 : -1.0;
}

double Math_Bezier3 (double x, double y1, double y2, double y3) {
  double y12 = Lerp(y1, y2, x);
  double y23 = Lerp(y2, y3, x);
  return Lerp(y12, y23, x);
}

double Math_Bezier4 (double x, double y1, double y2, double y3, double y4) {
  double y12 = Lerp(y1, y2, x);
  double y23 = Lerp(y2, y3, x);
  double y34 = Lerp(y3, y4, x);
  double y123 = Lerp(y12, y23, x);
  double y234 = Lerp(y23, y34, x);
  return Lerp(y123, y234, x);
}

double Math_Clamp (double x, double a, double b) {
  // NOTE : Not stable when b < a. Use Math_ClampSafe
  return x < a ? a : x > b ? b : x;
}

double Math_Clamp01 (double x) {
  return x < 0.0 ? 0.0 : x > 1.0 ? 1.0 : x;
}

double Math_ClampSafe (double x, double a, double b) {
  if (b < a) Swap(a, b);
  return x < a ? a : x > b ? b : x;
}

double Math_ClampUnit (double x) {
  return x < -1.0 ? -1.0 : x > 1.0 ? 1.0 : x;
}

double Math_ExpMap (double x, double p) {
  return 1.0 - Exp(-Pow(Abs(x), p));
}

double Math_ExpMapSigned (double x, double p) {
  return Sign2(x) * (1.0 - Exp(-Pow(Abs(x), p)));
}

double Math_ExpMap1 (double x) {
  return 1.0 - Exp(-Abs(x));
}

double Math_ExpMap1Signed (double x) {
  return Sign2(x) * (1.0 - Exp(-Abs(x)));
}

double Math_ExpMap2 (double x) {
  return 1.0 - Exp(-x * x);
}

double Math_ExpMap2Signed (double x) {
  return Sign2(x) * (1.0 - Exp(-x * x));
}

double Math_PowSigned (double x, double p) {
  return Sign2(x) * Pow(Abs(x), p);
}

double Math_Round (double x) {
  return Round(x);
}

double Math_Sign (double x) {
  return x > 0.0 ? 1.0 : x < 0.0 ? -1.0 : 0.0;
}


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

const WindowMode WindowMode_AlwaysOnTop = SDL_WINDOW_ALWAYS_ON_TOP;
const WindowMode WindowMode_Borderless  = SDL_WINDOW_BORDERLESS;
const WindowMode WindowMode_Fullscreen  = SDL_WINDOW_FULLSCREEN_DESKTOP;
const WindowMode WindowMode_Hidden      = SDL_WINDOW_HIDDEN;
const WindowMode WindowMode_Maximized   = SDL_WINDOW_MAXIMIZED;
const WindowMode WindowMode_Minimized   = SDL_WINDOW_MINIMIZED;
const WindowMode WindowMode_Resizable   = SDL_WINDOW_RESIZABLE;
const WindowMode WindowMode_Shown       = SDL_WINDOW_SHOWN;


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

const TexFormat TexFormat_R8       = GL_R8;
const TexFormat TexFormat_R16      = GL_R16;
const TexFormat TexFormat_R16F     = GL_R16F;
const TexFormat TexFormat_R32F     = GL_R32F;
const TexFormat TexFormat_RG8      = GL_RG8;
const TexFormat TexFormat_RG16     = GL_RG16;
const TexFormat TexFormat_RG16F    = GL_RG16F;
const TexFormat TexFormat_RG32F    = GL_RG32F;
const TexFormat TexFormat_RGB8     = GL_RGB8;
const TexFormat TexFormat_RGBA8    = GL_RGBA8;
const TexFormat TexFormat_RGBA16   = GL_RGBA16;
const TexFormat TexFormat_RGBA16F  = GL_RGBA16F;
const TexFormat TexFormat_RGBA32F  = GL_RGBA32F;
const TexFormat TexFormat_Depth16  = GL_DEPTH_COMPONENT16;
const TexFormat TexFormat_Depth24  = GL_DEPTH_COMPONENT24;
const TexFormat TexFormat_Depth32F = GL_DEPTH_COMPONENT32F;

int TexFormat_Components (TexFormat self) {
  switch (self) {
    case TexFormat_R8:
    case TexFormat_R16:
    case TexFormat_R16F:
    case TexFormat_R32F:
    case TexFormat_Depth16:
    case TexFormat_Depth24:
    case TexFormat_Depth32F:
      return 1;
    case TexFormat_RG8:
    case TexFormat_RG16:
    case TexFormat_RG16F:
    case TexFormat_RG32F:
      return 2;
    case TexFormat_RGB8:
      return 3;
    case TexFormat_RGBA8:
    case TexFormat_RGBA16:
    case TexFormat_RGBA16F:
    case TexFormat_RGBA32F:
      return 4;
  }
  return 0;
}

int TexFormat_GetSize (TexFormat self) {
  switch (self) {
    case TexFormat_R8:
      return 1;
    case TexFormat_R16:
    case TexFormat_R16F:
    case TexFormat_RG8:
    case TexFormat_Depth16:
      return 2;
    case TexFormat_RGB8:
    case TexFormat_Depth24:
      return 3;
    case TexFormat_R32F:
    case TexFormat_RG16:
    case TexFormat_RG16F:
    case TexFormat_RGBA8:
    case TexFormat_Depth32F:
      return 4;
    case TexFormat_RG32F:
    case TexFormat_RGBA16:
    case TexFormat_RGBA16F:
      return 8;
    case TexFormat_RGBA32F:
      return 16;
  }
  return 0;
}

bool TexFormat_IsColor (TexFormat self) {
  switch (self) {
    case TexFormat_Depth16:
    case TexFormat_Depth24:
    case TexFormat_Depth32F:
      return false;
  }
  return true;
}

bool TexFormat_IsDepth (TexFormat self) {
  switch (self) {
    case TexFormat_Depth16:
    case TexFormat_Depth24:
    case TexFormat_Depth32F:
      return true;
  }
  return false;
}

bool TexFormat_IsValid (TexFormat self) {
  switch (self) {
    case TexFormat_R8:
    case TexFormat_R16:
    case TexFormat_R16F:
    case TexFormat_R32F:
    case TexFormat_RG8:
    case TexFormat_RG16:
    case TexFormat_RG16F:
    case TexFormat_RG32F:
    case TexFormat_RGB8:
    case TexFormat_RGBA8:
    case TexFormat_RGBA16:
    case TexFormat_RGBA16F:
    case TexFormat_RGBA32F:
    case TexFormat_Depth16:
    case TexFormat_Depth24:
    case TexFormat_Depth32F:
      return true;
  }
  return false;
}


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

void CullFace_Pop() {
  RenderState_PopCullFace();
}

void CullFace_Push(CullFace cullFace) {
  RenderState_PushCullFace(cullFace);
}

void CullFace_PushNone() {
  RenderState_PushCullFace(CullFace_None);
}

void CullFace_PushBack() {
  RenderState_PushCullFace(CullFace_Back);
}

void CullFace_PushFront() {
  RenderState_PushCullFace(CullFace_Front);
}


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

#include <stdio.h>

#if WINDOWS
  #include <sys/stat.h>
#elif POSIX
  #include <sys/stat.h>
#endif

struct File {
  FILE* handle;
};

bool File_Exists (cstr path) {
  FILE* f = fopen(path, "rb");
  if (f) {
    fclose(f);
    return true;
  }
  return false;
}

bool File_IsDir (cstr path) {
  struct stat s;
  return stat(path, &s) == 0 && (s.st_mode & S_IFDIR);
}

static File* File_OpenMode (cstr path, cstr mode) {
  FILE* handle = fopen(path, mode);
  if (!handle)
    return 0;
  File* self = MemNew(File);
  self->handle = handle;
  return self;
}

File* File_Create (cstr path) {
  return File_OpenMode(path, "wb");
}

File* File_Open (cstr path) {
  return File_OpenMode(path, "ab");
}

void File_Close (File* self) {
  fclose(self->handle);
  MemFree(self);
}

Bytes* File_ReadBytes (cstr path) {
  FILE* file = fopen(path, "rb");
  if (!file)
    return 0;

  fseek(file, 0, SEEK_END);
  int64 size = (int64)ftell(file);
  if (size == 0) return 0;
  if (size < 0)
    Fatal("File_Read: failed to get size of file '%s'", path);
  rewind(file);

  if (size > UINT32_MAX)
    Fatal("File_Read: filesize of '%s' exceeds 32-bit capacity limit", path);

  Bytes* buffer = Bytes_Create((uint32)size);
  size_t result = fread(Bytes_GetData(buffer), (size_t)size, 1, file);
  if (result != 1)
    Fatal("File_Read: failed to read correct number of bytes from '%s'", path);
  fclose(file);
  return buffer;
}

cstr File_ReadCstr (cstr path) {
  FILE* file = fopen(path, "rb");
  if (!file)
    return 0;

  fseek(file, 0, SEEK_END);
  int64 size = (int64)ftell(file);
  if (size == 0) return 0;
  if (size < 0)
    Fatal("File_ReadAscii: failed to get size of file '%s'", path);
  rewind(file);

  char* buffer = MemNewArray(char, (size_t)size + 1);
  size_t result = fread(buffer, (size_t)size, 1, file);
  if (result != 1)
    Fatal("File_Read: failed to read correct number of bytes from '%s'", path);
  fclose(file);
  buffer[size] = 0;
  return buffer;
}

int64 File_Size (cstr path) {
  FILE* file = fopen(path, "rb");
  if (!file)
    return 0;

  fseek(file, 0, SEEK_END);
  int64 size = (int64)ftell(file);
  fclose(file);
  return size;
}

void File_Read (File* self, void* data, uint32 len) {
  fread(data, len, 1, self->handle);
}

void File_Write (File* self, void const* data, uint32 len) {
  fwrite(data, len, 1, self->handle);
}

void File_WriteStr (File* self, cstr data) {
  fwrite((void const*)data, StrLen(data), 1, self->handle);
}

#define READWRITE_X                                                            \
  X(uint8,   U8)                                                               \
  X(uint16, U16)                                                               \
  X(uint32, U32)                                                               \
  X(uint64, U64)                                                               \
  X(int8,    I8)                                                               \
  X(int16,  I16)                                                               \
  X(int32,  I32)                                                               \
  X(int64,  I64)                                                               \
  X(float,  F32)                                                               \
  X(double, F64)

#define X(T, N)                                                                \
  T File_Read##N(File* self) {                                                 \
    T value;                                                                   \
    fread((void*)&value, sizeof(T), 1, self->handle);                          \
    return value;                                                              \
  }                                                                            \
                                                                               \
  void File_Write##N(File* self, T value) {                                    \
    fwrite((void const*)&value, sizeof(T), 1, self->handle);                   \
  }

READWRITE_X
#undef X


text:
#include "Matrix.h"
#include "PhxMemory.h"
#include "ShaderVar.h"
#include "ShaderVarType.h"
#include "StrMap.h"
#include "Vec2.h"
#include "Vec3.h"
#include "Vec4.h"

#define DEFAULT_CAPACITY 4

struct VarStack {
  ShaderVarType type;
  int32 size;
  int32 capacity;
  int32 elemSize;
  void* data;
};

static StrMap* varMap = 0;

inline static VarStack* ShaderVar_GetStack (cstr var, ShaderVarType type) {
  VarStack* self = (VarStack*)StrMap_Get(varMap, var);
  if (!self) {
    if (!type)
      return 0;
    self = MemNew(VarStack);
    self->type = type;
    self->size = 0;
    self->capacity = DEFAULT_CAPACITY;
    self->elemSize = ShaderVarType_GetSize(type);
    self->data = MemAlloc(self->capacity * self->elemSize);
    StrMap_Set(varMap, var, self);
  }

  if (type && self->type != type)
    Fatal("ShaderVar_GetStack: Attempting to get stack of type <%s>"
          " for shader variable <%s> when existing stack has type <%s>",
          ShaderVarType_GetName(type), var, ShaderVarType_GetName(self->type));

  return self;
}

inline static void ShaderVar_Push (cstr var, ShaderVarType type, void const* value) {
  VarStack* self = ShaderVar_GetStack(var, type);
  if (self->size == self->capacity) {
    self->capacity *= 2;
    self->data = MemRealloc(self->data, self->capacity * self->elemSize);
  }
  MemCpy((char*)self->data + self->size * self->elemSize, value, self->elemSize);
  self->size++;
}

void ShaderVar_Init () {
  varMap = StrMap_Create(16);
}

void ShaderVar_Free () {
  StrMap_Free(varMap);
  varMap = 0;
}

void* ShaderVar_Get (cstr name, ShaderVarType type) {
  VarStack* self = ShaderVar_GetStack(name, 0);
  if (!self || self->size == 0)
    return 0;
  if (type && self->type != type)
    Fatal("ShaderVar_Get: Attempting to get variable <%s> with type <%s> when"
          " existing stack has type <%s>",
          name, ShaderVarType_GetName(type), ShaderVarType_GetName(self->type));
  return (char*)self->data + self->elemSize * (self->size - 1);
}

void ShaderVar_PushFloat (cstr name, float x) {
  ShaderVar_Push(name, ShaderVarType_Float, &x);
}

void ShaderVar_PushFloat2 (cstr name, float x, float y) {
  Vec2f value = { x, y };
  ShaderVar_Push(name, ShaderVarType_Float2, &value);
}

void ShaderVar_PushFloat3 (cstr name, float x, float y, float z) {
  Vec3f value = { x, y, z };
  ShaderVar_Push(name, ShaderVarType_Float3, &value);
}

void ShaderVar_PushFloat4 (cstr name, float x, float y, float z, float w) {
  Vec4f value = { x, y, z, w };
  ShaderVar_Push(name, ShaderVarType_Float4, &value);
}

void ShaderVar_PushInt (cstr name, int x) {
  int32 value = (int32)x;
  ShaderVar_Push(name, ShaderVarType_Int, &value);
}

void ShaderVar_PushMatrix (cstr name, Matrix* x) {
  ShaderVar_Push(name, ShaderVarType_Matrix, &x);
}

void ShaderVar_PushTex1D (cstr name, Tex1D* x) {
  ShaderVar_Push(name, ShaderVarType_Tex1D, &x);
}

void ShaderVar_PushTex2D (cstr name, Tex2D* x) {
  ShaderVar_Push(name, ShaderVarType_Tex2D, &x);
}

void ShaderVar_PushTex3D (cstr name, Tex3D* x) {
  ShaderVar_Push(name, ShaderVarType_Tex3D, &x);
}

void ShaderVar_PushTexCube (cstr name, TexCube* x) {
  ShaderVar_Push(name, ShaderVarType_TexCube, &x);
}

void ShaderVar_Pop (cstr name) {
  VarStack* self = ShaderVar_GetStack(name, 0);
  if (!self)
    Fatal("ShaderVar_Pop: Attempting to pop nonexistent stack <%s>", name);
  if (self->size == 0)
    Fatal("ShaderVar_Pop: Attempting to pop empty stack <%s>", name);
  self->size--;
}


text:
#include "Draw.h"
#include "KDTree.h"
#include "PhxMemory.h"
#include "Mesh.h"
#include "Vertex.h"
#include <stdlib.h>

int const kMaxLeafSize = 64;

struct Node {
  Node* next;
  uint64 id;
  Box3f box;
};

struct KDTree {
  Box3f box;
  KDTree* back;
  KDTree* front;
  Node* elems;
};

static int compareLowerX (void const* a, void const* b) {
  return (((Box3f const*)a)->lower.x < ((Box3f const*)b)->lower.x) ? -1 : 1;
}

static int compareLowerY (void const* a, void const* b) {
  return (((Box3f const*)a)->lower.y < ((Box3f const*)b)->lower.y) ? -1 : 1;
}

static int compareLowerZ (void const* a, void const* b) {
  return (((Box3f const*)a)->lower.z < ((Box3f const*)b)->lower.z) ? -1 : 1;
}

static KDTree* Partition (Box3f* boxes, int boxCount, int dim) {
  KDTree* self = MemNew(KDTree);
  if (boxCount <= kMaxLeafSize) {
    self->box = boxes[0];
    self->back = 0;
    self->front = 0;
    self->elems = 0;
    for (int i = 1; i < boxCount; ++i)
      self->box = Box3f_Union(self->box, boxes[i]);

    for (int i = 0; i < boxCount; ++i) {
      Node* node = MemNew(Node);
      node->box = boxes[i];
      node->next = self->elems;
      node->id = 0;
      self->elems = node;
    }
    return self;
  }

  if (dim == 0) qsort(boxes, boxCount, sizeof(Box3f), compareLowerX);
  if (dim == 1) qsort(boxes, boxCount, sizeof(Box3f), compareLowerY);
  if (dim == 2) qsort(boxes, boxCount, sizeof(Box3f), compareLowerZ);

  int boxCountBack = boxCount / 2;
  int boxCountFront = boxCount - boxCountBack;
  Box3f* boxesBack = MemNewArray(Box3f, boxCountBack);
  Box3f* boxesFront = MemNewArray(Box3f, boxCountFront);
  MemCpy(boxesBack, boxes, boxCountBack * sizeof(Box3f));
  MemCpy(boxesFront, boxes + boxCountBack, boxCountFront * sizeof(Box3f));

  self->back = Partition(boxesBack, boxCountBack, (dim + 1) % 3);
  self->front = Partition(boxesFront, boxCountFront, (dim + 1) % 3);
  self->box = Box3f_Union(self->back->box, self->front->box);
  self->elems = 0;

  MemFree(boxesBack);
  MemFree(boxesFront);
  return self;
}

KDTree* KDTree_FromMesh (Mesh* mesh) {
  int const indexCount = Mesh_GetIndexCount(mesh);
  int const* indexData = Mesh_GetIndexData(mesh);
  Vertex const* vertexData = Mesh_GetVertexData(mesh);

  int const boxCount = indexCount / 3;
  Box3f* boxes = MemNewArray(Box3f, boxCount);

  for (int i = 0; i < indexCount; i += 3) {
    Vertex const* v0 = vertexData + indexData[i + 0];
    Vertex const* v1 = vertexData + indexData[i + 1];
    Vertex const* v2 = vertexData + indexData[i + 2];
    boxes[i / 3] = Box3f_Create(
      Vec3f_Min(v0->p, Vec3f_Min(v1->p, v2->p)),
      Vec3f_Max(v0->p, Vec3f_Max(v1->p, v2->p)));
  }

  KDTree* self = Partition(boxes, boxCount, 0);
  MemFree(boxes);
  return self;
}

void KDTree_Free (KDTree* self) {
  if (self->back) KDTree_Free(self->back);
  if (self->front) KDTree_Free(self->front);
  Node* elem = self->elems;
  while (elem) {
    Node* next = elem->next;
    MemFree(elem);
    elem = next;
  }
  MemFree(self);
}

int KDTree_GetMemory (KDTree* self) {
  int memory = sizeof(KDTree);
  if (self->back) memory += KDTree_GetMemory(self->back);
  if (self->front) memory += KDTree_GetMemory(self->front);
  for (Node* elem = self->elems; elem; elem = elem->next)
    memory += sizeof(Node);
  return memory;
}

bool KDTree_IntersectRay (KDTree*, Matrix*, Vec3f const*, Vec3f const*) {
  return false;
}

void KDTree_Draw (KDTree* self, int maxDepth) {
  if (maxDepth < 0) return;
  Draw_Color(1, 1, 1, 1);
  Draw_Box3(&self->box);
  if (self->back) KDTree_Draw(self->back, maxDepth - 1);
  if (self->front) KDTree_Draw(self->front, maxDepth - 1);
#if 0
  Draw_Color(0, 1, 0, 1);
  for (Node* elem = self->elems; elem; elem = elem->next)
    Draw_Box3(&elem->box);
#endif
}