#include <cmath>
#include "Maths.h"


struct vec4f
{
  union {
    float v[4];
    struct { float x, y, z, w; };
  };
};


struct mat4f
{
  union {
    vec4f m[4];
    struct { vec4f t, b, n, w; };  // tangent, bi-normal, normal
  };
};


vec4f operator +(const vec4f& a, const vec4f& b)
{
  vec4f res = {{{ a.x + b.x, a.y + b.y, a.z + b.z, a.w + b.w }}};
  return res;
}


vec4f operator -(const vec4f& a, const vec4f& b)
{
  vec4f res = {{{ a.x - b.x, a.y - b.y, a.z - b.z, a.w - b.w }}};
  return res;
}


inline float dot3(const vec4f& a, const vec4f& b)
{
  //return Math::dotProduct(&a.x, &b.x, 1, 1, 3);
  return a.x * b.x + a.y * b.y + a.z * b.z;
}


vec4f operator *(const vec4f& a, const vec4f& b)
{
  vec4f res;
  res.x = a.x * b.x;
  res.y = a.y * b.y;
  res.z = a.z * b.z;
  res.w = a.w * b.w;
  return res;
}


vec4f scaleVector(const vec4f& a, float b)
{
  vec4f res;
  res.x = a.x * b;
  res.y = a.y * b;
  res.z = a.z * b;
  res.w = a.w * b;
  return res;
}
vec4f operator *(const vec4f& a, float b)
{
  return scaleVector(a, b);
}


struct rotation
{
  const vec4f& q()      // get the quaternion
  {
    return quaternion;
  }
  const mat4f& m()      // get as a matrix
  {
    if (dirty)
    {
      convert();
    }
    return cachedMatrix;
  }
  void set(vec4f quat)  // set
  {
    quaternion = quat;
    dirty = true;
  }
  // TODO: perhaps operator overload the get and sets
private:
  void convert()
  {
    Math::quatToMatrix4x4(&cachedMatrix.m[0].x, &quaternion.x);
    dirty = false;
  }
  vec4f quaternion;
  bool dirty = true;
  mat4f cachedMatrix; // only needs to be 3x3
};


vec4f rotateVector(const vec4f& vec, const mat4f& mat)
{
  vec4f res;
  Math::transformVector(&res.x, &mat.t.x, &vec.x);
  return res;
}
vec4f operator *(const vec4f& a, const mat4f& mat)
{
  return rotateVector(a, mat);
}


float vectorLength(vec4f& vec)
{
  return sqrt(Math::dotProduct(&vec.x, &vec.x, 1, 1, 3));
}


struct Ray
{
  vec4f start, end;
  vec4f invN;
};


// Top 8 looks better than top 32 !!!
#define TOPX  64 // 32


// An area - like a quad, but modelled as a 2D circular area floating in 3D space
struct Patch
{
  vec4f  normal; // <- instead a reference to some group it belongs to with shared TBN
                 // Could represent as 2 values and quantize to 8 bits and have a lookup table or something like that
  vec4f  position; // w of position is the radius
  int    quadIndex;

  float  w, h;

  int    u, v;

  vec4f  color;
  vec4f  newColor;

  bool   inShadow; // TODO: need a bit for each light that cast shadows
  float  directLightIntensity;

  float worstFF;
  float topFF[TOPX];
  int   topIdx[TOPX];
  //float  intensity;
  //float  newIntensity;
  float  totalFormFactors;
};


enum ShapeType
{
  Box
};


struct Shape
{
  ShapeType m_type;
  float x,y,z, w,h,d;  // width,height,depth
};


struct WindowParameters
{
  float w,h;
};


struct CameraParameters
{
  vec4f position;
  vec4f rotation;
};


struct LightParameters
{
  vec4f position;
  vec4f color;
  float power;
  float shininess;
  bool  castShadows;
};


struct RadiosityParameters
{
  int   topXToUse;
  float patchSize;
  float timesPerFrame;
  float a, b, c, d;
};


struct Vertex
{
  vec4f position;
  vec4f uv;
};


struct Quad
{
  // Vertex verts[4];  TODO: change to a vertex spec and array of them
  vec4f a, b, c, d;
  vec4f uvs[4];
  vec4f color;
  mat4f basis;
};


struct Texture
{
  uint32_t* bits;
  int w, h;
  uint32_t state;
  uint32_t texId;
};


template <typename T>
inline constexpr T constPow(const T base, const T exponent)
{
  return (exponent == 0) ? 1 : (base * constPow(base, exponent - 1));
}


// N-Dimentional tree (generalization of binary tree, quadtree, octree etc)
// Next generalization would be to make this support multiple trees in one (perhaps one root but masks of properties in the nodes)
// To make normals as maskable properties, would need to quantize the normals to perhaps 64 possible normals, so could use a 64bit mask
// Perhaps a Deltoidal hexecontahedron with 60 faces - each face is same shape and area - so regular without bias
// Or perhaps a dodecahedron with each hexagon face split in to triangles (pentakis dodecahedron) - makes 60 faces, 32 vertexes
template <int D>
class DTree
{
public:
  struct pos
  {
    float v[D];
  };

  class ObjectInterface
  {
  public:
    virtual pos position() = 0;
  };


  // Moved to public for query interface
  struct DTreePoint
  {
    int v[D];
  };

  // Moved to public for query interface
  struct DTreeBounds
  {
    DTreePoint origin;
    int size;
  };
private:

  // For a quadtree, D = 2, maxChildren = 4
  // For a octree,   D = 3, maxChildren = 8
  static const int maxChildren = constPow(2, D);

  class DTreeNode
  {
  public:
    DTreeNode* nodes[maxChildren];
    DTreeNode()
    {
      memset(nodes, 0, sizeof(nodes));
    }
    void AddItem(ObjectInterface* item)
    {
      items.push_back(item);
    }
    void RemoveItem(ObjectInterface* item)
    {
      items.erase(std::remove(items.begin(), items.end(), item), items.end());
    }
    size_t ItemCount()
    {
      return items.size();
    }
  //private:
    std::vector<ObjectInterface*> items;
  };

public:
  DTree(float _resolution = 1.0f)
  {
    resolution = _resolution;
  }

  ~DTree()
  {
    // TODO: clean-up
  }

  // probably needs to be a shared_ptr or may be a way to remove it from the DTree when it is destroyed
  void AddObject(ObjectInterface* item)
  {
    auto addFunctor = [](DTreeNode& node, ObjectInterface* item) { node.AddItem(item); };
    DTreePoint pnt = QuantizePoint(item->position());
    Expand(pnt);
    Traverse(addFunctor, *rootNode, bounds, pnt, item);
  }

  void RemoveObject(ObjectInterface* item) // need to quickly test the item belongs to the quadtree
  {
    // TODO: this doesn't recursively remove nodes as it traverses back up the tree
    //auto removeFunctor = [](DTreeNode& node, DTreeItemInterface* item) { node.RemoveItem(item); };
    //Traverse(removeFunctor, *rootNode, bounds, QuantizePoint(item->DTreeX(), item->DTreeY()), item);
    
    RemoveTraverse(*rootNode, bounds, QuantizePoint(item->position), item);
  }

  void MoveObject(ObjectInterface* item, pos pnt)
  {
    DTreePoint oldPnt = QuantizePoint(item->position());
    DTreePoint newPnt = QuantizePoint(pnt);
    MoveTraverse(*rootNode, bounds, oldPnt, newPnt, item);
  }

  static const int maxTreeDepth = 32;

  struct QueryContext
  {
    QueryContext(std::function<bool(const DTreeBounds&)> f) : func(f) {}

    // Functor to be called to decide if matches or not
    std::function<bool(const DTreeBounds&)> func;

    // Basically an explicit stack 
    int recurseDepth;

    /*
    struct Stack
    {
      int         state; // where to jump to
      DTreeNode*  node;
      DTreeBounds bounds;
      int         index;
      int         octant;
    };
    Stack stack[maxTreeDepth];
    */

    int         state[maxTreeDepth];
    DTreeNode*  nodeStack[maxTreeDepth];
    DTreeBounds bounds[maxTreeDepth];
    int         index[maxTreeDepth];
    int         octant[maxTreeDepth];
  };

  QueryContext InitializeQuery(std::function<bool(const DTreeBounds&)> func)
  {
    QueryContext context(func);
    context.recurseDepth = 0;
    QueryInitStackHelper(context, rootNode, bounds);
    return context;
  }

  void QueryInitStackHelper(QueryContext& context, DTreeNode* node, const DTreeBounds& b)
  {
    context.nodeStack[context.recurseDepth] = node;
    context.bounds[context.recurseDepth] = b;
    context.index[context.recurseDepth] = 0;
    context.octant[context.recurseDepth] = 0;
    context.state[context.recurseDepth] = 0;
  }

  ObjectInterface* QueryNextHelper(QueryContext& context, DTreeNode* node, const DTreeBounds& b)
  {
    context.recurseDepth++;
    QueryInitStackHelper(context, node, b);
    return QueryNext(context);
  }

#define CoroutineBegin    switch(state) { case 0:
#define CoroutineEmit(x)  do { state=__LINE__; return x; case __LINE__:; } while (0)
#define CoroutineEnd      }

  ObjectInterface* QueryNext(QueryContext& context) // Gets the next object, or returns nullptr if no more
  {
    DTreeNode* node = context.nodeStack[context.recurseDepth];
    DTreeBounds& b = context.bounds[context.recurseDepth];
    int& index = context.index[context.recurseDepth];
    int& q = context.octant[context.recurseDepth];
    int& state = context.state[context.recurseDepth];
    DTreeNode* childNode;
    DTreeBounds childBounds;

    // Can't use local variables from here down
    CoroutineBegin

    if (b.size == 1)
    {
      while (index < node->ItemCount())
      {
        index++;
        return node->items[index - 1];
      }
      context.recurseDepth--;
      return QueryNext(context); // Calling self, so don't need emit. What happens if use emit?
    }

    while (q < maxChildren)
    {
      childNode = node->nodes[q];
      if (childNode)
      {
        childBounds = ChildBounds(b, q);
        if (context.func(childBounds))
        {
          CoroutineEmit(QueryNextHelper(context, childNode, childBounds));
        }
      }
      q++;
    }

    if (context.recurseDepth)
    {
      context.recurseDepth--;
      return QueryNext(context); // Calling self, so don't need emit
    }

    CoroutineEnd

    return nullptr;          // Emit?
  }


  // Recursive but non-iterative and blocking implementation of the query
  std::vector<ObjectInterface*> QueryExecute(std::function<bool(const DTreeBounds&)> func)
  {
    std::vector<ObjectInterface*> ret;
    QueryExecuteRecurse(ret, func, rootNode, bounds);
    return ret;
  }

  // helper to the above function
  void QueryExecuteRecurse(std::vector<ObjectInterface*>& ret, std::function<bool(const DTreeBounds&)> func,
          DTreeNode* node, const DTreeBounds& b)
  {
    DTreeNode* childNode;
    DTreeBounds childBounds;

    if (b.size == 1)
    {
      int index = 0;
      while (index < node->ItemCount())
      {
        index++;
        ret.push_back(node->items[index - 1]);
      }
      return;
    }

    int q = 0;
    while (q < maxChildren)
    {
      childNode = node->nodes[q];
      if (childNode)
      {
        childBounds = ChildBounds(b, q);
        if (func(childBounds))
        {
          QueryExecuteRecurse(ret, func, childNode, childBounds);
        }
      }
      q++;
    }
  }

  // public so can debug
  DTreeBounds bounds;

private:
  /*
  enum class Direction : int 
  {
    Middle = 0,
    Left = 1<<0,  // 1   0001
    Right = 1<<1, // 2   0010
    Down = 1<<2,  // 4   0100
    Up = 1<<3,    // 8   1000
    UpLeft = Up | Left,
    UpRight = Up | Right,
    DownLeft = Down | Left,
    DownRight = Down | Right
  };
  */

  float resolution;
  DTreeNode* rootNode = nullptr;

  int QuantizeValue(float value)
  {
    return (int)std::floor((value + resolution * 0.5f) / resolution);
  }

  DTreePoint QuantizePoint(pos pnt)
  {
    DTreePoint p;
    for (int i = 0; i < D; i++)
    {
      p.v[i] = QuantizeValue(pnt.v[i]);
    }
    return p;
  }

  void Expand(const DTreePoint& pnt)
  {
    if (!rootNode)
    {
      rootNode = new DTreeNode;
      bounds.origin = pnt;
      bounds.size = 1;
      return;
    }
    ExpandRecurse(pnt);
  }

  static inline DTreeBounds ChildBounds(const DTreeBounds& b, int quadrant)
  {
    DTreeBounds childBounds = b;
    childBounds.size = b.size >> 1;

    // TODO: need to generalize to higher dimensions - need to resolve why x and y are inverted to each other to do this
    //childBounds.origin.x += (quadrant & 1) ? childBounds.size : 0;
    //childBounds.origin.y += (quadrant & 2) ? 0 : childBounds.size;
    
    for (int i = 0; i < D; i++)
    {
      childBounds.origin.v[i] += (quadrant & (1<<i)) ? childBounds.size : 0;
    }
    
    return childBounds;
  }
  
  static inline bool PointInBounds(const DTreePoint& pnt, const DTreeBounds& b)
  {
    //return (pnt.x >= b.origin.x && pnt.y >= b.origin.y && pnt.x < (b.origin.x + b.size) && pnt.y < (b.origin.y + b.size));

    // generalized to higher dimensions
    for (int i = 0; i < D; i++)
    {
      if ((pnt.v[i] < b.origin.v[i]) || (pnt.v[i] >= (b.origin.v[i] + b.size)))
        return false;
    }
    return true;
  }

  bool RemoveTraverse(DTreeNode& node, const DTreeBounds& b, const DTreePoint& pnt, ObjectInterface* item)
  {
    if (b.size == 1)
    {
      node.RemoveItem(item);
      return node.ItemCount() == 0;
    }

    for (int q = 0; q < maxChildren; q++)
    {
      DTreeBounds childBounds = ChildBounds(b, q);
      if (PointInBounds(pnt, childBounds))
      {
        //assert(node.nodes[q]);
        if (RemoveTraverse(*node.nodes[q], childBounds, pnt, item))
        {
          delete node.nodes[q];
          node.nodes[q] = 0;
        }
        break;
      }
    }
    
    //return !node.nodes[0] && !node.nodes[1] && !node.nodes[2] && !node.nodes[3];

    // generalized to higher dimensions:
    for (int i = 0; i < maxChildren; i++)
    {
      if (node.nodes[i])
        return false;
    }
    return true;
  }


  void MoveTraverse(DTreeNode& node, const DTreeBounds& b, const DTreePoint& oldPnt, const DTreePoint& newPnt, ObjectInterface* item)
  {
    //assert(b.size != 1)
    for (int q = 0; q < maxChildren; q++)
    {
      DTreeBounds childBounds = ChildBounds(b, q);
      bool oldIn = PointInBounds(oldPnt, childBounds);
      bool newIn = PointInBounds(newPnt, childBounds);
      if (oldIn && newIn)
      {
        //assert(node.nodes[q]);
        MoveTraverse(*node.nodes[q], childBounds, oldPnt, newPnt, item);
      }
      else if (oldIn)
      {
        //assert(node.nodes[q]);
        RemoveTraverse(*node.nodes[q], childBounds, oldPnt, item);
      }
      else if (newIn)
      {
        auto addFunctor = [](DTreeNode& node, ObjectInterface* item) { node.AddItem(item); };
        Traverse(addFunctor, *node.nodes[q], childBounds, newPnt, item);
      }
    }
  }

  template <typename F>
  void Traverse(const F& func, DTreeNode& node, const DTreeBounds& b, const DTreePoint& pnt, ObjectInterface* item)
  {
    if (b.size == 1)
    {
      func(node, item);
      return;
    }
    for (int q = 0; q < maxChildren; q++)
    {
      DTreeBounds childBounds = ChildBounds(b, q);
      if (PointInBounds(pnt, childBounds))
      {
        if (!node.nodes[q])
          node.nodes[q] = new DTreeNode;
        Traverse(func, *node.nodes[q], childBounds, pnt, item);
        return;
      }
    }

    /*
    // Generic traverse idea - callbacks for each part of the traversal
    auto actionFunctor = [func, item](DTreeNode& node, const DTreeBounds& bounds) { func(node, item); };
    auto terminateFunctor = [](const DTreeNode& node, const DTreeBounds& bounds) { return bounds.size == 1; };
    auto testFunctor = [pnt](const DTreeNode& node, const DTreeBounds& bounds) { return PointInBounds(pnt, bounds); };
    auto nullNodeFunctor = [](DTreeNode& node, int quadrant) { node.nodes[quadrant] = new DTreeNode; };
    TraverseGeneric(actionFunctor, terminateFunctor, testFunctor, nullNodeFunctor, node, b, true);
    */
  }

  template <typename ActionF, typename TerminateF, typename TestF, typename NullNode>
  void TraverseGeneric(const ActionF& action, const TerminateF& term, const TestF& test, const NullNode& nullNode, DTreeNode& node, const DTreeBounds& b, bool earlyOut)
  {
    if (term(node, b))
    {
      action(node, b);
      return;
    }
    for (int q = 0; q < maxChildren; q++)
    {
      DTreeBounds childBounds = ChildBounds(b, q);
      if (test(node, childBounds))
      {
        if (!node.nodes[q])
          nullNode(node, q);
        if (!node.nodes[q])
          TraverseGeneric(action, term, test, nullNode, *node.nodes[q], childBounds, earlyOut);
        if (earlyOut)
          return;
      }
    }
  }

  void ExpandRecurse(const DTreePoint& pnt)
  {
    // assert(rootNode);
    
    // First check if pnt is inside the bounds or not
    bool middle = true;
    for (int axis = 0; axis < D; axis++)
    {
      bool left = pnt.v[axis] < bounds.origin.v[axis];
      bool right = pnt.v[axis] >= (bounds.origin.v[axis] + bounds.size);
      if (left || right)
      {
        middle = false;
        break;
      }
    }
    if (middle)
      return;
  
    // Test: generalized to higher dimensions might be something like this:
    int octant = 0;
    for (int axis = 0; axis < D; axis++)
    {
      bool left = pnt.v[axis] < bounds.origin.v[axis];
      octant |= (left) ? (1<<D) : 0;
      bounds.origin.v[axis] -= (left) ? bounds.size : 0;
    }
    bounds.size <<= 1;
    DTreeNode* newRootNode = new DTreeNode;
    newRootNode->nodes[octant] = rootNode;
    rootNode = newRootNode;
    ExpandRecurse(pnt);

#if 0
    // TODO: generalize to higher dimensions
    int growDirectionFlags = 0; // Middle
    growDirectionFlags |= (pnt.x < bounds.origin.x)                  ? (1<<0) : 0;// ? Direction::Left : Direction::Middle;
    growDirectionFlags |= (pnt.x >= (bounds.origin.x + bounds.size)) ? (1<<1) : 0;// ? Direction::Right : Direction::Middle;
    growDirectionFlags |= (pnt.y < bounds.origin.y)                  ? (1<<2) : 0;// ? Direction::Down : Direction::Middle;
    growDirectionFlags |= (pnt.y >= (bounds.origin.y + bounds.size)) ? (1<<3) : 0;// ? Direction::Up : Direction::Middle;

    int oldRootQuadrant = 0;
    switch (growDirectionFlags) {
      case /* middle */     0:                return; // Nothing more to do, the point is already inside the current bounds
      case /* up */         (1<<3): 
      case /* up-left */    (1<<3) | (1<<0):  oldRootQuadrant = 3; break;
      case /* right */      (1<<1): 
      case /* up-right */   (1<<1) | (1<<3):  oldRootQuadrant = 2; break;
      case /* left */       (1<<0): 
      case /* down-left */  (1<<0) | (1<<2):  oldRootQuadrant = 1; break;
      case /* down */       (1<<2): 
      case /* down-right */ (1<<2) | (1<<1):  oldRootQuadrant = 0; break;
    }

    int q = oldRootQuadrant;
    bounds.origin.x -= (q & 1) ? bounds.size : 0;
    bounds.origin.y -= (q & 2) ? 0 : bounds.size;
    bounds.size <<= 1;
    DTreeNode* newRootNode = new DTreeNode;
    newRootNode->nodes[q] = rootNode;
    rootNode = newRootNode;
    ExpandRecurse(pnt);
#endif
  }
};



/*
struct PatchOctreeNode;
union PatchOctreeElement
{
  Patch*           patch;
  PatchOctreeNode* child;
};
struct PatchOctreeNode
{
  uint8_t            childMask;
  PatchOctreeElement children[8];
};
struct PatchOctree
{
  float            resolution;
  vec4f            boundsOrigin;
  vec4f            boundsSize;
  PatchOctreeNode* rootNode;
};
void InitOctee(PatchOctree& octree, float resolution)
{
  octree.rootNode = nullptr;
  octree.resolution = resolution;
}
void AddPatch(PatchOctree& octree, Patch& patch)
{
  if (octree.rootNode == nullptr)
  {
    octree.rootNode = new PatchOctreeNode;
    octree.rootNode.childMask = 1;
    memset(octree.rootNode.children, 0, sizeof(octree.rootNode.children));
    octree.rootNode.children[0].patch = &patch;
    octree.boundsOrigin = patch.position;
    octree.boundsSize.x = octree.resolution;
    octree.boundsSize.y = octree.resolution;
    octree.boundsSize.z = octree.resolution;
    return;
  }

}
*/

struct Scene
{
  WindowParameters window;
  RadiosityParameters settings;
  CameraParameters camera;
  LightParameters light;
  // PatchOctree patchOctree;
  DTree<3> patchOctree;
  Texture texture = { nullptr, -1, -1, 0, 0 }; // light-map
  std::vector<Shape> objects; // solid shapes
  std::vector<Quad> quads;    // large polygons of the surface of those shapes
  std::vector<Patch> patches; // small square areas over the polygons
};


/*
struct OctreeNode
{
  uint16_t   normalsPattern;  // quantized normal to 16-bits
  OctreeNode children[8];     // dynamic pointer based idea - more flexible and way you normally build it

//  uint16_t  firstChildIndex; // static octree idea - can be smaller if use blocks of data - can imply child block from table from current block
                               // smaller blocks means smaller indexes and easier to manage the memory - perhaps 256 nodes per block for a 8-bit index
};


struct OctreeBlock
{
  int          childOffsets[8]; // 
  OctreeBlock* childBlocks[8];  // Up to 8 child blocks - this is using pointers, again could use indexes
  OctreeNode   nodes[256];
};
*/


float simpleFormFactorInternal(const vec4f& p1, const vec4f& p2, const vec4f& n1, const vec4f& n2)
{
  vec4f patch1patch2 = p2 - p1;
  float magSqr = dot3(patch1patch2, patch1patch2);
  float dot1 =  dot3(n1, patch1patch2);
  float dot2 = -dot3(n2, patch1patch2); // negative because it should be dot with patch2patch1 which is just -patch1patch2
  if (dot2 < 0.0f || dot1 < 0.0f)
    return 0.0f;
  return (dot1 * dot2) / (M_PI * magSqr * magSqr); // the additional magSqr is a built-in normalization of patch2patch that is optimized away
}


// Simplified form factor calculation - need to compare the results from this and test to see how good it is
// https://en.wikipedia.org/wiki/View_factor
float simpleFormFactor(const Patch& patch1, const Patch& patch2, const Scene& scn)
{
  // get direction of patch 1 to patch 2
  vec4f patch1patch2 = patch2.position - patch1.position;
  float magSqr = dot3(patch1patch2, patch1patch2);

  /*
  if (magSqr < 3.0f)
  {
    float total = 0.0f;
    const Quad& q1 = scn.quads[patch1.quadIndex];
    const Quad& q2 = scn.quads[patch2.quadIndex];

    total += simpleFormFactorInternal(patch1.position + q1.basis.t + q1.basis.b, patch2.position + q2.basis.t + q2.basis.b, patch1.normal, patch2.normal);
    total += simpleFormFactorInternal(patch1.position + q1.basis.t + q1.basis.b, patch2.position + q2.basis.t - q2.basis.b, patch1.normal, patch2.normal);
    total += simpleFormFactorInternal(patch1.position + q1.basis.t + q1.basis.b, patch2.position - q2.basis.t + q2.basis.b, patch1.normal, patch2.normal);
    total += simpleFormFactorInternal(patch1.position + q1.basis.t + q1.basis.b, patch2.position - q2.basis.t - q2.basis.b, patch1.normal, patch2.normal);

    total += simpleFormFactorInternal(patch1.position + q1.basis.t - q1.basis.b, patch2.position + q2.basis.t + q2.basis.b, patch1.normal, patch2.normal);
    total += simpleFormFactorInternal(patch1.position + q1.basis.t - q1.basis.b, patch2.position + q2.basis.t - q2.basis.b, patch1.normal, patch2.normal);
    total += simpleFormFactorInternal(patch1.position + q1.basis.t - q1.basis.b, patch2.position - q2.basis.t + q2.basis.b, patch1.normal, patch2.normal);
    total += simpleFormFactorInternal(patch1.position + q1.basis.t - q1.basis.b, patch2.position - q2.basis.t - q2.basis.b, patch1.normal, patch2.normal);
    
    total += simpleFormFactorInternal(patch1.position - q1.basis.t + q1.basis.b, patch2.position + q2.basis.t + q2.basis.b, patch1.normal, patch2.normal);
    total += simpleFormFactorInternal(patch1.position - q1.basis.t + q1.basis.b, patch2.position + q2.basis.t - q2.basis.b, patch1.normal, patch2.normal);
    total += simpleFormFactorInternal(patch1.position - q1.basis.t + q1.basis.b, patch2.position - q2.basis.t + q2.basis.b, patch1.normal, patch2.normal);
    total += simpleFormFactorInternal(patch1.position - q1.basis.t + q1.basis.b, patch2.position - q2.basis.t - q2.basis.b, patch1.normal, patch2.normal);

    total += simpleFormFactorInternal(patch1.position - q1.basis.t - q1.basis.b, patch2.position + q2.basis.t + q2.basis.b, patch1.normal, patch2.normal);
    total += simpleFormFactorInternal(patch1.position - q1.basis.t - q1.basis.b, patch2.position + q2.basis.t - q2.basis.b, patch1.normal, patch2.normal);
    total += simpleFormFactorInternal(patch1.position - q1.basis.t - q1.basis.b, patch2.position - q2.basis.t + q2.basis.b, patch1.normal, patch2.normal);
    total += simpleFormFactorInternal(patch1.position - q1.basis.t - q1.basis.b, patch2.position - q2.basis.t - q2.basis.b, patch1.normal, patch2.normal);
    
    return total / 16.0f;
  }
  */

  float dot1 =  dot3(patch1.normal, patch1patch2);
  float dot2 = -dot3(patch2.normal, patch1patch2); // negative because it should be dot with patch2patch1 which is just -patch1patch2
  if (dot2 < 0.0f || dot1 < 0.0f)
    return 0.0f;
  return (dot1 * dot2) / (M_PI * magSqr * magSqr); // the additional magSqr is a built-in normalization of patch2patch that is optimized away
}


void fillTextureArea(uint32_t* tex, int texWidth, int x, int y, int w, int h, uint32_t col)
{
  uint32_t* ptr = tex + y * texWidth + x;
  for (y = 0; y < h; y++)
  {
    uint32_t* p = ptr + y * texWidth;
    for (x = 0; x < w; x++)
    {
      p[x] = 0xFF000000 | col;
      /*
      if (x < 1 || x > (w-1) || y < 1 || y > (h-1))
        p[x] = 0xFF0000FF;
      else
      {
        uint32_t w2 = w;
        uint32_t h2 = h;
        uint32_t x2 = x/2 + w/2;
        uint32_t y2 = y/2 + h/2;
        uint8_t red = ((255U * h2 / y2));// << 16U);
        uint8_t green = ((255U * w2 / x2));// << 8U);
        p[x] = 0xFF000000UL | (red << 16) | (green << 8);
      }
      */
    }
  }
}


void createTexture(Scene& scn)
{
  /*
  float totalArea = 0.0f;
  for (auto o : scn.objects)
  {
    if (o.m_type == Box)
    {
      totalArea += o.w * o.h * 2.0f;
      totalArea += o.w * o.d * 2.0f;
      totalArea += o.d * o.h * 2.0f;
    }
  }
  float patchSize = scn.settings.patchSize;
  float patchCount = totalArea / (patchSize * patchSize);
  float minTextureDim = sqrt(patchCount);
  if (minTextureDim > 4096.0f)
  {
    scn.texture = { nullptr, -1, -1, 0, 0 };
    printf("Scene will not be able to fit inside a single texture\n");
    return;
  }
  */

  float totalHeight = 0.0f; 
  float totalWidth = 0.0f; 
  for (auto o : scn.objects)
  {
    if (o.m_type == Box)
    {
      float w = o.w + o.w + o.d;
      float h = std::max(o.h, o.d);
      totalWidth = std::max(totalWidth, w) + 12.0f; // Add 2 pixel border region around each quad
      totalHeight += h + 4.0f;  // Add 2 pixels on either side
    }
  }
  float maxDim = std::max(totalWidth, totalHeight);
  float patchSize = scn.settings.patchSize;
  maxDim /= patchSize;
  int dim = (int)maxDim;
  int nextPowerOfTwo = 2;
  while (nextPowerOfTwo < dim)
    nextPowerOfTwo <<= 1;
  dim = nextPowerOfTwo;
  printf("Lightmap texture dim: %i\n", dim);
  Texture texture = { (uint32_t*)malloc(dim*dim*sizeof(uint32_t)), dim, dim, 0, 0 };
  for (int i = 0; i < dim*dim; i++)
    texture.bits[i] = 0xFF0F0FFF;
  scn.texture = texture;
}


void convertObjectsToQuads(Scene& scn)
{
  int v = 0;
  fillTextureArea(scn.texture.bits, scn.texture.w, 0, 0, scn.texture.w, scn.texture.h, 0);
  for (auto o : scn.objects)
  {
    if (o.m_type == Box)
    {
      // Populate the texture
      float w = o.w;
      float h = o.h;
      float d = o.d;
      float patchSize = scn.settings.patchSize;
      w /= patchSize;
      h /= patchSize;
      d /= patchSize;
      w += 1.0f;
      h += 1.0f;
      d += 1.0f;
      // TODO: trying to make them in to int positions, but need to have the fractional part for the
      // edge cases. When split in to patches, most are exactly patchSize x patchSize, but on the edges
      // it will be for example 0.3 . patchSize x 0.45 . patchSize  so the u2 and v2 for example will
      // not be whole numbers. Currently it is just rounding up to the next whole number, but this
      // will stretch the texture.
      // Need to also work out how the GPU samples the texture, might need a 0.5 bias to the u,v
      // coordinates so that is doesn't sample and blend from pixels off the edges of where we thing
      float u1 = 2.0f;
      float u2 = u1 + w + 4.0f;
      float u3 = u2 + w + 4.0f;
      float u4 = u3 + d + 2.0f;
      float v1 = v + 2.0f;
      float v2 = v1 + 2.0f + h;
      float v3 = v1 + 2.0f + d;
      float v4 = v1 + 2.0f + h;
      float tw = scn.texture.w;
      float th = scn.texture.h;
/*
      fillTextureArea(scn.texture.bits, scn.texture.w, u1, v, w, h, 0xFFFF0000);
      fillTextureArea(scn.texture.bits, scn.texture.w, u2, v, w, d, 0xFF00FF00);
      fillTextureArea(scn.texture.bits, scn.texture.w, u3, v, d, h, 0xFF0000FF);
      
      fillTextureArea(scn.texture.bits, scn.texture.w, u4+u1, v, w, h, 0xFFFF0000);
      fillTextureArea(scn.texture.bits, scn.texture.w, u4+u2, v, w, d, 0xFF00FF00);
      fillTextureArea(scn.texture.bits, scn.texture.w, u4+u3, v, d, h, 0xFF0000FF);
*/      
      float uvs[6][4][2] = {
        { { u1, v1 }, { u1, v2 }, { u2, v2 }, { u2, v1 } },  // 0 -> front  0
        { { u2, v1 }, { u2, v3 }, { u3, v3 }, { u3, v1 } },  // 1 -> bottom 1
        { { u3, v1 }, { u3, v4 }, { u4, v4 }, { u4, v1 } },  // 2 -> side   2
        
        { { u4+u3, v1 }, { u4+u3, v4 }, { u4+u4, v4 }, { u4+u4, v1 } },  // 2 -> side   3
        { { u4+u1, v1 }, { u4+u1, v2 }, { u4+u2, v2 }, { u4+u2, v1 } },  // 0 -> back   4
        { { u4+u2, v1 }, { u4+u2, v3 }, { u4+u3, v3 }, { u4+u3, v1 } },  // 1 -> top    5

        /*
        { { 0, 0 }, { 0, th }, { tw, th }, { tw, 0 } },
        { { 0, 0 }, { 0, th }, { tw, th }, { tw, 0 } },
        { { 0, 0 }, { 0, th }, { tw, th }, { tw, 0 } },
        { { 0, 0 }, { 0, th }, { tw, th }, { tw, 0 } },
        { { 0, 0 }, { 0, th }, { tw, th }, { tw, 0 } },
        */
      };
      v += std::max(h, d);
      v += 2.0f;

      vec4f verts[8];   // boxes have 8 verts
      for (int i = 0; i < 8; i++)
      {
        float x = o.x + ((i&1) ? o.w : 0.0f);
        float y = o.y + ((i&2) ? o.h : 0.0f);
        float z = o.z + ((i&4) ? o.d : 0.0f);
        verts[i] = {{{ x, y, z, 0.0f }}};  // 8 is 2^3 - 1 bit for each axis, bit 0 => x, bit 1 => y, bit 2 => z
      }

      // boxes have 6 faces, each face with 4 verts (one of the 8 verts above). A LUT of vert indexes for each face (with CW winding order)
      int vertIndexes[6][4] = { { 0, 2, 3, 1 }, { 2, 6, 7, 3 }, { 1, 3, 7, 5 }, { 0, 4, 6, 2 }, { 4, 5, 7, 6 }, { 0, 1, 5, 4 } };
      // 0, 4    1, 5     2,  3
      // G  R    B  R     G   B
      vec4f colors[6] = { {{{ 0.0f, 1.0f, 0.0f, 0.0f }}}, {{{ 0.0f, 0.0f, 1.0f, 0.0f }}}, {{{ 0.0f, 1.0f, 0.0f, 0.0f }}},
                          {{{ 0.0f, 0.0f, 1.0f, 0.0f }}}, {{{ 1.0f, 0.0f, 0.0f, 0.0f }}}, {{{ 1.0f, 0.0f, 0.0f, 0.0f }}} };
      for (int quad = 0; quad < 6; quad++)
      {
        Quad q;
        q.a = verts[vertIndexes[quad][0]];
        q.b = verts[vertIndexes[quad][1]];
        q.c = verts[vertIndexes[quad][2]];
        q.d = verts[vertIndexes[quad][3]];
        q.color = colors[quad];

        for (int i = 0; i < 4; i++)
          q.uvs[i] = {{{ uvs[quad][i][0]/float(tw), uvs[quad][i][1]/float(th), 0.0f, 0.0f }}};

        // Calculate basis vectors
        q.basis.t = q.b - q.a; // B->A
        q.basis.b = q.b - q.c; // B->C
        Math::normalizeVector(&q.basis.t.x);
        Math::normalizeVector(&q.basis.b.x);
        Math::crossProduct(&q.basis.n.x, &q.basis.t.x, &q.basis.b.x);
        q.basis.n = scaleVector(q.basis.n, -1.0f);
        Math::normalizeVector(&q.basis.n.x);
        q.basis.w = {{{ 0.0f, 0.0f, 0.0f, 1.0f }}};

        scn.quads.push_back(q);
      }
    }
  }
}


class PatchObject : public DTree<3>::ObjectInterface
{
public:
  PatchObject(Patch& ptch, size_t i)
    : idx(i)
    , m_patch(ptch)
  {
    for (int i = 0; i < 3; i++)
      p.v[i] = ptch.position.v[i];
  }
  DTree<3>::pos position() override
  {
    return p;
  }
  const Patch& patch() const
  {
    return m_patch;
  }
  size_t idx;
private:
  Patch& m_patch;
  DTree<3>::pos p;
};


void addPatchToScene(Scene& scn, Patch& p)
{
  size_t idx = scn.patches.size();
  PatchObject *obj = new PatchObject(p, idx);
  scn.patchOctree.AddObject(obj);
  scn.patches.push_back(p);
}


void convertQuadsToPatches(Scene& scn)
{
  float patchSize = scn.settings.patchSize;
  for (int qi = 0; qi < scn.quads.size(); qi++)
  {
    Quad& q = scn.quads[qi];

    float u1,u2,v1,v2;
    u1 = q.uvs[0].x * scn.texture.w;
    u2 = q.uvs[2].x * scn.texture.w;
    v1 = q.uvs[0].y * scn.texture.h;
    v2 = q.uvs[2].y * scn.texture.h;

    vec4f ba = q.b - q.a; // B->A
    vec4f bc = q.b - q.c; // B->C
    float len1 = vectorLength(bc);
    float len2 = vectorLength(ba);
    int jc = int((len1 /*+ patchSize*/) / patchSize);
    int ic = int((len2 /*+ patchSize*/) / patchSize);
    //for (float j = 0.0f; j <= len1; j+=patchSize)
    //for (float i = 0.0f; i <= len2; i+=patchSize)
    for (int ji = 0; ji <= jc; ji++)
    for (int ii = 0; ii <= ic; ii++)
    {
      float j = ji * patchSize;
      float i = ii * patchSize;
      Patch p;
      p.quadIndex = qi;
      p.color = q.color;
      p.normal = q.basis.n; // could avoid storing in the patch
      p.h = ((j + patchSize) <= len1) ? patchSize : (len1 - j);
      p.w = ((i + patchSize) <= len2) ? patchSize : (len2 - i);

      //p.u = int(u2 - ((u2-u1) * (j - 0.5*patchSize) / (len1 + patchSize)));
      //p.v = int(v1 + ((v2-v1) * (i + 0.5*patchSize) / (len2 + patchSize)));
      
      //p.u = int(u2 - ((u2-u1+2) * (j) / (len1 + patchSize)) + 0.0);//*patchSize);
      //p.v = int(v1 + ((v2-v1+2) * (i) / (len2 + patchSize)) + 0.0);//*patchSize);
      
      //p.u = int(u1 + (len1 - patchSize - j) / patchSize);
      //p.v = int(v1 + i / patchSize);
      
      // int ji2 = (ji == jc) ? jc-1 : ji;
      p.u = int(u1) + (jc - ji);//+ jc - (ji2 + 1);
      p.v = int(v1) + ii;
      
      //p.v = int(v1 + i / patchSize);
      //p.v = int(v1) + int(ii * float(ic+0.1f) / float(ic));
      
      //p.u = int(u2 - ((u2-u1+2) * (j) / (len1 + patchSize)) + 0.0);//*patchSize);
      //p.v = int(v1 + ((v2-v1+2) * (i) / (len2 + patchSize)) + 0.0);//*patchSize);
      
      // TODO: figure out why negative
      // b and t are facing wrong way so using q.d instead of q.b
      p.position = q.d + scaleVector(q.basis.t, i + (p.w*0.5f)) + scaleVector(q.basis.b, j + (p.h*0.5f));
      addPatchToScene(scn, p);
    }
  }
}

      
bool intersects(const Ray& r, const Shape& o)
{
  // ray:    start, end
  // shape:  x,y,z, w,h,d;
  //return false;
  float bMinX = o.x;
  float bMaxX = o.x + o.w;
  float bMinY = o.y;
  float bMaxY = o.y + o.h;
  float bMinZ = o.z;
  float bMaxZ = o.z + o.d;

  vec4f r0 = r.start;


  if (r.start.x < bMinX && r.end.x < bMinX)
    return false;
  if (r.start.y < bMinY && r.end.y < bMinY)
    return false;
  if (r.start.z < bMinZ && r.end.z < bMinZ)
    return false;
  if (r.start.x > bMaxX && r.end.x > bMaxX)
    return false;
  if (r.start.y > bMaxY && r.end.y > bMaxY)
    return false;
  if (r.start.z > bMaxZ && r.end.z > bMaxZ)
    return false;


  float d1 = (bMinX - r0.x)*r.invN.x;
  float d2 = (bMaxX - r0.x)*r.invN.x;
/*
  r0.x = bMinX - d1/r.invN.x

  if (d1 <= 0.0f)
    return false;
*/
  float tmin = std::min(d1, d2);
  float tmax = std::max(d1, d2);

  float ty1 = (bMinY - r0.y)*r.invN.y;
  float ty2 = (bMaxY - r0.y)*r.invN.y;
  //if (ty1 <= 0.0f)
    //return false;

  tmin = std::max(tmin, std::min(ty1, ty2));
  tmax = std::min(tmax, std::max(ty1, ty2));

  float tz1 = (bMinZ - r0.z)*r.invN.z;
  float tz2 = (bMaxZ - r0.z)*r.invN.z;

  tmin = std::max(tmin, std::min(tz1, tz2));
  tmax = std::min(tmax, std::max(tz1, tz2));
  if (tmax <= 0.0f)
    return false;

  return (tmax >= tmin); // && (tmax > 0.0f);
}


void processPatchLight(Patch& p, const LightParameters& light, const Scene& scn)
{
  vec4f l = light.position;
  bool shadowTest = light.castShadows;
  p.inShadow = false;
  if (shadowTest)
  {
    Ray r = { p.position, l };
    vec4f rayN = r.end - r.start;
    Math::normalizeVector(&rayN.x);
    r.start = r.start + (rayN * 0.00001f);
    vec4f invN = {{{ 1.0f/rayN.x, 1.0f/rayN.y, 1.0f/rayN.z, 0.0f }}};
    r.invN = invN;

    // TODO: can do this same thing for figuring out the TOPX FFs are directly connected
    // TODO: make TOPX a runtime value - perhaps could progressively increase over time
    //       until convergence - need a frame-to-frame measure of convergence and a quality
    //       setting on when to stop
    for (auto&o : scn.objects)
    {
      if (intersects(r, o))
      {
        p.inShadow = true;
        break;
      }
    }
  }

  if (!p.inShadow)
  {
    vec4f& n = p.normal;
    vec4f toL = l - p.position;
    Math::normalizeVector(&toL.x);
    float intensity = Math::dotProduct(&toL.x, &n.x);
    p.directLightIntensity += (0.7f * intensity);
  }
}


void initializePatches(Scene& scn)
{
  for (auto& p : scn.patches)
  {
    for (int i = 0; i < TOPX; i++)
    {
      p.worstFF = 0.0f;
      p.topFF[i] = 0.0f;
      p.topIdx[i] = -1;
    }
  }
}


void initializePatchLighting(Scene& scn)
{
  for (auto& p : scn.patches)
  {
    p.directLightIntensity = 0.3f;
    
    // TODO: iterate list of lights
    processPatchLight(p, scn.light, scn);

    // TODO: idea is we need a shadow bit to tell if we apply spec or not
    //       spec of a given light shouldn't happen where that light is in shadow
    //       could use the alpha channel and bits of it for this shadow bit for
    //       a number of shadow casting lights
    p.color = p.color * p.directLightIntensity;
  }
}


float nusseltFormFactor(const Patch& patch1, const Patch& patch2, Scene& scene)
{
  // Could cache the tangents and bi-normals
  mat4f p1 = scene.quads[patch1.quadIndex].basis;
  mat4f p2 = scene.quads[patch2.quadIndex].basis;

  // get relative position of patch 2 to patch 1
  vec4f vecToPatch2 = patch2.position - patch1.position;

  // The corners in world space could be cached instead but at more storage cost
  vec4f corner1 = vecToPatch2 + p2.t + p2.b;
  vec4f corner2 = vecToPatch2 - p2.t + p2.b;
  vec4f corner3 = vecToPatch2 - p2.t - p2.b;
  vec4f corner4 = vecToPatch2 + p2.t - p2.b;

  // This is the projection on to a unit hemisphere
  Math::normalizeVector(&corner1.x);
  Math::normalizeVector(&corner2.x);
  Math::normalizeVector(&corner3.x);
  Math::normalizeVector(&corner4.x);

  // rotate in to coordinate space of patch 1
  corner1 = rotateVector(corner1, p1);
  corner2 = rotateVector(corner2, p1);
  corner3 = rotateVector(corner3, p1);
  corner4 = rotateVector(corner4, p1);

  // Now the x,y of the corners is their projection on to the circle
  vec4f v = corner1 - corner2;
  vec4f w = corner3 - corner2;
  float area = 0.5f * std::fabs(v.x * w.y - v.y * w.x);
  v = corner3 - corner4;
  w = corner1 - corner4;
  area += 0.5f * std::fabs(v.x * w.y - v.y * w.x);

  return area;
}


struct ConeShape
{
   vec4f  apex;
   vec4f  normal;   // direction from apex out along the axis of the solid part of the cone
   float  angle;    // angle in radians of the direction from the normal to the outside of cone
   float  length;   // for a closed cone, the length from apex along the normal until reach the base of the cone
   // only need 3 float values for the apex and normal, so could put the angle and length in the w components of those
};


void CreateConeFromPatch(ConeShape& cone, const Patch& p)
{
  cone.apex = p.position;
  cone.normal = p.normal;
  cone.angle = Math::degreesToRadians(90.0);
  cone.length = 0.5f; // Should be some kind of tweakable value
}


void calculateFormFactorScales(Scene& scn)
{
  printf("calculating form factors\n");
  printf("please wait...\n");
  for (auto& p1 : scn.patches)
  {
    p1.totalFormFactors = 0.001f;

    // TODO: This is where we can use the octree to select a cone radiating from the patch p1 so
    // we can reduce the number of patches we test it against (instead of iterating all other patches)

    // Remember the idea to make the octree be a multi-tree of normals


    auto f = [](const DTree<3>::DTreeBounds& b)
    {
      // TODO: cone vs box intersection test
      return true;
    };
    auto ctx = scn.patchOctree.InitializeQuery(f);

    // This is alternative code to iterate via the octree, but seems to not be working - probably not returning all
    PatchObject* obj = nullptr;
    while ((obj = (PatchObject*)scn.patchOctree.QueryNext(ctx)) != nullptr)
    {
      int idx = obj->idx;

/*
    // //for (auto& p2 : scn.patches)
    for (int idx = 0; idx < scn.patches.size(); idx++)
    {
*/

      auto& p2 = scn.patches[idx];
      if (&p1 != &p2)
      {
        float ff = simpleFormFactor(p1, p2, scn);
        p1.totalFormFactors += ff;

        // Only add to the topX if it is better than the worst one we have
        if (ff > p1.worstFF)
        {
          // We aren't taking in to account shadows, are we?
          for (int i = 0; i < TOPX; i++)
          {
            if (p1.topIdx[i] == -1 || p1.topFF[i] < ff)
            {
              p1.topIdx[i] = idx;
              p1.topFF[i] = ff;
              break;
            }
          }

          // Update worstFF
          p1.worstFF = ff;
          for (int i = 0; i < TOPX; i++)
          {
            if (p1.topFF[i] < p1.worstFF)
            {
              p1.worstFF = p1.topFF[i];
            }
          }
        }


      }
    }
  }
  printf("done\n");
}


void calculateFormFactors(Scene& scn)
{
  //printf("calculating form factors\n");
  //printf("please wait...\n");
 
  for (auto& p1 : scn.patches)
    p1.newColor = p1.color;

  int topX = std::max(scn.settings.topXToUse, TOPX);

  // Collect pass
  for (auto& p1 : scn.patches)
  {
#if 1
    for (int i = 0; i < topX; i++)
    {
      if (p1.topIdx[i] != -1)
      {
        auto& p2 = scn.patches[p1.topIdx[i]];
        float ff = p1.topFF[i];
#else
    for (auto& p2 : scn.patches)
    {
      if (&p1 != &p2)
      {
        float ff = simpleFormFactor(p1, p2);
#endif
        //float a1 = p1.w * p1.h;
        //float a2 = p2.w * p2.h;
        float ff1 = 1.0f * ff;// / p1.totalFormFactors;
        float ff2 = 1.0f * ff;// / p2.totalFormFactors;

        /*
        //float ff = nusseltFormFactor(p1, p2, scn);
        const float maxTransfer = 2.0f;//0.05f;
        if ( ff1 < 0.0f )
          ff1 = 0.0f;
        if ( ff1 > maxTransfer )
          ff1 = maxTransfer;
        if ( ff2 < 0.0f )
          ff2 = 0.0f;
        if ( ff2 > maxTransfer )
          ff2 = maxTransfer;
          */

        /*
        float p1top2 = p1.intensity * ff;// / p1.totalFormFactors;
        float p2top1 = p2.intensity * ff;// / p2.totalFormFactors;
        p1.newIntensity += p2top1;
        p2.newIntensity -= p2top1;
        p1.newIntensity -= p1top2;
        p2.newIntensity += p1top2;
        */
        vec4f p1top2 = p1.color * ff1;
        vec4f p2top1 = p2.color * ff2;
        p1.newColor = p1.newColor + p2top1 - p1top2;
        p2.newColor = p2.newColor - p2top1 + p1top2;
      }
    }
  }

  for (auto& p : scn.patches)
  {
    float i = p.directLightIntensity;
    vec4f newColor = scn.quads[p.quadIndex].color * i * 0.05f;
    p.color = p.newColor * 0.95f + newColor;
    //p.intensity = std::min(1.0f, p.newIntensity * 0.95f + i * 0.05f);
    
    uint32_t col = 0xFF000000;
    float r = std::max(std::min(p.color.x, 1.0f), 0.0f);
    float g = std::max(std::min(p.color.y, 1.0f), 0.0f);
    float b = std::max(std::min(p.color.z, 1.0f), 0.0f);
    col |= int(r * 255.0f) << 16;
    col |= int(g * 255.0f) <<  8;
    col |= int(b * 255.0f) <<  0;
    scn.texture.bits[p.v * scn.texture.w + p.u] = col;
  }

  /*
  // Transmit pass
  for (auto& p1 : scn.patches)
  {
    for (auto& p2 : scn.patches)
    {
      if (&p1 != &p2)
      {
        float ff = -simpleFormFactor(p1, p2);
        float p1top2 = 0.1f * p1.intensity * ff / p1.totalFormFactors;
        //float p2top1 = p2.intensity * ff;// / p2.totalFormFactors;
        //p1.intensity += p2top1;
        //p2.intensity -= p2top1;
        p1.intensity -= p1top2;
        p2.intensity += p1top2;
      }
    }
  }
  */

  //printf("done\n");
}


Scene parseFile(const char* fileName)
{
  Scene scn;
  FILE* file = fopen(fileName, "rt");
  if (file)
  {
    char str[128];
    int converted = 0;
    float a, b, c, d, e, f, g, h;
    char v[128];
    while ((converted = fscanf(file, "%s %f, %f, %f, %f, %f, %f, %f, %f, %s\n", str, &a, &b, &c, &d, &e, &f, &g, &h, v)) >= 1)
    {
      if (strcmp(str, "Window,") == 0 && converted == 3)
      {
        scn.window = { a, b };
      }
      else if (strcmp(str, "Settings,") == 0 && converted == 8)
      {
        scn.settings = { int(a), b, c, d, e, f, g };
      }
      else if (strcmp(str, "Camera,") == 0 && converted == 7)
      {
        scn.camera = { {{{ a, b, c, 0.0f }}}, {{{ d, e, f, 0.0f }}} };
      }
      else if (strcmp(str, "Light,") == 0 && converted == 10)
      {
        bool castShadows = (strcmp(v, "true") == 0) ? true : false;
        scn.light = { {{{ a, b, c, 0.0f }}}, {{{ d, e, f, 0.0f }}}, g, h, castShadows };
      }
      else if (strcmp(str, "Box,") == 0 && converted == 7)
      {
        Shape obj = { Box, a, b, c, d, e, f };
        scn.objects.push_back(obj);
      }
      else
      {
        printf("Unknown type: %s\n", str);
      }
    }
  } else {
    printf("file not found\n");
  }
  fclose(file);
  return scn;
}


void dumpSceneInfo(Scene& scn)
{
  printf("scn   objs: %lu   quads: %lu   patchs: %lu\n", scn.objects.size(), scn.quads.size(), scn.patches.size());

  for (auto q : scn.quads)
  {
    printf("quad:   %04f %04f %04f   %04f %04f %04f   %04f %04f %04f   %04f %04f %04f\n",
        q.a.x, q.a.y, q.a.z,   q.b.x, q.b.y, q.b.z,   q.c.x, q.c.y, q.c.z,   q.d.x, q.d.y, q.d.z);
  }

  // 200,000 patches
  // 200,000 x 200,000 potential interactions
  // 40,000,000,000 - At 1 GHz, it is 40s for each cycle needed
  
  // 50,000 patches
  // 50,000 x 50,000 potential interactions
  // 2,500,000,000 - At 1 GHz, it is 2.5s for each cycle needed

  // TODO: optimization ideas:
  // perhaps could optimize with spatial structures
  // BSP could allow only checking patches in front of given patch
  // Quad-tree could allow testing near by patches
  // perhaps could check patches against quads first to speed up
  // perhaps could check patches against quads in a spatial structure
  // could put patches in buckets based on normals and find patches with opposite normals
  // lots of ways to narrow the search. Probably need both close by ones and opposite normal ones

  // could put in a octree/quadtree with bitmask of normal classification - can search by distance and classification quickly
  // could adaptively change the normal classification search with distance in the search - only search more widely for normals which are exactly opposite


  // 2,260 patches
  // 2,260 x 2,260 potential interactions
  // 5,107,600 - At 1 GHz, it is 195 cycles per second
}


vec4f reflect(vec4f in, vec4f norm)
{
  return in - (norm * (2.0f * dot3(in, norm)));
}


#include <OpenGL/gl.h> // suggest not include directly but make the framework provide an include which includes the system one

void updateTexture(Texture& tex)
{
  if (!tex.bits)
    return;
  
  // Creating texture
  if (tex.state == 0)
  {
    if (tex.texId != 0)
      glDeleteTextures(1, &tex.texId);
    glGenTextures(1, &tex.texId);
    tex.state = 1;
  }

  glBindTexture(GL_TEXTURE_2D, tex.texId);
  glActiveTexture(GL_TEXTURE0);
  
  // Setting parameters
  if (tex.state == 1)
  {
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);//NEAREST);
    tex.state = 2;
  }

  // Uploading
  if (tex.state == 2)
  {
    // upload texture
    //printf("Uploading texture %i %i\n", tex.w, tex.h);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, tex.w, tex.h, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, tex.bits);
    // We need to keep re-uploading if we are still refining the radiosity solution
    //tex.state = 3;
  }
}

void drawScene(Scene& scene, bool phongType, bool drawUsingPatches, bool useLightMap, float* modelViewMatrix, float* translation)
{
  if (!drawUsingPatches && useLightMap)
  {
    glEnable(GL_TEXTURE_2D);
    updateTexture(scene.texture);
  }
  glBegin(GL_TRIANGLES);
  {
    vec4f l = scene.light.position;
    float shininess = scene.light.shininess;
    float lightPower = scene.light.power;
    vec4f lightColor = scene.light.color;

    if (drawUsingPatches)
    {
      for (auto p : scene.patches)
      {
        mat4f p2 = scene.quads[p.quadIndex].basis;
        vec4f vecToPatch2 = p.position;
        vec4f t = scaleVector(p2.t, p.w * 0.5f);
        vec4f b = scaleVector(p2.b, p.h * 0.5f);
        vec4f qa = vecToPatch2 - t - b;
        vec4f qb = vecToPatch2 - t + b;
        vec4f qc = vecToPatch2 + t + b;
        vec4f qd = vecToPatch2 + t - b;
        vec4f v[4] = { qa, qb, qc, qd };
        int index[6] = { 0, 1, 2,  2, 3, 0 };
        for (int i = 0; i < 6; i++)
        {
		      const vec4f zero = {{{ 0.0f, 0.0f, 0.0f, 0.0f }}};
		      // const vec4f cam = {{{ translation[0], translation[1], translation[2], 0.0f }}};
		      // const vec4f cam = {{{ modelViewMatrix[12], modelViewMatrix[13], modelViewMatrix[14], 0.0f }}};
		      // const vec4f cam = {{{ modelViewMatrix[3], modelViewMatrix[7], modelViewMatrix[11], 0.0f }}};
          int j = index[i];

          //vec4f& n = q.basis.n;
          vec4f N = p2.n;
          vec4f V = v[j];
          vec4f L = l;

          Math::transformVector(&V.x, modelViewMatrix, &v[j].x);

          vec4f toL = L - V;
          float distSqr = dot3(toL, toL);
          toL = toL * (1.0f/sqrt(distSqr));
		      //vec4f viewDir = cam - V;
		      vec4f viewDir = zero - V;
          Math::normalizeVector(&viewDir.x);
          float angle;
          vec4f color = p.color;

          if (!p.inShadow)
          {
            float pf = 1.0f;
            if (phongType)
            {
              // Real Phong
              vec4f fromL = zero - toL;
              vec4f R = reflect(fromL, N);
              angle = dot3(R, viewDir);
              pf = 0.25f;
              pf = 0.35f;
            }
            else
            {
              // Blinn-Phong
              vec4f H = toL + viewDir;
              Math::normalizeVector(&H.x); // ??? How is Blinn-Phong an optimization??
              angle = dot3(H, N);
            }

            if (angle < 0.0f)
              angle = 0.0f;
            float specIntensity = std::pow(angle, shininess * pf);

            color = color + (lightColor * (specIntensity * lightPower / distSqr));
          }

          //glColor3f(p.color.x, p.color.y, p.color.z);
          glColor3f(color.x, color.y, color.z);
          
          glVertex3f(v[j].x, v[j].y, v[j].z);
        }
      }
    }
    else
    {
      if (useLightMap)
      {
        // Textured quads using light map
        for (auto q : scene.quads)
        {
          vec4f v[4] = { q.a, q.b, q.c, q.d };
          int index[6] = { 0, 1, 2,  2, 3, 0 };
          for (int i = 0; i < 6; i++)
          {
            int j = index[i];
            //glTexCoord2f(q.uvs[j].x/float(scene.texture.w), (scene.texture.h-q.uvs[j].y)/float(scene.texture.h));
            glTexCoord2f(q.uvs[j].x, q.uvs[j].y);
            //glColor3f(q.color.x, q.color.y, q.color.z);
            glVertex3f(v[j].x, v[j].y, v[j].z);
          }
        }
      }
      else
      {
        // Gourard L.N shaded flat color quads
        for (auto q : scene.quads)
        {
          vec4f v[4] = { q.a, q.b, q.c, q.d };
          float LdotN[4];
          for (int i = 0; i < 4; i++)
          {
            vec4f& n = q.basis.n;
            vec4f toL = l - v[i];
            Math::normalizeVector(&toL.x);
            LdotN[i] = Math::dotProduct(&toL.x, &n.x);
          }
          int index[6] = { 0, 1, 2,  2, 3, 0 };
          for (int i = 0; i < 6; i++)
          {
            int j = index[i];
            float d = LdotN[j];
            glColor3f(d, d, d); glVertex3f(v[j].x, v[j].y, v[j].z);
          }
        }
      }
    }
  }
  glEnd();
}


#include "Widget.h"
#include "CommonWidgets.h"
#include "Painter.h"
#include "Application.h"
USING_NAMESPACE


class OpenGLWindow : public Widget
{
public:
  OpenGLWindow(Widget* a_parent, const char* a_name, Scene& scn)
    : Widget(a_parent, a_name), scene(scn)
    , vbox(this)
    , quitButton(&vbox, "Quit")
  {
    new CheckBox(&vbox, drawPatches, "Render Patches");
    new CheckBox(&vbox, useLightMap, "Use Light Map");
    new CheckBox(&vbox, useRealPhong, "Use Real Phong (or Blinn-Phong)");
    HBox *fovH = new HBox(&vbox);
    new Label(fovH, fovLabel);
    new Slider(fovH, fovValue);

    // TODO: how to handle destruction of owned objects?
    //       in example above we have vbox which is a member of the window so will
    //       automatically be created and destroyed by the C++ language when the
    //       window is created and destroyed. However it is also 'owned' by the
    //       window in that we passed 'this' to it. Similarly quitButton is owned
    //       by vbox, but also will automatically be created and destroyed by the
    //       window. Another case is objects that are owned but are not automatically
    //       destroyed. In the above code, two unnamed checkboxes are created.
    //       They will currently not get destroyed. If the objects automatically
    //       destroy their children, that would be a problem for vbox and quitButton.
    //       A solution might be to deleteLater where if the object is not deleted
    //       within the next frame, it will then automatically be deleted. This
    //       causes a frame delay and is not explicit. Another solution is to use
    //       a policy - possibly application wide or possibly for the window.
    //       The policy could be one of many, firstly to do nothing as currently
    //       is the case (everything is the programmers responsibility). Second
    //       policy could be the default one which is a debug mode that does nothing
    //       however everything is reference counted and the code can check and
    //       warn at runtime that something could leak. The third policy could
    //       do the deleteLater thing.

    fovLabel = "FOV";
    fovValue = 32000;//45;
    onFovChanged(fovValue.value());
    connect(quitButton.activated, this, &OpenGLWindow::onQuit); 
    connect(fovValue.valueChanged, this, &OpenGLWindow::onFovChanged); 

    tx = (int)scn.camera.position.x;
    tx = (int)scn.camera.position.y;
    rx = (int)scn.camera.rotation.x;
    ry = (int)scn.camera.rotation.y;
  
    lightPosition = scene.light.position;
  }

  int frame = 0;
  vec4f lightPosition;
  void updateLight()
  {
    frame++;
    vec4f pos;
    pos.x =  20.0f * cos(float(frame) / 10.0f); 
    pos.z = -20.0f * sin(float(frame) / 10.0f); 
    pos.y = 0.0f;
    pos.w = 0.0f;
    scene.light.position = lightPosition + pos;
    void initializePatchLighting(Scene& scn);
    initializePatchLighting(scene);
  }

  float fovRadians;
  void onFovChanged(int f)
  {
    printf("fov changed: %i\n", f);
    float ang = 180.0f * float(f) / 65536.0f;
    fovRadians = Math::degreesToRadians(ang);
  }

  void onQuit(int)
  {
    exit(0);
  }

  void keyEvent(KeyEvent& a_event) override
  {
    printf("got key event %c\n", a_event.m_key);
    if (int(a_event.m_key) == 'w' || int(a_event.m_key) == 'W')
    {
      tz += 1;
    }
    if (int(a_event.m_key) == 's')
    {
      tz -= 1;
    }
    if (int(a_event.m_key) == 'a')
    {
      tx += 1;
    }
    if (int(a_event.m_key) == 'd')
    {
      tx -= 1;
    }
    repaint();
  }

  void mouseEvent(MouseEvent& a_event) override
  {
    int x = a_event.m_position.m_x;
    int y = a_event.m_position.m_y;
    if (lastX == -1) { lastX = x; lastY = y; }
    if (a_event.m_buttons == MB_Right)
    {
      tx += x - lastX; ty += y - lastY;
    }
    else if (a_event.m_buttons == MB_Left)
    {
      trackingMouse = !trackingMouse;
      x = -1;
    }
    else if (trackingMouse)
    {
      rx += x - lastX; ry += y - lastY;
    }
    else
    {
      x = -1;
    }
    lastX = x; lastY = y;
    repaint();
  }
    
  void paintEvent(PaintEvent& a_event) override
  {
    Painter p(this);
    p.setPen(0x000000);
    int w = width() / 2;
    int h = height() / 2;
    p.drawLine(5, 5, w-5, 5);
    p.drawLine(5, 5, 5, h-5);
    p.drawLine(w-5, 5, w-5, h-5);
    p.drawLine(5, h-5, w-5, h-5);
    //repaint();
  }

  void updateRadiosity()
  {
    static int frame = 0;
    int frameWait = 1;
    float timesPerFrame = scene.settings.timesPerFrame;
    if (timesPerFrame < 1.0f)
    {
      frameWait = int(1.0f / timesPerFrame);
      timesPerFrame = 1.0f;
    }
    if ((frame % frameWait) == 0)
      for (int i = 0; i < timesPerFrame; i++)
        calculateFormFactors(scene);
    frame++;
  }

  //void _glPaint()
  void glPaint() override
  {
    updateLight();
    updateRadiosity();

    float projectionMatrix[16];
    float w = width();
    float h = height();
    Math::makePerspectiveMatrix4x4(projectionMatrix, fovRadians, w / h, 1.0f, 1000.f);
    glMatrixMode(GL_PROJECTION);
    glLoadMatrixf(projectionMatrix);

    float modelViewMatrix[16];
    float translation[4] = { float(tx), float(ty), float(tz), 0.0f };
    float rotation[4] = { float(ry), float(rx), float(rz), 0.0f };
    Math::translationRotationScaleToMatrix4x4(modelViewMatrix, translation, rotation, 1.0f);
    glMatrixMode(GL_MODELVIEW);
    glLoadMatrixf(modelViewMatrix);

    glDepthMask(GL_TRUE);
    glDisable(GL_BLEND);
    glDisable(GL_TEXTURE_2D);
    glEnable(GL_DEPTH_TEST);
    glEnable(GL_CULL_FACE);
    glCullFace(GL_FRONT);
    glFrontFace(GL_CW);
    //glDisable(GL_CULL_FACE);
    
    //glDepthFunc(GL_LESS);
    glClearDepth(1.0f);
    glClear(GL_DEPTH_BUFFER_BIT);

    glViewport(25, 25, width() - 50, height() - 50); // clip the drawing to this and 

    /*
    glBegin(GL_TRIANGLES);
      glColor3f( 1.0f, 0.0f, 0.0f ); glVertex3f( 0.0f, 0.0f, 10.0f );
      glColor3f( 0.0f, 1.0f, 0.0f ); glVertex3f( 0.0f, 10.0f, 10.0f );
      glColor3f( 0.0f, 0.0f, 1.0f ); glVertex3f( 10.0f, 1.0f, 10.0f );
    glEnd();
    */

    drawScene(scene, useRealPhong.value(), drawPatches.value(), useLightMap.value(), modelViewMatrix, translation);

    // Reset state
    glColor3f(1.0f, 1.0f, 1.0f);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    glDepthMask(GL_FALSE);
    glDisable(GL_DEPTH_TEST);
    glDisable(GL_CULL_FACE);
    glDisable(GL_TEXTURE_2D);
  }

private:
  Scene&           scene;
  Property<bool>   useRealPhong;
  Property<bool>   drawPatches;
  Property<bool>   useLightMap;
  Property<String> fovLabel;
  Property<int>    fovValue;
  VBox             vbox;
  Button           quitButton;

  bool trackingMouse = false;
  int tx = 0, ty = 0, tz = -120;
  int rx = 0, ry = 0, rz = 0;
  int lastX = -1, lastY = -1;
};


void formFactorDisplayTest(Scene& scn)
{
  Application app;
  OpenGLWindow testWin(0, "Test Window", scn);
  testWin.setNewSize((int)scn.window.w, (int)scn.window.h);
  app.exec();
}


void octreeTest()
{
  DTree<3> testOctree;

  int idx = 0;
  for (int i = 0; i < 4; i++)
  for (int j = 0; j < 4; j++)
  for (int k = 0; k < 4; k++)
  {
    Patch p;
    p.position.v[0] = i;
    p.position.v[1] = j;
    p.position.v[2] = k;
    PatchObject *obj = new PatchObject(p, idx++);
    testOctree.AddObject(obj);
  }

  printf("bounds: %i,%i,%i  size: %i\n",
      testOctree.bounds.origin.v[0],
      testOctree.bounds.origin.v[1],
      testOctree.bounds.origin.v[2],
      testOctree.bounds.size);
  
  auto f = [](const DTree<3>::DTreeBounds& b)
  {
    return true;
  };
  
  std::vector<DTree<3>::ObjectInterface*> objList = testOctree.QueryExecute(f);
  for (auto o : objList)
  {
    PatchObject* obj = (PatchObject*)o;
    int idx = obj->idx;
    printf("idx: %i\n", idx);
  }
  printf("---\n");
    
  auto ctx = testOctree.InitializeQuery(f);

  // This is alternative code to iterate via the octree, but seems to not be working - probably not returning all
  PatchObject* obj = nullptr;
  while ((obj = (PatchObject*)testOctree.QueryNext(ctx)) != nullptr)
  {
    int idx = obj->idx;
    printf("idx: %i\n", idx);
  }
  

  exit(0);
}


void MakeRegularDodecahedron(std::vector<vec4f>& verts, float radius)
{
  verts.clear();
  verts.reserve(20); // The number of vertices expected

  float phi = Math::phi();
  float a = radius / sqrt(3.0f);
  float b = a / phi;
  float c = a * phi;

  float v[2] = { -1.0f, 1.0f };
  for (int i = 0; i < 2; i++)
  {
    for (int j = 0; j < 2; j++)
    {
      float ib = v[i] * b;
      float jc = v[j] * c;
      verts.push_back((vec4f){{{ 0.0f, ib, jc, 0.0f }}});
      verts.push_back((vec4f){{{ ib, jc, 0.0f, 0.0f }}});
      verts.push_back((vec4f){{{ ib, 0.0f, jc, 0.0f }}});
      for (int k = 0; k < 2; k++)
      {
        verts.push_back((vec4f){{{ v[i] * a, v[j] * a, v[k] * a, 0.0f }}});
      }
    }
  }
}


template <typename T>
class BlendProperty : public AbstractProperty<T>
{
public:
	BlendProperty() {}
	BlendProperty(const T& val) : m_value(val) {}
	BlendProperty(const BlendProperty<T>& other) : m_value(other.m_value) {}
  
	BlendProperty<T>& operator=(T const & a_newValue)
	{
		AbstractProperty<T>::setValue(a_newValue);
		return *this;
	}

  float tween(float time);

protected:
	T const & get() const
  {
    return m_value;
  }
	void set(T a_newValue)
  {
    m_lastSetTime = m_lastTime;
    m_previousValue = m_value;
    m_value = a_newValue;
  }

private:
  float m_lastSetTime = 0.0f;
  float m_lastTime = 0.0f;
	T m_previousValue;
	T m_value;
};

  
template <typename T>
float BlendProperty<T>::tween(float time)
{
  m_lastTime = time;
  float deltaTime = time - m_lastSetTime;
  T deltaValue = m_value - m_previousValue;
  if (deltaTime >= 1.0f) // 1 second
    return float(m_value);
  return float(m_previousValue) + float(deltaValue) * deltaTime;
}


template <>
float BlendProperty<bool>::tween(float time)
{
  float v = (m_value) ? 1.0f : 0.0f;
  float l = (m_previousValue) ? 1.0f : 0.0f;
  m_lastTime = time;
  float deltaTime = time - m_lastSetTime;
  if (deltaTime >= 1.0f) // 1 second
    return v;
  return l + (v - l) * deltaTime;
}


class GeometryExplorerWindow : public Widget
{
public:
  GeometryExplorerWindow(Widget* a_parent, const char* a_name)
    : Widget(a_parent, a_name)
    , vbox(this)
    , quitButton(&vbox, "Quit")
  {
    connect(quitButton.activated, this, &GeometryExplorerWindow::onQuit); 
    new CheckBox(&vbox, regular,   "Regular Shapes");   // IAP unlock
    new CheckBox(&vbox, euclidean, "Euclidean Space");  // IAP unlock
    new CheckBox(&vbox, fractal,   "Fractal Shapes");   // IAP unlock
    new CheckBox(&vbox, color,     "Use Colours");
    new CheckBox(&vbox, ortho,     "Orthographic Projection");
    new CheckBox(&vbox, spherical, "Projected on a Sphere");
    new CheckBox(&vbox, dual,      "Display the dual");
    Grid *g = new Grid(&vbox, 2);
    new GridSlider(g, fov,         "Field of view");
    new GridSlider(g, scale,       "Size");
    new GridSlider(g, dimensions,  "Dimensions");
    new GridSlider(g, complexity,  "Complexity");
    new GridSlider(g, view,        "View"); // Camera angle
    startTimer(30);
  }
  
  void onQuit()
  {
    exit(0);
  }
  
  VBox                vbox;
  Button              quitButton;
  float               time = 0.0f;       // 0 -> infinity
  BlendProperty<bool> regular = true;    // true or false  => restrict to platonic or constrained type of shapes
  BlendProperty<bool> euclidean = true;  // true or false  => allow non-euclidean geometry so space can curve?
  BlendProperty<bool> fractal = false;   // true or false  => show fractal geometries
  BlendProperty<bool> color = false;     // true or false  => colorize
  BlendProperty<bool> ortho = false;     // true or false  => display with orthographic projection
  BlendProperty<bool> spherical = false; // true or false  => projected on to a sphere
  BlendProperty<bool> dual = false;      // true or false  => display the dual
  BlendProperty<int>  fov = 32000;       // field of view
  BlendProperty<int>  scale = 32000;     // scale
  BlendProperty<int>  dimensions = 2;    // 0 -> 3         => perhaps later could expand to higher dimensions, but only 3 at first  
  BlendProperty<int>  complexity = 10;   // 0.0 -> 1.0     => from least complex to most complex type of geometry
  BlendProperty<int>  view = 0;          // the orientation to center the orthographic projection about - spinning, edge, normal, vertex, face

	void timerEvent(TimerEvent& a_event) override
  {
    repaint();
  }

  void paintEvent(PaintEvent& a_event) override
  {
    time += 0.05f;

    Painter p(this);
    p.setPen(0x000000);
    int w = width() / 2;
    int h = height() / 2;
    p.drawLine(5, 5, w-5, 5);
    p.drawLine(5, 5, 5, h-5);
    p.drawLine(w-5, 5, w-5, h-5);
    p.drawLine(5, h-5, w-5, h-5);
  }

  void glPaint() override
  {
    float size = float(scale.tween(time)) / 3200.0f;
    float dim = (dimensions.tween(time) *  4.0f) / 65536.0f;//>> 16;
    float num = (complexity.tween(time) * 60.0f) / 65536.0f;//>> 16;
    
    float d1 = std::min(std::max(dim - 0.0f, 0.0f), 1.0f);
    float d2 = std::min(std::max(dim - 1.0f, 0.0f), 1.0f);
    float d3 = std::min(std::max(dim - 2.0f, 0.0f), 1.0f);
    float d4 = std::min(std::max(dim - 3.0f, 0.0f), 1.0f);

    float projectionMatrix[16];
    float w = width();
    float h = height();
    float ang = 180.0f * float(fov()) / 65536.0f;
    float fovRadians = Math::degreesToRadians(ang);
  
    Math::makePerspectiveMatrix4x4(projectionMatrix, fovRadians, w / h, 1.0f, 1000.f);
    glMatrixMode(GL_PROJECTION);
    glLoadMatrixf(projectionMatrix);

    float modelViewMatrix[16];
    int tx = 0, ty = 0, tz = -120;
    float rx = time * 10.0f * d3;//std::max(dim - 2.0f, 0.0f);
    float ry = time * 12.0f * d4;//std::max(dim - 3.0f, 0.0f);
    float rz = time * 14.0f;
    float translation[4] = { float(tx), float(ty), float(tz), 0.0f };
    float rotation[4] = { float(ry), float(rx), float(rz), 0.0f };
    Math::translationRotationScaleToMatrix4x4(modelViewMatrix, translation, rotation, 1.0f);
    glMatrixMode(GL_MODELVIEW);
    glLoadMatrixf(modelViewMatrix);

    glDepthMask(GL_TRUE);
    glDisable(GL_BLEND);
    glDisable(GL_TEXTURE_2D);
    glEnable(GL_DEPTH_TEST);

    glEnable(GL_CULL_FACE);
    glCullFace(GL_FRONT);
    glFrontFace(GL_CW);
    glDisable(GL_CULL_FACE);
    
    //glDepthFunc(GL_LESS);
    glClearDepth(1.0f);
    glClear(GL_DEPTH_BUFFER_BIT);

    glViewport(25, 25, width() - 50, height() - 50); // clip the drawing to this and 

    /*
    glBegin(GL_TRIANGLES);
      glColor3f( 1.0f, 0.0f, 0.0f ); glVertex3f( 0.0f, 0.0f, 10.0f );
      glColor3f( 0.0f, 1.0f, 0.0f ); glVertex3f( 0.0f, 10.0f, 10.0f );
      glColor3f( 0.0f, 0.0f, 1.0f ); glVertex3f( 10.0f, 1.0f, 10.0f );
    glEnd();
    */




    float a = d1 * size;//std::min(size, 1.0f);  //   dim 0 -> 1   1.0 -> 10.0
    float b = d2 * size * sqrt(2.0);//std::min(size, 1.0f);  //   dim 0 -> 1   1.0 -> 10.0
    float c = d3 * size * sqrt(2.0);//std::min(size, 1.0f);  //   dim 0 -> 1   1.0 -> 10.0
    float radians = 0.5f * 3.1429f;
    vec4f verts[5][32];
    verts[0][0] = (vec4f){{{ 0.0f, b * 0.333f, c, 0.0f }}};
    verts[0][1] = (vec4f){{{ cosf(radians), sinf(radians) + b, 0.0f, 0.0f }}};
    for (int i = 0; i < 14; i++)
    {
      radians = (0.5f - float(i)/13.0f) * 3.1429f;
      verts[0][i+2] = (vec4f){{{ cosf(radians) + a, sinf(radians), 0.0f, 0.0f }}};
    }
    radians = -0.5f * 3.1429f;
    verts[0][16] = (vec4f){{{ cosf(radians), sinf(radians), 0.0f, 0.0f }}};
    for (int i = 0; i < 14; i++)
    {
      radians = (-0.5f - float(i)/13.0f) * 3.1429f;
      verts[0][i+1+16] = (vec4f){{{ cosf(radians) - a, sinf(radians), 0.0f, 0.0f }}};
    }
    verts[0][31] = verts[0][1];




    verts[1][0] = (vec4f){{{ 0.0f, b * 0.333f, c, 0.0f }}};
    verts[1][1] = (vec4f){{{ cosf(radians), sinf(radians) + b, 0.0f, 0.0f }}};
    for (int i = 0; i < 14; i++)
    {
      radians = (0.5f - float(i)/13.0f) * 3.1429f;
      verts[1][i+2] = (vec4f){{{ cosf(radians) + a, sinf(radians), 0.0f, 0.0f }}};
    }
    radians = -0.5f * 3.1429f;
    verts[1][16] = (vec4f){{{ cosf(radians), sinf(radians), 0.0f, 0.0f }}};
    for (int i = 0; i < 14; i++)
    {
      radians = (-0.5f - float(i)/13.0f) * 3.1429f;
      verts[1][i+1+16] = (vec4f){{{ cosf(radians) - a, sinf(radians), 0.0f, 0.0f }}};
    }
    verts[1][31] = verts[0][1];


    {
      vec4f vert[32];
      for (int i = 0; i < 32; i++)
      {
        // TODO: blend verts[x] and verts[x+1]
        //  vert[i] = verts[0][i];
        vert[i] = verts[1][i];
      }
      glBegin(GL_TRIANGLES);
      for (int i = 0; i < 30; i++)
      {
        glColor3f( 0.0f, 0.0f, 0.0f ); glVertex3f( vert[0].v[0],   vert[0].v[1],   vert[0].v[2]   );
        glColor3f( 0.0f, 0.0f, 0.0f ); glVertex3f( vert[i+1].v[0], vert[i+1].v[1], vert[i+1].v[2] );
        glColor3f( 0.0f, 0.0f, 0.0f ); glVertex3f( vert[i+2].v[0], vert[i+2].v[1], vert[i+2].v[2] );
      }
      glEnd();
    }

    glBegin(GL_TRIANGLES);
    {
      std::vector<vec4f> dodecahedronVerts;
      MakeRegularDodecahedron(dodecahedronVerts, 10.0f);
      //for (vec4f& v : dodecahedronVerts)
      for (int i = 0; i < dodecahedronVerts.size() - 2; i++)
      {
        //vec4f& v = dodecahedronVerts[i];
        vec4f* vert = &dodecahedronVerts[0];
        glColor3f( 1.0f, 0.0f, 0.0f ); glVertex3f( vert[0].v[0],   vert[0].v[1],   vert[0].v[2]   );
        glColor3f( 0.0f, 1.0f, 0.0f ); glVertex3f( vert[i+1].v[0], vert[i+1].v[1], vert[i+1].v[2] );
        glColor3f( 0.0f, 0.0f, 1.0f ); glVertex3f( vert[i+2].v[0], vert[i+2].v[1], vert[i+2].v[2] );

      }
    }
    glEnd();



    /*
    if (dim == 0)
    {
      glBegin(GL_TRIANGLES);
        glColor3f( 0.0f, 0.0f, 0.0f );
        glVertex3f( 0.0f, 0.0f, 0.0f );
        glColor3f( 0.0f, 0.0f, 0.0f );
        glVertex3f( 0.0f, 0.0f, 0.0f );
        glColor3f( 0.0f, 0.0f, 0.0f );
        glVertex3f( 0.0f, 0.0f, 0.0f );
      glEnd();
    }
    else if (dim == 1)
    {
      glBegin(GL_TRIANGLES);
        glColor3f( 0.0f, 0.0f, 0.0f );
        glVertex3f( 0.0f, 10.0f, 0.0f );
        glColor3f( 0.0f, 0.0f, 0.0f );
        glVertex3f( 0.0f, 10.0f, 1.0f );
        glColor3f( 0.0f, 0.0f, 0.0f );
        glVertex3f( 0.0f, 0.0f, 0.0f );
        
        glColor3f( 0.0f, 0.0f, 0.0f );
        glVertex3f( 0.0f, 0.0f, 0.0f );
        glColor3f( 0.0f, 0.0f, 0.0f );
        glVertex3f( 0.0f, 0.0f, 1.0f );
        glColor3f( 0.0f, 0.0f, 0.0f );
        glVertex3f( 0.0f, 10.0f, 1.0f );
      glEnd();
    }
    else if (dim == 2)
    {
      glBegin(GL_TRIANGLES);
        glColor3f( 0.0f, 0.0f, 0.0f );
        glVertex3f( 10.0f, -4.0f, 0.0f );
        glColor3f( 0.0f, 0.0f, 0.0f );
        glVertex3f( 0.0f, 8.0f, 0.0f );
        glColor3f( 0.0f, 0.0f, 0.0f );
        glVertex3f( -10.0f, -4.0f, 0.0f );
      glEnd();
    }
    else
    {
      MakeRegularDodecahedron(verts, size);
      glBegin(GL_TRIANGLES);
      for (vec4f& v : verts)
      {
        glColor3f( 1.0f, 0.0f, 0.0f );
        glVertex3f( v.v[0], v.v[1], v.v[2] );
        glColor3f( 1.0f, 0.0f, 0.0f );
        glVertex3f( v.v[0]+1.0f, v.v[1], v.v[2] );
        glColor3f( 1.0f, 0.0f, 0.0f );
        glVertex3f( v.v[0], v.v[1]+1.0f, v.v[2] );
      }
      glEnd();
    }
    */
    

    // Reset state
    glColor3f(1.0f, 1.0f, 1.0f);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    glDepthMask(GL_FALSE);
    glDisable(GL_DEPTH_TEST);
    glDisable(GL_CULL_FACE);
    glDisable(GL_TEXTURE_2D);
  }
};


void GeometryExplorerMain()
{
  Application app;
  GeometryExplorerWindow testWin(0, "Geometry Explorer");
  testWin.setNewSize(600, 800);
  app.exec();
}


void GeometryTests()
{
  printf("phi = %f\n", Math::phi());
  std::vector<vec4f> dodecahedronVerts;
  MakeRegularDodecahedron(dodecahedronVerts, 10.0f);
  for (vec4f& v : dodecahedronVerts)
  {
    printf("vert[] = { %f, %f, %f }\n", v.v[0], v.v[1], v.v[2]);
  }
  
  GeometryExplorerMain();
  exit(0);
}


void formFactorTest()
{
  //GeometryTests();
  
  // octreeTest();
  // exit(0);

  Scene scn = parseFile("scene.txt");
  createTexture(scn);
  convertObjectsToQuads(scn);
  convertQuadsToPatches(scn);
  initializePatches(scn);
  initializePatchLighting(scn);
  calculateFormFactorScales(scn);
  calculateFormFactors(scn);
  //dumpSceneInfo(scn);
  formFactorDisplayTest(scn);
  exit(0);
}


