#include "planet/PlanetFace.h" #include #include 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() { 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]; } } } 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) { 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(); } else if (camToOrigin > m_radius * 2.0 && !m_leaf) { this->merge(); return; } if (!m_leaf) { for (int i = 0; i < 4; i++) { m_children[i]->tick(camera, dtime); } return; } } 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; 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_planetFace->getPlanet()->getTransformation()); glDrawElements(GL_TRIANGLES, buffers->getVertexCount(), GL_UNSIGNED_SHORT, nullptr); } void PlanetFaceNode::generate() { if (m_generated) return; int vertexCount = RESOLUTION * RESOLUTION; Vertex vertices[vertexCount]; 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 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; float y2 = vertex.y * vertex.y; float z2 = vertex.z * vertex.z; glm::vec3 point = glm::vec3( vertex.x * sqrt(1.0f - ((y2 + z2) / 2.0f) + ((y2 * z2) / 3.0f)), vertex.y * sqrt(1.0f - ((z2 + x2) / 2.0f) + ((z2 * x2) / 3.0f)), vertex.z * sqrt(1.0f - ((x2 + y2) / 2.0f) + ((x2 * y2) / 3.0f)) ); // Get noise height and multiply by radius 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[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)) 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) * 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; } bool PlanetFaceNode::subdivide() { 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]); // 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; 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() { // 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]; m_children[i] = 0; } // We're a leaf now m_leaf = true; generate(); return true; } void PlanetFaceNode::dispose() { m_generated = false; } bool PlanetFaceNode::isLeaf() { return m_leaf; } glm::vec3 PlanetFaceNode::getAbsolutePosition() { return m_planetFace->getPlanet()->getPosition(); } PlanetFace::PlanetFace( Planet* planet, const unsigned int face ) : m_planet(planet), m_face(face) { 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() { delete m_lod; } void PlanetFace::draw(Camera* camera, Shader* shader) { m_lod->draw(camera, 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; }