voxspatium/src/planet/PlanetFace.cpp

337 lines
9.3 KiB
C++

#include "planet/PlanetFace.h"
#include <time.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 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)
{
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);
}
generate();
}
PlanetFaceNode::~PlanetFaceNode()
{
if (!m_leaf)
{
for (int i = 0; i < 4; i++)
{
delete m_children[i];
}
return;
}
}
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) {
this->subdivide();
return;
} else if (camToOrigin > splitDistance * 2.0 && !m_leaf) {
this->merge();
return;
}
if (!m_leaf)
{
for (int i = 0; i < 4; i++)
{
m_children[i]->tick(camera, dtime);
}
}
}
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
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;
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 && j == RESOLUTION / 2))
m_center = pos;
}
}
glGenVertexArrays(1, &m_vao);
glBindVertexArray(m_vao);
glGenBuffers(1, &m_vbo);
glBindBuffer(GL_ARRAY_BUFFER, m_vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(Vertex) * vertices.size(), &(vertices[0]), GL_STATIC_DRAW);
m_generated = true;
setIndexBuffer(PlanetBufferIndex::Base);
}
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_children[TOP_LEFT] = new PlanetFaceNode(m_planet, m_planetFace, m_pos, TOP_LEFT, lv);
m_children[TOP_RIGHT] = new PlanetFaceNode(m_planet, m_planetFace, m_pos + stepForward, TOP_RIGHT, lv);
m_children[BOTTOM_RIGHT] = new PlanetFaceNode(m_planet, m_planetFace, (m_pos + stepLeft) + stepForward, BOTTOM_RIGHT, lv);
m_children[BOTTOM_LEFT] = new PlanetFaceNode(m_planet, m_planetFace, (m_pos + stepLeft), BOTTOM_LEFT, lv);
m_leaf = false;
for (int i = 0; i < 4; i++)
m_children[i]->m_parent = this;
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]->dispose();
delete m_children[i];
}
// We're a leaf now
m_leaf = true;
generate();
return true;
}
void PlanetFaceNode::dispose()
{
m_generated = false;
}
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;
}
}
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 * (m_planet->getRadius() / 2.0f), face, 0);
}
PlanetFace::~PlanetFace()
{
delete m_lod;
}
void PlanetFace::draw(Camera* camera, Shader* shader)
{
m_lod->draw(camera, shader);
}
void PlanetFace::tick(Camera* camera, GLfloat dtime)
{
m_lod->tick(camera, dtime);
}