WIP on new canvas system

This commit is contained in:
Michaël Lemaire 2014-06-12 17:45:59 +02:00
parent 8099361cc9
commit cf58bea1b7
28 changed files with 700 additions and 99 deletions

View file

@ -1,48 +1,69 @@
#include "WidgetPreviewCanvas.h"
#include "tools.h"
#include "Canvas.h"
#include "CanvasPreview.h"
#include <QPainter>
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
}
void WidgetPreviewCanvas::canvasCleared(const Color &col)
{
// TODO
}
void WidgetPreviewCanvas::canvasPainted(int x, int y, const Color &col)
{
// TODO
}
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())
{
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)
{
pixbuf->fill(colorToQColor(col));
}
void WidgetPreviewCanvas::canvasPainted(int x, int y, const Color &col)
{
pixbuf->setPixel(x, pixbuf->height() - 1 - y, colorToQColor(col).rgb());
}
void WidgetPreviewCanvas::timerEvent(QTimerEvent *)
{
if (canvas)
{
if (!inited)
{
canvas->getPreview()->initLive(this);
inited = true;
}
canvas->getPreview()->updateLive(this);
}
}

View file

@ -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;
};
}

View file

@ -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);

View file

@ -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();

View file

@ -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;
};

View file

@ -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);

View file

@ -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;

View file

@ -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);

View file

@ -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;
}

View file

@ -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;}

View file

@ -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;
}

View file

@ -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;
};
}

View file

@ -3,13 +3,15 @@
#include <cassert>
#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());
}
}

View file

@ -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;
};
}

View file

@ -1,22 +1,137 @@
#include "CanvasPreview.h"
#include "Color.h"
#include "CanvasLiveClient.h"
#include "Mutex.h"
#include <cassert>
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();
}

View file

@ -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;
};
}

View file

@ -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, &current);
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);
}
}
}
}

View file

@ -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);
};
}

View file

@ -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);
}
}
}

View file

@ -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);
};
}

View file

@ -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<Rasterizer *> &SoftwareCanvasRenderer::getRasterizers() const
{
return rasterizers;
}
void SoftwareCanvasRenderer::rasterize(CanvasPortion *portion, bool threaded)
{
std::vector<Rasterizer> rasterizers;
renderer->getRasterizers(&rasterizers);
for (auto &rasterizer:rasterizers)
for (auto &rasterizer:getRasterizers())
{
rasterizer->rasterizeToCanvas(portion);
}
}

View file

@ -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<Rasterizer*> &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<Rasterizer*> rasterizers;
bool started;
};

View file

@ -108,22 +108,15 @@ void SoftwareRenderer::prepare()
//fluid_medium->registerMedium(water_renderer);
}
void SoftwareRenderer::getRasterizers(std::vector<Rasterizer> *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();
}

View file

@ -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<Rasterizer> *array);
/*!
* \brief Start the rasterization process.
*/

View file

@ -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();

View file

@ -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;
};

View file

@ -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;

View file

@ -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();
};
}