refactor: extract Orbiter class

split render logic across objects
move rendering code from main loop to Orbiter
make objects responsible for setting up their own render contexts
This commit is contained in:
ktyl 2024-02-26 20:00:59 +00:00
parent 51c755be64
commit 441a0748d1
10 changed files with 166 additions and 79 deletions

View File

@ -30,6 +30,7 @@ add_executable(${PROJECT_NAME}
src/icosphere.cpp src/icosphere.cpp
src/gfx.cpp src/gfx.cpp
src/orbit.cpp src/orbit.cpp
src/orbiter.cpp
src/widget.cpp src/widget.cpp
) )

View File

@ -146,8 +146,11 @@ void Icosphere::updateModelMatrix()
glUniformMatrix4fv(modelLocation, 1, GL_FALSE, &model[0][0]); glUniformMatrix4fv(modelLocation, 1, GL_FALSE, &model[0][0]);
} }
void Icosphere::render() void Icosphere::render(const float time)
{ {
glUseProgram(_shaderProgram);
updateModelViewProjectionMatrix(_shaderProgram, time);
updateModelMatrix(); updateModelMatrix();
glBindVertexArray(_vao); glBindVertexArray(_vao);

View File

@ -11,7 +11,7 @@ class Icosphere
{ {
public: public:
Icosphere(float radius, int subdivisions, GLuint shaderProgram); Icosphere(float radius, int subdivisions, GLuint shaderProgram);
void render(); void render(const float time);
void setPosition(glm::vec3 position); void setPosition(glm::vec3 position);

View File

@ -42,6 +42,7 @@
#include "gfx.hpp" #include "gfx.hpp"
#include "icosphere.hpp" #include "icosphere.hpp"
#include "orbit.hpp" #include "orbit.hpp"
#include "orbiter.hpp"
#include "widget.hpp" #include "widget.hpp"
int main() int main()
@ -54,7 +55,7 @@ int main()
GLuint unlitProgram = compileShaderProgram("./frag_unlit.glsl"); GLuint unlitProgram = compileShaderProgram("./frag_unlit.glsl");
Icosphere planet(0.2, 3, litProgram); Icosphere planet(0.2, 3, litProgram);
Icosphere orbiter(0.07, 2, litProgram);
std::vector<float> keplerianElements(6); std::vector<float> keplerianElements(6);
keplerianElements[astro::semiMajorAxisIndex] = .75; keplerianElements[astro::semiMajorAxisIndex] = .75;
@ -62,9 +63,10 @@ int main()
keplerianElements[astro::inclinationIndex] = 3.142 / 2.0 + 1; keplerianElements[astro::inclinationIndex] = 3.142 / 2.0 + 1;
keplerianElements[astro::argumentOfPeriapsisIndex] = 2.0; keplerianElements[astro::argumentOfPeriapsisIndex] = 2.0;
keplerianElements[astro::longitudeOfAscendingNodeIndex] = 0; keplerianElements[astro::longitudeOfAscendingNodeIndex] = 0;
Orbit orbit(keplerianElements); Orbit orbit(keplerianElements, unlitProgram);
Widget widget(orbit, unlitProgram); Icosphere orbiterSphere(0.07, 2, litProgram);
Orbiter orbiter(orbiterSphere, orbit, unlitProgram);
// Main loop // Main loop
while (!glfwWindowShouldClose(window)) while (!glfwWindowShouldClose(window))
@ -73,39 +75,8 @@ int main()
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
float time = glfwGetTime(); float time = glfwGetTime();
const float orbitalPeriod = 6.284;
const int ANIM_ORBITING = 0; orbiter.render(time);
const int ANIM_ECCENTRICITY = 1;
int animation = (int)(time / orbitalPeriod) % 2 == 1;
glm::vec3 pos;
if (animation == ANIM_ORBITING)
{
pos = orbit.getPosition(time);
}
else if (animation == ANIM_ECCENTRICITY)
{
float e = .25 + .2 * sin(time);
keplerianElements[astro::eccentricityIndex] = e;
orbit.setElements(keplerianElements);
pos = orbit.getPosition(0);
}
orbiter.setPosition(pos);
// Render lit objects
glUseProgram(litProgram);
updateModelViewProjectionMatrix(litProgram, time);
planet.render();
orbiter.render();
// Render unlit objects
glUseProgram(unlitProgram);
updateModelViewProjectionMatrix(unlitProgram, time);
orbit.render();
widget.render();
glfwSwapBuffers(window); glfwSwapBuffers(window);
glfwPollEvents(); glfwPollEvents();

View File

@ -1,10 +1,14 @@
#include "gfx.hpp"
#include "orbit.hpp" #include "orbit.hpp"
#include "astro/stateVectorIndices.hpp" #include "astro/stateVectorIndices.hpp"
#include "astro/orbitalElementConversions.hpp" #include "astro/orbitalElementConversions.hpp"
Orbit::Orbit(Vector6 keplerianElements) : #include <glm/gtc/matrix_transform.hpp>
_keplerianElements(keplerianElements)
Orbit::Orbit(Vector6 keplerianElements, GLuint shaderProgram) :
_keplerianElements(keplerianElements),
_shaderProgram(shaderProgram)
{ {
glGenVertexArrays(1, &_vao); glGenVertexArrays(1, &_vao);
glGenBuffers(1, &_vbo); glGenBuffers(1, &_vbo);
@ -45,6 +49,32 @@ void Orbit::setElements(Vector6 keplerianElements)
regenerateVertices(); regenerateVertices();
} }
void Orbit::getElements(Vector6& keplerianElements) const
{
keplerianElements = _keplerianElements;
}
glm::mat4 Orbit::getLookAlongMatrix(const float meanAnomaly)
{
glm::vec3 position = getPosition(meanAnomaly);
// get the tangent of the orbital ellipse
glm::vec3 tan = getTangent(meanAnomaly);
// we want to point along the orbit
glm::vec3 target = position + tan;
// TODO: this is not 'up' with respect to the orbited body!
// 'up' is just the normalized position vector because the orbit is centred at the origin
glm::vec3 up = glm::normalize(position);
// easy peasy lookAt matrix
glm::mat4 look = glm::lookAt(position, target, up);
// invert the lookat matrix because it's meant for cameras, cameras work backwards and
// we are not a camera
glm::mat4 lookAlong = glm::inverse(look);
return lookAlong;
}
float Orbit::getEccentricAnomaly(const float meanAnomaly) float Orbit::getEccentricAnomaly(const float meanAnomaly)
{ {
const float eccentricity = _keplerianElements[astro::eccentricityIndex]; const float eccentricity = _keplerianElements[astro::eccentricityIndex];
@ -92,8 +122,11 @@ glm::vec3 Orbit::getTangent(const float meanAnomaly)
return glm::normalize(ahead - behind); return glm::normalize(ahead - behind);
} }
void Orbit::render() void Orbit::render(const float time)
{ {
glUseProgram(_shaderProgram);
updateModelViewProjectionMatrix(_shaderProgram, time);
glBindVertexArray(_vao); glBindVertexArray(_vao);
glBindBuffer(GL_ARRAY_BUFFER, _vbo); glBindBuffer(GL_ARRAY_BUFFER, _vbo);

View File

@ -10,15 +10,17 @@ typedef std::vector<float> Vector6;
class Orbit class Orbit
{ {
public: public:
Orbit(Vector6 keplerianElements); Orbit(Vector6 keplerianElements, GLuint shaderProgram);
void render(); void render(const float time);
// TODO: meanAnomaly in all these arguments actually means eccentricMeanAnomaly, // TODO: meanAnomaly in all these arguments actually means eccentricMeanAnomaly,
// will have to change that when adding non-ellipctical orbits - don't get confused! // will have to change that when adding non-ellipctical orbits - don't get confused!
glm::vec3 getPosition(const float meanAnomaly); glm::vec3 getPosition(const float meanAnomaly);
glm::vec3 getTangent(const float meanAnomaly); glm::vec3 getTangent(const float meanAnomaly);
glm::mat4 getLookAlongMatrix(const float meanAnomaly);
void setElements(Vector6 keplerianElements); void setElements(Vector6 keplerianElements);
void getElements(Vector6& keplerianElements) const;
~Orbit(); ~Orbit();
private: private:
@ -27,6 +29,7 @@ private:
GLuint _vbo; GLuint _vbo;
GLuint _vao; GLuint _vao;
GLuint _shaderProgram;
std::vector<float> _vertices; std::vector<float> _vertices;
Vector6 _keplerianElements; Vector6 _keplerianElements;

67
src/orbiter.cpp Normal file
View File

@ -0,0 +1,67 @@
#include "orbiter.hpp"
#include <GLFW/glfw3.h>
#include <iostream>
#include <astro/stateVectorIndices.hpp>
Orbiter::Orbiter(Icosphere& sphere, Orbit& orbit, GLuint unlitShaderProgram) :
_sphere(sphere),
_orbit(orbit),
_widget(unlitShaderProgram)
{
}
float Orbiter::getMeanAnomaly()
{
float time = glfwGetTime();
return time;
}
glm::vec3 Orbiter::getPosition(const float time)
{
std::vector<float> keplerianElements(6);
_orbit.getElements(keplerianElements);
int animation = (int)(time/ORBITAL_PERIOD) % 2 == 1;
if (animation == ANIM_ORBITING)
return _orbit.getPosition(time);
// TODO: i want to modify the eccentricity of the orbit with a control,
// not an automatic animation
if (animation == ANIM_ECCENTRICITY)
{
// TODO: what are these magic numbers
float e = .25 + .2 * sin(time);
keplerianElements[astro::eccentricityIndex] = e;
// TODO: extract set from getter
_orbit.setElements(keplerianElements);
return _orbit.getPosition(0);
}
std::cerr << "unknown animation " << animation << std::endl;
throw 1;
}
void Orbiter::render(const float time)
{
const float meanAnomaly = time;
glm::vec3 pos = getPosition(meanAnomaly);
// render orbit
_orbit.render(time);
// render widget
glm::mat4 widgetMatrix = _orbit.getLookAlongMatrix(time);
_widget.setModelMatrix(widgetMatrix);
_widget.render(time);
// render sphere
_sphere.setPosition(pos);
_sphere.render(time);
}
Orbiter::~Orbiter()
{
}

29
src/orbiter.hpp Normal file
View File

@ -0,0 +1,29 @@
#pragma once
#include "icosphere.hpp"
#include "orbit.hpp"
#include "widget.hpp"
class Orbiter
{
public:
Orbiter(Icosphere& sphere, Orbit& orbit, GLuint unlitShaderProgram);
~Orbiter();
void render(const float time);
private:
void updateModelMatrix();
float getMeanAnomaly();
glm::vec3 getPosition(const float time);
void getOrbitalElements(const float time, Vector6& keplerianElements);
Icosphere& _sphere;
Orbit& _orbit;
Widget _widget;
const float ORBITAL_PERIOD = 6.284;
const int ANIM_ORBITING = 0;
const int ANIM_ECCENTRICITY = 1;
};

View File

@ -1,10 +1,7 @@
#include "widget.hpp" #include "widget.hpp"
#include "gfx.hpp" #include "gfx.hpp"
#include <glm/gtc/matrix_transform.hpp>
#include <GLFW/glfw3.h>
Widget::Widget(Orbit& orbit, GLuint shaderProgram) : Widget::Widget(GLuint shaderProgram) :
_orbit(orbit),
_shaderProgram(shaderProgram) _shaderProgram(shaderProgram)
{ {
const float lineLength = 0.1; const float lineLength = 0.1;
@ -30,36 +27,22 @@ Widget::Widget(Orbit& orbit, GLuint shaderProgram) :
glBindVertexArray(0); glBindVertexArray(0);
} }
void Widget::render() void Widget::render(const float time)
{ {
updateModelMatrix(); glUseProgram(_shaderProgram);
updateModelViewProjectionMatrix(_shaderProgram, time);
GLint modelLocation = getShaderUniformLocation(_shaderProgram, "_Model");
glUniformMatrix4fv(modelLocation, 1, GL_FALSE, &_modelMatrix[0][0]);
glBindVertexArray(_vao); glBindVertexArray(_vao);
glBindBuffer(GL_ARRAY_BUFFER, _vbo); glBindBuffer(GL_ARRAY_BUFFER, _vbo);
glDrawArrays(GL_LINES, 0, 6); glDrawArrays(GL_LINES, 0, 6);
} }
void Widget::updateModelMatrix() void Widget::setModelMatrix(const glm::mat4 matrix)
{ {
float p = glfwGetTime(); _modelMatrix = matrix;
_position = _orbit.getPosition(p);
// get the tangent of the orbital ellipse
glm::vec3 tan = _orbit.getTangent(p);
// we want to point along the orbit
glm::vec3 target = _position + tan;
// 'up' is just the normalized position vector because the orbit is centred at the origin
glm::vec3 up = glm::normalize(_position);
// easy peasy lookAt matrix
glm::mat4 look = glm::lookAt(_position, target, up);
// invert the lookat matrix because it's meant for cameras, cameras work backwards and
// we are not a camera
glm::mat4 model = glm::inverse(look);
GLint modelLocation = getShaderUniformLocation(_shaderProgram, "_Model");
glUniformMatrix4fv(modelLocation, 1, GL_FALSE, &model[0][0]);
} }
Widget::~Widget() Widget::~Widget()

View File

@ -1,28 +1,25 @@
#pragma once #pragma once
#include "orbit.hpp" #include <GL/glew.h>
#include <glm/glm.hpp> #include "glm/glm.hpp"
class Widget class Widget
{ {
public: public:
// A widget is renderer at a point on the orbit. // A widget is 3D cross.
// It consists of 3 orthagonally intersecting lines. // It consists of 3 orthagonally intersecting lines.
Widget(Orbit& orbit, GLuint shaderProgram); Widget(GLuint shaderProgram);
void render(); void render(const float time);
void setModelMatrix(const glm::mat4 matrix);
void setPosition(glm::vec3 position);
~Widget(); ~Widget();
private: private:
Orbit& _orbit;
glm::vec3 _position;
GLuint _vao; GLuint _vao;
GLuint _vbo; GLuint _vbo;
GLuint _shaderProgram; GLuint _shaderProgram;
glm::mat4 _modelMatrix;
void updateModelMatrix();
float _lineLength = 0.2; float _lineLength = 0.2;
float _vertices[3*6] = float _vertices[3*6] =