544 lines
12 KiB
C++
544 lines
12 KiB
C++
// axisarrow.cpp
|
|
//
|
|
// Copyright (C) 2007-2009, Celestia Development Team
|
|
// Original version by Chris Laurel <claurel@gmail.com>
|
|
//
|
|
// This program is free software; you can redistribute it and/or
|
|
// modify it under the terms of the GNU General Public License
|
|
// as published by the Free Software Foundation; either version 2
|
|
// of the License, or (at your option) any later version.
|
|
|
|
#include <algorithm>
|
|
#include <vector>
|
|
#include <celmath/mathlib.h>
|
|
#include <GL/glew.h>
|
|
#include "vecgl.h"
|
|
#include "axisarrow.h"
|
|
#include "selection.h"
|
|
#include "frame.h"
|
|
#include "body.h"
|
|
#include "timelinephase.h"
|
|
#include "shadermanager.h"
|
|
#include "vertexobject.h"
|
|
#include "render.h"
|
|
|
|
using namespace Eigen;
|
|
using namespace std;
|
|
using namespace celmath;
|
|
using namespace celgl;
|
|
|
|
// draw a simple circle or annulus
|
|
#define DRAW_ANNULUS 0
|
|
|
|
constexpr const float shaftLength = 0.85f;
|
|
constexpr const float headLength = 0.10f;
|
|
constexpr const float shaftRadius = 0.010f;
|
|
constexpr const float headRadius = 0.025f;
|
|
constexpr const unsigned nSections = 30;
|
|
|
|
|
|
static void initArrowAndLetters(void (*fn)(VertexObject&, size_t))
|
|
{
|
|
static_assert(sizeof(Vector3f) == 3*sizeof(GLfloat), "sizeof(Vector3f) != 3*sizeof(GLfloat)");
|
|
|
|
static VertexObject vo(GL_ARRAY_BUFFER, 0, GL_STATIC_DRAW);
|
|
static size_t count = 0; // number of vertices in the arrow
|
|
|
|
vo.bind();
|
|
if (vo.initialized())
|
|
{
|
|
fn(vo, count);
|
|
vo.unbind();
|
|
return;
|
|
}
|
|
|
|
// circle at bottom of a shaft
|
|
vector<Vector3f> circle;
|
|
// arrow shaft
|
|
vector<Vector3f> shaft;
|
|
// annulus
|
|
vector<Vector3f> annulus;
|
|
// head of the arrow
|
|
vector<Vector3f> head;
|
|
|
|
for (int i = 0; i <= nSections; i++)
|
|
{
|
|
float c, s;
|
|
sincos((i * 2.0f * (float)PI) / nSections, c, s);
|
|
|
|
// circle at bottom
|
|
Vector3f v0(shaftRadius * c, shaftRadius * s, 0.0f);
|
|
if (i > 0)
|
|
circle.push_back(v0);
|
|
circle.push_back(Vector3f::Zero());
|
|
circle.push_back(v0);
|
|
|
|
// shaft
|
|
Vector3f v1(shaftRadius * c, shaftRadius * s, shaftLength);
|
|
Vector3f v1prev;
|
|
if (i > 0)
|
|
{
|
|
shaft.push_back(v0); // left triangle
|
|
|
|
shaft.push_back(v0); // right
|
|
shaft.push_back(v1prev);
|
|
shaft.push_back(v1);
|
|
}
|
|
shaft.push_back(v0); // left
|
|
shaft.push_back(v1);
|
|
v1prev = v1;
|
|
|
|
// annulus
|
|
Vector3f v2(headRadius * c, headRadius * s, shaftLength);
|
|
#if DRAW_ANNULUS
|
|
Vector3f v2prev;
|
|
if (i > 0)
|
|
{
|
|
annulus.push_back(v2);
|
|
|
|
annulus.push_back(v2);
|
|
annulus.push_back(v2prev);
|
|
annulus.push_back(v1);
|
|
}
|
|
annulus.push_back(v2);
|
|
annulus.push_back(v1);
|
|
v2prev = v1;
|
|
#else
|
|
Vector3f v3(0.0f, 0.0f, shaftLength);
|
|
if (i > 0)
|
|
annulus.push_back(v2);
|
|
annulus.push_back(v2);
|
|
annulus.push_back(v3);
|
|
#endif
|
|
|
|
// head
|
|
Vector3f v4(0.0f, 0.0f, shaftLength + headLength);
|
|
if (i > 0)
|
|
head.push_back(v2);
|
|
head.push_back(v4);
|
|
head.push_back(v2);
|
|
}
|
|
|
|
circle.push_back(circle[1]);
|
|
shaft.push_back(shaft[0]);
|
|
#if DRAW_ANNULUS
|
|
annulus.push_back(annulus[0]);
|
|
#else
|
|
annulus.push_back(annulus[1]);
|
|
#endif
|
|
head.push_back(head[1]);
|
|
|
|
|
|
GLfloat lettersVtx[] =
|
|
{
|
|
// X
|
|
0, 0, 0,
|
|
1, 0, 1,
|
|
1, 0, 0,
|
|
0, 0, 1,
|
|
// Y
|
|
0, 0, 1,
|
|
0.5f, 0, 0.5f,
|
|
1, 0, 1,
|
|
0.5f, 0, 0.5f,
|
|
0.5f, 0, 0,
|
|
0.5f, 0, 0.5f,
|
|
// Z
|
|
0, 0, 1,
|
|
1, 0, 1,
|
|
0, 0, 0,
|
|
1, 0, 0
|
|
};
|
|
|
|
GLintptr offset = 0;
|
|
count = circle.size() + shaft.size() + annulus.size() + head.size();
|
|
GLsizeiptr size = sizeof(lettersVtx) + count * sizeof(GLfloat) * 3;
|
|
GLsizeiptr s = 0;
|
|
|
|
vo.allocate(size);
|
|
|
|
s = circle.size() * sizeof(Vector3f);
|
|
vo.setBufferData(circle.data(), offset, s);
|
|
offset += s;
|
|
|
|
s = shaft.size() * sizeof(Vector3f);
|
|
vo.setBufferData(shaft.data(), offset, s);
|
|
offset += s;
|
|
|
|
s = annulus.size() * sizeof(Vector3f);
|
|
vo.setBufferData(annulus.data(), offset, s);
|
|
offset += s;
|
|
|
|
s = head.size() * sizeof(Vector3f);
|
|
vo.setBufferData(head.data(), offset, s);
|
|
offset += s;
|
|
|
|
vo.setBufferData(lettersVtx, offset, sizeof(lettersVtx));
|
|
|
|
vo.setVertices(3, GL_FLOAT, GL_FALSE, 0, 0);
|
|
|
|
fn(vo, count);
|
|
vo.unbind();
|
|
}
|
|
|
|
static void RenderArrow()
|
|
{
|
|
initArrowAndLetters([](VertexObject& vo, size_t count) { vo.draw(GL_TRIANGLES, count); });
|
|
}
|
|
|
|
// Draw letter x in xz plane
|
|
static void RenderX()
|
|
{
|
|
initArrowAndLetters([](VertexObject& vo, size_t offset) { vo.draw(GL_LINES, 4, offset); });
|
|
}
|
|
|
|
|
|
// Draw letter y in xz plane
|
|
static void RenderY()
|
|
{
|
|
initArrowAndLetters([](VertexObject& vo, size_t offset) { vo.draw(GL_LINES, 6, offset+4); });
|
|
}
|
|
|
|
|
|
// Draw letter z in xz plane
|
|
static void RenderZ()
|
|
{
|
|
initArrowAndLetters([](VertexObject& vo, size_t offset) { vo.draw(GL_LINE_STRIP, 4, offset+10); });
|
|
}
|
|
|
|
|
|
/****** ArrowReferenceMark base class ******/
|
|
|
|
ArrowReferenceMark::ArrowReferenceMark(const Body& _body) :
|
|
body(_body),
|
|
size(1.0),
|
|
color(1.0f, 1.0f, 1.0f),
|
|
#ifdef USE_HDR
|
|
opacity(0.0f)
|
|
#else
|
|
opacity(1.0f)
|
|
#endif
|
|
{
|
|
}
|
|
|
|
|
|
void
|
|
ArrowReferenceMark::setSize(float _size)
|
|
{
|
|
size = _size;
|
|
}
|
|
|
|
|
|
void
|
|
ArrowReferenceMark::setColor(Color _color)
|
|
{
|
|
color = _color;
|
|
}
|
|
|
|
|
|
void
|
|
ArrowReferenceMark::render(Renderer* renderer,
|
|
const Vector3f& /* position */,
|
|
float /* discSize */,
|
|
double tdb) const
|
|
{
|
|
Vector3d v = getDirection(tdb);
|
|
if (v.norm() < 1.0e-12)
|
|
{
|
|
// Skip rendering of zero-length vectors
|
|
return;
|
|
}
|
|
|
|
v.normalize();
|
|
Quaterniond q;
|
|
q.setFromTwoVectors(Vector3d::UnitZ(), v);
|
|
|
|
if (opacity == 1.0f)
|
|
{
|
|
// Enable depth buffering
|
|
glEnable(GL_DEPTH_TEST);
|
|
glDepthMask(GL_TRUE);
|
|
glDisable(GL_BLEND);
|
|
}
|
|
else
|
|
{
|
|
glEnable(GL_DEPTH_TEST);
|
|
glDepthMask(GL_FALSE);
|
|
glEnable(GL_BLEND);
|
|
#ifdef USE_HDR
|
|
glBlendFunc(GL_ONE_MINUS_SRC_ALPHA, GL_SRC_ALPHA);
|
|
#else
|
|
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
|
#endif
|
|
}
|
|
|
|
glPushMatrix();
|
|
glRotate(q.cast<float>());
|
|
glScalef(size, size, size);
|
|
|
|
CelestiaGLProgram* prog = renderer->getShaderManager().getShader(shadprop);
|
|
if (prog == nullptr)
|
|
return;
|
|
prog->use();
|
|
prog->color = Color(color, opacity).toVector4();
|
|
|
|
RenderArrow();
|
|
glPopMatrix();
|
|
|
|
glUseProgram(0);
|
|
glDisable(GL_DEPTH_TEST);
|
|
glDepthMask(GL_FALSE);
|
|
glEnable(GL_BLEND);
|
|
glBlendFunc(GL_SRC_ALPHA, GL_ONE);
|
|
}
|
|
|
|
|
|
/****** AxesReferenceMark base class ******/
|
|
|
|
AxesReferenceMark::AxesReferenceMark(const Body& _body) :
|
|
body(_body),
|
|
size(),
|
|
#ifdef USE_HDR
|
|
opacity(0.0f)
|
|
#else
|
|
opacity(1.0f)
|
|
#endif
|
|
{
|
|
}
|
|
|
|
|
|
void
|
|
AxesReferenceMark::setSize(float _size)
|
|
{
|
|
size = _size;
|
|
}
|
|
|
|
|
|
void
|
|
AxesReferenceMark::setOpacity(float _opacity)
|
|
{
|
|
opacity = _opacity;
|
|
#ifdef USE_HDR
|
|
opacity = 1.0f - opacity;
|
|
#endif
|
|
}
|
|
|
|
|
|
void
|
|
AxesReferenceMark::render(Renderer* renderer,
|
|
const Vector3f& /* position */,
|
|
float /* discSize */,
|
|
double tdb) const
|
|
{
|
|
Quaterniond q = getOrientation(tdb);
|
|
|
|
if (opacity == 1.0f)
|
|
{
|
|
// Enable depth buffering
|
|
glEnable(GL_DEPTH_TEST);
|
|
glDepthMask(GL_TRUE);
|
|
glDisable(GL_BLEND);
|
|
}
|
|
else
|
|
{
|
|
glEnable(GL_DEPTH_TEST);
|
|
glDepthMask(GL_FALSE);
|
|
glEnable(GL_BLEND);
|
|
#ifdef USE_HDR
|
|
glBlendFunc(GL_ONE_MINUS_SRC_ALPHA, GL_SRC_ALPHA);
|
|
#else
|
|
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
|
#endif
|
|
}
|
|
|
|
glPushMatrix();
|
|
glRotate(q.cast<float>());
|
|
glScalef(size, size, size);
|
|
|
|
#if 0
|
|
// Simple line axes
|
|
glBegin(GL_LINES);
|
|
|
|
glColor4f(1.0f, 0.0f, 0.0f, 1.0f);
|
|
glVertex3f(0.0f, 0.0f, 0.0f);
|
|
glVertex3f(-1.0f, 0.0f, 0.0f);
|
|
|
|
glColor4f(0.0f, 1.0f, 0.0f, 1.0f);
|
|
glVertex3f(0.0f, 0.0f, 0.0f);
|
|
glVertex3f(0.0f, 0.0f, 1.0f);
|
|
|
|
glColor4f(0.0f, 0.0f, 1.0f, 1.0f);
|
|
glVertex3f(0.0f, 0.0f, 0.0f);
|
|
glVertex3f(0.0f, 1.0f, 0.0f);
|
|
|
|
glEnd();
|
|
#endif
|
|
|
|
float labelScale = 0.1f;
|
|
|
|
CelestiaGLProgram* prog = renderer->getShaderManager().getShader(shadprop);
|
|
if (prog == nullptr)
|
|
return;
|
|
prog->use();
|
|
|
|
// x-axis
|
|
glPushMatrix();
|
|
glRotatef(90.0f, 0.0f, 1.0f, 0.0f);
|
|
prog->color = { 1.0f, 0.0f, 0.0f, opacity };
|
|
RenderArrow();
|
|
glTranslatef(0.1f, 0.0f, 0.75f);
|
|
glScalef(labelScale, labelScale, labelScale);
|
|
RenderX();
|
|
glPopMatrix();
|
|
|
|
// y-axis
|
|
glPushMatrix();
|
|
glRotatef(180.0f, 0.0f, 1.0f, 0.0f);
|
|
prog->color = { 0.0f, 1.0f, 0.0f, opacity };
|
|
RenderArrow();
|
|
glTranslatef(0.1f, 0.0f, 0.75f);
|
|
glScalef(labelScale, labelScale, labelScale);
|
|
RenderY();
|
|
glPopMatrix();
|
|
|
|
// z-axis
|
|
glPushMatrix();
|
|
glRotatef(-90.0f, 1.0f, 0.0f, 0.0f);
|
|
prog->color = { 0.0f, 0.0f, 1.0f, opacity };
|
|
RenderArrow();
|
|
glTranslatef(0.1f, 0.0f, 0.75f);
|
|
glScalef(labelScale, labelScale, labelScale);
|
|
RenderZ();
|
|
glPopMatrix();
|
|
|
|
glPopMatrix();
|
|
|
|
glUseProgram(0);
|
|
glDisable(GL_DEPTH_TEST);
|
|
glDepthMask(GL_FALSE);
|
|
glEnable(GL_BLEND);
|
|
glBlendFunc(GL_SRC_ALPHA, GL_ONE);
|
|
}
|
|
|
|
|
|
/****** VelocityVectorArrow implementation ******/
|
|
|
|
VelocityVectorArrow::VelocityVectorArrow(const Body& _body) :
|
|
ArrowReferenceMark(_body)
|
|
{
|
|
setTag("velocity vector");
|
|
setColor(Color(0.6f, 0.6f, 0.9f));
|
|
setSize(body.getRadius() * 2.0f);
|
|
}
|
|
|
|
Vector3d
|
|
VelocityVectorArrow::getDirection(double tdb) const
|
|
{
|
|
auto phase = body.getTimeline()->findPhase(tdb);
|
|
return phase->orbitFrame()->getOrientation(tdb).conjugate() * phase->orbit()->velocityAtTime(tdb);
|
|
}
|
|
|
|
|
|
/****** SunDirectionArrow implementation ******/
|
|
|
|
SunDirectionArrow::SunDirectionArrow(const Body& _body) :
|
|
ArrowReferenceMark(_body)
|
|
{
|
|
setTag("sun direction");
|
|
setColor(Color(1.0f, 1.0f, 0.4f));
|
|
setSize(body.getRadius() * 2.0f);
|
|
}
|
|
|
|
Vector3d
|
|
SunDirectionArrow::getDirection(double tdb) const
|
|
{
|
|
const Body* b = &body;
|
|
Star* sun = nullptr;
|
|
while (b != nullptr)
|
|
{
|
|
Selection center = b->getOrbitFrame(tdb)->getCenter();
|
|
if (center.star() != nullptr)
|
|
sun = center.star();
|
|
b = center.body();
|
|
}
|
|
|
|
if (sun != nullptr)
|
|
return Selection(sun).getPosition(tdb).offsetFromKm(body.getPosition(tdb));
|
|
|
|
return Vector3d::Zero();
|
|
}
|
|
|
|
|
|
/****** SpinVectorArrow implementation ******/
|
|
|
|
SpinVectorArrow::SpinVectorArrow(const Body& _body) :
|
|
ArrowReferenceMark(_body)
|
|
{
|
|
setTag("spin vector");
|
|
setColor(Color(0.6f, 0.6f, 0.6f));
|
|
setSize(body.getRadius() * 2.0f);
|
|
}
|
|
|
|
Vector3d
|
|
SpinVectorArrow::getDirection(double tdb) const
|
|
{
|
|
auto phase = body.getTimeline()->findPhase(tdb);
|
|
return phase->bodyFrame()->getOrientation(tdb).conjugate() * phase->rotationModel()->angularVelocityAtTime(tdb);
|
|
}
|
|
|
|
|
|
/****** BodyToBodyDirectionArrow implementation ******/
|
|
|
|
/*! Create a new body-to-body direction arrow pointing from the origin body toward
|
|
* the specified target object.
|
|
*/
|
|
BodyToBodyDirectionArrow::BodyToBodyDirectionArrow(const Body& _body, const Selection& _target) :
|
|
ArrowReferenceMark(_body),
|
|
target(_target)
|
|
{
|
|
setTag("body to body");
|
|
setColor(Color(0.0f, 0.5f, 0.0f));
|
|
setSize(body.getRadius() * 2.0f);
|
|
}
|
|
|
|
|
|
Vector3d
|
|
BodyToBodyDirectionArrow::getDirection(double tdb) const
|
|
{
|
|
return target.getPosition(tdb).offsetFromKm(body.getPosition(tdb));
|
|
}
|
|
|
|
|
|
/****** BodyAxisArrows implementation ******/
|
|
|
|
BodyAxisArrows::BodyAxisArrows(const Body& _body) :
|
|
AxesReferenceMark(_body)
|
|
{
|
|
setTag("body axes");
|
|
setOpacity(1.0);
|
|
setSize(body.getRadius() * 2.0f);
|
|
}
|
|
|
|
Quaterniond
|
|
BodyAxisArrows::getOrientation(double tdb) const
|
|
{
|
|
return (Quaterniond(AngleAxis<double>(PI, Vector3d::UnitY())) * body.getEclipticToBodyFixed(tdb)).conjugate();
|
|
}
|
|
|
|
|
|
/****** FrameAxisArrows implementation ******/
|
|
|
|
FrameAxisArrows::FrameAxisArrows(const Body& _body) :
|
|
AxesReferenceMark(_body)
|
|
{
|
|
setTag("frame axes");
|
|
setOpacity(0.5);
|
|
setSize(body.getRadius() * 2.0f);
|
|
}
|
|
|
|
Quaterniond
|
|
FrameAxisArrows::getOrientation(double tdb) const
|
|
{
|
|
return body.getEclipticToFrame(tdb).conjugate();
|
|
}
|