//
// 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]);
}
}
}