Compare commits
5 Commits
master
...
trashtiert
Author | SHA1 | Date |
---|---|---|
Evert Prants | 20c04c0ab7 | |
Evert Prants | c1e3e2fd3d | |
Evert Prants | 44c54b03bf | |
Evert Prants | 9a9ebf1a9b | |
Evert Prants | f56701fbdc |
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"C_Cpp.default.configurationProvider": "ms-vscode.cmake-tools",
|
||||
"files.associations": {
|
||||
"*.def": "cpp"
|
||||
}
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
#version 330
|
||||
|
||||
in vec2 uv_p;
|
||||
in vec3 testo;
|
||||
|
||||
void main(void) {
|
||||
gl_FragColor = vec4(uv_p.xy, 0.5f, 1.0f);
|
||||
gl_FragColor = vec4((testo.x / 2) + 0.5, (testo.y / 2) + 0.5, (testo.z / 2) + 0.5, 1.0f);
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
in vec3 position;
|
||||
in vec3 normal;
|
||||
in vec2 uv;
|
||||
out vec2 uv_p;
|
||||
out vec3 testo;
|
||||
|
||||
uniform mat4 projectionMatrix;
|
||||
uniform mat4 viewMatrix;
|
||||
|
@ -12,5 +12,5 @@ uniform mat4 modelMatrix;
|
|||
void main(void) {
|
||||
gl_Position = projectionMatrix * viewMatrix * modelMatrix * vec4(position, 1.0);
|
||||
|
||||
uv_p = vec2(clamp(uv.x + normal.x, 0.0, 1.0), uv.y);
|
||||
testo = normal;
|
||||
}
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
|
||||
#include "planet/Planet.h"
|
||||
|
||||
Application::Application() : m_width(1920), m_height(1080), m_lock(true)
|
||||
Application::Application() : m_width(1920), m_height(1080), m_lock(true), m_pauseLod(false)
|
||||
{
|
||||
|
||||
}
|
||||
|
@ -126,16 +126,16 @@ void Application::handleEvents()
|
|||
|
||||
// Handle Camera Movement Keys
|
||||
if(Input::getInstance().isKeyDown(SDLK_w))
|
||||
m_camera->processKeyboard(Camera_Movement::FORWARD, 0.01f);
|
||||
m_camera->processKeyboard(Camera_Movement::CAM_FORWARD, 0.01f);
|
||||
|
||||
if(Input::getInstance().isKeyDown(SDLK_s))
|
||||
m_camera->processKeyboard(Camera_Movement::BACKWARD, 0.01f);
|
||||
m_camera->processKeyboard(Camera_Movement::CAM_BACKWARD, 0.01f);
|
||||
|
||||
if(Input::getInstance().isKeyDown(SDLK_d))
|
||||
m_camera->processKeyboard(Camera_Movement::RIGHT, 0.01f);
|
||||
m_camera->processKeyboard(Camera_Movement::CAM_RIGHT, 0.01f);
|
||||
|
||||
if(Input::getInstance().isKeyDown(SDLK_a))
|
||||
m_camera->processKeyboard(Camera_Movement::LEFT, 0.01f);
|
||||
m_camera->processKeyboard(Camera_Movement::CAM_LEFT, 0.01f);
|
||||
|
||||
// Handle Camera Zoom
|
||||
m_camera->processMouseScroll((float) Input::getInstance().getMouseWheelVertical() / 10.0f);
|
||||
|
@ -159,6 +159,9 @@ void Application::handleEvents()
|
|||
SDL_ShowCursor(SDL_ENABLE);
|
||||
}
|
||||
}
|
||||
|
||||
if (Input::getInstance().isKeyPressed(SDLK_p))
|
||||
m_pauseLod = !m_pauseLod;
|
||||
}
|
||||
|
||||
|
||||
|
@ -176,7 +179,7 @@ void Application::run()
|
|||
PlanetNoiseParams noise{15.f, 15.f, .8f, 1.f/.8f};
|
||||
Planet* pl = new Planet(glm::vec3(0.0f,0.0f,0.0f), 500.0f, noise);
|
||||
// Create the shader and link them together
|
||||
Shader& chunkShader = Shader::createShader("data/shaders/chunk.vert", "data/shaders/chunk.frag");
|
||||
Shader& chunkShader = Shader::createShader("../data/shaders/chunk.vert", "../data/shaders/chunk.frag");
|
||||
chunkShader.linkShaders();
|
||||
|
||||
// Set attribute arrays
|
||||
|
@ -194,7 +197,7 @@ void Application::run()
|
|||
handleEvents();
|
||||
|
||||
// Clear color buffer
|
||||
glClearColor(0.39f, 0.58f, 0.93f, 1.f);
|
||||
glClearColor(0.0f, 0.0f, 0.0f, 1.f);
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
|
||||
deltaTime = ((m_now - m_last) / (double)SDL_GetPerformanceFrequency());
|
||||
|
@ -204,7 +207,8 @@ void Application::run()
|
|||
glEnable(GL_CULL_FACE);
|
||||
glCullFace(GL_BACK);
|
||||
|
||||
pl->tick(m_camera, deltaTime);
|
||||
if (!m_pauseLod)
|
||||
pl->tick(m_camera, deltaTime);
|
||||
|
||||
if (m_wireframe)
|
||||
glPolygonMode( GL_FRONT_AND_BACK, GL_LINE );
|
||||
|
|
|
@ -30,6 +30,8 @@ class Application : public Singleton<Application>
|
|||
void initialize();
|
||||
void exit() { m_run = false; }
|
||||
|
||||
inline bool isLodPaused() const { return m_pauseLod; }
|
||||
|
||||
inline glm::vec2 getScreenDimensions() const { return glm::vec2(m_width, m_height); }
|
||||
|
||||
friend class Singleton<Application>;
|
||||
|
@ -47,6 +49,7 @@ class Application : public Singleton<Application>
|
|||
bool m_run;
|
||||
bool m_wireframe;
|
||||
bool m_lock;
|
||||
bool m_pauseLod;
|
||||
|
||||
void handleEvents();
|
||||
void run();
|
||||
|
|
|
@ -166,13 +166,13 @@ void Camera::processKeyboard(Camera_Movement direction, GLfloat deltaTime)
|
|||
{
|
||||
GLfloat velocity = m_movementSpeed * deltaTime;
|
||||
|
||||
if (direction == FORWARD)
|
||||
if (direction == CAM_FORWARD)
|
||||
m_position += m_front * velocity;
|
||||
if (direction == BACKWARD)
|
||||
if (direction == CAM_BACKWARD)
|
||||
m_position -= m_front * velocity;
|
||||
if (direction == LEFT)
|
||||
if (direction == CAM_LEFT)
|
||||
m_position -= m_right * velocity;
|
||||
if (direction == RIGHT)
|
||||
if (direction == CAM_RIGHT)
|
||||
m_position += m_right * velocity;
|
||||
}
|
||||
|
||||
|
@ -204,22 +204,23 @@ void Camera::processMouseMovement(GLfloat xoffset, GLfloat yoffset, GLboolean co
|
|||
|
||||
void Camera::processMouseScroll(GLfloat yoffset)
|
||||
{
|
||||
if (m_zoom >= 44.0f && m_zoom <= 45.0f)
|
||||
{
|
||||
m_zoom -= yoffset;
|
||||
}
|
||||
// if (m_zoom >= 44.0f && m_zoom <= 45.0f)
|
||||
// {
|
||||
// m_zoom -= yoffset;
|
||||
// }
|
||||
|
||||
if (m_zoom <= 44.0f)
|
||||
{
|
||||
m_zoom = 44.0f;
|
||||
}
|
||||
// if (m_zoom <= 44.0f)
|
||||
// {
|
||||
// m_zoom = 44.0f;
|
||||
// }
|
||||
|
||||
if (m_zoom >= 45.0f)
|
||||
{
|
||||
m_zoom = 45.0f;
|
||||
}
|
||||
// if (m_zoom >= 45.0f)
|
||||
// {
|
||||
// m_zoom = 45.0f;
|
||||
// }
|
||||
|
||||
updateProjection();
|
||||
// updateProjection();
|
||||
m_movementSpeed += yoffset * 50.0f;
|
||||
}
|
||||
|
||||
void Camera::updateProjection(void)
|
||||
|
|
|
@ -22,10 +22,10 @@
|
|||
|
||||
// Defines several possible options for camera movement. Used as abstraction to stay away from window-system specific input methods
|
||||
enum Camera_Movement {
|
||||
FORWARD,
|
||||
BACKWARD,
|
||||
LEFT,
|
||||
RIGHT
|
||||
CAM_FORWARD,
|
||||
CAM_BACKWARD,
|
||||
CAM_LEFT,
|
||||
CAM_RIGHT
|
||||
};
|
||||
|
||||
// Default camera values
|
||||
|
|
|
@ -154,7 +154,7 @@ GLuint Shader::getAttribLocation(const std::string& attrbuteName)
|
|||
GLint attrib = glGetAttribLocation(m_programID, attrbuteName.c_str());
|
||||
if (attrib == GL_INVALID_OPERATION || attrib < 0)
|
||||
{
|
||||
logWarn("Attribute " + attrbuteName + " doesn't exist in program.");
|
||||
// logWarn("Attribute " + attrbuteName + " doesn't exist in program.");
|
||||
}
|
||||
|
||||
return attrib;
|
||||
|
|
|
@ -0,0 +1,595 @@
|
|||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Sphere.cpp
|
||||
// ==========
|
||||
// Sphere for OpenGL with (radius, sectors, stacks)
|
||||
// The min number of sectors is 3 and the min number of stacks are 2.
|
||||
//
|
||||
// AUTHOR: Song Ho Ahn (song.ahn@gmail.com)
|
||||
// CREATED: 2017-11-01
|
||||
// UPDATED: 2020-05-20
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h> // include windows.h to avoid thousands of compile errors even though this class is not depending on Windows
|
||||
#endif
|
||||
|
||||
#ifdef __APPLE__
|
||||
#include <OpenGL/gl.h>
|
||||
#else
|
||||
#include <GL/gl.h>
|
||||
#endif
|
||||
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
#include <cmath>
|
||||
#include "Sphere.h"
|
||||
|
||||
|
||||
|
||||
// constants //////////////////////////////////////////////////////////////////
|
||||
const int MIN_SECTOR_COUNT = 3;
|
||||
const int MIN_STACK_COUNT = 2;
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// ctor
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
Sphere::Sphere(float radius, int sectors, int stacks, bool smooth) : interleavedStride(32)
|
||||
{
|
||||
set(radius, sectors, stacks, smooth);
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// setters
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
void Sphere::set(float radius, int sectors, int stacks, bool smooth)
|
||||
{
|
||||
this->radius = radius;
|
||||
this->sectorCount = sectors;
|
||||
if(sectors < MIN_SECTOR_COUNT)
|
||||
this->sectorCount = MIN_SECTOR_COUNT;
|
||||
this->stackCount = stacks;
|
||||
if(sectors < MIN_STACK_COUNT)
|
||||
this->sectorCount = MIN_STACK_COUNT;
|
||||
this->smooth = smooth;
|
||||
|
||||
if(smooth)
|
||||
buildVerticesSmooth();
|
||||
else
|
||||
buildVerticesFlat();
|
||||
}
|
||||
|
||||
void Sphere::setRadius(float radius)
|
||||
{
|
||||
if(radius != this->radius)
|
||||
set(radius, sectorCount, stackCount, smooth);
|
||||
}
|
||||
|
||||
void Sphere::setSectorCount(int sectors)
|
||||
{
|
||||
if(sectors != this->sectorCount)
|
||||
set(radius, sectors, stackCount, smooth);
|
||||
}
|
||||
|
||||
void Sphere::setStackCount(int stacks)
|
||||
{
|
||||
if(stacks != this->stackCount)
|
||||
set(radius, sectorCount, stacks, smooth);
|
||||
}
|
||||
|
||||
void Sphere::setSmooth(bool smooth)
|
||||
{
|
||||
if(this->smooth == smooth)
|
||||
return;
|
||||
|
||||
this->smooth = smooth;
|
||||
if(smooth)
|
||||
buildVerticesSmooth();
|
||||
else
|
||||
buildVerticesFlat();
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// print itself
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
void Sphere::printSelf() const
|
||||
{
|
||||
std::cout << "===== Sphere =====\n"
|
||||
<< " Radius: " << radius << "\n"
|
||||
<< " Sector Count: " << sectorCount << "\n"
|
||||
<< " Stack Count: " << stackCount << "\n"
|
||||
<< "Smooth Shading: " << (smooth ? "true" : "false") << "\n"
|
||||
<< "Triangle Count: " << getTriangleCount() << "\n"
|
||||
<< " Index Count: " << getIndexCount() << "\n"
|
||||
<< " Vertex Count: " << getVertexCount() << "\n"
|
||||
<< " Normal Count: " << getNormalCount() << "\n"
|
||||
<< "TexCoord Count: " << getTexCoordCount() << std::endl;
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// draw a sphere in VertexArray mode
|
||||
// OpenGL RC must be set before calling it
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
void Sphere::draw() const
|
||||
{
|
||||
// interleaved array
|
||||
glEnableClientState(GL_VERTEX_ARRAY);
|
||||
glEnableClientState(GL_NORMAL_ARRAY);
|
||||
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
|
||||
glVertexPointer(3, GL_FLOAT, interleavedStride, &interleavedVertices[0]);
|
||||
glNormalPointer(GL_FLOAT, interleavedStride, &interleavedVertices[3]);
|
||||
glTexCoordPointer(2, GL_FLOAT, interleavedStride, &interleavedVertices[6]);
|
||||
|
||||
glDrawElements(GL_TRIANGLES, (unsigned int)indices.size(), GL_UNSIGNED_INT, indices.data());
|
||||
|
||||
glDisableClientState(GL_VERTEX_ARRAY);
|
||||
glDisableClientState(GL_NORMAL_ARRAY);
|
||||
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// draw lines only
|
||||
// the caller must set the line width before call this
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
void Sphere::drawLines(const float lineColor[4]) const
|
||||
{
|
||||
// set line colour
|
||||
glColor4fv(lineColor);
|
||||
glMaterialfv(GL_FRONT, GL_DIFFUSE, lineColor);
|
||||
|
||||
// draw lines with VA
|
||||
glDisable(GL_LIGHTING);
|
||||
glDisable(GL_TEXTURE_2D);
|
||||
glEnableClientState(GL_VERTEX_ARRAY);
|
||||
glVertexPointer(3, GL_FLOAT, 0, vertices.data());
|
||||
|
||||
glDrawElements(GL_LINES, (unsigned int)lineIndices.size(), GL_UNSIGNED_INT, lineIndices.data());
|
||||
|
||||
glDisableClientState(GL_VERTEX_ARRAY);
|
||||
glEnable(GL_LIGHTING);
|
||||
glEnable(GL_TEXTURE_2D);
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// draw a sphere surfaces and lines on top of it
|
||||
// the caller must set the line width before call this
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
void Sphere::drawWithLines(const float lineColor[4]) const
|
||||
{
|
||||
glEnable(GL_POLYGON_OFFSET_FILL);
|
||||
glPolygonOffset(1.0, 1.0f); // move polygon backward
|
||||
this->draw();
|
||||
glDisable(GL_POLYGON_OFFSET_FILL);
|
||||
|
||||
// draw lines with VA
|
||||
drawLines(lineColor);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*@@ FIXME: when the radius = 0
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// update vertex positions only
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
void Sphere::updateRadius()
|
||||
{
|
||||
float scale = sqrtf(radius * radius / (vertices[0] * vertices[0] + vertices[1] * vertices[1] + vertices[2] * vertices[2]));
|
||||
|
||||
std::size_t i, j;
|
||||
std::size_t count = vertices.size();
|
||||
for(i = 0, j = 0; i < count; i += 3, j += 8)
|
||||
{
|
||||
vertices[i] *= scale;
|
||||
vertices[i+1] *= scale;
|
||||
vertices[i+2] *= scale;
|
||||
|
||||
// for interleaved array
|
||||
interleavedVertices[j] *= scale;
|
||||
interleavedVertices[j+1] *= scale;
|
||||
interleavedVertices[j+2] *= scale;
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// dealloc vectors
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
void Sphere::clearArrays()
|
||||
{
|
||||
std::vector<float>().swap(vertices);
|
||||
std::vector<float>().swap(normals);
|
||||
std::vector<float>().swap(texCoords);
|
||||
std::vector<unsigned int>().swap(indices);
|
||||
std::vector<unsigned int>().swap(lineIndices);
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// build vertices of sphere with smooth shading using parametric equation
|
||||
// x = r * cos(u) * cos(v)
|
||||
// y = r * cos(u) * sin(v)
|
||||
// z = r * sin(u)
|
||||
// where u: stack(latitude) angle (-90 <= u <= 90)
|
||||
// v: sector(longitude) angle (0 <= v <= 360)
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
void Sphere::buildVerticesSmooth()
|
||||
{
|
||||
const float PI = acos(-1);
|
||||
|
||||
// clear memory of prev arrays
|
||||
clearArrays();
|
||||
|
||||
float x, y, z, xy; // vertex position
|
||||
float nx, ny, nz, lengthInv = 1.0f / radius; // normal
|
||||
float s, t; // texCoord
|
||||
|
||||
float sectorStep = 2 * PI / sectorCount;
|
||||
float stackStep = PI / stackCount;
|
||||
float sectorAngle, stackAngle;
|
||||
|
||||
for(int i = 0; i <= stackCount; ++i)
|
||||
{
|
||||
stackAngle = PI / 2 - i * stackStep; // starting from pi/2 to -pi/2
|
||||
xy = radius * cosf(stackAngle); // r * cos(u)
|
||||
z = radius * sinf(stackAngle); // r * sin(u)
|
||||
|
||||
// add (sectorCount+1) vertices per stack
|
||||
// the first and last vertices have same position and normal, but different tex coords
|
||||
for(int j = 0; j <= sectorCount; ++j)
|
||||
{
|
||||
sectorAngle = j * sectorStep; // starting from 0 to 2pi
|
||||
|
||||
// vertex position
|
||||
x = xy * cosf(sectorAngle); // r * cos(u) * cos(v)
|
||||
y = xy * sinf(sectorAngle); // r * cos(u) * sin(v)
|
||||
addVertex(x, y, z);
|
||||
|
||||
// normalized vertex normal
|
||||
nx = x * lengthInv;
|
||||
ny = y * lengthInv;
|
||||
nz = z * lengthInv;
|
||||
addNormal(nx, ny, nz);
|
||||
|
||||
// vertex tex coord between [0, 1]
|
||||
s = (float)j / sectorCount;
|
||||
t = (float)i / stackCount;
|
||||
addTexCoord(s, t);
|
||||
}
|
||||
}
|
||||
|
||||
// indices
|
||||
// k1--k1+1
|
||||
// | / |
|
||||
// | / |
|
||||
// k2--k2+1
|
||||
unsigned int k1, k2;
|
||||
for(int i = 0; i < stackCount; ++i)
|
||||
{
|
||||
k1 = i * (sectorCount + 1); // beginning of current stack
|
||||
k2 = k1 + sectorCount + 1; // beginning of next stack
|
||||
|
||||
for(int j = 0; j < sectorCount; ++j, ++k1, ++k2)
|
||||
{
|
||||
// 2 triangles per sector excluding 1st and last stacks
|
||||
if(i != 0)
|
||||
{
|
||||
addIndices(k1, k2, k1+1); // k1---k2---k1+1
|
||||
}
|
||||
|
||||
if(i != (stackCount-1))
|
||||
{
|
||||
addIndices(k1+1, k2, k2+1); // k1+1---k2---k2+1
|
||||
}
|
||||
|
||||
// vertical lines for all stacks
|
||||
lineIndices.push_back(k1);
|
||||
lineIndices.push_back(k2);
|
||||
if(i != 0) // horizontal lines except 1st stack
|
||||
{
|
||||
lineIndices.push_back(k1);
|
||||
lineIndices.push_back(k1 + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// generate interleaved vertex array as well
|
||||
buildInterleavedVertices();
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// generate vertices with flat shading
|
||||
// each triangle is independent (no shared vertices)
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
void Sphere::buildVerticesFlat()
|
||||
{
|
||||
const float PI = acos(-1);
|
||||
|
||||
// tmp vertex definition (x,y,z,s,t)
|
||||
struct Vertex
|
||||
{
|
||||
float x, y, z, s, t;
|
||||
};
|
||||
std::vector<Vertex> tmpVertices;
|
||||
|
||||
float sectorStep = 2 * PI / sectorCount;
|
||||
float stackStep = PI / stackCount;
|
||||
float sectorAngle, stackAngle;
|
||||
|
||||
// compute all vertices first, each vertex contains (x,y,z,s,t) except normal
|
||||
for(int i = 0; i <= stackCount; ++i)
|
||||
{
|
||||
stackAngle = PI / 2 - i * stackStep; // starting from pi/2 to -pi/2
|
||||
float xy = radius * cosf(stackAngle); // r * cos(u)
|
||||
float z = radius * sinf(stackAngle); // r * sin(u)
|
||||
|
||||
// add (sectorCount+1) vertices per stack
|
||||
// the first and last vertices have same position and normal, but different tex coords
|
||||
for(int j = 0; j <= sectorCount; ++j)
|
||||
{
|
||||
sectorAngle = j * sectorStep; // starting from 0 to 2pi
|
||||
|
||||
Vertex vertex;
|
||||
vertex.x = xy * cosf(sectorAngle); // x = r * cos(u) * cos(v)
|
||||
vertex.y = xy * sinf(sectorAngle); // y = r * cos(u) * sin(v)
|
||||
vertex.z = z; // z = r * sin(u)
|
||||
vertex.s = (float)j/sectorCount; // s
|
||||
vertex.t = (float)i/stackCount; // t
|
||||
tmpVertices.push_back(vertex);
|
||||
}
|
||||
}
|
||||
|
||||
// clear memory of prev arrays
|
||||
clearArrays();
|
||||
|
||||
Vertex v1, v2, v3, v4; // 4 vertex positions and tex coords
|
||||
std::vector<float> n; // 1 face normal
|
||||
|
||||
int i, j, k, vi1, vi2;
|
||||
int index = 0; // index for vertex
|
||||
for(i = 0; i < stackCount; ++i)
|
||||
{
|
||||
vi1 = i * (sectorCount + 1); // index of tmpVertices
|
||||
vi2 = (i + 1) * (sectorCount + 1);
|
||||
|
||||
for(j = 0; j < sectorCount; ++j, ++vi1, ++vi2)
|
||||
{
|
||||
// get 4 vertices per sector
|
||||
// v1--v3
|
||||
// | |
|
||||
// v2--v4
|
||||
v1 = tmpVertices[vi1];
|
||||
v2 = tmpVertices[vi2];
|
||||
v3 = tmpVertices[vi1 + 1];
|
||||
v4 = tmpVertices[vi2 + 1];
|
||||
|
||||
// if 1st stack and last stack, store only 1 triangle per sector
|
||||
// otherwise, store 2 triangles (quad) per sector
|
||||
if(i == 0) // a triangle for first stack ==========================
|
||||
{
|
||||
// put a triangle
|
||||
addVertex(v1.x, v1.y, v1.z);
|
||||
addVertex(v2.x, v2.y, v2.z);
|
||||
addVertex(v4.x, v4.y, v4.z);
|
||||
|
||||
// put tex coords of triangle
|
||||
addTexCoord(v1.s, v1.t);
|
||||
addTexCoord(v2.s, v2.t);
|
||||
addTexCoord(v4.s, v4.t);
|
||||
|
||||
// put normal
|
||||
n = computeFaceNormal(v1.x,v1.y,v1.z, v2.x,v2.y,v2.z, v4.x,v4.y,v4.z);
|
||||
for(k = 0; k < 3; ++k) // same normals for 3 vertices
|
||||
{
|
||||
addNormal(n[0], n[1], n[2]);
|
||||
}
|
||||
|
||||
// put indices of 1 triangle
|
||||
addIndices(index, index+1, index+2);
|
||||
|
||||
// indices for line (first stack requires only vertical line)
|
||||
lineIndices.push_back(index);
|
||||
lineIndices.push_back(index+1);
|
||||
|
||||
index += 3; // for next
|
||||
}
|
||||
else if(i == (stackCount-1)) // a triangle for last stack =========
|
||||
{
|
||||
// put a triangle
|
||||
addVertex(v1.x, v1.y, v1.z);
|
||||
addVertex(v2.x, v2.y, v2.z);
|
||||
addVertex(v3.x, v3.y, v3.z);
|
||||
|
||||
// put tex coords of triangle
|
||||
addTexCoord(v1.s, v1.t);
|
||||
addTexCoord(v2.s, v2.t);
|
||||
addTexCoord(v3.s, v3.t);
|
||||
|
||||
// put normal
|
||||
n = computeFaceNormal(v1.x,v1.y,v1.z, v2.x,v2.y,v2.z, v3.x,v3.y,v3.z);
|
||||
for(k = 0; k < 3; ++k) // same normals for 3 vertices
|
||||
{
|
||||
addNormal(n[0], n[1], n[2]);
|
||||
}
|
||||
|
||||
// put indices of 1 triangle
|
||||
addIndices(index, index+1, index+2);
|
||||
|
||||
// indices for lines (last stack requires both vert/hori lines)
|
||||
lineIndices.push_back(index);
|
||||
lineIndices.push_back(index+1);
|
||||
lineIndices.push_back(index);
|
||||
lineIndices.push_back(index+2);
|
||||
|
||||
index += 3; // for next
|
||||
}
|
||||
else // 2 triangles for others ====================================
|
||||
{
|
||||
// put quad vertices: v1-v2-v3-v4
|
||||
addVertex(v1.x, v1.y, v1.z);
|
||||
addVertex(v2.x, v2.y, v2.z);
|
||||
addVertex(v3.x, v3.y, v3.z);
|
||||
addVertex(v4.x, v4.y, v4.z);
|
||||
|
||||
// put tex coords of quad
|
||||
addTexCoord(v1.s, v1.t);
|
||||
addTexCoord(v2.s, v2.t);
|
||||
addTexCoord(v3.s, v3.t);
|
||||
addTexCoord(v4.s, v4.t);
|
||||
|
||||
// put normal
|
||||
n = computeFaceNormal(v1.x,v1.y,v1.z, v2.x,v2.y,v2.z, v3.x,v3.y,v3.z);
|
||||
for(k = 0; k < 4; ++k) // same normals for 4 vertices
|
||||
{
|
||||
addNormal(n[0], n[1], n[2]);
|
||||
}
|
||||
|
||||
// put indices of quad (2 triangles)
|
||||
addIndices(index, index+1, index+2);
|
||||
addIndices(index+2, index+1, index+3);
|
||||
|
||||
// indices for lines
|
||||
lineIndices.push_back(index);
|
||||
lineIndices.push_back(index+1);
|
||||
lineIndices.push_back(index);
|
||||
lineIndices.push_back(index+2);
|
||||
|
||||
index += 4; // for next
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// generate interleaved vertex array as well
|
||||
buildInterleavedVertices();
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// generate interleaved vertices: V/N/T
|
||||
// stride must be 32 bytes
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
void Sphere::buildInterleavedVertices()
|
||||
{
|
||||
std::vector<float>().swap(interleavedVertices);
|
||||
|
||||
std::size_t i, j;
|
||||
std::size_t count = vertices.size();
|
||||
for(i = 0, j = 0; i < count; i += 3, j += 2)
|
||||
{
|
||||
interleavedVertices.push_back(vertices[i]);
|
||||
interleavedVertices.push_back(vertices[i+1]);
|
||||
interleavedVertices.push_back(vertices[i+2]);
|
||||
|
||||
interleavedVertices.push_back(normals[i]);
|
||||
interleavedVertices.push_back(normals[i+1]);
|
||||
interleavedVertices.push_back(normals[i+2]);
|
||||
|
||||
interleavedVertices.push_back(texCoords[j]);
|
||||
interleavedVertices.push_back(texCoords[j+1]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// add single vertex to array
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
void Sphere::addVertex(float x, float y, float z)
|
||||
{
|
||||
vertices.push_back(x);
|
||||
vertices.push_back(y);
|
||||
vertices.push_back(z);
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// add single normal to array
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
void Sphere::addNormal(float nx, float ny, float nz)
|
||||
{
|
||||
normals.push_back(nx);
|
||||
normals.push_back(ny);
|
||||
normals.push_back(nz);
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// add single texture coord to array
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
void Sphere::addTexCoord(float s, float t)
|
||||
{
|
||||
texCoords.push_back(s);
|
||||
texCoords.push_back(t);
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// add 3 indices to array
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
void Sphere::addIndices(unsigned int i1, unsigned int i2, unsigned int i3)
|
||||
{
|
||||
indices.push_back(i1);
|
||||
indices.push_back(i2);
|
||||
indices.push_back(i3);
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// return face normal of a triangle v1-v2-v3
|
||||
// if a triangle has no surface (normal length = 0), then return a zero vector
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
std::vector<float> Sphere::computeFaceNormal(float x1, float y1, float z1, // v1
|
||||
float x2, float y2, float z2, // v2
|
||||
float x3, float y3, float z3) // v3
|
||||
{
|
||||
const float EPSILON = 0.000001f;
|
||||
|
||||
std::vector<float> normal(3, 0.0f); // default return value (0,0,0)
|
||||
float nx, ny, nz;
|
||||
|
||||
// find 2 edge vectors: v1-v2, v1-v3
|
||||
float ex1 = x2 - x1;
|
||||
float ey1 = y2 - y1;
|
||||
float ez1 = z2 - z1;
|
||||
float ex2 = x3 - x1;
|
||||
float ey2 = y3 - y1;
|
||||
float ez2 = z3 - z1;
|
||||
|
||||
// cross product: e1 x e2
|
||||
nx = ey1 * ez2 - ez1 * ey2;
|
||||
ny = ez1 * ex2 - ex1 * ez2;
|
||||
nz = ex1 * ey2 - ey1 * ex2;
|
||||
|
||||
// normalize only if the length is > 0
|
||||
float length = sqrtf(nx * nx + ny * ny + nz * nz);
|
||||
if(length > EPSILON)
|
||||
{
|
||||
// normalize
|
||||
float lengthInv = 1.0f / length;
|
||||
normal[0] = nx * lengthInv;
|
||||
normal[1] = ny * lengthInv;
|
||||
normal[2] = nz * lengthInv;
|
||||
}
|
||||
|
||||
return normal;
|
||||
}
|
|
@ -0,0 +1,99 @@
|
|||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Sphere.h
|
||||
// ========
|
||||
// Sphere for OpenGL with (radius, sectors, stacks)
|
||||
// The min number of sectors is 3 and The min number of stacks are 2.
|
||||
//
|
||||
// AUTHOR: Song Ho Ahn (song.ahn@gmail.com)
|
||||
// CREATED: 2017-11-01
|
||||
// UPDATED: 2020-05-20
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef GEOMETRY_SPHERE_H
|
||||
#define GEOMETRY_SPHERE_H
|
||||
|
||||
#include <vector>
|
||||
|
||||
class Sphere
|
||||
{
|
||||
public:
|
||||
// ctor/dtor
|
||||
Sphere(float radius=1.0f, int sectorCount=36, int stackCount=18, bool smooth=true);
|
||||
~Sphere() {}
|
||||
|
||||
// getters/setters
|
||||
float getRadius() const { return radius; }
|
||||
int getSectorCount() const { return sectorCount; }
|
||||
int getStackCount() const { return stackCount; }
|
||||
void set(float radius, int sectorCount, int stackCount, bool smooth=true);
|
||||
void setRadius(float radius);
|
||||
void setSectorCount(int sectorCount);
|
||||
void setStackCount(int stackCount);
|
||||
void setSmooth(bool smooth);
|
||||
|
||||
// for vertex data
|
||||
unsigned int getVertexCount() const { return (unsigned int)vertices.size() / 3; }
|
||||
unsigned int getNormalCount() const { return (unsigned int)normals.size() / 3; }
|
||||
unsigned int getTexCoordCount() const { return (unsigned int)texCoords.size() / 2; }
|
||||
unsigned int getIndexCount() const { return (unsigned int)indices.size(); }
|
||||
unsigned int getLineIndexCount() const { return (unsigned int)lineIndices.size(); }
|
||||
unsigned int getTriangleCount() const { return getIndexCount() / 3; }
|
||||
unsigned int getVertexSize() const { return (unsigned int)vertices.size() * sizeof(float); }
|
||||
unsigned int getNormalSize() const { return (unsigned int)normals.size() * sizeof(float); }
|
||||
unsigned int getTexCoordSize() const { return (unsigned int)texCoords.size() * sizeof(float); }
|
||||
unsigned int getIndexSize() const { return (unsigned int)indices.size() * sizeof(unsigned int); }
|
||||
unsigned int getLineIndexSize() const { return (unsigned int)lineIndices.size() * sizeof(unsigned int); }
|
||||
const float* getVertices() const { return vertices.data(); }
|
||||
const float* getNormals() const { return normals.data(); }
|
||||
const float* getTexCoords() const { return texCoords.data(); }
|
||||
const unsigned int* getIndices() const { return indices.data(); }
|
||||
const unsigned int* getLineIndices() const { return lineIndices.data(); }
|
||||
|
||||
// for interleaved vertices: V/N/T
|
||||
unsigned int getInterleavedVertexCount() const { return getVertexCount(); } // # of vertices
|
||||
unsigned int getInterleavedVertexSize() const { return (unsigned int)interleavedVertices.size() * sizeof(float); } // # of bytes
|
||||
int getInterleavedStride() const { return interleavedStride; } // should be 32 bytes
|
||||
const float* getInterleavedVertices() const { return interleavedVertices.data(); }
|
||||
|
||||
// draw in VertexArray mode
|
||||
void draw() const; // draw surface
|
||||
void drawLines(const float lineColor[4]) const; // draw lines only
|
||||
void drawWithLines(const float lineColor[4]) const; // draw surface and lines
|
||||
|
||||
// debug
|
||||
void printSelf() const;
|
||||
|
||||
protected:
|
||||
|
||||
private:
|
||||
// member functions
|
||||
void buildVerticesSmooth();
|
||||
void buildVerticesFlat();
|
||||
void buildInterleavedVertices();
|
||||
void clearArrays();
|
||||
void addVertex(float x, float y, float z);
|
||||
void addNormal(float x, float y, float z);
|
||||
void addTexCoord(float s, float t);
|
||||
void addIndices(unsigned int i1, unsigned int i2, unsigned int i3);
|
||||
std::vector<float> computeFaceNormal(float x1, float y1, float z1,
|
||||
float x2, float y2, float z2,
|
||||
float x3, float y3, float z3);
|
||||
|
||||
// memeber vars
|
||||
float radius;
|
||||
int sectorCount; // longitude, # of slices
|
||||
int stackCount; // latitude, # of stacks
|
||||
bool smooth;
|
||||
std::vector<float> vertices;
|
||||
std::vector<float> normals;
|
||||
std::vector<float> texCoords;
|
||||
std::vector<unsigned int> indices;
|
||||
std::vector<unsigned int> lineIndices;
|
||||
|
||||
// interleaved
|
||||
std::vector<float> interleavedVertices;
|
||||
int interleavedStride; // # of bytes to hop to the next vertex (should be 32 bytes)
|
||||
|
||||
};
|
||||
|
||||
#endif
|
|
@ -2,13 +2,27 @@
|
|||
#include "planet/PlanetFace.h"
|
||||
|
||||
Planet::Planet(glm::vec3 position, float radius, PlanetNoiseParams &noiseParams) :
|
||||
m_position(position), m_radius(radius), m_noise(noiseParams.frequency, noiseParams.amplitude, noiseParams.lacunarity, noiseParams.persistence),
|
||||
m_ibuffers(new PlanetIndexBuffer(15))
|
||||
m_position(position), m_radius(radius), m_noise(noiseParams.frequency, noiseParams.amplitude, noiseParams.lacunarity, noiseParams.persistence)
|
||||
{
|
||||
for (unsigned int a = 0; a <= 4; a++)
|
||||
{
|
||||
for (unsigned int b = 0; b <= 4; b++)
|
||||
{
|
||||
for (unsigned int c = 0; c <= 4; c++)
|
||||
{
|
||||
for (unsigned int d = 0; d <= 4; d++)
|
||||
{
|
||||
m_ibuffers[a][b][c][d] = new PlanetIndexBuffer(16, a, b, c, d);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < 6; i++)
|
||||
{
|
||||
m_faces[i] = new PlanetFace(this, i);
|
||||
}
|
||||
connectFaces();
|
||||
}
|
||||
|
||||
Planet::~Planet()
|
||||
|
@ -17,7 +31,21 @@ Planet::~Planet()
|
|||
{
|
||||
delete m_faces[i];
|
||||
}
|
||||
delete m_ibuffers;
|
||||
|
||||
for (unsigned int a = 0; a <= 4; a++)
|
||||
{
|
||||
for (unsigned int b = 0; b <= 4; b++)
|
||||
{
|
||||
for (unsigned int c = 0; c <= 4; c++)
|
||||
{
|
||||
for (unsigned int d = 0; d <= 4; d++)
|
||||
{
|
||||
delete m_ibuffers[a][b][c][d];
|
||||
m_ibuffers[a][b][c][d] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Planet::draw(Camera* camera, Shader* shader)
|
||||
|
@ -34,11 +62,33 @@ 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);
|
||||
}
|
||||
|
||||
glm::mat4 Planet::getTransformation()
|
||||
void Planet::connectFaces()
|
||||
{
|
||||
m_faces[FACE_FRONT]->connect(TOP, m_faces[FACE_TOP]);
|
||||
m_faces[FACE_FRONT]->connect(LEFT, m_faces[FACE_LEFT]);
|
||||
m_faces[FACE_FRONT]->connect(BOTTOM, m_faces[FACE_BOTTOM]);
|
||||
m_faces[FACE_LEFT]->connect(TOP, m_faces[FACE_TOP]);
|
||||
m_faces[FACE_LEFT]->connect(LEFT, m_faces[FACE_BACK]);
|
||||
m_faces[FACE_LEFT]->connect(BOTTOM, m_faces[FACE_BOTTOM]);
|
||||
m_faces[FACE_BACK]->connect(TOP, m_faces[FACE_TOP]);
|
||||
m_faces[FACE_BACK]->connect(LEFT, m_faces[FACE_RIGHT]);
|
||||
m_faces[FACE_BACK]->connect(BOTTOM, m_faces[FACE_BOTTOM]);
|
||||
m_faces[FACE_RIGHT]->connect(TOP, m_faces[FACE_TOP]);
|
||||
m_faces[FACE_RIGHT]->connect(LEFT, m_faces[FACE_FRONT]);
|
||||
m_faces[FACE_RIGHT]->connect(BOTTOM, m_faces[FACE_BOTTOM]);
|
||||
}
|
||||
|
||||
const glm::mat4 Planet::getTransformation()
|
||||
{
|
||||
glm::mat4 newMat = glm::mat4(1.0f);
|
||||
newMat = glm::translate(newMat, m_position);
|
||||
return newMat;
|
||||
}
|
||||
|
||||
PlanetIndexBuffer* Planet::getIndexBuffer(const unsigned int detailTop, const unsigned int detailRight, const unsigned int detailBottom, const unsigned int detailLeft) {
|
||||
return m_ibuffers[detailTop][detailRight][detailBottom][detailLeft];
|
||||
}
|
||||
|
|
|
@ -33,12 +33,14 @@ class Planet
|
|||
void draw(Camera* camera, Shader* shader);
|
||||
|
||||
inline SimplexNoise& getNoise() { return m_noise; }
|
||||
inline PlanetIndexBuffer* getBuffers() { return m_ibuffers; }
|
||||
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:
|
||||
PlanetIndexBuffer* m_ibuffers;
|
||||
void connectFaces();
|
||||
|
||||
PlanetIndexBuffer* m_ibuffers[5][5][5][5];
|
||||
SimplexNoise m_noise;
|
||||
glm::vec3 m_position;
|
||||
float m_radius;
|
||||
|
|
|
@ -2,57 +2,252 @@
|
|||
#include <time.h>
|
||||
#include <math.h>
|
||||
|
||||
// Correlates directly to Face enum
|
||||
const glm::vec3 FACE_NORMALS[6] = {
|
||||
// FACE_BOTTOM
|
||||
glm::vec3(0.0f, -1.0f, 0.0f),
|
||||
// FACE_TOP
|
||||
glm::vec3(0.0f, 1.0f, 0.0f),
|
||||
// FACE_LEFT
|
||||
glm::vec3(-1.0f, 0.0f, 0.0f),
|
||||
// FACE_RIGHT
|
||||
glm::vec3(1.0f, 0.0f, 0.0f),
|
||||
// FACE_FRONT
|
||||
glm::vec3(0.0f, 0.0f, -1.0f),
|
||||
// FACE_BACK
|
||||
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)
|
||||
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();
|
||||
}
|
||||
|
||||
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];
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
// 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) {
|
||||
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 > splitDistance * 2.0 && !m_leaf) {
|
||||
} else if (camToOrigin > m_radius * 2.0 && !m_leaf) {
|
||||
this->merge();
|
||||
return;
|
||||
}
|
||||
|
@ -63,15 +258,10 @@ void PlanetFaceNode::tick(Camera* camera, GLfloat dtime)
|
|||
{
|
||||
m_children[i]->tick(camera, dtime);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
|
@ -87,13 +277,20 @@ void PlanetFaceNode::draw(Camera* camera, Shader* shader)
|
|||
if (!m_generated)
|
||||
return;
|
||||
|
||||
shader->setBuffers(m_vao, m_vbo, m_ebo);
|
||||
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_planet->getTransformation());
|
||||
shader->setUniform("modelMatrix", m_planetFace->getPlanet()->getTransformation());
|
||||
|
||||
glDrawElements(GL_TRIANGLES, m_indices, GL_UNSIGNED_INT, nullptr);
|
||||
glDrawElements(GL_TRIANGLES, buffers->getVertexCount(), GL_UNSIGNED_SHORT, nullptr);
|
||||
}
|
||||
|
||||
void PlanetFaceNode::generate()
|
||||
|
@ -101,27 +298,22 @@ void PlanetFaceNode::generate()
|
|||
if (m_generated)
|
||||
return;
|
||||
|
||||
std::vector<Vertex> vertices;
|
||||
int vertexCount = RESOLUTION * RESOLUTION;
|
||||
Vertex vertices[vertexCount];
|
||||
|
||||
float divisionLevel = (float)pow(2.0, m_level);
|
||||
float radius = m_planet->getRadius();
|
||||
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;
|
||||
|
@ -134,11 +326,17 @@ void PlanetFaceNode::generate()
|
|||
);
|
||||
|
||||
// Get noise height and multiply by radius
|
||||
float height = m_planet->getNoise().fractal(8, point.x, point.y, point.z) * 20.0f;
|
||||
glm::vec3 pos = -(height + radius) * point;
|
||||
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.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))
|
||||
|
@ -152,37 +350,68 @@ 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;
|
||||
|
||||
// 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;
|
||||
setIndexBuffer(PlanetBufferIndex::Base);
|
||||
}
|
||||
|
||||
bool PlanetFaceNode::subdivide()
|
||||
{
|
||||
if (m_level == 8)
|
||||
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]);
|
||||
|
||||
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_planet, m_planetFace, m_pos + stepForward - stepLeft, TOP_LEFT, lv);
|
||||
m_children[TOP_RIGHT] = new PlanetFaceNode(m_planet, m_planetFace, m_pos - stepForward - stepLeft, TOP_RIGHT, lv);
|
||||
m_children[BOTTOM_RIGHT] = new PlanetFaceNode(m_planet, m_planetFace, m_pos - stepForward + stepLeft, BOTTOM_RIGHT, lv);
|
||||
m_children[BOTTOM_LEFT] = new PlanetFaceNode(m_planet, m_planetFace, m_pos + stepForward + stepLeft, BOTTOM_LEFT, lv);
|
||||
// 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;
|
||||
|
||||
for (int i = 0; i < 4; i++)
|
||||
m_children[i]->m_parent = this;
|
||||
|
||||
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()
|
||||
{
|
||||
|
@ -195,6 +424,7 @@ bool PlanetFaceNode::merge()
|
|||
{
|
||||
m_children[i]->dispose();
|
||||
delete m_children[i];
|
||||
m_children[i] = 0;
|
||||
}
|
||||
|
||||
// We're a leaf now
|
||||
|
@ -213,128 +443,28 @@ 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;
|
||||
}
|
||||
glm::vec3 PlanetFaceNode::getAbsolutePosition()
|
||||
{
|
||||
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(planet, this, m_normal, face, 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()
|
||||
|
@ -349,5 +479,24 @@ void PlanetFace::draw(Camera* camera, Shader* 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;
|
||||
}
|
||||
|
|
|
@ -6,21 +6,31 @@
|
|||
#include "Shader.h"
|
||||
#include "Camera.h"
|
||||
|
||||
#define RESOLUTION 15
|
||||
#define RESOLUTION 17
|
||||
|
||||
enum Face
|
||||
{
|
||||
FACE_BOTTOM, FACE_TOP,
|
||||
FACE_LEFT, FACE_RIGHT,
|
||||
FACE_FRONT, FACE_BACK
|
||||
FACE_FRONT,
|
||||
FACE_LEFT,
|
||||
FACE_BACK,
|
||||
FACE_RIGHT,
|
||||
FACE_TOP,
|
||||
FACE_BOTTOM,
|
||||
};
|
||||
|
||||
enum LODFace
|
||||
enum Quadrant
|
||||
{
|
||||
TOP_LEFT, TOP_RIGHT,
|
||||
BOTTOM_RIGHT, BOTTOM_LEFT
|
||||
};
|
||||
|
||||
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
|
||||
{
|
||||
glm::vec3 position;
|
||||
|
@ -34,7 +44,7 @@ class PlanetFace;
|
|||
class PlanetFaceNode
|
||||
{
|
||||
public:
|
||||
PlanetFaceNode(Planet* planet, PlanetFace* face, glm::vec3 position, const unsigned int index, const unsigned int level);
|
||||
PlanetFaceNode(PlanetFace* face, PlanetFaceNode* parent, const unsigned int index);
|
||||
~PlanetFaceNode();
|
||||
|
||||
void tick(Camera* camera, GLfloat dtime);
|
||||
|
@ -42,31 +52,37 @@ class PlanetFaceNode
|
|||
|
||||
bool isLeaf();
|
||||
|
||||
inline Planet* getPlanet() const { return m_planet; }
|
||||
inline PlanetFace* getPlanetFace() const { return m_planetFace; }
|
||||
|
||||
inline glm::vec3 getAbsolutePosition() const { return m_planet->getPosition() + m_pos; }
|
||||
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]; }
|
||||
|
||||
void setNeighbor(const unsigned int side, PlanetFaceNode *neighbor);
|
||||
unsigned int mirrorSide(const unsigned int side) const;
|
||||
const PlanetFaceNode* getEqualOrHigherNeighbor(const unsigned int side) const;
|
||||
unsigned int getNeighborDetailDifference(const unsigned int side) const;
|
||||
private:
|
||||
void generate();
|
||||
bool subdivide();
|
||||
bool merge();
|
||||
void dispose();
|
||||
|
||||
void setIndexBuffer(PlanetBufferIndex buf);
|
||||
void getNeighbors();
|
||||
void findNeighbor(const unsigned int side);
|
||||
void getNeighbor(const unsigned int side);
|
||||
void updateNeighborDetailDifferences(const unsigned int side);
|
||||
unsigned int mirrorQuadrant(const unsigned int side, const unsigned int quadrant) const;
|
||||
|
||||
Planet* m_planet;
|
||||
PlanetFace* m_planetFace;
|
||||
|
||||
PlanetFaceNode* m_parent;
|
||||
PlanetFaceNode* m_children[4];
|
||||
PlanetFaceNode* m_neighbors[4];
|
||||
unsigned int neighborDetailDifferences[4];
|
||||
|
||||
PlanetFaceNode* m_neighborTop;
|
||||
PlanetFaceNode* m_neighborRight;
|
||||
PlanetFaceNode* m_neighborLeft;
|
||||
PlanetFaceNode* m_neighborBottom;
|
||||
|
||||
unsigned int m_index;
|
||||
unsigned int m_quadrant;
|
||||
unsigned int m_level;
|
||||
|
||||
bool m_dirty;
|
||||
|
@ -76,10 +92,8 @@ 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;
|
||||
};
|
||||
|
||||
class PlanetFace
|
||||
|
@ -94,12 +108,20 @@ 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; }
|
||||
|
||||
void connect(const unsigned int side, PlanetFace* face);
|
||||
bool hasSplitsLeft();
|
||||
private:
|
||||
Planet* m_planet;
|
||||
unsigned int m_face;
|
||||
unsigned int m_splits;
|
||||
|
||||
glm::vec3 m_normal;
|
||||
glm::mat4 m_orientation;
|
||||
|
||||
PlanetFaceNode* m_lod;
|
||||
};
|
||||
|
|
|
@ -1,145 +1,179 @@
|
|||
#include "PlanetIndexBuffer.h"
|
||||
|
||||
PlanetIndexBuffer::PlanetIndexBuffer(unsigned int resolution) : m_resolution(resolution) {
|
||||
createBuffer(PlanetBufferIndex::Base);
|
||||
createBuffer(PlanetBufferIndex::FixR, false, true, false, false);
|
||||
createBuffer(PlanetBufferIndex::FixL, false, false, true, false);
|
||||
createBuffer(PlanetBufferIndex::FixB, false, false, false, true);
|
||||
createBuffer(PlanetBufferIndex::FixT, true, false, false, false);
|
||||
createBuffer(PlanetBufferIndex::FixTR, true, true, false, false);
|
||||
createBuffer(PlanetBufferIndex::FixTL, true, false, true, false);
|
||||
createBuffer(PlanetBufferIndex::FixBR, false, true, false, true);
|
||||
createBuffer(PlanetBufferIndex::FixBL, false, false, true, true);
|
||||
PlanetIndexBuffer::PlanetIndexBuffer(
|
||||
unsigned int resolution,
|
||||
const unsigned int detailTop,
|
||||
const unsigned int detailRight,
|
||||
const unsigned int detailBottom,
|
||||
const unsigned int detailLeft
|
||||
) : m_resolution(resolution) {
|
||||
TriangleFanList triangleFans;
|
||||
|
||||
// Build the patch body (without edges)
|
||||
for (unsigned int x = 2; x < m_resolution - 1; x += 2)
|
||||
{
|
||||
for (unsigned int y = 2; y < m_resolution - 1; y += 2)
|
||||
{
|
||||
addTriangleFan(triangleFans);
|
||||
addTriangleFanVertex(triangleFans, x, y);
|
||||
addTriangleFanVertex(triangleFans, x + 1, y + 1);
|
||||
addTriangleFanVertex(triangleFans, x, y + 1);
|
||||
addTriangleFanVertex(triangleFans, x - 1, y + 1);
|
||||
addTriangleFanVertex(triangleFans, x - 1, y);
|
||||
addTriangleFanVertex(triangleFans, x - 1, y - 1);
|
||||
addTriangleFanVertex(triangleFans, x, y - 1);
|
||||
addTriangleFanVertex(triangleFans, x + 1, y - 1);
|
||||
addTriangleFanVertex(triangleFans, x + 1, y);
|
||||
addTriangleFanVertex(triangleFans, x + 1, y + 1);
|
||||
}
|
||||
}
|
||||
|
||||
// Build all four edges
|
||||
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;
|
||||
for (unsigned int i = 0; i < triangleFans.size(); i++)
|
||||
if (triangleFans[i].size() > 2)
|
||||
triangleCount += triangleFans[i].size() - 2;
|
||||
m_vertexCount = 3 * triangleCount;
|
||||
|
||||
// Create an index array
|
||||
GLushort *indexData = new GLushort[m_vertexCount];
|
||||
int n = 0;
|
||||
for (unsigned int i = 0; i < triangleFans.size(); i++)
|
||||
{
|
||||
for (unsigned int j = 2; j < triangleFans[i].size(); j++)
|
||||
{
|
||||
indexData[n++] = triangleFans[i][0];
|
||||
indexData[n++] = triangleFans[i][j-1];
|
||||
indexData[n++] = triangleFans[i][j];
|
||||
}
|
||||
}
|
||||
|
||||
// Generate the index buffer object (IBO)
|
||||
glGenBuffers(1, &m_ibo);
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_ibo);
|
||||
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(GLushort) * m_vertexCount, indexData, GL_STATIC_DRAW);
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
|
||||
|
||||
// Delete unnecessary data
|
||||
triangleFans.clear();
|
||||
delete[] indexData;
|
||||
}
|
||||
|
||||
PlanetIndexBuffer::~PlanetIndexBuffer() {
|
||||
for (int i = 0; i < 9; i++) {
|
||||
if (m_buffers[i] == nullptr) continue;
|
||||
glDeleteBuffers(1, &m_buffers[i]->ebo);
|
||||
m_buffers[i]->indices.clear();
|
||||
delete m_buffers[i];
|
||||
}
|
||||
glDeleteBuffers(1, &m_ibo);
|
||||
}
|
||||
|
||||
PIB* PlanetIndexBuffer::getBuffer(PlanetBufferIndex idx) {
|
||||
return m_buffers[idx];
|
||||
void PlanetIndexBuffer::rotateIndices(unsigned int &x, unsigned int &y, const unsigned int rotation)
|
||||
{
|
||||
// Change the indices to match the given rotation
|
||||
switch (rotation)
|
||||
{
|
||||
case TOP:
|
||||
default:
|
||||
return;
|
||||
case RIGHT:
|
||||
std::swap(x, y);
|
||||
y = m_resolution - y;
|
||||
return;
|
||||
case BOTTOM:
|
||||
x = m_resolution - x;
|
||||
y = m_resolution - y;
|
||||
return;
|
||||
case LEFT:
|
||||
std::swap(x, y);
|
||||
x = m_resolution - x;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void PlanetIndexBuffer::createBuffer(PlanetBufferIndex idx, bool fanTop, bool fanRight, bool fanLeft, bool fanBottom) {
|
||||
PIB* buffer = new PIB();
|
||||
// Create indices for mesh
|
||||
for (unsigned int gz = 0; gz < m_resolution - 1; gz++)
|
||||
{
|
||||
bool slantLeft = gz % 2 == 0;
|
||||
for (unsigned int gx = 0; gx < m_resolution - 1; gx++)
|
||||
{
|
||||
unsigned int topLeft = (gz * m_resolution) + gx;
|
||||
unsigned int topRight = topLeft + 1;
|
||||
unsigned int bottomLeft = ((gz + 1) * m_resolution) + gx;
|
||||
unsigned int bottomRight = bottomLeft + 1;
|
||||
|
||||
bool uTri1 = true;
|
||||
bool uTri2 = true;
|
||||
unsigned int tri1[3];
|
||||
unsigned int tri2[3];
|
||||
|
||||
if (slantLeft)
|
||||
{
|
||||
tri1[0] = topLeft;
|
||||
tri1[1] = bottomLeft;
|
||||
tri1[2] = bottomRight;
|
||||
tri2[0] = topLeft;
|
||||
tri2[1] = bottomRight;
|
||||
tri2[2] = topRight;
|
||||
}
|
||||
else
|
||||
{
|
||||
tri1[0] = topLeft;
|
||||
tri1[1] = bottomLeft;
|
||||
tri1[2] = topRight;
|
||||
tri2[0] = bottomLeft;
|
||||
tri2[1] = bottomRight;
|
||||
tri2[2] = topRight;
|
||||
}
|
||||
|
||||
if (fanTop && gz == 0)
|
||||
{
|
||||
if (gx % 2 == 0)
|
||||
{
|
||||
tri2[0] = topLeft;
|
||||
tri2[1] = bottomRight;
|
||||
tri2[2] = topRight + 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
uTri1 = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (fanRight && gx == m_resolution - 2)
|
||||
{
|
||||
if (gz % 2 == 0)
|
||||
{
|
||||
tri2[0] = topRight;
|
||||
tri2[1] = bottomLeft;
|
||||
tri2[2] = bottomRight + m_resolution;
|
||||
}
|
||||
else
|
||||
{
|
||||
uTri2 = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (fanBottom && gz == m_resolution - 2)
|
||||
{
|
||||
if (gx % 2 == 0)
|
||||
{
|
||||
tri2[0] = bottomLeft;
|
||||
tri2[1] = bottomRight + 1;
|
||||
tri2[2] = topRight;
|
||||
}
|
||||
else
|
||||
{
|
||||
uTri1 = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (fanLeft && gx == 0)
|
||||
{
|
||||
if (gz % 2 == 0)
|
||||
{
|
||||
tri1[0] = topLeft;
|
||||
tri1[1] = bottomLeft + m_resolution;
|
||||
tri1[2] = bottomRight;
|
||||
}
|
||||
else
|
||||
{
|
||||
uTri1 = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (uTri1)
|
||||
{
|
||||
buffer->indices.push_back(tri1[0]);
|
||||
buffer->indices.push_back(tri1[1]);
|
||||
buffer->indices.push_back(tri1[2]);
|
||||
}
|
||||
|
||||
if (uTri2)
|
||||
{
|
||||
buffer->indices.push_back(tri2[0]);
|
||||
buffer->indices.push_back(tri2[1]);
|
||||
buffer->indices.push_back(tri2[2]);
|
||||
}
|
||||
|
||||
slantLeft = !slantLeft;
|
||||
}
|
||||
}
|
||||
|
||||
glGenBuffers(1, &(buffer->ebo));
|
||||
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer->ebo);
|
||||
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(unsigned int) * buffer->indices.size(), &(buffer->indices[0]), GL_STATIC_DRAW);
|
||||
|
||||
m_buffers[idx] = buffer;
|
||||
// Creates and adds a triangle fan
|
||||
void PlanetIndexBuffer::addTriangleFan(TriangleFanList &triangleFans)
|
||||
{
|
||||
triangleFans.push_back(TriangleFan());
|
||||
}
|
||||
|
||||
// adds a vertex index to the most recently added triangle fan
|
||||
void PlanetIndexBuffer::addTriangleFanVertex(TriangleFanList &triangleFans, unsigned int x, unsigned int y, const unsigned int rotation)
|
||||
{
|
||||
// Rotate and use the x and y indices
|
||||
PlanetIndexBuffer::rotateIndices(x, y, rotation);
|
||||
triangleFans.back().push_back(index1D(x,y));
|
||||
}
|
||||
|
||||
void PlanetIndexBuffer::buildEdge(TriangleFanList &triangleFans, const unsigned int side, const unsigned int detail)
|
||||
{
|
||||
// If detail difference is 0 (default case) or too high
|
||||
if (detail == 0 || detail > 4)
|
||||
{
|
||||
for (unsigned int x = 0; x < m_resolution - 1; x += 2)
|
||||
{
|
||||
addTriangleFan(triangleFans);
|
||||
addTriangleFanVertex(triangleFans, x + 1, m_resolution - 1, side);
|
||||
for (int d = -1; d <= 1; d++)
|
||||
addTriangleFanVertex(triangleFans, x + 1 - d, m_resolution, side);
|
||||
|
||||
if (x > 0)
|
||||
{
|
||||
addTriangleFan(triangleFans);
|
||||
addTriangleFanVertex(triangleFans, x, m_resolution, side);
|
||||
for (int d = -1; d <= 1; d++)
|
||||
addTriangleFanVertex(triangleFans, x + d, m_resolution - 1, side);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Calculate step sizes; step = 2^detail
|
||||
const int step = 1 << detail;
|
||||
const int halfStep = step / 2;
|
||||
|
||||
unsigned int x = 0;
|
||||
for (x = 0; x < m_resolution; x += step)
|
||||
{
|
||||
addTriangleFan(triangleFans);
|
||||
addTriangleFanVertex(triangleFans, x, m_resolution, side);
|
||||
addTriangleFanVertex(triangleFans, x + halfStep, m_resolution - 1, side);
|
||||
addTriangleFanVertex(triangleFans, x + step, m_resolution, side);
|
||||
|
||||
if (x > 0)
|
||||
{
|
||||
addTriangleFan(triangleFans);
|
||||
addTriangleFanVertex(triangleFans, x, m_resolution, side);
|
||||
for (int d = -halfStep; d <= halfStep; d++)
|
||||
addTriangleFanVertex(triangleFans, x + d, m_resolution - 1, side);
|
||||
}
|
||||
}
|
||||
|
||||
if (step > 2)
|
||||
{
|
||||
addTriangleFan(triangleFans);
|
||||
addTriangleFanVertex(triangleFans, 0, m_resolution, side);
|
||||
for (int d = 1; d <= halfStep; d++)
|
||||
addTriangleFanVertex(triangleFans, d, m_resolution - 1, side);
|
||||
|
||||
addTriangleFan(triangleFans);
|
||||
addTriangleFanVertex(triangleFans, m_resolution, m_resolution, side);
|
||||
for (int d = -halfStep; d <= -1; d++)
|
||||
addTriangleFanVertex(triangleFans, m_resolution + d, m_resolution - 1, side);
|
||||
}
|
||||
}
|
||||
|
||||
GLsizei PlanetIndexBuffer::getVertexCount() const
|
||||
{
|
||||
return m_vertexCount;
|
||||
}
|
||||
|
||||
GLuint PlanetIndexBuffer::getIbo() const
|
||||
{
|
||||
return m_ibo;
|
||||
}
|
||||
|
||||
unsigned int PlanetIndexBuffer::index1D(const unsigned int x, const unsigned int y) {
|
||||
return ((x) + (y) * (m_resolution + 1));
|
||||
}
|
||||
|
|
|
@ -3,24 +3,37 @@
|
|||
|
||||
#include "util/Common.h"
|
||||
|
||||
struct PIB {
|
||||
GLuint ebo;
|
||||
std::vector<uint> indices;
|
||||
enum Direction
|
||||
{
|
||||
TOP, RIGHT,
|
||||
BOTTOM, LEFT,
|
||||
};
|
||||
|
||||
enum PlanetBufferIndex {
|
||||
Base, FixR, FixL, FixB, FixT, FixTR, FixTL, FixBR, FixBL
|
||||
};
|
||||
|
||||
typedef std::vector<GLushort> TriangleFan;
|
||||
typedef std::vector<TriangleFan> TriangleFanList;
|
||||
|
||||
class PlanetIndexBuffer {
|
||||
public:
|
||||
PlanetIndexBuffer(unsigned int resolution);
|
||||
PlanetIndexBuffer(unsigned int resolution, const unsigned int detailTop, const unsigned int detailRight, const unsigned int detailBottom, const unsigned int detailLeft);
|
||||
~PlanetIndexBuffer();
|
||||
|
||||
PIB* getBuffer(PlanetBufferIndex idx);
|
||||
|
||||
// Instance methods
|
||||
GLsizei getVertexCount() const;
|
||||
GLuint getIbo() const;
|
||||
private:
|
||||
void createBuffer(PlanetBufferIndex idx, bool fanTop = false, bool fanRight = false, bool fanLeft = false, bool fanBottom = false);
|
||||
PIB* m_buffers[9];
|
||||
void rotateIndices(unsigned int &x, unsigned int &y, const unsigned int rotation);
|
||||
void addTriangleFan(TriangleFanList &triangleFans);
|
||||
void addTriangleFanVertex(TriangleFanList &triangleFans, unsigned int x, unsigned int y, const unsigned int rotation = 0);
|
||||
void buildEdge(TriangleFanList &triangleFans, const unsigned int side, const unsigned int detail = 0);
|
||||
|
||||
// Instance variables
|
||||
GLsizei m_vertexCount;
|
||||
GLuint m_ibo;
|
||||
|
||||
// TODO: move to utility
|
||||
unsigned int index1D(const unsigned int x, const unsigned int y);
|
||||
|
||||
unsigned int m_resolution;
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in New Issue