Nicolas Winkler 5 роки тому
батько
коміт
df2fdab757
9 змінених файлів з 518 додано та 1 видалено
  1. 3 1
      Almond.cpp
  2. 3 0
      Almond.h
  3. 6 0
      Almond.pro
  4. 179 0
      EscapeTimeVisualWidget.cpp
  5. 42 0
      EscapeTimeVisualWidget.h
  6. 88 0
      FractalWidgetUtils.cpp
  7. 85 0
      FractalWidgetUtils.h
  8. 64 0
      FractalZoomWidget.cpp
  9. 48 0
      FractalZoomWidget.h

+ 3 - 1
Almond.cpp

@@ -20,6 +20,7 @@ Almond::Almond(QWidget* parent) :
     mw = std::make_unique<MandelWidget>(mandelContext,
                                         &mandelContext.getDefaultGenerator(),
                                         ui.centralWidget);
+    etvw = new EscapeTimeVisualWidget(this);
     customGeneratorDialog = std::make_unique<CustomGenerator>(mandelContext);
     customGenerator = nullptr;
     customViewSave = mnd::MandelViewport::centerView();
@@ -32,7 +33,8 @@ Almond::Almond(QWidget* parent) :
     mandelViewSave = mw->getViewport();
 
     QObject::connect(mw.get(), &MandelWidget::pointSelected, this, &Almond::pointSelected);
-    ui.mandel_container->addWidget(mw.get());
+    ui.mandel_container->addWidget(etvw);
+    //ui.mandel_container->addWidget(mw.get());
     ui.maxIterations->setValidator(new QIntValidator(1, 1000000000, this));
 
     ui.backgroundProgress->setEnabled(false);

+ 3 - 0
Almond.h

@@ -13,6 +13,8 @@
 #include "choosegenerators.h"
 #include "customgenerator.h"
 
+#include "EscapeTimeVisualWidget.h"
+
 #include "AlmondMenuWidget.h"
 #include "ExportImageMenu.h"
 #include "ExportVideoMenu.h"
@@ -53,6 +55,7 @@ private:
     QWidget* cw;
 public:
     std::unique_ptr<MandelWidget> mw;
+    EscapeTimeVisualWidget* etvw;
 private:
     //std::unique_ptr<BenchmarkDialog> benchmarkDialog;
     std::unique_ptr<CustomGenerator> customGeneratorDialog;

+ 6 - 0
Almond.pro

@@ -30,8 +30,11 @@ SOURCES += \
         AlmondMenuWidget.cpp \
         BackgroundTask.cpp \
         Color.cpp \
+        EscapeTimeVisualWidget.cpp \
         ExportImageMenu.cpp \
         ExportVideoMenu.cpp \
+        FractalWidgetUtils.cpp \
+        FractalZoomWidget.cpp \
         GradientMenu.cpp \
         GradientWidget.cpp \
         MandelWidget.cpp \
@@ -47,8 +50,11 @@ HEADERS += \
         AlmondMenuWidget.h \
         BackgroundTask.h \
         Color.h \
+        EscapeTimeVisualWidget.h \
         ExportImageMenu.h \
         ExportVideoMenu.h \
+        FractalWidgetUtils.h \
+        FractalZoomWidget.h \
         GradientMenu.h \
         GradientWidget.h \
         MandelWidget.h \

+ 179 - 0
EscapeTimeVisualWidget.cpp

@@ -0,0 +1,179 @@
+#include "EscapeTimeVisualWidget.h"
+
+#include "Bitmap.h"
+
+#include <QOpenGLShaderProgram>
+#include <QOpenGLContext>
+#include <QOpenGLFunctions>
+
+
+
+ETVImage::ETVImage(EscapeTimeVisualWidget& owner) :
+    owner{ owner }
+{
+    auto& gl = *owner.context()->functions();
+    gl.glGenTextures(1, &textureId);
+    gl.glActiveTexture(GL_TEXTURE0);
+    gl.glBindTexture(GL_TEXTURE_2D, textureId);
+
+    Bitmap<float> img{512, 512};
+    for (int i = 0; i < img.width; i++) {
+        for (int j = 0; j < img.height; j++) {
+            img.get(i, j) = (i ^ j);
+        }
+    }
+    gl.glTexImage2D(GL_TEXTURE_2D, 0, GL_R32F, int(img.width), int(img.height), 0, GL_RED, GL_FLOAT, img.pixels.get());
+    gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+    gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+    gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+    gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+    gl.glBindTexture(GL_TEXTURE_2D, 0);
+}
+
+
+ETVImage::~ETVImage(void)
+{
+    auto& gl = *owner.context()->functions();
+    gl.glDeleteTextures(1, &textureId);
+}
+
+
+void ETVImage::draw(float x, float y, float w, float h,
+                    float tx, float ty, float tw, float th)
+{
+    auto& gl = *owner.context()->functions();
+
+    GLfloat const vertices[] = {
+        x, y,  0.0f,
+        x, y + h, 0.0f,
+        x + w, y, 0.0f,
+        x + w, y + h, 0.0f,
+    };
+
+    GLfloat const texCoords[] = {
+        tx,      ty,
+        tx,      ty + th,
+        tx + tw, ty,
+        tx + tw, ty + th,
+    };
+
+    QColor color{ 255, 255, 255 };
+    auto& program = owner.program;
+    int vertexLoc = program->attributeLocation("vertex");
+    int texCoordsLoc = program->attributeLocation("texCoord");
+    int colorLocation = program->uniformLocation("color");
+    int texLoc = program->uniformLocation("tex");
+    int gradLoc = program->uniformLocation("gradient");
+    program->setAttributeArray(vertexLoc, vertices, 3);
+    program->setAttributeArray(texCoordsLoc, texCoords, 2);
+    program->enableAttributeArray(vertexLoc);
+    program->enableAttributeArray(texCoordsLoc);
+    program->setUniformValue(colorLocation, color);
+
+
+    gl.glEnable(GL_TEXTURE_2D);
+
+    gl.glUniform1i(texLoc, 0);
+    gl.glUniform1i(gradLoc, 2);
+
+    gl.glActiveTexture(GL_TEXTURE0);
+    gl.glBindTexture(GL_TEXTURE_2D, textureId);
+    gl.glActiveTexture(GL_TEXTURE2);
+    gl.glBindTexture(GL_TEXTURE_2D, owner.gradientTextureId);
+
+    gl.glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
+    program->disableAttributeArray(vertexLoc);
+    program->disableAttributeArray(texCoordsLoc);
+    gl.glActiveTexture(GL_TEXTURE0);
+}
+
+
+EscapeTimeVisualWidget::EscapeTimeVisualWidget(QWidget* parent) :
+    QOpenGLWidget{ parent }
+{
+}
+
+
+void EscapeTimeVisualWidget::initializeGL(void)
+{
+    auto& gl = *this->context()->functions();
+    gl.glClearColor(0, 0, 0, 0);
+
+    gl.glDisable(GL_DEPTH_TEST);
+
+    // looks not even better
+    //gl.glEnable(GL_FRAMEBUFFER_SRGB);
+
+    //glShadeModel(GL_SMOOTH);
+
+    program = new QOpenGLShaderProgram{ this->context() };
+    bool vert = program->addShaderFromSourceCode(QOpenGLShader::Vertex,
+    "attribute highp vec4 vertex;\n"
+    "attribute highp vec2 texCoord;\n"
+    "uniform highp mat4 matrix;\n"
+    "varying highp vec2 texc;\n"
+    "void main(void)\n"
+    "{\n"
+    "   gl_Position = matrix * vertex;\n"
+    "   texc = texCoord;\n"
+    "}");
+    bool frag = program->addShaderFromSourceCode(QOpenGLShader::Fragment,
+    "uniform sampler2D gradient;\n"
+    "uniform sampler2D tex;\n"
+    "uniform mediump vec4 color;\n"
+    "varying highp vec2 texc;\n"
+    "void main(void)\n"
+    "{\n"
+    "   float v = texture2D(tex, texc).r;\n"
+    "   gl_FragColor = texture2D(gradient, vec2(v*0.005, 0.0));\n"
+//    "   gl_FragColor = gl_FragColor * texture2D(tex, texc);\n"
+//    "   float v = texture2D(tex, texc).r;\n"
+//    "   gl_FragColor = vec4(v, 1.0 - v, v*v, 1);\n"
+//    "   gl_FragColor.g = 0.3;\n"
+    "}");
+    //program.link();
+    bool bound = program->bind();
+
+    unsigned char pix[] = { 255, 0, 0, 0, 255, 0, 0, 0, 255 };
+
+    GLuint id;
+    gl.glEnable(GL_TEXTURE_2D);
+    gl.glGenTextures(1, &id);
+    gl.glBindTexture(GL_TEXTURE_2D, id);
+
+    gl.glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB8, 3, 1, 0, GL_RGB, GL_UNSIGNED_BYTE, reinterpret_cast<char*> (pix));
+    gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
+    gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+    gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+    gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+    gl.glBindTexture(GL_TEXTURE_2D, 0);
+
+    gradientTextureId = id;
+
+    //gl3.glBindSampler(0, id);
+}
+
+
+void EscapeTimeVisualWidget::resizeGL(int w, int h)
+{
+    auto& gl = *this->context()->functions();
+    float pixelRatio = this->devicePixelRatioF();
+    gl.glViewport(0, 0, w * pixelRatio, h * pixelRatio);
+
+    QMatrix4x4 pmvMatrix;
+    pmvMatrix.ortho(QRectF{ 0, 0, w * pixelRatio, h * pixelRatio });
+    int matrixLocation = program->uniformLocation("matrix");
+    program->setUniformValue(matrixLocation, pmvMatrix);
+}
+
+
+void EscapeTimeVisualWidget::paintGL(void)
+{
+    ETVImage etvi{ *this };
+
+    auto& gl = *this->context()->functions();
+    gl.glClearColor(0.0, 0.2, 0.0, 1.0);
+    gl.glClear(GL_COLOR_BUFFER_BIT);
+
+    etvi.draw(100, 100, 700, 700);
+}

+ 42 - 0
EscapeTimeVisualWidget.h

@@ -0,0 +1,42 @@
+#ifndef ESCAPETIMEVISUALWIDGET_H
+#define ESCAPETIMEVISUALWIDGET_H
+
+#include <QOpenGLWidget>
+
+class QOpenGLShaderProgram;
+
+
+class EscapeTimeVisualWidget;
+
+
+class ETVImage
+{
+    GLuint textureId;
+    EscapeTimeVisualWidget& owner;
+public:
+    ETVImage(EscapeTimeVisualWidget& owner);
+    ~ETVImage(void);
+
+    void draw(float x, float y, float w, float h,
+              float tx = 0.0f, float ty = 0.0f,
+              float tw = 1.0f, float th = 1.0f);
+};
+
+
+class EscapeTimeVisualWidget :
+    public QOpenGLWidget
+{
+    Q_OBJECT
+
+    friend class ETVImage;
+    QOpenGLShaderProgram* program;
+    GLuint gradientTextureId;
+public:
+    EscapeTimeVisualWidget(QWidget* parent = nullptr);
+
+    void initializeGL(void) override;
+    void resizeGL(int w, int h) override;
+    void paintGL(void) override;
+};
+
+#endif // ESCAPETIMEVISUALWIDGET_H

+ 88 - 0
FractalWidgetUtils.cpp

@@ -0,0 +1,88 @@
+#include "FractalWidgetUtils.h"
+
+
+CellImage::~CellImage(void)
+{
+}
+
+ImageClip::ImageClip(std::shared_ptr<ETVImage> tex,
+                 float tx, float ty, float tw, float th) :
+    etvImage{ std::move(tex) },
+    tx{ tx }, ty{ ty }, tw{ tw }, th{ th }
+{
+}
+
+
+ImageClip::~ImageClip(void)
+{
+}
+
+
+void ImageClip::drawRect(float x, float y, float width, float height)
+{
+    etvImage->draw(x, y, width, height, tx, ty, tw, th);
+}
+
+
+ImageClip ImageClip::clip(float x, float y, float w, float h)
+{
+    float tx = this->tx + x * this->tw;
+    float ty = this->ty + y * this->th;
+    float tw = this->tw * w;
+    float th = this->th * h;
+    return ImageClip{ this->etvImage, tx, ty, tw, th };
+}
+
+
+std::shared_ptr<CellImage> ImageClip::clip(short i, short j)
+{
+    return std::make_shared<ImageClip>(clip(i * 0.5f, j * 0.5f, 0.5f, 0.5f));
+}
+
+
+int ImageClip::getRecalcPriority() const
+{
+    return int(1.0f / tw);
+}
+
+
+QuadImage::QuadImage(std::shared_ptr<CellImage> i00,
+                 std::shared_ptr<CellImage> i01,
+                 std::shared_ptr<CellImage> i10,
+                 std::shared_ptr<CellImage> i11) :
+    cells{ { std::move(i00), std::move(i01) },
+           { std::move(i10), std::move(i11) } }
+{
+}
+
+
+QuadImage::~QuadImage(void)
+{
+}
+
+
+void QuadImage::drawRect(QOpenGLShaderProgram* program,
+        float x, float y, float width, float height)
+{
+    for (int i = 0; i < 2; i++) {
+        for (int j = 0; j < 2; j++) {
+            this->cells[i][j]->drawRect(program,
+                                        x + i * 0.5f * width,
+                                        y + j * 0.5f * height,
+                                        width * 0.5f,
+                                        height * 0.5f);
+        }
+    }
+}
+
+
+std::shared_ptr<CellImage> QuadImage::clip(short i, short j)
+{
+    return cells[i][j];
+}
+
+
+int QuadImage::getRecalcPriority() const
+{
+    return 1;
+}

+ 85 - 0
FractalWidgetUtils.h

@@ -0,0 +1,85 @@
+#ifndef FRACTALWIDGETUTILS_H
+#define FRACTALWIDGETUTILS_H
+
+#include "Mandel.h"
+#include "EscapeTimeVisualWidget.h"
+#include <unordered_map>
+#include <QMetaType>
+
+using GridIndex = mnd::Integer;
+Q_DECLARE_METATYPE(GridIndex)
+Q_DECLARE_METATYPE(mnd::Real)
+
+
+class CellImage
+{
+public:
+    CellImage(void) = default;
+    CellImage(CellImage&& b) = default;
+    CellImage(const CellImage& b) = delete;
+    virtual ~CellImage(void);
+
+    virtual void drawRect(float x, float y, float width, float height) = 0;
+    virtual std::shared_ptr<CellImage> clip(short i, short j) = 0;
+    virtual int getRecalcPriority(void) const = 0;
+};
+
+
+class ImageClip :
+    public CellImage
+{
+    std::shared_ptr<ETVImage> etvImage;
+    float tx, ty, tw, th;
+public:
+    ImageClip(std::shared_ptr<ETVImage> tex,
+              float tx, float ty, float tw, float th);
+
+    inline ImageClip(std::shared_ptr<ETVImage> tex) :
+        ImageClip{ tex, 0.0f, 0.0f, 1.0f, 1.0f }
+    {}
+
+    ImageClip(ImageClip&&) = default;
+    ImageClip(const ImageClip&) = delete;
+
+    virtual ~ImageClip(void);
+
+    void drawRect(float x, float y, float width, float height) override;
+
+    ImageClip clip(float x, float y, float w, float h);
+    std::shared_ptr<CellImage> clip(short i, short j) override;
+    int getRecalcPriority(void) const override;
+};
+
+
+class QuadImage :
+    public CellImage
+{
+    std::shared_ptr<CellImage> cells[2][2];
+public:
+    QuadImage(std::shared_ptr<CellImage> i00,
+              std::shared_ptr<CellImage> i01,
+              std::shared_ptr<CellImage> i10,
+              std::shared_ptr<CellImage> i11);
+
+    QuadImage(QuadImage&&) = default;
+    QuadImage(const QuadImage&) = delete;
+
+    virtual ~QuadImage(void);
+
+    void drawRect(float x, float y, float width, float height) override;
+    std::shared_ptr<CellImage> clip(short i, short j) override;
+    int getRecalcPriority(void) const override;
+};
+
+
+struct GridElement
+{
+    bool enoughResolution;
+    std::shared_ptr<CellImage> img;
+    inline GridElement(bool enoughResolution, std::shared_ptr<CellImage> img) :
+        enoughResolution{ enoughResolution },
+        img{ std::move(img) }
+    {}
+};
+
+#endif // FRACTALWIDGETUTILS_H

+ 64 - 0
FractalZoomWidget.cpp

@@ -0,0 +1,64 @@
+#include "FractalZoomWidget.h"
+
+
+SliceGrid::SliceGrid(FractalZoomWidget& owner, int level) :
+    owner{ owner },
+    level{ level },
+    dpp{ owner.getDpp(level) }
+{
+}
+
+
+std::pair<GridIndex, GridIndex> SliceGrid::getCellIndices(mnd::Real x, mnd::Real y)
+{
+    return { GridIndex(mnd::floor(x / dpp / FractalZoomWidget::chunkSize)), GridIndex(mnd::floor(y / dpp / MandelView::chunkSize)) };
+}
+
+
+std::pair<mnd::Real, mnd::Real> SliceGrid::getPositions(GridIndex x, GridIndex y)
+{
+    return { mnd::Real(x) * dpp * FractalZoomWidget::chunkSize, mnd::Real(y) * dpp * MandelView::chunkSize };
+}
+
+
+GridElement* SliceGrid::getCell(GridIndex i, GridIndex j)
+{
+    auto cIt = cells.find({i, j});
+    if (cIt != cells.end()) {
+        return cIt->second.get();
+    }
+    else {
+        return nullptr;
+    }
+}
+
+
+void TexGrid::setCell(GridIndex i, GridIndex j, std::unique_ptr<GridElement> tex)
+{
+    cells[{i, j}] = std::move(tex);
+}
+
+
+void TexGrid::clearCells(void)
+{
+    cells.clear();
+}
+
+
+void TexGrid::clearUncleanCells(void)
+{
+    for (auto it = cells.begin(); it != cells.end();) {
+        if (it->second->img->getRecalcPriority() > 1)
+            cells.erase(it++);
+        else ++it;
+    }
+}
+
+
+FractalZoomWidget::FractalZoomWidget(QWidget* parent) :
+    EscapeTimeVisualWidget{ parent }
+{
+}
+
+
+

+ 48 - 0
FractalZoomWidget.h

@@ -0,0 +1,48 @@
+#ifndef FRACTALZOOMWIDGET_H
+#define FRACTALZOOMWIDGET_H
+
+#include "EscapeTimeVisualWidget.h"
+#include "FractalWidgetUtils.h"
+
+class FractalZoomWidget;
+
+
+///
+/// \brief represents a grid of images at a certain depth
+///        in the fractal.
+///
+class SliceGrid
+{
+public:
+    FractalZoomWidget& owner;
+    int level;
+    mnd::Real dpp;
+    std::unordered_map<std::pair<GridIndex, GridIndex>, std::unique_ptr<GridElement>, IndexPairHash> cells;
+public:
+    SliceGrid(FractalZoomWidget& owner, int level);
+
+    std::pair<GridIndex, GridIndex> getCellIndices(mnd::Real x, mnd::Real y);
+    std::pair<mnd::Real, mnd::Real> getPositions(GridIndex i, GridIndex j);
+    GridElement* getCell(GridIndex i, GridIndex j);
+    void setCell(GridIndex i, GridIndex j, std::unique_ptr<GridElement> tex);
+
+    inline size_t countAllocatedCells(void) const { return cells.size(); }
+    void clearCells(void);
+    void clearUncleanCells(void);
+};
+
+
+class FractalZoomWidget :
+    public EscapeTimeVisualWidget
+{
+    Q_OBJECT
+
+    mnd::MandelGenerator* generator;
+
+public:
+    static const int chunkSize = 256;
+
+    FractalZoomWidget(QWidget* parent = nullptr);
+};
+
+#endif // FRACTALZOOMWIDGET_H