text:
#include "ArrayList.h"
#include "BlendMode.h"
#include "Button.h"
#include "ClipRect.h"
#include "Draw.h"
#include "Font.h"
#include "Hash.h"
#include "HashMap.h"
#include "ImGui.h"
#include "Input.h"
#include "MemPool.h"
#include "PhxString.h"
#include "RenderState.h"
#include "Shader.h"
#include "Tex2D.h"
#include "Vec2.h"
#include "Vec4.h"
#include "Viewport.h"

#include <stdio.h>

#include "ImGuiInternal.h"

static bool init = false;

static void ImGui_Init () {
  if (init) return;
  init = true;
  self.layer = 0;
  self.layerLast = 0;
  self.style = 0;
  self.clipRect = 0;
  self.cursorStack = 0;
  self.dragging = 0;

  self.data = HashMap_Create(0, 128);
  self.layoutPool = MemPool_CreateAuto(sizeof(ImGuiLayout));
  self.widgetPool = MemPool_CreateAuto(sizeof(ImGuiWidget));
  self.stylePool = MemPool_CreateAuto(sizeof(ImGuiStyle));
  self.clipRectPool = MemPool_CreateAuto(sizeof(ImGuiClipRect));
  self.cursorPool = MemPool_CreateAuto(sizeof(ImGuiCursor));
  self.tex2DPool = MemPool_CreateAuto(sizeof(ImGuiTex2D));
  self.panelPool = MemPool_CreateAuto(sizeof(ImGuiPanel));
  self.rectPool = MemPool_CreateAuto(sizeof(ImGuiRect));
  self.textPool = MemPool_CreateAuto(sizeof(ImGuiText));
  self.linePool = MemPool_CreateAuto(sizeof(ImGuiLine));
}

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

void ImGui_Begin (float sx, float sy) {
  ImGui_Init();

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

  if (!Input_GetDown(Button_Mouse_Left))
    self.dragging = 0;
  if (self.dragging)
    self.focus[FocusType_Mouse] = self.dragging;

  self.cursor = Vec2f_Create(0, 0);

  if (self.layerLast) {
    ImGuiLayer_Free(self.layerLast);
    self.layerLast = 0;
  }

  MemPool_Clear(self.layoutPool);
  MemPool_Clear(self.widgetPool);
  MemPool_Clear(self.stylePool);
  MemPool_Clear(self.clipRectPool);
  MemPool_Clear(self.cursorPool);
  MemPool_Clear(self.tex2DPool);
  MemPool_Clear(self.panelPool);
  MemPool_Clear(self.rectPool);
  MemPool_Clear(self.textPool);
  MemPool_Clear(self.linePool);

  self.style = 0;
  ImGui_PushDefaultStyle();
  self.layout = 0;
  ImGui_PushLayout(sx, sy, false);

  self.widget = 0;
  self.widgetLast = 0;
  ImGui_BeginWidget(sx, sy);

  self.layer = 0;
  ImGui_PushLayer(true);

  Vec2i mouse;
  Input_GetMousePosition(&mouse);
  self.mouse.x = (float)mouse.x;
  self.mouse.y = (float)mouse.y;

  self.activate = Input_GetPressed(Button_Mouse_Left);
  self.forceSize = Vec2f_Create(0, 0);
}

void ImGui_End () {
  ImGui_PopLayer();
  ImGui_EndWidget();
  ImGui_PopLayout();

  if (self.layer != 0) Fatal("ImGui_End: layer stack not empty");
  if (self.widget != 0) Fatal("ImGui_End: widget stack not empty");
  if (self.layout != 0) Fatal("ImGui_End: layout stack not empty");
}

void ImGui_Draw () {
  RenderState_PushBlendMode(BlendMode_Alpha);
  Draw_LineWidth(1);
  ImGui_DrawLayer(self.layerLast);
  RenderState_PopBlendMode();
}

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

void ImGui_AlignCursor (float sx, float sy, float alignX, float alignY) {
  TransformSize(sx, sy);
  ImGui_SetCursor(
    self.layout->lower.x + alignX * (self.layout->size.x - sx),
    self.layout->lower.y + alignY * (self.layout->size.y - sy));
}

float ImGui_GetCursorX () {
  return self.cursor.x;
}

float ImGui_GetCursorY () {
  return self.cursor.y;
}

void ImGui_PushCursor () {
  ImGuiCursor* cursor = (ImGuiCursor*)MemPool_Alloc(self.cursorPool);
  cursor->prev = self.cursorStack;
  cursor->pos = self.cursor;
  self.cursorStack = cursor;
}

void ImGui_PopCursor () {
  ImGuiCursor* cursor = self.cursorStack;
  self.cursor = cursor->pos;
  self.cursorStack = cursor->prev;
  MemPool_Dealloc(self.cursorPool, cursor);
}

void ImGui_SetCursor (float cx, float cy) {
  TransformPos(cx, cy);
  self.cursor = Vec2f_Create(cx, cy);
  self.layout->spacing = Vec2f_Create(0, 0);
}

void ImGui_SetCursorX (float x) {
  ImGui_SetCursor(x, self.cursor.y);
}

void ImGui_SetCursorY (float y) {
  ImGui_SetCursor(self.cursor.x, y);
}

void ImGui_Indent () {
  self.cursor.x += 2.0f * self.style->padding.x;
}

void ImGui_Undent () {
  self.cursor.x -= 2.0f * self.style->padding.x;
}

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

void ImGui_BeginGroup (float sx, float sy, bool horizontal) {
  ImGui_BeginWidget(sx, sy);
  ImGui_PushLayout(sx, sy, horizontal);
}

void ImGui_BeginGroupX (float sy) {
  ImGui_BeginWidget(0, sy);
  ImGui_PushLayout(0, sy, true);
}

void ImGui_BeginGroupY (float sx) {
  ImGui_BeginWidget(sx, 0);
  ImGui_PushLayout(sx, 0, false);
}

void ImGui_EndGroup () {
  ImGui_PopLayout();
  ImGui_EndWidget();
}

void ImGui_BeginPanel (float sx, float sy) {
  ImGui_BeginGroup(sx, sy, false);
  ImGui_PushLayer(false);
  ImGui_PushLayer(true);
  ImGui_Pad();
}

void ImGui_EndPanel () {
  ImGui_Unpad();
  ImGui_PopLayer();
  EmitPanel(self.style->frameColor, self.widget->pos, self.widget->size, 1.0f, 12.0f);
  ImGui_PopLayer();
  ImGui_EndGroup();
}

void ImGui_BeginWindow (cstr title, float sx, float sy) {
  uint64 hash = HashPeekNext();
  ImGuiData* data = GetData(hash);
  self.cursor.x += data->offset.x;
  self.cursor.y += data->offset.y;
  ImGui_BeginPanel(sx, sy);
  // ImGui_TextEx(self.style->fontSubheading, title, 0.4f, 0.4f, 0.4f, 1.0f);
}

void ImGui_EndWindow () {
  ImGui_EndPanel();
  ImGuiData* data = GetData(self.widgetLast->hash);
  self.cursor.x -= data->offset.x;
  self.cursor.y -= data->offset.y;

  if (ImGui_FocusLast(FocusType_Mouse)) {
    if (Input_GetDown(Button_Mouse_Left)) {
      Vec2i delta; Input_GetMouseDelta(&delta);
      data->offset.x += delta.x;
      data->offset.y += delta.y;
      self.dragging = self.widgetLast->hash;
    }
  }
}

void ImGui_BeginScrollFrame (float sx, float sy) {
  ImGui_BeginGroup(sx, sy, false);
  ImGui_PushLayer(true);
  ImGui_Pad();

  ImGuiData* data = GetData(self.widget->hash);
  self.cursor.y -= data->scroll;
}

void ImGui_EndScrollFrame () {
  ImGuiData* data = GetData(self.widget->hash);
  self.cursor.y += data->scroll;

  ImGuiLayout* layout = self.layout;
  ImGui_PopLayer();

  float scroll = data->scroll;
  float virtualSize = self.cursor.y - layout->lower.y;
  float scrollMax = virtualSize - layout->size.y;
  Vec2f scrollPos = Vec2f_Create(layout->lower.x + layout->size.x, layout->lower.y);
  Vec2f scrollSize = Vec2f_Create(self.style->scrollBarSize.x, layout->size.y);

  uint64 handleHash = HashNext();

  if (layout->size.y < virtualSize) {
    float handleSizeY = layout->size.y * (layout->size.y / virtualSize);
    handleSizeY = Clamp(handleSizeY, 16.0f, 128.0f);
    float handleOffset = (layout->size.y - handleSizeY) * (scroll / scrollMax);

    Vec2f handlePos = Vec2f_Create(scrollPos.x, scrollPos.y + handleOffset);
    Vec2f handleSize = Vec2f_Create(self.style->scrollBarSize.x, handleSizeY);
    bool handleFocus = TryFocusRect(handleHash, FocusType_Mouse, handlePos, handleSize);

    EmitPanel(
      handleFocus ? self.style->buttonColorFocus : Vec4f_Create(0.3f, 0.4f, 0.5f, 1.0f),
      handlePos,
      handleSize,
      handleFocus ? 0.5f : 0.25f,
      4.0f);
  }

  ImGui_Unpad();
  ImGui_EndGroup();
  EmitPanel(
    Vec4f_Create(0.0f, 0.0f, 0.0f, 0.5f),
    self.widgetLast->pos,
    self.widgetLast->size,
    0.25f);

  if (ImGui_FocusLast(FocusType_Scroll)) {
    Vec2i scroll; Input_GetMouseScroll(&scroll);
    data->scroll -= 10.0f * scroll.y;
  }

  data->scroll = Clamp(data->scroll, 0.0f, scrollMax);
}

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

void ImGui_SetNextWidth (float sx) {
  self.forceSize.x = sx;
}

void ImGui_SetNextHeight (float sy) {
  self.forceSize.y = sy;
}

void ImGui_PushStyle () {
  ImGuiStyle* style = (ImGuiStyle*)MemPool_Alloc(self.stylePool);
  MemCpy(style, self.style, sizeof(ImGuiStyle));
  style->prev = self.style;
  self.style = style;
}

void ImGui_PushStyleFont (Font* font) {
  ImGui_PushStyle();
  self.style->font = font;
}

void ImGui_PushStylePadding (float px, float py) {
  ImGui_PushStyle();
  self.style->padding = Vec2f_Create(px, py);
}

void ImGui_PushStyleSpacing (float x, float y) {
  ImGui_PushStyle();
  self.style->spacing = Vec2f_Create(x, y);
}

void ImGui_PushStyleTextColor (float r, float g, float b, float a) {
  ImGui_PushStyle();
  self.style->textColor = Vec4f_Create(r, g, b, a);
}

void ImGui_PopStyle () {
  if (!self.style->prev)
    Fatal("ImGui_PopStyle: Attempting to pop an empty stack");
  ImGuiStyle* style = self.style;
  self.style = style->prev;
  MemPool_Dealloc(self.stylePool, style);
}

void ImGui_SetFont (Font* font) {
  ImGui_PushStyleFont(font);
  self.layout->styleVars++;
}

void ImGui_SetSpacing (float sx, float sy) {
  ImGui_PushStyleSpacing(sx, sy);
  self.layout->styleVars++;
}

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

bool ImGui_Button (cstr label) {
  return ImGui_ButtonEx(label, 0, 32);
}

bool ImGui_ButtonEx (cstr label, float sx, float sy) {
  ImGui_BeginWidget(sx, sy);
  bool focus = ImGui_FocusCurrent(FocusType_Mouse);
  Vec4f color = focus ? self.style->buttonColorFocus : self.style->buttonColor;
  EmitPanel(color, self.widget->pos, self.widget->size, focus ? 1.0f : 0.5f);

  Vec2i bound;
  Font_GetSize2(self.style->font, &bound, label);
  Vec2f labelPos = Vec2f_Create(
    self.widget->pos.x + 0.5f * (self.widget->size.x - bound.x),
    self.widget->pos.y + 0.5f * (self.widget->size.y - bound.y));
  Vec2f labelSize = Vec2f_Create(bound.x, bound.y);

  EmitText(
    self.style->font,
    focus ? self.style->textColorFocus : self.style->textColor,
    Vec2f_Create(labelPos.x, labelPos.y + bound.y),
    label);

  ImGui_EndWidget();
  return focus && self.activate;
}

bool ImGui_Checkbox (bool value) {
  ImGui_BeginWidget(16.0f, 16.0f);
  bool focus = ImGui_FocusCurrent(FocusType_Mouse);
  if (focus && self.activate)
    value = !value;
  if (focus)
    EmitRect(
      self.style->buttonColorFocus,
      self.widget->pos,
      self.widget->size,
      true);

  EmitPanel(
    value ? self.style->buttonColorFocus : self.style->buttonColor,
    Vec2f_Create(self.widget->pos.x + 2, self.widget->pos.y + 2),
    Vec2f_Create(self.widget->size.x - 4, self.widget->size.y - 4));

  ImGui_EndWidget();
  return value;
}

void ImGui_Divider () {
  ImGui_BeginWidget(
    self.layout->horizontal ? 2 : 0,
    self.layout->horizontal ? 0 : 2);
  EmitLine(
    self.style->buttonColorFocus,
    self.widget->pos,
    Vec2f_Create(
      self.widget->pos.x + (self.layout->horizontal ? 0 : self.widget->size.x),
      self.widget->pos.y + (self.layout->horizontal ? self.widget->size.y : 0)));
  ImGui_EndWidget();
}

bool ImGui_Selectable (cstr label) {
  Vec2i bound;
  Font_GetSize2(self.style->font, &bound, label);
  ImGui_BeginWidget(
    self.layout->horizontal ? bound.x + 4.0f : 0,
    self.layout->horizontal ? 0 : 4.0f + Font_GetLineHeight(self.style->font));

  bool focus = ImGui_FocusCurrent(FocusType_Mouse);
  if (focus)
    EmitRect(
      self.style->buttonColorFocus,
      self.widget->pos,
      self.widget->size);

  EmitText(
    self.style->font,
    focus ? self.style->textColorFocus : self.style->textColor,
    Vec2f_Create(
      self.widget->pos.x + 2.0f,
      self.widget->pos.y + bound.y + 0.5f * (self.widget->size.y - bound.y)),
    label);

  ImGui_EndWidget();
  return focus && self.activate;
}

void ImGui_Tex2D (Tex2D* tex) {
  Vec2i size; Tex2D_GetSize(tex, &size);
  Vec2f sizef = Vec2f_Create(size.x, size.y);
  ImGui_BeginWidget(size.x, size.y);
  EmitTex2D(tex, self.cursor, sizef);
  ImGui_EndWidget();
}

void ImGui_Text (cstr text) {
  ImGui_TextEx(self.style->font, text, UNPACK4(self.style->textColor));
}

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

void ImGui_TextEx (Font* font, cstr text, float r, float g, float b, float a) {
  Vec2i bound;
  Font_GetSize2(self.style->font, &bound, text);
  ImGui_BeginWidget(bound.x, self.layout->horizontal ? 0 : Font_GetLineHeight(self.style->font));
  EmitText(
    font,
    Vec4f_Create(r, g, b, a),
    Vec2f_Create(
      self.widget->pos.x,
      self.widget->pos.y + bound.y + 0.5f * (self.widget->size.y - bound.y)),
    text);
  ImGui_EndWidget();
}


text:
#include "LodMesh.h"
#include "PhxMemory.h"
#include "Mesh.h"
#include "RefCounted.h"

/* TODO : Merge meshes into single IBO/VBO so that we can skip all the rebinds
 *        (profiling shows that they are a huge perf drain in the rendering
 *         pipeline) */

struct LodMeshEntry {
  LodMeshEntry* next;
  Mesh* mesh;
  float dMin;
  float dMax;
};

struct LodMesh {
  RefCounted;
  LodMeshEntry* head;
};

LodMesh* LodMesh_Create () {
  LodMesh* self = MemNew(LodMesh);
  RefCounted_Init(self);
  self->head = 0;
  return self;
}

void LodMesh_Acquire (LodMesh* self) {
  RefCounted_Acquire(self);
}

void LodMesh_Free (LodMesh* self) {
  RefCounted_Free(self) {
    LodMeshEntry* e = self->head;
    while (e) {
      LodMeshEntry* next = e->next;
      Mesh_Free(e->mesh);
      MemFree(e);
      e = next;
    }
    MemFree(self);
  }
}

void LodMesh_Add (LodMesh* self, Mesh* mesh, float dMin, float dMax) {
  LodMeshEntry* e = MemNew(LodMeshEntry);
  e->mesh = mesh;
  e->dMin = dMin * dMin;
  e->dMax = dMax * dMax;
  e->next = self->head;
  self->head = e;
}

void LodMesh_Draw (LodMesh* self, float d2) {
  for (LodMeshEntry* e = self->head; e; e = e->next)
    if (e->dMin <= d2 && d2 <= e->dMax)
      Mesh_Draw(e->mesh);
}

Mesh* LodMesh_Get (LodMesh* self, float d2) {
  for (LodMeshEntry* e = self->head; e; e = e->next)
    if (e->dMin <= d2 && d2 <= e->dMax)
      return e->mesh;
  return 0;
}

repo_name: JoshParnell/libphx

repo_language: C

mime_type: text/x-c++


text:
#include "Bytes.h"
#include "DataFormat.h"
#include "PhxMemory.h"
#include "OpenGL.h"
#include "PixelFormat.h"
#include "RefCounted.h"
#include "RenderTarget.h"
#include "Tex3D.h"
#include "TexFormat.h"
#include "Vec3.h"

struct Tex3D {
  RefCounted;
  uint handle;
  Vec3i size;
  TexFormat format;
};

inline static void Tex3D_Init () {
  GLCALL(glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_NEAREST))
  GLCALL(glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_NEAREST))
  GLCALL(glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE))
  GLCALL(glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE))
  GLCALL(glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE))
}

Tex3D* Tex3D_Create (int sx, int sy, int sz, TexFormat format) {
  if (!TexFormat_IsValid(format))
    Fatal("Tex3D_Create: Invalid texture format requested");
  if (TexFormat_IsDepth(format))
    Fatal("Tex3D_Create: Cannot create 3D texture with depth format");

  Tex3D* self = MemNew(Tex3D);
  RefCounted_Init(self);
  self->size = Vec3i_Create(sx, sy, sz);
  self->format = format;

  GLCALL(glGenTextures(1, &self->handle))
  GLCALL(glActiveTexture(GL_TEXTURE0))
  GLCALL(glBindTexture(GL_TEXTURE_3D, self->handle))
  GLCALL(glTexImage3D(GL_TEXTURE_3D, 0,
    self->format,
    self->size.x,
    self->size.y,
    self->size.z,
    0, GL_RED, GL_UNSIGNED_BYTE, 0))
  Tex3D_Init();
  GLCALL(glBindTexture(GL_TEXTURE_3D, 0))
  return self;
}

void Tex3D_Acquire (Tex3D* self) {
  RefCounted_Acquire(self);
}

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

void Tex3D_Pop(Tex3D*) {
  RenderTarget_Pop();
}

void Tex3D_Push(Tex3D* self, int layer) {
  RenderTarget_PushTex3D(self, layer);
}

void Tex3D_PushLevel(Tex3D* self, int layer, int level) {
  RenderTarget_PushTex3DLevel(self, layer, level);
}

void Tex3D_Draw (Tex3D* self, int layer, float x, float y, float xs, float ys) {
  float r = (float)(layer + 1) / (float)(self->size.z + 1);
  GLCALL(glEnable(GL_TEXTURE_3D))
  GLCALL(glBindTexture(GL_TEXTURE_3D, self->handle))
  glBegin(GL_QUADS);
  glTexCoord3f(0, 0, r); glVertex2f(x, y);
  glTexCoord3f(0, 1, r); glVertex2f(x, y + ys);
  glTexCoord3f(1, 1, r); glVertex2f(x + xs, y + ys);
  glTexCoord3f(1, 0, r); glVertex2f(x + xs, y);
  GLCALL(glEnd())
  GLCALL(glDisable(GL_TEXTURE_3D))
}

void Tex3D_GenMipmap (Tex3D* self) {
  GLCALL(glBindTexture(GL_TEXTURE_3D, self->handle))
  GLCALL(glGenerateMipmap(GL_TEXTURE_3D))
  GLCALL(glBindTexture(GL_TEXTURE_3D, 0))
}

void Tex3D_GetData (Tex3D* self, void* data, PixelFormat pf, DataFormat df) {
  GLCALL(glBindTexture(GL_TEXTURE_3D, self->handle))
  GLCALL(glGetTexImage(GL_TEXTURE_3D, 0, pf, df, data))
  GLCALL(glBindTexture(GL_TEXTURE_3D, 0))
}

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

TexFormat Tex3D_GetFormat (Tex3D* self) {
  return self->format;
}

uint Tex3D_GetHandle (Tex3D* self) {
  return self->handle;
}

void Tex3D_GetSize (Tex3D* self, Vec3i* out) {
  *out = self->size;
}

void Tex3D_GetSizeLevel (Tex3D* self, Vec3i* out, int level) {
  *out = self->size;
  for (int i = 0; i < level; ++i) {
    out->x /= 2;
    out->y /= 2;
    out->z /= 2;
  }
}

void Tex3D_SetData (Tex3D* self, void const* data, PixelFormat pf, DataFormat df) {
  GLCALL(glBindTexture(GL_TEXTURE_3D, self->handle))
  GLCALL(glTexImage3D(GL_TEXTURE_3D,
    0, self->format,
    self->size.x, self->size.y, self->size.z,
    0, pf, df, data))
  GLCALL(glBindTexture(GL_TEXTURE_3D, 0))
}

void Tex3D_SetDataBytes (Tex3D* self, Bytes* data, PixelFormat pf, DataFormat df) {
  Tex3D_SetData(self, Bytes_GetData(data), pf, df);
}

void Tex3D_SetMagFilter (Tex3D* self, TexFilter filter) {
  GLCALL(glBindTexture(GL_TEXTURE_3D, self->handle))
  GLCALL(glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, filter))
  GLCALL(glBindTexture(GL_TEXTURE_3D, 0))
}

void Tex3D_SetMinFilter (Tex3D* self, TexFilter filter) {
  GLCALL(glBindTexture(GL_TEXTURE_3D, self->handle))
  GLCALL(glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, filter))
  GLCALL(glBindTexture(GL_TEXTURE_3D, 0))
}

void Tex3D_SetWrapMode (Tex3D* self, TexWrapMode mode) {
  GLCALL(glBindTexture(GL_TEXTURE_3D, self->handle))
  GLCALL(glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, mode))
  GLCALL(glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, mode))
  GLCALL(glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, mode))
  GLCALL(glBindTexture(GL_TEXTURE_3D, 0))
}


text:
#include "BlendMode.h"
#include "Bytes.h"
#include "ClipRect.h"
#include "CubeFace.h"
#include "CullFace.h"
#include "DataFormat.h"
#include "Draw.h"
#include "File.h"
#include "GLMatrix.h"
#include "PhxMemory.h"
#include "OpenGL.h"
#include "PixelFormat.h"
#include "RefCounted.h"
#include "RenderState.h"
#include "RenderTarget.h"
#include "Shader.h"
#include "ShaderState.h"
#include "TimeStamp.h"
#include "PhxString.h"
#include "TexCube.h"
#include "TexFormat.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 Face {
  CubeFace face;
  Vec3f look;
  Vec3f up;
};

static Face const kFaces[] = {
  { CubeFace_PX, { 1,  0,  0}, {0, 1,  0} },
  { CubeFace_NX, {-1,  0,  0}, {0, 1,  0} },
  { CubeFace_PY, { 0,  1,  0}, {0, 0, -1} },
  { CubeFace_NY, { 0, -1,  0}, {0, 0,  1} },
  { CubeFace_PZ, { 0,  0,  1}, {0, 1,  0} },
  { CubeFace_NZ, { 0,  0, -1}, {0, 1,  0} },
};

static cstr const kFaceExt[] = {
  "px", "py", "pz",
  "nx", "ny", "nz",
};

struct TexCube {
  RefCounted;
  uint handle;
  int size;
  TexFormat format;
};

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

TexCube* TexCube_Create (int size, TexFormat format) {
  if (!TexFormat_IsValid(format))
    Fatal("TexCube_Create: Invalid texture format requested");
  if (TexFormat_IsDepth(format))
    Fatal("TexCube_Create: Cannot create cubemap with depth format");

  TexCube* self = MemNew(TexCube);
  RefCounted_Init(self);
  GLCALL(glGenTextures(1, &self->handle))
  self->size = size;
  self->format = format;
  GLCALL(glBindTexture(GL_TEXTURE_CUBE_MAP, self->handle))
  GLCALL(glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X, 0, format, size, size, 0, GL_RED, GL_BYTE, 0))
  GLCALL(glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_Y, 0, format, size, size, 0, GL_RED, GL_BYTE, 0))
  GLCALL(glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_Z, 0, format, size, size, 0, GL_RED, GL_BYTE, 0))
  GLCALL(glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_X, 0, format, size, size, 0, GL_RED, GL_BYTE, 0))
  GLCALL(glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, 0, format, size, size, 0, GL_RED, GL_BYTE, 0))
  GLCALL(glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_Z, 0, format, size, size, 0, GL_RED, GL_BYTE, 0))
  TexCube_InitParameters();
  GLCALL(glBindTexture(GL_TEXTURE_CUBE_MAP, 0))
  return self;
}

void TexCube_Acquire (TexCube* self) {
  RefCounted_Acquire(self);
}

void TexCube_Clear (TexCube* self, float r, float g, float b, float a) {
  for (int i = 0; i < 6; i++) {
    Face face = kFaces[i];
    RenderTarget_Push(self->size, self->size);
    RenderTarget_BindTexCube(self, face.face);
    Draw_Clear(r, g, b, a);
    RenderTarget_Pop();
  }
}

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

TexCube* TexCube_Load (cstr path) {
  TexCube* self = MemNew(TexCube);
  GLCALL(glGenTextures(1, &self->handle))
  GLCALL(glBindTexture(GL_TEXTURE_CUBE_MAP, self->handle))

  int components = 0;
  int dataLayout = 0;

  for (int i = 0; i < 6; ++i) {
    cstr facePath = StrAdd3(path, kFaceExt[i], ".jpg");
    int sx, sy, lcomponents;
    uchar* data = Tex2D_LoadRaw(facePath, &sx, &sy, &lcomponents);
    if (!data)
      Fatal("TexCube_Load failed to load cubemap face from '%s'", facePath);
    if (sx != sy)
      Fatal("TexCube_Load loaded cubemap face is not square");
    if (i) {
      if (sx != self->size || sy != self->size)
        Fatal("TexCube_Load loaded cubemap faces have different resolutions");
      if (lcomponents != components)
        Fatal("TexCube_Load loaded cubemap faces have different number of components");
    } else {
      components = lcomponents;
      self->size = sx;
      self->format =
        components == 4 ? TexFormat_RGBA8 :
        components == 3 ? TexFormat_RGB8 :
        components == 2 ? TexFormat_RG8 : TexFormat_R8;
      dataLayout =
        components == 4 ? GL_RGBA :
        components == 3 ? GL_RGB :
        components == 2 ? GL_RG : GL_RED;
    }

    GLCALL(glTexImage2D(kFaces[i].face, 0, self->format, self->size, self->size, 0, dataLayout, GL_UNSIGNED_BYTE, data))
    MemFree(facePath);
    MemFree(data);
  }

  TexCube_InitParameters();
  GLCALL(glBindTexture(GL_TEXTURE_CUBE_MAP, 0))
  return self;
}

void TexCube_GetData (
  TexCube* self,
  void* data,
  CubeFace face,
  int level,
  PixelFormat pf,
  DataFormat df)
{
  GLCALL(glBindTexture(GL_TEXTURE_CUBE_MAP, self->handle))
  GLCALL(glGetTexImage(face, level, pf, df, data))
  GLCALL(glBindTexture(GL_TEXTURE_CUBE_MAP, 0))
}

Bytes* TexCube_GetDataBytes (
  TexCube* self,
  CubeFace face,
  int level,
  PixelFormat pf,
  DataFormat df)
{
  int size = self->size * self->size;
  size *= DataFormat_GetSize(df);
  size *= PixelFormat_Components(pf);
  Bytes* data = Bytes_Create(size);
  TexCube_GetData(self, Bytes_GetData(data), face, level, pf, df);
  Bytes_Rewind(data);
  return data;
}

TexFormat TexCube_GetFormat (TexCube* self) {
  return self->format;
}

uint TexCube_GetHandle (TexCube* self) {
  return self->handle;
}

int TexCube_GetSize (TexCube* self) {
  return self->size;
}

void TexCube_Generate (TexCube* self, ShaderState* state) {
  GLMatrix_ModeP();  GLMatrix_Push(); GLMatrix_Clear();
  GLMatrix_ModeWV(); GLMatrix_Push(); GLMatrix_Clear();
  RenderState_PushAllDefaults();
  ShaderState_Start(state);

  for (int i = 0; i < 6; i++) {
    Face face = kFaces[i];
    int size = self->size;
    float fSize = (float) self->size;
    RenderTarget_Push(size, size);
    RenderTarget_BindTexCube(self, face.face);
    Draw_Clear(0, 0, 0, 1);
    Shader_SetFloat3("cubeLook", UNPACK3(face.look));
    Shader_SetFloat3("cubeUp", UNPACK3(face.up));
    Shader_SetFloat("cubeSize", fSize);

    int j = 1;
    int jobSize = 1;
    while (j <= size) {
      TimeStamp time = TimeStamp_Get();

      ClipRect_Push(0, j - 1, size, jobSize);
      Draw_Rect(0, 0, fSize, fSize);
      Draw_Flush();
      ClipRect_Pop();

      j += jobSize;
      double elapsed = TimeStamp_GetElapsed(time);
      jobSize = Max(1, (int)Floor(0.25 * jobSize / elapsed + 0.5));
      jobSize = Min(jobSize, (size - j + 1));
    }

    RenderTarget_Pop();
  }

  ShaderState_Stop(state);
  RenderState_PopAll();
  GLMatrix_ModeP();  GLMatrix_Pop();
  GLMatrix_ModeWV(); GLMatrix_Pop();
}

void TexCube_GenMipmap (TexCube* self) {
  GLCALL(glBindTexture(GL_TEXTURE_CUBE_MAP, self->handle))
  GLCALL(glGenerateMipmap(GL_TEXTURE_CUBE_MAP))
  GLCALL(glBindTexture(GL_TEXTURE_CUBE_MAP, 0))
}

void TexCube_SetData (
  TexCube* self,
  void const* data,
  CubeFace face,
  int level,
  PixelFormat pf,
  DataFormat df)
{
  GLCALL(glBindTexture(GL_TEXTURE_CUBE_MAP, self->handle))
  GLCALL(glTexImage2D(face, level, self->format, self->size, self->size, 0, pf, df, data))
  GLCALL(glBindTexture(GL_TEXTURE_CUBE_MAP, 0))
}

void TexCube_SetDataBytes (
  TexCube* self,
  Bytes* data,
  CubeFace face,
  int level,
  PixelFormat pf,
  DataFormat df)
{
  TexCube_SetData(self, Bytes_GetData(data), face, level, pf, df);
}

void TexCube_SetMagFilter (TexCube* self, TexFilter filter) {
  GLCALL(glBindTexture(GL_TEXTURE_CUBE_MAP, self->handle))
  GLCALL(glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, filter))
  GLCALL(glBindTexture(GL_TEXTURE_CUBE_MAP, 0))
}

void TexCube_SetMinFilter (TexCube* self, TexFilter filter) {
  GLCALL(glBindTexture(GL_TEXTURE_CUBE_MAP, self->handle))
  GLCALL(glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, filter))
  GLCALL(glBindTexture(GL_TEXTURE_CUBE_MAP, 0))
}

void TexCube_Save (TexCube* self, cstr path) {
  TexCube_SaveLevel(self, path, 0);
}

void TexCube_SaveLevel (TexCube* self, cstr path, int level) {
  int size = self->size >> level;
  GLCALL(glBindTexture(GL_TEXTURE_CUBE_MAP, self->handle))
  uchar* buffer = MemNewArray(uchar, 4 * size * size);
  for (int i = 0; i < 6; ++i) {
    CubeFace face = kFaces[i].face;
    cstr facePath = StrAdd3(path, kFaceExt[i], ".png");
    GLCALL(glGetTexImage(face, level, GL_RGBA, GL_UNSIGNED_BYTE, buffer))
    Tex2D_Save_Png(facePath, size, size, 4, buffer);
    MemFree(facePath);
  }
  MemFree(buffer);
  GLCALL(glBindTexture(GL_TEXTURE_CUBE_MAP, 0))
}


text:
#include "Draw.h"
#include "Matrix.h"
#include "PhxMemory.h"
#include "Mesh.h"
#include "Octree.h"
#include "Vertex.h"

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

struct Octree {
  Box3f box;
  Octree* child[8];
  Node* elems;
};

Octree* Octree_Create (Box3f box) {
  Octree* self = MemNew(Octree);
  MemZero(self, sizeof(Octree));
  self->box = box;
  return self;
}

void Octree_Free (Octree* self) {
  for (int i = 0; i < 8; ++i)
    if (self->child[i])
      Octree_Free(self->child[i]);

  Node* elem = self->elems;
  while (elem) {
    Node* next = elem->next;
    MemFree(elem);
    elem = next;
  }
  MemFree(self);
}

Octree* Octree_FromMesh (Mesh* mesh) {
  Box3f meshBox; Mesh_GetBound(mesh, &meshBox);
  Octree* self = Octree_Create(meshBox);

  int indexCount = Mesh_GetIndexCount(mesh);
  int const* indexData = Mesh_GetIndexData(mesh);
  Vertex const* vertexData = Mesh_GetVertexData(mesh);

  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];
    Box3f box = Box3f_Create(
      Vec3f_Min(v0->p, Vec3f_Min(v1->p, v2->p)),
      Vec3f_Max(v0->p, Vec3f_Max(v1->p, v2->p)));
    Octree_Add(self, box, i / 3);
  }

  return self;
}

static void Octree_GetAvgLoadImpl (Octree* self, double* load, double* nodes) {
  *nodes += 1;
  for (Node* elem = self->elems; elem; elem = elem->next)
    *load += 1;
  for (int i = 0; i < 8; ++i)
    if (self->child[i])
      Octree_GetAvgLoadImpl(self->child[i], load, nodes);
}

double Octree_GetAvgLoad (Octree* self) {
  double load = 0;
  double nodes = 0;
  Octree_GetAvgLoadImpl(self, &load, &nodes);
  return load / nodes;
}

int Octree_GetMaxLoad (Octree* self) {
  int load = 0;
  for (Node* elem = self->elems; elem; elem = elem->next)
    load += 1;
  for (int i = 0; i < 8; ++i)
    if (self->child[i])
      load = Max(load, Octree_GetMaxLoad(self->child[i]));
  return load;
}

int Octree_GetMemory (Octree* self) {
  int memory = sizeof(Octree);
  for (int i = 0; i < 8; ++i)
    if (self->child[i])
      memory += Octree_GetMemory(self->child[i]);
  for (Node* elem = self->elems; elem; elem = elem->next)
    memory += sizeof(Node);
  return memory;
}

static bool Octree_IntersectRayImpl (Octree* self, Vec3f o, Vec3f di) {
  if (!Box3f_IntersectsRay(self->box, o, di))
    return false;

  for (Node* elem = self->elems; elem; elem = elem->next)
    if (Box3f_IntersectsRay(elem->box, o, di))
      return true;

  for (int i = 0; i < 8; ++i)
    if (self->child[i])
      if (Octree_IntersectRayImpl(self->child[i], o, di))
        return true;

  return false;
}

bool Octree_IntersectRay (
  Octree* self,
  Matrix* matrix,
  Vec3f const* ro,
  Vec3f const* rd)
{
  Matrix* inv = Matrix_Inverse(matrix);
  Vec3f invRo; Matrix_MulPoint(inv, &invRo, ro->x, ro->y, ro->z);
  Vec3f invRd; Matrix_MulDir(inv, &invRd, rd->x, rd->y, rd->z);
  Matrix_Free(inv);
  return Octree_IntersectRayImpl(self, invRo, Vec3f_Rcp(invRd));
}

static void Octree_Insert (Octree* self, Box3f box, uint32 id) {
  Node* elem = MemNew(Node);
  elem->box = box;
  elem->id = id;
  elem->next = self->elems;
  self->elems = elem;
}

static void Octree_AddDepth (Octree* self, Box3f box, uint32 id, int depth) {
  Vec3f const* const NO_ALIAS L = &self->box.lower;
  Vec3f const* const NO_ALIAS U = &self->box.upper;
  Vec3f const C = Box3f_Center(self->box);
  Box3f const childBound[8] = {
    { { L->x, L->y, L->z }, {  C.x,  C.y,  C.z } },
    { {  C.x, L->y, L->z }, { U->x,  C.y,  C.z } },
    { { L->x,  C.y, L->z }, {  C.x, U->y,  C.z } },
    { {  C.x,  C.y, L->z }, { U->x, U->y,  C.z } },
    { { L->x, L->y,  C.z }, {  C.x,  C.y, U->z } },
    { {  C.x, L->y,  C.z }, { U->x,  C.y, U->z } },
    { { L->x,  C.y,  C.z }, {  C.x, U->y, U->z } },
    { {  C.x,  C.y,  C.z }, { U->x, U->y, U->z } }
  };

  int intersections = 0;
  int lastIntersection = 0;
  for (int i = 0; i < 8; ++i) {
    if (Box3f_IntersectsBox(box, childBound[i])) {
      intersections++;
      lastIntersection = i;
    }
  }

  if (intersections == 0)
    return;

  if (intersections == 1) {
    if (!self->child[lastIntersection])
      self->child[lastIntersection] = Octree_Create(childBound[lastIntersection]);
    Octree_AddDepth(
      self->child[lastIntersection],
      Box3f_Intersection(box, childBound[lastIntersection]), id, depth + 1);
    return;
  }

  Octree_Insert(self, box, id);
}

void Octree_Add (Octree* self, Box3f box, uint32 id) {
  Octree_AddDepth(self, box, id, 0);
}

void Octree_Draw (Octree* self) {
  Draw_Color(1, 1, 1, 1);
  Draw_Box3(&self->box);
  Draw_Color(0, 1, 0, 1);
  for (Node* elem = self->elems; elem; elem = elem->next)
    Draw_Box3(&elem->box);
  for (int i = 0; i < 8; ++i)
    if (self->child[i])
      Octree_Draw(self->child[i]);
}


text:
#include "PhxMath.h"
#include "PhxFloat.h"
#include "Quat.h"
#include "Vec3.h"

#include <stdio.h>

Quat Quat_Create (float x, float y, float z, float w) {
  Quat self;
  self.x = x;
  self.y = y;
  self.z = z;
  self.w = w;
  return self;
}

void Quat_GetAxisX (Quat const* q, Vec3f* out) {
  out->x = 1.0f - 2.0f * (q->y*q->y + q->z*q->z);
  out->y =        2.0f * (q->x*q->y + q->z*q->w);
  out->z =        2.0f * (q->x*q->z - q->y*q->w);
  Assert(Vec3f_Validate(*out) == Error_None);
}

void Quat_GetAxisY (Quat const* q, Vec3f* out) {
  out->x =        2.0f * (q->x*q->y - q->z*q->w);
  out->y = 1.0f - 2.0f * (q->x*q->x + q->z*q->z);
  out->z =        2.0f * (q->y*q->z + q->x*q->w);
  Assert(Vec3f_Validate(*out) == Error_None);
}

void Quat_GetAxisZ (Quat const* q, Vec3f* out) {
  out->x =        2.0f * (q->x*q->z + q->y*q->w);
  out->y =        2.0f * (q->y*q->z - q->x*q->w);
  out->z = 1.0f - 2.0f * (q->x*q->x + q->y*q->y);
  Assert(Vec3f_Validate(*out) == Error_None);
}

void Quat_GetForward (Quat const* q, Vec3f* out) {
  Quat_GetAxisZ(q, out);
  out->x = -out->x;
  out->y = -out->y;
  out->z = -out->z;
}

void Quat_GetRight (Quat const* q, Vec3f* out) {
  Quat_GetAxisX(q, out);
}

void Quat_GetUp (Quat const* q, Vec3f* out) {
  Quat_GetAxisY(q, out);
}

void Quat_Identity (Quat* out) {
  out->x = 0.0f;
  out->y = 0.0f;
  out->z = 0.0f;
  out->w = 1.0f;
}

void Quat_Canonicalize (Quat const* q, Quat* out) {
  float value = !Float_ApproximatelyEqual(q->w, 0.0f) ? q->w
              : !Float_ApproximatelyEqual(q->z, 0.0f) ? q->z
              : !Float_ApproximatelyEqual(q->y, 0.0f) ? q->y
              : !Float_ApproximatelyEqual(q->x, 0.0f) ? q->x
              : 0.0f;

  if (value < 0.0f) {
    out->x = -q->x;
    out->y = -q->y;
    out->z = -q->z;
    out->w = -q->w;
  } else {
    out->x = q->x;
    out->y = q->y;
    out->z = q->z;
    out->w = q->w;
  }
  Assert(Quat_Validate(out) == Error_None);
}

void Quat_ICanonicalize (Quat* q) {
  float value = !Float_ApproximatelyEqual(q->w, 0.0f) ? q->w
              : !Float_ApproximatelyEqual(q->z, 0.0f) ? q->z
              : !Float_ApproximatelyEqual(q->y, 0.0f) ? q->y
              : !Float_ApproximatelyEqual(q->x, 0.0f) ? q->x
              : 0.0f;

  if (value < 0.0f) {
    q->x = -q->x;
    q->y = -q->y;
    q->z = -q->z;
    q->w = -q->w;
  }
  Assert(Quat_Validate(q) == Error_None);
}

float Quat_Dot (Quat const* q, Quat const* p) {
  return q->x*p->x
       + q->y*p->y
       + q->z*p->z
       + q->w*p->w;
}

bool Quat_Equal (Quat const* q, Quat const* p) {
  Quat cq; Quat_Canonicalize(q, &cq);
  Quat cp; Quat_Canonicalize(p, &cp);
  return cq.x == cp.x
      && cq.y == cp.y
      && cq.z == cp.z
      && cq.w == cp.w;
}

bool Quat_ApproximatelyEqual (Quat const* q, Quat const* p) {
  Quat cq; Quat_Canonicalize(q, &cq);
  Quat cp; Quat_Canonicalize(p, &cp);
  return Abs(cq.x - cp.x) < 1e-3f
      && Abs(cq.y - cp.y) < 1e-3f
      && Abs(cq.z - cp.z) < 1e-3f
      && Abs(cq.w - cp.w) < 1e-3f;
}

void Quat_Inverse (Quat const* q, Quat* out) {
  float magSq = q->x*q->x
              + q->y*q->y
              + q->z*q->z
              + q->w*q->w;
  Assert(magSq > 1e-12f);
  out->x = -q->x / magSq,
  out->y = -q->y / magSq,
  out->z = -q->z / magSq,
  out->w =  q->w / magSq;
  Assert(Quat_Validate(out) == Error_None);
}

void Quat_IInverse (Quat* q) {
  float magSq = q->x*q->x
              + q->y*q->y
              + q->z*q->z
              + q->w*q->w;
  Assert(magSq > 1e-12f);
  q->x = -q->x/magSq;
  q->y = -q->y/magSq;
  q->z = -q->z/magSq;
  q->w =  q->w/magSq;
  Assert(Quat_Validate(q) == Error_None);
}

void Quat_Lerp (Quat const* q, Quat const* p, float t, Quat* out) {
  float d = Quat_Dot(p, q);
  Quat dp;
  if (d < 0.0f) {
    dp.x = -p->x;
    dp.y = -p->y;
    dp.z = -p->z;
    dp.w = -p->w;
  } else {
    dp = *p;
  }

  float x = q->x + (dp.x - q->x) * t;
  float y = q->y + (dp.y - q->y) * t;
  float z = q->z + (dp.z - q->z) * t;
  float w = q->w + (dp.w - q->w) * t;
  float rcpMag = 1.0f / Sqrt(x*x + y*y + z*z + w*w);
  out->x = x*rcpMag;
  out->y = y*rcpMag;
  out->z = z*rcpMag;
  out->w = w*rcpMag;
  Assert(Quat_Validate(out) == Error_None);
}

void Quat_ILerp (Quat* q, Quat const* p, float t) {
  float d = Quat_Dot(p, q);
  Quat dp;
  if (d < 0.0f) {
    dp.x = -p->x,
    dp.y = -p->y,
    dp.z = -p->z,
    dp.w = -p->w;
  } else {
    dp = *p;
  }

  float x = q->x + (dp.x - q->x) * t;
  float y = q->y + (dp.y - q->y) * t;
  float z = q->z + (dp.z - q->z) * t;
  float w = q->w + (dp.w - q->w) * t;
  float rcpMag = 1.0f / Sqrt(x*x + y*y + z*z + w*w);
  q->x = x*rcpMag;
  q->y = y*rcpMag;
  q->z = z*rcpMag;
  q->w = w*rcpMag;
  Assert(Quat_Validate(q) == Error_None);
}

/* TODO : The Lua side implementation of this looks much cleaner.
  return (Quat(
    a.w*b.x + a.x*b.w + a.y*b.z - a.z*b.y,
    a.w*b.y - a.x*b.z + a.y*b.w + a.z*b.x,
    a.w*b.z + a.x*b.y - a.y*b.x + a.z*b.w,
    a.w*b.w - a.x*b.x - a.y*b.y - a.z*b.z))
*/
void Quat_Mul (Quat const* q, Quat const* p, Quat* out) {
  Vec3f qv = { q->x, q->y, q->z };
  Vec3f pv = { p->x, p->y, p->z };
  Vec3f rv = Vec3f_Add(
    Vec3f_Add(
      Vec3f_Muls(qv, p->w),
      Vec3f_Muls(pv, q->w)),
    Vec3f_Cross(qv, pv));
  out->x = rv.x;
  out->y = rv.y;
  out->z = rv.z;
  out->w = q->w*p->w - Vec3f_Dot(qv, pv);
  Assert(Quat_Validate(out) == Error_None);
}

void Quat_IMul (Quat* q, Quat const* p) {
  Vec3f qv = { q->x, q->y, q->z };
  Vec3f pv = { p->x, p->y, p->z };
  Vec3f rv = Vec3f_Add(
    Vec3f_Add(
      Vec3f_Muls(qv, p->w),
      Vec3f_Muls(pv, q->w)),
    Vec3f_Cross(qv, pv));
  q->x = rv.x;
  q->y = rv.y;
  q->z = rv.z;
  q->w = q->w*p->w - Vec3f_Dot(qv, pv);
  Assert(Quat_Validate(q) == Error_None);
}

void Quat_MulV (Quat const* q, Vec3f const* v, Vec3f* out) {
  Vec3f u = { q->x, q->y, q->z };
  float w = q->w;
  Vec3f t = Vec3f_Cross(u, *v);
  *out = Vec3f_Add(
    Vec3f_Muls(u, 2.0f * Vec3f_Dot(u, *v)),
    Vec3f_Add(
      Vec3f_Muls(*v, 2.0f * w * w - 1.0f),
      Vec3f_Muls(t, 2.0f * w)
    )
  );
  Assert(Vec3f_Validate(*out) == Error_None);
}

void Quat_Normalize (Quat const* q, Quat* out) {
  float mag = Sqrt(q->x*q->x + q->y*q->y + q->z*q->z + q->w*q->w);
  Assert(mag != 0.0f);
  out->x = q->x / mag;
  out->y = q->y / mag;
  out->z = q->z / mag;
  out->w = q->w / mag;
  Assert(Quat_Validate(q) == Error_None);
}

void Quat_INormalize (Quat* q) {
  float mag = Sqrt(q->x*q->x + q->y*q->y + q->z*q->z + q->w*q->w);
  Assert(mag != 0.0f);
  q->x /= mag;
  q->y /= mag;
  q->z /= mag;
  q->w /= mag;
  Assert(Quat_Validate(q) == Error_None);
}

void Quat_Scale (Quat const* q, float scale, Quat* out) {
  out->x = scale * q->x;
  out->y = scale * q->y;
  out->z = scale * q->z;
  out->w = scale * q->w;
  Assert(Quat_Validate(out) == Error_None);
}

void Quat_IScale (Quat* q, float scale) {
  q->x *= scale;
  q->y *= scale;
  q->z *= scale;
  q->w *= scale;
  Assert(Quat_Validate(q) == Error_None);
}

/* TODO : There was a normalized q in Quat.ext, but it wasn't used. Is this correct? */
void Quat_Slerp (Quat const* q, Quat const* p, float t, Quat* out) {
  Quat np; Quat_Normalize(p, &np);
  float d = Quat_Dot(q, p);
  if (d < 0.0f) {
    np.x = -np.x;
    np.y = -np.y;
    np.z = -np.z;
    np.w = -np.w;
    d = -d;
  }

  if (d > 0.9995f) {
    Quat_Lerp(q, p, t, out);
    Assert(Quat_Validate(out) == Error_None);
    return;
  }

  d = ClampUnit(d);
  float angle = t * Acos(d);
  Quat c = {
    p->x - d*q->x,
    p->y - d*q->y,
    p->z - d*q->z,
    p->w - d*q->w
  };
  Quat_INormalize(&c);

  float fa = Cos(angle);
  float fc = Sin(angle);
  out->x = fa*q->x + fc*c.x;
  out->y = fa*q->y + fc*c.y;
  out->z = fa*q->z + fc*c.z;
  out->w = fa*q->w + fc*c.w;
  Assert(Quat_Validate(out) == Error_None);
}

/* TODO : There was a normalized q in Quat.ext, but it wasn't used. Is this correct? */
void Quat_ISlerp (Quat* q, Quat const* p, float t) {
  Quat np; Quat_Normalize(p, &np);
  float d = Quat_Dot(q, p);
  if (d < 0.0f) {
    np.x = -np.x;
    np.y = -np.y;
    np.z = -np.z;
    np.w = -np.w;
    d = -d;
  }

  if (d > 0.9995f) {
    Quat_ILerp(q, p, t);
    Assert(Quat_Validate(q) == Error_None);
    return;
  }

  d = ClampUnit(d);
  float angle = t * Acos(d);
  Quat c = {
    p->x - d*q->x,
    p->y - d*q->y,
    p->z - d*q->z,
    p->w - d*q->w
  };
  Quat_INormalize(&c);

  float fa = Cos(angle);
  float fc = Sin(angle);
  q->x = fa*q->x + fc*c.x;
  q->y = fa*q->y + fc*c.y;
  q->z = fa*q->z + fc*c.z;
  q->w = fa*q->w + fc*c.w;
  Assert(Quat_Validate(q) == Error_None);
}

cstr Quat_ToString (Quat const* q) {
  static char buffer[512];
  snprintf(buffer, (size_t) Array_GetSize(buffer),
    "(%.4f, %.4f, %.4f, %.4f)",
    q->x, q->y, q->z, q->w);
  return buffer;
}

Error Quat_Validate(Quat const* q) {
  Error e = Error_None;
  e |= Float_Validate(q->x);
  e |= Float_Validate(q->y);
  e |= Float_Validate(q->z);
  e |= Float_Validate(q->w);
  return e;
}

void Quat_FromAxisAngle (Vec3f const* axis, float radians, Quat* out) {
  radians *= 0.5f;
  Vec3f v = Vec3f_Muls(*axis, Sin(radians));
  out->x = v.x;
  out->y = v.y;
  out->z = v.z;
  out->w = Cos(radians);
  Assert(Quat_Validate(out) == Error_None);
}

void Quat_FromBasis (Vec3f const* x, Vec3f const* y, Vec3f const* z, Quat* out) {
  float r = x->x + y->y + z->z;
  if (r > 0.0f) {
    out->w = Sqrt(r + 1.0f) * 0.5f;
    float w4 = 1.0f / (4.0f * out->w);
    out->x = (y->z - z->y) * w4;
    out->y = (z->x - x->z) * w4;
    out->z = (x->y - y->x) * w4;

  } else if (x->x > y->y && x->x > z->z) {
    out->x = Sqrt(1.0f + x->x - y->y - z->z) * 0.5f;
    float x4 = 1.0f / (4.0f * out->x);
    out->y = (y->x + x->y) * x4;
    out->z = (z->x + x->z) * x4;
    out->w = (y->z - z->y) * x4;

  } else if (y->y > z->z) {
    out->y = Sqrt(1.0f + y->y - x->x - z->z) * 0.5f;
    float y4 = 1.0f / (4.0f * out->y);
    out->x = (y->x + x->y) * y4;
    out->z = (z->y + y->z) * y4;
    out->w = (z->x - x->z) * y4;

  } else {
    out->z = Sqrt(1.0f + z->z - x->x - y->y) * 0.5f;
    float z4 = 1.0f / (4.0f * out->z);
    out->x = (z->x + x->z) * z4;
    out->y = (z->y + y->z) * z4;
    out->w = (x->y - y->x) * z4;
  }
  Assert(Quat_Validate(out) == Error_None);
}

void Quat_FromLookUp (Vec3f const* look, Vec3f const* up, Quat* out) {
  Vec3f z = Vec3f_Normalize(Vec3f_Muls(*look, -1.0f));
  Vec3f x = Vec3f_Normalize(Vec3f_Cross(*up, z));
  Vec3f y = Vec3f_Cross(z, x);
  Quat_FromBasis(&x, &y, &z, out);
}

void Quat_FromRotateTo (Vec3f const* from, Vec3f const* to, Quat* out) {
  Vec3f axis = Vec3f_Cross(
    Vec3f_Normalize(*from),
    Vec3f_Normalize(*to)
  );
  float angle = Asin(Vec3f_Length(axis));
  Quat_FromAxisAngle(&axis, angle, out);
}


text:
#include "Draw.h"
#include "Metric.h"
#include "OpenGL.h"
#include "Vec4.h"

/* TODO JP : Replace all immediates with static VBO/IBOs & glDraw*. */

#define MAX_STACK_DEPTH 16

static float alphaStack[MAX_STACK_DEPTH];
static int alphaIndex = -1;
static Vec4f color = { 1, 1, 1, 1 };

void Draw_PushAlpha (float a) {
  if (alphaIndex + 1 >= MAX_STACK_DEPTH)
      Fatal("Draw_PushAlpha: Maximum alpha stack depth exceeded");

  float prevAlpha = alphaIndex >= 0 ? alphaStack[alphaIndex] : 1;
  float alpha = a * prevAlpha;
  alphaStack[++alphaIndex] = alpha;
  GLCALL(glColor4f(color.x, color.y, color.z, color.w * alpha));
}

void Draw_PopAlpha () {
  if (alphaIndex < 0)
      Fatal("Draw_PopAlpha Attempting to pop an empty alpha stack");

  alphaIndex--;
  float alpha = alphaIndex >= 0 ? alphaStack[alphaIndex] : 1;
  GLCALL(glColor4f(color.x, color.y, color.z, color.w * alpha));
}

void Draw_Axes (
  Vec3f const* pos,
  Vec3f const* x,
  Vec3f const* y,
  Vec3f const* z,
  float scale,
  float _alpha)
{
  Vec3f left    = Vec3f_Add(*pos, Vec3f_Muls(*x, scale));
  Vec3f up      = Vec3f_Add(*pos, Vec3f_Muls(*y, scale));
  Vec3f forward = Vec3f_Add(*pos, Vec3f_Muls(*z, scale));
  glBegin(GL_LINES);
  glColor4f(1, 0.25f, 0.25f, _alpha);
  glVertex3f(UNPACK3(*pos));
  glVertex3f(UNPACK3(left));
  glColor4f(0.25f, 1, 0.25f, _alpha);
  glVertex3f(UNPACK3(*pos));
  glVertex3f(UNPACK3(up));
  glColor4f(0.25f, 0.25f, 1, _alpha);
  glVertex3f(UNPACK3(*pos));
  glVertex3f(UNPACK3(forward));
  GLCALL(glEnd())

  glBegin(GL_POINTS);
  glColor4f(1, 1, 1, _alpha);
  glVertex3f(UNPACK3(*pos));
  GLCALL(glEnd())
}

void Draw_Border (float s, float x, float y, float w, float h) {
  Draw_Rect(x, y, w, s);
  Draw_Rect(x, y + h - s, w, s);
  Draw_Rect(x, y + s, s, h - 2*s);
  Draw_Rect(x + w - s, y + s, s, h - 2*s);
}

void Draw_Box3 (Box3f const* self) {
  Metric_AddDrawImm(6, 12, 24);
  glBegin(GL_QUADS);
  /* Left. */
  glVertex3f(self->lower.x, self->lower.y, self->lower.z);
  glVertex3f(self->lower.x, self->lower.y, self->upper.z);
  glVertex3f(self->lower.x, self->upper.y, self->upper.z);
  glVertex3f(self->lower.x, self->upper.y, self->lower.z);

  /* Right. */
  glVertex3f(self->upper.x, self->lower.y, self->lower.z);
  glVertex3f(self->upper.x, self->upper.y, self->lower.z);
  glVertex3f(self->upper.x, self->upper.y, self->upper.z);
  glVertex3f(self->upper.x, self->lower.y, self->upper.z);

  /* Front. */
  glVertex3f(self->lower.x, self->lower.y, self->upper.z);
  glVertex3f(self->upper.x, self->lower.y, self->upper.z);
  glVertex3f(self->upper.x, self->upper.y, self->upper.z);
  glVertex3f(self->lower.x, self->upper.y, self->upper.z);

  /* Back. */
  glVertex3f(self->lower.x, self->lower.y, self->lower.z);
  glVertex3f(self->lower.x, self->upper.y, self->lower.z);
  glVertex3f(self->upper.x, self->upper.y, self->lower.z);
  glVertex3f(self->upper.x, self->lower.y, self->lower.z);

  /* Top. */
  glVertex3f(self->lower.x, self->upper.y, self->lower.z);
  glVertex3f(self->lower.x, self->upper.y, self->upper.z);
  glVertex3f(self->upper.x, self->upper.y, self->upper.z);
  glVertex3f(self->upper.x, self->upper.y, self->lower.z);

  /* Bottom. */
  glVertex3f(self->lower.x, self->lower.y, self->lower.z);
  glVertex3f(self->upper.x, self->lower.y, self->lower.z);
  glVertex3f(self->upper.x, self->lower.y, self->upper.z);
  glVertex3f(self->lower.x, self->lower.y, self->upper.z);
  GLCALL(glEnd())
}

void Draw_Clear (float r, float g, float b, float a) {
  GLCALL(glClearColor(r, g, b, a))
  GLCALL(glClear(GL_COLOR_BUFFER_BIT))
}

void Draw_ClearDepth (float d) {
  GLCALL(glClearDepth(d))
  GLCALL(glClear(GL_DEPTH_BUFFER_BIT))
}

void Draw_Color (float r, float g, float b, float a) {
  float alpha = alphaIndex >= 0 ? alphaStack[alphaIndex] : 1;
  color = Vec4f_Create(r, g, b, a);
  GLCALL(glColor4f(r, g, b, a * alpha))
}

void Draw_Flush () {
  Metric_Inc(Metric_Flush);
  GLCALL(glFinish())
}

void Draw_Line (float x1, float y1, float x2, float y2) {
  glBegin(GL_LINES);
  glVertex2f(x1, y1);
  glVertex2f(x2, y2);
  GLCALL(glEnd())
}

void Draw_Line3 (Vec3f const* p1, Vec3f const* p2) {
  glBegin(GL_LINES);
  glVertex3f(UNPACK3(*p1));
  glVertex3f(UNPACK3(*p2));
  GLCALL(glEnd())
}

void Draw_LineWidth (float width) {
  GLCALL(glLineWidth(width))
}

void Draw_Plane (Vec3f const* p, Vec3f const* n, float scale) {
  Vec3f e1 = Abs(n->x) < 0.7f ? Vec3f_Create(1, 0, 0) : Vec3f_Create(0, 1, 0);
  e1 = Vec3f_Normalize(Vec3f_Reject(e1, *n));
  Vec3f e2 = Vec3f_Cross(*n, e1);

  Vec3f p0 = Vec3f_Add(*p, Vec3f_Add(Vec3f_Muls(e1, -scale), Vec3f_Muls(e2, -scale)));
  Vec3f p1 = Vec3f_Add(*p, Vec3f_Add(Vec3f_Muls(e1,  scale), Vec3f_Muls(e2, -scale)));
  Vec3f p2 = Vec3f_Add(*p, Vec3f_Add(Vec3f_Muls(e1,  scale), Vec3f_Muls(e2,  scale)));
  Vec3f p3 = Vec3f_Add(*p, Vec3f_Add(Vec3f_Muls(e1, -scale), Vec3f_Muls(e2,  scale)));

  Metric_AddDrawImm(1, 2, 4);
  glBegin(GL_QUADS);
  glVertex3f(UNPACK3(p0));
  glVertex3f(UNPACK3(p1));
  glVertex3f(UNPACK3(p2));
  glVertex3f(UNPACK3(p3));
  GLCALL(glEnd())
}

void Draw_Point (float x, float y) {
  glBegin(GL_POINTS);
  glVertex2f(x, y);
  GLCALL(glEnd())
}

void Draw_Point3 (float x, float y, float z) {
  glBegin(GL_POINTS);
  glVertex3f(x, y, z);
  GLCALL(glEnd())
}

void Draw_PointSize (float size) {
  GLCALL(glPointSize(size))
}

void Draw_Poly (Vec2f const* points, int count) {
  Metric_AddDrawImm(1, count - 2, count);
  glBegin(GL_POLYGON);
  for (int i = 0; i < count; ++i)
    glVertex2f(UNPACK2(points[i]));
  GLCALL(glEnd());
}

void Draw_Poly3 (Vec3f const* points, int count) {
  Metric_AddDrawImm(1, count - 2, count);
  glBegin(GL_POLYGON);
  for (int i = 0; i < count; ++i)
    glVertex3f(UNPACK3(points[i]));
  GLCALL(glEnd());
}

void Draw_Quad (Vec2f const* p1, Vec2f const* p2, Vec2f const* p3, Vec2f const* p4) {
  Metric_AddDrawImm(1, 2, 4);
  glBegin(GL_QUADS);
  glTexCoord2f(0, 0); glVertex2f(UNPACK2(*p1));
  glTexCoord2f(0, 1); glVertex2f(UNPACK2(*p2));
  glTexCoord2f(1, 1); glVertex2f(UNPACK2(*p3));
  glTexCoord2f(1, 0); glVertex2f(UNPACK2(*p4));
  GLCALL(glEnd())
}

void Draw_Quad3 (Vec3f const* p1, Vec3f const* p2, Vec3f const* p3, Vec3f const* p4) {
  Metric_AddDrawImm(1, 2, 4);
  glBegin(GL_QUADS);
  glTexCoord2f(0, 0); glVertex3f(UNPACK3(*p1));
  glTexCoord2f(0, 1); glVertex3f(UNPACK3(*p2));
  glTexCoord2f(1, 1); glVertex3f(UNPACK3(*p3));
  glTexCoord2f(1, 0); glVertex3f(UNPACK3(*p4));
  GLCALL(glEnd())
}

void Draw_Rect (float x1, float y1, float xs, float ys) {
  float x2 = x1 + xs;
  float y2 = y1 + ys;
  Metric_AddDrawImm(1, 2, 4);
  glBegin(GL_QUADS);
  glTexCoord2f(0, 0); glVertex2f(x1, y1);
  glTexCoord2f(0, 1); glVertex2f(x1, y2);
  glTexCoord2f(1, 1); glVertex2f(x2, y2);
  glTexCoord2f(1, 0); glVertex2f(x2, y1);
  GLCALL(glEnd())
}

void Draw_SmoothLines (bool enabled) {
  if (enabled) {
    GLCALL(glEnable(GL_LINE_SMOOTH))
    GLCALL(glHint(GL_LINE_SMOOTH_HINT, GL_NICEST))
  } else {
    GLCALL(glDisable(GL_LINE_SMOOTH))
    GLCALL(glHint(GL_LINE_SMOOTH_HINT, GL_FASTEST))
  }
}

void Draw_SmoothPoints (bool enabled) {
  if (enabled) {
    GLCALL(glEnable(GL_POINT_SMOOTH))
    GLCALL(glHint(GL_POINT_SMOOTH_HINT, GL_NICEST))
  } else {
    GLCALL(glDisable(GL_POINT_SMOOTH))
    GLCALL(glHint(GL_POINT_SMOOTH_HINT, GL_FASTEST))
  }
}

inline static Vec3f Spherical (float r, float yaw, float pitch) {
  return Vec3f_Create(
    r * Sin(pitch) * Cos(yaw),
    r * Cos(pitch),
    r * Sin(pitch) * Sin(yaw));
}

/* TODO JP : Lazy creation of VBO / IBO & glDraw instead of immediate. */
void Draw_Sphere (Vec3f const* p, float r) {
  const size_t res = 7;
  const float fRes = float(res);

  /* First Row */ {
    Metric_AddDrawImm(res, res, res * 3);
    glBegin(GL_TRIANGLES);
    float lastTheta = float(res - 1) / fRes * Tau;
    float phi = 1.0f / fRes * Pi;
    Vec3f tc = Vec3f_Add(*p, Spherical(r, 0, 0));
    for (size_t iTheta = 0; iTheta < res; iTheta++) {
      float theta = float(iTheta) / fRes * Tau;
      Vec3f br = Vec3f_Add(*p, Spherical(r, lastTheta, phi));
      Vec3f bl = Vec3f_Add(*p, Spherical(r, theta, phi));
      glVertex3f(UNPACK3(br));
      glVertex3f(UNPACK3(tc));
      glVertex3f(UNPACK3(bl));
      lastTheta = theta;
    }
    GLCALL(glEnd())
  }

  /* Middle Rows */ {
    Metric_AddDrawImm(res - 2, 2 * (res - 2), 4 * (res - 2));
    glBegin(GL_QUADS);
    float lastPhi = 1.0f / fRes * Pi;
    float lastTheta = float(res - 1) / fRes * Tau;

    for (size_t iPhi = 2; iPhi < res; iPhi++) {
      float phi = float(iPhi) / fRes * Pi;
      for (size_t iTheta = 0; iTheta < res; iTheta++) {
        float theta = float(iTheta) / fRes * Tau;
        Vec3f br = Vec3f_Add(*p, Spherical(r, lastTheta, phi));
        Vec3f tr = Vec3f_Add(*p, Spherical(r, lastTheta, lastPhi));
        Vec3f tl = Vec3f_Add(*p, Spherical(r, theta, lastPhi));
        Vec3f bl = Vec3f_Add(*p, Spherical(r, theta, phi));
        glVertex3f(UNPACK3(br));
        glVertex3f(UNPACK3(tr));
        glVertex3f(UNPACK3(tl));
        glVertex3f(UNPACK3(bl));
        lastTheta = theta;
      }
      lastPhi = phi;
    }
    GLCALL(glEnd())
  }

  /* Bottom Row */ {
    Metric_AddDrawImm(res, res, res * 3);
    glBegin(GL_TRIANGLES);
    float lastTheta = float(res - 1) / fRes * Tau;
    float phi = float(res - 1) / fRes * Pi;
    Vec3f bc = Vec3f_Add(*p, Spherical(r, 0, Pi));

    for (size_t iTheta = 0; iTheta < res; iTheta++) {
      float theta = float(iTheta) / fRes * Tau;
      Vec3f tr = Vec3f_Add(*p, Spherical(r, lastTheta, phi));
      Vec3f tl = Vec3f_Add(*p, Spherical(r, theta, phi));
      glVertex3f(UNPACK3(tr));
      glVertex3f(UNPACK3(tl));
      glVertex3f(UNPACK3(bc));
      lastTheta = theta;
    }
    GLCALL(glEnd())
  }
}

void Draw_Tri (Vec2f const* v1, Vec2f const* v2, Vec2f const* v3) {
  Metric_AddDrawImm(1, 1, 3);
  glBegin(GL_TRIANGLES);
  glTexCoord2f(0, 0); glVertex2f(UNPACK2(*v1));
  glTexCoord2f(0, 1); glVertex2f(UNPACK2(*v2));
  glTexCoord2f(1, 1); glVertex2f(UNPACK2(*v3));
  GLCALL(glEnd())
}

void Draw_Tri3 (Vec3f const* v1, Vec3f const* v2, Vec3f const* v3) {
  Metric_AddDrawImm(1, 1, 3);
  glBegin(GL_TRIANGLES);
  glTexCoord2f(0, 0); glVertex3f(UNPACK3(*v1));
  glTexCoord2f(0, 1); glVertex3f(UNPACK3(*v2));
  glTexCoord2f(1, 1); glVertex3f(UNPACK3(*v3));
  GLCALL(glEnd())
}


text:
#include "Bullet.h"
#include "CollisionShape.h"
#include "PhxMemory.h"
#include "Physics.h"
#include "PhysicsDefs.h"
#include "RigidBody.h"
#include "Trigger.h"

/* --- Helper Functions ----------------------------------------------------- */

bool RigidBody_IsChild (RigidBody* self) {
  return self->parent != 0 && self->parent != self;
}

bool RigidBody_IsCompound (RigidBody* self) {
  return self->parent != 0;
}

bool RigidBody_IsParent (RigidBody* self) {
  return self->parent != 0 && self->parent == self;
}

inline static void RigidBody_SetFlag (RigidBody* self, int flag, bool enable) {
  btRigidBody* rigidBody = self->handle;

  int flags = rigidBody->getCollisionFlags();
  flags = enable ? flags | flag : flags & ~flag;
  rigidBody->setCollisionFlags(flags);
}

RigidBody* RigidBody_GetPart (RigidBody* self, int iCompound) {
  Assert(RigidBody_IsCompound(self));
  self = self->parent;
  while (self->iCompound != iCompound) self = self->next;
  return self;
}

btTransform RigidBody_GetWorldTransform (RigidBody* self) {
  btTransform transform;

  if (!RigidBody_IsChild(self)) {
    btRigidBody* rigidBody = self->handle;
    transform = rigidBody->getWorldTransform();
  } else {
    btRigidBody*    rigidBody = self->parent->handle;
    CollisionShape* cmpShape  = CollisionShape_GetCached(self->iCompoundShape);
    transform = rigidBody->getWorldTransform();
    transform *= cmpShape->compound.handle->getChildTransform(self->iCompound);
  }

  return transform;
}

inline static void RigidBody_RecalculateInertia (RigidBody* self) {
  /* NOTE : We use the parent's shape to calculate inertia. Compound shapes
            calculate inertia from the bounding box, not the actual shapes, so
            if we e.g. attach something to a ship using a sphere collision shape
            all of a sudden it will switch to inertia for a box and drastically
            change the controls. It looks like the proper solution would be to
            use btCompoundShape::calculatePrincipalAxisTransform to get the
            proper inertia and offset the center of mass. In turn, this means we
            have to get rid of our assumption that the parent has identity
            position within the compound. However, this seems unlikely to add
            much value to the game, so we aren't going to bother. */

  btRigidBody*    rigidBody = self->handle;
  CollisionShape* shape     = CollisionShape_GetCached(self->iShape);

  btVector3 inertia(0, 0, 0);
  shape->base.handle->calculateLocalInertia(self->mass, inertia);
  /* HACK: Hulls are wrapped in btUniformScalingShape. When calculating inertia,
           first it's calculated for a unit sized version of the underlying
           shape, then the scaling wrapper multiplies is by the scale. However,
           inertia is rougly proportional to r^2 and this means the final
           calculation is off by an additional factor of scale. We fix this here
           to ensure wrapped hull has approximately the same inertia a plain
           scaled hull would. Otherwise inertia is flat out wrong and the
           presence of a btUniformScalingShape wrapper leads to wildly different
           ship controls for the same model. */
  if (shape->type == CollisionShapeType_Hull)
    inertia *= shape->hull.handle->getUniformScalingFactor();

  rigidBody->setMassProps(self->mass, inertia);
  rigidBody->updateInertiaTensor();
}

inline static RigidBody* RigidBody_Create (CollisionShape* shape) {
  /* NOTE : We only create compounds through attaching. */
  Assert(shape->type != CollisionShapeType_Compound);

  RigidBody* self = MemNewZero(RigidBody);
  self->type           = PhysicsType_RigidBody;
  self->mass           = 1.0f;
  self->iCompound      = -1;
  self->iCompoundShape = -1;

  btVector3 inertia(0, 0, 0);
  shape->base.handle->calculateLocalInertia(self->mass, inertia);

  btRigidBody* rigidBody = new btRigidBody(self->mass, 0, shape->base.handle, inertia);
  rigidBody->setFlags(BT_DISABLE_WORLD_GRAVITY);
  rigidBody->setRestitution(0.4f);
  rigidBody->setUserPointer(self);

  self->handle         = rigidBody;
  self->iShape         = shape->iShape;
  self->collisionGroup = CollisionGroup_Default;
  self->collisionMask  = CollisionMask_All;
  return self;
}

/* --- Implementation ------------------------------------------------------- */

RigidBody* RigidBody_CreateBox () {
  Vec3f halfExtents = { 1, 1, 1 };
  CollisionShape* shape = CollisionShape_CreateBox(&halfExtents);
  return RigidBody_Create(shape);
}

RigidBody* RigidBody_CreateBoxFromMesh (Mesh* mesh) {
  CollisionShape* shape = CollisionShape_CreateBoxFromMesh(mesh);
  return RigidBody_Create(shape);
}

RigidBody* RigidBody_CreateSphere () {
  CollisionShape* shape = CollisionShape_CreateSphere(1);
  return RigidBody_Create(shape);
}

RigidBody* RigidBody_CreateSphereFromMesh (Mesh* mesh) {
  CollisionShape* shape = CollisionShape_CreateSphereFromMesh(mesh);
  return RigidBody_Create(shape);
}

RigidBody* RigidBody_CreateHullFromMesh (Mesh* mesh) {
  CollisionShape* shape = CollisionShape_CreateHullFromMesh(mesh);
  return RigidBody_Create(shape);
}

inline static void RigidBody_FreeImpl (RigidBody* self) {
  Trigger* trigger = self->triggers;
  while (trigger) {
    Trigger* toFree = trigger;
    trigger = trigger->next;
    Trigger_Free(toFree);
  }

  CollisionShape* shape = CollisionShape_GetCached(self->iShape);
  CollisionShape_Free(shape);
  delete self->handle;
  MemFree(self);
}

void RigidBody_Free (RigidBody* self) {
  if (RigidBody_IsChild(self))
    Fatal("RigidBody_Free: Not supported on children.");
  if (self->physics)
    Fatal("RigidBody_Free: Object is still present in Physics.");

  if (RigidBody_IsParent(self)) {
    RigidBody* child = self->next;
    while (child) {
      RigidBody* toFree = child;
      child = child->next;
      RigidBody_FreeImpl(toFree);
    }

    CollisionShape* shape = CollisionShape_GetCached(self->iCompoundShape);
    CollisionShape_Free(shape);
  }

  RigidBody_FreeImpl(self);
}

void RigidBody_ApplyForce (RigidBody* self, Vec3f* force) {
  if (RigidBody_IsChild(self))
    Fatal("RigidBody_ApplyForce: Not supported on children.");

  btRigidBody* rigidBody = self->handle;
  rigidBody->applyCentralForce(Vec3f_ToBullet(force));
  rigidBody->activate();
}

void RigidBody_ApplyTorque (RigidBody* self, Vec3f* torque) {
  if (RigidBody_IsChild(self))
    Fatal("RigidBody_ApplyTorque: Not supported on children.");

  btRigidBody* rigidBody = self->handle;
  rigidBody->applyTorque(Vec3f_ToBullet(torque));
  rigidBody->activate();
}

void RigidBody_Attach (RigidBody* parent, RigidBody* child, Vec3f* pos, Quat* rot) {
  if (RigidBody_IsChild(parent))
    Fatal("RigidBody_Attach: Recursive attachment is not supported. Parent is already attached to something.");
  if (RigidBody_IsCompound(child))
    Fatal("RigidBody_Attach: Child is already part of a compound.");
  if (parent == child)
    Fatal("RigidBody_Attach: Cannot attach object to itself.");
  if (child->physics)
    Fatal("RigidBody_Attach: Child has not been removed from physics.");

  //Convert parent to a compound
  if (!RigidBody_IsParent(parent)) {
    btRigidBody*    pBody  = parent->handle;
    CollisionShape* pShape = CollisionShape_GetCached(parent->iShape);
    btTransform     pTrans = btTransform::getIdentity();

    CollisionShape shapeDef = {};
    shapeDef.scale = 1.0f;
    shapeDef.type  = CollisionShapeType_Compound;
    CollisionShape* cmpShape = CollisionShape_Create(shapeDef);

    cmpShape->compound.handle->addChildShape(pTrans, pShape->base.handle);
    pBody->setCollisionShape(cmpShape->compound.handle);
    parent->iCompound      = 0;
    parent->iCompoundShape = cmpShape->iShape;
    parent->parent         = parent;
    Physics_FlushCachedRigidBodyData(parent->physics, parent);
  }

  /* NOTE : Position is relative to the unscaled parent. */
  CollisionShape* pShape   = CollisionShape_GetCached(parent->iShape);
  CollisionShape* cmpShape = CollisionShape_GetCached(parent->iCompoundShape);
  CollisionShape* cShape   = CollisionShape_GetCached(child->iShape);
  btTransform     cTrans   = btTransform(Quat_ToBullet(rot), Vec3f_ToBullet(pos) * pShape->scale);

  //Insert child into the compound list
  child->parent = parent;
  child->next   = parent->next;
  parent->next  = child;

  //Add child to the compound
  cmpShape->compound.handle->addChildShape(cTrans, cShape->base.handle);
  child->iCompound      = cmpShape->compound.handle->getNumChildShapes() - 1;
  child->iCompoundShape = cmpShape->iShape;
}

void RigidBody_Detach (RigidBody* parent, RigidBody* child) {
  if (child->parent != parent)
    Fatal("RigidBody_Detach: Child is not attached to parent.");
  if (child == parent)
    Fatal("RigidBody_Detach: Cannot detach object from itself.");

  btRigidBody*     pBody       = parent->handle;
  btRigidBody*     cBody       = child->handle;
  btCompoundShape* compound    = (btCompoundShape*) pBody->getCollisionShape();
  btTransform      cLocalTrans = compound->getChildTransform(child->iCompound);

  //Remove child from the compound
  { /* HACK : btCompoundShape does a 'remove fast' internally. */
    int iLast = compound->getNumChildShapes() - 1;
    RigidBody* last = parent;
    while (last->iCompound != iLast) last = last->next;
    last->iCompound = child->iCompound;
  }
  compound->removeChildShapeByIndex(child->iCompound);
  child->iCompound      = -1;
  child->iCompoundShape = -1;

  //Remove child from the compound list
  RigidBody* prev = parent;
  while (prev->next != child) prev = prev->next;
  prev->next    = child->next;
  child->next   = 0;
  child->parent = 0;

  //Apply current position, rotation, and velocity
  btTransform  cTrans = pBody->getWorldTransform() * cLocalTrans;
  btVector3    cVel   = pBody->getVelocityInLocalPoint(cLocalTrans.getOrigin());
  cBody->setWorldTransform(cTrans);
  cBody->setLinearVelocity(cVel);

  //Convert parent to single object
  if (parent->next == 0) {
    CollisionShape* pShape = CollisionShape_GetCached(parent->iShape);

    compound->removeChildShapeByIndex(parent->iCompound);
    pBody->setCollisionShape(pShape->base.handle);
    parent->iCompound      = -1;
    parent->iCompoundShape = -1;
    parent->parent         = 0;
    Physics_FlushCachedRigidBodyData(parent->physics, parent);
  } else {
    compound->recalculateLocalAabb();
  }
}

/* TODO: Should scale be inverted? */
Matrix* RigidBody_GetToLocalMatrix (RigidBody* self) {
  float       scale     = RigidBody_GetScale(self);
  btTransform transform = RigidBody_GetWorldTransform(self).inverse();
  Matrix_FromTransform(&transform, &self->mat, scale);
  return &self->mat;
}

Matrix* RigidBody_GetToWorldMatrix (RigidBody* self) {
  float       scale     = RigidBody_GetScale(self);
  btTransform transform = RigidBody_GetWorldTransform(self);
  Matrix_FromTransform(&transform, &self->mat, scale);
  return &self->mat;
}

void RigidBody_SetCollidable (RigidBody* self, bool collidable) {
  typedef btCollisionObject::CollisionFlags Flags;
  RigidBody_SetFlag(self, Flags::CF_NO_CONTACT_RESPONSE, !collidable);

  if (self->physics) {
    btRigidBody*       rigidBody = self->handle;
    btBroadphaseProxy* proxy     = rigidBody->getBroadphaseHandle();

    proxy->m_collisionFilterGroup = collidable ? self->collisionGroup : 0;
  }
}

void RigidBody_SetCollisionGroup (RigidBody* self, int group) {
  btRigidBody* rigidBody = self->handle;
  self->collisionGroup = group;

  if (self->physics) {
    typedef btCollisionObject::CollisionFlags Flags;
    btBroadphaseProxy* proxy = rigidBody->getBroadphaseHandle();

    int  flags      = rigidBody->getCollisionFlags();
    bool collidable = !HAS_FLAG(flags, Flags::CF_NO_CONTACT_RESPONSE);
    proxy->m_collisionFilterGroup = collidable ? self->collisionGroup : 0;
  }
}

void RigidBody_SetCollisionMask (RigidBody* self, int mask) {
  btRigidBody* rigidBody = self->handle;
  self->collisionMask = mask;

  if (self->physics) {
    btBroadphaseProxy* proxy = rigidBody->getBroadphaseHandle();
    proxy->m_collisionFilterMask = mask;
  }
}

void RigidBody_SetDrag (RigidBody* self, float linear, float angular) {
  btRigidBody* rigidBody = self->handle;
  rigidBody->setDamping(linear, angular);
}

void RigidBody_SetFriction (RigidBody* self, float friction) {
  btRigidBody* rigidBody = self->handle;
  rigidBody->setFriction(friction);
}

void RigidBody_SetKinematic (RigidBody* self, bool kinematic) {
  typedef btCollisionObject::CollisionFlags Flags;
  RigidBody_SetFlag(self, Flags::CF_KINEMATIC_OBJECT, kinematic);
}

float RigidBody_GetMass (RigidBody* self) {
  return self->mass;
}

void RigidBody_SetMass (RigidBody* self, float mass) {
  self->mass = mass;
  RigidBody_RecalculateInertia(self);
}

void RigidBody_GetPos (RigidBody* self, Vec3f* pos) {
  btTransform transform = RigidBody_GetWorldTransform(self);
  *pos = Vec3f_FromBullet(transform.getOrigin());
}

void RigidBody_GetPosLocal (RigidBody* self, Vec3f* pos) {
  if (!RigidBody_IsChild(self))
    Fatal("RigidBody_GetPosLocal: Only allowed on children.");

  float            pScale   = RigidBody_GetScale(self->parent);
  CollisionShape*  cmpShape = CollisionShape_GetCached(self->iCompoundShape);
  btCompoundShape* compound = cmpShape->compound.handle;
  btTransform& transform = compound->getChildTransform(self->iCompound);
  *pos = Vec3f_FromBullet((1.0f / pScale) * transform.getOrigin());
}

void RigidBody_SetPos (RigidBody* self, Vec3f* pos) {
  if (RigidBody_IsChild(self))
    Fatal("RigidBody_SetPos: Not allowed on children.");

  btRigidBody* rigidBody = self->handle;
  btTransform  transform = rigidBody->getWorldTransform();
  transform.setOrigin(Vec3f_ToBullet(pos));
  rigidBody->setWorldTransform(transform);
}

void RigidBody_SetPosLocal (RigidBody* self, Vec3f* pos) {
  if (!RigidBody_IsChild(self))
    Fatal("RigidBody_SetPosLocal: Only allowed on children.");

  float            pScale   = RigidBody_GetScale(self->parent);
  CollisionShape*  cmpShape = CollisionShape_GetCached(self->iCompoundShape);
  btCompoundShape* compound = cmpShape->compound.handle;
  btTransform transform = compound->getChildTransform(self->iCompound);
  transform.setOrigin(pScale * Vec3f_ToBullet(pos));
  compound->updateChildTransform(self->iCompound, transform);
}

void RigidBody_GetBoundingBox (RigidBody* self, Box3f* box) {
  CollisionShape* shape     = CollisionShape_GetCached(self->iShape);
  btTransform     transform = RigidBody_GetWorldTransform(self);

  btVector3 min, max;
  shape->base.handle->getAabb(transform, min, max);
  box->lower = Vec3f_FromBullet(min);
  box->upper = Vec3f_FromBullet(max);
}

void RigidBody_GetBoundingBoxCompound (RigidBody* self, Box3f* box) {
  if (!RigidBody_IsParent(self))
    Fatal("RigidBody_GetBoundingBoxCompound: Only enabled for parents.");

  CollisionShape* cmpShape  = CollisionShape_GetCached(self->iCompoundShape);
  btTransform     transform = RigidBody_GetWorldTransform(self);

  btVector3 min, max;
  cmpShape->base.handle->getAabb(transform, min, max);
  box->lower = Vec3f_FromBullet(min);
  box->upper = Vec3f_FromBullet(max);
}

void RigidBody_GetBoundingBoxLocal (RigidBody* self, Box3f* box) {
  CollisionShape* shape     = CollisionShape_GetCached(self->iShape);
  btTransform     transform = btTransform::getIdentity();

  btVector3 min, max;
  shape->base.handle->getAabb(transform, min, max);
  box->lower = Vec3f_FromBullet(min);
  box->upper = Vec3f_FromBullet(max);
}

void RigidBody_GetBoundingBoxLocalCompound (RigidBody* self, Box3f* box) {
  if (!RigidBody_IsParent(self))
    Fatal("RigidBody_GetBoundingBoxLocalCompound: Only enabled for parents.");

  CollisionShape* cmpShape  = CollisionShape_GetCached(self->iCompoundShape);
  btTransform     transform = btTransform::getIdentity();

  btVector3 min, max;
  cmpShape->base.handle->getAabb(transform, min, max);
  box->lower = Vec3f_FromBullet(min);
  box->upper = Vec3f_FromBullet(max);
}

float RigidBody_GetBoundingRadius (RigidBody* self) {
  CollisionShape* shape = CollisionShape_GetCached(self->iShape);

  float radius = 0;
  switch (shape->type) {
    /* NOTE : btSphereShape doesn't override the virtual function
              btCollisionShape::getBoundingSphere so it falls back to
              calculating a sphere that encompases the local bounding box that
              encompases the original sphere. Lovely. */
    case CollisionShapeType_Sphere: {
      radius = shape->scale * shape->sphere.radius;
      break;
    }

    default: {
      btVector3 center;
      shape->base.handle->getBoundingSphere(center, radius);
      radius += center.length();
      break;
    }
  }
  return radius;
}

float RigidBody_GetBoundingRadiusCompound (RigidBody* self) {
  if (!RigidBody_IsParent(self))
    Fatal("RigidBody_GetBoundingBoxCompound: Only enabled for parents.");

  CollisionShape* cmpShape = CollisionShape_GetCached(self->iCompoundShape);
  float radius;
  btVector3 center;
  cmpShape->base.handle->getBoundingSphere(center, radius);
  radius += center.length();
  return radius;
}

RigidBody* RigidBody_GetParentBody (RigidBody* self) {
  return self->parent == self ? 0 : self->parent;
}

void RigidBody_SetRestitution (RigidBody* self, float restitution) {
  btRigidBody* rigidBody = self->handle;
  rigidBody->setRestitution(restitution);
}

void RigidBody_GetRot (RigidBody* self, Quat* rot) {
  btTransform transform = RigidBody_GetWorldTransform(self);
  *rot = Quat_FromBullet(transform.getRotation());
}

void RigidBody_GetRotLocal (RigidBody* self, Quat* rot) {
  if (!RigidBody_IsChild(self))
    Fatal("RigidBody_GetRotLocal: Only allowed on children.");

  CollisionShape*  cmpShape = CollisionShape_GetCached(self->iCompoundShape);
  btCompoundShape* compound = cmpShape->compound.handle;
  btTransform& transform = compound->getChildTransform(self->iCompound);
  *rot = Quat_FromBullet(transform.getRotation());
}

void RigidBody_SetRot (RigidBody* self, Quat* rot) {
  if (RigidBody_IsChild(self))
    Fatal("RigidBody_SetRot: Not allowed on children.");

  btRigidBody* rigidBody = self->handle;
  btTransform  transform = rigidBody->getWorldTransform();
  transform.setRotation(Quat_ToBullet(rot));
  rigidBody->setWorldTransform(transform);
}

void RigidBody_SetRotLocal (RigidBody* self, Quat* rot) {
  if (!RigidBody_IsChild(self))
    Fatal("RigidBody_SetRotLocal: Only allowed on children.");

  CollisionShape*  cmpShape = CollisionShape_GetCached(self->iCompoundShape);
  btCompoundShape* compound = cmpShape->compound.handle;
  btTransform transform = compound->getChildTransform(self->iCompound);
  transform.setRotation(Quat_ToBullet(rot));
  compound->updateChildTransform(self->iCompound, transform);
}

float RigidBody_GetScale (RigidBody* self) {
  /* NOTE : Only uniform scale is supported. */
  CollisionShape* shape = CollisionShape_GetCached(self->iShape);
  return shape->scale;
}

void RigidBody_SetScale (RigidBody* self, float scale) {
  /* NOTE : Only uniform scale is supported. */

  /* NOTE : Since scale is not inherited and the Bullet API for scaling a
            compound shape sucks, when a parent object is rescaled we only
            rescale its individual shape. */

  /* NOTE : We scale the positions of children to maintain their position
            relative to the parent. */

  btRigidBody* rigidBody = self->handle;

  CollisionShape shapeDef = *CollisionShape_GetCached(self->iShape);
  float scaleRatio = scale / shapeDef.scale;
  shapeDef.scale = scale;
  CollisionShape* shape = CollisionShape_Create(shapeDef);

  if (!RigidBody_IsCompound(self)) {
    rigidBody->setCollisionShape(shape->base.handle);
    self->iShape = shape->iShape;
    RigidBody_RecalculateInertia(self);

  } else {
    CollisionShape*  cmpShape = CollisionShape_GetCached(self->iCompoundShape);
    btCompoundShape* compound = cmpShape->compound.handle;

    //Children keep the same relative position
    if (RigidBody_IsParent(self)) {
      RigidBody* child = self->next;
      while (child) {
        btTransform& cTrans = compound->getChildTransform(child->iCompound);
        cTrans.getOrigin() *= scaleRatio;
        compound->updateChildTransform(child->iCompound, cTrans, false);
        child = child->next;
      }
    }

    btTransform transform = compound->getChildTransform(self->iCompound);
    compound->removeChildShapeByIndex(self->iCompound);
    compound->addChildShape(transform, shape->base.handle);
    int iLast = compound->getNumChildShapes() - 1;
    { /* HACK : btCompoundShape does a 'remove fast' internally. */
      RigidBody* last = self->parent;
      while (last->iCompound != iLast) last = last->next;
      last->iCompound = self->iCompound;
    }
    self->iCompound = iLast;
    self->iShape    = shape->iShape;

    /* NOTE: removeChildShape calls recalculateLocalAabb but
             removeChildShapeByIndex does not. addChildShape updates the AABB
             with the new shape only, it is not a full recalculation. So, if our
             new scalle is smaller than our old scale the AABB won't shrink as
             expected. Thus, we need to force a full recalculate. */
    compound->recalculateLocalAabb();
    if (RigidBody_IsParent(self))
      RigidBody_RecalculateInertia(self);
  }
}

void RigidBody_SetSleepThreshold (RigidBody* self, float linear, float angular) {
  btRigidBody* rigidBody = self->handle;
  rigidBody->setSleepingThresholds(linear, angular);
}

float RigidBody_GetSpeed (RigidBody* self) {
  if (RigidBody_IsChild(self))
    Fatal("RigidBody_GetSpeed: Not supported on children.");

  btRigidBody* rigidBody = self->handle;
  float speed = rigidBody->getLinearVelocity().length();
  return speed;
}

void RigidBody_GetVelocity (RigidBody* self, Vec3f* velocity) {
  if (RigidBody_IsChild(self))
    Fatal("RigidBody_GetVelocity: Not supported on children.");

  btRigidBody* rigidBody = self->handle;
  *velocity = Vec3f_FromBullet(rigidBody->getLinearVelocity());
}

void RigidBody_GetVelocityA (RigidBody* self, Vec3f* velocityA) {
  if (RigidBody_IsChild(self))
    Fatal("RigidBody_GetVelocityA: Not supported on children.");

  btRigidBody* rigidBody = self->handle;
  *velocityA = Vec3f_FromBullet(rigidBody->getAngularVelocity());
}

/* NOTE : We assume the parent has identity position and rotation within the
          compound. */

/* NOTE : Detach could be made O(1) if a doubly linked list were used in place
          of the singly linked list. (Attach is O(1) in both cases.) */

/* NOTE : Free strategy: Freed parents will free all children. Children cannot
          be freed directly. Rigidbodies will not be removed from physics when
          freed, this must be done manually. */

/* NOTE : Coumans says btConvexHullShape is more efficient than
          btConvexPointCloudShape.
          https://pybullet.org/Bullet/phpBB3/viewtopic.php?t=3102 */

/* NOTE : btConvexHullShape duplicates the vertex positions.
          btConvexTriangleMeshShape does not, but the class comment says
          it's less efficient. */

/* NOTE : btShapeHull can be used to reduce the number of hull vertices.
          http://www.bulletphysics.org/mediawiki-1.5.8/index.php?title=BtShapeHull_vertex_reduction_utility */

/* NOTE : btConvexHull, btCompoundShape, and btUniformScaling all have shit
          implementations and APIs. I'm quite sure they are hurting performance
          and generally being obtuse pains in the dick. Josh, when you get
          annoyed with Bullet and start trying to optimize it, I'd strongly
          consider implementing these from scratch. */

/* TODO : Pool allocate Bullet objects if possible. Does Bullet have a pooling
          mechanism? Use our own arrays? */

/* TODO : btUniformScalingShape::getAabb looks like it might be quite slow. It's
          not clear why it doesn't simply forward the call to the wrapped shape
          and scale the result. */


text:
#include "Hash.h"

/* --- Fowler–Noll–Vo ------------------------------------------------------- */

uint32 Hash_FNV32 (void const* buf, int len) {
  uchar const* curr = (uchar const*)buf;
  uchar const* end = curr + len;
  uint32 self = UINT32_C(2166136261);
  while (curr < end) {
    self ^= (uint32)*curr++;
    self *= UINT32_C(16777619);
  }
  return self;
}

uint64 Hash_FNV64 (void const* buf, int len) {
  uchar const* curr = (uchar const*)buf;
  uchar const* end = curr + len;
  uint64 self = UINT64_C(14695981039346656037);
  while (curr < end) {
    self ^= (uint64)*curr++;
    self *= UINT64_C(1099511628211);
  }
  return self;
}

uint32 Hash_FNVStr32 (cstr s) {
  uint32 self = UINT32_C(2166136261);
  while (*s) {
    self ^= (uint32)*s++;
    self *= UINT32_C(16777619);
  }
  return self;
}

uint64 Hash_FNVStr64 (cstr s) {
  uint64 self = UINT64_C(14695981039346656037);
  while (*s) {
    self ^= (uint64)*s++;
    self *= UINT64_C(1099511628211);
  }
  return self;
}

uint64 Hash_FNV64_Init () {
  return UINT64_C(14695981039346656037);
}

uint64 Hash_FNV64_Incremental (uint64 self, void const* buf, int len) {
  uchar const* curr = (uchar const*)buf;
  uchar const* end = curr + len;
  while (curr < end) {
    self ^= (uint64)*curr++;
    self *= UINT64_C(1099511628211);
  }
  return self;
}

/* --- Murmur3 -------------------------------------------------------------- */

inline static uint32 rotl32 (uint32 x, int8 r) {
  return (x << r) | (x >> (32 - r));
}

inline static uint32 fmix32 (uint32 h) {
  h ^= h >> 16; h *= 0x85EBCA6B;
  h ^= h >> 13; h *= 0xC2B2AE35;
  h ^= h >> 16;
  return h;
}

uint32 Hash_Murmur3 (void const* key, int len) {
  uint8 const* data = (uint8 const*)key;
  uint32 h1 = 0xDEADBEEF;
  uint32 const c1 = 0xCC9E2D51;
  uint32 const c2 = 0x1B873593;

  const int nblocks = len / 4;
  uint32 const* blocks = (uint32 const*)(data + nblocks*4);

  for (int i = -nblocks; i; i++) {
    uint32 k1 = blocks[i];
    k1 *= c1;
    k1 = rotl32(k1, 15);
    k1 *= c2;

    h1 ^= k1;
    h1 = rotl32(h1, 13);
    h1 = h1 * 5 + 0xE6546B64;
  }

  uint8 const* tail = (uint8 const*)(data + nblocks*4);
  uint32 k1 = 0;

  switch (len & 3) {
    case 3: k1 ^= tail[2] << 16;
    case 2: k1 ^= tail[1] << 8;
    case 1: k1 ^= tail[0];
            k1 *= c1; k1 = rotl32(k1, 15); k1 *= c2; h1 ^= k1;
  };

  h1 ^= len;
  h1 = fmix32(h1);
  return h1;
}

/* --- XXHASH64 --------------------------------------------------------------
 *   https://github.com/Cyan4973/xxHash/blob/dev/xxhash.c
 * -------------------------------------------------------------------------- */

#define XXH_rotl32(x,r) ((x << r) | (x >> (32 - r)))
#define XXH_rotl64(x,r) ((x << r) | (x >> (64 - r)))

static const uint64 PRIME64_1 = 11400714785074694791ULL;
static const uint64 PRIME64_2 = 14029467366897019727ULL;
static const uint64 PRIME64_3 =  1609587929392839161ULL;
static const uint64 PRIME64_4 =  9650029242287828579ULL;
static const uint64 PRIME64_5 =  2870177450012600261ULL;

static uint64 XXH64_round (uint64 acc, uint64 val) {
  acc += val * PRIME64_2;
  acc  = XXH_rotl64(acc, 31);
  acc *= PRIME64_1;
  return acc;
}

static uint64 XXH64_mergeRound (uint64 acc, uint64 val) {
  val  = XXH64_round(0, val);
  acc ^= val;
  acc  = acc * PRIME64_1 + PRIME64_4;
  return acc;
}

uint64 Hash_XX64 (void const* buf, int len, uint64 seed) {
  uint8 const* p = (uint8 const*)buf;
  uint8 const* end = p + len;
  uint64 hash;

  if (len >= 32) {
    uint8 const* const limit = end - 32;
    uint64 v1 = seed + PRIME64_1 + PRIME64_2;
    uint64 v2 = seed + PRIME64_2;
    uint64 v3 = seed + 0;
    uint64 v4 = seed - PRIME64_1;

    do {
      v1 = XXH64_round(v1, *(uint64 const*)p); p += 8;
      v2 = XXH64_round(v2, *(uint64 const*)p); p += 8;
      v3 = XXH64_round(v3, *(uint64 const*)p); p += 8;
      v4 = XXH64_round(v4, *(uint64 const*)p); p += 8;
    } while (p <= limit);

    hash =
      XXH_rotl64(v1,  1) +
      XXH_rotl64(v2,  7) +
      XXH_rotl64(v3, 12) +
      XXH_rotl64(v4, 18);

    hash = XXH64_mergeRound(hash, v1);
    hash = XXH64_mergeRound(hash, v2);
    hash = XXH64_mergeRound(hash, v3);
    hash = XXH64_mergeRound(hash, v4);
  } else {
    hash = seed + PRIME64_5;
  }

  hash += (uint64)len;

  while (p + 8 <= end) {
    uint64 const k1 = XXH64_round(0, *(uint64 const*)p);
    hash ^= k1;
    hash = XXH_rotl64(hash, 27) * PRIME64_1 + PRIME64_4;
    p += 8;
  }

  if (p + 4 <= end) {
    hash ^= (uint64)(*(uint32*)p) * PRIME64_1;
    hash = XXH_rotl64(hash, 23) * PRIME64_2 + PRIME64_3;
    p += 4;
  }

  while (p < end) {
    hash ^= (*p) * PRIME64_5;
    hash = XXH_rotl64(hash, 11) * PRIME64_1;
    p++;
  }

  hash ^= hash >> 33; hash *= PRIME64_2;
  hash ^= hash >> 29; hash *= PRIME64_3;
  hash ^= hash >> 32;
  return hash;
}


text:
#include "ArrayList.h"
#include "Box3.h"
#include "Hash.h"
#include "HashGrid.h"
#include "MemPool.h"
#include "PhxMemory.h"
#include "PhxMath.h"
#include "Profiler.h"

#define OPT_SPARSEUPDATE 1

struct HashGridElem {
  uint64 version;
  void* object;
  int32 lower[3];
  int32 upper[3];
};

struct HashGridCell {
  uint64 version;
  ArrayList(HashGridElem*, elems);
};

struct HashGrid {
  uint64 version;
  HashGridCell* cells;
  MemPool* elemPool;
  uint32 cellCount;
  float cellSize;
  uint32 mask;
  ArrayList(void*, results);
};

HashGrid* HashGrid_Create (float cellSize, uint32 cellCount) {
  uint32 logCount = 0;
  while (cellCount > 1) {
    cellCount /= 2;
    logCount++;
  }
  cellCount = 1 << logCount;

  HashGrid* self = MemNew(HashGrid);
  self->version = 0;
  self->cells = MemNewArrayZero(HashGridCell, cellCount);
  self->elemPool = MemPool_Create(sizeof(HashGridElem), 0x1000U / sizeof(HashGridElem));
  self->cellCount = cellCount;
  self->cellSize = cellSize;
  self->mask = (1 << logCount) - 1;
  ArrayList_Init(self->results);
  for (uint32 i = 0; i < cellCount; ++i)
    ArrayList_Init(self->cells[i].elems);
  return self;
}

void HashGrid_Free (HashGrid* self) {
  ArrayList_Free(self->results);
  for (uint32 i = 0; i < self->cellCount; ++i)
    ArrayList_Free(self->cells[i].elems);
  MemPool_Free(self->elemPool);
  MemFree(self->cells);
  MemFree(self);
}

inline static HashGridCell* HashGrid_GetCell (HashGrid* self, int32 x, int32 y, int32 z) {
  int32 p[3] = { x, y, z };
  uint64 hash = Hash_XX64(p, sizeof(p), 0x0ULL);
  return self->cells + (hash & self->mask);
}

static void HashGrid_AddElem (HashGrid* self, HashGridElem* elem) {
  self->version++;
  for (int32 x = elem->lower[0]; x <= elem->upper[0]; ++x)
  for (int32 y = elem->lower[1]; y <= elem->upper[1]; ++y)
  for (int32 z = elem->lower[2]; z <= elem->upper[2]; ++z) {
    HashGridCell* cell = HashGrid_GetCell(self, x, y, z);

    /* If cell version is out-of-date, we need to insert. Otherwise, we have
     * already inserted into this cell (e.g., we have encountered a modulus
     * hash collision) and should not insert a duplicate. */
    if (cell->version != self->version) {
      cell->version = self->version;
      ArrayList_Append(cell->elems, elem);
    }
  }
}

static void HashGrid_RemoveElem (HashGrid* self, HashGridElem* elem) {
  self->version++;
  for (int32 x = elem->lower[0]; x <= elem->upper[0]; ++x)
  for (int32 y = elem->lower[1]; y <= elem->upper[1]; ++y)
  for (int32 z = elem->lower[2]; z <= elem->upper[2]; ++z) {
    HashGridCell* cell = HashGrid_GetCell(self, x, y, z);
    if (cell->version != self->version) {
      cell->version = self->version;
      ArrayList_RemoveFast(cell->elems, elem);
    }
  }
}

inline static int32 HashGrid_ToLocal (HashGrid* self, float x) {
  return (int32)Floor(x / self->cellSize);
}

HashGridElem* HashGrid_Add (HashGrid* self, void* object, Box3f const* box) {
  HashGridElem* elem = (HashGridElem*)MemPool_Alloc(self->elemPool);
  elem->object = object;
  elem->lower[0] = HashGrid_ToLocal(self, box->lower.x);
  elem->lower[1] = HashGrid_ToLocal(self, box->lower.y);
  elem->lower[2] = HashGrid_ToLocal(self, box->lower.z);
  elem->upper[0] = HashGrid_ToLocal(self, box->upper.x);
  elem->upper[1] = HashGrid_ToLocal(self, box->upper.y);
  elem->upper[2] = HashGrid_ToLocal(self, box->upper.z);
  HashGrid_AddElem(self, elem);
  return elem;
}

void HashGrid_Clear (HashGrid* self) {
  self->version = 0;
  for (uint32 i = 0; i < self->cellCount; ++i) {
    ArrayList_Clear(self->cells[i].elems);
    self->cells[i].version = 0;
  }
  MemPool_Clear(self->elemPool);
  ArrayList_Clear(self->results);
}

void HashGrid_Remove (HashGrid* self, HashGridElem* elem) {
  HashGrid_RemoveElem(self, elem);
  MemPool_Dealloc(self->elemPool, elem);
}

void HashGrid_Update (HashGrid* self, HashGridElem* elem, Box3f const* box) {
  FRAME_BEGIN;
  int32 lower[3] = {
    HashGrid_ToLocal(self, box->lower.x),
    HashGrid_ToLocal(self, box->lower.y),
    HashGrid_ToLocal(self, box->lower.z),
  };

  int32 upper[3] = {
    HashGrid_ToLocal(self, box->upper.x),
    HashGrid_ToLocal(self, box->upper.y),
    HashGrid_ToLocal(self, box->upper.z),
  };

  if (lower[0] == elem->lower[0] && upper[0] == elem->upper[0] &&
      lower[1] == elem->lower[1] && upper[1] == elem->upper[1] &&
      lower[2] == elem->lower[2] && upper[2] == elem->upper[2]) {
    FRAME_END;
    return;
  }

#if OPT_SPARSEUPDATE
  int32 lowerUnion[3] = {
    Min(lower[0], elem->lower[0]),
    Min(lower[1], elem->lower[1]),
    Min(lower[2], elem->lower[2]),
  };

  int32 upperUnion[3] = {
    Max(upper[0], elem->upper[0]),
    Max(upper[1], elem->upper[1]),
    Max(upper[2], elem->upper[2]),
  };

  uint64 vRemove = ++self->version;
  uint64 vAdd = ++self->version;

  /* TODO OPT : Can skip entire ranges rather than by splitting the inPrev and
   *            inCurr computation along each axis. */
  for (int32 x = lowerUnion[0]; x <= upperUnion[0]; ++x)
  for (int32 y = lowerUnion[1]; y <= upperUnion[1]; ++y)
  for (int32 z = lowerUnion[2]; z <= upperUnion[2]; ++z) {
    bool inPrev =
      elem->lower[0] <= x &&
      elem->lower[1] <= y &&
      elem->lower[2] <= z &&
      x <= elem->upper[0] &&
      y <= elem->upper[1] &&
      z <= elem->upper[2];

    bool inCurr =
      lower[0] <= x &&
      lower[1] <= y &&
      lower[2] <= z &&
      x <= upper[0] &&
      y <= upper[1] &&
      z <= upper[2];

    /* Early out: cell is part of both previous and new bounding box, no change
     * required. */
    if (inPrev && inCurr)
      continue;

    HashGridCell* cell = HashGrid_GetCell(self, x, y, z);

    /* Early out: cell has already had elem inserted, no update required. */
    if (cell->version == vAdd)
      continue;

    /* Early out: cell has already had elem removed and this is not part of the
     * insertion frontier. */
    if (cell->version == vRemove && inPrev)
      continue;

    /* inPrev but not inCurr -> remove elem and mark cell as having removed. */
    if (inPrev) {
      ArrayList_RemoveFast(cell->elems, elem);
      cell->version = vRemove;
      continue;
    }

    /* Final case: insertion frontier. We must add the elem to this cell. */
    if (cell->version != vRemove)
      ArrayList_RemoveFast(cell->elems, elem);
    ArrayList_Append(cell->elems, elem);
    cell->version = vAdd;
  }
  elem->lower[0] = lower[0];
  elem->lower[1] = lower[1];
  elem->lower[2] = lower[2];
  elem->upper[0] = upper[0];
  elem->upper[1] = upper[1];
  elem->upper[2] = upper[2];
#else
  HashGrid_RemoveElem(self, elem);
  elem->lower[0] = lower[0];
  elem->lower[1] = lower[1];
  elem->lower[2] = lower[2];
  elem->upper[0] = upper[0];
  elem->upper[1] = upper[1];
  elem->upper[2] = upper[2];
  HashGrid_AddElem(self, elem);
#endif

  FRAME_END;
}

void** HashGrid_GetResults (HashGrid* self) {
  return ArrayList_GetData(self->results);
}

int HashGrid_QueryBox (HashGrid* self, Box3f const* box) {
  ArrayList_Clear(self->results);
  self->version++;

  int32 lower[3] = {
    HashGrid_ToLocal(self, box->lower.x),
    HashGrid_ToLocal(self, box->lower.y),
    HashGrid_ToLocal(self, box->lower.z),
  };

  int32 upper[3] = {
    HashGrid_ToLocal(self, box->upper.x),
    HashGrid_ToLocal(self, box->upper.y),
    HashGrid_ToLocal(self, box->upper.z),
  };

  for (int32 x = lower[0]; x <= upper[0]; ++x)
  for (int32 y = lower[1]; y <= upper[1]; ++y)
  for (int32 z = lower[2]; z <= upper[2]; ++z) {
    HashGridCell* cell = HashGrid_GetCell(self, x, y, z);
    if (cell->version != self->version) {
      cell->version = self->version;
      for (int32 i = 0; i < ArrayList_GetSize(cell->elems); ++i) {
        HashGridElem* elem = ArrayList_Get(cell->elems, i);

        /* We must use per-element versioning here due to the fact that the same
         * element may exist in many distinct cells, hence we might encounter
         * it many times. We should only report it once regardless. */
        if (elem->version != self->version) {
          elem->version = self->version;
          ArrayList_Append(self->results, elem->object);
        }
      }
    }
  }

  return ArrayList_GetSize(self->results);
}

int HashGrid_QueryPoint (HashGrid* self, Vec3f const* p) {
  /* Since a point query is restricted to a single cell, we don't need to use
   * versioning here. */
  ArrayList_Clear(self->results);

  HashGridCell* cell = HashGrid_GetCell(self,
    HashGrid_ToLocal(self, p->x),
    HashGrid_ToLocal(self, p->y),
    HashGrid_ToLocal(self, p->z));

  for (int32 i = 0; i < ArrayList_GetSize(cell->elems); ++i) {
    HashGridElem* elem = ArrayList_Get(cell->elems, i);
    ArrayList_Append(self->results, elem->object);
  }

  return ArrayList_GetSize(self->results);
}


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

const CubeFace CubeFace_PX = GL_TEXTURE_CUBE_MAP_POSITIVE_X;
const CubeFace CubeFace_NX = GL_TEXTURE_CUBE_MAP_NEGATIVE_X;
const CubeFace CubeFace_PY = GL_TEXTURE_CUBE_MAP_POSITIVE_Y;
const CubeFace CubeFace_NY = GL_TEXTURE_CUBE_MAP_NEGATIVE_Y;
const CubeFace CubeFace_PZ = GL_TEXTURE_CUBE_MAP_POSITIVE_Z;
const CubeFace CubeFace_NZ = GL_TEXTURE_CUBE_MAP_NEGATIVE_Z;

static CubeFace const kFaces[] = {
  CubeFace_PX,
  CubeFace_NX,
  CubeFace_PY,
  CubeFace_NY,
  CubeFace_PZ,
  CubeFace_NZ,
};

CubeFace CubeFace_Get (int index) {
  return kFaces[index];
}


text:
#include "DataFormat.h"
#include "Draw.h"
#include "PhxMemory.h"
#include "Mesh.h"
#include "PixelFormat.h"
#include "RenderState.h"
#include "RenderTarget.h"
#include "Shader.h"
#include "PhxMath.h"
#include "Tex2D.h"
#include "TexFormat.h"
#include "Vec4.h"
#include "Vertex.h"

#define USE_CPU 0

#if USE_CPU

#include "BSP.h"
#include "Ray.h"
#include "RNG.h"

/* TODO : This is broken...BSP returning negatives & NaNs...?
 *        Probably would be better if we could do it this way, though, since
 *        we should see scalability gains due to BSP acceleration. This can
 *        also be made physically-accurate, unlike the GPU version. */
void Mesh_ComputeAO (Mesh* self, float radius) {
  int vertexCount = Mesh_GetVertexCount(self);
  Vertex* vertexData = Mesh_GetVertexData(self);

  BSP* bsp = BSP_Create(self);
  RNG* rng = RNG_Create(vertexCount);

  const int kSamples = 128;
  Ray ray;
  ray.tMin = 0.0f;
  ray.tMax = 1e6f;
  float t;
  for (int i = 0; i < vertexCount; ++i) {
    Vertex* v = vertexData + i;
    ray.p = Vec3f_Add(v->p, Vec3f_Muls(v->n, 0.01f));
    float occlusion = 0;
    for (int j = 0; j < kSamples; ++j) {
      /* NOTE : Ignoring the projected area differential factor here. Could
       *        easily importance sample if necessary:
       *          Z = rng:getUniform();
       *          D = sqrt(1.0 - z*z) * Normalize(Reject(D, N)) */
      RNG_GetDir3(rng, &ray.dir);
      ray.dir = Vec3f_Muls(ray.dir, Sign(Vec3f_Dot(ray.dir, v->n)));
      if (BSP_IntersectRay(bsp, &ray, &t))
        occlusion += Exp(-t / radius);
    }
    v->uv.x = 1.0f - occlusion / (float)kSamples;
  }

  BSP_Free(bsp);
  RNG_Free(rng);
}

#else

/* TODO : Needs to be asymptotically faster. N^2 currently. */
void Mesh_ComputeAO (Mesh* self, float radius) {
  int indexCount = Mesh_GetIndexCount(self);
  int vertexCount = Mesh_GetVertexCount(self);
  int* indexData = Mesh_GetIndexData(self);
  Vertex* vertexData = Mesh_GetVertexData(self);

  int sDim = (int)Ceil(Sqrt((double)(indexCount / 3)));
  int vDim = (int)Ceil(Sqrt((double)vertexCount));
  int surfels = sDim * sDim;
  int vertices = vDim * vDim;
  int bufSize = Max(surfels, vertices);

  Vec4f* pointBuffer = MemNewArray(Vec4f, bufSize);
  Vec4f* normalBuffer = MemNewArray(Vec4f, bufSize);

  /* Fill buffers with surfel data. */
  MemZero(pointBuffer, sizeof(Vec4f) * bufSize);
  MemZero(normalBuffer, sizeof(Vec4f) * bufSize);
  for (int i = 0; i < indexCount; i += 3) {
    Vertex const* v1 = vertexData + indexData[i + 0];
    Vertex const* v2 = vertexData + indexData[i + 1];
    Vertex const* v3 = vertexData + indexData[i + 2];

    Vec3f normal = Vec3f_Cross(Vec3f_Sub(v3->p, v1->p), Vec3f_Sub(v2->p, v1->p));
    float length = Vec3f_Length(normal);
    float area = 0.5f * length / Pi;
    if (Abs(length) > 1e-6)
      Vec3f_IDivs(&normal, length);
    else
      normal = Vec3f_Create(1, 0, 0);

    /* Face center. */ {
      Vec3f center = Vec3f_Divs(Vec3f_Add(v1->p, Vec3f_Add(v2->p, v3->p)), 3.0f);
      pointBuffer[i / 3] = Vec4f_Create(center.x, center.y, center.z, area);
    }

    normalBuffer[i / 3] = Vec4f_Create(normal.x, normal.y, normal.z, 0);
  }

  Tex2D* texSPoints = Tex2D_Create(sDim, sDim, TexFormat_RGBA32F);
  Tex2D* texSNormals = Tex2D_Create(sDim, sDim, TexFormat_RGBA32F);
  Tex2D_SetData(texSPoints, pointBuffer, PixelFormat_RGBA, DataFormat_Float);
  Tex2D_SetData(texSNormals, normalBuffer, PixelFormat_RGBA, DataFormat_Float);

  /* Fill buffers with vertex data. */
  MemZero(pointBuffer, sizeof(Vec4f) * bufSize);
  MemZero(normalBuffer, sizeof(Vec4f) * bufSize);
  for (int i = 0; i < vertexCount; ++i) {
    Vertex const* v = vertexData + i;
    pointBuffer[i] = Vec4f_Create(v->p.x, v->p.y, v->p.z, 0);
    normalBuffer[i] = Vec4f_Create(v->n.x, v->n.y, v->n.z, 0);
  }

  Tex2D* texVPoints = Tex2D_Create(vDim, vDim, TexFormat_RGBA32F);
  Tex2D* texVNormals = Tex2D_Create(vDim, vDim, TexFormat_RGBA32F);
  Tex2D_SetData(texVPoints, pointBuffer, PixelFormat_RGBA, DataFormat_Float);
  Tex2D_SetData(texVNormals, normalBuffer, PixelFormat_RGBA, DataFormat_Float);
  MemFree(pointBuffer);
  MemFree(normalBuffer);

  Tex2D* texOutput = Tex2D_Create(vDim, vDim, TexFormat_R32F);

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

  RenderState_PushAllDefaults();
  RenderTarget_PushTex2D(texOutput);
    Shader_Start(shader);
    Shader_SetInt("sDim", sDim);
    Shader_SetFloat("radius", radius);
    Shader_SetTex2D("sPointBuffer", texSPoints);
    Shader_SetTex2D("sNormalBuffer", texSNormals);
    Shader_SetTex2D("vPointBuffer", texVPoints);
    Shader_SetTex2D("vNormalBuffer", texVNormals);
    Draw_Rect(-1, -1, 2, 2);
    Shader_Stop(shader);
  RenderTarget_Pop();
  RenderState_PopAll();

  /* Write result to mesh. */ {
    float* result = MemNewArray(float, vDim * vDim);
    Tex2D_GetData(texOutput, result, PixelFormat_Red, DataFormat_Float);
    for (int i = 0; i < vertexCount; ++i)
      vertexData[i].uv.x = result[i];
    MemFree(result);
  }

  Tex2D_Free(texOutput);
  Tex2D_Free(texSPoints);
  Tex2D_Free(texSNormals);
  Tex2D_Free(texVPoints);
  Tex2D_Free(texVNormals);
}

#endif

/* SDF-Based Occlusion. */
void Mesh_ComputeOcclusion (Mesh* self, Tex3D* sdf, float radius) {
  int vertexCount = Mesh_GetVertexCount(self);
  Vertex* vertexData = Mesh_GetVertexData(self);

  int vDim = (int)Ceil(Sqrt((double)vertexCount));
  Tex2D* texPoints = Tex2D_Create(vDim, vDim, TexFormat_RGBA32F);
  Tex2D* texOutput = Tex2D_Create(vDim, vDim, TexFormat_R32F);

  /* Fill input texture with vertex positions. */ {
    Vec3f* pointBuffer = MemNewArray(Vec3f, vDim * vDim);
    for (int i = 0; i < vertexCount; ++i)
      pointBuffer[i] = vertexData[i].p;
    Tex2D_SetData(texPoints, pointBuffer, PixelFormat_RGB, DataFormat_Float);
    MemFree(pointBuffer);
  }

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

  /* Compute. */ {
    RenderState_PushAllDefaults();
    RenderTarget_PushTex2D(texOutput);
      Shader_Start(shader);
      Shader_SetFloat("radius", radius);
      Shader_SetTex2D("points", texPoints);
      Shader_SetTex3D("sdf", sdf);
      Draw_Rect(-1, -1, 2, 2);
      Shader_Stop(shader);
    RenderTarget_Pop();
    RenderState_PopAll();
  }

  /* Extract results and copy to mesh vertices. */ {
    float* result = MemNewArray(float, vDim * vDim);
    Tex2D_GetData(texOutput, result, PixelFormat_Red, DataFormat_Float);
    for (int i = 0; i < vertexCount; ++i)
      vertexData[i].uv.x = result[i];
    MemFree(result);
  }

  Tex2D_Free(texPoints);
  Tex2D_Free(texOutput);
}


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

const PixelFormat PixelFormat_Red             = GL_RED;
const PixelFormat PixelFormat_RG              = GL_RG;
const PixelFormat PixelFormat_RGB             = GL_RGB;
const PixelFormat PixelFormat_BGR             = GL_BGR;
const PixelFormat PixelFormat_RGBA            = GL_RGBA;
const PixelFormat PixelFormat_BGRA            = GL_BGRA;
const PixelFormat PixelFormat_Depth_Component = GL_DEPTH_COMPONENT;

int PixelFormat_Components (PixelFormat self) {
  switch (self) {
    case PixelFormat_Red:
    case PixelFormat_Depth_Component:
      return 1;
    case PixelFormat_RG:
      return 2;
    case PixelFormat_RGB:
    case PixelFormat_BGR:
      return 3;
    case PixelFormat_RGBA:
    case PixelFormat_BGRA:
      return 4;
  }
  return 0;
}


text:
#include "ResourceType.h"

const ResourceType ResourceType_Font    = 0x0;
const ResourceType ResourceType_Mesh    = 0x1;
const ResourceType ResourceType_Other   = 0x2;
const ResourceType ResourceType_Script  = 0x3;
const ResourceType ResourceType_Shader  = 0x4;
const ResourceType ResourceType_Sound   = 0x5;
const ResourceType ResourceType_Tex1D   = 0x6;
const ResourceType ResourceType_Tex2D   = 0x7;
const ResourceType ResourceType_Tex3D   = 0x8;
const ResourceType ResourceType_TexCube = 0x9;

cstr ResourceType_ToString (ResourceType self) {
  switch (self) {
    case ResourceType_Font:    return "Font";
    case ResourceType_Mesh:    return "Mesh";
    case ResourceType_Other:   return "Other";
    case ResourceType_Script:  return "Script";
    case ResourceType_Shader:  return "Shader";
    case ResourceType_Sound:   return "Sound";
    case ResourceType_Tex1D:   return "Tex1D";
    case ResourceType_Tex2D:   return "Tex2D";
    case ResourceType_Tex3D:   return "Tex3D";
    case ResourceType_TexCube: return "TexCube";
  }
  return 0;
}