skein/src/orbit.cpp
ktyl 441a0748d1 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
2024-03-03 23:17:07 +00:00

142 lines
3.9 KiB
C++

#include "gfx.hpp"
#include "orbit.hpp"
#include "astro/stateVectorIndices.hpp"
#include "astro/orbitalElementConversions.hpp"
#include <glm/gtc/matrix_transform.hpp>
Orbit::Orbit(Vector6 keplerianElements, GLuint shaderProgram) :
_keplerianElements(keplerianElements),
_shaderProgram(shaderProgram)
{
glGenVertexArrays(1, &_vao);
glGenBuffers(1, &_vbo);
regenerateVertices();
}
void Orbit::regenerateVertices()
{
_vertices.clear();
for (int i = 0; i < _vertexCount; i++)
{
float t = (float)i / (float)_vertexCount * 2.0 * _pi;
glm::vec3 pos = getPosition(t);
_vertices.push_back(pos.x);
_vertices.push_back(pos.y);
_vertices.push_back(pos.z);
}
glBindVertexArray(_vao);
glBindBuffer(GL_ARRAY_BUFFER, _vbo);
size_t vboBufferSize = _vertices.size() * sizeof(float);
glBufferData(GL_ARRAY_BUFFER, vboBufferSize, &_vertices[0], GL_STATIC_DRAW);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
}
void Orbit::setElements(Vector6 keplerianElements)
{
_keplerianElements = keplerianElements;
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)
{
const float eccentricity = _keplerianElements[astro::eccentricityIndex];
float eccentricAnomaly = astro::convertEllipticalMeanAnomalyToEccentricAnomaly(
eccentricity,
meanAnomaly,
(float)10e-3,
100);
return eccentricAnomaly;
}
// Interpolate a position around the orbit.
// t is in range 0..1 and wraps.
glm::vec3 Orbit::getPosition(const float meanAnomaly)
{
Vector6 cartesian = getCartesianCoordinates(meanAnomaly);
return glm::vec3(
cartesian[astro::xPositionIndex],
cartesian[astro::yPositionIndex],
cartesian[astro::zPositionIndex]);
}
float Orbit::getTrueAnomaly(const float meanAnomaly)
{
const float eccentricAnomaly = getEccentricAnomaly(meanAnomaly);
const float eccentricity = _keplerianElements[astro::eccentricityIndex];
return astro::convertEccentricAnomalyToTrueAnomaly(
eccentricAnomaly,
eccentricity);
}
Vector6 Orbit::getCartesianCoordinates(const float meanAnomaly)
{
Vector6 kepler(_keplerianElements);
kepler[astro::trueAnomalyIndex] = getTrueAnomaly(meanAnomaly);
return astro::convertKeplerianToCartesianElements(kepler, 1.0);
}
glm::vec3 Orbit::getTangent(const float meanAnomaly)
{
float epsilon = 0.01;
glm::vec3 ahead = getPosition(meanAnomaly + epsilon);
glm::vec3 behind = getPosition(meanAnomaly - epsilon);
return glm::normalize(ahead - behind);
}
void Orbit::render(const float time)
{
glUseProgram(_shaderProgram);
updateModelViewProjectionMatrix(_shaderProgram, time);
glBindVertexArray(_vao);
glBindBuffer(GL_ARRAY_BUFFER, _vbo);
glDrawArrays(GL_LINE_LOOP, 0, _vertices.size() / 3);
}
Orbit::~Orbit()
{
glDeleteVertexArrays(1, &_vao);
glDeleteBuffers(1, &_vbo);
}