Big common application code refactoring

* Allow to parse four element vectors
 * Allow colors defined in HTML notation (#rrggbbaa)
   in CEL scripts
 * Refactor Overlay::Rectangle to an independent class
 * Refactor CelestiaCore::OverlayImage to an independent class
 * Refactor CelestiaCore::View to an independent class
 * Rename Renderer::getScreenSize to getViewport
 * Add wrappers for scissors test and MSAA control
pull/444/head
Hleb Valoshka 2019-10-08 23:00:19 +03:00
parent 6e44de22f1
commit e3baaad767
27 changed files with 1067 additions and 669 deletions

View File

@ -74,6 +74,8 @@ set(CELENGINE_SOURCES
opencluster.h opencluster.h
overlay.cpp overlay.cpp
overlay.h overlay.h
overlayimage.cpp
overlayimage.h
parseobject.cpp parseobject.cpp
parseobject.h parseobject.h
parser.cpp parser.cpp
@ -84,6 +86,7 @@ set(CELENGINE_SOURCES
# particlesystem.h # particlesystem.h
planetgrid.cpp planetgrid.cpp
planetgrid.h planetgrid.h
rectangle.h
referencemark.h referencemark.h
rendcontext.cpp rendcontext.cpp
rendcontext.h rendcontext.h

View File

@ -228,6 +228,24 @@ int Console::getHeight() const
} }
void Console::setColor(float r, float g, float b, float a) const
{
glColor4f(r, g, b, a);
}
void Console::setColor(const Color& c) const
{
glColor4f(c.red(), c.green(), c.blue(), c.alpha());
}
void Console::moveBy(float dx, float dy, float dz) const
{
glTranslatef(dx, dy, dz);
}
// //
// ConsoleStreamBuf implementation // ConsoleStreamBuf implementation
// //

View File

@ -56,6 +56,11 @@ class Console : public std::ostream
void setScale(int, int); void setScale(int, int);
void setFont(TextureFont*); void setFont(TextureFont*);
void setColor(float r, float g, float b, float a) const;
void setColor(const Color& c) const;
void moveBy(float dx, float dy, float dz = 0.0f) const;
void print(wchar_t); void print(wchar_t);
void print(char*); void print(char*);
void newline(); void newline();

View File

@ -15,7 +15,9 @@
#include <celutil/debug.h> #include <celutil/debug.h>
#include "vecgl.h" #include "vecgl.h"
#include "overlay.h" #include "overlay.h"
#include "rectangle.h"
#include "render.h" #include "render.h"
#include "texture.h"
using namespace std; using namespace std;
using namespace Eigen; using namespace Eigen;
@ -166,101 +168,31 @@ void Overlay::print(const char* s)
} }
} }
static void drawRect(const Renderer& r, void Overlay::drawRectangle(const Rect& r)
const array<float, 8>& vertices,
const vector<Vector4f>& colors,
Overlay::RectType type,
float linewidth)
{ {
uint32_t p = 0; if (useTexture && r.tex == nullptr)
switch (type)
{
case Overlay::RectType::Textured:
p |= ShaderProperties::HasTexture;
// [[ fallthrough ]]
case Overlay::RectType::Outlined:
case Overlay::RectType::Filled:
switch (colors.size())
{
case 0:
break;
case 1:
p |= ShaderProperties::UniformColor;
break;
case 4:
p |= ShaderProperties::PerVertexColor;
break;
default:
fmt::fprintf(cerr, "Incorrect number of colors: %i\n", colors.size());
break;
}
default:
break;
}
auto prog = r.getShaderManager().getShader(ShaderProperties(p));
if (prog == nullptr)
return;
constexpr array<short, 8> texels = {0, 1, 1, 1, 1, 0, 0, 0};
auto s = static_cast<GLsizeiptr>(memsize(vertices) + memsize(texels) + 4*4*sizeof(GLfloat));
static celgl::VertexObject vo{ GL_ARRAY_BUFFER, s, GL_DYNAMIC_DRAW };
vo.bindWritable();
if (!vo.initialized())
{
vo.allocate();
vo.setBufferData(texels.data(), memsize(vertices), memsize(texels));
vo.setVertices(2, GL_FLOAT);
vo.setTextureCoords(2, GL_SHORT, false, 0, memsize(vertices));
vo.setColors(4, GL_FLOAT, false, 0, memsize(vertices) + memsize(texels));
}
vo.setBufferData(vertices.data(), 0, memsize(vertices));
if (colors.size() == 4)
vo.setBufferData(colors.data(), memsize(vertices) + memsize(texels), 4*4*sizeof(GLfloat));
prog->use();
if (type == Overlay::RectType::Textured)
prog->samplerParam("tex") = 0;
if (colors.size() == 1)
prog->vec4Param("color") = colors[0];
if (type != Overlay::RectType::Outlined)
{
vo.draw(GL_TRIANGLE_FAN, 4);
}
else
{
if (linewidth != 1.0f)
glLineWidth(linewidth);
vo.draw(GL_LINE_LOOP, 4);
if (linewidth != 1.0f)
glLineWidth(1.0f);
}
glUseProgram(0);
vo.unbind();
}
void Overlay::rect(const Overlay::Rectangle& r)
{
if (useTexture && r.type != Overlay::RectType::Textured)
{ {
glDisable(GL_TEXTURE_2D); glDisable(GL_TEXTURE_2D);
useTexture = false; useTexture = false;
} }
vector<Vector4f> cv; renderer.drawRectangle(r);
for (const Color& c : r.colors)
cv.push_back(c.toVector4());
array<float, 8> coord = { r.x, r.y, r.x+r.w, r.y, r.x+r.w, r.y+r.h, r.x, r.y+r.h };
drawRect(renderer, coord, cv, r.type, r.lw);
} }
void Overlay::setColor(float r, float g, float b, float a) const
{
glColor4f(r, g, b, a);
}
void Overlay::setColor(const Color& c) const
{
glColor4f(c.red(), c.green(), c.blue(), c.alpha());
}
void Overlay::moveBy(float dx, float dy, float dz) const
{
glTranslatef(dx, dy, dz);
}
// //
// OverlayStreamBuf implementation // OverlayStreamBuf implementation

View File

@ -10,15 +10,17 @@
#ifndef _OVERLAY_H_ #ifndef _OVERLAY_H_
#define _OVERLAY_H_ #define _OVERLAY_H_
#include <string> #include <algorithm>
#include <array>
#include <iostream> #include <iostream>
#include <vector> #include <string>
#include <celtxf/texturefont.h>
#include <celutil/color.h> #include <celutil/color.h>
#include <celtxf/texturefont.h>
class Overlay; class Overlay;
class Renderer; class Renderer;
class Rect;
// Custom streambuf class to support C++ operator style output. The // Custom streambuf class to support C++ operator style output. The
// output is completely unbuffered so that it can coexist with printf // output is completely unbuffered so that it can coexist with printf
@ -60,32 +62,24 @@ class Overlay : public std::ostream
void setWindowSize(int, int); void setWindowSize(int, int);
void setFont(TextureFont*); void setFont(TextureFont*);
enum class RectType void setColor(float r, float g, float b, float a) const;
void setColor(const Color& c) const;
void moveBy(float dx, float dy, float dz = 0.0f) const;
void savePos() const
{ {
Outlined = 0x0001, glPushMatrix();
Filled = 0x0002, };
Textured = 0x0004 void restorePos() const
{
glPopMatrix();
};
const Renderer& getRenderer() const
{
return renderer;
}; };
struct Rectangle void drawRectangle(const Rect&);
{
Rectangle() = default;
Rectangle(float _x, float _y, float _w, float _h, const Color& _c, RectType _t, float _lw = 1.0f) :
x(_x), y(_y), w(_w), h(_h), type(_t), lw(_lw)
{
colors.push_back(_c);
};
Rectangle(float _x, float _y, float _w, float _h, const std::vector<Color>& _c, RectType _t, float _lw = 1.0f) :
x(_x), y(_y), w(_w), h(_h), colors(_c), type(_t), lw(_lw)
{
};
float x, y, w, h;
float lw { 1.0f };
RectType type { RectType::Filled };
std::vector<Color> colors;
};
void rect(const Rectangle&);
void beginText(); void beginText();
void endText(); void endText();

View File

@ -0,0 +1,65 @@
#include <algorithm>
#include <iostream>
#include <celmath/mathlib.h>
#include "overlayimage.h"
#include "rectangle.h"
#include "render.h"
using namespace celmath;
OverlayImage::OverlayImage(fs::path f, Renderer *r) :
filename(std::move(f)),
renderer(r)
{
texture = std::unique_ptr<Texture>(LoadTextureFromFile(fs::path("images") / filename));
}
void OverlayImage::setColor(const Color& c)
{
colors.fill(c);
}
void OverlayImage::setColor(std::array<Color, 4>& c)
{
std::copy(c.begin(), c.end(), colors.begin());
}
void OverlayImage::render(float curr_time, int width, int height)
{
if (renderer == nullptr || texture == nullptr || (curr_time >= start + duration))
return;
float xSize = texture->getWidth();
float ySize = texture->getHeight();
// center overlay image horizontally if offsetX = 0
float left = (width * (1 + offsetX) - xSize)/2;
// center overlay image vertically if offsetY = 0
float bottom = (height * (1 + offsetY) - ySize)/2;
if (fitscreen)
{
float coeffx = xSize / width; // overlay pict width/view window width ratio
float coeffy = ySize / height; // overlay pict height/view window height ratio
xSize /= coeffx; // new overlay picture width size to fit viewport
ySize /= coeffy; // new overlay picture height to fit viewport
left = (width - xSize) / 2; // to be sure overlay pict is centered in viewport
bottom = 0; // overlay pict locked at bottom of screen
}
float alpha = 1.0f;
if (curr_time > start + fadeafter)
{
alpha = clamp(start + duration - curr_time);
}
Rect r(left, bottom, xSize, ySize);
r.tex = texture.get();
for (size_t i = 0; i < colors.size(); i++)
{
r.colors[i] = Color(colors[i], colors[i].alpha() * alpha);
}
r.nColors = 4;
renderer->drawRectangle(r);
}

View File

@ -0,0 +1,60 @@
#pragma once
#include <array>
#include <memory>
#include <celcompat/filesystem.h>
#include "texture.h"
class Renderer;
class OverlayImage
{
public:
OverlayImage(fs::path, Renderer*);
OverlayImage() = delete;
~OverlayImage() = default;
OverlayImage(OverlayImage&) = delete;
OverlayImage(OverlayImage&&) = delete;
void render(float, int, int);
bool isNewImage(const fs::path& f) const
{
return filename != f;
}
void setStartTime(float t)
{
start = t;
}
void setDuration(float t)
{
duration = t;
}
void setFadeAfter(float t)
{
fadeafter = t;
}
void setOffset(float x, float y)
{
offsetX = x;
offsetY = y;
}
void fitScreen(bool t)
{
fitscreen = t;
}
void setColor(const Color& c);
void setColor(std::array<Color, 4>& c);
private:
float start { 0.0f };
float duration { 0.0f };
float fadeafter { 0.0f };
float offsetX { 0.0f };
float offsetY { 0.0f };
bool fitscreen { false };
std::array<Color, 4> colors;
fs::path filename;
std::unique_ptr<Texture> texture;
Renderer *renderer { nullptr };
};

View File

@ -442,6 +442,44 @@ bool AssociativeArray::getVector(const string& key, Vector3f& val) const
} }
bool AssociativeArray::getVector(const string& key, Vector4d& val) const
{
Value* v = getValue(key);
if (v == nullptr || v->getType() != Value::ArrayType)
return false;
ValueArray* arr = v->getArray();
if (arr->size() != 4)
return false;
Value* x = (*arr)[0];
Value* y = (*arr)[1];
Value* z = (*arr)[2];
Value* w = (*arr)[3];
if (x->getType() != Value::NumberType ||
y->getType() != Value::NumberType ||
z->getType() != Value::NumberType ||
w->getType() != Value::NumberType)
return false;
val = Vector4d(x->getNumber(), y->getNumber(), z->getNumber(), w->getNumber());
return true;
}
bool AssociativeArray::getVector(const string& key, Vector4f& val) const
{
Vector4d vecVal;
if (!getVector(key, vecVal))
return false;
val = vecVal.cast<float>();
return true;
}
/** /**
* Retrieves a quaternion, scaled to an associated angle unit. * Retrieves a quaternion, scaled to an associated angle unit.
* *
@ -490,14 +528,40 @@ bool AssociativeArray::getRotation(const string& key, Eigen::Quaternionf& val) c
bool AssociativeArray::getColor(const string& key, Color& val) const bool AssociativeArray::getColor(const string& key, Color& val) const
{ {
Vector3d vecVal; Vector4d vec4;
if (getVector(key, vec4))
{
Vector4f vec4f = vec4.cast<float>();
val = Color(vec4f);
return true;
}
if (!getVector(key, vecVal)) Vector3d vec3;
return false; if (getVector(key, vec3))
{
Vector3f vec3f = vec3.cast<float>();
val = Color(vec3f);
return true;
}
val = Color((float) vecVal.x(), (float) vecVal.y(), (float) vecVal.z()); string rgba;
if (getString(key, rgba))
{
int r, g, b, a;
int ret = sscanf(rgba.c_str(), "#%2x%2x%2x%2x", &r, &g, &b, &a);
switch (ret)
{
case 3:
a = 0xFF;
case 4:
val = Color((char unsigned)r, (char unsigned)g, (unsigned char)b, (unsigned char)a);
return true;
default:
return false;
}
}
return true; return false;
} }

View File

@ -42,6 +42,8 @@ class AssociativeArray
bool getBoolean(const std::string&, bool&) const; bool getBoolean(const std::string&, bool&) const;
bool getVector(const std::string&, Eigen::Vector3d&) const; bool getVector(const std::string&, Eigen::Vector3d&) const;
bool getVector(const std::string&, Eigen::Vector3f&) const; bool getVector(const std::string&, Eigen::Vector3f&) const;
bool getVector(const std::string&, Eigen::Vector4d&) const;
bool getVector(const std::string&, Eigen::Vector4f&) const;
bool getRotation(const std::string&, Eigen::Quaternionf&) const; bool getRotation(const std::string&, Eigen::Quaternionf&) const;
bool getColor(const std::string&, Color&) const; bool getColor(const std::string&, Color&) const;
bool getAngle(const std::string&, double&, double = 1.0, double = 0.0) const; bool getAngle(const std::string&, double&, double = 1.0, double = 0.0) const;

View File

@ -0,0 +1,61 @@
// rectangle.h
//
// Copyright (C) 2019, Celestia Development Team
//
// 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.
#pragma once
#include <algorithm>
#include <array>
#include <celutil/color.h>
class Renderer;
class Texture;
class Rect
{
public:
enum class Type
{
BorderOnly = 0x0001,
Filled = 0x0002,
};
Rect() = default;
Rect(float _x, float _y, float _w, float _h) :
x(_x), y(_y), w(_w), h(_h)
{
};
void setColor(const Color &_color)
{
color = _color;
nColors = 1;
}
void setColor(const std::array<Color,4> _colors)
{
std::copy(_colors.begin(), _colors.end(), colors.begin());
nColors = 4;
}
void setLineWidth(float _lw)
{
lw = _lw;
}
void setType(Type _type)
{
type = _type;
}
float x, y, w, h;
float lw { 1.0f };
union
{
std::array<Color,4> colors;
Color color;
};
Texture *tex { nullptr };
Type type { Type::Filled };
int nColors { 0 };
};

View File

@ -58,6 +58,7 @@ std::ofstream hdrlog;
#include "modelgeometry.h" #include "modelgeometry.h"
#include "curveplot.h" #include "curveplot.h"
#include "shadermanager.h" #include "shadermanager.h"
#include "rectangle.h"
#include <celutil/debug.h> #include <celutil/debug.h>
#include <celmath/frustum.h> #include <celmath/frustum.h>
#include <celmath/distance.h> #include <celmath/distance.h>
@ -7886,7 +7887,7 @@ void Renderer::setSolarSystemMaxDistance(float t)
SolarSystemMaxDistance = clamp(t, 1.0f, 10.0f); SolarSystemMaxDistance = clamp(t, 1.0f, 10.0f);
} }
void Renderer::getScreenSize(int* x, int* y, int* w, int* h) const void Renderer::getViewport(int* x, int* y, int* w, int* h) const
{ {
GLint viewport[4]; GLint viewport[4];
glGetIntegerv(GL_VIEWPORT, viewport); glGetIntegerv(GL_VIEWPORT, viewport);
@ -7900,12 +7901,63 @@ void Renderer::getScreenSize(int* x, int* y, int* w, int* h) const
*h = viewport[3]; *h = viewport[3];
} }
void Renderer::getScreenSize(std::array<int, 4>& viewport) const void Renderer::getViewport(std::array<int, 4>& viewport) const
{ {
static_assert(sizeof(int) == sizeof(GLint), "int and GLint size mismatch"); static_assert(sizeof(int) == sizeof(GLint), "int and GLint size mismatch");
glGetIntegerv(GL_VIEWPORT, &viewport[0]); glGetIntegerv(GL_VIEWPORT, &viewport[0]);
} }
void Renderer::setViewport(int x, int y, int w, int h) const
{
glViewport(x, y, w, h);
}
void Renderer::setViewport(const std::array<int, 4>& viewport) const
{
glViewport(viewport[0], viewport[1], viewport[2], viewport[3]);
}
void Renderer::setScissor(int x, int y, int w, int h)
{
if ((m_GLStateFlag & ScissorTest) == 0)
{
glEnable(GL_SCISSOR_TEST);
m_GLStateFlag |= ScissorTest;
}
glScissor(x, y, w, h);
}
void Renderer::removeScissor()
{
if ((m_GLStateFlag & ScissorTest) != 0)
{
glDisable(GL_SCISSOR_TEST);
m_GLStateFlag &= ~ScissorTest;
}
}
void Renderer::enableMSAA()
{
if ((m_GLStateFlag & Multisaple) == 0)
{
glEnable(GL_MULTISAMPLE);
m_GLStateFlag |= Multisaple;
}
}
void Renderer::disableMSAA()
{
if ((m_GLStateFlag & Multisaple) != 0)
{
glDisable(GL_MULTISAMPLE);
m_GLStateFlag &= ~Multisaple;
}
}
bool Renderer::isMSAAEnabled() const
{
return (m_GLStateFlag & Multisaple) != 0;;
}
constexpr GLenum toGLFormat(Renderer::PixelFormat format) constexpr GLenum toGLFormat(Renderer::PixelFormat format)
{ {
return (GLenum) format; return (GLenum) format;
@ -7918,3 +7970,93 @@ bool Renderer::captureFrame(int x, int y, int w, int h, Renderer::PixelFormat fo
return glGetError() == GL_NO_ERROR; return glGetError() == GL_NO_ERROR;
} }
static void drawRectangle(const Renderer &renderer, const Rect &r)
{
uint32_t p = r.tex == nullptr ? 0 : ShaderProperties::HasTexture;
switch (r.nColors)
{
case 0:
break;
case 1:
p |= ShaderProperties::UniformColor;
break;
case 4:
p |= ShaderProperties::PerVertexColor;
break;
default:
fmt::fprintf(cerr, "Incorrect number of colors: %i\n", r.nColors);
}
auto prog = renderer.getShaderManager().getShader(ShaderProperties(p));
if (prog == nullptr)
return;
constexpr array<short, 8> texels = {0, 1, 1, 1, 1, 0, 0, 0};
array<float, 8> vertices = { r.x, r.y, r.x+r.w, r.y, r.x+r.w, r.y+r.h, r.x, r.y+r.h };
auto s = static_cast<GLsizeiptr>(memsize(vertices) + memsize(texels) + 4*4*sizeof(GLfloat));
static celgl::VertexObject vo{ GL_ARRAY_BUFFER, s, GL_DYNAMIC_DRAW };
vo.bindWritable();
if (!vo.initialized())
{
vo.allocate();
vo.setBufferData(texels.data(), memsize(vertices), memsize(texels));
vo.setVertices(2, GL_FLOAT);
vo.setTextureCoords(2, GL_SHORT, false, 0, memsize(vertices));
vo.setColors(4, GL_FLOAT, false, 0, memsize(vertices) + memsize(texels));
}
vo.setBufferData(vertices.data(), 0, memsize(vertices));
if (r.nColors == 4)
{
array<Vector4f, 4> ct;
for (size_t i = 0; i < 4; i++)
ct[i] = r.colors[i].toVector4();
vo.setBufferData(ct.data(), memsize(vertices) + memsize(texels), 4*4*sizeof(GLfloat));
}
prog->use();
if (r.tex != nullptr)
{
glEnable(GL_TEXTURE_2D);
r.tex->bind();
prog->samplerParam("tex") = 0;
}
if (r.nColors == 1)
prog->vec4Param("color") = r.colors[0].toVector4();
if (r.type != Rect::Type::BorderOnly)
{
vo.draw(GL_TRIANGLE_FAN, 4);
}
else
{
if (r.lw != 1.0f)
glLineWidth(r.lw);
vo.draw(GL_LINE_LOOP, 4);
if (r.lw != 1.0f)
glLineWidth(1.0f);
}
glUseProgram(0);
vo.unbind();
}
void Renderer::drawRectangle(const Rect &r) const
{
::drawRectangle(*this, r);
}
void Renderer::setRenderRegion(int x, int y, int width, int height, bool withScissor)
{
if (withScissor)
setScissor(x, y, width, height);
else
removeScissor();
setViewport(x, y, width, height);
resize(width, height);
}

View File

@ -21,11 +21,11 @@
#endif #endif
#include <celengine/starcolors.h> #include <celengine/starcolors.h>
#include <celengine/rendcontext.h> #include <celengine/rendcontext.h>
#include "celengine/vertexobject.h"
#include <celtxf/texturefont.h> #include <celtxf/texturefont.h>
#include <vector> #include <vector>
#include <list> #include <list>
#include <string> #include <string>
#include "vertexobject.h"
class RendererWatcher; class RendererWatcher;
@ -33,6 +33,7 @@ class FrameTree;
class ReferenceMark; class ReferenceMark;
class CurvePlot; class CurvePlot;
class AsterismList; class AsterismList;
class Rect;
struct LightSource struct LightSource
{ {
@ -246,8 +247,20 @@ class Renderer
void setOrbitMask(int); void setOrbitMask(int);
int getScreenDpi() const; int getScreenDpi() const;
void setScreenDpi(int); void setScreenDpi(int);
void getScreenSize(int* x, int* y, int* w, int* h) const;
void getScreenSize(std::array<int, 4>& viewport) const; // GL wrappers
void getViewport(int* x, int* y, int* w, int* h) const;
void getViewport(std::array<int, 4>& viewport) const;
void setViewport(int x, int y, int w, int h) const;
void setViewport(const std::array<int, 4>& viewport) const;
void setScissor(int x, int y, int w, int h);
void removeScissor();
void enableMSAA();
void disableMSAA();
bool isMSAAEnabled() const;
void drawRectangle(const Rect& r) const;
void setRenderRegion(int x, int y, int width, int height, bool withScissor = true);
const ColorTemperatureTable* getStarColorTable() const; const ColorTemperatureTable* getStarColorTable() const;
void setStarColorTable(const ColorTemperatureTable*); void setStarColorTable(const ColorTemperatureTable*);
bool getVideoSync() const; bool getVideoSync() const;
@ -709,6 +722,13 @@ class Renderer
uint32_t frameCount; uint32_t frameCount;
int currentIntervalIndex{ 0 }; int currentIntervalIndex{ 0 };
enum GLStateFlags
{
ScissorTest = 0x0001,
Multisaple = 0x0002,
};
int m_GLStateFlag { 0 };
celgl::VertexObject markerVO{ GL_ARRAY_BUFFER, 0, GL_STATIC_DRAW }; celgl::VertexObject markerVO{ GL_ARRAY_BUFFER, 0, GL_STATIC_DRAW };

View File

@ -24,6 +24,8 @@ set(CELESTIA_SOURCES
scriptmenu.h scriptmenu.h
url.cpp url.cpp
url.h url.h
view.cpp
view.h
) )
set(CELX_SOURCES set(CELX_SOURCES

View File

@ -145,7 +145,7 @@ bool AVICapture::captureFrame()
// Get the dimensions of the current viewport // Get the dimensions of the current viewport
int x, y, w, h; int x, y, w, h;
renderer->getScreenSize(&x, &y, &w, &h); renderer->getViewport(&x, &y, &w, &h);
x += (w - width) / 2; x += (w - width) / 2;
y += (h - height) / 2; y += (h - height) / 2;

View File

@ -52,6 +52,7 @@
#include <celutil/debug.h> #include <celutil/debug.h>
#include <celutil/color.h> #include <celutil/color.h>
#include <celengine/vecgl.h> #include <celengine/vecgl.h>
#include <celengine/rectangle.h>
#ifdef CELX #ifdef CELX
#include <celephem/scriptobject.h> #include <celephem/scriptobject.h>
@ -169,143 +170,15 @@ float ComputeRotationCoarseness(Simulation& sim)
} }
View::View(View::Type _type,
Observer* _observer,
float _x, float _y,
float _width, float _height) :
type(_type),
observer(_observer),
parent(nullptr),
child1(nullptr),
child2(nullptr),
x(_x),
y(_y),
width(_width),
height(_height),
renderFlags(0),
labelMode(0),
zoom(1),
alternateZoom(1)
{
}
void View::mapWindowToView(float wx, float wy, float& vx, float& vy) const
{
vx = (wx - x) / width;
vy = (wy + (y + height - 1)) / height;
vx = (vx - 0.5f) * (width / height);
vy = 0.5f - vy;
}
void View::walkTreeResize(View* sibling, int sign) {
float ratio;
switch (parent->type)
{
case View::HorizontalSplit:
ratio = parent->height / (parent->height - height);
sibling->height *= ratio;
if (sign == 1)
{
sibling->y = parent->y + (sibling->y - parent->y) * ratio;
}
else
{
sibling->y = parent->y + (sibling->y - (y + height)) * ratio;
}
break;
case View::VerticalSplit:
ratio = parent->width / (parent->width - width);
sibling->width *= ratio;
if (sign == 1)
{
sibling->x = parent->x + (sibling->x - parent->x) * ratio;
}
else
{
sibling->x = parent->x + (sibling->x - (x + width) ) * ratio;
}
break;
case View::ViewWindow:
break;
}
if (sibling->child1) walkTreeResize(sibling->child1, sign);
if (sibling->child2) walkTreeResize(sibling->child2, sign);
}
bool View::walkTreeResizeDelta(View* v, float delta, bool check)
{
View *p=v;
int sign = -1;
float ratio;
double newSize;
if (v->child1)
{
if (!walkTreeResizeDelta(v->child1, delta, check))
return false;
}
if (v->child2)
{
if (!walkTreeResizeDelta(v->child2, delta, check))
return false;
}
while ( p != child1 && p != child2 && (p = p->parent) ) ;
if (p == child1) sign = 1;
switch (type)
{
case View::HorizontalSplit:
delta = -delta;
ratio = (p->height + sign * delta) / p->height;
newSize = v->height * ratio;
if (newSize <= .1) return false;
if (check) return true;
v->height = (float) newSize;
if (sign == 1)
{
v->y = p->y + (v->y - p->y) * ratio;
}
else
{
v->y = p->y + delta + (v->y - p->y) * ratio;
}
break;
case View::VerticalSplit:
ratio = (p->width + sign * delta) / p->width;
newSize = v->width * ratio;
if (newSize <= .1) return false;
if (check) return true;
v->width = (float) newSize;
if (sign == 1)
{
v->x = p->x + (v->x - p->x) * ratio;
}
else
{
v->x = p->x + delta + (v->x - p->x) * ratio;
}
break;
case View::ViewWindow:
break;
}
return true;
}
CelestiaCore::CelestiaCore() : CelestiaCore::CelestiaCore() :
oldFOV(stdFOV) oldFOV(stdFOV),
{
/* Get a renderer here so it may be queried for capabilities of the /* Get a renderer here so it may be queried for capabilities of the
underlying engine even before rendering is enabled. It's initRenderer() underlying engine even before rendering is enabled. It's initRenderer()
routine will be called much later. */ routine will be called much later. */
renderer = new Renderer(); renderer(new Renderer()),
timer = new Timer(); timer(new Timer()),
execEnv(new CoreExecutionEnvironment(*this))
execEnv = new CoreExecutionEnvironment(*this); {
for (int i = 0; i < KeyCount; i++) for (int i = 0; i < KeyCount; i++)
{ {
@ -607,7 +480,8 @@ void CelestiaCore::mouseButtonDown(float x, float y, int button)
} }
} }
} }
if (v2 != nullptr) { if (v2 != nullptr)
{
// Look for common ancestor to v1 & v2 = split being draged. // Look for common ancestor to v1 & v2 = split being draged.
View *p1 = v1, *p2 = v2; View *p1 = v1, *p2 = v2;
while ( (p1 = p1->parent) != nullptr ) while ( (p1 = p1->parent) != nullptr )
@ -661,8 +535,8 @@ void CelestiaCore::mouseButtonUp(float x, float y, int button)
float pickX, pickY; float pickX, pickY;
float aspectRatio = ((float) width / (float) height); float aspectRatio = ((float) width / (float) height);
(*activeView)->mapWindowToView((float) x / (float) width, (*activeView)->mapWindowToView((float) x / (float) width,
(float) y / (float) height, (float) y / (float) height,
pickX, pickY); pickX, pickY);
Vector3f pickRay = Vector3f pickRay =
sim->getActiveObserver()->getPickRay(pickX * aspectRatio, pickY); sim->getActiveObserver()->getPickRay(pickX * aspectRatio, pickY);
@ -678,8 +552,8 @@ void CelestiaCore::mouseButtonUp(float x, float y, int button)
float pickX, pickY; float pickX, pickY;
float aspectRatio = ((float) width / (float) height); float aspectRatio = ((float) width / (float) height);
(*activeView)->mapWindowToView((float) x / (float) width, (*activeView)->mapWindowToView((float) x / (float) width,
(float) y / (float) height, (float) y / (float) height,
pickX, pickY); pickX, pickY);
Vector3f pickRay = Vector3f pickRay =
sim->getActiveObserver()->getPickRay(pickX * aspectRatio, pickY); sim->getActiveObserver()->getPickRay(pickX * aspectRatio, pickY);
@ -742,9 +616,6 @@ void CelestiaCore::mouseMove(float x, float y)
if (views.size() > 1 && cursorHandler != nullptr) if (views.size() > 1 && cursorHandler != nullptr)
{ {
/*View* v1 = 0; Unused*/
/*View* v2 = 0; Unused*/
for (const auto v : views) for (const auto v : views)
{ {
if (v->type == View::ViewWindow) if (v->type == View::ViewWindow)
@ -2355,55 +2226,43 @@ void CelestiaCore::draw()
// I'm not certain that a special case for one view is required; but, // I'm not certain that a special case for one view is required; but,
// it's possible that there exists some broken hardware out there // it's possible that there exists some broken hardware out there
// that has to fall back to software rendering if the scissor test // that has to fall back to software rendering if the scissor test
// is enable. To keep performance on this hypothetical hardware // is enabled. To keep performance on this hypothetical hardware
// reasonable in the typical single view case, we'll use this // reasonable in the typical single view case, we'll use this
// scissorless special case. I'm only paranoid because I've been // scissorless special case. I'm only paranoid because I've been
// burned by crap hardware so many times. cjl // burned by crap hardware so many times. cjl
glViewport(0, 0, width, height); renderer->setRenderRegion(0, 0, width, height, false);
renderer->resize(width, height);
sim->render(*renderer); sim->render(*renderer);
} }
else else
{ {
glEnable(GL_SCISSOR_TEST);
for (const auto view : views) for (const auto view : views)
{ {
if (view->type == View::ViewWindow) if (view->type == View::ViewWindow)
{ {
glScissor((GLint) (view->x * width), view->switchTo(width, height);
(GLint) (view->y * height),
(GLsizei) (view->width * width),
(GLsizei) (view->height * height));
glViewport((GLint) (view->x * width),
(GLint) (view->y * height),
(GLsizei) (view->width * width),
(GLsizei) (view->height * height));
renderer->resize((int) (view->width * width),
(int) (view->height * height));
sim->render(*renderer, *view->observer); sim->render(*renderer, *view->observer);
} }
} }
glDisable(GL_SCISSOR_TEST); renderer->setRenderRegion(0, 0, width, height, false);
glViewport(0, 0, width, height);
} }
GLboolean toggleAA = glIsEnabled(GL_MULTISAMPLE); bool toggleAA = renderer->isMSAAEnabled();
if (toggleAA && (renderer->getRenderFlags() & Renderer::ShowCloudMaps)) if (toggleAA && (renderer->getRenderFlags() & Renderer::ShowCloudMaps))
glDisable(GL_MULTISAMPLE); renderer->disableMSAA();
renderOverlay(); renderOverlay();
if (showConsole) if (showConsole)
{ {
console.setFont(font); console.setFont(font);
glColor4f(1.0f, 1.0f, 1.0f, 1.0f); console.setColor(1.0f, 1.0f, 1.0f, 1.0f);
console.begin(); console.begin();
glTranslatef(0.0f, 200.0f, 0.0f); console.moveBy(0.0f, 200.0f, 0.0f);
console.render(ConsolePageRows); console.render(ConsolePageRows);
console.end(); console.end();
} }
if (toggleAA) if (toggleAA)
glEnable(GL_MULTISAMPLE); renderer->enableMSAA();
if (movieCapture != nullptr && recording) if (movieCapture != nullptr && recording)
movieCapture->captureFrame(); movieCapture->captureFrame();
@ -2432,9 +2291,11 @@ void CelestiaCore::resize(GLsizei w, GLsizei h)
if (h == 0) if (h == 0)
h = 1; h = 1;
glViewport(0, 0, w, h);
if (renderer != nullptr) if (renderer != nullptr)
{
renderer->setViewport(0, 0, w, h);
renderer->resize(w, h); renderer->resize(w, h);
}
if (overlay != nullptr) if (overlay != nullptr)
overlay->setWindowSize(w, h); overlay->setWindowSize(w, h);
console.setScale(w, h); console.setScale(w, h);
@ -2497,88 +2358,35 @@ void CelestiaCore::setViewChanged()
void CelestiaCore::splitView(View::Type type, View* av, float splitPos) void CelestiaCore::splitView(View::Type type, View* av, float splitPos)
{ {
setViewChanged(); if (type == View::ViewWindow)
return;
if (av == nullptr) if (av == nullptr)
av = (*activeView); av = *activeView;
bool vertical = ( type == View::VerticalSplit );
Observer* o = sim->addObserver();
bool tooSmall = false;
switch (type) // If active view is too small, don't split it. if (!av->isSplittable(type))
{
case View::HorizontalSplit:
if (av->height < 0.2f) tooSmall = true;
break;
case View::VerticalSplit:
if (av->width < 0.2f) tooSmall = true;
break;
case View::ViewWindow:
return;
break;
}
if (tooSmall)
{ {
flash(_("View too small to be split")); flash(_("View too small to be split"));
return; return;
} }
flash(_("Added view"));
setViewChanged();
Observer* o = sim->addObserver();
// Make the new observer a copy of the old one // Make the new observer a copy of the old one
// TODO: This works, but an assignment operator for Observer // TODO: This works, but an assignment operator for Observer
// should be defined. // should be defined.
*o = *(sim->getActiveObserver()); *o = *(sim->getActiveObserver());
float w1, h1, w2, h2; View* split, *view;
if (vertical) av->split(type, o, splitPos, &split, &view);
{
w1 = av->width * splitPos;
w2 = av->width - w1;
h1 = av->height;
h2 = av->height;
}
else
{
w1 = av->width;
w2 = av->width;
h1 = av->height * splitPos;
h2 = av->height - h1;
}
View* split = new View(type,
0,
av->x,
av->y,
av->width,
av->height);
split->parent = av->parent;
if (av->parent != nullptr)
{
if (av->parent->child1 == av)
av->parent->child1 = split;
else
av->parent->child2 = split;
}
split->child1 = av;
av->width = w1;
av->height = h1;
av->parent = split;
View* view = new View(View::ViewWindow,
o,
av->x + (vertical ? w1 : 0),
av->y + (vertical ? 0 : h1),
w2, h2);
split->child2 = view;
view->parent = split;
view->zoom = av->zoom;
views.push_back(split); views.push_back(split);
views.push_back(view); views.push_back(view);
setFOVFromZoom(); setFOVFromZoom();
flash(_("Added view"));
} }
void CelestiaCore::setFOVFromZoom() void CelestiaCore::setFOVFromZoom()
@ -2605,29 +2413,23 @@ void CelestiaCore::singleView(View* av)
setViewChanged(); setViewChanged();
if (av == nullptr) if (av == nullptr)
av = (*activeView); av = *activeView;
list<View*>::iterator i = views.begin(); list<View*>::iterator i = views.begin();
while(i != views.end()) while(i != views.end())
{ {
if ((*i) != av) if ((*i) != av)
{ {
sim->removeObserver((*i)->observer); sim->removeObserver((*i)->getObserver());
delete (*i)->observer; delete (*i)->getObserver();
delete (*i); delete (*i);
i=views.erase(i); i = views.erase(i);
} }
else else
i++; ++i;
} }
av->x = 0.0f; av->reset();
av->y = 0.0f;
av->width = 1.0f;
av->height = 1.0f;
av->parent = nullptr;
av->child1 = nullptr;
av->child2 = nullptr;
activeView = views.begin(); activeView = views.begin();
sim->setActiveObserver((*activeView)->observer); sim->setActiveObserver((*activeView)->observer);
@ -2636,60 +2438,37 @@ void CelestiaCore::singleView(View* av)
void CelestiaCore::setActiveView(View* v) void CelestiaCore::setActiveView(View* v)
{ {
activeView = find(views.begin(),views.end(),v); activeView = find(views.begin(), views.end(), v);
sim->setActiveObserver((*activeView)->observer); sim->setActiveObserver((*activeView)->observer);
} }
void CelestiaCore::deleteView(View* v) void CelestiaCore::deleteView(View* v)
{ {
if (v == nullptr) if (v == nullptr)
v = (*activeView); v = *activeView;
if (v->parent == nullptr) return; if (v->isRootView())
return;
//Erase view and parent view from views //Erase view and parent view from views
list<View*>::iterator i = views.begin(); for (auto i = views.begin(); i != views.end(); )
while(i != views.end())
{ {
if ((*i == v) || (*i == v->parent)) if ((*i == v) || (*i == v->parent))
i=views.erase(i); i = views.erase(i);
else else
i++; ++i;
} }
int sign; sim->removeObserver(v->getObserver());
View *sibling; delete(v->getObserver());
if (v->parent->child1 == v) auto sibling = View::remove(v);
{
sibling = v->parent->child2;
sign = -1;
}
else
{
sibling = v->parent->child1;
sign = 1;
}
sibling->parent = v->parent->parent;
if (v->parent->parent != nullptr) {
if (v->parent->parent->child1 == v->parent)
v->parent->parent->child1 = sibling;
else
v->parent->parent->child2 = sibling;
}
v->walkTreeResize(sibling, sign);
sim->removeObserver(v->observer);
delete(v->observer);
View* nextActiveView = sibling; View* nextActiveView = sibling;
while (nextActiveView->type != View::ViewWindow) while (nextActiveView->type != View::ViewWindow)
nextActiveView = nextActiveView->child1; nextActiveView = nextActiveView->child1;
activeView = find(views.begin(),views.end(),nextActiveView); activeView = find(views.begin(), views.end(), nextActiveView);
sim->setActiveObserver((*activeView)->observer); sim->setActiveObserver((*activeView)->observer);
delete(v->parent);
delete(v);
if (!showActiveViewFrame) if (!showActiveViewFrame)
flashFrameStart = currentTime; flashFrameStart = currentTime;
setFOVFromZoom(); setFOVFromZoom();
@ -3260,66 +3039,12 @@ static void displaySelectionName(Overlay& overlay,
#endif #endif
void CelestiaCore::setScriptImage(float duration, void CelestiaCore::setScriptImage(std::unique_ptr<OverlayImage> &&_image)
float xoffset,
float yoffset,
float alpha,
const fs::path& filename,
bool fitscreen)
{ {
if (image == nullptr || image->isNewImage(filename)) image = std::move(_image);
{
delete image;
image = new CelestiaCore::OverlayImage(filename, overlay);
}
image->setStartTime((float) currentTime); image->setStartTime((float) currentTime);
image->setDuration(duration);
image->setOffset(xoffset, yoffset);
image->setAlpha(alpha);
image->fitScreen(fitscreen);
} }
CelestiaCore::OverlayImage::OverlayImage(fs::path f, Overlay* o) :
filename(std::move(f)),
overlay(o)
{
texture = LoadTextureFromFile(fs::path("images") / filename);
}
void CelestiaCore::OverlayImage::render(float curr_time, int width, int height)
{
if (texture == nullptr || (curr_time >= start + duration))
return;
float xSize = texture->getWidth();
float ySize = texture->getHeight();
// center overlay image horizontally if offsetX = 0
float left = (width * (1 + offsetX) - xSize)/2;
// center overlay image vertically if offsetY = 0
float bottom = (height * (1 + offsetY) - ySize)/2;
if (fitscreen)
{
float coeffx = xSize / width; // overlay pict width/view window width ratio
float coeffy = ySize / height; // overlay pict height/view window height ratio
xSize = xSize / coeffx; // new overlay picture width size to fit viewport
ySize = ySize / coeffy; // new overlay picture height to fit viewport
left = (width - xSize) / 2; // to be sure overlay pict is centered in viewport
bottom = 0; // overlay pict locked at bottom of screen
}
glEnable(GL_TEXTURE_2D);
texture->bind();
Overlay::Rectangle r(left, bottom, xSize, ySize, {Color::White, alpha}, Overlay::RectType::Textured);
overlay->rect(r);
}
void CelestiaCore::renderOverlay() void CelestiaCore::renderOverlay()
{ {
#ifdef CELX #ifdef CELX
@ -3343,51 +3068,34 @@ void CelestiaCore::renderOverlay()
if (runningScript) if (runningScript)
{ {
#endif #endif
if (image) if (image != nullptr)
image->render((float) currentTime, width, height); image->render((float) currentTime, width, height);
} }
if (views.size() > 1) if (views.size() > 1)
{ {
Overlay::Rectangle r(0, 0, 0, 0, frameColor, Overlay::RectType::Outlined, 1);
// Render a thin border arround all views // Render a thin border arround all views
if (showViewFrames || resizeSplit) if (showViewFrames || resizeSplit)
{ {
for(const auto v : views) for(const auto v : views)
{ {
if (v->type == View::ViewWindow) if (v->type == View::ViewWindow)
{ v->drawBorder(width, height, frameColor);
r.x = v->x * width;
r.y = v->y * height;
r.w = v->width * width - 1;
r.h = v->height * height - 1;
overlay->rect(r);
}
} }
} }
// Render a very simple border around the active view // Render a very simple border around the active view
View* av = *activeView; View* av = *activeView;
r.x = av->x * width;
r.y = av->y * height;
r.w = av->width * width - 1;
r.h = av->height * height - 1;
if (showActiveViewFrame) if (showActiveViewFrame)
{ {
r.colors[0] = activeFrameColor; av->drawBorder(width, height, activeFrameColor, 2);
r.lw = 2;
overlay->rect(r);
} }
if (currentTime < flashFrameStart + 0.5) if (currentTime < flashFrameStart + 0.5)
{ {
float alpha = (float) (1.0 - (currentTime - flashFrameStart) / 0.5); float alpha = (float) (1.0 - (currentTime - flashFrameStart) / 0.5);
r.colors[0] = {activeFrameColor, alpha}; av->drawBorder(width, height, {activeFrameColor, alpha}, 8);
r.lw = 8;
overlay->rect(r);
} }
} }
@ -3415,20 +3123,18 @@ void CelestiaCore::renderOverlay()
if (dateWidth > dateStrWidth) dateStrWidth = dateWidth; if (dateWidth > dateStrWidth) dateStrWidth = dateWidth;
// Time and date // Time and date
glPushMatrix(); overlay->savePos();
glColor4f(0.7f, 0.7f, 1.0f, 1.0f); overlay->setColor(0.7f, 0.7f, 1.0f, 1.0f);
glTranslatef( (float) (width - dateStrWidth), overlay->moveBy(width - dateStrWidth, height - fontHeight);
(float) (height - fontHeight),
0.0f);
overlay->beginText(); overlay->beginText();
overlay->print(dateStr); overlay->print(dateStr);
if (lightTravelFlag && lt > 0.0) if (lightTravelFlag && lt > 0.0)
{ {
glColor4f(0.42f, 1.0f, 1.0f, 1.0f); overlay->setColor(0.42f, 1.0f, 1.0f, 1.0f);
*overlay << _(" LT"); *overlay << _(" LT");
glColor4f(0.7f, 0.7f, 1.0f, 1.0f); overlay->setColor(0.7f, 0.7f, 1.0f, 1.0f);
} }
*overlay << '\n'; *overlay << '\n';
@ -3455,21 +3161,21 @@ void CelestiaCore::renderOverlay()
if (sim->getPauseState() == true) if (sim->getPauseState() == true)
{ {
glColor4f(1.0f, 0.0f, 0.0f, 1.0f); overlay->setColor(1.0f, 0.0f, 0.0f, 1.0f);
*overlay << _(" (Paused)"); *overlay << _(" (Paused)");
} }
} }
overlay->endText(); overlay->endText();
glPopMatrix(); overlay->restorePos();
} }
if (hudDetail > 0 && (overlayElements & ShowVelocity)) if (hudDetail > 0 && (overlayElements & ShowVelocity))
{ {
// Speed // Speed
glPushMatrix(); overlay->savePos();
glTranslatef(0.0f, (float) (fontHeight * 2 + 5), 0.0f); overlay->moveBy(0.0f, fontHeight * 2 + 5);
glColor4f(0.7f, 0.7f, 1.0f, 1.0f); overlay->setColor(0.7f, 0.7f, 1.0f, 1.0f);
overlay->beginText(); overlay->beginText();
*overlay << '\n'; *overlay << '\n';
@ -3492,7 +3198,7 @@ void CelestiaCore::renderOverlay()
displaySpeed(*overlay, sim->getObserver().getVelocity().norm()); displaySpeed(*overlay, sim->getObserver().getVelocity().norm());
overlay->endText(); overlay->endText();
glPopMatrix(); overlay->restorePos();
} }
Universe *u = sim->getUniverse(); Universe *u = sim->getUniverse();
@ -3500,11 +3206,10 @@ void CelestiaCore::renderOverlay()
if (hudDetail > 0 && (overlayElements & ShowFrame)) if (hudDetail > 0 && (overlayElements & ShowFrame))
{ {
// Field of view and camera mode in lower right corner // Field of view and camera mode in lower right corner
glPushMatrix(); overlay->savePos();
glTranslatef((float) (width - emWidth * 15), overlay->moveBy(width - emWidth * 15, fontHeight * 3 + 5);
(float) (fontHeight * 3 + 5), 0.0f);
overlay->beginText(); overlay->beginText();
glColor4f(0.6f, 0.6f, 1.0f, 1); overlay->setColor(0.6f, 0.6f, 1.0f, 1);
if (sim->getObserverMode() == Observer::Travelling) if (sim->getObserverMode() == Observer::Travelling)
{ {
@ -3562,23 +3267,23 @@ void CelestiaCore::renderOverlay()
} }
} }
glColor4f(0.7f, 0.7f, 1.0f, 1.0f); overlay->setColor(0.7f, 0.7f, 1.0f, 1.0f);
// Field of view // Field of view
float fov = radToDeg(sim->getActiveObserver()->getFOV()); float fov = radToDeg(sim->getActiveObserver()->getFOV());
fmt::fprintf(*overlay, _("FOV: %s (%.2fx)\n"), fmt::fprintf(*overlay, _("FOV: %s (%.2fx)\n"),
angleToStr(fov), (*activeView)->zoom); angleToStr(fov), (*activeView)->zoom);
overlay->endText(); overlay->endText();
glPopMatrix(); overlay->restorePos();
} }
// Selection info // Selection info
Selection sel = sim->getSelection(); Selection sel = sim->getSelection();
if (!sel.empty() && hudDetail > 0 && (overlayElements & ShowSelection)) if (!sel.empty() && hudDetail > 0 && (overlayElements & ShowSelection))
{ {
glPushMatrix(); overlay->savePos();
glColor4f(0.7f, 0.7f, 1.0f, 1.0f); overlay->setColor(0.7f, 0.7f, 1.0f, 1.0f);
glTranslatef(0.0f, (float) (height - titleFont->getHeight()), 0.0f); overlay->moveBy(0.0f, height - titleFont->getHeight());
overlay->beginText(); overlay->beginText();
Vector3d v = sel.getPosition(sim->getTime()).offsetFromKm(sim->getObserver().getPosition()); Vector3d v = sel.getPosition(sim->getTime()).offsetFromKm(sim->getObserver().getPosition());
@ -3608,9 +3313,8 @@ void CelestiaCore::renderOverlay()
} }
overlay->setFont(titleFont); overlay->setFont(titleFont);
*overlay << selectionNames; *overlay << selectionNames << '\n';
overlay->setFont(font); overlay->setFont(font);
*overlay << '\n';
displayStarInfo(*overlay, displayStarInfo(*overlay,
hudDetail, hudDetail,
*(sel.star()), *(sel.star()),
@ -3636,9 +3340,8 @@ void CelestiaCore::renderOverlay()
} }
overlay->setFont(titleFont); overlay->setFont(titleFont);
*overlay << selectionNames; *overlay << selectionNames << '\n';
overlay->setFont(font); overlay->setFont(font);
*overlay << '\n';
displayDSOinfo(*overlay, displayDSOinfo(*overlay,
*sel.deepsky(), *sel.deepsky(),
astro::kilometersToLightYears(v.norm()) - sel.deepsky()->getRadius()); astro::kilometersToLightYears(v.norm()) - sel.deepsky()->getRadius());
@ -3656,11 +3359,11 @@ void CelestiaCore::renderOverlay()
// Skip displaying the primary name if there's a localized version // Skip displaying the primary name if there's a localized version
// of the name. // of the name.
vector<string>::const_iterator firstName = names.begin(); auto firstName = names.begin();
if (sel.body()->hasLocalizedName()) if (sel.body()->hasLocalizedName())
firstName++; ++firstName;
for (vector<string>::const_iterator iter = firstName; iter != names.end(); iter++) for (auto iter = firstName; iter != names.end(); ++iter)
{ {
if (iter != firstName) if (iter != firstName)
selectionNames += " / "; selectionNames += " / ";
@ -3699,9 +3402,7 @@ void CelestiaCore::renderOverlay()
*overlay << sel.location()->getName(true).c_str(); *overlay << sel.location()->getName(true).c_str();
overlay->setFont(font); overlay->setFont(font);
*overlay << '\n'; *overlay << '\n';
displayLocationInfo(*overlay, displayLocationInfo(*overlay, *(sel.location()), v.norm());
*(sel.location()),
v.norm());
break; break;
default: default:
@ -3759,18 +3460,19 @@ void CelestiaCore::renderOverlay()
overlay->endText(); overlay->endText();
glPopMatrix(); overlay->restorePos();
} }
// Text input // Text input
if (textEnterMode & KbAutoComplete) if (textEnterMode & KbAutoComplete)
{ {
overlay->setFont(titleFont); overlay->setFont(titleFont);
glPushMatrix(); overlay->savePos();
Overlay::Rectangle r(0, 0, width, 100, consoleColor, Overlay::RectType::Filled); Rect r(0, 0, width, 100);
overlay->rect(r); r.setColor(consoleColor);
glTranslatef(0.0f, fontHeight * 3.0f + 35.0f, 0.0f); overlay->drawRectangle(r);
glColor4f(0.6f, 0.6f, 1.0f, 1.0f); overlay->moveBy(0.0f, fontHeight * 3.0f + 35.0f);
overlay->setColor(0.6f, 0.6f, 1.0f, 1.0f);
overlay->beginText(); overlay->beginText();
fmt::fprintf(*overlay, _("Target name: %s"), typedText); fmt::fprintf(*overlay, _("Target name: %s"), typedText);
overlay->endText(); overlay->endText();
@ -3780,7 +3482,7 @@ void CelestiaCore::renderOverlay()
int nb_cols = 4; int nb_cols = 4;
int nb_lines = 3; int nb_lines = 3;
int start = 0; int start = 0;
glTranslatef(3.0f, -font->getHeight() - 3.0f, 0.0f); overlay->moveBy(3.0f, -font->getHeight() - 3.0f);
vector<std::string>::const_iterator iter = typedTextCompletion.begin(); vector<std::string>::const_iterator iter = typedTextCompletion.begin();
if (typedTextCompletionIdx >= nb_cols * nb_lines) if (typedTextCompletionIdx >= nb_cols * nb_lines)
{ {
@ -3789,22 +3491,22 @@ void CelestiaCore::renderOverlay()
} }
for (int i=0; iter < typedTextCompletion.end() && i < nb_cols; i++) for (int i=0; iter < typedTextCompletion.end() && i < nb_cols; i++)
{ {
glPushMatrix(); overlay->savePos();
overlay->beginText(); overlay->beginText();
for (int j = 0; iter < typedTextCompletion.end() && j < nb_lines; iter++, j++) for (int j = 0; iter < typedTextCompletion.end() && j < nb_lines; iter++, j++)
{ {
if (i * nb_lines + j == typedTextCompletionIdx - start) if (i * nb_lines + j == typedTextCompletionIdx - start)
glColor4f(1.0f, 0.6f, 0.6f, 1); overlay->setColor(1.0f, 0.6f, 0.6f, 1);
else else
glColor4f(0.6f, 0.6f, 1.0f, 1); overlay->setColor(0.6f, 0.6f, 1.0f, 1);
*overlay << *iter << "\n"; *overlay << *iter << "\n";
} }
overlay->endText(); overlay->endText();
glPopMatrix(); overlay->restorePos();
glTranslatef((float) (width/nb_cols), 0.0f, 0.0f); overlay->moveBy((float) (width/nb_cols), 0.0f, 0.0f);
} }
} }
glPopMatrix(); overlay->restorePos();
overlay->setFont(font); overlay->setFont(font);
} }
@ -3828,17 +3530,17 @@ void CelestiaCore::renderOverlay()
y -= fontHeight; y -= fontHeight;
overlay->setFont(titleFont); overlay->setFont(titleFont);
glPushMatrix(); overlay->savePos();
float alpha = 1.0f; float alpha = 1.0f;
if (currentTime > messageStart + messageDuration - 0.5) if (currentTime > messageStart + messageDuration - 0.5)
alpha = (float) ((messageStart + messageDuration - currentTime) / 0.5); alpha = (float) ((messageStart + messageDuration - currentTime) / 0.5);
glColor4f(textColor.red(), textColor.green(), textColor.blue(), alpha); overlay->setColor(textColor.red(), textColor.green(), textColor.blue(), alpha);
glTranslatef((float) x, (float) y, 0.0f); overlay->moveBy(x, y);
overlay->beginText(); overlay->beginText();
*overlay << messageText; *overlay << messageText;
overlay->endText(); overlay->endText();
glPopMatrix(); overlay->restorePos();
overlay->setFont(font); overlay->setFont(font);
} }
@ -3846,18 +3548,18 @@ void CelestiaCore::renderOverlay()
{ {
int movieWidth = movieCapture->getWidth(); int movieWidth = movieCapture->getWidth();
int movieHeight = movieCapture->getHeight(); int movieHeight = movieCapture->getHeight();
glPushMatrix(); overlay->savePos();
Color color(1, 0, 0, 1); Color color(1.0f, 0.0f, 0.0f, 1.0f);
glColor(color); overlay->setColor(color);
Overlay::Rectangle r((width - movieWidth) / 2 - 1, Rect r((width - movieWidth) / 2 - 1,
(height - movieHeight) / 2 - 1, (height - movieHeight) / 2 - 1,
movieWidth + 1, movieWidth + 1,
movieHeight + 1, movieHeight + 1);
color, r.setColor(color);
Overlay::RectType::Outlined); r.setType(Rect::Type::BorderOnly);
overlay->rect(r); overlay->drawRectangle(r);
glTranslatef((float) ((width - movieWidth) / 2), overlay->moveBy((float) ((width - movieWidth) / 2),
(float) ((height + movieHeight) / 2 + 2), 0.0f); (float) ((height + movieHeight) / 2 + 2));
overlay->beginText(); overlay->beginText();
fmt::fprintf(*overlay, _("%dx%d at %f fps %s"), fmt::fprintf(*overlay, _("%dx%d at %f fps %s"),
movieWidth, movieHeight, movieWidth, movieHeight,
@ -3865,12 +3567,11 @@ void CelestiaCore::renderOverlay()
recording ? _("Recording") : _("Paused")); recording ? _("Recording") : _("Paused"));
overlay->endText(); overlay->endText();
glPopMatrix(); overlay->restorePos();
glPushMatrix(); overlay->savePos();
glTranslatef((float) ((width + movieWidth) / 2 - emWidth * 5), overlay->moveBy((float) ((width + movieWidth) / 2 - emWidth * 5),
(float) ((height + movieHeight) / 2 + 2), (float) ((height + movieHeight) / 2 + 2));
0.0f);
float sec = movieCapture->getFrameCount() / float sec = movieCapture->getFrameCount() /
movieCapture->getFrameRate(); movieCapture->getFrameRate();
auto min = (int) (sec / 60); auto min = (int) (sec / 60);
@ -3878,28 +3579,27 @@ void CelestiaCore::renderOverlay()
overlay->beginText(); overlay->beginText();
fmt::fprintf(*overlay, "%3d:%05.2f", min, sec); fmt::fprintf(*overlay, "%3d:%05.2f", min, sec);
overlay->endText(); overlay->endText();
glPopMatrix(); overlay->restorePos();
glPushMatrix(); overlay->savePos();
glTranslatef((float) ((width - movieWidth) / 2), overlay->moveBy((float) ((width - movieWidth) / 2),
(float) ((height - movieHeight) / 2 - fontHeight - 2), (float) ((height - movieHeight) / 2 - fontHeight - 2));
0.0f);
overlay->beginText(); overlay->beginText();
*overlay << _("F11 Start/Pause F12 Stop"); *overlay << _("F11 Start/Pause F12 Stop");
overlay->endText(); overlay->endText();
glPopMatrix(); overlay->restorePos();
glPopMatrix(); overlay->restorePos();
} }
if (editMode) if (editMode)
{ {
glPushMatrix(); overlay->savePos();
glTranslatef((float) ((width - font->getWidth(_("Edit Mode"))) / 2), overlay->moveBy((float) ((width - font->getWidth(_("Edit Mode"))) / 2),
(float) (height - fontHeight), 0.0f); (float) (height - fontHeight));
glColor4f(1, 0, 1, 1); overlay->setColor(1, 0, 1, 1);
*overlay << _("Edit Mode"); *overlay << _("Edit Mode");
glPopMatrix(); overlay->restorePos();
} }
// Show logo at start // Show logo at start
@ -4213,10 +3913,12 @@ bool CelestiaCore::initSimulation(const fs::path& configFileName,
} }
sim = new Simulation(universe); sim = new Simulation(universe);
if((renderer->getRenderFlags() & Renderer::ShowAutoMag) == 0) if ((renderer->getRenderFlags() & Renderer::ShowAutoMag) == 0)
sim->setFaintestVisible(config->faintestVisible); {
sim->setFaintestVisible(config->faintestVisible);
}
View* view = new View(View::ViewWindow, sim->getActiveObserver(), 0.0f, 0.0f, 1.0f, 1.0f); View* view = new View(View::ViewWindow, renderer, sim->getActiveObserver(), 0.0f, 0.0f, 1.0f, 1.0f);
views.push_back(view); views.push_back(view);
activeView = views.begin(); activeView = views.begin();

View File

@ -21,11 +21,13 @@
#include <celengine/universe.h> #include <celengine/universe.h>
#include <celengine/render.h> #include <celengine/render.h>
#include <celengine/simulation.h> #include <celengine/simulation.h>
#include <celengine/overlayimage.h>
#include <GL/glew.h> #include <GL/glew.h>
#include "configfile.h" #include "configfile.h"
#include "favorites.h" #include "favorites.h"
#include "destination.h" #include "destination.h"
#include "moviecapture.h" #include "moviecapture.h"
#include "view.h"
#ifdef CELX #ifdef CELX
#include "celx.h" #include "celx.h"
#endif #endif
@ -47,40 +49,6 @@ public:
virtual void update(const std::string&) = 0; virtual void update(const std::string&) = 0;
}; };
class View
{
public:
enum Type {
ViewWindow = 1,
HorizontalSplit = 2,
VerticalSplit = 3
};
View(Type, Observer*, float, float, float, float);
void mapWindowToView(float, float, float&, float&) const;
public:
Type type;
Observer* observer;
View *parent;
View *child1;
View *child2;
float x;
float y;
float width;
float height;
uint64_t renderFlags;
int labelMode;
float zoom;
float alternateZoom;
void walkTreeResize(View*, int);
bool walkTreeResizeDelta(View*, float, bool);
};
class CelestiaCore // : public Watchable<CelestiaCore> class CelestiaCore // : public Watchable<CelestiaCore>
{ {
public: public:
@ -197,37 +165,6 @@ class CelestiaCore // : public Watchable<CelestiaCore>
ShowFrame = 0x010, ShowFrame = 0x010,
}; };
private:
class OverlayImage
{
public:
OverlayImage(fs::path, Overlay*);
~OverlayImage() { delete texture; }
OverlayImage() =default;
OverlayImage(OverlayImage&) =delete;
OverlayImage(OverlayImage&&) =delete;
void render(float, int, int);
inline bool isNewImage(const fs::path& f) { return filename != f; }
void setStartTime(float t) { start = t; }
void setDuration(float t) { duration = t; }
void setOffset(float x, float y) { offsetX = x; offsetY = y; }
void setAlpha(float t) { alpha = t; }
void fitScreen(bool t) { fitscreen = t; }
private:
float start{ 0.0f };
float duration{ 0.0f };
float offsetX{ 0.0f };
float offsetY{ 0.0f };
float alpha{ 0.0f };
bool fitscreen{ false };
fs::path filename;
Texture* texture{ nullptr };
Overlay* overlay;
};
public: public:
CelestiaCore(); CelestiaCore();
~CelestiaCore(); ~CelestiaCore();
@ -383,7 +320,7 @@ class CelestiaCore // : public Watchable<CelestiaCore>
void fatalError(const std::string&, bool visual = true); void fatalError(const std::string&, bool visual = true);
void setScriptImage(float, float, float, float, const fs::path&, bool); void setScriptImage(std::unique_ptr<OverlayImage>&&);
const std::string& getTypedText() const { return typedText; } const std::string& getTypedText() const { return typedText; }
void setTypedText(const char *); void setTypedText(const char *);
@ -425,7 +362,7 @@ class CelestiaCore // : public Watchable<CelestiaCore>
const Color activeFrameColor{ 0.5f, 0.5f, 1.0f, 1.0f }; const Color activeFrameColor{ 0.5f, 0.5f, 1.0f, 1.0f };
const Color consoleColor{ 0.7f, 0.7f, 1.0f, 0.2f }; const Color consoleColor{ 0.7f, 0.7f, 1.0f, 0.2f };
OverlayImage *image{ nullptr }; std::unique_ptr<OverlayImage> image;
std::string typedText; std::string typedText;
std::vector<std::string> typedTextCompletion; std::vector<std::string> typedTextCompletion;

View File

@ -28,6 +28,7 @@
#include "url.h" #include "url.h"
#include "imagecapture.h" #include "imagecapture.h"
#include "celestiacore.h" #include "celestiacore.h"
#include "view.h"
using namespace Eigen; using namespace Eigen;
@ -282,7 +283,7 @@ int celestia_getscreendimension(lua_State* l)
// Get the dimensions of the current viewport // Get the dimensions of the current viewport
int w, h; int w, h;
CelestiaCore* appCore = to_celestia(l, 1); CelestiaCore* appCore = to_celestia(l, 1);
appCore->getRenderer()->getScreenSize(nullptr, nullptr, &w, &h); appCore->getRenderer()->getViewport(nullptr, nullptr, &w, &h);
lua_pushnumber(l, w); lua_pushnumber(l, w);
lua_pushnumber(l, h); lua_pushnumber(l, h);
return 2; return 2;
@ -1916,7 +1917,7 @@ static int celestia_takescreenshot(lua_State* l)
// Get the dimensions of the current viewport // Get the dimensions of the current viewport
array<GLint, 4> viewport; array<GLint, 4> viewport;
appCore->getRenderer()->getScreenSize(viewport); appCore->getRenderer()->getViewport(viewport);
fs::path path = appCore->getConfig()->scriptScreenshotDirectory; fs::path path = appCore->getConfig()->scriptScreenshotDirectory;
@ -2081,7 +2082,14 @@ static int celestia_overlay(lua_State* l)
else else
fitscreen = (bool) Celx_SafeGetNumber(l, 7, WrongType, "Sixth argument to celestia:overlay must be a number or a boolean(fitscreen)", 0); fitscreen = (bool) Celx_SafeGetNumber(l, 7, WrongType, "Sixth argument to celestia:overlay must be a number or a boolean(fitscreen)", 0);
appCore->setScriptImage(duration, xoffset, yoffset, alpha, filename, fitscreen); auto image = unique_ptr<OverlayImage>(new OverlayImage(filename, appCore->getRenderer()));
image->setDuration(duration);
image->setFadeAfter(duration); // FIXME
image->setOffset(xoffset, yoffset);
image->setColor({Color::White, alpha}); // FIXME
image->fitScreen(fitscreen);
appCore->setScriptImage(std::move(image));
return 0; return 0;
} }

View File

@ -773,39 +773,57 @@ Command* CommandParser::parseCommand()
} }
else if (commandName == "overlay") else if (commandName == "overlay")
{ {
float duration; float duration = 3.0f;
float xoffset; float fadeafter;
float yoffset; float xoffset = 0.0f;
float alpha; float yoffset = 0.0f;
float alpha = 1.0f;
bool hasAlpha = true;
string filename; string filename;
bool fitscreen; bool fitscreen = false;
Color color(Color::White);
if(!paramList->getNumber("duration", duration)) paramList->getNumber("duration", duration);
duration = 3; paramList->getNumber("xoffset", xoffset);
if(!paramList->getNumber("xoffset", xoffset)) paramList->getNumber("yoffset", yoffset);
xoffset = 0.0; if (paramList->getNumber("alpha", alpha))
if(!paramList->getNumber("yoffset", yoffset)) hasAlpha = true;
yoffset = 0.0; paramList->getString("filename", filename);
if(!paramList->getNumber("alpha", alpha))
alpha = 1; if (!paramList->getBoolean("fitscreen", fitscreen))
if(!paramList->getString("filename", filename))
filename = "";
if(!paramList->getBoolean("fitscreen", fitscreen))
{ {
int f; int f;
if(!paramList->getNumber("fitscreen", f)) // backward compatibility with celestia ed implementation
fitscreen = false; if (paramList->getNumber("fitscreen", f))
else fitscreen = (bool) f;
fitscreen = (bool) f;
} }
cmd = new CommandScriptImage(duration, xoffset, yoffset, alpha, filename, fitscreen); array<Color, 4> colors;
paramList->getColor("color", color);
colors.fill(hasAlpha ? Color(color, alpha) : color);
if (paramList->getColor("colortop", color))
colors[0] = colors[1] = hasAlpha ? Color(color, alpha) : color;
if (paramList->getColor("colorbottom", color))
colors[2] = colors[3] = hasAlpha ? Color(color, alpha) : color;
if (paramList->getColor("colortopleft", color))
colors[0] = hasAlpha ? Color(color, alpha) : color;
if (paramList->getColor("colortopright", color))
colors[1] = hasAlpha ? Color(color, alpha) : color;
if (paramList->getColor("colorbottomright", color))
colors[2] = hasAlpha ? Color(color, alpha) : color;
if (paramList->getColor("colorbottomleft", color))
colors[3] = hasAlpha ? Color(color, alpha) : color;
if (!paramList->getNumber("fadeafter", fadeafter))
fadeafter = duration;
cmd = new CommandScriptImage(duration, fadeafter, xoffset, yoffset, filename, fitscreen, colors);
} }
else if (commandName == "verbosity") else if (commandName == "verbosity")
{ {
int level; int level;
if(!paramList->getNumber("level", level)) if (!paramList->getNumber("level", level))
level = 2; level = 2;
cmd = new CommandVerbosity(level); cmd = new CommandVerbosity(level);

View File

@ -649,7 +649,7 @@ void CommandCapture::process(ExecutionEnvironment& env)
// Get the dimensions of the current viewport // Get the dimensions of the current viewport
array<int, 4> viewport; array<int, 4> viewport;
r->getScreenSize(viewport); r->getViewport(viewport);
if (compareIgnoringCase(type, "jpeg") == 0) if (compareIgnoringCase(type, "jpeg") == 0)
{ {
@ -952,21 +952,30 @@ double RepeatCommand::getDuration() const
} }
// ScriptImage command // ScriptImage command
CommandScriptImage::CommandScriptImage(float _duration, float _xoffset, CommandScriptImage::CommandScriptImage(float _duration, float _fadeafter,
float _yoffset, float _alpha, float _xoffset, float _yoffset,
std::string _filename, bool _fitscreen) : fs::path _filename,
bool _fitscreen,
array<Color,4> &_colors) :
duration(_duration), duration(_duration),
fadeafter(_fadeafter),
xoffset(_xoffset), xoffset(_xoffset),
yoffset(_yoffset), yoffset(_yoffset),
alpha(_alpha),
filename(std::move(_filename)), filename(std::move(_filename)),
fitscreen(_fitscreen) fitscreen(_fitscreen)
{ {
copy(_colors.begin(), _colors.end(), colors.begin());
} }
void CommandScriptImage::process(ExecutionEnvironment& env) void CommandScriptImage::process(ExecutionEnvironment& env)
{ {
env.getCelestiaCore()->setScriptImage(duration, xoffset, yoffset, alpha, filename, fitscreen); auto image = unique_ptr<OverlayImage>(new OverlayImage(filename, env.getRenderer()));
image->setDuration(duration);
image->setFadeAfter(fadeafter);
image->setOffset(xoffset, yoffset);
image->setColor(colors);
image->fitScreen(fitscreen);
env.getCelestiaCore()->setScriptImage(std::move(image));
} }
// Verbosity command // Verbosity command

View File

@ -12,6 +12,7 @@
#define MAX_CONSTELLATIONS 100 #define MAX_CONSTELLATIONS 100
#include <array>
#include <iostream> #include <iostream>
#include <celengine/execenv.h> #include <celengine/execenv.h>
#include <celengine/astro.h> #include <celengine/astro.h>
@ -716,17 +717,17 @@ class RepeatCommand : public Command
class CommandScriptImage : public InstantaneousCommand class CommandScriptImage : public InstantaneousCommand
{ {
public: public:
CommandScriptImage(float _duration, float _xoffset, float _yoffset, CommandScriptImage(float, float, float, float, fs::path, bool, std::array<Color, 4>&);
float _alpha, std::string, bool _fitscreen);
void process(ExecutionEnvironment&); void process(ExecutionEnvironment&);
private: private:
double duration; float duration;
float fadeafter;
float xoffset; float xoffset;
float yoffset; float yoffset;
float alpha; fs::path filename;
std::string filename;
int fitscreen; int fitscreen;
std::array<Color, 4> colors;
}; };
class CommandVerbosity : public InstantaneousCommand class CommandVerbosity : public InstantaneousCommand

View File

@ -1124,7 +1124,7 @@ static void captureImage(const char* filename, AppData* app)
{ {
/* Get the dimensions of the current viewport */ /* Get the dimensions of the current viewport */
array<int, 4> viewport; array<int, 4> viewport;
app->renderer->getScreenSize(viewport); app->renderer->getViewport(viewport);
bool success = false; bool success = false;
ContentType type = DetermineFileType(filename); ContentType type = DetermineFileType(filename);
@ -1183,7 +1183,7 @@ static void captureMovie(const char* filename, int aspect, float fps, float qual
{ {
/* Get the dimensions of the current viewport */ /* Get the dimensions of the current viewport */
array<int, 4> viewport; array<int, 4> viewport;
app->renderer->getScreenSize(viewport); app->renderer->getViewport(viewport);
MovieCapture* movieCapture = new OggTheoraCapture(app->renderer); MovieCapture* movieCapture = new OggTheoraCapture(app->renderer);
switch (aspect) switch (aspect)

View File

@ -338,7 +338,7 @@ bool OggTheoraCapture::captureFrame()
// Get the dimensions of the current viewport // Get the dimensions of the current viewport
int x, y, w, h; int x, y, w, h;
renderer->getScreenSize(&x, &y, &w, &h); renderer->getViewport(&x, &y, &w, &h);
x += (w - frame_x) / 2; x += (w - frame_x) / 2;
y += (h - frame_y) / 2; y += (h - frame_y) / 2;

View File

@ -0,0 +1,266 @@
// view.cpp
//
// Copyright (C) 2001-2019, Celestia Development Team
//
// 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 <celengine/rectangle.h>
#include <celengine/render.h>
#include <celutil/color.h>
#include "view.h"
#include<iostream>
View::View(View::Type _type,
Renderer *_renderer,
Observer *_observer,
float _x, float _y,
float _width, float _height) :
type(_type),
renderer(_renderer),
observer(_observer),
x(_x),
y(_y),
width(_width),
height(_height)
{
}
void View::mapWindowToView(float wx, float wy, float& vx, float& vy) const
{
vx = (wx - x) / width;
vy = (wy + (y + height - 1)) / height;
vx = (vx - 0.5f) * (width / height);
vy = 0.5f - vy;
}
void View::walkTreeResize(View* sibling, int sign)
{
float ratio;
switch (parent->type)
{
case View::HorizontalSplit:
ratio = parent->height / (parent->height - height);
sibling->height *= ratio;
if (sign == 1)
{
sibling->y = parent->y + (sibling->y - parent->y) * ratio;
}
else
{
sibling->y = parent->y + (sibling->y - (y + height)) * ratio;
}
break;
case View::VerticalSplit:
ratio = parent->width / (parent->width - width);
sibling->width *= ratio;
if (sign == 1)
{
sibling->x = parent->x + (sibling->x - parent->x) * ratio;
}
else
{
sibling->x = parent->x + (sibling->x - (x + width) ) * ratio;
}
break;
case View::ViewWindow:
break;
}
if (sibling->child1 != nullptr)
walkTreeResize(sibling->child1, sign);
if (sibling->child2 != nullptr)
walkTreeResize(sibling->child2, sign);
}
bool View::walkTreeResizeDelta(View* v, float delta, bool check)
{
View *p = v;
int sign = -1;
float ratio;
double newSize;
if (v->child1 != nullptr)
{
if (!walkTreeResizeDelta(v->child1, delta, check))
return false;
}
if (v->child2 != nullptr)
{
if (!walkTreeResizeDelta(v->child2, delta, check))
return false;
}
while ( p != child1 && p != child2 && (p = p->parent) != nullptr ) ;
if (p == child1)
sign = 1;
switch (type)
{
case View::HorizontalSplit:
delta = -delta;
ratio = (p->height + sign * delta) / p->height;
newSize = v->height * ratio;
if (newSize <= .1)
return false;
if (check)
return true;
v->height = (float) newSize;
if (sign == 1)
{
v->y = p->y + (v->y - p->y) * ratio;
}
else
{
v->y = p->y + delta + (v->y - p->y) * ratio;
}
break;
case View::VerticalSplit:
ratio = (p->width + sign * delta) / p->width;
newSize = v->width * ratio;
if (newSize <= .1)
return false;
if (check)
return true;
v->width = (float) newSize;
if (sign == 1)
{
v->x = p->x + (v->x - p->x) * ratio;
}
else
{
v->x = p->x + delta + (v->x - p->x) * ratio;
}
break;
case View::ViewWindow:
break;
}
return true;
}
Observer* View::getObserver() const
{
return observer;
}
void View::switchTo(int gWidth, int gHeight)
{
renderer->setRenderRegion(int(x * gWidth),
int(y * gHeight),
int(width * gWidth),
int(height * gHeight));
}
bool View::isSplittable(Type type) const
{
// If active view is too small, don't split it.
return (type == View::HorizontalSplit && height >= 0.2f) ||
(type == View::VerticalSplit && width >= 0.2f);
}
bool View::isRootView() const
{
return parent == nullptr;
}
void View::split(Type type, Observer *o, float splitPos, View **split, View **view)
{
float w1, h1, w2, h2, x1, y1;
w1 = w2 = width;
h1 = h2 = height;
x1 = x;
y1 = y;
if (type == View::VerticalSplit)
{
w1 *= splitPos;
w2 -= w1;
x1 += w1;
}
else
{
h1 *= splitPos;
h2 -= h1;
y1 += h1;
}
*split = new View(type,
renderer,
nullptr,
x, y, width, height);
(*split)->parent = parent;
if (parent != nullptr)
{
if (parent->child1 == this)
parent->child1 = *split;
else
parent->child2 = *split;
}
(*split)->child1 = this;
width = w1;
height = h1;
parent = *split;
*view = new View(View::ViewWindow,
renderer,
o,
x1, y1, w2, h2);
(*split)->child2 = *view;
(*view)->parent = *split;
(*view)->zoom = zoom;
}
View* View::remove(View* v)
{
int sign;
View *sibling;
if (v->parent->child1 == v)
{
sibling = v->parent->child2;
sign = -1;
}
else
{
sibling = v->parent->child1;
sign = 1;
}
sibling->parent = v->parent->parent;
if (v->parent->parent != nullptr)
{
if (v->parent->parent->child1 == v->parent)
v->parent->parent->child1 = sibling;
else
v->parent->parent->child2 = sibling;
}
v->walkTreeResize(sibling, sign);
delete(v->parent);
delete(v);
return sibling;
}
void View::reset()
{
x = 0.0f;
y = 0.0f;
width = 1.0f;
height = 1.0f;
parent = nullptr;
child1 = nullptr;
child2 = nullptr;
}
void View::drawBorder(int gWidth, int gHeight, const Color &color, float linewidth)
{
Rect r(x * gWidth, y * gHeight, width * gWidth - 1, height * gHeight - 1);
r.setColor(color);
r.setType(Rect::Type::BorderOnly);
r.setLineWidth(linewidth);
renderer->drawRectangle(r);
}

View File

@ -0,0 +1,68 @@
// view.h
//
// Copyright (C) 2001-2019, the Celestia Development Team
//
// 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.
#pragma once
class Renderer;
class Observer;
class Color;
//namespace celestia
//{
class View
{
public:
enum Type
{
ViewWindow = 1,
HorizontalSplit = 2,
VerticalSplit = 3
};
View(Type, Renderer*, Observer*, float, float, float, float);
View() = delete;
~View() = default;
View(const View&) = delete;
View(View&&) = delete;
const View& operator=(const View&) = delete;
View& operator=(View&&) = delete;
void mapWindowToView(float, float, float&, float&) const;
void walkTreeResize(View*, int);
bool walkTreeResizeDelta(View*, float, bool);
void switchTo(int gWidth, int gHeight);
Observer* getObserver() const;
bool isRootView() const;
bool isSplittable(Type type) const;
void split(Type type, Observer *o, float splitPos, View **split, View **view);
void reset();
static View* remove(View*);
void drawBorder(int gWidth, int gHeight, const Color &color, float linewidth = 1.0f);
public:
Type type;
Renderer *renderer;
Observer *observer;
View *parent { nullptr };
View *child1 { nullptr };
View *child2 { nullptr };
float x;
float y;
float width;
float height;
uint64_t renderFlags { 0 };
int labelMode { 0 };
float zoom { 1.0f };
float alternateZoom { 1.0f };
};
//}

View File

@ -2684,7 +2684,7 @@ static void HandleCaptureImage(HWND hWnd)
// Get the dimensions of the current viewport // Get the dimensions of the current viewport
array<int,4> viewport; array<int,4> viewport;
appCore->getRenderer()->getScreenSize(viewport); appCore->getRenderer()->getViewport(viewport);
bool success = false; bool success = false;

View File

@ -43,12 +43,30 @@ Color::Color(float r, float g, float b, float a)
} }
Color::Color(unsigned char r, unsigned char g, unsigned char b) Color::Color(unsigned char r, unsigned char g, unsigned char b) :
Color(r, g, b, 0xff)
{
}
Color::Color(unsigned char r, unsigned char g, unsigned char b, unsigned char a)
{ {
c[Red] = r; c[Red] = r;
c[Green] = g; c[Green] = g;
c[Blue] = b; c[Blue] = b;
c[Alpha] = 0xff; c[Alpha] = a;
}
Color::Color(const Eigen::Vector3f &c) :
Color(c.x(), c.y(), c.z())
{
}
Color::Color(const Eigen::Vector4f &c) :
Color(c.x(), c.y(), c.z(), c.w())
{
} }

View File

@ -22,9 +22,13 @@ class Color
Color(float, float, float); Color(float, float, float);
Color(float, float, float, float); Color(float, float, float, float);
Color(unsigned char, unsigned char, unsigned char); Color(unsigned char, unsigned char, unsigned char);
Color(unsigned char, unsigned char, unsigned char, unsigned char);
Color(const Color&, float); Color(const Color&, float);
Color(const Eigen::Vector3f&);
Color(const Eigen::Vector4f&);
enum { enum
{
Red = 0, Red = 0,
Green = 1, Green = 1,
Blue = 2, Blue = 2,
@ -52,7 +56,6 @@ class Color
private: private:
static void buildX11ColorMap(); static void buildX11ColorMap();
private:
unsigned char c[4]; unsigned char c[4];
typedef std::map<const std::string, Color> ColorMap; typedef std::map<const std::string, Color> ColorMap;