Newer
Older
Import / research / 3d-experiments / Framework / Shapes.cpp
//
//  Sphere.cpp
//  BlockyFroggy
//
//  Created by John Ryland on 21/9/17.
//  Copyright © 2017 John Ryland. All rights reserved.
//

#include <cstdio>
#include <cmath>
#include <map>
#include <algorithm>
#include <cstdint>
#include "Shapes.h"


// Based on code from here:
//   http://blog.andreaskahler.com/2009/06/creating-icosphere-mesh-in-code.html
// Ideas for texturing
//   http://www.mvps.org/directx/articles/spheremap.htm
struct SphereCreatorState
{
  std::vector<vec3f>         positions;
  std::map<uint64_t, size_t> middlePointIndexCache;
};


// add vertex to mesh, fix position to be on unit sphere, return index
static void addVertex(std::vector<vec3f>& positions, const vec3f& p)
{
  float length = sqrt(p.x * p.x + p.y * p.y + p.z * p.z);
  positions.emplace_back(vec3f{p.x/length, p.y/length, p.z/length});
}


// return index of point in the middle of p1 and p2
static size_t getMiddlePoint(SphereCreatorState& state, size_t p1, size_t p2)
{
  // first check if we have it already
  uint64_t key = (p1 < p2) ? (p1 << 32 | p2) : (p2 << 32 | p1);
/*
  bool firstIsSmaller = p1 < p2;
  uint64_t smallerIndex = firstIsSmaller ? p1 : p2;
  uint64_t greaterIndex = firstIsSmaller ? p2 : p1;
  uint64_t key = (smallerIndex << 32) | greaterIndex;
  */
  if (state.middlePointIndexCache.count(key))
  {
    return state.middlePointIndexCache[key];
  }
  
  // not in cache, calculate it
  vec3f point1 = state.positions[p1];
  vec3f point2 = state.positions[p2];
  vec3f middle = vec3f{(point1.x + point2.x) * 0.5f,
                    (point1.y + point2.y) * 0.5f, (point1.z + point2.z) * 0.5f};
  
  // add vertex makes sure point is on unit sphere
  addVertex(state.positions, middle);
  size_t i = state.positions.size() - 1;
  
  // store it, return index
  state.middlePointIndexCache[key] = i;
  return i;
}


void CreateIcoSphere(int recursionLevel, std::vector<vec3f>& outputVertexBuffer)
{
  struct triangle
  {
    size_t x, y, z;
  };
  SphereCreatorState state;
  
  // create 12 vertices of a icosahedron
  float t = (1.0 + sqrt(5.0f)) / 2.0;
  float triX[12] = {  -1,  1, -1,  1,  0,  0,  0,  0,  t,  t, -t, -t  };
  float triY[12] = {   t,  t, -t, -t, -1,  1, -1,  1,  0,  0,  0,  0  };
  float triZ[12] = {   0,  0,  0,  0,  t,  t, -t, -t, -1,  1, -1,  1  };
  for (int i = 0; i < 12; i++)
    addVertex(state.positions, vec3f{triX[i], triY[i], triZ[i]});

  // create 20 triangles of the icosahedron
  std::vector<triangle> startFaces;
  size_t facesP0[20] = {  0,  0,  0,  0,  0,   1,  5, 11, 10,  7,    3,  3,  3,  3,  3,   4,  2,  6,  8,  9 };
  size_t facesP1[20] = { 11,  5,  1,  7, 10,   5, 11, 10,  7,  1,    9,  4,  2,  6,  8,   9,  4,  2,  6,  8 };
  size_t facesP2[20] = {  5,  1,  7, 10, 11,   9,  4,  2,  6,  8,    4,  2,  6,  8,  9,   5, 11, 10,  7,  1 };
  for (int i = 0; i < 20; i++)
    startFaces.emplace_back(triangle{ facesP0[i], facesP1[i], facesP2[i] });
  
  // refine triangles
  std::vector<triangle> workingFaces;
  std::vector<triangle>* faces = &startFaces;
  std::vector<triangle>* faces2 = &workingFaces;
  for (int i = 0; i < recursionLevel; i++)
  {
    for (int r = 0; r < faces->size(); r++)
    {
      triangle tri = (*faces)[r];
      // replace triangle by 4 triangles
      size_t a = getMiddlePoint(state, tri.x, tri.y);
      size_t b = getMiddlePoint(state, tri.y, tri.z);
      size_t c = getMiddlePoint(state, tri.z, tri.x);
      faces2->emplace_back(triangle{tri.x, a, c});
      faces2->emplace_back(triangle{tri.y, b, a});
      faces2->emplace_back(triangle{tri.z, c, b});
      faces2->emplace_back(triangle{a, b, c});
    }
    faces->clear();
    std::vector<triangle>* oldFaces = faces;
    faces = faces2;
    faces2 = oldFaces;
  }
  
  // done, now add triangles to mesh
  outputVertexBuffer.clear();
  for (int r = 0; r < faces->size(); r++)
  {
    triangle tri = (*faces)[r];
    outputVertexBuffer.emplace_back(state.positions[tri.x]);
    outputVertexBuffer.emplace_back(state.positions[tri.y]);
    outputVertexBuffer.emplace_back(state.positions[tri.z]);
  }
}


void CreateCube(std::vector<vec3f>& outputVertexBuffer)
{
  outputVertexBuffer.reserve(6*6);
  for (int j = 0; j < 6; j++) {
    for (int i = 0; i < 6; i++) {
      const int triIndexes[6] = { 0, 1, 3, 0, 3, 2 };
      int index = triIndexes[(j / 3) ? i : (5 - i)];
      float v[3] = { (j / 3) ? 0.5f : -0.5f, (index & 1) ? -0.5f : 0.5f, (index & 2) ? -0.5f : 0.5f };
      outputVertexBuffer.emplace_back(vec3f{ { v[(j+0)%3], v[(j+1)%3], v[(j+2)%3] } });
    }
  }
}


void CreateTexturedCube(std::vector<vec3f>& outputVertexBuffer)
{
  outputVertexBuffer.reserve(6*6*2);
  for (int j = 0; j < 6; j++) {
    for (int i = 0; i < 6; i++) {
      const int triIndexes[6] = { 0, 1, 3, 0, 3, 2 };
      int index = triIndexes[(j / 3) ? i : (5 - i)];
      float v[3] = { (j / 3) ? 0.5f : -0.5f, (index & 1) ? -0.5f : 0.5f, (index & 2) ? -0.5f : 0.5f };
      outputVertexBuffer.emplace_back(vec3f{ { v[(j+0)%3], v[(j+1)%3], v[(j+2)%3] } });
      
      float u2 = (0.25f + (j % 3) * 85.0f + (v[1] + 0.5f) * 84.5f) / 256.0f;
      float v2 = (0.25f + (j / 3) * 85.0f + (v[2] + 0.5f) * 84.5f) / 256.0f;
      outputVertexBuffer.emplace_back(vec3f{ { u2, v2, 0.0f } });
    }
  }
}


void CreateBumpCube(std::vector<vec3f>& outputVertexBuffer)
{
/*
  outputVertexBuffer.reserve(6*6*2);
  for (int j = 0; j < 6; j++) {
    for (int i = 0; i < 6; i++) {
      const int triIndexes[6] = { 0, 1, 3, 0, 3, 2 };
      int index = triIndexes[(j / 3) ? i : (5 - i)];
      float v[3] = { (j / 3) ? 0.5f : -0.5f, (index & 1) ? -0.5f : 0.5f, (index & 2) ? -0.5f : 0.5f };
      outputVertexBuffer.emplace_back(vec3f{ { v[(j+0)%3], v[(j+1)%3], v[(j+2)%3] } });

      outputVertexBuffer.emplace_back(n);
      outputVertexBuffer.emplace_back(t);
      
      float u2 = (0.25f + (j % 3) * 85.0f + (v[1] + 0.5f) * 84.5f) / 256.0f;
      float v2 = (0.25f + (j / 3) * 85.0f + (v[2] + 0.5f) * 84.5f) / 256.0f;
      outputVertexBuffer.emplace_back(vec3f{ { u2, v2, 0.0f } });
    }
  }
  return;
*/


  outputVertexBuffer.reserve(6*6*2);
  vec3f norm = { 0.0, 0.0, 1.0 };
  vec3f uv0 = { 0.0, 0.0, 0.0 };
  vec3f uv1 = { 0.0, 1.0, 0.0 };
  vec3f uv2 = { 1.0, 0.0, 0.0 };
  vec3f uv3 = { 1.0, 1.0, 0.0 };

/*
  outputVertexBuffer.emplace_back(vec3f{ -0.5, -0.5, 0.0 });
  outputVertexBuffer.emplace_back(norm);
  outputVertexBuffer.emplace_back(uv0);

  outputVertexBuffer.emplace_back(vec3f{ -0.5, 0.5, 0.0 });
  outputVertexBuffer.emplace_back(norm);
  outputVertexBuffer.emplace_back(uv1);

  outputVertexBuffer.emplace_back(vec3f{ 0.5, 0.5, 0.0 });
  outputVertexBuffer.emplace_back(norm);
  outputVertexBuffer.emplace_back(uv3);


  outputVertexBuffer.emplace_back(vec3f{ -0.5, -0.5, 0.0 });
  outputVertexBuffer.emplace_back(norm);
  outputVertexBuffer.emplace_back(uv0);

  outputVertexBuffer.emplace_back(vec3f{ 0.5, 0.5, 0.0 });
  outputVertexBuffer.emplace_back(norm);
  outputVertexBuffer.emplace_back(uv3);

  outputVertexBuffer.emplace_back(vec3f{ 0.5, -0.5, 0.0 });
  outputVertexBuffer.emplace_back(norm);
  outputVertexBuffer.emplace_back(uv2);



  outputVertexBuffer.emplace_back(vec3f{ -0.5, -0.5, 0.0 });
  outputVertexBuffer.emplace_back(norm);
  outputVertexBuffer.emplace_back(uv0);

  outputVertexBuffer.emplace_back(vec3f{ 0.5, 0.5, 0.0 });
  outputVertexBuffer.emplace_back(norm);
  outputVertexBuffer.emplace_back(uv3);

  outputVertexBuffer.emplace_back(vec3f{ -0.5, 0.5, 0.0 });
  outputVertexBuffer.emplace_back(norm);
  outputVertexBuffer.emplace_back(uv1);



  outputVertexBuffer.emplace_back(vec3f{ -0.5, -0.5, 0.0 });
  outputVertexBuffer.emplace_back(norm);
  outputVertexBuffer.emplace_back(uv0);

  outputVertexBuffer.emplace_back(vec3f{ 0.5, -0.5, 0.0 });
  outputVertexBuffer.emplace_back(norm);
  outputVertexBuffer.emplace_back(uv2);

  outputVertexBuffer.emplace_back(vec3f{ 0.5, 0.5, 0.0 });
  outputVertexBuffer.emplace_back(norm);
  outputVertexBuffer.emplace_back(uv3);
*/

  outputVertexBuffer.reserve(6*6*2);
  vec3f ns[6] = { { 1.0,  0.0,  0.0 }, { 0.0, 1.0,  0.0 }, { 0.0, 0.0, 1.0 }, { -1.0,  0.0,  0.0 }, {  0.0, -1.0,  0.0 }, {  0.0,  0.0, -1.0 } };
  vec3f ts[6] = { { 0.0, -1.0,  0.0 }, { 0.0, 0.0, -1.0 }, { 0.0, 1.0, 0.0 }, {  0.0, -1.0,  0.0 }, {  0.0,  0.0, -1.0 }, {  0.0,  1.0,  0.0 } };
  vec3f bs[6] = { { 0.0,  0.0,  1.0 }, { 1.0, 0.0,  0.0 }, { 1.0, 0.0, 0.0 }, {  0.0,  0.0, -1.0 }, { -1.0,  0.0,  0.0 }, { -1.0,  0.0,  0.0 } };
  vec3f col[6] = { { 1.0,  0.0,  0.0 }, { 0.0, 1.0,  0.0 }, { 0.0, 0.0, 1.0 }, {  0.0, 1.0,  1.0 }, {  1.0,  0.0,  1.0 }, {  1.0,  1.0,  0.0 } };

/*
    vec3f  p[6] = { { 0.5, -0.5, -0.5 }, { 0.5,  0.5,  0.5 }, { 0.5, -0.5,  0.5 }, { 0.5, -0.5, -0.5 }, { 0.5,  0.5, -0.5 }, { 0.5, 0.5,  0.5 } };
    vec3f uv[6] = { {       0.0,  0.0 }, {       1.0,  1.0 }, {       0.0,  1.0 }, {       0.0,  0.0 }, {       1.0,  0.0 }, {      1.0,  1.0 } };
    for (int i = 0; i < 6; i++) {
      outputVertexBuffer.emplace_back(p[i]);
      outputVertexBuffer.emplace_back(vec3f{  0.0,  1.0,  0.0 });
      outputVertexBuffer.emplace_back(vec3f{  0.0,  0.0,  1.0 });
      outputVertexBuffer.emplace_back(vec3f{  0.5,  0.5,  0.5 });
      outputVertexBuffer.emplace_back(uv[i]);
    }
*/

  //  for (int j = 0; j < 6; j++) {  // for six faces
  for (int j = 0; j < 6; j++) {  // for just one face   0,2,3  ok  1,4,6  bad
    //  if (j == 1 || j == 4 || j == 6)
    //      continue;
    vec3f& n = ns[j];
    vec3f& t = ts[j];
    vec3f& b = bs[j];
    vec3f pnts[4];
    vec3f uvs[4] = { { 0, 0 }, { 0, 1 }, { 1, 0 }, { 1, 1 } };
    for (int i = 0; i < 4; i++)
      for (int c = 0; c < 3; c++)
      {
        pnts[i].v[c] = 0.5f * (n.v[c] + ((i&2) ? -t.v[c] : t.v[c]) + ((i&1) ? -b.v[c] : b.v[c]));
//        uvs[i].v[c] = ((i&2) ? -t.v[c] : 0.0) + ((i&1) ? b.v[c] : 0.0);
      }

    const int pntIndexes[6] = { 0, 1, 3, 0, 3, 2 };
    for (int i = 0; i < 6; i++) {
    
      //P - A = T(up - ua) + B(vp - va)
      
      //int index = pntIndexes[(j / 3) ? i : (5 - i)];
      int index = pntIndexes[i];
      outputVertexBuffer.emplace_back(pnts[index]);
      outputVertexBuffer.emplace_back(n);
      outputVertexBuffer.emplace_back(t);
      //outputVertexBuffer.emplace_back(col[j]);
      outputVertexBuffer.emplace_back(uvs[index]);
    }
  }
  
}