Compare commits

...

5 Commits

Author SHA1 Message Date
Evert Prants 20c04c0ab7
working lod 2023-02-24 15:34:40 +02:00
Evert Prants c1e3e2fd3d
new topology code 2023-02-23 21:33:50 +02:00
Evert Prants 44c54b03bf
random 2022-10-01 12:00:55 +03:00
Evert Prants 9a9ebf1a9b
not crashing 2022-09-28 22:20:47 +03:00
Evert Prants f56701fbdc
trash tier tests 2022-02-25 18:39:45 +02:00
16 changed files with 1381 additions and 403 deletions

6
.vscode/settings.json vendored Normal file
View File

@ -0,0 +1,6 @@
{
"C_Cpp.default.configurationProvider": "ms-vscode.cmake-tools",
"files.associations": {
"*.def": "cpp"
}
}

View File

@ -1,7 +1,7 @@
#version 330
in vec2 uv_p;
in vec3 testo;
void main(void) {
gl_FragColor = vec4(uv_p.xy, 0.5f, 1.0f);
gl_FragColor = vec4((testo.x / 2) + 0.5, (testo.y / 2) + 0.5, (testo.z / 2) + 0.5, 1.0f);
}

View File

@ -3,7 +3,7 @@
in vec3 position;
in vec3 normal;
in vec2 uv;
out vec2 uv_p;
out vec3 testo;
uniform mat4 projectionMatrix;
uniform mat4 viewMatrix;
@ -12,5 +12,5 @@ uniform mat4 modelMatrix;
void main(void) {
gl_Position = projectionMatrix * viewMatrix * modelMatrix * vec4(position, 1.0);
uv_p = vec2(clamp(uv.x + normal.x, 0.0, 1.0), uv.y);
testo = normal;
}

View File

@ -20,7 +20,7 @@
#include "planet/Planet.h"
Application::Application() : m_width(1920), m_height(1080), m_lock(true)
Application::Application() : m_width(1920), m_height(1080), m_lock(true), m_pauseLod(false)
{
}
@ -126,16 +126,16 @@ void Application::handleEvents()
// Handle Camera Movement Keys
if(Input::getInstance().isKeyDown(SDLK_w))
m_camera->processKeyboard(Camera_Movement::FORWARD, 0.01f);
m_camera->processKeyboard(Camera_Movement::CAM_FORWARD, 0.01f);
if(Input::getInstance().isKeyDown(SDLK_s))
m_camera->processKeyboard(Camera_Movement::BACKWARD, 0.01f);
m_camera->processKeyboard(Camera_Movement::CAM_BACKWARD, 0.01f);
if(Input::getInstance().isKeyDown(SDLK_d))
m_camera->processKeyboard(Camera_Movement::RIGHT, 0.01f);
m_camera->processKeyboard(Camera_Movement::CAM_RIGHT, 0.01f);
if(Input::getInstance().isKeyDown(SDLK_a))
m_camera->processKeyboard(Camera_Movement::LEFT, 0.01f);
m_camera->processKeyboard(Camera_Movement::CAM_LEFT, 0.01f);
// Handle Camera Zoom
m_camera->processMouseScroll((float) Input::getInstance().getMouseWheelVertical() / 10.0f);
@ -159,6 +159,9 @@ void Application::handleEvents()
SDL_ShowCursor(SDL_ENABLE);
}
}
if (Input::getInstance().isKeyPressed(SDLK_p))
m_pauseLod = !m_pauseLod;
}
@ -176,7 +179,7 @@ void Application::run()
PlanetNoiseParams noise{15.f, 15.f, .8f, 1.f/.8f};
Planet* pl = new Planet(glm::vec3(0.0f,0.0f,0.0f), 500.0f, noise);
// Create the shader and link them together
Shader& chunkShader = Shader::createShader("data/shaders/chunk.vert", "data/shaders/chunk.frag");
Shader& chunkShader = Shader::createShader("../data/shaders/chunk.vert", "../data/shaders/chunk.frag");
chunkShader.linkShaders();
// Set attribute arrays
@ -194,7 +197,7 @@ void Application::run()
handleEvents();
// Clear color buffer
glClearColor(0.39f, 0.58f, 0.93f, 1.f);
glClearColor(0.0f, 0.0f, 0.0f, 1.f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
deltaTime = ((m_now - m_last) / (double)SDL_GetPerformanceFrequency());
@ -204,7 +207,8 @@ void Application::run()
glEnable(GL_CULL_FACE);
glCullFace(GL_BACK);
pl->tick(m_camera, deltaTime);
if (!m_pauseLod)
pl->tick(m_camera, deltaTime);
if (m_wireframe)
glPolygonMode( GL_FRONT_AND_BACK, GL_LINE );

View File

@ -30,6 +30,8 @@ class Application : public Singleton<Application>
void initialize();
void exit() { m_run = false; }
inline bool isLodPaused() const { return m_pauseLod; }
inline glm::vec2 getScreenDimensions() const { return glm::vec2(m_width, m_height); }
friend class Singleton<Application>;
@ -47,6 +49,7 @@ class Application : public Singleton<Application>
bool m_run;
bool m_wireframe;
bool m_lock;
bool m_pauseLod;
void handleEvents();
void run();

View File

@ -166,13 +166,13 @@ void Camera::processKeyboard(Camera_Movement direction, GLfloat deltaTime)
{
GLfloat velocity = m_movementSpeed * deltaTime;
if (direction == FORWARD)
if (direction == CAM_FORWARD)
m_position += m_front * velocity;
if (direction == BACKWARD)
if (direction == CAM_BACKWARD)
m_position -= m_front * velocity;
if (direction == LEFT)
if (direction == CAM_LEFT)
m_position -= m_right * velocity;
if (direction == RIGHT)
if (direction == CAM_RIGHT)
m_position += m_right * velocity;
}
@ -204,22 +204,23 @@ void Camera::processMouseMovement(GLfloat xoffset, GLfloat yoffset, GLboolean co
void Camera::processMouseScroll(GLfloat yoffset)
{
if (m_zoom >= 44.0f && m_zoom <= 45.0f)
{
m_zoom -= yoffset;
}
// if (m_zoom >= 44.0f && m_zoom <= 45.0f)
// {
// m_zoom -= yoffset;
// }
if (m_zoom <= 44.0f)
{
m_zoom = 44.0f;
}
// if (m_zoom <= 44.0f)
// {
// m_zoom = 44.0f;
// }
if (m_zoom >= 45.0f)
{
m_zoom = 45.0f;
}
// if (m_zoom >= 45.0f)
// {
// m_zoom = 45.0f;
// }
updateProjection();
// updateProjection();
m_movementSpeed += yoffset * 50.0f;
}
void Camera::updateProjection(void)

View File

@ -22,10 +22,10 @@
// Defines several possible options for camera movement. Used as abstraction to stay away from window-system specific input methods
enum Camera_Movement {
FORWARD,
BACKWARD,
LEFT,
RIGHT
CAM_FORWARD,
CAM_BACKWARD,
CAM_LEFT,
CAM_RIGHT
};
// Default camera values

View File

@ -154,7 +154,7 @@ GLuint Shader::getAttribLocation(const std::string& attrbuteName)
GLint attrib = glGetAttribLocation(m_programID, attrbuteName.c_str());
if (attrib == GL_INVALID_OPERATION || attrib < 0)
{
logWarn("Attribute " + attrbuteName + " doesn't exist in program.");
// logWarn("Attribute " + attrbuteName + " doesn't exist in program.");
}
return attrib;

View File

@ -0,0 +1,595 @@
///////////////////////////////////////////////////////////////////////////////
// Sphere.cpp
// ==========
// Sphere for OpenGL with (radius, sectors, stacks)
// The min number of sectors is 3 and the min number of stacks are 2.
//
// AUTHOR: Song Ho Ahn (song.ahn@gmail.com)
// CREATED: 2017-11-01
// UPDATED: 2020-05-20
///////////////////////////////////////////////////////////////////////////////
#ifdef _WIN32
#include <windows.h> // include windows.h to avoid thousands of compile errors even though this class is not depending on Windows
#endif
#ifdef __APPLE__
#include <OpenGL/gl.h>
#else
#include <GL/gl.h>
#endif
#include <iostream>
#include <iomanip>
#include <cmath>
#include "Sphere.h"
// constants //////////////////////////////////////////////////////////////////
const int MIN_SECTOR_COUNT = 3;
const int MIN_STACK_COUNT = 2;
///////////////////////////////////////////////////////////////////////////////
// ctor
///////////////////////////////////////////////////////////////////////////////
Sphere::Sphere(float radius, int sectors, int stacks, bool smooth) : interleavedStride(32)
{
set(radius, sectors, stacks, smooth);
}
///////////////////////////////////////////////////////////////////////////////
// setters
///////////////////////////////////////////////////////////////////////////////
void Sphere::set(float radius, int sectors, int stacks, bool smooth)
{
this->radius = radius;
this->sectorCount = sectors;
if(sectors < MIN_SECTOR_COUNT)
this->sectorCount = MIN_SECTOR_COUNT;
this->stackCount = stacks;
if(sectors < MIN_STACK_COUNT)
this->sectorCount = MIN_STACK_COUNT;
this->smooth = smooth;
if(smooth)
buildVerticesSmooth();
else
buildVerticesFlat();
}
void Sphere::setRadius(float radius)
{
if(radius != this->radius)
set(radius, sectorCount, stackCount, smooth);
}
void Sphere::setSectorCount(int sectors)
{
if(sectors != this->sectorCount)
set(radius, sectors, stackCount, smooth);
}
void Sphere::setStackCount(int stacks)
{
if(stacks != this->stackCount)
set(radius, sectorCount, stacks, smooth);
}
void Sphere::setSmooth(bool smooth)
{
if(this->smooth == smooth)
return;
this->smooth = smooth;
if(smooth)
buildVerticesSmooth();
else
buildVerticesFlat();
}
///////////////////////////////////////////////////////////////////////////////
// print itself
///////////////////////////////////////////////////////////////////////////////
void Sphere::printSelf() const
{
std::cout << "===== Sphere =====\n"
<< " Radius: " << radius << "\n"
<< " Sector Count: " << sectorCount << "\n"
<< " Stack Count: " << stackCount << "\n"
<< "Smooth Shading: " << (smooth ? "true" : "false") << "\n"
<< "Triangle Count: " << getTriangleCount() << "\n"
<< " Index Count: " << getIndexCount() << "\n"
<< " Vertex Count: " << getVertexCount() << "\n"
<< " Normal Count: " << getNormalCount() << "\n"
<< "TexCoord Count: " << getTexCoordCount() << std::endl;
}
///////////////////////////////////////////////////////////////////////////////
// draw a sphere in VertexArray mode
// OpenGL RC must be set before calling it
///////////////////////////////////////////////////////////////////////////////
void Sphere::draw() const
{
// interleaved array
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_NORMAL_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glVertexPointer(3, GL_FLOAT, interleavedStride, &interleavedVertices[0]);
glNormalPointer(GL_FLOAT, interleavedStride, &interleavedVertices[3]);
glTexCoordPointer(2, GL_FLOAT, interleavedStride, &interleavedVertices[6]);
glDrawElements(GL_TRIANGLES, (unsigned int)indices.size(), GL_UNSIGNED_INT, indices.data());
glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_NORMAL_ARRAY);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
}
///////////////////////////////////////////////////////////////////////////////
// draw lines only
// the caller must set the line width before call this
///////////////////////////////////////////////////////////////////////////////
void Sphere::drawLines(const float lineColor[4]) const
{
// set line colour
glColor4fv(lineColor);
glMaterialfv(GL_FRONT, GL_DIFFUSE, lineColor);
// draw lines with VA
glDisable(GL_LIGHTING);
glDisable(GL_TEXTURE_2D);
glEnableClientState(GL_VERTEX_ARRAY);
glVertexPointer(3, GL_FLOAT, 0, vertices.data());
glDrawElements(GL_LINES, (unsigned int)lineIndices.size(), GL_UNSIGNED_INT, lineIndices.data());
glDisableClientState(GL_VERTEX_ARRAY);
glEnable(GL_LIGHTING);
glEnable(GL_TEXTURE_2D);
}
///////////////////////////////////////////////////////////////////////////////
// draw a sphere surfaces and lines on top of it
// the caller must set the line width before call this
///////////////////////////////////////////////////////////////////////////////
void Sphere::drawWithLines(const float lineColor[4]) const
{
glEnable(GL_POLYGON_OFFSET_FILL);
glPolygonOffset(1.0, 1.0f); // move polygon backward
this->draw();
glDisable(GL_POLYGON_OFFSET_FILL);
// draw lines with VA
drawLines(lineColor);
}
/*@@ FIXME: when the radius = 0
///////////////////////////////////////////////////////////////////////////////
// update vertex positions only
///////////////////////////////////////////////////////////////////////////////
void Sphere::updateRadius()
{
float scale = sqrtf(radius * radius / (vertices[0] * vertices[0] + vertices[1] * vertices[1] + vertices[2] * vertices[2]));
std::size_t i, j;
std::size_t count = vertices.size();
for(i = 0, j = 0; i < count; i += 3, j += 8)
{
vertices[i] *= scale;
vertices[i+1] *= scale;
vertices[i+2] *= scale;
// for interleaved array
interleavedVertices[j] *= scale;
interleavedVertices[j+1] *= scale;
interleavedVertices[j+2] *= scale;
}
}
*/
///////////////////////////////////////////////////////////////////////////////
// dealloc vectors
///////////////////////////////////////////////////////////////////////////////
void Sphere::clearArrays()
{
std::vector<float>().swap(vertices);
std::vector<float>().swap(normals);
std::vector<float>().swap(texCoords);
std::vector<unsigned int>().swap(indices);
std::vector<unsigned int>().swap(lineIndices);
}
///////////////////////////////////////////////////////////////////////////////
// build vertices of sphere with smooth shading using parametric equation
// x = r * cos(u) * cos(v)
// y = r * cos(u) * sin(v)
// z = r * sin(u)
// where u: stack(latitude) angle (-90 <= u <= 90)
// v: sector(longitude) angle (0 <= v <= 360)
///////////////////////////////////////////////////////////////////////////////
void Sphere::buildVerticesSmooth()
{
const float PI = acos(-1);
// clear memory of prev arrays
clearArrays();
float x, y, z, xy; // vertex position
float nx, ny, nz, lengthInv = 1.0f / radius; // normal
float s, t; // texCoord
float sectorStep = 2 * PI / sectorCount;
float stackStep = PI / stackCount;
float sectorAngle, stackAngle;
for(int i = 0; i <= stackCount; ++i)
{
stackAngle = PI / 2 - i * stackStep; // starting from pi/2 to -pi/2
xy = radius * cosf(stackAngle); // r * cos(u)
z = radius * sinf(stackAngle); // r * sin(u)
// add (sectorCount+1) vertices per stack
// the first and last vertices have same position and normal, but different tex coords
for(int j = 0; j <= sectorCount; ++j)
{
sectorAngle = j * sectorStep; // starting from 0 to 2pi
// vertex position
x = xy * cosf(sectorAngle); // r * cos(u) * cos(v)
y = xy * sinf(sectorAngle); // r * cos(u) * sin(v)
addVertex(x, y, z);
// normalized vertex normal
nx = x * lengthInv;
ny = y * lengthInv;
nz = z * lengthInv;
addNormal(nx, ny, nz);
// vertex tex coord between [0, 1]
s = (float)j / sectorCount;
t = (float)i / stackCount;
addTexCoord(s, t);
}
}
// indices
// k1--k1+1
// | / |
// | / |
// k2--k2+1
unsigned int k1, k2;
for(int i = 0; i < stackCount; ++i)
{
k1 = i * (sectorCount + 1); // beginning of current stack
k2 = k1 + sectorCount + 1; // beginning of next stack
for(int j = 0; j < sectorCount; ++j, ++k1, ++k2)
{
// 2 triangles per sector excluding 1st and last stacks
if(i != 0)
{
addIndices(k1, k2, k1+1); // k1---k2---k1+1
}
if(i != (stackCount-1))
{
addIndices(k1+1, k2, k2+1); // k1+1---k2---k2+1
}
// vertical lines for all stacks
lineIndices.push_back(k1);
lineIndices.push_back(k2);
if(i != 0) // horizontal lines except 1st stack
{
lineIndices.push_back(k1);
lineIndices.push_back(k1 + 1);
}
}
}
// generate interleaved vertex array as well
buildInterleavedVertices();
}
///////////////////////////////////////////////////////////////////////////////
// generate vertices with flat shading
// each triangle is independent (no shared vertices)
///////////////////////////////////////////////////////////////////////////////
void Sphere::buildVerticesFlat()
{
const float PI = acos(-1);
// tmp vertex definition (x,y,z,s,t)
struct Vertex
{
float x, y, z, s, t;
};
std::vector<Vertex> tmpVertices;
float sectorStep = 2 * PI / sectorCount;
float stackStep = PI / stackCount;
float sectorAngle, stackAngle;
// compute all vertices first, each vertex contains (x,y,z,s,t) except normal
for(int i = 0; i <= stackCount; ++i)
{
stackAngle = PI / 2 - i * stackStep; // starting from pi/2 to -pi/2
float xy = radius * cosf(stackAngle); // r * cos(u)
float z = radius * sinf(stackAngle); // r * sin(u)
// add (sectorCount+1) vertices per stack
// the first and last vertices have same position and normal, but different tex coords
for(int j = 0; j <= sectorCount; ++j)
{
sectorAngle = j * sectorStep; // starting from 0 to 2pi
Vertex vertex;
vertex.x = xy * cosf(sectorAngle); // x = r * cos(u) * cos(v)
vertex.y = xy * sinf(sectorAngle); // y = r * cos(u) * sin(v)
vertex.z = z; // z = r * sin(u)
vertex.s = (float)j/sectorCount; // s
vertex.t = (float)i/stackCount; // t
tmpVertices.push_back(vertex);
}
}
// clear memory of prev arrays
clearArrays();
Vertex v1, v2, v3, v4; // 4 vertex positions and tex coords
std::vector<float> n; // 1 face normal
int i, j, k, vi1, vi2;
int index = 0; // index for vertex
for(i = 0; i < stackCount; ++i)
{
vi1 = i * (sectorCount + 1); // index of tmpVertices
vi2 = (i + 1) * (sectorCount + 1);
for(j = 0; j < sectorCount; ++j, ++vi1, ++vi2)
{
// get 4 vertices per sector
// v1--v3
// | |
// v2--v4
v1 = tmpVertices[vi1];
v2 = tmpVertices[vi2];
v3 = tmpVertices[vi1 + 1];
v4 = tmpVertices[vi2 + 1];
// if 1st stack and last stack, store only 1 triangle per sector
// otherwise, store 2 triangles (quad) per sector
if(i == 0) // a triangle for first stack ==========================
{
// put a triangle
addVertex(v1.x, v1.y, v1.z);
addVertex(v2.x, v2.y, v2.z);
addVertex(v4.x, v4.y, v4.z);
// put tex coords of triangle
addTexCoord(v1.s, v1.t);
addTexCoord(v2.s, v2.t);
addTexCoord(v4.s, v4.t);
// put normal
n = computeFaceNormal(v1.x,v1.y,v1.z, v2.x,v2.y,v2.z, v4.x,v4.y,v4.z);
for(k = 0; k < 3; ++k) // same normals for 3 vertices
{
addNormal(n[0], n[1], n[2]);
}
// put indices of 1 triangle
addIndices(index, index+1, index+2);
// indices for line (first stack requires only vertical line)
lineIndices.push_back(index);
lineIndices.push_back(index+1);
index += 3; // for next
}
else if(i == (stackCount-1)) // a triangle for last stack =========
{
// put a triangle
addVertex(v1.x, v1.y, v1.z);
addVertex(v2.x, v2.y, v2.z);
addVertex(v3.x, v3.y, v3.z);
// put tex coords of triangle
addTexCoord(v1.s, v1.t);
addTexCoord(v2.s, v2.t);
addTexCoord(v3.s, v3.t);
// put normal
n = computeFaceNormal(v1.x,v1.y,v1.z, v2.x,v2.y,v2.z, v3.x,v3.y,v3.z);
for(k = 0; k < 3; ++k) // same normals for 3 vertices
{
addNormal(n[0], n[1], n[2]);
}
// put indices of 1 triangle
addIndices(index, index+1, index+2);
// indices for lines (last stack requires both vert/hori lines)
lineIndices.push_back(index);
lineIndices.push_back(index+1);
lineIndices.push_back(index);
lineIndices.push_back(index+2);
index += 3; // for next
}
else // 2 triangles for others ====================================
{
// put quad vertices: v1-v2-v3-v4
addVertex(v1.x, v1.y, v1.z);
addVertex(v2.x, v2.y, v2.z);
addVertex(v3.x, v3.y, v3.z);
addVertex(v4.x, v4.y, v4.z);
// put tex coords of quad
addTexCoord(v1.s, v1.t);
addTexCoord(v2.s, v2.t);
addTexCoord(v3.s, v3.t);
addTexCoord(v4.s, v4.t);
// put normal
n = computeFaceNormal(v1.x,v1.y,v1.z, v2.x,v2.y,v2.z, v3.x,v3.y,v3.z);
for(k = 0; k < 4; ++k) // same normals for 4 vertices
{
addNormal(n[0], n[1], n[2]);
}
// put indices of quad (2 triangles)
addIndices(index, index+1, index+2);
addIndices(index+2, index+1, index+3);
// indices for lines
lineIndices.push_back(index);
lineIndices.push_back(index+1);
lineIndices.push_back(index);
lineIndices.push_back(index+2);
index += 4; // for next
}
}
}
// generate interleaved vertex array as well
buildInterleavedVertices();
}
///////////////////////////////////////////////////////////////////////////////
// generate interleaved vertices: V/N/T
// stride must be 32 bytes
///////////////////////////////////////////////////////////////////////////////
void Sphere::buildInterleavedVertices()
{
std::vector<float>().swap(interleavedVertices);
std::size_t i, j;
std::size_t count = vertices.size();
for(i = 0, j = 0; i < count; i += 3, j += 2)
{
interleavedVertices.push_back(vertices[i]);
interleavedVertices.push_back(vertices[i+1]);
interleavedVertices.push_back(vertices[i+2]);
interleavedVertices.push_back(normals[i]);
interleavedVertices.push_back(normals[i+1]);
interleavedVertices.push_back(normals[i+2]);
interleavedVertices.push_back(texCoords[j]);
interleavedVertices.push_back(texCoords[j+1]);
}
}
///////////////////////////////////////////////////////////////////////////////
// add single vertex to array
///////////////////////////////////////////////////////////////////////////////
void Sphere::addVertex(float x, float y, float z)
{
vertices.push_back(x);
vertices.push_back(y);
vertices.push_back(z);
}
///////////////////////////////////////////////////////////////////////////////
// add single normal to array
///////////////////////////////////////////////////////////////////////////////
void Sphere::addNormal(float nx, float ny, float nz)
{
normals.push_back(nx);
normals.push_back(ny);
normals.push_back(nz);
}
///////////////////////////////////////////////////////////////////////////////
// add single texture coord to array
///////////////////////////////////////////////////////////////////////////////
void Sphere::addTexCoord(float s, float t)
{
texCoords.push_back(s);
texCoords.push_back(t);
}
///////////////////////////////////////////////////////////////////////////////
// add 3 indices to array
///////////////////////////////////////////////////////////////////////////////
void Sphere::addIndices(unsigned int i1, unsigned int i2, unsigned int i3)
{
indices.push_back(i1);
indices.push_back(i2);
indices.push_back(i3);
}
///////////////////////////////////////////////////////////////////////////////
// return face normal of a triangle v1-v2-v3
// if a triangle has no surface (normal length = 0), then return a zero vector
///////////////////////////////////////////////////////////////////////////////
std::vector<float> Sphere::computeFaceNormal(float x1, float y1, float z1, // v1
float x2, float y2, float z2, // v2
float x3, float y3, float z3) // v3
{
const float EPSILON = 0.000001f;
std::vector<float> normal(3, 0.0f); // default return value (0,0,0)
float nx, ny, nz;
// find 2 edge vectors: v1-v2, v1-v3
float ex1 = x2 - x1;
float ey1 = y2 - y1;
float ez1 = z2 - z1;
float ex2 = x3 - x1;
float ey2 = y3 - y1;
float ez2 = z3 - z1;
// cross product: e1 x e2
nx = ey1 * ez2 - ez1 * ey2;
ny = ez1 * ex2 - ex1 * ez2;
nz = ex1 * ey2 - ey1 * ex2;
// normalize only if the length is > 0
float length = sqrtf(nx * nx + ny * ny + nz * nz);
if(length > EPSILON)
{
// normalize
float lengthInv = 1.0f / length;
normal[0] = nx * lengthInv;
normal[1] = ny * lengthInv;
normal[2] = nz * lengthInv;
}
return normal;
}

View File

@ -0,0 +1,99 @@
///////////////////////////////////////////////////////////////////////////////
// Sphere.h
// ========
// Sphere for OpenGL with (radius, sectors, stacks)
// The min number of sectors is 3 and The min number of stacks are 2.
//
// AUTHOR: Song Ho Ahn (song.ahn@gmail.com)
// CREATED: 2017-11-01
// UPDATED: 2020-05-20
///////////////////////////////////////////////////////////////////////////////
#ifndef GEOMETRY_SPHERE_H
#define GEOMETRY_SPHERE_H
#include <vector>
class Sphere
{
public:
// ctor/dtor
Sphere(float radius=1.0f, int sectorCount=36, int stackCount=18, bool smooth=true);
~Sphere() {}
// getters/setters
float getRadius() const { return radius; }
int getSectorCount() const { return sectorCount; }
int getStackCount() const { return stackCount; }
void set(float radius, int sectorCount, int stackCount, bool smooth=true);
void setRadius(float radius);
void setSectorCount(int sectorCount);
void setStackCount(int stackCount);
void setSmooth(bool smooth);
// for vertex data
unsigned int getVertexCount() const { return (unsigned int)vertices.size() / 3; }
unsigned int getNormalCount() const { return (unsigned int)normals.size() / 3; }
unsigned int getTexCoordCount() const { return (unsigned int)texCoords.size() / 2; }
unsigned int getIndexCount() const { return (unsigned int)indices.size(); }
unsigned int getLineIndexCount() const { return (unsigned int)lineIndices.size(); }
unsigned int getTriangleCount() const { return getIndexCount() / 3; }
unsigned int getVertexSize() const { return (unsigned int)vertices.size() * sizeof(float); }
unsigned int getNormalSize() const { return (unsigned int)normals.size() * sizeof(float); }
unsigned int getTexCoordSize() const { return (unsigned int)texCoords.size() * sizeof(float); }
unsigned int getIndexSize() const { return (unsigned int)indices.size() * sizeof(unsigned int); }
unsigned int getLineIndexSize() const { return (unsigned int)lineIndices.size() * sizeof(unsigned int); }
const float* getVertices() const { return vertices.data(); }
const float* getNormals() const { return normals.data(); }
const float* getTexCoords() const { return texCoords.data(); }
const unsigned int* getIndices() const { return indices.data(); }
const unsigned int* getLineIndices() const { return lineIndices.data(); }
// for interleaved vertices: V/N/T
unsigned int getInterleavedVertexCount() const { return getVertexCount(); } // # of vertices
unsigned int getInterleavedVertexSize() const { return (unsigned int)interleavedVertices.size() * sizeof(float); } // # of bytes
int getInterleavedStride() const { return interleavedStride; } // should be 32 bytes
const float* getInterleavedVertices() const { return interleavedVertices.data(); }
// draw in VertexArray mode
void draw() const; // draw surface
void drawLines(const float lineColor[4]) const; // draw lines only
void drawWithLines(const float lineColor[4]) const; // draw surface and lines
// debug
void printSelf() const;
protected:
private:
// member functions
void buildVerticesSmooth();
void buildVerticesFlat();
void buildInterleavedVertices();
void clearArrays();
void addVertex(float x, float y, float z);
void addNormal(float x, float y, float z);
void addTexCoord(float s, float t);
void addIndices(unsigned int i1, unsigned int i2, unsigned int i3);
std::vector<float> computeFaceNormal(float x1, float y1, float z1,
float x2, float y2, float z2,
float x3, float y3, float z3);
// memeber vars
float radius;
int sectorCount; // longitude, # of slices
int stackCount; // latitude, # of stacks
bool smooth;
std::vector<float> vertices;
std::vector<float> normals;
std::vector<float> texCoords;
std::vector<unsigned int> indices;
std::vector<unsigned int> lineIndices;
// interleaved
std::vector<float> interleavedVertices;
int interleavedStride; // # of bytes to hop to the next vertex (should be 32 bytes)
};
#endif

View File

@ -2,13 +2,27 @@
#include "planet/PlanetFace.h"
Planet::Planet(glm::vec3 position, float radius, PlanetNoiseParams &noiseParams) :
m_position(position), m_radius(radius), m_noise(noiseParams.frequency, noiseParams.amplitude, noiseParams.lacunarity, noiseParams.persistence),
m_ibuffers(new PlanetIndexBuffer(15))
m_position(position), m_radius(radius), m_noise(noiseParams.frequency, noiseParams.amplitude, noiseParams.lacunarity, noiseParams.persistence)
{
for (unsigned int a = 0; a <= 4; a++)
{
for (unsigned int b = 0; b <= 4; b++)
{
for (unsigned int c = 0; c <= 4; c++)
{
for (unsigned int d = 0; d <= 4; d++)
{
m_ibuffers[a][b][c][d] = new PlanetIndexBuffer(16, a, b, c, d);
}
}
}
}
for (int i = 0; i < 6; i++)
{
m_faces[i] = new PlanetFace(this, i);
}
connectFaces();
}
Planet::~Planet()
@ -17,7 +31,21 @@ Planet::~Planet()
{
delete m_faces[i];
}
delete m_ibuffers;
for (unsigned int a = 0; a <= 4; a++)
{
for (unsigned int b = 0; b <= 4; b++)
{
for (unsigned int c = 0; c <= 4; c++)
{
for (unsigned int d = 0; d <= 4; d++)
{
delete m_ibuffers[a][b][c][d];
m_ibuffers[a][b][c][d] = 0;
}
}
}
}
}
void Planet::draw(Camera* camera, Shader* shader)
@ -34,11 +62,33 @@ void Planet::tick(Camera* camera, GLfloat dtime)
{
m_faces[i]->tick(camera, dtime);
}
// m_faces[FACE_TOP]->tick(camera, dtime);
// m_faces[FACE_BACK]->tick(camera, dtime);
}
glm::mat4 Planet::getTransformation()
void Planet::connectFaces()
{
m_faces[FACE_FRONT]->connect(TOP, m_faces[FACE_TOP]);
m_faces[FACE_FRONT]->connect(LEFT, m_faces[FACE_LEFT]);
m_faces[FACE_FRONT]->connect(BOTTOM, m_faces[FACE_BOTTOM]);
m_faces[FACE_LEFT]->connect(TOP, m_faces[FACE_TOP]);
m_faces[FACE_LEFT]->connect(LEFT, m_faces[FACE_BACK]);
m_faces[FACE_LEFT]->connect(BOTTOM, m_faces[FACE_BOTTOM]);
m_faces[FACE_BACK]->connect(TOP, m_faces[FACE_TOP]);
m_faces[FACE_BACK]->connect(LEFT, m_faces[FACE_RIGHT]);
m_faces[FACE_BACK]->connect(BOTTOM, m_faces[FACE_BOTTOM]);
m_faces[FACE_RIGHT]->connect(TOP, m_faces[FACE_TOP]);
m_faces[FACE_RIGHT]->connect(LEFT, m_faces[FACE_FRONT]);
m_faces[FACE_RIGHT]->connect(BOTTOM, m_faces[FACE_BOTTOM]);
}
const glm::mat4 Planet::getTransformation()
{
glm::mat4 newMat = glm::mat4(1.0f);
newMat = glm::translate(newMat, m_position);
return newMat;
}
PlanetIndexBuffer* Planet::getIndexBuffer(const unsigned int detailTop, const unsigned int detailRight, const unsigned int detailBottom, const unsigned int detailLeft) {
return m_ibuffers[detailTop][detailRight][detailBottom][detailLeft];
}

View File

@ -33,12 +33,14 @@ class Planet
void draw(Camera* camera, Shader* shader);
inline SimplexNoise& getNoise() { return m_noise; }
inline PlanetIndexBuffer* getBuffers() { return m_ibuffers; }
inline PlanetFace* getFace(const unsigned int face) { return m_faces[face]; }
PlanetIndexBuffer* getIndexBuffer(const unsigned int detailTop, const unsigned int detailRight, const unsigned int detailBottom, const unsigned int detailLeft);
glm::mat4 getTransformation();
const glm::mat4 getTransformation();
private:
PlanetIndexBuffer* m_ibuffers;
void connectFaces();
PlanetIndexBuffer* m_ibuffers[5][5][5][5];
SimplexNoise m_noise;
glm::vec3 m_position;
float m_radius;

View File

@ -2,57 +2,252 @@
#include <time.h>
#include <math.h>
// Correlates directly to Face enum
const glm::vec3 FACE_NORMALS[6] = {
// FACE_BOTTOM
glm::vec3(0.0f, -1.0f, 0.0f),
// FACE_TOP
glm::vec3(0.0f, 1.0f, 0.0f),
// FACE_LEFT
glm::vec3(-1.0f, 0.0f, 0.0f),
// FACE_RIGHT
glm::vec3(1.0f, 0.0f, 0.0f),
// FACE_FRONT
glm::vec3(0.0f, 0.0f, -1.0f),
// FACE_BACK
glm::vec3(0.0f, 0.0f, 1.0f)
};
PlanetFaceNode::PlanetFaceNode(Planet* planet, PlanetFace* face, glm::vec3 position, const unsigned int index, const unsigned int level) :
m_planet(planet), m_planetFace(face), m_pos(position), m_index(index), m_level(level), m_generated(false), m_dirty(true), m_leaf(true)
PlanetFaceNode::PlanetFaceNode(
PlanetFace* face,
PlanetFaceNode* parent,
const unsigned int index
) :
m_planetFace(face),
m_quadrant(index),
m_level(parent ? parent->m_level + 1 : 0),
m_generated(false),
m_dirty(true),
m_leaf(true),
m_parent(parent),
m_children(),
m_neighbors(),
neighborDetailDifferences()
{
glm::vec3 normal = face->getNormal();
// normal 0 1 0 left 1 0 0 forward 0 0 -1
// normal 0 0 -1 left 0 -1 0 forward -1 0 0
m_left = glm::vec3(normal.y, normal.z, normal.x);
m_forward = glm::cross(normal, m_left);
generate();
}
PlanetFaceNode::~PlanetFaceNode()
{
for (unsigned int i = 0; i < 4; i++)
{
if (m_neighbors[i])
{
m_neighbors[i]->setNeighbor(mirrorSide(i), 0);
}
}
if (!m_leaf)
{
for (int i = 0; i < 4; i++)
{
delete m_children[i];
}
return;
}
}
unsigned int PlanetFaceNode::mirrorSide(const unsigned int side) const
{
// If no neighbor; use default mirroring
if (!m_neighbors[side])
return MIRROR(side);
// Get source and destination faces
const unsigned int f0 = m_planetFace->getFace();
const unsigned int f1 = m_neighbors[side]->m_planetFace->getFace();
// If within the same face or faces with equal properties
if (f0 == f1 || (f0 < 4 && f1 < 4))
return MIRROR(side);
// Source face
switch (f0)
{
// Top face; always end up north
case FACE_TOP:
return TOP;
// Source bottom; always end up south
case FACE_BOTTOM:
return BOTTOM;
}
// Destination face
switch (f1)
{
// Top face; rotate to the source face
case FACE_TOP:
return MIRROR(f0);
// Bottom face; rotate to the source face
case FACE_BOTTOM:
return (4 - f0) % 4;
}
return MIRROR(side);
}
unsigned int PlanetFaceNode::mirrorQuadrant(const unsigned int side, const unsigned int quadrant) const
{
// If mirroring within the parent node
if (!ADJACENT(side, quadrant))
return REFLECT(side, quadrant);
// If no parent or parent neighbor
if (!m_parent || !m_parent->m_neighbors[side])
return REFLECT(side, quadrant);
// Get source and destination faces
const unsigned int f0 = m_planetFace->getFace();
const unsigned int f1 = m_parent->m_neighbors[side]->m_planetFace->getFace();
// If within the same face or faces with equal properties
if (f0 == f1 || (f0 < 4 && f1 < 4))
return REFLECT(side, quadrant);
// Source face
switch (f0)
{
case FACE_FRONT:
return REFLECT(side, quadrant);
case FACE_LEFT:
switch(quadrant)
{
case TOP_RIGHT:
case BOTTOM_LEFT:
return BOTTOM_LEFT;
case TOP_LEFT:
case BOTTOM_RIGHT:
return TOP_LEFT;
}
return REFLECT(side, quadrant);
case FACE_BACK:
switch (quadrant)
{
case TOP_RIGHT:
return TOP_LEFT;
case TOP_LEFT:
return TOP_RIGHT;
case BOTTOM_RIGHT:
return BOTTOM_LEFT;
case BOTTOM_LEFT:
return BOTTOM_RIGHT;
}
return REFLECT(side, quadrant);
case FACE_RIGHT:
switch (quadrant)
{
case TOP_RIGHT:
case BOTTOM_LEFT:
return TOP_RIGHT;
case TOP_LEFT:
case BOTTOM_RIGHT:
return BOTTOM_RIGHT;
}
return REFLECT(side, quadrant);
case FACE_TOP:
switch (quadrant)
{
case TOP_RIGHT:
case BOTTOM_LEFT:
return (side == TOP || side == BOTTOM) ? TOP_LEFT : TOP_RIGHT;
case TOP_LEFT:
case BOTTOM_RIGHT:
return (side == TOP || side == BOTTOM) ? TOP_RIGHT : TOP_LEFT;
}
return REFLECT(side, quadrant);
case FACE_BOTTOM:
switch (quadrant)
{
case TOP_RIGHT:
case BOTTOM_LEFT:
return (side == TOP || side == BOTTOM) ? BOTTOM_RIGHT : BOTTOM_LEFT;
case TOP_LEFT:
case BOTTOM_RIGHT:
return (side == TOP || side == BOTTOM) ? BOTTOM_LEFT : BOTTOM_RIGHT;
}
}
return REFLECT(side, quadrant);
}
void PlanetFaceNode::setNeighbor(const unsigned int side, PlanetFaceNode *neighbor)
{
// Connect the nodes and update neighbor detail differences
m_neighbors[side] = neighbor;
if (neighbor)
{
const unsigned int sideMirrored = mirrorSide(side);
neighbor->m_neighbors[sideMirrored] = this;
neighbor->updateNeighborDetailDifferences(sideMirrored);
}
updateNeighborDetailDifferences(side);
}
// Returns the neighbor of equal depth if it exists; otherwise its youngest ancestor
const PlanetFaceNode* PlanetFaceNode::getEqualOrHigherNeighbor(const unsigned int side) const
{
// Find the youngest ancestor with a neighbor in the given direction
for (const PlanetFaceNode *node = this; node != 0; node = node->m_parent)
{
if (node->m_neighbors[side])
{
return node->m_neighbors[side];
}
}
return 0;
}
void PlanetFaceNode::findNeighbor(const unsigned int side)
{
// If the current node has no neighbor in the given direction, but its parent does
if (!m_neighbors[side] && m_parent && m_parent->m_neighbors[side])
{
// If a valid neighbor is found (child of the parent's neighbor); use it
if (PlanetFaceNode *neighbor = m_parent->m_neighbors[side]->m_children[mirrorQuadrant(side, m_quadrant)])
setNeighbor(side, neighbor);
else
return;
}
// If no leaf node; find child node neighbors
if (!isLeaf())
for (unsigned int i = 0; i < 4; i++)
if (ADJACENT(side, i))
m_children[i]->findNeighbor(side);
}
void PlanetFaceNode::updateNeighborDetailDifferences(const unsigned int side)
{
// Update neighbor detail differences
for (unsigned int i = 0; i < 4; i++)
{
if (const PlanetFaceNode *neighbor = getEqualOrHigherNeighbor(i)) {
neighborDetailDifferences[i] = std::min(m_level - neighbor->m_level, (unsigned int)4); // 4 max difference
}
}
// Force child nodes on the updated side to redraw
if (!isLeaf())
{
for (unsigned int i = 0; i < 4; i++)
{
if (ADJACENT(side, i))
{
m_children[i]->updateNeighborDetailDifferences(side);
}
}
}
}
// Returns the neighbor detail (depth) difference [0,MAX_DETAIL_DIFFERENCE]
unsigned int PlanetFaceNode::getNeighborDetailDifference(const unsigned int side) const
{
return neighborDetailDifferences[side];
}
void PlanetFaceNode::tick(Camera* camera, GLfloat dtime)
{
// TODO: based on planet transform
float camToOrigin = glm::distance(camera->getPosition(), (m_planet->getPosition() + m_center));
float divisionLevel = (float)pow(2, m_level);
float splitDistance = m_planet->getRadius() / divisionLevel;
if (camToOrigin < splitDistance * 1.5 && m_leaf) {
glm::mat4 transform = m_planetFace->getPlanet()->getTransformation();
glm::transpose(transform);
glm::vec3 campos = glm::vec3(transform * glm::vec4(camera->getPosition(), 1.0f));
float camToOrigin = glm::distance(campos, (m_center));
if (camToOrigin < m_radius * 2.0 && m_leaf) {
this->subdivide();
return;
} else if (camToOrigin > splitDistance * 2.0 && !m_leaf) {
} else if (camToOrigin > m_radius * 2.0 && !m_leaf) {
this->merge();
return;
}
@ -63,15 +258,10 @@ void PlanetFaceNode::tick(Camera* camera, GLfloat dtime)
{
m_children[i]->tick(camera, dtime);
}
return;
}
}
void PlanetFaceNode::setIndexBuffer(PlanetBufferIndex buf) {
PIB* indx = m_planet->getBuffers()->getBuffer(buf);
m_ebo = indx->ebo;
m_indices = indx->indices.size();
}
void PlanetFaceNode::draw(Camera* camera, Shader* shader)
{
// TODO: occlusion culling
@ -87,13 +277,20 @@ void PlanetFaceNode::draw(Camera* camera, Shader* shader)
if (!m_generated)
return;
shader->setBuffers(m_vao, m_vbo, m_ebo);
PlanetIndexBuffer* buffers = m_planetFace->getPlanet()->getIndexBuffer(
getNeighborDetailDifference(TOP),
getNeighborDetailDifference(RIGHT),
getNeighborDetailDifference(BOTTOM),
getNeighborDetailDifference(LEFT)
);
shader->setBuffers(m_vao, m_vbo, buffers->getIbo());
shader->use();
camera->shaderViewProjection(*shader);
shader->setUniform("modelMatrix", m_planet->getTransformation());
shader->setUniform("modelMatrix", m_planetFace->getPlanet()->getTransformation());
glDrawElements(GL_TRIANGLES, m_indices, GL_UNSIGNED_INT, nullptr);
glDrawElements(GL_TRIANGLES, buffers->getVertexCount(), GL_UNSIGNED_SHORT, nullptr);
}
void PlanetFaceNode::generate()
@ -101,27 +298,22 @@ void PlanetFaceNode::generate()
if (m_generated)
return;
std::vector<Vertex> vertices;
int vertexCount = RESOLUTION * RESOLUTION;
Vertex vertices[vertexCount];
float divisionLevel = (float)pow(2.0, m_level);
float radius = m_planet->getRadius();
float divisionLevel = 1.0f / (float)pow(2.0, m_level);
float radius = m_planetFace->getPlanet()->getRadius();
float halfRes = (RESOLUTION - 1) / 2.f;
glm::vec2 rpos = getPosition();
for (int i = 0; i < RESOLUTION; i++)
{
for (int j = 0; j < RESOLUTION; j++)
{
// Get the 2D index of the vertex on the plane from zero to one (1 = RESOLUTION - 1)
glm::vec2 index = glm::vec2(i, j) / (RESOLUTION - 1.0f);
// Generate the vertices on the plane using left and forward vectors.
// here 2 * index - 1 is used to convert 0 - 1 to -1 - 1, that is to
// generate the vertices starting from the center point of the unit plane.
glm::vec3 iv = (m_forward * (2.0f * index.x - 1.0f)) / divisionLevel;
glm::vec3 jv = (m_left * (2.0f * index.y - 1.0f)) / divisionLevel;
// Add the unit left and forward vectors to the origin, m_pos here
// being the center point, which in division level zero is the offset
// normal from the center of the cube.
glm::vec3 vertex = m_pos + jv + iv;
// Get vertex position in the quadtree
glm::vec2 treePos = (glm::vec2(i, j) - halfRes) * divisionLevel / halfRes + rpos;
glm::vec3 vertex = glm::vec3(treePos, 1.0f);
// Orient the vertex to the face
vertex = glm::vec3(glm::vec4(vertex, 1.0f) * m_planetFace->getOrientation());
// Normalize and multiply by radius to create a spherical mesh (unit sphere)
float x2 = vertex.x * vertex.x;
@ -134,11 +326,17 @@ void PlanetFaceNode::generate()
);
// Get noise height and multiply by radius
float height = m_planet->getNoise().fractal(8, point.x, point.y, point.z) * 20.0f;
glm::vec3 pos = -(height + radius) * point;
float height = m_planetFace->getPlanet()->getNoise().fractal(8, point.x, point.y, point.z) * 20.0f;
glm::vec3 pos = (height + radius) * point;
// Texture coordinate
glm::vec2 texCoord = glm::vec2(i, j) / (float)(RESOLUTION - 1);
texCoord = texCoord * (1.0f - (1.0f / 128.f)) + 0.5f * (1.0f / 128.f);
// Add vertex
vertices.push_back({ pos, point, glm::vec2(j * (1.0 / RESOLUTION), i * (1.0 / RESOLUTION)) });
vertices[INDEX1D(i, j)].position = pos;
vertices[INDEX1D(i, j)].normal = point;
vertices[INDEX1D(i, j)].uv = texCoord;
// Set center
if ((i == RESOLUTION / 2 && j == RESOLUTION / 2))
@ -152,37 +350,68 @@ void PlanetFaceNode::generate()
glGenBuffers(1, &m_vbo);
glBindBuffer(GL_ARRAY_BUFFER, m_vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(Vertex) * vertices.size(), &(vertices[0]), GL_STATIC_DRAW);
glBufferData(GL_ARRAY_BUFFER, sizeof(Vertex) * vertexCount, vertices, GL_STATIC_DRAW);
glm::vec3 boundingSphereCenterF = glm::vec3(0.0, 0.0, 0.0);
float boundingSphereRadiusF = 0.0;
// Calculate mean position and use as bounding sphere center
for (int i = 0; i < vertexCount; i++)
{
boundingSphereCenterF += vertices[i].position;
}
boundingSphereCenterF /= (float)vertexCount;
// Find the largest distance from the center to a vertex
for (int i = 0; i < vertexCount; i++)
{
glm::vec3 length = (vertices[i].position - boundingSphereCenterF);
boundingSphereRadiusF = std::max(boundingSphereRadiusF, glm::dot(length, length));
}
boundingSphereRadiusF = std::sqrt(boundingSphereRadiusF);
m_center = boundingSphereCenterF;
m_radius = boundingSphereRadiusF;
m_generated = true;
setIndexBuffer(PlanetBufferIndex::Base);
}
bool PlanetFaceNode::subdivide()
{
if (m_level == 8)
if (m_level == 8 || !m_planetFace->hasSplitsLeft() || !m_leaf)
return false;
m_children[TOP_LEFT] = new PlanetFaceNode(m_planetFace, this, TOP_LEFT);
m_children[TOP_RIGHT] = new PlanetFaceNode(m_planetFace, this, TOP_RIGHT);
m_children[BOTTOM_RIGHT] = new PlanetFaceNode(m_planetFace, this, BOTTOM_RIGHT);
m_children[BOTTOM_LEFT] = new PlanetFaceNode(m_planetFace, this, BOTTOM_LEFT);
// Connect the children
m_children[TOP_LEFT]->setNeighbor(RIGHT, m_children[TOP_RIGHT]);
m_children[TOP_RIGHT]->setNeighbor(BOTTOM, m_children[BOTTOM_RIGHT]);
m_children[BOTTOM_RIGHT]->setNeighbor(LEFT, m_children[BOTTOM_LEFT]);
m_children[BOTTOM_LEFT]->setNeighbor(TOP, m_children[TOP_LEFT]);
int lv = m_level + 1;
// Calculate distance to move the vertices on the unit plane based on division level
glm::vec3 stepLeft = m_left * (1.0f / (float)pow(2, lv));
glm::vec3 stepForward = m_forward * (1.0f / (float)pow(2, lv));
m_children[TOP_LEFT] = new PlanetFaceNode(m_planet, m_planetFace, m_pos + stepForward - stepLeft, TOP_LEFT, lv);
m_children[TOP_RIGHT] = new PlanetFaceNode(m_planet, m_planetFace, m_pos - stepForward - stepLeft, TOP_RIGHT, lv);
m_children[BOTTOM_RIGHT] = new PlanetFaceNode(m_planet, m_planetFace, m_pos - stepForward + stepLeft, BOTTOM_RIGHT, lv);
m_children[BOTTOM_LEFT] = new PlanetFaceNode(m_planet, m_planetFace, m_pos + stepForward + stepLeft, BOTTOM_LEFT, lv);
// Connect neighbors
for (int i = 0; i < 4; i++)
{
if (m_neighbors[i] && !m_neighbors[i]->isLeaf())
{
m_neighbors[i]->findNeighbor(mirrorSide(i));
}
}
// No longer leaf
m_leaf = false;
for (int i = 0; i < 4; i++)
m_children[i]->m_parent = this;
this->dispose();
return true;
}
glm::vec2 PlanetFaceNode::getPosition()
{
return m_parent ? m_parent->getPosition() + glm::vec2(ADJACENT(RIGHT, m_quadrant) ? 1.0 : -1.0, ADJACENT(TOP, m_quadrant) ? 1.0 : -1.0) * (1.0f / (1 << m_level)) : glm::vec2(0.0, 0.0);
}
bool PlanetFaceNode::merge()
{
@ -195,6 +424,7 @@ bool PlanetFaceNode::merge()
{
m_children[i]->dispose();
delete m_children[i];
m_children[i] = 0;
}
// We're a leaf now
@ -213,128 +443,28 @@ bool PlanetFaceNode::isLeaf()
return m_leaf;
}
void PlanetFaceNode::getNeighbors() {
if (m_level == 0)
{
// The m_neighbors of root nodes never change
if (m_neighborTop != nullptr) return;
std::cout << m_planet->getFace(FACE_TOP)->getLODRoot() << std::endl;
switch (m_index)
{
// Front face
case FACE_FRONT:
m_neighborTop = m_planet->getFace(FACE_TOP)->getLODRoot();
m_neighborLeft = m_planet->getFace(FACE_LEFT)->getLODRoot();
m_neighborRight = m_planet->getFace(FACE_RIGHT)->getLODRoot();
m_neighborBottom = m_planet->getFace(FACE_BOTTOM)->getLODRoot();
break;
// Back face
case FACE_BACK:
m_neighborTop = m_planet->getFace(FACE_TOP)->getLODRoot();
m_neighborLeft = m_planet->getFace(FACE_RIGHT)->getLODRoot();
m_neighborRight = m_planet->getFace(FACE_LEFT)->getLODRoot();
m_neighborBottom = m_planet->getFace(FACE_BOTTOM)->getLODRoot();
break;
// Left face
case FACE_LEFT:
m_neighborTop = m_planet->getFace(FACE_TOP)->getLODRoot();
m_neighborLeft = m_planet->getFace(FACE_BACK)->getLODRoot();
m_neighborRight = m_planet->getFace(FACE_FRONT)->getLODRoot();
m_neighborBottom = m_planet->getFace(FACE_BOTTOM)->getLODRoot();
break;
// Right face
case FACE_RIGHT:
m_neighborTop = m_planet->getFace(FACE_TOP)->getLODRoot();
m_neighborLeft = m_planet->getFace(FACE_FRONT)->getLODRoot();
m_neighborRight = m_planet->getFace(FACE_BACK)->getLODRoot();
m_neighborBottom = m_planet->getFace(FACE_BOTTOM)->getLODRoot();
break;
// Top face
case FACE_TOP:
m_neighborTop = m_planet->getFace(FACE_BACK)->getLODRoot();
m_neighborLeft = m_planet->getFace(FACE_LEFT)->getLODRoot();
m_neighborRight = m_planet->getFace(FACE_RIGHT)->getLODRoot();
m_neighborBottom = m_planet->getFace(FACE_FRONT)->getLODRoot();
break;
// Bottom face
case FACE_BOTTOM:
m_neighborTop = m_planet->getFace(FACE_FRONT)->getLODRoot();
m_neighborLeft = m_planet->getFace(FACE_LEFT)->getLODRoot();
m_neighborRight = m_planet->getFace(FACE_RIGHT)->getLODRoot();
m_neighborBottom = m_planet->getFace(FACE_BACK)->getLODRoot();
break;
}
return;
}
if (m_parent == nullptr) return;
switch (m_index)
{
// Top left corner
case TOP_LEFT:
if (m_parent->m_neighborTop != nullptr)
m_neighborTop = m_parent->m_neighborTop->m_children[BOTTOM_LEFT];
if (m_parent->m_children[TOP_RIGHT] != nullptr)
m_neighborRight = m_parent->m_children[TOP_RIGHT];
if (m_parent->m_children[BOTTOM_LEFT] != nullptr)
m_neighborBottom = m_parent->m_children[BOTTOM_LEFT];
if (m_parent->m_neighborLeft != nullptr)
m_neighborLeft = m_parent->m_neighborLeft->m_children[TOP_RIGHT];
break;
// Top right corner
case TOP_RIGHT:
if (m_parent->m_neighborTop != nullptr)
m_neighborTop = m_parent->m_neighborTop->m_children[BOTTOM_RIGHT];
if (m_parent->m_neighborRight != nullptr)
m_neighborRight = m_parent->m_neighborRight->m_children[TOP_LEFT];
if (m_parent->m_children[BOTTOM_RIGHT] != nullptr)
m_neighborBottom = m_parent->m_children[BOTTOM_RIGHT];
if (m_parent->m_children[TOP_LEFT] != nullptr)
m_neighborLeft = m_parent->m_children[TOP_LEFT];
break;
// Bottom right corner
case BOTTOM_RIGHT:
if (m_parent->m_children[TOP_RIGHT] != nullptr)
m_neighborTop = m_parent->m_children[TOP_RIGHT];
if (m_parent->m_neighborRight != nullptr)
m_neighborRight = m_parent->m_neighborRight->m_children[BOTTOM_LEFT];
if (m_parent->m_neighborBottom != nullptr)
m_neighborBottom = m_parent->m_neighborBottom->m_children[TOP_RIGHT];
if (m_parent->m_children[BOTTOM_LEFT] != nullptr)
m_neighborLeft = m_parent->m_children[BOTTOM_LEFT];
break;
// Bottom left corner
case BOTTOM_LEFT:
if (m_parent->m_children[TOP_RIGHT] != nullptr)
m_neighborTop = m_parent->m_children[TOP_RIGHT];
if (m_parent->m_children[BOTTOM_RIGHT] != nullptr)
m_neighborRight = m_parent->m_children[BOTTOM_RIGHT];
if (m_parent->m_neighborBottom != nullptr)
m_neighborBottom = m_parent->m_neighborBottom->m_children[TOP_RIGHT];
if (m_parent->m_neighborLeft != nullptr)
m_neighborLeft = m_parent->m_neighborLeft->m_children[BOTTOM_RIGHT];
break;
}
glm::vec3 PlanetFaceNode::getAbsolutePosition()
{
return m_planetFace->getPlanet()->getPosition();
}
PlanetFace::PlanetFace(Planet* planet, const unsigned int face) :
m_planet(planet), m_face(face)
PlanetFace::PlanetFace(
Planet* planet,
const unsigned int face
) :
m_planet(planet),
m_face(face)
{
m_normal = FACE_NORMALS[m_face];
m_lod = new PlanetFaceNode(planet, this, m_normal, face, 0);
if (face < 4)
{
m_orientation = glm::rotate(glm::mat4(1.0f), (float)((double)face * -0.5 * M_PI), glm::vec3(0.0f, -1.0f, 0.0f));
}
else
{
m_orientation = glm::rotate(glm::mat4(1.0f), (float)((face == FACE_BOTTOM ? 0.5 : -0.5) * M_PI), glm::vec3(-1.0f, 0.0f, 0.0f));
}
m_lod = new PlanetFaceNode(this, 0, 0);
}
PlanetFace::~PlanetFace()
@ -349,5 +479,24 @@ void PlanetFace::draw(Camera* camera, Shader* shader)
void PlanetFace::tick(Camera* camera, GLfloat dtime)
{
m_splits = MAX_SPLITS_PER_UPDATE;
m_lod->tick(camera, dtime);
}
void PlanetFace::connect(const unsigned int side, PlanetFace* face)
{
if (m_lod && face->getLODRoot()) {
m_lod->setNeighbor(side, face->getLODRoot());
}
}
bool PlanetFace::hasSplitsLeft()
{
if (m_splits == 0)
{
return false;
}
m_splits--;
return true;
}

View File

@ -6,21 +6,31 @@
#include "Shader.h"
#include "Camera.h"
#define RESOLUTION 15
#define RESOLUTION 17
enum Face
{
FACE_BOTTOM, FACE_TOP,
FACE_LEFT, FACE_RIGHT,
FACE_FRONT, FACE_BACK
FACE_FRONT,
FACE_LEFT,
FACE_BACK,
FACE_RIGHT,
FACE_TOP,
FACE_BOTTOM,
};
enum LODFace
enum Quadrant
{
TOP_LEFT, TOP_RIGHT,
BOTTOM_RIGHT, BOTTOM_LEFT
};
const unsigned int MAX_SPLITS_PER_UPDATE = 2;
#define MIRROR(s) (((s) + 2) % 4)
#define ADJACENT(s, q) ((4 + (q) - (s)) % 4 <= 1)
#define REFLECT(s, q) ((s) % 2 ? ((q) % 2 ? (q) - 1 : (q) + 1) : 3 - (q))
#define INDEX1D(x, y) ((x) + (y) * 17)
struct Vertex
{
glm::vec3 position;
@ -34,7 +44,7 @@ class PlanetFace;
class PlanetFaceNode
{
public:
PlanetFaceNode(Planet* planet, PlanetFace* face, glm::vec3 position, const unsigned int index, const unsigned int level);
PlanetFaceNode(PlanetFace* face, PlanetFaceNode* parent, const unsigned int index);
~PlanetFaceNode();
void tick(Camera* camera, GLfloat dtime);
@ -42,31 +52,37 @@ class PlanetFaceNode
bool isLeaf();
inline Planet* getPlanet() const { return m_planet; }
inline PlanetFace* getPlanetFace() const { return m_planetFace; }
inline glm::vec3 getAbsolutePosition() const { return m_planet->getPosition() + m_pos; }
glm::vec3 getAbsolutePosition();
glm::vec2 getPosition();
inline PlanetFaceNode* getNeighborAt(Direction dir) const { return m_neighbors[dir]; }
inline PlanetFaceNode* getChildAt(Quadrant quad) const { return m_children[quad]; }
void setNeighbor(const unsigned int side, PlanetFaceNode *neighbor);
unsigned int mirrorSide(const unsigned int side) const;
const PlanetFaceNode* getEqualOrHigherNeighbor(const unsigned int side) const;
unsigned int getNeighborDetailDifference(const unsigned int side) const;
private:
void generate();
bool subdivide();
bool merge();
void dispose();
void setIndexBuffer(PlanetBufferIndex buf);
void getNeighbors();
void findNeighbor(const unsigned int side);
void getNeighbor(const unsigned int side);
void updateNeighborDetailDifferences(const unsigned int side);
unsigned int mirrorQuadrant(const unsigned int side, const unsigned int quadrant) const;
Planet* m_planet;
PlanetFace* m_planetFace;
PlanetFaceNode* m_parent;
PlanetFaceNode* m_children[4];
PlanetFaceNode* m_neighbors[4];
unsigned int neighborDetailDifferences[4];
PlanetFaceNode* m_neighborTop;
PlanetFaceNode* m_neighborRight;
PlanetFaceNode* m_neighborLeft;
PlanetFaceNode* m_neighborBottom;
unsigned int m_index;
unsigned int m_quadrant;
unsigned int m_level;
bool m_dirty;
@ -76,10 +92,8 @@ class PlanetFaceNode
GLuint m_ebo, m_vao, m_vbo;
int m_indices;
glm::vec3 m_pos;
glm::vec3 m_center;
glm::vec3 m_left;
glm::vec3 m_forward;
float m_radius;
};
class PlanetFace
@ -94,12 +108,20 @@ class PlanetFace
inline Planet* getPlanet() const { return m_planet; }
inline const glm::vec3 getNormal() const { return m_normal; }
inline const glm::mat4 getOrientation() const { return m_orientation; }
inline PlanetFaceNode* getLODRoot() { return m_lod; }
inline unsigned int getFace() const { return m_face; }
void connect(const unsigned int side, PlanetFace* face);
bool hasSplitsLeft();
private:
Planet* m_planet;
unsigned int m_face;
unsigned int m_splits;
glm::vec3 m_normal;
glm::mat4 m_orientation;
PlanetFaceNode* m_lod;
};

View File

@ -1,145 +1,179 @@
#include "PlanetIndexBuffer.h"
PlanetIndexBuffer::PlanetIndexBuffer(unsigned int resolution) : m_resolution(resolution) {
createBuffer(PlanetBufferIndex::Base);
createBuffer(PlanetBufferIndex::FixR, false, true, false, false);
createBuffer(PlanetBufferIndex::FixL, false, false, true, false);
createBuffer(PlanetBufferIndex::FixB, false, false, false, true);
createBuffer(PlanetBufferIndex::FixT, true, false, false, false);
createBuffer(PlanetBufferIndex::FixTR, true, true, false, false);
createBuffer(PlanetBufferIndex::FixTL, true, false, true, false);
createBuffer(PlanetBufferIndex::FixBR, false, true, false, true);
createBuffer(PlanetBufferIndex::FixBL, false, false, true, true);
PlanetIndexBuffer::PlanetIndexBuffer(
unsigned int resolution,
const unsigned int detailTop,
const unsigned int detailRight,
const unsigned int detailBottom,
const unsigned int detailLeft
) : m_resolution(resolution) {
TriangleFanList triangleFans;
// Build the patch body (without edges)
for (unsigned int x = 2; x < m_resolution - 1; x += 2)
{
for (unsigned int y = 2; y < m_resolution - 1; y += 2)
{
addTriangleFan(triangleFans);
addTriangleFanVertex(triangleFans, x, y);
addTriangleFanVertex(triangleFans, x + 1, y + 1);
addTriangleFanVertex(triangleFans, x, y + 1);
addTriangleFanVertex(triangleFans, x - 1, y + 1);
addTriangleFanVertex(triangleFans, x - 1, y);
addTriangleFanVertex(triangleFans, x - 1, y - 1);
addTriangleFanVertex(triangleFans, x, y - 1);
addTriangleFanVertex(triangleFans, x + 1, y - 1);
addTriangleFanVertex(triangleFans, x + 1, y);
addTriangleFanVertex(triangleFans, x + 1, y + 1);
}
}
// Build all four edges
buildEdge(triangleFans, TOP, detailTop);
buildEdge(triangleFans, RIGHT, detailRight);
buildEdge(triangleFans, BOTTOM, detailBottom);
buildEdge(triangleFans, LEFT, detailLeft);
// Count the number of triangles and reserve three vertices per triangle
int triangleCount = 0;
for (unsigned int i = 0; i < triangleFans.size(); i++)
if (triangleFans[i].size() > 2)
triangleCount += triangleFans[i].size() - 2;
m_vertexCount = 3 * triangleCount;
// Create an index array
GLushort *indexData = new GLushort[m_vertexCount];
int n = 0;
for (unsigned int i = 0; i < triangleFans.size(); i++)
{
for (unsigned int j = 2; j < triangleFans[i].size(); j++)
{
indexData[n++] = triangleFans[i][0];
indexData[n++] = triangleFans[i][j-1];
indexData[n++] = triangleFans[i][j];
}
}
// Generate the index buffer object (IBO)
glGenBuffers(1, &m_ibo);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_ibo);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(GLushort) * m_vertexCount, indexData, GL_STATIC_DRAW);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
// Delete unnecessary data
triangleFans.clear();
delete[] indexData;
}
PlanetIndexBuffer::~PlanetIndexBuffer() {
for (int i = 0; i < 9; i++) {
if (m_buffers[i] == nullptr) continue;
glDeleteBuffers(1, &m_buffers[i]->ebo);
m_buffers[i]->indices.clear();
delete m_buffers[i];
}
glDeleteBuffers(1, &m_ibo);
}
PIB* PlanetIndexBuffer::getBuffer(PlanetBufferIndex idx) {
return m_buffers[idx];
void PlanetIndexBuffer::rotateIndices(unsigned int &x, unsigned int &y, const unsigned int rotation)
{
// Change the indices to match the given rotation
switch (rotation)
{
case TOP:
default:
return;
case RIGHT:
std::swap(x, y);
y = m_resolution - y;
return;
case BOTTOM:
x = m_resolution - x;
y = m_resolution - y;
return;
case LEFT:
std::swap(x, y);
x = m_resolution - x;
return;
}
}
void PlanetIndexBuffer::createBuffer(PlanetBufferIndex idx, bool fanTop, bool fanRight, bool fanLeft, bool fanBottom) {
PIB* buffer = new PIB();
// Create indices for mesh
for (unsigned int gz = 0; gz < m_resolution - 1; gz++)
{
bool slantLeft = gz % 2 == 0;
for (unsigned int gx = 0; gx < m_resolution - 1; gx++)
{
unsigned int topLeft = (gz * m_resolution) + gx;
unsigned int topRight = topLeft + 1;
unsigned int bottomLeft = ((gz + 1) * m_resolution) + gx;
unsigned int bottomRight = bottomLeft + 1;
bool uTri1 = true;
bool uTri2 = true;
unsigned int tri1[3];
unsigned int tri2[3];
if (slantLeft)
{
tri1[0] = topLeft;
tri1[1] = bottomLeft;
tri1[2] = bottomRight;
tri2[0] = topLeft;
tri2[1] = bottomRight;
tri2[2] = topRight;
}
else
{
tri1[0] = topLeft;
tri1[1] = bottomLeft;
tri1[2] = topRight;
tri2[0] = bottomLeft;
tri2[1] = bottomRight;
tri2[2] = topRight;
}
if (fanTop && gz == 0)
{
if (gx % 2 == 0)
{
tri2[0] = topLeft;
tri2[1] = bottomRight;
tri2[2] = topRight + 1;
}
else
{
uTri1 = false;
}
}
if (fanRight && gx == m_resolution - 2)
{
if (gz % 2 == 0)
{
tri2[0] = topRight;
tri2[1] = bottomLeft;
tri2[2] = bottomRight + m_resolution;
}
else
{
uTri2 = false;
}
}
if (fanBottom && gz == m_resolution - 2)
{
if (gx % 2 == 0)
{
tri2[0] = bottomLeft;
tri2[1] = bottomRight + 1;
tri2[2] = topRight;
}
else
{
uTri1 = false;
}
}
if (fanLeft && gx == 0)
{
if (gz % 2 == 0)
{
tri1[0] = topLeft;
tri1[1] = bottomLeft + m_resolution;
tri1[2] = bottomRight;
}
else
{
uTri1 = false;
}
}
if (uTri1)
{
buffer->indices.push_back(tri1[0]);
buffer->indices.push_back(tri1[1]);
buffer->indices.push_back(tri1[2]);
}
if (uTri2)
{
buffer->indices.push_back(tri2[0]);
buffer->indices.push_back(tri2[1]);
buffer->indices.push_back(tri2[2]);
}
slantLeft = !slantLeft;
}
}
glGenBuffers(1, &(buffer->ebo));
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer->ebo);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(unsigned int) * buffer->indices.size(), &(buffer->indices[0]), GL_STATIC_DRAW);
m_buffers[idx] = buffer;
// Creates and adds a triangle fan
void PlanetIndexBuffer::addTriangleFan(TriangleFanList &triangleFans)
{
triangleFans.push_back(TriangleFan());
}
// adds a vertex index to the most recently added triangle fan
void PlanetIndexBuffer::addTriangleFanVertex(TriangleFanList &triangleFans, unsigned int x, unsigned int y, const unsigned int rotation)
{
// Rotate and use the x and y indices
PlanetIndexBuffer::rotateIndices(x, y, rotation);
triangleFans.back().push_back(index1D(x,y));
}
void PlanetIndexBuffer::buildEdge(TriangleFanList &triangleFans, const unsigned int side, const unsigned int detail)
{
// If detail difference is 0 (default case) or too high
if (detail == 0 || detail > 4)
{
for (unsigned int x = 0; x < m_resolution - 1; x += 2)
{
addTriangleFan(triangleFans);
addTriangleFanVertex(triangleFans, x + 1, m_resolution - 1, side);
for (int d = -1; d <= 1; d++)
addTriangleFanVertex(triangleFans, x + 1 - d, m_resolution, side);
if (x > 0)
{
addTriangleFan(triangleFans);
addTriangleFanVertex(triangleFans, x, m_resolution, side);
for (int d = -1; d <= 1; d++)
addTriangleFanVertex(triangleFans, x + d, m_resolution - 1, side);
}
}
return;
}
// Calculate step sizes; step = 2^detail
const int step = 1 << detail;
const int halfStep = step / 2;
unsigned int x = 0;
for (x = 0; x < m_resolution; x += step)
{
addTriangleFan(triangleFans);
addTriangleFanVertex(triangleFans, x, m_resolution, side);
addTriangleFanVertex(triangleFans, x + halfStep, m_resolution - 1, side);
addTriangleFanVertex(triangleFans, x + step, m_resolution, side);
if (x > 0)
{
addTriangleFan(triangleFans);
addTriangleFanVertex(triangleFans, x, m_resolution, side);
for (int d = -halfStep; d <= halfStep; d++)
addTriangleFanVertex(triangleFans, x + d, m_resolution - 1, side);
}
}
if (step > 2)
{
addTriangleFan(triangleFans);
addTriangleFanVertex(triangleFans, 0, m_resolution, side);
for (int d = 1; d <= halfStep; d++)
addTriangleFanVertex(triangleFans, d, m_resolution - 1, side);
addTriangleFan(triangleFans);
addTriangleFanVertex(triangleFans, m_resolution, m_resolution, side);
for (int d = -halfStep; d <= -1; d++)
addTriangleFanVertex(triangleFans, m_resolution + d, m_resolution - 1, side);
}
}
GLsizei PlanetIndexBuffer::getVertexCount() const
{
return m_vertexCount;
}
GLuint PlanetIndexBuffer::getIbo() const
{
return m_ibo;
}
unsigned int PlanetIndexBuffer::index1D(const unsigned int x, const unsigned int y) {
return ((x) + (y) * (m_resolution + 1));
}

View File

@ -3,24 +3,37 @@
#include "util/Common.h"
struct PIB {
GLuint ebo;
std::vector<uint> indices;
enum Direction
{
TOP, RIGHT,
BOTTOM, LEFT,
};
enum PlanetBufferIndex {
Base, FixR, FixL, FixB, FixT, FixTR, FixTL, FixBR, FixBL
};
typedef std::vector<GLushort> TriangleFan;
typedef std::vector<TriangleFan> TriangleFanList;
class PlanetIndexBuffer {
public:
PlanetIndexBuffer(unsigned int resolution);
PlanetIndexBuffer(unsigned int resolution, const unsigned int detailTop, const unsigned int detailRight, const unsigned int detailBottom, const unsigned int detailLeft);
~PlanetIndexBuffer();
PIB* getBuffer(PlanetBufferIndex idx);
// Instance methods
GLsizei getVertexCount() const;
GLuint getIbo() const;
private:
void createBuffer(PlanetBufferIndex idx, bool fanTop = false, bool fanRight = false, bool fanLeft = false, bool fanBottom = false);
PIB* m_buffers[9];
void rotateIndices(unsigned int &x, unsigned int &y, const unsigned int rotation);
void addTriangleFan(TriangleFanList &triangleFans);
void addTriangleFanVertex(TriangleFanList &triangleFans, unsigned int x, unsigned int y, const unsigned int rotation = 0);
void buildEdge(TriangleFanList &triangleFans, const unsigned int side, const unsigned int detail = 0);
// Instance variables
GLsizei m_vertexCount;
GLuint m_ibo;
// TODO: move to utility
unsigned int index1D(const unsigned int x, const unsigned int y);
unsigned int m_resolution;
};