#include "planet/PlanetFace.h" 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) { 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() { for (int i = 0; i < 4; i++) { delete m_children[i]; } glDeleteBuffers(1, &m_vao); glDeleteBuffers(1, &m_vbo); glDeleteBuffers(1, &m_ebo); } void PlanetFaceNode::tick(Camera* camera, GLfloat dtime) { } void PlanetFaceNode::draw(Camera* camera, Shader* shader) { if (!m_generated) return; shader->setBuffers(m_vao, m_vbo, m_ebo); shader->use(); camera->shaderViewProjection(*shader); shader->setUniform("modelMatrix", glm::translate(glm::mat4(1.0f), this->getAbsolutePosition())); glDrawElements(GL_TRIANGLES, m_indices, GL_UNSIGNED_INT, nullptr); } void PlanetFaceNode::generate() { if (m_generated) return; std::vector vertices; std::vector 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 = vertNormal * radius; // Add vertices.push_back({ pos, vertNormal, glm::vec2(j * (1.0 / RESOLUTION), i * (1.0 / RESOLUTION)) }); } } 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(topLeft); indices.push_back(topRight); indices.push_back(bottomLeft); indices.push_back(bottomLeft); indices.push_back(topRight); indices.push_back(bottomRight); } } 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; m_leaf = true; } void PlanetFaceNode::subdivide() { if (m_level == 4) return; 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[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); m_leaf = false; this->dispose(); } void PlanetFaceNode::merge() { if (m_level == 0 && m_leaf) return; for (int i = 0; i < 4; i++) { m_children[i]->merge(); m_children[i]->dispose(); delete m_children[i]; } m_leaf = 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); m_planetFace->generate(); } PlanetFace::~PlanetFace() { delete m_planetFace; } void PlanetFace::draw(Camera* camera, Shader* shader) { m_planetFace->draw(camera, shader); } void PlanetFace::tick(Camera* camera, GLfloat dtime) { m_planetFace->tick(camera, dtime); }