diff --git a/src/celengine/CMakeLists.txt b/src/celengine/CMakeLists.txt index 6cbdcdd6f..271044ea8 100644 --- a/src/celengine/CMakeLists.txt +++ b/src/celengine/CMakeLists.txt @@ -74,6 +74,8 @@ set(CELENGINE_SOURCES opencluster.h overlay.cpp overlay.h + overlayimage.cpp + overlayimage.h parseobject.cpp parseobject.h parser.cpp @@ -84,6 +86,7 @@ set(CELENGINE_SOURCES # particlesystem.h planetgrid.cpp planetgrid.h + rectangle.h referencemark.h rendcontext.cpp rendcontext.h diff --git a/src/celengine/console.cpp b/src/celengine/console.cpp index 5ea538f27..8c7eca20c 100644 --- a/src/celengine/console.cpp +++ b/src/celengine/console.cpp @@ -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 // diff --git a/src/celengine/console.h b/src/celengine/console.h index f6161b2c3..d65b0f753 100644 --- a/src/celengine/console.h +++ b/src/celengine/console.h @@ -56,6 +56,11 @@ class Console : public std::ostream void setScale(int, int); 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(char*); void newline(); diff --git a/src/celengine/overlay.cpp b/src/celengine/overlay.cpp index 620f6d2da..296d3facf 100644 --- a/src/celengine/overlay.cpp +++ b/src/celengine/overlay.cpp @@ -15,7 +15,9 @@ #include #include "vecgl.h" #include "overlay.h" +#include "rectangle.h" #include "render.h" +#include "texture.h" using namespace std; using namespace Eigen; @@ -166,101 +168,31 @@ void Overlay::print(const char* s) } } -static void drawRect(const Renderer& r, - const array& vertices, - const vector& colors, - Overlay::RectType type, - float linewidth) +void Overlay::drawRectangle(const Rect& r) { - uint32_t p = 0; - 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 texels = {0, 1, 1, 1, 1, 0, 0, 0}; - - auto s = static_cast(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) + if (useTexture && r.tex == nullptr) { glDisable(GL_TEXTURE_2D); useTexture = false; } - vector cv; - for (const Color& c : r.colors) - cv.push_back(c.toVector4()); - - array 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); + renderer.drawRectangle(r); } +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 diff --git a/src/celengine/overlay.h b/src/celengine/overlay.h index 5b9fcbb2d..f9a4b8b13 100644 --- a/src/celengine/overlay.h +++ b/src/celengine/overlay.h @@ -10,15 +10,17 @@ #ifndef _OVERLAY_H_ #define _OVERLAY_H_ -#include +#include +#include #include -#include -#include +#include #include +#include class Overlay; class Renderer; +class Rect; // Custom streambuf class to support C++ operator style output. The // 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 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, - Filled = 0x0002, - Textured = 0x0004 + glPushMatrix(); + }; + void restorePos() const + { + glPopMatrix(); + }; + const Renderer& getRenderer() const + { + return renderer; }; - struct Rectangle - { - 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& _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 colors; - }; - - void rect(const Rectangle&); + void drawRectangle(const Rect&); void beginText(); void endText(); diff --git a/src/celengine/overlayimage.cpp b/src/celengine/overlayimage.cpp new file mode 100644 index 000000000..313aca1d5 --- /dev/null +++ b/src/celengine/overlayimage.cpp @@ -0,0 +1,65 @@ +#include +#include +#include +#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(LoadTextureFromFile(fs::path("images") / filename)); +} + +void OverlayImage::setColor(const Color& c) +{ + colors.fill(c); +} + +void OverlayImage::setColor(std::array& 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); +} diff --git a/src/celengine/overlayimage.h b/src/celengine/overlayimage.h new file mode 100644 index 000000000..bb797a331 --- /dev/null +++ b/src/celengine/overlayimage.h @@ -0,0 +1,60 @@ +#pragma once + +#include +#include +#include +#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& 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 colors; + + fs::path filename; + std::unique_ptr texture; + Renderer *renderer { nullptr }; +}; diff --git a/src/celengine/parser.cpp b/src/celengine/parser.cpp index 877524ece..55de29e7e 100644 --- a/src/celengine/parser.cpp +++ b/src/celengine/parser.cpp @@ -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(); + return true; +} + + /** * 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 { - Vector3d vecVal; + Vector4d vec4; + if (getVector(key, vec4)) + { + Vector4f vec4f = vec4.cast(); + val = Color(vec4f); + return true; + } - if (!getVector(key, vecVal)) - return false; + Vector3d vec3; + if (getVector(key, vec3)) + { + Vector3f vec3f = vec3.cast(); + 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; } diff --git a/src/celengine/parser.h b/src/celengine/parser.h index 3362b1f4b..759b8bdb1 100644 --- a/src/celengine/parser.h +++ b/src/celengine/parser.h @@ -42,6 +42,8 @@ class AssociativeArray bool getBoolean(const std::string&, bool&) const; bool getVector(const std::string&, Eigen::Vector3d&) 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 getColor(const std::string&, Color&) const; bool getAngle(const std::string&, double&, double = 1.0, double = 0.0) const; diff --git a/src/celengine/rectangle.h b/src/celengine/rectangle.h new file mode 100644 index 000000000..38356a3bb --- /dev/null +++ b/src/celengine/rectangle.h @@ -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 +#include +#include + +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 _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 colors; + Color color; + }; + Texture *tex { nullptr }; + Type type { Type::Filled }; + int nColors { 0 }; +}; diff --git a/src/celengine/render.cpp b/src/celengine/render.cpp index 7c28c1e98..84222e536 100644 --- a/src/celengine/render.cpp +++ b/src/celengine/render.cpp @@ -58,6 +58,7 @@ std::ofstream hdrlog; #include "modelgeometry.h" #include "curveplot.h" #include "shadermanager.h" +#include "rectangle.h" #include #include #include @@ -7886,7 +7887,7 @@ void Renderer::setSolarSystemMaxDistance(float t) 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]; glGetIntegerv(GL_VIEWPORT, viewport); @@ -7900,12 +7901,63 @@ void Renderer::getScreenSize(int* x, int* y, int* w, int* h) const *h = viewport[3]; } -void Renderer::getScreenSize(std::array& viewport) const +void Renderer::getViewport(std::array& viewport) const { static_assert(sizeof(int) == sizeof(GLint), "int and GLint size mismatch"); 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& 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) { 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; } + +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 texels = {0, 1, 1, 1, 1, 0, 0, 0}; + array 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(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 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); +} diff --git a/src/celengine/render.h b/src/celengine/render.h index 40f811dfc..c6d164bde 100644 --- a/src/celengine/render.h +++ b/src/celengine/render.h @@ -21,11 +21,11 @@ #endif #include #include +#include "celengine/vertexobject.h" #include #include #include #include -#include "vertexobject.h" class RendererWatcher; @@ -33,6 +33,7 @@ class FrameTree; class ReferenceMark; class CurvePlot; class AsterismList; +class Rect; struct LightSource { @@ -246,8 +247,20 @@ class Renderer void setOrbitMask(int); int getScreenDpi() const; void setScreenDpi(int); - void getScreenSize(int* x, int* y, int* w, int* h) const; - void getScreenSize(std::array& viewport) const; + + // GL wrappers + void getViewport(int* x, int* y, int* w, int* h) const; + void getViewport(std::array& viewport) const; + void setViewport(int x, int y, int w, int h) const; + void setViewport(const std::array& 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; void setStarColorTable(const ColorTemperatureTable*); bool getVideoSync() const; @@ -709,6 +722,13 @@ class Renderer uint32_t frameCount; int currentIntervalIndex{ 0 }; + enum GLStateFlags + { + ScissorTest = 0x0001, + Multisaple = 0x0002, + }; + + int m_GLStateFlag { 0 }; celgl::VertexObject markerVO{ GL_ARRAY_BUFFER, 0, GL_STATIC_DRAW }; diff --git a/src/celestia/CMakeLists.txt b/src/celestia/CMakeLists.txt index aba6b5ffd..a02b6b5c5 100644 --- a/src/celestia/CMakeLists.txt +++ b/src/celestia/CMakeLists.txt @@ -24,6 +24,8 @@ set(CELESTIA_SOURCES scriptmenu.h url.cpp url.h + view.cpp + view.h ) set(CELX_SOURCES diff --git a/src/celestia/avicapture.cpp b/src/celestia/avicapture.cpp index 16c9bc964..84750cc9e 100644 --- a/src/celestia/avicapture.cpp +++ b/src/celestia/avicapture.cpp @@ -145,7 +145,7 @@ bool AVICapture::captureFrame() // Get the dimensions of the current viewport int x, y, w, h; - renderer->getScreenSize(&x, &y, &w, &h); + renderer->getViewport(&x, &y, &w, &h); x += (w - width) / 2; y += (h - height) / 2; diff --git a/src/celestia/celestiacore.cpp b/src/celestia/celestiacore.cpp index fab7a63bb..7979c2dec 100644 --- a/src/celestia/celestiacore.cpp +++ b/src/celestia/celestiacore.cpp @@ -52,6 +52,7 @@ #include #include #include +#include #ifdef CELX #include @@ -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() : - oldFOV(stdFOV) -{ + oldFOV(stdFOV), /* Get a renderer here so it may be queried for capabilities of the underlying engine even before rendering is enabled. It's initRenderer() routine will be called much later. */ - renderer = new Renderer(); - timer = new Timer(); - - execEnv = new CoreExecutionEnvironment(*this); + renderer(new Renderer()), + timer(new Timer()), + execEnv(new CoreExecutionEnvironment(*this)) +{ 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. View *p1 = v1, *p2 = v2; while ( (p1 = p1->parent) != nullptr ) @@ -661,8 +535,8 @@ void CelestiaCore::mouseButtonUp(float x, float y, int button) float pickX, pickY; float aspectRatio = ((float) width / (float) height); (*activeView)->mapWindowToView((float) x / (float) width, - (float) y / (float) height, - pickX, pickY); + (float) y / (float) height, + pickX, pickY); Vector3f pickRay = sim->getActiveObserver()->getPickRay(pickX * aspectRatio, pickY); @@ -678,8 +552,8 @@ void CelestiaCore::mouseButtonUp(float x, float y, int button) float pickX, pickY; float aspectRatio = ((float) width / (float) height); (*activeView)->mapWindowToView((float) x / (float) width, - (float) y / (float) height, - pickX, pickY); + (float) y / (float) height, + pickX, pickY); Vector3f pickRay = sim->getActiveObserver()->getPickRay(pickX * aspectRatio, pickY); @@ -742,9 +616,6 @@ void CelestiaCore::mouseMove(float x, float y) if (views.size() > 1 && cursorHandler != nullptr) { - /*View* v1 = 0; Unused*/ - /*View* v2 = 0; Unused*/ - for (const auto v : views) { 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, // it's possible that there exists some broken hardware out there // 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 - // 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 - glViewport(0, 0, width, height); - renderer->resize(width, height); + renderer->setRenderRegion(0, 0, width, height, false); sim->render(*renderer); } else { - glEnable(GL_SCISSOR_TEST); for (const auto view : views) { if (view->type == View::ViewWindow) { - glScissor((GLint) (view->x * width), - (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)); + view->switchTo(width, height); sim->render(*renderer, *view->observer); } } - glDisable(GL_SCISSOR_TEST); - glViewport(0, 0, width, height); + renderer->setRenderRegion(0, 0, width, height, false); } - GLboolean toggleAA = glIsEnabled(GL_MULTISAMPLE); + bool toggleAA = renderer->isMSAAEnabled(); if (toggleAA && (renderer->getRenderFlags() & Renderer::ShowCloudMaps)) - glDisable(GL_MULTISAMPLE); + renderer->disableMSAA(); renderOverlay(); if (showConsole) { console.setFont(font); - glColor4f(1.0f, 1.0f, 1.0f, 1.0f); + console.setColor(1.0f, 1.0f, 1.0f, 1.0f); console.begin(); - glTranslatef(0.0f, 200.0f, 0.0f); + console.moveBy(0.0f, 200.0f, 0.0f); console.render(ConsolePageRows); console.end(); } if (toggleAA) - glEnable(GL_MULTISAMPLE); + renderer->enableMSAA(); if (movieCapture != nullptr && recording) movieCapture->captureFrame(); @@ -2432,9 +2291,11 @@ void CelestiaCore::resize(GLsizei w, GLsizei h) if (h == 0) h = 1; - glViewport(0, 0, w, h); if (renderer != nullptr) + { + renderer->setViewport(0, 0, w, h); renderer->resize(w, h); + } if (overlay != nullptr) overlay->setWindowSize(w, h); console.setScale(w, h); @@ -2497,88 +2358,35 @@ void CelestiaCore::setViewChanged() void CelestiaCore::splitView(View::Type type, View* av, float splitPos) { - setViewChanged(); + if (type == View::ViewWindow) + return; if (av == nullptr) - av = (*activeView); - bool vertical = ( type == View::VerticalSplit ); - Observer* o = sim->addObserver(); - bool tooSmall = false; + av = *activeView; - switch (type) // If active view is too small, don't split it. - { - 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) + if (!av->isSplittable(type)) { flash(_("View too small to be split")); return; } - flash(_("Added view")); + + setViewChanged(); + + Observer* o = sim->addObserver(); // Make the new observer a copy of the old one // TODO: This works, but an assignment operator for Observer // should be defined. *o = *(sim->getActiveObserver()); - float w1, h1, w2, h2; - if (vertical) - { - 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; - + View* split, *view; + av->split(type, o, splitPos, &split, &view); views.push_back(split); views.push_back(view); setFOVFromZoom(); + + flash(_("Added view")); } void CelestiaCore::setFOVFromZoom() @@ -2605,29 +2413,23 @@ void CelestiaCore::singleView(View* av) setViewChanged(); if (av == nullptr) - av = (*activeView); + av = *activeView; list::iterator i = views.begin(); while(i != views.end()) { if ((*i) != av) { - sim->removeObserver((*i)->observer); - delete (*i)->observer; + sim->removeObserver((*i)->getObserver()); + delete (*i)->getObserver(); delete (*i); - i=views.erase(i); + i = views.erase(i); } else - i++; + ++i; } - av->x = 0.0f; - av->y = 0.0f; - av->width = 1.0f; - av->height = 1.0f; - av->parent = nullptr; - av->child1 = nullptr; - av->child2 = nullptr; + av->reset(); activeView = views.begin(); sim->setActiveObserver((*activeView)->observer); @@ -2636,60 +2438,37 @@ void CelestiaCore::singleView(View* av) void CelestiaCore::setActiveView(View* v) { - activeView = find(views.begin(),views.end(),v); + activeView = find(views.begin(), views.end(), v); sim->setActiveObserver((*activeView)->observer); } void CelestiaCore::deleteView(View* v) { if (v == nullptr) - v = (*activeView); + v = *activeView; - if (v->parent == nullptr) return; + if (v->isRootView()) + return; //Erase view and parent view from views - list::iterator i = views.begin(); - while(i != views.end()) + for (auto i = views.begin(); i != views.end(); ) { if ((*i == v) || (*i == v->parent)) - i=views.erase(i); + i = views.erase(i); else - i++; + ++i; } - 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; - } + sim->removeObserver(v->getObserver()); + delete(v->getObserver()); + auto sibling = View::remove(v); - v->walkTreeResize(sibling, sign); - - sim->removeObserver(v->observer); - delete(v->observer); View* nextActiveView = sibling; while (nextActiveView->type != View::ViewWindow) nextActiveView = nextActiveView->child1; - activeView = find(views.begin(),views.end(),nextActiveView); + activeView = find(views.begin(), views.end(), nextActiveView); sim->setActiveObserver((*activeView)->observer); - delete(v->parent); - delete(v); - if (!showActiveViewFrame) flashFrameStart = currentTime; setFOVFromZoom(); @@ -3260,66 +3039,12 @@ static void displaySelectionName(Overlay& overlay, #endif -void CelestiaCore::setScriptImage(float duration, - float xoffset, - float yoffset, - float alpha, - const fs::path& filename, - bool fitscreen) +void CelestiaCore::setScriptImage(std::unique_ptr &&_image) { - if (image == nullptr || image->isNewImage(filename)) - { - delete image; - image = new CelestiaCore::OverlayImage(filename, overlay); - } + image = std::move(_image); 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() { #ifdef CELX @@ -3343,51 +3068,34 @@ void CelestiaCore::renderOverlay() if (runningScript) { #endif - if (image) + if (image != nullptr) image->render((float) currentTime, width, height); } if (views.size() > 1) { - Overlay::Rectangle r(0, 0, 0, 0, frameColor, Overlay::RectType::Outlined, 1); - // Render a thin border arround all views if (showViewFrames || resizeSplit) { for(const auto v : views) { if (v->type == View::ViewWindow) - { - 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); - } + v->drawBorder(width, height, frameColor); } } // Render a very simple border around the active view 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) { - r.colors[0] = activeFrameColor; - r.lw = 2; - overlay->rect(r); + av->drawBorder(width, height, activeFrameColor, 2); } if (currentTime < flashFrameStart + 0.5) { float alpha = (float) (1.0 - (currentTime - flashFrameStart) / 0.5); - r.colors[0] = {activeFrameColor, alpha}; - r.lw = 8; - overlay->rect(r); + av->drawBorder(width, height, {activeFrameColor, alpha}, 8); } } @@ -3415,20 +3123,18 @@ void CelestiaCore::renderOverlay() if (dateWidth > dateStrWidth) dateStrWidth = dateWidth; // Time and date - glPushMatrix(); - glColor4f(0.7f, 0.7f, 1.0f, 1.0f); - glTranslatef( (float) (width - dateStrWidth), - (float) (height - fontHeight), - 0.0f); + overlay->savePos(); + overlay->setColor(0.7f, 0.7f, 1.0f, 1.0f); + overlay->moveBy(width - dateStrWidth, height - fontHeight); overlay->beginText(); overlay->print(dateStr); 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"); - glColor4f(0.7f, 0.7f, 1.0f, 1.0f); + overlay->setColor(0.7f, 0.7f, 1.0f, 1.0f); } *overlay << '\n'; @@ -3455,21 +3161,21 @@ void CelestiaCore::renderOverlay() 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->endText(); - glPopMatrix(); + overlay->restorePos(); } if (hudDetail > 0 && (overlayElements & ShowVelocity)) { // Speed - glPushMatrix(); - glTranslatef(0.0f, (float) (fontHeight * 2 + 5), 0.0f); - glColor4f(0.7f, 0.7f, 1.0f, 1.0f); + overlay->savePos(); + overlay->moveBy(0.0f, fontHeight * 2 + 5); + overlay->setColor(0.7f, 0.7f, 1.0f, 1.0f); overlay->beginText(); *overlay << '\n'; @@ -3492,7 +3198,7 @@ void CelestiaCore::renderOverlay() displaySpeed(*overlay, sim->getObserver().getVelocity().norm()); overlay->endText(); - glPopMatrix(); + overlay->restorePos(); } Universe *u = sim->getUniverse(); @@ -3500,11 +3206,10 @@ void CelestiaCore::renderOverlay() if (hudDetail > 0 && (overlayElements & ShowFrame)) { // Field of view and camera mode in lower right corner - glPushMatrix(); - glTranslatef((float) (width - emWidth * 15), - (float) (fontHeight * 3 + 5), 0.0f); + overlay->savePos(); + overlay->moveBy(width - emWidth * 15, fontHeight * 3 + 5); overlay->beginText(); - glColor4f(0.6f, 0.6f, 1.0f, 1); + overlay->setColor(0.6f, 0.6f, 1.0f, 1); 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 float fov = radToDeg(sim->getActiveObserver()->getFOV()); fmt::fprintf(*overlay, _("FOV: %s (%.2fx)\n"), angleToStr(fov), (*activeView)->zoom); overlay->endText(); - glPopMatrix(); + overlay->restorePos(); } // Selection info Selection sel = sim->getSelection(); if (!sel.empty() && hudDetail > 0 && (overlayElements & ShowSelection)) { - glPushMatrix(); - glColor4f(0.7f, 0.7f, 1.0f, 1.0f); - glTranslatef(0.0f, (float) (height - titleFont->getHeight()), 0.0f); + overlay->savePos(); + overlay->setColor(0.7f, 0.7f, 1.0f, 1.0f); + overlay->moveBy(0.0f, height - titleFont->getHeight()); overlay->beginText(); Vector3d v = sel.getPosition(sim->getTime()).offsetFromKm(sim->getObserver().getPosition()); @@ -3608,9 +3313,8 @@ void CelestiaCore::renderOverlay() } overlay->setFont(titleFont); - *overlay << selectionNames; + *overlay << selectionNames << '\n'; overlay->setFont(font); - *overlay << '\n'; displayStarInfo(*overlay, hudDetail, *(sel.star()), @@ -3636,9 +3340,8 @@ void CelestiaCore::renderOverlay() } overlay->setFont(titleFont); - *overlay << selectionNames; + *overlay << selectionNames << '\n'; overlay->setFont(font); - *overlay << '\n'; displayDSOinfo(*overlay, *sel.deepsky(), 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 // of the name. - vector::const_iterator firstName = names.begin(); + auto firstName = names.begin(); if (sel.body()->hasLocalizedName()) - firstName++; + ++firstName; - for (vector::const_iterator iter = firstName; iter != names.end(); iter++) + for (auto iter = firstName; iter != names.end(); ++iter) { if (iter != firstName) selectionNames += " / "; @@ -3699,9 +3402,7 @@ void CelestiaCore::renderOverlay() *overlay << sel.location()->getName(true).c_str(); overlay->setFont(font); *overlay << '\n'; - displayLocationInfo(*overlay, - *(sel.location()), - v.norm()); + displayLocationInfo(*overlay, *(sel.location()), v.norm()); break; default: @@ -3759,18 +3460,19 @@ void CelestiaCore::renderOverlay() overlay->endText(); - glPopMatrix(); + overlay->restorePos(); } // Text input if (textEnterMode & KbAutoComplete) { overlay->setFont(titleFont); - glPushMatrix(); - Overlay::Rectangle r(0, 0, width, 100, consoleColor, Overlay::RectType::Filled); - overlay->rect(r); - glTranslatef(0.0f, fontHeight * 3.0f + 35.0f, 0.0f); - glColor4f(0.6f, 0.6f, 1.0f, 1.0f); + overlay->savePos(); + Rect r(0, 0, width, 100); + r.setColor(consoleColor); + overlay->drawRectangle(r); + overlay->moveBy(0.0f, fontHeight * 3.0f + 35.0f); + overlay->setColor(0.6f, 0.6f, 1.0f, 1.0f); overlay->beginText(); fmt::fprintf(*overlay, _("Target name: %s"), typedText); overlay->endText(); @@ -3780,7 +3482,7 @@ void CelestiaCore::renderOverlay() int nb_cols = 4; int nb_lines = 3; int start = 0; - glTranslatef(3.0f, -font->getHeight() - 3.0f, 0.0f); + overlay->moveBy(3.0f, -font->getHeight() - 3.0f); vector::const_iterator iter = typedTextCompletion.begin(); if (typedTextCompletionIdx >= nb_cols * nb_lines) { @@ -3789,22 +3491,22 @@ void CelestiaCore::renderOverlay() } for (int i=0; iter < typedTextCompletion.end() && i < nb_cols; i++) { - glPushMatrix(); + overlay->savePos(); overlay->beginText(); for (int j = 0; iter < typedTextCompletion.end() && j < nb_lines; iter++, j++) { 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 - glColor4f(0.6f, 0.6f, 1.0f, 1); + overlay->setColor(0.6f, 0.6f, 1.0f, 1); *overlay << *iter << "\n"; } overlay->endText(); - glPopMatrix(); - glTranslatef((float) (width/nb_cols), 0.0f, 0.0f); + overlay->restorePos(); + overlay->moveBy((float) (width/nb_cols), 0.0f, 0.0f); } } - glPopMatrix(); + overlay->restorePos(); overlay->setFont(font); } @@ -3828,17 +3530,17 @@ void CelestiaCore::renderOverlay() y -= fontHeight; overlay->setFont(titleFont); - glPushMatrix(); + overlay->savePos(); float alpha = 1.0f; if (currentTime > messageStart + messageDuration - 0.5) alpha = (float) ((messageStart + messageDuration - currentTime) / 0.5); - glColor4f(textColor.red(), textColor.green(), textColor.blue(), alpha); - glTranslatef((float) x, (float) y, 0.0f); + overlay->setColor(textColor.red(), textColor.green(), textColor.blue(), alpha); + overlay->moveBy(x, y); overlay->beginText(); *overlay << messageText; overlay->endText(); - glPopMatrix(); + overlay->restorePos(); overlay->setFont(font); } @@ -3846,18 +3548,18 @@ void CelestiaCore::renderOverlay() { int movieWidth = movieCapture->getWidth(); int movieHeight = movieCapture->getHeight(); - glPushMatrix(); - Color color(1, 0, 0, 1); - glColor(color); - Overlay::Rectangle r((width - movieWidth) / 2 - 1, - (height - movieHeight) / 2 - 1, - movieWidth + 1, - movieHeight + 1, - color, - Overlay::RectType::Outlined); - overlay->rect(r); - glTranslatef((float) ((width - movieWidth) / 2), - (float) ((height + movieHeight) / 2 + 2), 0.0f); + overlay->savePos(); + Color color(1.0f, 0.0f, 0.0f, 1.0f); + overlay->setColor(color); + Rect r((width - movieWidth) / 2 - 1, + (height - movieHeight) / 2 - 1, + movieWidth + 1, + movieHeight + 1); + r.setColor(color); + r.setType(Rect::Type::BorderOnly); + overlay->drawRectangle(r); + overlay->moveBy((float) ((width - movieWidth) / 2), + (float) ((height + movieHeight) / 2 + 2)); overlay->beginText(); fmt::fprintf(*overlay, _("%dx%d at %f fps %s"), movieWidth, movieHeight, @@ -3865,12 +3567,11 @@ void CelestiaCore::renderOverlay() recording ? _("Recording") : _("Paused")); overlay->endText(); - glPopMatrix(); + overlay->restorePos(); - glPushMatrix(); - glTranslatef((float) ((width + movieWidth) / 2 - emWidth * 5), - (float) ((height + movieHeight) / 2 + 2), - 0.0f); + overlay->savePos(); + overlay->moveBy((float) ((width + movieWidth) / 2 - emWidth * 5), + (float) ((height + movieHeight) / 2 + 2)); float sec = movieCapture->getFrameCount() / movieCapture->getFrameRate(); auto min = (int) (sec / 60); @@ -3878,28 +3579,27 @@ void CelestiaCore::renderOverlay() overlay->beginText(); fmt::fprintf(*overlay, "%3d:%05.2f", min, sec); overlay->endText(); - glPopMatrix(); + overlay->restorePos(); - glPushMatrix(); - glTranslatef((float) ((width - movieWidth) / 2), - (float) ((height - movieHeight) / 2 - fontHeight - 2), - 0.0f); + overlay->savePos(); + overlay->moveBy((float) ((width - movieWidth) / 2), + (float) ((height - movieHeight) / 2 - fontHeight - 2)); overlay->beginText(); *overlay << _("F11 Start/Pause F12 Stop"); overlay->endText(); - glPopMatrix(); + overlay->restorePos(); - glPopMatrix(); + overlay->restorePos(); } if (editMode) { - glPushMatrix(); - glTranslatef((float) ((width - font->getWidth(_("Edit Mode"))) / 2), - (float) (height - fontHeight), 0.0f); - glColor4f(1, 0, 1, 1); + overlay->savePos(); + overlay->moveBy((float) ((width - font->getWidth(_("Edit Mode"))) / 2), + (float) (height - fontHeight)); + overlay->setColor(1, 0, 1, 1); *overlay << _("Edit Mode"); - glPopMatrix(); + overlay->restorePos(); } // Show logo at start @@ -4213,10 +3913,12 @@ bool CelestiaCore::initSimulation(const fs::path& configFileName, } sim = new Simulation(universe); - if((renderer->getRenderFlags() & Renderer::ShowAutoMag) == 0) - sim->setFaintestVisible(config->faintestVisible); + if ((renderer->getRenderFlags() & Renderer::ShowAutoMag) == 0) + { + 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); activeView = views.begin(); diff --git a/src/celestia/celestiacore.h b/src/celestia/celestiacore.h index 4e05146f4..d8ab98c5b 100644 --- a/src/celestia/celestiacore.h +++ b/src/celestia/celestiacore.h @@ -21,11 +21,13 @@ #include #include #include +#include #include #include "configfile.h" #include "favorites.h" #include "destination.h" #include "moviecapture.h" +#include "view.h" #ifdef CELX #include "celx.h" #endif @@ -47,40 +49,6 @@ public: 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 { public: @@ -197,37 +165,6 @@ class CelestiaCore // : public Watchable 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: CelestiaCore(); ~CelestiaCore(); @@ -383,7 +320,7 @@ class CelestiaCore // : public Watchable void fatalError(const std::string&, bool visual = true); - void setScriptImage(float, float, float, float, const fs::path&, bool); + void setScriptImage(std::unique_ptr&&); const std::string& getTypedText() const { return typedText; } void setTypedText(const char *); @@ -425,7 +362,7 @@ class CelestiaCore // : public Watchable const Color activeFrameColor{ 0.5f, 0.5f, 1.0f, 1.0f }; const Color consoleColor{ 0.7f, 0.7f, 1.0f, 0.2f }; - OverlayImage *image{ nullptr }; + std::unique_ptr image; std::string typedText; std::vector typedTextCompletion; diff --git a/src/celestia/celx_celestia.cpp b/src/celestia/celx_celestia.cpp index e955d9e39..26b5f1f44 100644 --- a/src/celestia/celx_celestia.cpp +++ b/src/celestia/celx_celestia.cpp @@ -28,6 +28,7 @@ #include "url.h" #include "imagecapture.h" #include "celestiacore.h" +#include "view.h" using namespace Eigen; @@ -282,7 +283,7 @@ int celestia_getscreendimension(lua_State* l) // Get the dimensions of the current viewport int w, h; 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, h); return 2; @@ -1916,7 +1917,7 @@ static int celestia_takescreenshot(lua_State* l) // Get the dimensions of the current viewport array viewport; - appCore->getRenderer()->getScreenSize(viewport); + appCore->getRenderer()->getViewport(viewport); fs::path path = appCore->getConfig()->scriptScreenshotDirectory; @@ -2081,7 +2082,14 @@ static int celestia_overlay(lua_State* l) else 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(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; } diff --git a/src/celestia/cmdparser.cpp b/src/celestia/cmdparser.cpp index 216170752..3a6f41894 100644 --- a/src/celestia/cmdparser.cpp +++ b/src/celestia/cmdparser.cpp @@ -773,39 +773,57 @@ Command* CommandParser::parseCommand() } else if (commandName == "overlay") { - float duration; - float xoffset; - float yoffset; - float alpha; + float duration = 3.0f; + float fadeafter; + float xoffset = 0.0f; + float yoffset = 0.0f; + float alpha = 1.0f; + bool hasAlpha = true; string filename; - bool fitscreen; + bool fitscreen = false; + Color color(Color::White); - if(!paramList->getNumber("duration", duration)) - duration = 3; - if(!paramList->getNumber("xoffset", xoffset)) - xoffset = 0.0; - if(!paramList->getNumber("yoffset", yoffset)) - yoffset = 0.0; - if(!paramList->getNumber("alpha", alpha)) - alpha = 1; - if(!paramList->getString("filename", filename)) - filename = ""; - if(!paramList->getBoolean("fitscreen", fitscreen)) + paramList->getNumber("duration", duration); + paramList->getNumber("xoffset", xoffset); + paramList->getNumber("yoffset", yoffset); + if (paramList->getNumber("alpha", alpha)) + hasAlpha = true; + paramList->getString("filename", filename); + + if (!paramList->getBoolean("fitscreen", fitscreen)) { - int f; - if(!paramList->getNumber("fitscreen", f)) - fitscreen = false; - else - fitscreen = (bool) f; + int f; + // backward compatibility with celestia ed implementation + if (paramList->getNumber("fitscreen", f)) + fitscreen = (bool) f; } - cmd = new CommandScriptImage(duration, xoffset, yoffset, alpha, filename, fitscreen); + array 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") { int level; - if(!paramList->getNumber("level", level)) + if (!paramList->getNumber("level", level)) level = 2; cmd = new CommandVerbosity(level); diff --git a/src/celestia/command.cpp b/src/celestia/command.cpp index 108017746..4b5bba862 100644 --- a/src/celestia/command.cpp +++ b/src/celestia/command.cpp @@ -649,7 +649,7 @@ void CommandCapture::process(ExecutionEnvironment& env) // Get the dimensions of the current viewport array viewport; - r->getScreenSize(viewport); + r->getViewport(viewport); if (compareIgnoringCase(type, "jpeg") == 0) { @@ -952,21 +952,30 @@ double RepeatCommand::getDuration() const } // ScriptImage command -CommandScriptImage::CommandScriptImage(float _duration, float _xoffset, - float _yoffset, float _alpha, - std::string _filename, bool _fitscreen) : +CommandScriptImage::CommandScriptImage(float _duration, float _fadeafter, + float _xoffset, float _yoffset, + fs::path _filename, + bool _fitscreen, + array &_colors) : duration(_duration), + fadeafter(_fadeafter), xoffset(_xoffset), yoffset(_yoffset), - alpha(_alpha), filename(std::move(_filename)), fitscreen(_fitscreen) { + copy(_colors.begin(), _colors.end(), colors.begin()); } void CommandScriptImage::process(ExecutionEnvironment& env) { - env.getCelestiaCore()->setScriptImage(duration, xoffset, yoffset, alpha, filename, fitscreen); + auto image = unique_ptr(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 diff --git a/src/celestia/command.h b/src/celestia/command.h index 2ccd1a66e..c5dbbc298 100644 --- a/src/celestia/command.h +++ b/src/celestia/command.h @@ -12,6 +12,7 @@ #define MAX_CONSTELLATIONS 100 +#include #include #include #include @@ -716,17 +717,17 @@ class RepeatCommand : public Command class CommandScriptImage : public InstantaneousCommand { public: - CommandScriptImage(float _duration, float _xoffset, float _yoffset, - float _alpha, std::string, bool _fitscreen); + CommandScriptImage(float, float, float, float, fs::path, bool, std::array&); void process(ExecutionEnvironment&); private: - double duration; + float duration; + float fadeafter; float xoffset; float yoffset; - float alpha; - std::string filename; + fs::path filename; int fitscreen; + std::array colors; }; class CommandVerbosity : public InstantaneousCommand diff --git a/src/celestia/gtk/actions.cpp b/src/celestia/gtk/actions.cpp index 4b4cfd46a..bdc57c0fd 100644 --- a/src/celestia/gtk/actions.cpp +++ b/src/celestia/gtk/actions.cpp @@ -1124,7 +1124,7 @@ static void captureImage(const char* filename, AppData* app) { /* Get the dimensions of the current viewport */ array viewport; - app->renderer->getScreenSize(viewport); + app->renderer->getViewport(viewport); bool success = false; 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 */ array viewport; - app->renderer->getScreenSize(viewport); + app->renderer->getViewport(viewport); MovieCapture* movieCapture = new OggTheoraCapture(app->renderer); switch (aspect) diff --git a/src/celestia/oggtheoracapture.cpp b/src/celestia/oggtheoracapture.cpp index 408644367..2c4481e60 100644 --- a/src/celestia/oggtheoracapture.cpp +++ b/src/celestia/oggtheoracapture.cpp @@ -338,7 +338,7 @@ bool OggTheoraCapture::captureFrame() // Get the dimensions of the current viewport int x, y, w, h; - renderer->getScreenSize(&x, &y, &w, &h); + renderer->getViewport(&x, &y, &w, &h); x += (w - frame_x) / 2; y += (h - frame_y) / 2; diff --git a/src/celestia/view.cpp b/src/celestia/view.cpp new file mode 100644 index 000000000..7e9ab2189 --- /dev/null +++ b/src/celestia/view.cpp @@ -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 +#include +#include +#include "view.h" + +#include + +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); +} diff --git a/src/celestia/view.h b/src/celestia/view.h new file mode 100644 index 000000000..65a31be93 --- /dev/null +++ b/src/celestia/view.h @@ -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 }; +}; + +//} diff --git a/src/celestia/win32/winmain.cpp b/src/celestia/win32/winmain.cpp index 70ac1f720..cf2150cbe 100644 --- a/src/celestia/win32/winmain.cpp +++ b/src/celestia/win32/winmain.cpp @@ -2684,7 +2684,7 @@ static void HandleCaptureImage(HWND hWnd) // Get the dimensions of the current viewport array viewport; - appCore->getRenderer()->getScreenSize(viewport); + appCore->getRenderer()->getViewport(viewport); bool success = false; diff --git a/src/celutil/color.cpp b/src/celutil/color.cpp index a03142835..4d6b887bd 100644 --- a/src/celutil/color.cpp +++ b/src/celutil/color.cpp @@ -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[Green] = g; 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()) +{ } diff --git a/src/celutil/color.h b/src/celutil/color.h index e678d8f27..667ebd1e6 100644 --- a/src/celutil/color.h +++ b/src/celutil/color.h @@ -22,9 +22,13 @@ class Color Color(float, float, float); Color(float, float, float, float); Color(unsigned char, unsigned char, unsigned char); + Color(unsigned char, unsigned char, unsigned char, unsigned char); Color(const Color&, float); + Color(const Eigen::Vector3f&); + Color(const Eigen::Vector4f&); - enum { + enum + { Red = 0, Green = 1, Blue = 2, @@ -52,7 +56,6 @@ class Color private: static void buildX11ColorMap(); - private: unsigned char c[4]; typedef std::map ColorMap;