258 lines
6.2 KiB
C++
258 lines
6.2 KiB
C++
#include "planet/PlanetFace.h"
|
|
|
|
// Correlates directly to Face enum
|
|
const glm::vec3 FACE_NORMALS[6] = {
|
|
glm::vec3(0.0f, -1.0f, 0.0f),
|
|
glm::vec3(0.0f, 1.0f, 0.0f),
|
|
glm::vec3(-1.0f, 0.0f, 0.0f),
|
|
glm::vec3(1.0f, 0.0f, 0.0f),
|
|
glm::vec3(0.0f, 0.0f, -1.0f),
|
|
glm::vec3(0.0f, 0.0f, 1.0f)
|
|
};
|
|
|
|
PlanetFaceNode::PlanetFaceNode(Planet* planet, PlanetFace* face, glm::vec3 position, const unsigned int level) :
|
|
m_planet(planet), m_planetFace(face), m_pos(position), m_level(level), m_generated(false), m_dirty(true), m_leaf(true)
|
|
{
|
|
glm::vec3 normal = face->getNormal();
|
|
m_left = glm::vec3(normal.y, normal.z, normal.x);
|
|
m_forward = glm::cross(normal, m_left);
|
|
|
|
if (level == 0)
|
|
{
|
|
float radius = m_planet->getRadius() / 2.0f;
|
|
m_pos = m_pos - (m_left * radius);
|
|
m_pos = m_pos - (m_forward * radius);
|
|
}
|
|
}
|
|
|
|
PlanetFaceNode::~PlanetFaceNode()
|
|
{
|
|
if (!m_leaf)
|
|
{
|
|
for (int i = 0; i < 4; i++)
|
|
{
|
|
delete m_children[i];
|
|
}
|
|
return;
|
|
}
|
|
|
|
glDeleteBuffers(1, &m_vao);
|
|
glDeleteBuffers(1, &m_vbo);
|
|
glDeleteBuffers(1, &m_ebo);
|
|
}
|
|
|
|
void PlanetFaceNode::tick(Camera* camera, GLfloat dtime, bool& tickUpdatedLOD, bool& tickGeneratedFace)
|
|
{
|
|
if (tickUpdatedLOD == true || tickGeneratedFace == true)
|
|
return;
|
|
|
|
// 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)
|
|
tickUpdatedLOD = this->subdivide();
|
|
else if (camToOrigin > splitDistance * 2 && !m_leaf)
|
|
tickUpdatedLOD = this->merge();
|
|
|
|
if (tickUpdatedLOD)
|
|
return;
|
|
|
|
// TODO: fix weird vanishing meshes
|
|
|
|
if (m_leaf && m_generated == false)
|
|
{
|
|
tickGeneratedFace = true;
|
|
generate();
|
|
return;
|
|
}
|
|
|
|
if (!m_leaf)
|
|
{
|
|
for (int i = 0; i < 4; i++)
|
|
{
|
|
m_children[i]->tick(camera, dtime, tickUpdatedLOD, tickGeneratedFace);
|
|
if (tickGeneratedFace || tickUpdatedLOD)
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void PlanetFaceNode::draw(Camera* camera, Shader* shader)
|
|
{
|
|
// TODO: occlusion culling
|
|
if (!m_leaf)
|
|
{
|
|
for (int i = 0; i < 4; i++)
|
|
{
|
|
m_children[i]->draw(camera, shader);
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (!m_generated)
|
|
return;
|
|
|
|
shader->setBuffers(m_vao, m_vbo, m_ebo);
|
|
shader->use();
|
|
|
|
camera->shaderViewProjection(*shader);
|
|
shader->setUniform("modelMatrix", m_planet->getTransformation());
|
|
|
|
glDrawElements(GL_TRIANGLES, m_indices, GL_UNSIGNED_INT, nullptr);
|
|
}
|
|
|
|
void PlanetFaceNode::generate()
|
|
{
|
|
if (m_generated)
|
|
return;
|
|
|
|
std::vector<Vertex> vertices;
|
|
std::vector<unsigned int> indices;
|
|
|
|
float divisionLevel = (float)pow(2.0, m_level);
|
|
float radius = m_planet->getRadius();
|
|
for (int i = 0; i < RESOLUTION; i++)
|
|
{
|
|
for (int j = 0; j < RESOLUTION; j++)
|
|
{
|
|
float iindex = i / (RESOLUTION - 1.0);
|
|
float jindex = j / (RESOLUTION - 1.0);
|
|
|
|
// From the left and forward vectors, we can calculate an oriented vertex
|
|
glm::vec3 iv = ((m_left * iindex) * radius) / divisionLevel;
|
|
glm::vec3 jv = ((m_forward * jindex) * radius) / divisionLevel;
|
|
|
|
// Add the scaled left and forward to the centered origin
|
|
glm::vec3 vertex = m_pos + (iv + jv);
|
|
|
|
// Normalize and multiply by radius to create a spherical mesh
|
|
glm::vec3 vertNormal = glm::normalize(vertex);
|
|
glm::vec3 pos = (m_planet->getNoise().fractal(m_level + 1, vertNormal.x, vertNormal.y, vertNormal.z) + radius) * vertNormal;
|
|
|
|
// Add vertex
|
|
vertices.push_back({ pos, vertNormal, glm::vec2(j * (1.0 / RESOLUTION), i * (1.0 / RESOLUTION)) });
|
|
|
|
// Set center
|
|
if ((i == RESOLUTION / 2 and j == RESOLUTION / 2))
|
|
m_center = pos;
|
|
}
|
|
}
|
|
|
|
// Create indices for mesh
|
|
for (int gz = 0; gz < RESOLUTION - 1; gz++)
|
|
{
|
|
for (int gx = 0; gx < RESOLUTION - 1; gx++)
|
|
{
|
|
int topLeft = (gz * RESOLUTION) + gx;
|
|
int topRight = topLeft + 1;
|
|
int bottomLeft = ((gz + 1) * RESOLUTION) + gx;
|
|
int bottomRight = bottomLeft + 1;
|
|
|
|
indices.push_back(bottomLeft);
|
|
indices.push_back(bottomRight);
|
|
indices.push_back(topRight);
|
|
indices.push_back(topRight);
|
|
indices.push_back(topLeft);
|
|
indices.push_back(bottomLeft);
|
|
}
|
|
}
|
|
|
|
m_indices = indices.size();
|
|
|
|
glGenVertexArrays(1, &m_vao);
|
|
glBindVertexArray(m_vao);
|
|
|
|
glGenBuffers(1, &m_vbo);
|
|
glGenBuffers(1, &m_ebo);
|
|
|
|
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_ebo);
|
|
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(unsigned int) * m_indices, &(indices[0]), GL_STATIC_DRAW);
|
|
|
|
glBindBuffer(GL_ARRAY_BUFFER, m_vbo);
|
|
glBufferData(GL_ARRAY_BUFFER, sizeof(Vertex) * vertices.size(), &(vertices[0]), GL_STATIC_DRAW);
|
|
|
|
m_generated = true;
|
|
}
|
|
|
|
bool PlanetFaceNode::subdivide()
|
|
{
|
|
if (m_level == 8)
|
|
return false;
|
|
|
|
int lv = m_level + 1;
|
|
float radius = m_planet->getRadius();
|
|
|
|
glm::vec3 stepLeft = m_left * (radius / (float)pow(2, lv));
|
|
glm::vec3 stepForward = m_forward * (radius / (float)pow(2, lv));
|
|
|
|
m_leaf = false;
|
|
|
|
m_children[0] = new PlanetFaceNode(m_planet, m_planetFace, m_pos + stepForward, lv);
|
|
m_children[1] = new PlanetFaceNode(m_planet, m_planetFace, (m_pos + stepLeft) + stepForward, lv);
|
|
m_children[2] = new PlanetFaceNode(m_planet, m_planetFace, m_pos, lv);
|
|
m_children[3] = new PlanetFaceNode(m_planet, m_planetFace, (m_pos + stepLeft), lv);
|
|
|
|
this->dispose();
|
|
|
|
return true;
|
|
}
|
|
|
|
bool PlanetFaceNode::merge()
|
|
{
|
|
// Leaves don't ever need to be merged
|
|
if (m_leaf)
|
|
return false;
|
|
|
|
// Merge and dispose the children
|
|
for (int i = 0; i < 4; i++)
|
|
{
|
|
m_children[i]->merge();
|
|
m_children[i]->dispose();
|
|
delete m_children[i];
|
|
}
|
|
|
|
// We're a leaf now
|
|
m_leaf = true;
|
|
return true;
|
|
}
|
|
|
|
void PlanetFaceNode::dispose()
|
|
{
|
|
m_generated = false;
|
|
glDeleteBuffers(1, &m_vao);
|
|
glDeleteBuffers(1, &m_vbo);
|
|
glDeleteBuffers(1, &m_ebo);
|
|
}
|
|
|
|
bool PlanetFaceNode::isLeaf()
|
|
{
|
|
return m_leaf;
|
|
}
|
|
|
|
PlanetFace::PlanetFace(Planet* planet, const unsigned int face) :
|
|
m_planet(planet), m_face(face)
|
|
{
|
|
m_normal = FACE_NORMALS[m_face];
|
|
m_planetFace = new PlanetFaceNode(planet, this, m_normal * (m_planet->getRadius() / 2.0f), 0);
|
|
}
|
|
|
|
PlanetFace::~PlanetFace()
|
|
{
|
|
delete m_planetFace;
|
|
}
|
|
|
|
void PlanetFace::draw(Camera* camera, Shader* shader)
|
|
{
|
|
m_planetFace->draw(camera, shader);
|
|
}
|
|
|
|
void PlanetFace::tick(Camera* camera, GLfloat dtime)
|
|
{
|
|
// Do only one operation per tick, TODO: maybe allow more?
|
|
bool tickUpdatedLOD = false;
|
|
bool tickGeneratedFace = false;
|
|
m_planetFace->tick(camera, dtime, tickUpdatedLOD, tickGeneratedFace);
|
|
}
|