diff --git a/src/interface/desktop/WidgetPreviewCanvas.cpp b/src/interface/desktop/WidgetPreviewCanvas.cpp index ade72a8..47d989a 100644 --- a/src/interface/desktop/WidgetPreviewCanvas.cpp +++ b/src/interface/desktop/WidgetPreviewCanvas.cpp @@ -1,48 +1,69 @@ #include "WidgetPreviewCanvas.h" +#include "tools.h" #include "Canvas.h" #include "CanvasPreview.h" +#include + WidgetPreviewCanvas::WidgetPreviewCanvas(QWidget *parent) : QWidget(parent), canvas(NULL) { + pixbuf = new QImage(); + inited = false; + startTimer(1000); } +WidgetPreviewCanvas::~WidgetPreviewCanvas() +{ + delete pixbuf; +} + void WidgetPreviewCanvas::setCanvas(const Canvas *canvas) { this->canvas = canvas; } +void WidgetPreviewCanvas::paintEvent(QPaintEvent *event) +{ + QPainter painter(this); + painter.drawImage(0, 0, *pixbuf); +} + void WidgetPreviewCanvas::canvasResized(int width, int height) { - // TODO + if (QSize(width, height) != this->size()) + { + setMaximumSize(width, height); + setMinimumSize(width, height); + resize(width, height); + + delete pixbuf; + pixbuf = new QImage(width, height, QImage::Format_ARGB32); + } } void WidgetPreviewCanvas::canvasCleared(const Color &col) { - // TODO + pixbuf->fill(colorToQColor(col)); } void WidgetPreviewCanvas::canvasPainted(int x, int y, const Color &col) { - // TODO + pixbuf->setPixel(x, pixbuf->height() - 1 - y, colorToQColor(col).rgb()); } void WidgetPreviewCanvas::timerEvent(QTimerEvent *) { - // Refresh the view - CanvasPreview *preview = canvas->getPreview(); if (canvas) { - int width = preview->getWidth(); - int height = preview->getHeight(); - - if (QSize(width, height) != this->size()) + if (!inited) { - setMaximumSize(width, height); - setMinimumSize(width, height); - resize(width, height); + canvas->getPreview()->initLive(this); + inited = true; } + + canvas->getPreview()->updateLive(this); } } diff --git a/src/interface/desktop/WidgetPreviewCanvas.h b/src/interface/desktop/WidgetPreviewCanvas.h index 6aacafe..a862fc9 100644 --- a/src/interface/desktop/WidgetPreviewCanvas.h +++ b/src/interface/desktop/WidgetPreviewCanvas.h @@ -17,12 +17,15 @@ class WidgetPreviewCanvas : public QWidget, public CanvasLiveClient Q_OBJECT public: explicit WidgetPreviewCanvas(QWidget *parent = 0); + virtual ~WidgetPreviewCanvas(); /*! * \brief Set the canvas to watch and display, null to stop watching. */ void setCanvas(const Canvas *canvas); + virtual void paintEvent(QPaintEvent* event); + protected: virtual void canvasResized(int width, int height); virtual void canvasCleared(const Color &col); @@ -30,7 +33,9 @@ protected: virtual void timerEvent(QTimerEvent *event); private: + QImage* pixbuf; const Canvas *canvas; + bool inited; }; } diff --git a/src/interface/desktop/common/freeformhelper.cpp b/src/interface/desktop/common/freeformhelper.cpp index 7a927f8..433867e 100644 --- a/src/interface/desktop/common/freeformhelper.cpp +++ b/src/interface/desktop/common/freeformhelper.cpp @@ -14,7 +14,7 @@ #include "dialogexplorer.h" #include "DesktopScenery.h" #include "BasePreview.h" -#include "SoftwareRenderer.h" +#include "SoftwareCanvasRenderer.h" #include "CameraDefinition.h" #include "tools.h" @@ -246,7 +246,8 @@ void FreeFormHelper::processExploreClicked() void FreeFormHelper::processRenderClicked() { - SoftwareRenderer renderer(DesktopScenery::getCurrent()); + SoftwareCanvasRenderer renderer; + renderer.setScenery(DesktopScenery::getCurrent()); emit needAlterRenderer(&renderer); diff --git a/src/interface/desktop/dialogrender.cpp b/src/interface/desktop/dialogrender.cpp index 59b6b70..ceec5b4 100644 --- a/src/interface/desktop/dialogrender.cpp +++ b/src/interface/desktop/dialogrender.cpp @@ -51,7 +51,7 @@ static void _renderUpdate(double progress) class RenderThread:public QThread { public: - RenderThread(DialogRender* dialog, SoftwareRenderer* renderer, RenderArea::RenderParams params):QThread() + RenderThread(DialogRender* dialog, SoftwareCanvasRenderer* renderer, RenderArea::RenderParams params):QThread() { _dialog = dialog; _renderer = renderer; @@ -59,12 +59,13 @@ public: } void run() { + _renderer->render(); _renderer->start(_params); _dialog->tellRenderEnded(); } private: DialogRender* _dialog; - SoftwareRenderer* _renderer; + SoftwareCanvasRenderer* _renderer; RenderArea::RenderParams _params; }; @@ -86,14 +87,14 @@ public: } }; -DialogRender::DialogRender(QWidget *parent, SoftwareRenderer* renderer): +DialogRender::DialogRender(QWidget *parent, SoftwareCanvasRenderer* renderer): QDialog(parent, Qt::WindowTitleHint | Qt::WindowMaximizeButtonHint | Qt::WindowMinimizeButtonHint | Qt::WindowCloseButtonHint) { pixbuf_lock = new QMutex(); pixbuf = new QImage(1, 1, QImage::Format_ARGB32); _current_dialog = this; _render_thread = NULL; - _renderer = renderer; + canvas_renderer = renderer; setModal(true); setWindowTitle(tr("Paysages 3D - Render")); @@ -105,7 +106,6 @@ DialogRender::DialogRender(QWidget *parent, SoftwareRenderer* renderer): _scroll->setWidget(area); layout()->addWidget(_scroll); - canvas_renderer = new SoftwareCanvasRenderer(); canvas_preview = new WidgetPreviewCanvas(this); canvas_preview->setCanvas(canvas_renderer->getCanvas()); layout()->addWidget(canvas_preview); @@ -159,7 +159,7 @@ DialogRender::~DialogRender() { if (_render_thread) { - _renderer->interrupt(); + canvas_renderer->interrupt(); _render_thread->wait(); delete _render_thread; @@ -190,9 +190,9 @@ void DialogRender::startRender(RenderArea::RenderParams params) canvas_renderer->setSize(params.width, params.height, params.antialias); applyRenderSize(params.width, params.height); - _renderer->setPreviewCallbacks(_renderStart, _renderDraw, _renderUpdate); + canvas_renderer->setPreviewCallbacks(_renderStart, _renderDraw, _renderUpdate); - _render_thread = new RenderThread(this, _renderer, params); + _render_thread = new RenderThread(this, canvas_renderer, params); _render_thread->start(); exec(); @@ -218,7 +218,7 @@ void DialogRender::saveRender() filepath = filepath.append(".png"); } std::string filepathstr = filepath.toStdString(); - if (_renderer->render_area->saveToFile((char*)filepathstr.c_str())) + if (canvas_renderer->render_area->saveToFile((char*)filepathstr.c_str())) { QMessageBox::information(this, "Message", QString(tr("The picture %1 has been saved.")).arg(filepath)); } @@ -232,13 +232,13 @@ void DialogRender::saveRender() void DialogRender::toneMappingChanged() { ColorProfile profile((ColorProfile::ToneMappingOperator)_tonemapping_control->currentIndex(), ((double)_exposure_control->value()) * 0.01); - _renderer->render_area->setToneMapping(profile); + canvas_renderer->render_area->setToneMapping(profile); } void DialogRender::loadLastRender() { - applyRenderSize(_renderer->render_width, _renderer->render_height); - _renderer->setPreviewCallbacks(_renderStart, _renderDraw, _renderUpdate); + applyRenderSize(canvas_renderer->render_width, canvas_renderer->render_height); + canvas_renderer->setPreviewCallbacks(_renderStart, _renderDraw, _renderUpdate); renderEnded(); toneMappingChanged(); diff --git a/src/interface/desktop/dialogrender.h b/src/interface/desktop/dialogrender.h index 62c6aa3..3ea599a 100644 --- a/src/interface/desktop/dialogrender.h +++ b/src/interface/desktop/dialogrender.h @@ -19,7 +19,7 @@ class DialogRender : public QDialog { Q_OBJECT public: - explicit DialogRender(QWidget *parent, SoftwareRenderer* renderer); + explicit DialogRender(QWidget *parent, SoftwareCanvasRenderer* renderer); ~DialogRender(); void tellRenderSize(int width, int height); @@ -56,7 +56,6 @@ private: QPushButton* _save_button; QThread* _render_thread; QLabel* _timer; - SoftwareRenderer* _renderer; QProgressBar* _progress; time_t _started; }; diff --git a/src/interface/desktop/formrender.cpp b/src/interface/desktop/formrender.cpp index 9a24dab..26f58a0 100644 --- a/src/interface/desktop/formrender.cpp +++ b/src/interface/desktop/formrender.cpp @@ -7,7 +7,7 @@ #include "tools.h" #include "DesktopScenery.h" #include "PackStream.h" -#include "SoftwareRenderer.h" +#include "SoftwareCanvasRenderer.h" #include "BasePreview.h" #include "CloudsDefinition.h" #include "CameraDefinition.h" @@ -103,7 +103,8 @@ void FormRender::startQuickRender() { delete _renderer; } - _renderer = new SoftwareRenderer(DesktopScenery::getCurrent()); + _renderer = new SoftwareCanvasRenderer(); + _renderer->setScenery(DesktopScenery::getCurrent()); _renderer_inited = true; DialogRender* dialog = new DialogRender(this, _renderer); @@ -119,7 +120,8 @@ void FormRender::startRender() { delete _renderer; } - _renderer = new SoftwareRenderer(DesktopScenery::getCurrent()); + _renderer = new SoftwareCanvasRenderer(); + _renderer->setScenery(DesktopScenery::getCurrent()); _renderer_inited = true; DialogRender* dialog = new DialogRender(this, _renderer); diff --git a/src/interface/desktop/formrender.h b/src/interface/desktop/formrender.h index b555575..9f4afcd 100644 --- a/src/interface/desktop/formrender.h +++ b/src/interface/desktop/formrender.h @@ -31,7 +31,7 @@ protected slots: private: RenderArea::RenderParams _params; CameraDefinition* _camera; - SoftwareRenderer* _renderer; + SoftwareCanvasRenderer* _renderer; bool _renderer_inited; BasePreview* _preview_landscape; Base2dPreviewRenderer* _preview_landscape_renderer; diff --git a/src/render/software/Canvas.cpp b/src/render/software/Canvas.cpp index ee82e3f..809b170 100644 --- a/src/render/software/Canvas.cpp +++ b/src/render/software/Canvas.cpp @@ -13,7 +13,7 @@ Canvas::Canvas() height = 1; portions.push_back(new CanvasPortion()); - preview = new CanvasPreview(); + preview = new CanvasPreview; } Canvas::~Canvas() @@ -27,8 +27,8 @@ Canvas::~Canvas() void Canvas::setSize(int width, int height) { - horizontal_portion_count = 1 + width / 400; - vertical_portion_count = 1 + height / 400; + horizontal_portion_count = 1 + (width - 1) / 400; + vertical_portion_count = 1 + (height - 1) / 400; int portion_width = width / horizontal_portion_count; int portion_height = height / vertical_portion_count; @@ -47,7 +47,7 @@ void Canvas::setSize(int width, int height) done_width = 0; for (int x = 0; x < horizontal_portion_count; x++) { - CanvasPortion *portion = new CanvasPortion(); + CanvasPortion *portion = new CanvasPortion(preview); portion->setSize((x == horizontal_portion_count - 1) ? width - done_width : portion_width, (y == vertical_portion_count - 1) ? height - done_height : portion_height); diff --git a/src/render/software/CanvasFragment.cpp b/src/render/software/CanvasFragment.cpp index c5a5564..7af7b90 100644 --- a/src/render/software/CanvasFragment.cpp +++ b/src/render/software/CanvasFragment.cpp @@ -7,5 +7,10 @@ CanvasFragment::CanvasFragment() CanvasFragment::CanvasFragment(double z, const Vector3 &location, int client, bool opaque): z(z), location(location), client(client), opaque(opaque) { - color = COLOR_BLACK; + color = COLOR_WHITE; +} + +void CanvasFragment::setColor(const Color &col) +{ + color = col; } diff --git a/src/render/software/CanvasFragment.h b/src/render/software/CanvasFragment.h index 7175add..520415c 100644 --- a/src/render/software/CanvasFragment.h +++ b/src/render/software/CanvasFragment.h @@ -18,6 +18,8 @@ public: CanvasFragment(); CanvasFragment(double z, const Vector3 &location, int client=0, bool opaque=true); + void setColor(const Color &col); + inline bool getOpaque() const {return opaque;} inline double getZ() const {return z;} inline const Vector3 &getLocation() const {return location;} diff --git a/src/render/software/CanvasPixel.cpp b/src/render/software/CanvasPixel.cpp index 7cc4cf6..b4287ab 100644 --- a/src/render/software/CanvasPixel.cpp +++ b/src/render/software/CanvasPixel.cpp @@ -5,6 +5,7 @@ CanvasPixel::CanvasPixel() { count = 0; + composite = COLOR_BLACK; } const CanvasFragment *CanvasPixel::getFrontFragment() const @@ -85,4 +86,16 @@ void CanvasPixel::pushFragment(const CanvasFragment &fragment) } } } + + updateComposite(); +} + +void CanvasPixel::updateComposite() +{ + Color result(0.0, 0.0, 0.0, 1.0); + for (int i = 0; i < count; i++) + { + result.mask(fragments[i].getColor()); + } + composite = result; } diff --git a/src/render/software/CanvasPixel.h b/src/render/software/CanvasPixel.h index da32e3f..bcbab0a 100644 --- a/src/render/software/CanvasPixel.h +++ b/src/render/software/CanvasPixel.h @@ -21,14 +21,17 @@ public: CanvasPixel(); inline int getFragmentCount() const {return count;} + inline const Color &getComposite() const {return composite;} const CanvasFragment *getFrontFragment() const; void reset(); void pushFragment(const CanvasFragment &fragment); + void updateComposite(); private: int count; CanvasFragment fragments[MAX_FRAGMENT_COUNT]; + Color composite; }; } diff --git a/src/render/software/CanvasPortion.cpp b/src/render/software/CanvasPortion.cpp index e017592..7b1438f 100644 --- a/src/render/software/CanvasPortion.cpp +++ b/src/render/software/CanvasPortion.cpp @@ -3,13 +3,15 @@ #include #include "CanvasPixel.h" +#include "CanvasPreview.h" #define CHECK_COORDINATES() assert(x >= 0); \ assert(x < width); \ assert(y >= 0); \ assert(y < height) -CanvasPortion::CanvasPortion() +CanvasPortion::CanvasPortion(CanvasPreview* preview): + preview(preview) { width = 1; height = 1; @@ -58,5 +60,12 @@ void CanvasPortion::pushFragment(int x, int y, const CanvasFragment &fragment) CHECK_COORDINATES(); CanvasPixel &pixel = pixels[y * width + x]; + Color old_color = pixel.getComposite(); + pixel.pushFragment(fragment); + + if (preview) + { + preview->pushPixel(x, y, old_color, pixel.getComposite()); + } } diff --git a/src/render/software/CanvasPortion.h b/src/render/software/CanvasPortion.h index e735b7f..85be35e 100644 --- a/src/render/software/CanvasPortion.h +++ b/src/render/software/CanvasPortion.h @@ -14,7 +14,7 @@ namespace software { class SOFTWARESHARED_EXPORT CanvasPortion { public: - CanvasPortion(); + CanvasPortion(CanvasPreview* preview=NULL); ~CanvasPortion(); inline int getWidth() const {return width;} @@ -36,6 +36,7 @@ private: int width; int height; CanvasPixel *pixels; + CanvasPreview* preview; }; } diff --git a/src/render/software/CanvasPreview.cpp b/src/render/software/CanvasPreview.cpp index 3a7a427..61b63ef 100644 --- a/src/render/software/CanvasPreview.cpp +++ b/src/render/software/CanvasPreview.cpp @@ -1,22 +1,137 @@ #include "CanvasPreview.h" +#include "Color.h" +#include "CanvasLiveClient.h" +#include "Mutex.h" + +#include + CanvasPreview::CanvasPreview() { width = 1; height = 1; - pixels = new CanvasPreviewPixel[1]; + pixels = new Color[1]; + + dirty_left = 1; + dirty_right = -1; + dirty_down = 1; + dirty_up = -1; + + lock = new Mutex(); } CanvasPreview::~CanvasPreview() { delete [] pixels; + delete lock; } void CanvasPreview::setSize(int real_width, int real_height, int preview_width, int preview_height) { + lock->acquire(); + delete [] pixels; - pixels = new CanvasPreviewPixel[preview_width * preview_height]; + pixels = new Color[preview_width * preview_height]; width = preview_width; height = preview_height; + + dirty_left = width; + dirty_right = -1; + dirty_down = height; + dirty_up = -1; + + lock->release(); + + reset(); +} + +void CanvasPreview::reset() +{ + lock->acquire(); + + int n = width * height; + for (int i = 0; i < n; i++) + { + pixels[i] = COLOR_BLACK; + } + + lock->release(); +} + +void CanvasPreview::initLive(CanvasLiveClient *client) +{ + client->canvasResized(width, height); + client->canvasCleared(COLOR_BLACK); + + setAllDirty(); +} + +void CanvasPreview::updateLive(CanvasLiveClient *client) +{ + int x, y; + + lock->acquire(); + + for (y = dirty_down; y <= dirty_up; y++) + { + for (x = dirty_left; x <= dirty_right; x++) + { + client->canvasPainted(x, y, pixels[y * width + x]); + } + } + + dirty_left = width; + dirty_right = -1; + dirty_down = height; + dirty_up = -1; + + lock->release(); +} + +void CanvasPreview::pushPixel(int real_x, int real_y, const Color &old_color, const Color &new_color) +{ + lock->acquire(); + + // TODO Assert-check and transform coordinates + int x = real_x; + int y = real_y; + double fact = 1.0; + + Color* pixel = pixels + (real_y * width + real_x); + pixel->r = pixel->r - old_color.r * fact + new_color.r * fact; + pixel->g = pixel->g - old_color.g * fact + new_color.g * fact; + pixel->b = pixel->b - old_color.b * fact + new_color.b * fact; + + // Set pixel dirty + if (x < dirty_left) + { + dirty_left = x; + } + if (x > dirty_right) + { + dirty_right = x; + } + if (y < dirty_down) + { + dirty_down = y; + } + if (y > dirty_up) + { + dirty_up = y; + } + + lock->release(); +} + +void CanvasPreview::setAllDirty() +{ + lock->acquire(); + + dirty_left = 0; + dirty_right = width - 1; + dirty_down = 0; + dirty_up = height - 1; + + lock->release(); } diff --git a/src/render/software/CanvasPreview.h b/src/render/software/CanvasPreview.h index f10e2dc..e6607d3 100644 --- a/src/render/software/CanvasPreview.h +++ b/src/render/software/CanvasPreview.h @@ -6,12 +6,6 @@ namespace paysages { namespace software { -typedef struct { - double red; - double green; - double blue; -} CanvasPreviewPixel; - /** * @brief Smaller preview of a Canvas rendering, that can be watched live. */ @@ -27,15 +21,25 @@ public: void setSize(int real_width, int real_height, int preview_width, int preview_height); void reset(); - void initLive(CanvasLiveClient &client); - void updateLive(CanvasLiveClient &client); + void initLive(CanvasLiveClient *client); + void updateLive(CanvasLiveClient *client); - void pushPixel(int real_x, int real_y, Color old_color, Color new_color); + void pushPixel(int real_x, int real_y, const Color &old_color, const Color &new_color); + +protected: + void setAllDirty(); private: - CanvasPreviewPixel *pixels; + Mutex *lock; + + Color *pixels; int width; int height; + + int dirty_left; + int dirty_right; + int dirty_down; + int dirty_up; }; } diff --git a/src/render/software/Rasterizer.cpp b/src/render/software/Rasterizer.cpp index 2545ba1..f54a8c6 100644 --- a/src/render/software/Rasterizer.cpp +++ b/src/render/software/Rasterizer.cpp @@ -1,5 +1,337 @@ #include "Rasterizer.h" -Rasterizer::Rasterizer() +#include "SoftwareRenderer.h" +#include "CameraDefinition.h" +#include "CanvasPortion.h" +#include "CanvasFragment.h" +#include "Vector3.h" + +struct paysages::software::ScanPoint +{ + int x; + int y; + struct { + double x; + double y; + double z; + } pixel; + struct { + double x; + double y; + double z; + } location; + int client; +}; + +struct paysages::software::RenderScanlines +{ + ScanPoint* up; + ScanPoint* down; + int left; + int right; +}; + +Rasterizer::Rasterizer(SoftwareRenderer* renderer, int client_id): + renderer(renderer), client_id(client_id) { } + +Rasterizer::~Rasterizer() +{ +} + +void Rasterizer::pushProjectedTriangle(CanvasPortion *canvas, const Vector3 &pixel1, const Vector3 &pixel2, const Vector3 &pixel3, const Vector3 &location1, const Vector3 &location2, const Vector3 &location3) +{ + ScanPoint point1, point2, point3; + double limit_width = (double)(canvas->getWidth() - 1); + double limit_height = (double)(canvas->getHeight() - 1); + + /* Filter if outside screen */ + if (pixel1.z < 1.0 || pixel2.z < 1.0 || pixel3.z < 1.0 || (pixel1.x < 0.0 && pixel2.x < 0.0 && pixel3.x < 0.0) || (pixel1.y < 0.0 && pixel2.y < 0.0 && pixel3.y < 0.0) || (pixel1.x > limit_width && pixel2.x > limit_width && pixel3.x > limit_width) || (pixel1.y > limit_height && pixel2.y > limit_height && pixel3.y > limit_height)) + { + return; + } + + /* Prepare vertices */ + // FIXME Apply canvas portion offset + point1.pixel.x = pixel1.x; + point1.pixel.y = pixel1.y; + point1.pixel.z = pixel1.z; + point1.location.x = location1.x; + point1.location.y = location1.y; + point1.location.z = location1.z; + point1.client = client_id; + + point2.pixel.x = pixel2.x; + point2.pixel.y = pixel2.y; + point2.pixel.z = pixel2.z; + point2.location.x = location2.x; + point2.location.y = location2.y; + point2.location.z = location2.z; + point2.client = client_id; + + point3.pixel.x = pixel3.x; + point3.pixel.y = pixel3.y; + point3.pixel.z = pixel3.z; + point3.location.x = location3.x; + point3.location.y = location3.y; + point3.location.z = location3.z; + point3.client = client_id; + + /* Prepare scanlines */ + // TODO Don't create scanlines for each triangles (one by thread is more appropriate) + RenderScanlines scanlines; + int width = canvas->getWidth(); + scanlines.left = width; + scanlines.right = -1; + scanlines.up = new ScanPoint[width]; + scanlines.down = new ScanPoint[width]; + + /* Render edges in scanlines */ + pushScanLineEdge(canvas, &scanlines, &point1, &point2); + pushScanLineEdge(canvas, &scanlines, &point2, &point3); + pushScanLineEdge(canvas, &scanlines, &point3, &point1); + + /* Commit scanlines to area */ + renderScanLines(canvas, &scanlines); + + /* Free scalines */ + delete[] scanlines.up; + delete[] scanlines.down; +} + +void Rasterizer::pushTriangle(CanvasPortion *canvas, const Vector3 &v1, const Vector3 &v2, const Vector3 &v3) +{ + Vector3 p1, p2, p3; + + p1 = getRenderer()->projectPoint(v1); + p2 = getRenderer()->projectPoint(v2); + p3 = getRenderer()->projectPoint(v3); + + pushProjectedTriangle(canvas, p1, p2, p3, v1, v2, v3); +} + +void Rasterizer::pushQuad(CanvasPortion *canvas, const Vector3 &v1, const Vector3 &v2, const Vector3 &v3, const Vector3 &v4) +{ + pushTriangle(canvas, v2, v3, v1); + pushTriangle(canvas, v4, v1, v3); +} + +void Rasterizer::pushDisplacedTriangle(CanvasPortion *canvas, const Vector3 &v1, const Vector3 &v2, const Vector3 &v3, const Vector3 &ov1, const Vector3 &ov2, const Vector3 &ov3) +{ + Vector3 p1, p2, p3; + + p1 = getRenderer()->projectPoint(v1); + p2 = getRenderer()->projectPoint(v2); + p3 = getRenderer()->projectPoint(v3); + + pushProjectedTriangle(canvas, p1, p2, p3, ov1, ov2, ov3); +} + +void Rasterizer::pushDisplacedQuad(CanvasPortion *canvas, const Vector3 &v1, const Vector3 &v2, const Vector3 &v3, const Vector3 &v4, const Vector3 &ov1, const Vector3 &ov2, const Vector3 &ov3, const Vector3 &ov4) +{ + pushDisplacedTriangle(canvas, v2, v3, v1, ov2, ov3, ov1); + pushDisplacedTriangle(canvas, v4, v1, v3, ov4, ov1, ov3); +} + +void Rasterizer::rasterizeToCanvas(CanvasPortion *) +{ +} + +void Rasterizer::scanGetDiff(ScanPoint* v1, ScanPoint* v2, ScanPoint* result) +{ + result->pixel.x = v2->pixel.x - v1->pixel.x; + result->pixel.y = v2->pixel.y - v1->pixel.y; + result->pixel.z = v2->pixel.z - v1->pixel.z; + result->location.x = v2->location.x - v1->location.x; + result->location.y = v2->location.y - v1->location.y; + result->location.z = v2->location.z - v1->location.z; + result->client = v1->client; +} + +void Rasterizer::scanInterpolate(CameraDefinition* camera, ScanPoint* v1, ScanPoint* diff, double value, ScanPoint* result) +{ + Vector3 vec1(v1->pixel.x, v1->pixel.y, v1->pixel.z); + Vector3 vecdiff(diff->pixel.x, diff->pixel.y, diff->pixel.z); + double v1depth = camera->getRealDepth(vec1); + double v2depth = camera->getRealDepth(vec1.add(vecdiff)); + double factor = ((1.0 - value) / v1depth + value / v2depth); + + result->pixel.x = v1->pixel.x + diff->pixel.x * value; + result->pixel.y = v1->pixel.y + diff->pixel.y * value; + result->pixel.z = v1->pixel.z + diff->pixel.z * value; + result->location.x = ((1.0 - value) * (v1->location.x / v1depth) + value * (v1->location.x + diff->location.x) / v2depth) / factor; + result->location.y = ((1.0 - value) * (v1->location.y / v1depth) + value * (v1->location.y + diff->location.y) / v2depth) / factor; + result->location.z = ((1.0 - value) * (v1->location.z / v1depth) + value * (v1->location.z + diff->location.z) / v2depth) / factor; + result->client = v1->client; +} + +void Rasterizer::pushScanPoint(CanvasPortion* canvas, RenderScanlines* scanlines, ScanPoint* point) +{ + point->x = (int)floor(point->pixel.x); + point->y = (int)floor(point->pixel.y); + + if (point->x < 0 || point->x >= canvas->getWidth()) + { + // Point outside scanline range + return; + } + else if (scanlines->right < 0) + { + // First point pushed + scanlines->left = point->x; + scanlines->right = point->x; + scanlines->up[point->x] = *point; + scanlines->down[point->x] = *point; + } + else if (point->x > scanlines->right) + { + // Grow scanlines to right + for (int x = scanlines->right + 1; x < point->x; x++) + { + scanlines->up[x].y = -1; + scanlines->down[x].y = canvas->getHeight(); + } + scanlines->right = point->x; + scanlines->up[point->x] = *point; + scanlines->down[point->x] = *point; + } + else if (point->x < scanlines->left) + { + // Grow scanlines to left + for (int x = point->x + 1; x < scanlines->left; x++) + { + scanlines->up[x].y = -1; + scanlines->down[x].y = canvas->getHeight(); + } + scanlines->left = point->x; + scanlines->up[point->x] = *point; + scanlines->down[point->x] = *point; + } + else + { + // Expand existing scanline + if (point->y > scanlines->up[point->x].y) + { + scanlines->up[point->x] = *point; + } + if (point->y < scanlines->down[point->x].y) + { + scanlines->down[point->x] = *point; + } + } +} + +void Rasterizer::pushScanLineEdge(CanvasPortion *canvas, RenderScanlines *scanlines, ScanPoint *point1, ScanPoint *point2) +{ + double dx, fx; + ScanPoint diff, point; + int startx = lround(point1->pixel.x); + int endx = lround(point2->pixel.x); + int curx; + + if (endx < startx) + { + pushScanLineEdge(canvas, scanlines, point2, point1); + } + else if (endx < 0 || startx >= canvas->getWidth()) + { + return; + } + else if (startx == endx) + { + pushScanPoint(canvas, scanlines, point1); + pushScanPoint(canvas, scanlines, point2); + } + else + { + if (startx < 0) + { + startx = 0; + } + if (endx >= canvas->getWidth()) + { + endx = canvas->getWidth() - 1; + } + + dx = point2->pixel.x - point1->pixel.x; + scanGetDiff(point1, point2, &diff); + for (curx = startx; curx <= endx; curx++) + { + fx = (double)curx + 0.5; + if (fx < point1->pixel.x) + { + fx = point1->pixel.x; + } + else if (fx > point2->pixel.x) + { + fx = point2->pixel.x; + } + fx = fx - point1->pixel.x; + scanInterpolate(renderer->render_camera, point1, &diff, fx / dx, &point); + + /*point.pixel.x = (double)curx;*/ + + pushScanPoint(canvas, scanlines, &point); + } + } +} + +void Rasterizer::renderScanLines(CanvasPortion *canvas, RenderScanlines* scanlines) +{ + int x, starty, endy, cury; + ScanPoint diff; + double dy, fy; + ScanPoint up, down, current; + + if (scanlines->right > 0) + { + for (x = scanlines->left; x <= scanlines->right; x++) + { + up = scanlines->up[x]; + down = scanlines->down[x]; + + starty = down.y; + endy = up.y; + + if (endy < 0 || starty >= canvas->getHeight()) + { + continue; + } + + if (starty < 0) + { + starty = 0; + } + if (endy >= canvas->getHeight()) + { + endy = canvas->getHeight() - 1; + } + + dy = up.pixel.y - down.pixel.y; + scanGetDiff(&down, &up, &diff); + + current.x = x; + for (cury = starty; cury <= endy; cury++) + { + fy = (double)cury + 0.5; + if (fy < down.pixel.y) + { + fy = down.pixel.y; + } + else if (fy > up.pixel.y) + { + fy = up.pixel.y; + } + fy = fy - down.pixel.y; + + current.y = cury; + scanInterpolate(renderer->render_camera, &down, &diff, fy / dy, ¤t); + + CanvasFragment fragment(current.pixel.z, Vector3(current.location.x, current.location.y, current.location.z), current.client); + fragment.setColor((cury == starty || cury == endy) ? COLOR_GREY : COLOR_WHITE); + canvas->pushFragment(current.x, current.y, fragment); + } + } + } +} diff --git a/src/render/software/Rasterizer.h b/src/render/software/Rasterizer.h index 422a8e2..ec75297 100644 --- a/src/render/software/Rasterizer.h +++ b/src/render/software/Rasterizer.h @@ -6,13 +6,40 @@ namespace paysages { namespace software { +typedef struct ScanPoint ScanPoint; +typedef struct RenderScanlines RenderScanlines; + /** * @brief Base abstract class for scenery pieces that can be rasterized to polygons. */ class SOFTWARESHARED_EXPORT Rasterizer { public: - Rasterizer(); + Rasterizer(SoftwareRenderer *renderer, int client_id); + virtual ~Rasterizer(); + + inline SoftwareRenderer *getRenderer() const {return renderer;} + + virtual void rasterize() = 0; + virtual void rasterizeToCanvas(CanvasPortion* canvas); + +protected: + void pushProjectedTriangle(CanvasPortion *canvas, const Vector3 &pixel1, const Vector3 &pixel2, const Vector3 &pixel3, const Vector3 &location1, const Vector3 &location2, const Vector3 &location3); + + void pushTriangle(CanvasPortion *canvas, const Vector3 &v1, const Vector3 &v2, const Vector3 &v3); + void pushQuad(CanvasPortion *canvas, const Vector3 &v1, const Vector3 &v2, const Vector3 &v3, const Vector3 &v4); + void pushDisplacedTriangle(CanvasPortion *canvas, const Vector3 &v1, const Vector3 &v2, const Vector3 &v3, const Vector3 &ov1, const Vector3 &ov2, const Vector3 &ov3); + void pushDisplacedQuad(CanvasPortion *canvas, const Vector3 &v1, const Vector3 &v2, const Vector3 &v3, const Vector3 &v4, const Vector3 &ov1, const Vector3 &ov2, const Vector3 &ov3, const Vector3 &ov4); + + SoftwareRenderer *renderer; + int client_id; + +private: + void scanGetDiff(ScanPoint *v1, ScanPoint *v2, ScanPoint *result); + void scanInterpolate(CameraDefinition *camera, ScanPoint *v1, ScanPoint *diff, double value, ScanPoint *result); + void pushScanPoint(CanvasPortion *canvas, RenderScanlines *scanlines, ScanPoint *point); + void pushScanLineEdge(CanvasPortion *canvas, RenderScanlines *scanlines, ScanPoint *point1, ScanPoint *point2); + void renderScanLines(CanvasPortion *canvas, RenderScanlines *scanlines); }; } diff --git a/src/render/software/SkyRasterizer.cpp b/src/render/software/SkyRasterizer.cpp index 226e070..b0016a3 100644 --- a/src/render/software/SkyRasterizer.cpp +++ b/src/render/software/SkyRasterizer.cpp @@ -6,11 +6,12 @@ #include "AtmosphereRenderer.h" #include "AtmosphereResult.h" #include "CloudsRenderer.h" +#include "Rasterizer.h" #define SPHERE_SIZE 20000.0 -SkyRasterizer::SkyRasterizer(SoftwareRenderer* renderer): - renderer(renderer) +SkyRasterizer::SkyRasterizer(SoftwareRenderer* renderer, int client_id): + Rasterizer(renderer, client_id) { } @@ -83,3 +84,58 @@ void SkyRasterizer::rasterize() } } } + +void SkyRasterizer::rasterizeToCanvas(CanvasPortion* canvas) +{ + int res_i, res_j; + int i, j; + double step_i, step_j; + double current_i, current_j; + Vector3 vertex1, vertex2, vertex3, vertex4; + Vector3 camera_location, direction; + + res_i = renderer->render_quality * 40; + res_j = renderer->render_quality * 20; + step_i = M_PI * 2.0 / (double)res_i; + step_j = M_PI / (double)res_j; + + camera_location = renderer->getCameraLocation(VECTOR_ZERO); + + for (j = 0; j < res_j; j++) + { + if (!renderer->addRenderProgress(0.0)) + { + return; + } + + current_j = (double)(j - res_j / 2) * step_j; + + for (i = 0; i < res_i; i++) + { + current_i = (double)i * step_i; + + direction.x = SPHERE_SIZE * cos(current_i) * cos(current_j); + direction.y = SPHERE_SIZE * sin(current_j); + direction.z = SPHERE_SIZE * sin(current_i) * cos(current_j); + vertex1 = camera_location.add(direction); + + direction.x = SPHERE_SIZE * cos(current_i + step_i) * cos(current_j); + direction.y = SPHERE_SIZE * sin(current_j); + direction.z = SPHERE_SIZE * sin(current_i + step_i) * cos(current_j); + vertex2 = camera_location.add(direction); + + direction.x = SPHERE_SIZE * cos(current_i + step_i) * cos(current_j + step_j); + direction.y = SPHERE_SIZE * sin(current_j + step_j); + direction.z = SPHERE_SIZE * sin(current_i + step_i) * cos(current_j + step_j); + vertex3 = camera_location.add(direction); + + direction.x = SPHERE_SIZE * cos(current_i) * cos(current_j + step_j); + direction.y = SPHERE_SIZE * sin(current_j + step_j); + direction.z = SPHERE_SIZE * sin(current_i) * cos(current_j + step_j); + vertex4 = camera_location.add(direction); + + /* TODO Triangles at poles */ + pushQuad(canvas, vertex1, vertex4, vertex3, vertex2); + } + } +} diff --git a/src/render/software/SkyRasterizer.h b/src/render/software/SkyRasterizer.h index c6432fc..3184df3 100644 --- a/src/render/software/SkyRasterizer.h +++ b/src/render/software/SkyRasterizer.h @@ -11,11 +11,10 @@ namespace software { class SOFTWARESHARED_EXPORT SkyRasterizer: public Rasterizer { public: - SkyRasterizer(SoftwareRenderer* renderer); - void rasterize(); + SkyRasterizer(SoftwareRenderer* renderer, int client_id); -private: - SoftwareRenderer* renderer; + virtual void rasterize(); + virtual void rasterizeToCanvas(CanvasPortion* canvas); }; } diff --git a/src/render/software/SoftwareCanvasRenderer.cpp b/src/render/software/SoftwareCanvasRenderer.cpp index f8c9742..684f573 100644 --- a/src/render/software/SoftwareCanvasRenderer.cpp +++ b/src/render/software/SoftwareCanvasRenderer.cpp @@ -3,18 +3,29 @@ #include "Rasterizer.h" #include "SoftwareRenderer.h" #include "Canvas.h" +#include "TerrainRasterizer.h" +#include "WaterRasterizer.h" +#include "SkyRasterizer.h" +#include "CameraDefinition.h" SoftwareCanvasRenderer::SoftwareCanvasRenderer() { started = false; - renderer = new SoftwareRenderer(); canvas = new Canvas(); + + rasterizers.push_back(new TerrainRasterizer(this, 0)); + rasterizers.push_back(new WaterRasterizer(this, 1)); + rasterizers.push_back(new SkyRasterizer(this, 2)); } SoftwareCanvasRenderer::~SoftwareCanvasRenderer() { - delete renderer; delete canvas; + + for (auto &rasterizer: rasterizers) + { + delete rasterizer; + } } void SoftwareCanvasRenderer::setSize(int width, int height, int samples) @@ -31,17 +42,22 @@ void SoftwareCanvasRenderer::render() started = true; CanvasPortion *portion = canvas->at(0, 0); + render_camera->setRenderSize(canvas->getWidth(), canvas->getHeight()); + rasterize(portion, true); postProcess(portion, true); } +const std::vector &SoftwareCanvasRenderer::getRasterizers() const +{ + return rasterizers; +} + void SoftwareCanvasRenderer::rasterize(CanvasPortion *portion, bool threaded) { - std::vector rasterizers; - renderer->getRasterizers(&rasterizers); - - for (auto &rasterizer:rasterizers) + for (auto &rasterizer:getRasterizers()) { + rasterizer->rasterizeToCanvas(portion); } } diff --git a/src/render/software/SoftwareCanvasRenderer.h b/src/render/software/SoftwareCanvasRenderer.h index 847fbd7..3f51569 100644 --- a/src/render/software/SoftwareCanvasRenderer.h +++ b/src/render/software/SoftwareCanvasRenderer.h @@ -3,6 +3,8 @@ #include "software_global.h" +#include "SoftwareRenderer.h" + namespace paysages { namespace software { @@ -14,11 +16,11 @@ namespace software { * * It tries to keep a canvas portion rasterized ahead of the post processing. */ -class SOFTWARESHARED_EXPORT SoftwareCanvasRenderer +class SOFTWARESHARED_EXPORT SoftwareCanvasRenderer: public SoftwareRenderer { public: SoftwareCanvasRenderer(); - ~SoftwareCanvasRenderer(); + virtual ~SoftwareCanvasRenderer(); inline const Canvas *getCanvas() const {return canvas;} @@ -34,6 +36,11 @@ public: */ void render(); + /*! + * \brief Get the list of objects that can be rasterized to polygons on a canvas. + */ + virtual const std::vector &getRasterizers() const; + protected: /** * @brief Rasterize the scenery into a canvas portion. @@ -50,8 +57,8 @@ protected: void postProcess(CanvasPortion* portion, bool threaded=true); private: - SoftwareRenderer* renderer; Canvas* canvas; + std::vector rasterizers; bool started; }; diff --git a/src/render/software/SoftwareRenderer.cpp b/src/render/software/SoftwareRenderer.cpp index 12cdb64..2af4f4a 100644 --- a/src/render/software/SoftwareRenderer.cpp +++ b/src/render/software/SoftwareRenderer.cpp @@ -108,22 +108,15 @@ void SoftwareRenderer::prepare() //fluid_medium->registerMedium(water_renderer); } -void SoftwareRenderer::getRasterizers(std::vector *array) -{ - array->push_back(TerrainRasterizer(this)); - array->push_back(WaterRasterizer(this)); - array->push_back(SkyRasterizer(this)); -} - void SoftwareRenderer::rasterize() { - TerrainRasterizer terrain(this); - terrain.renderSurface(); + TerrainRasterizer terrain(this, 0); + terrain.rasterize(); - WaterRasterizer water(this); - water.renderSurface(); + WaterRasterizer water(this, 1); + water.rasterize(); - SkyRasterizer sky(this); + SkyRasterizer sky(this, 2); sky.rasterize(); } diff --git a/src/render/software/SoftwareRenderer.h b/src/render/software/SoftwareRenderer.h index 08b2d76..9f269f3 100644 --- a/src/render/software/SoftwareRenderer.h +++ b/src/render/software/SoftwareRenderer.h @@ -59,11 +59,6 @@ public: */ virtual void prepare(); - /*! - * \brief Get the list of objects that can be rasterized to polygons on a canvas. - */ - virtual void getRasterizers(std::vector *array); - /*! * \brief Start the rasterization process. */ diff --git a/src/render/software/TerrainRasterizer.cpp b/src/render/software/TerrainRasterizer.cpp index cf53310..36f1d17 100644 --- a/src/render/software/TerrainRasterizer.cpp +++ b/src/render/software/TerrainRasterizer.cpp @@ -9,8 +9,8 @@ #include "Scenery.h" #include "ParallelQueue.h" -TerrainRasterizer::TerrainRasterizer(SoftwareRenderer* renderer): - renderer(renderer) +TerrainRasterizer::TerrainRasterizer(SoftwareRenderer* renderer, int client_id): + Rasterizer(renderer, client_id) { } @@ -228,7 +228,7 @@ int TerrainRasterizer::processChunk(TerrainChunkInfo* chunk, double progress) return !renderer->render_interrupt; } -void TerrainRasterizer::renderSurface() +void TerrainRasterizer::rasterize() { queue = new ParallelQueue(); diff --git a/src/render/software/TerrainRasterizer.h b/src/render/software/TerrainRasterizer.h index 83c9eaf..7426b5f 100644 --- a/src/render/software/TerrainRasterizer.h +++ b/src/render/software/TerrainRasterizer.h @@ -22,7 +22,7 @@ public: } TerrainChunkInfo; public: - TerrainRasterizer(SoftwareRenderer* renderer); + TerrainRasterizer(SoftwareRenderer* renderer, int client_id); /** * Method called for each chunk tessellated by getTessellationInfo. @@ -46,10 +46,9 @@ public: * * This will push the rasterized quads in the render area, waiting for post process. */ - void renderSurface(); + virtual void rasterize(); private: - SoftwareRenderer* renderer; ParallelQueue* queue; }; diff --git a/src/render/software/WaterRasterizer.cpp b/src/render/software/WaterRasterizer.cpp index 2ec32d1..40350b2 100644 --- a/src/render/software/WaterRasterizer.cpp +++ b/src/render/software/WaterRasterizer.cpp @@ -4,8 +4,8 @@ #include "WaterRenderer.h" #include "ParallelQueue.h" -WaterRasterizer::WaterRasterizer(SoftwareRenderer* renderer): - renderer(renderer) +WaterRasterizer::WaterRasterizer(SoftwareRenderer* renderer, int client_id): + Rasterizer(renderer, client_id) { } @@ -64,7 +64,7 @@ static int _parallelJobCallback(ParallelQueue*, int, void* data, int stopping) return 0; } -void WaterRasterizer::renderSurface() +void WaterRasterizer::rasterize() { ParallelRasterInfo* info; ParallelQueue* queue; diff --git a/src/render/software/WaterRasterizer.h b/src/render/software/WaterRasterizer.h index 031f937..62062be 100644 --- a/src/render/software/WaterRasterizer.h +++ b/src/render/software/WaterRasterizer.h @@ -11,12 +11,9 @@ namespace software { class WaterRasterizer: public Rasterizer { public: - WaterRasterizer(SoftwareRenderer* renderer); + WaterRasterizer(SoftwareRenderer* renderer, int client_id); - void renderSurface(); - -private: - SoftwareRenderer* renderer; + virtual void rasterize(); }; }