From 20c04c0ab7f913e3d8e5841b2fec91fc31122dcf Mon Sep 17 00:00:00 2001 From: Evert Prants Date: Fri, 24 Feb 2023 15:34:40 +0200 Subject: [PATCH] working lod --- src/Application.cpp | 2 +- src/planet/Planet.cpp | 3 +- src/planet/Planet.h | 2 +- src/planet/PlanetFace.cpp | 149 +++++++++++++++++-------------- src/planet/PlanetFace.h | 11 +-- src/planet/PlanetIndexBuffer.cpp | 8 +- 6 files changed, 94 insertions(+), 81 deletions(-) diff --git a/src/Application.cpp b/src/Application.cpp index c70b150..628e559 100644 --- a/src/Application.cpp +++ b/src/Application.cpp @@ -205,7 +205,7 @@ void Application::run() // TEST CODE glEnable(GL_DEPTH_TEST); glEnable(GL_CULL_FACE); - glCullFace(GL_FRONT); + glCullFace(GL_BACK); if (!m_pauseLod) pl->tick(m_camera, deltaTime); diff --git a/src/planet/Planet.cpp b/src/planet/Planet.cpp index bdea6c4..37d560a 100644 --- a/src/planet/Planet.cpp +++ b/src/planet/Planet.cpp @@ -62,6 +62,7 @@ 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); } @@ -81,7 +82,7 @@ void Planet::connectFaces() m_faces[FACE_RIGHT]->connect(BOTTOM, m_faces[FACE_BOTTOM]); } -glm::mat4 Planet::getTransformation() +const glm::mat4 Planet::getTransformation() { glm::mat4 newMat = glm::mat4(1.0f); newMat = glm::translate(newMat, m_position); diff --git a/src/planet/Planet.h b/src/planet/Planet.h index 68a299c..749d3a5 100644 --- a/src/planet/Planet.h +++ b/src/planet/Planet.h @@ -36,7 +36,7 @@ class Planet 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: void connectFaces(); diff --git a/src/planet/PlanetFace.cpp b/src/planet/PlanetFace.cpp index 22c0090..3838c0d 100644 --- a/src/planet/PlanetFace.cpp +++ b/src/planet/PlanetFace.cpp @@ -2,33 +2,22 @@ #include #include -// Correlates directly to Face enum -const glm::vec3 FACE_NORMALS[6] = { - // FACE_FRONT - glm::vec3(0.0f, 0.0f, -1.0f), - // FACE_LEFT - glm::vec3(-1.0f, 0.0f, 0.0f), - // FACE_BACK - glm::vec3(0.0f, 0.0f, 1.0f), - // FACE_RIGHT - glm::vec3(1.0f, 0.0f, 0.0f), - // FACE_TOP - glm::vec3(0.0f, 1.0f, 0.0f), - // FACE_BOTTOM - glm::vec3(0.0f, -1.0f, 0.0f) -}; - -PlanetFaceNode::PlanetFaceNode(PlanetFace* face, PlanetFaceNode* parent, glm::vec3 position, const unsigned int index) : - m_planetFace(face), m_pos(position), m_index(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() +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(); } @@ -192,8 +181,12 @@ const PlanetFaceNode* PlanetFaceNode::getEqualOrHigherNeighbor(const unsigned in { // 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; } @@ -203,7 +196,7 @@ void PlanetFaceNode::findNeighbor(const unsigned int side) 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_index)]) + if (PlanetFaceNode *neighbor = m_parent->m_neighbors[side]->m_children[mirrorQuadrant(side, m_quadrant)]) setNeighbor(side, neighbor); else return; @@ -220,8 +213,11 @@ 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)) + { + 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()) @@ -244,12 +240,13 @@ unsigned int PlanetFaceNode::getNeighborDetailDifference(const unsigned int side void PlanetFaceNode::tick(Camera* camera, GLfloat dtime) { - // TODO: based on planet transform - float camToOrigin = glm::distance(camera->getPosition(), (m_planetFace->getPlanet()->getPosition() + m_center)); + 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 > m_radius * 2.0 && !m_leaf) { this->merge(); return; @@ -301,27 +298,22 @@ void PlanetFaceNode::generate() if (m_generated) return; - std::vector vertices; + int vertexCount = RESOLUTION * RESOLUTION; + Vertex vertices[vertexCount]; - float divisionLevel = (float)pow(2.0, m_level); + 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; @@ -335,10 +327,16 @@ void PlanetFaceNode::generate() // 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; + 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)) @@ -352,21 +350,20 @@ 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; - int patchVerticesTotal = RESOLUTION * RESOLUTION; // Calculate mean position and use as bounding sphere center - for (int i = 0; i < patchVerticesTotal; i++) + for (int i = 0; i < vertexCount; i++) { boundingSphereCenterF += vertices[i].position; } - boundingSphereCenterF /= (float)patchVerticesTotal; + boundingSphereCenterF /= (float)vertexCount; // Find the largest distance from the center to a vertex - for (int i = 0; i < patchVerticesTotal; i++) + for (int i = 0; i < vertexCount; i++) { glm::vec3 length = (vertices[i].position - boundingSphereCenterF); boundingSphereRadiusF = std::max(boundingSphereRadiusF, glm::dot(length, length)); @@ -381,19 +378,13 @@ void PlanetFaceNode::generate() bool PlanetFaceNode::subdivide() { - if (m_level == 10 || !m_planetFace->hasSplitsLeft() || !m_leaf) + if (m_level == 8 || !m_planetFace->hasSplitsLeft() || !m_leaf) return false; - 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_planetFace, this, m_pos + stepForward - stepLeft, TOP_LEFT); - m_children[TOP_RIGHT] = new PlanetFaceNode(m_planetFace, this, m_pos - stepForward - stepLeft, TOP_RIGHT); - m_children[BOTTOM_RIGHT] = new PlanetFaceNode(m_planetFace, this, m_pos - stepForward + stepLeft, BOTTOM_RIGHT); - m_children[BOTTOM_LEFT] = new PlanetFaceNode(m_planetFace, this, m_pos + stepForward + stepLeft, BOTTOM_LEFT); + 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]); @@ -403,8 +394,12 @@ bool PlanetFaceNode::subdivide() // 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; @@ -413,6 +408,10 @@ bool PlanetFaceNode::subdivide() 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() { @@ -446,14 +445,26 @@ bool PlanetFaceNode::isLeaf() glm::vec3 PlanetFaceNode::getAbsolutePosition() { - return m_planetFace->getPlanet()->getPosition() + m_pos; + 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(this, 0, m_normal, 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() diff --git a/src/planet/PlanetFace.h b/src/planet/PlanetFace.h index b65c187..c754846 100644 --- a/src/planet/PlanetFace.h +++ b/src/planet/PlanetFace.h @@ -29,6 +29,7 @@ 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 { @@ -43,7 +44,7 @@ class PlanetFace; class PlanetFaceNode { public: - PlanetFaceNode(PlanetFace* face, PlanetFaceNode* parent, glm::vec3 position, const unsigned int index); + PlanetFaceNode(PlanetFace* face, PlanetFaceNode* parent, const unsigned int index); ~PlanetFaceNode(); void tick(Camera* camera, GLfloat dtime); @@ -54,6 +55,7 @@ class PlanetFaceNode inline PlanetFace* getPlanetFace() const { return m_planetFace; } 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]; } @@ -80,7 +82,7 @@ class PlanetFaceNode PlanetFaceNode* m_neighbors[4]; unsigned int neighborDetailDifferences[4]; - unsigned int m_index; + unsigned int m_quadrant; unsigned int m_level; bool m_dirty; @@ -90,10 +92,7 @@ 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; }; @@ -109,6 +108,7 @@ 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; } @@ -121,6 +121,7 @@ class PlanetFace unsigned int m_splits; glm::vec3 m_normal; + glm::mat4 m_orientation; PlanetFaceNode* m_lod; }; diff --git a/src/planet/PlanetIndexBuffer.cpp b/src/planet/PlanetIndexBuffer.cpp index b6b6b19..542132e 100644 --- a/src/planet/PlanetIndexBuffer.cpp +++ b/src/planet/PlanetIndexBuffer.cpp @@ -29,10 +29,10 @@ PlanetIndexBuffer::PlanetIndexBuffer( } // Build all four edges - buildEdge(triangleFans, TOP, detailLeft); - buildEdge(triangleFans, RIGHT, detailBottom); - buildEdge(triangleFans, BOTTOM, detailRight); - buildEdge(triangleFans, LEFT, detailTop); + 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;