Merge branch 'canvas'
This commit is contained in:
commit
4087bde594
75 changed files with 2913 additions and 1916 deletions
8
Makefile
8
Makefile
|
@ -43,17 +43,17 @@ else
|
||||||
endif
|
endif
|
||||||
|
|
||||||
run_cli:build
|
run_cli:build
|
||||||
LD_LIBRARY_PATH=$(LIBRARY_PATH) ${RUNNER} ${BUILDPATH}/interface/commandline/paysages-cli
|
LD_LIBRARY_PATH=$(LIBRARY_PATH) ${RUNNER} ${BUILDPATH}/interface/commandline/paysages-cli $(ARGS)
|
||||||
|
|
||||||
run:build
|
run:build
|
||||||
LD_LIBRARY_PATH=$(LIBRARY_PATH) ${RUNNER} ${BUILDPATH}/interface/desktop/paysages-gui
|
LD_LIBRARY_PATH=$(LIBRARY_PATH) ${RUNNER} ${BUILDPATH}/interface/desktop/paysages-gui $(ARGS)
|
||||||
|
|
||||||
profile:build
|
profile:build
|
||||||
LD_LIBRARY_PATH=${LIBRARY_PATH} perf record -g fp ${BUILDPATH}/interface/desktop/paysages-gui $(ARGS)
|
LD_LIBRARY_PATH=${LIBRARY_PATH} perf record -g ${BUILDPATH}/interface/desktop/paysages-gui $(ARGS)
|
||||||
perf report -g
|
perf report -g
|
||||||
|
|
||||||
profile_cli:build
|
profile_cli:build
|
||||||
LD_LIBRARY_PATH=${LIBRARY_PATH} perf record -g fp ${BUILDPATH}/interface/commandline/paysages-cli $(ARGS)
|
LD_LIBRARY_PATH=${LIBRARY_PATH} perf record -g ${BUILDPATH}/interface/commandline/paysages-cli $(ARGS)
|
||||||
perf report -g
|
perf report -g
|
||||||
|
|
||||||
package:build
|
package:build
|
||||||
|
|
|
@ -45,7 +45,7 @@ void NoiseGenerator::save(PackStream* stream)
|
||||||
{
|
{
|
||||||
NoiseLevel* level = levels + x;
|
NoiseLevel* level = levels + x;
|
||||||
|
|
||||||
stream->write(&level->wavelength);
|
stream->write(&level->frequency);
|
||||||
stream->write(&level->amplitude);
|
stream->write(&level->amplitude);
|
||||||
stream->write(&level->minvalue);
|
stream->write(&level->minvalue);
|
||||||
}
|
}
|
||||||
|
@ -69,7 +69,7 @@ void NoiseGenerator::load(PackStream* stream)
|
||||||
{
|
{
|
||||||
NoiseLevel* level = levels + x;
|
NoiseLevel* level = levels + x;
|
||||||
|
|
||||||
stream->read(&level->wavelength);
|
stream->read(&level->frequency);
|
||||||
stream->read(&level->amplitude);
|
stream->read(&level->amplitude);
|
||||||
stream->read(&level->minvalue);
|
stream->read(&level->minvalue);
|
||||||
}
|
}
|
||||||
|
@ -215,7 +215,7 @@ void NoiseGenerator::addLevelSimple(double scaling, double minvalue, double maxv
|
||||||
{
|
{
|
||||||
NoiseLevel level;
|
NoiseLevel level;
|
||||||
|
|
||||||
level.wavelength = scaling;
|
level.frequency = 1.0 / scaling;
|
||||||
level.minvalue = minvalue;
|
level.minvalue = minvalue;
|
||||||
level.amplitude = maxvalue - minvalue;
|
level.amplitude = maxvalue - minvalue;
|
||||||
|
|
||||||
|
@ -230,7 +230,7 @@ void NoiseGenerator::addLevels(int level_count, NoiseLevel start_level, double s
|
||||||
{
|
{
|
||||||
addLevel(start_level);
|
addLevel(start_level);
|
||||||
start_level.minvalue += start_level.amplitude * (1.0 - amplitude_factor) * center_factor;
|
start_level.minvalue += start_level.amplitude * (1.0 - amplitude_factor) * center_factor;
|
||||||
start_level.wavelength *= scaling_factor;
|
start_level.frequency /= scaling_factor;
|
||||||
start_level.amplitude *= amplitude_factor;
|
start_level.amplitude *= amplitude_factor;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -239,7 +239,7 @@ void NoiseGenerator::addLevelsSimple(int level_count, double scaling, double min
|
||||||
{
|
{
|
||||||
NoiseLevel level;
|
NoiseLevel level;
|
||||||
|
|
||||||
level.wavelength = scaling;
|
level.frequency = 1.0 / scaling;
|
||||||
level.minvalue = minvalue;
|
level.minvalue = minvalue;
|
||||||
level.amplitude = maxvalue - minvalue;
|
level.amplitude = maxvalue - minvalue;
|
||||||
addLevels(level_count, level, 0.5, 0.5, center_factor);
|
addLevels(level_count, level, 0.5, 0.5, center_factor);
|
||||||
|
@ -284,7 +284,7 @@ void NoiseGenerator::setLevelSimple(int index, double scaling, double minvalue,
|
||||||
{
|
{
|
||||||
NoiseLevel level;
|
NoiseLevel level;
|
||||||
|
|
||||||
level.wavelength = scaling;
|
level.frequency = 1.0 / scaling;
|
||||||
level.minvalue = minvalue;
|
level.minvalue = minvalue;
|
||||||
level.amplitude = maxvalue - minvalue;
|
level.amplitude = maxvalue - minvalue;
|
||||||
|
|
||||||
|
@ -313,7 +313,7 @@ void NoiseGenerator::normalizeAmplitude(double minvalue, double maxvalue, int ad
|
||||||
levels[level].amplitude *= factor;
|
levels[level].amplitude *= factor;
|
||||||
if (adjust_scaling)
|
if (adjust_scaling)
|
||||||
{
|
{
|
||||||
levels[level].wavelength *= factor;
|
levels[level].frequency /= factor;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
height_offset = minvalue + (height_offset - current_minvalue) * factor;
|
height_offset = minvalue + (height_offset - current_minvalue) * factor;
|
||||||
|
@ -371,7 +371,7 @@ static inline double _fixValue(double value, double ridge, double curve)
|
||||||
|
|
||||||
inline double NoiseGenerator::_get1DLevelValue(NoiseLevel* level, const NoiseState::NoiseOffset &offset, double x)
|
inline double NoiseGenerator::_get1DLevelValue(NoiseLevel* level, const NoiseState::NoiseOffset &offset, double x)
|
||||||
{
|
{
|
||||||
return level->minvalue + _fixValue(_func_noise_1d(x / level->wavelength + offset.x), function.ridge_factor, function.curve_factor) * level->amplitude;
|
return level->minvalue + _fixValue(_func_noise_1d(x * level->frequency + offset.x), function.ridge_factor, function.curve_factor) * level->amplitude;
|
||||||
}
|
}
|
||||||
|
|
||||||
double NoiseGenerator::get1DLevel(int level, double x)
|
double NoiseGenerator::get1DLevel(int level, double x)
|
||||||
|
@ -428,7 +428,7 @@ double NoiseGenerator::get1DDetail(double x, double detail)
|
||||||
|
|
||||||
inline double NoiseGenerator::_get2DLevelValue(NoiseLevel* level, const NoiseState::NoiseOffset &offset, double x, double y)
|
inline double NoiseGenerator::_get2DLevelValue(NoiseLevel* level, const NoiseState::NoiseOffset &offset, double x, double y)
|
||||||
{
|
{
|
||||||
return level->minvalue + _fixValue(_func_noise_2d(x / level->wavelength + offset.x, y / level->wavelength + offset.y), function.ridge_factor, function.curve_factor) * level->amplitude;
|
return level->minvalue + _fixValue(_func_noise_2d(x * level->frequency + offset.x, y * level->frequency + offset.y), function.ridge_factor, function.curve_factor) * level->amplitude;
|
||||||
}
|
}
|
||||||
|
|
||||||
double NoiseGenerator::get2DLevel(int level, double x, double y)
|
double NoiseGenerator::get2DLevel(int level, double x, double y)
|
||||||
|
@ -485,7 +485,7 @@ double NoiseGenerator::get2DDetail(double x, double y, double detail)
|
||||||
|
|
||||||
inline double NoiseGenerator::_get3DLevelValue(NoiseLevel* level, const NoiseState::NoiseOffset &offset, double x, double y, double z)
|
inline double NoiseGenerator::_get3DLevelValue(NoiseLevel* level, const NoiseState::NoiseOffset &offset, double x, double y, double z)
|
||||||
{
|
{
|
||||||
return level->minvalue + _fixValue(_func_noise_3d(x / level->wavelength + offset.x, y / level->wavelength + offset.y, z / level->wavelength + offset.z), function.ridge_factor, function.curve_factor) * level->amplitude;
|
return level->minvalue + _fixValue(_func_noise_3d(x * level->frequency + offset.x, y * level->frequency + offset.y, z * level->frequency + offset.z), function.ridge_factor, function.curve_factor) * level->amplitude;
|
||||||
}
|
}
|
||||||
|
|
||||||
double NoiseGenerator::get3DLevel(int level, double x, double y, double z)
|
double NoiseGenerator::get3DLevel(int level, double x, double y, double z)
|
||||||
|
|
|
@ -28,7 +28,7 @@ public:
|
||||||
|
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
double wavelength;
|
double frequency;
|
||||||
double amplitude;
|
double amplitude;
|
||||||
double minvalue;
|
double minvalue;
|
||||||
} NoiseLevel;
|
} NoiseLevel;
|
||||||
|
|
|
@ -90,11 +90,14 @@ void CameraDefinition::validate()
|
||||||
|
|
||||||
projector = mperspective.mult(Matrix4::newLookAt(location, target, up));
|
projector = mperspective.mult(Matrix4::newLookAt(location, target, up));
|
||||||
unprojector = projector.inversed();
|
unprojector = projector.inversed();
|
||||||
|
|
||||||
|
inv_x_factor = 1.0 / (0.5 * width);
|
||||||
|
inv_y_factor = 1.0 / (0.5 * height);
|
||||||
}
|
}
|
||||||
|
|
||||||
double CameraDefinition::getRealDepth(const Vector3 &projected) const
|
double CameraDefinition::getRealDepth(const Vector3 &projected) const
|
||||||
{
|
{
|
||||||
Vector3 v(projected.x / (0.5 * width) - 1.0, -(projected.y / (0.5 * height) - 1.0), projected.z);
|
Vector3 v(projected.x * inv_x_factor - 1.0, -(projected.y * inv_x_factor - 1.0), projected.z);
|
||||||
return unperspective.transform(v).z;
|
return unperspective.transform(v).z;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -85,6 +85,8 @@ private:
|
||||||
Matrix4 projector;
|
Matrix4 projector;
|
||||||
Matrix4 unprojector;
|
Matrix4 unprojector;
|
||||||
Matrix4 unperspective;
|
Matrix4 unperspective;
|
||||||
|
double inv_x_factor;
|
||||||
|
double inv_y_factor;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,16 +5,18 @@
|
||||||
|
|
||||||
#include "CameraDefinition.h"
|
#include "CameraDefinition.h"
|
||||||
#include "AtmosphereDefinition.h"
|
#include "AtmosphereDefinition.h"
|
||||||
#include "SoftwareRenderer.h"
|
#include "SoftwareCanvasRenderer.h"
|
||||||
#include "Scenery.h"
|
#include "Scenery.h"
|
||||||
|
#include "RenderConfig.h"
|
||||||
|
#include "ColorProfile.h"
|
||||||
|
|
||||||
void startRender(SoftwareRenderer* renderer, char* outputpath, RenderArea::RenderParams params)
|
void startRender(SoftwareCanvasRenderer *renderer, char *outputpath)
|
||||||
{
|
{
|
||||||
printf("\rRendering %s ... \n", outputpath);
|
printf("\rRendering %s ... \n", outputpath);
|
||||||
renderer->start(params);
|
renderer->render();
|
||||||
printf("\rSaving %s ... \n", outputpath);
|
printf("\rSaving %s ... \n", outputpath);
|
||||||
remove(outputpath);
|
remove(outputpath);
|
||||||
renderer->render_area->saveToFile(outputpath);
|
renderer->saveToDisk(outputpath);
|
||||||
}
|
}
|
||||||
|
|
||||||
void displayHelp()
|
void displayHelp()
|
||||||
|
@ -43,9 +45,9 @@ void _previewUpdate(double progress)
|
||||||
|
|
||||||
int main(int argc, char** argv)
|
int main(int argc, char** argv)
|
||||||
{
|
{
|
||||||
SoftwareRenderer* renderer;
|
SoftwareCanvasRenderer* renderer;
|
||||||
char* conf_file_path = NULL;
|
char* conf_file_path = NULL;
|
||||||
RenderArea::RenderParams conf_render_params = {800, 600, 1, 5};
|
RenderConfig conf_render_params(800, 600, 1, 5);
|
||||||
int conf_first_picture = 0;
|
int conf_first_picture = 0;
|
||||||
int conf_nb_pictures = 1;
|
int conf_nb_pictures = 1;
|
||||||
double conf_daytime_start = 0.4;
|
double conf_daytime_start = 0.4;
|
||||||
|
@ -177,13 +179,14 @@ int main(int argc, char** argv)
|
||||||
Vector3 step = {conf_camera_step_x, conf_camera_step_y, conf_camera_step_z};
|
Vector3 step = {conf_camera_step_x, conf_camera_step_y, conf_camera_step_z};
|
||||||
camera->setLocation(camera->getLocation().add(step));
|
camera->setLocation(camera->getLocation().add(step));
|
||||||
|
|
||||||
renderer = new SoftwareRenderer(scenery);
|
renderer = new SoftwareCanvasRenderer();
|
||||||
renderer->setPreviewCallbacks(NULL, NULL, _previewUpdate);
|
renderer->setConfig(conf_render_params);
|
||||||
|
renderer->setScenery(scenery);
|
||||||
|
|
||||||
if (outputcount >= conf_first_picture)
|
if (outputcount >= conf_first_picture)
|
||||||
{
|
{
|
||||||
sprintf(outputpath, "output/pic%05d.png", outputcount);
|
sprintf(outputpath, "output/pic%05d.png", outputcount);
|
||||||
startRender(renderer, outputpath, conf_render_params);
|
startRender(renderer, outputpath);
|
||||||
}
|
}
|
||||||
|
|
||||||
delete renderer;
|
delete renderer;
|
||||||
|
|
6
src/interface/desktop/WidgetCanvas.cpp
Normal file
6
src/interface/desktop/WidgetCanvas.cpp
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
#include "WidgetCanvas.h"
|
||||||
|
|
||||||
|
WidgetCanvas::WidgetCanvas(QWidget *parent) :
|
||||||
|
QWidget(parent)
|
||||||
|
{
|
||||||
|
}
|
29
src/interface/desktop/WidgetCanvas.h
Normal file
29
src/interface/desktop/WidgetCanvas.h
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
#ifndef WIDGETCANVAS_H
|
||||||
|
#define WIDGETCANVAS_H
|
||||||
|
|
||||||
|
#include "desktop_global.h"
|
||||||
|
|
||||||
|
#include <QWidget>
|
||||||
|
|
||||||
|
namespace paysages {
|
||||||
|
namespace desktop {
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Widget to display the full content of a Canvas.
|
||||||
|
*/
|
||||||
|
class WidgetCanvas : public QWidget
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
explicit WidgetCanvas(QWidget *parent = 0);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // WIDGETCANVAS_H
|
77
src/interface/desktop/WidgetPreviewCanvas.cpp
Normal file
77
src/interface/desktop/WidgetPreviewCanvas.cpp
Normal file
|
@ -0,0 +1,77 @@
|
||||||
|
#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();
|
||||||
|
|
||||||
|
startTimer(500);
|
||||||
|
}
|
||||||
|
|
||||||
|
WidgetPreviewCanvas::~WidgetPreviewCanvas()
|
||||||
|
{
|
||||||
|
delete pixbuf;
|
||||||
|
}
|
||||||
|
|
||||||
|
void WidgetPreviewCanvas::setCanvas(const Canvas *canvas)
|
||||||
|
{
|
||||||
|
if (not this->canvas)
|
||||||
|
{
|
||||||
|
this->canvas = canvas;
|
||||||
|
canvas->getPreview()->initLive(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void WidgetPreviewCanvas::setToneMapping(const ColorProfile &profile)
|
||||||
|
{
|
||||||
|
if (canvas)
|
||||||
|
{
|
||||||
|
canvas->getPreview()->setToneMapping(profile);
|
||||||
|
canvas->getPreview()->updateLive(this);
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void WidgetPreviewCanvas::paintEvent(QPaintEvent *)
|
||||||
|
{
|
||||||
|
QPainter painter(this);
|
||||||
|
painter.drawImage(0, 0, *pixbuf);
|
||||||
|
}
|
||||||
|
|
||||||
|
void WidgetPreviewCanvas::canvasResized(int width, int height)
|
||||||
|
{
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
canvas->getPreview()->updateLive(this);
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
}
|
50
src/interface/desktop/WidgetPreviewCanvas.h
Normal file
50
src/interface/desktop/WidgetPreviewCanvas.h
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
#ifndef WIDGETPREVIEWCANVAS_H
|
||||||
|
#define WIDGETPREVIEWCANVAS_H
|
||||||
|
|
||||||
|
#include "desktop_global.h"
|
||||||
|
|
||||||
|
#include <QWidget>
|
||||||
|
#include "CanvasLiveClient.h"
|
||||||
|
|
||||||
|
namespace paysages {
|
||||||
|
namespace desktop {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Widget to display a live-updated preview of a Canvas software rendering.
|
||||||
|
*/
|
||||||
|
class WidgetPreviewCanvas : public QWidget, public CanvasLiveClient
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
explicit WidgetPreviewCanvas(QWidget *parent = 0);
|
||||||
|
virtual ~WidgetPreviewCanvas();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the canvas to watch and display, null to stop watching.
|
||||||
|
*
|
||||||
|
* This function must be called from the graphics thread.
|
||||||
|
*/
|
||||||
|
void setCanvas(const Canvas *canvas);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the tone mapping to apply to pixel colors.
|
||||||
|
*/
|
||||||
|
void setToneMapping(const ColorProfile &profile);
|
||||||
|
|
||||||
|
virtual void paintEvent(QPaintEvent* event);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual void canvasResized(int width, int height);
|
||||||
|
virtual void canvasCleared(const Color &col);
|
||||||
|
virtual void canvasPainted(int x, int y, const Color &col);
|
||||||
|
virtual void timerEvent(QTimerEvent *event);
|
||||||
|
|
||||||
|
private:
|
||||||
|
QImage* pixbuf;
|
||||||
|
const Canvas *canvas;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // WIDGETPREVIEWCANVAS_H
|
|
@ -12,9 +12,10 @@
|
||||||
#include "mainwindow.h"
|
#include "mainwindow.h"
|
||||||
#include "dialogrender.h"
|
#include "dialogrender.h"
|
||||||
#include "dialogexplorer.h"
|
#include "dialogexplorer.h"
|
||||||
|
#include "RenderConfig.h"
|
||||||
#include "DesktopScenery.h"
|
#include "DesktopScenery.h"
|
||||||
#include "BasePreview.h"
|
#include "BasePreview.h"
|
||||||
#include "SoftwareRenderer.h"
|
#include "SoftwareCanvasRenderer.h"
|
||||||
#include "CameraDefinition.h"
|
#include "CameraDefinition.h"
|
||||||
#include "tools.h"
|
#include "tools.h"
|
||||||
|
|
||||||
|
@ -246,15 +247,16 @@ void FreeFormHelper::processExploreClicked()
|
||||||
|
|
||||||
void FreeFormHelper::processRenderClicked()
|
void FreeFormHelper::processRenderClicked()
|
||||||
{
|
{
|
||||||
SoftwareRenderer renderer(DesktopScenery::getCurrent());
|
RenderConfig params(400, 300, 1, 3);
|
||||||
|
|
||||||
|
SoftwareCanvasRenderer renderer;
|
||||||
|
renderer.setConfig(params);
|
||||||
|
renderer.setScenery(DesktopScenery::getCurrent());
|
||||||
|
|
||||||
emit needAlterRenderer(&renderer);
|
emit needAlterRenderer(&renderer);
|
||||||
|
|
||||||
DialogRender* dialog = new DialogRender(_form_widget, &renderer);
|
DialogRender dialog(_form_widget, &renderer);
|
||||||
RenderArea::RenderParams params = {400, 300, 1, 3};
|
dialog.startRender();
|
||||||
dialog->startRender(params);
|
|
||||||
|
|
||||||
delete dialog;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void FreeFormHelper::processDecimalChange(double value)
|
void FreeFormHelper::processDecimalChange(double value)
|
||||||
|
|
|
@ -52,7 +52,9 @@ HEADERS += \
|
||||||
lighting/SmallPreviewHues.h \
|
lighting/SmallPreviewHues.h \
|
||||||
textures/DialogTexturesLayer.h \
|
textures/DialogTexturesLayer.h \
|
||||||
desktop_global.h \
|
desktop_global.h \
|
||||||
DesktopScenery.h
|
DesktopScenery.h \
|
||||||
|
WidgetCanvas.h \
|
||||||
|
WidgetPreviewCanvas.h
|
||||||
|
|
||||||
SOURCES += \
|
SOURCES += \
|
||||||
terrain/widgetheightmap.cpp \
|
terrain/widgetheightmap.cpp \
|
||||||
|
@ -96,7 +98,9 @@ SOURCES += \
|
||||||
lighting/SmallPreviewColor.cpp \
|
lighting/SmallPreviewColor.cpp \
|
||||||
lighting/SmallPreviewHues.cpp \
|
lighting/SmallPreviewHues.cpp \
|
||||||
textures/DialogTexturesLayer.cpp \
|
textures/DialogTexturesLayer.cpp \
|
||||||
DesktopScenery.cpp
|
DesktopScenery.cpp \
|
||||||
|
WidgetCanvas.cpp \
|
||||||
|
WidgetPreviewCanvas.cpp
|
||||||
|
|
||||||
FORMS += \
|
FORMS += \
|
||||||
terrain/dialogterrainpainting.ui \
|
terrain/dialogterrainpainting.ui \
|
||||||
|
|
|
@ -10,6 +10,9 @@ namespace paysages {
|
||||||
namespace desktop {
|
namespace desktop {
|
||||||
class BaseInput;
|
class BaseInput;
|
||||||
class BaseForm;
|
class BaseForm;
|
||||||
|
|
||||||
|
class WidgetCanvas;
|
||||||
|
class WidgetPreviewCanvas;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -282,7 +282,7 @@ void DialogNoise::addLevel()
|
||||||
NoiseGenerator::NoiseLevel level;
|
NoiseGenerator::NoiseLevel level;
|
||||||
|
|
||||||
level.amplitude = 0.1;
|
level.amplitude = 0.1;
|
||||||
level.wavelength = 0.1;
|
level.frequency = 0.1;
|
||||||
|
|
||||||
_current->addLevel(level);
|
_current->addLevel(level);
|
||||||
|
|
||||||
|
@ -330,7 +330,7 @@ void DialogNoise::levelChanged(int row)
|
||||||
((PreviewLevel*)previewLevel)->setLevel(row);
|
((PreviewLevel*)previewLevel)->setLevel(row);
|
||||||
|
|
||||||
slider_height->setValue(_current_level_params.amplitude * 1000.0);
|
slider_height->setValue(_current_level_params.amplitude * 1000.0);
|
||||||
slider_scaling->setValue(_current_level_params.wavelength * 1000.0);
|
slider_scaling->setValue(_current_level_params.frequency * 1000.0);
|
||||||
}
|
}
|
||||||
// TODO else ...
|
// TODO else ...
|
||||||
}
|
}
|
||||||
|
@ -345,7 +345,7 @@ void DialogNoise::heightChanged(int value)
|
||||||
|
|
||||||
void DialogNoise::scalingChanged(int value)
|
void DialogNoise::scalingChanged(int value)
|
||||||
{
|
{
|
||||||
_current_level_params.wavelength = ((double)value) / 1000.0;
|
_current_level_params.frequency = ((double)value) / 1000.0;
|
||||||
_current->setLevel(_current_level, _current_level_params);
|
_current->setLevel(_current_level, _current_level_params);
|
||||||
previewLevel->redraw();
|
previewLevel->redraw();
|
||||||
previewTotal->redraw();
|
previewTotal->redraw();
|
||||||
|
|
|
@ -18,90 +18,45 @@
|
||||||
#include <QComboBox>
|
#include <QComboBox>
|
||||||
#include "tools.h"
|
#include "tools.h"
|
||||||
|
|
||||||
#include "SoftwareRenderer.h"
|
|
||||||
#include "Scenery.h"
|
#include "Scenery.h"
|
||||||
#include "ColorProfile.h"
|
#include "ColorProfile.h"
|
||||||
|
#include "SoftwareCanvasRenderer.h"
|
||||||
static DialogRender* _current_dialog;
|
#include "WidgetPreviewCanvas.h"
|
||||||
|
#include "Canvas.h"
|
||||||
static void _renderStart(int width, int height, const Color &background)
|
|
||||||
{
|
|
||||||
_current_dialog->pixbuf_lock->lock();
|
|
||||||
delete _current_dialog->pixbuf;
|
|
||||||
_current_dialog->pixbuf = new QImage(width, height, QImage::Format_ARGB32);
|
|
||||||
_current_dialog->pixbuf->fill(colorToQColor(background).rgb());
|
|
||||||
_current_dialog->pixbuf_lock->unlock();
|
|
||||||
|
|
||||||
_current_dialog->tellRenderSize(width, height);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void _renderDraw(int x, int y, const Color &col)
|
|
||||||
{
|
|
||||||
_current_dialog->pixbuf->setPixel(x, _current_dialog->pixbuf->height() - 1 - y, colorToQColor(col).rgb());
|
|
||||||
}
|
|
||||||
|
|
||||||
static void _renderUpdate(double progress)
|
|
||||||
{
|
|
||||||
_current_dialog->area->update();
|
|
||||||
_current_dialog->tellProgressChange(progress);
|
|
||||||
}
|
|
||||||
|
|
||||||
class RenderThread:public QThread
|
class RenderThread:public QThread
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
RenderThread(DialogRender* dialog, SoftwareRenderer* renderer, RenderArea::RenderParams params):QThread()
|
RenderThread(DialogRender* dialog, SoftwareCanvasRenderer* renderer):QThread()
|
||||||
{
|
{
|
||||||
_dialog = dialog;
|
_dialog = dialog;
|
||||||
_renderer = renderer;
|
_renderer = renderer;
|
||||||
_params = params;
|
|
||||||
}
|
}
|
||||||
void run()
|
void run()
|
||||||
{
|
{
|
||||||
_renderer->start(_params);
|
_renderer->render();
|
||||||
_dialog->tellRenderEnded();
|
_dialog->tellRenderEnded();
|
||||||
}
|
}
|
||||||
private:
|
private:
|
||||||
DialogRender* _dialog;
|
DialogRender* _dialog;
|
||||||
SoftwareRenderer* _renderer;
|
SoftwareCanvasRenderer* _renderer;
|
||||||
RenderArea::RenderParams _params;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class _RenderArea:public QWidget
|
DialogRender::DialogRender(QWidget *parent, SoftwareCanvasRenderer* renderer):
|
||||||
{
|
|
||||||
public:
|
|
||||||
_RenderArea(QWidget* parent):
|
|
||||||
QWidget(parent)
|
|
||||||
{
|
|
||||||
setMinimumSize(800, 600);
|
|
||||||
}
|
|
||||||
|
|
||||||
void paintEvent(QPaintEvent*)
|
|
||||||
{
|
|
||||||
QPainter painter(this);
|
|
||||||
_current_dialog->pixbuf_lock->lock();
|
|
||||||
painter.drawImage(0, 0, *_current_dialog->pixbuf);
|
|
||||||
_current_dialog->pixbuf_lock->unlock();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
DialogRender::DialogRender(QWidget *parent, SoftwareRenderer* renderer):
|
|
||||||
QDialog(parent, Qt::WindowTitleHint | Qt::WindowMaximizeButtonHint | Qt::WindowMinimizeButtonHint | Qt::WindowCloseButtonHint)
|
QDialog(parent, Qt::WindowTitleHint | Qt::WindowMaximizeButtonHint | Qt::WindowMinimizeButtonHint | Qt::WindowCloseButtonHint)
|
||||||
{
|
{
|
||||||
pixbuf_lock = new QMutex();
|
pixbuf_lock = new QMutex();
|
||||||
pixbuf = new QImage(1, 1, QImage::Format_ARGB32);
|
pixbuf = new QImage(1, 1, QImage::Format_ARGB32);
|
||||||
_current_dialog = this;
|
|
||||||
_render_thread = NULL;
|
_render_thread = NULL;
|
||||||
_renderer = renderer;
|
canvas_renderer = renderer;
|
||||||
|
|
||||||
setModal(true);
|
setModal(true);
|
||||||
setWindowTitle(tr("Paysages 3D - Render"));
|
setWindowTitle(tr("Paysages 3D - Render"));
|
||||||
setLayout(new QVBoxLayout());
|
setLayout(new QVBoxLayout());
|
||||||
|
|
||||||
_scroll = new QScrollArea(this);
|
canvas_preview = new WidgetPreviewCanvas(this);
|
||||||
_scroll->setAlignment(Qt::AlignCenter);
|
canvas_preview->setCanvas(canvas_renderer->getCanvas());
|
||||||
area = new _RenderArea(_scroll);
|
layout()->addWidget(canvas_preview);
|
||||||
_scroll->setWidget(area);
|
|
||||||
layout()->addWidget(_scroll);
|
|
||||||
|
|
||||||
// Status bar
|
// Status bar
|
||||||
_info = new QWidget(this);
|
_info = new QWidget(this);
|
||||||
|
@ -140,19 +95,19 @@ DialogRender::DialogRender(QWidget *parent, SoftwareRenderer* renderer):
|
||||||
_actions->layout()->addWidget(_save_button);
|
_actions->layout()->addWidget(_save_button);
|
||||||
|
|
||||||
// Connections
|
// Connections
|
||||||
//connect(this, SIGNAL(renderSizeChanged(int, int)), this, SLOT(applyRenderSize(int, int)));
|
|
||||||
connect(this, SIGNAL(progressChanged(double)), this, SLOT(applyProgress(double)));
|
|
||||||
connect(this, SIGNAL(renderEnded()), this, SLOT(applyRenderEnded()));
|
connect(this, SIGNAL(renderEnded()), this, SLOT(applyRenderEnded()));
|
||||||
connect(_save_button, SIGNAL(clicked()), this, SLOT(saveRender()));
|
connect(_save_button, SIGNAL(clicked()), this, SLOT(saveRender()));
|
||||||
connect(_tonemapping_control, SIGNAL(currentIndexChanged(int)), this, SLOT(toneMappingChanged()));
|
connect(_tonemapping_control, SIGNAL(currentIndexChanged(int)), this, SLOT(toneMappingChanged()));
|
||||||
connect(_exposure_control, SIGNAL(valueChanged(int)), this, SLOT(toneMappingChanged()));
|
connect(_exposure_control, SIGNAL(valueChanged(int)), this, SLOT(toneMappingChanged()));
|
||||||
|
|
||||||
|
toneMappingChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
DialogRender::~DialogRender()
|
DialogRender::~DialogRender()
|
||||||
{
|
{
|
||||||
if (_render_thread)
|
if (_render_thread)
|
||||||
{
|
{
|
||||||
_renderer->interrupt();
|
canvas_renderer->interrupt();
|
||||||
_render_thread->wait();
|
_render_thread->wait();
|
||||||
|
|
||||||
delete _render_thread;
|
delete _render_thread;
|
||||||
|
@ -161,31 +116,20 @@ DialogRender::~DialogRender()
|
||||||
delete pixbuf_lock;
|
delete pixbuf_lock;
|
||||||
}
|
}
|
||||||
|
|
||||||
void DialogRender::tellRenderSize(int width, int height)
|
|
||||||
{
|
|
||||||
emit renderSizeChanged(width, height);
|
|
||||||
}
|
|
||||||
|
|
||||||
void DialogRender::tellProgressChange(double value)
|
|
||||||
{
|
|
||||||
emit progressChanged(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
void DialogRender::tellRenderEnded()
|
void DialogRender::tellRenderEnded()
|
||||||
{
|
{
|
||||||
emit renderEnded();
|
emit renderEnded();
|
||||||
}
|
}
|
||||||
|
|
||||||
void DialogRender::startRender(RenderArea::RenderParams params)
|
void DialogRender::startRender()
|
||||||
{
|
{
|
||||||
_started = time(NULL);
|
_started = time(NULL);
|
||||||
|
|
||||||
applyRenderSize(params.width, params.height);
|
_render_thread = new RenderThread(this, canvas_renderer);
|
||||||
_renderer->setPreviewCallbacks(_renderStart, _renderDraw, _renderUpdate);
|
|
||||||
|
|
||||||
_render_thread = new RenderThread(this, _renderer, params);
|
|
||||||
_render_thread->start();
|
_render_thread->start();
|
||||||
|
|
||||||
|
startTimer(100);
|
||||||
|
|
||||||
exec();
|
exec();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -193,8 +137,6 @@ void DialogRender::applyRenderEnded()
|
||||||
{
|
{
|
||||||
_info->hide();
|
_info->hide();
|
||||||
_actions->show();
|
_actions->show();
|
||||||
|
|
||||||
area->update();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void DialogRender::saveRender()
|
void DialogRender::saveRender()
|
||||||
|
@ -208,8 +150,7 @@ void DialogRender::saveRender()
|
||||||
{
|
{
|
||||||
filepath = filepath.append(".png");
|
filepath = filepath.append(".png");
|
||||||
}
|
}
|
||||||
std::string filepathstr = filepath.toStdString();
|
if (canvas_renderer->saveToDisk(filepath.toStdString()))
|
||||||
if (_renderer->render_area->saveToFile((char*)filepathstr.c_str()))
|
|
||||||
{
|
{
|
||||||
QMessageBox::information(this, "Message", QString(tr("The picture %1 has been saved.")).arg(filepath));
|
QMessageBox::information(this, "Message", QString(tr("The picture %1 has been saved.")).arg(filepath));
|
||||||
}
|
}
|
||||||
|
@ -223,35 +164,26 @@ void DialogRender::saveRender()
|
||||||
void DialogRender::toneMappingChanged()
|
void DialogRender::toneMappingChanged()
|
||||||
{
|
{
|
||||||
ColorProfile profile((ColorProfile::ToneMappingOperator)_tonemapping_control->currentIndex(), ((double)_exposure_control->value()) * 0.01);
|
ColorProfile profile((ColorProfile::ToneMappingOperator)_tonemapping_control->currentIndex(), ((double)_exposure_control->value()) * 0.01);
|
||||||
_renderer->render_area->setToneMapping(profile);
|
canvas_preview->setToneMapping(profile);
|
||||||
}
|
}
|
||||||
|
|
||||||
void DialogRender::loadLastRender()
|
void DialogRender::loadLastRender()
|
||||||
{
|
{
|
||||||
applyRenderSize(_renderer->render_width, _renderer->render_height);
|
|
||||||
_renderer->setPreviewCallbacks(_renderStart, _renderDraw, _renderUpdate);
|
|
||||||
renderEnded();
|
renderEnded();
|
||||||
toneMappingChanged();
|
toneMappingChanged();
|
||||||
|
|
||||||
exec();
|
exec();
|
||||||
}
|
}
|
||||||
|
|
||||||
void DialogRender::applyRenderSize(int width, int height)
|
void DialogRender::timerEvent(QTimerEvent *)
|
||||||
{
|
|
||||||
area->setMinimumSize(width, height);
|
|
||||||
area->setMaximumSize(width, height);
|
|
||||||
area->resize(width, height);
|
|
||||||
_scroll->setMinimumSize(width > 800 ? 820 : width + 20, height > 600 ? 620 : height + 20);
|
|
||||||
}
|
|
||||||
|
|
||||||
void DialogRender::applyProgress(double value)
|
|
||||||
{
|
{
|
||||||
double diff = difftime(time(NULL), _started);
|
double diff = difftime(time(NULL), _started);
|
||||||
int hours = (int)floor(diff / 3600.0);
|
int hours = (int)floor(diff / 3600.0);
|
||||||
int minutes = (int)floor((diff - 3600.0 * hours) / 60.0);
|
int minutes = (int)floor((diff - 3600.0 * hours) / 60.0);
|
||||||
int seconds = (int)floor(diff - 3600.0 * hours - 60.0 * minutes);
|
int seconds = (int)floor(diff - 3600.0 * hours - 60.0 * minutes);
|
||||||
_timer->setText(tr("%1:%2.%3").arg(hours).arg(minutes, 2, 10, QLatin1Char('0')).arg(seconds, 2, 10, QLatin1Char('0')));
|
_timer->setText(tr("%1:%2.%3").arg(hours).arg(minutes, 2, 10, QLatin1Char('0')).arg(seconds, 2, 10, QLatin1Char('0')));
|
||||||
_progress->setValue((int)(value * 1000.0));
|
|
||||||
|
_progress->setValue((int)(canvas_renderer->getProgress() * 1000.0));
|
||||||
_progress->update();
|
_progress->update();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,6 @@
|
||||||
|
|
||||||
#include <ctime>
|
#include <ctime>
|
||||||
#include <QDialog>
|
#include <QDialog>
|
||||||
#include "RenderArea.h"
|
|
||||||
|
|
||||||
class QThread;
|
class QThread;
|
||||||
class QProgressBar;
|
class QProgressBar;
|
||||||
|
@ -19,33 +18,30 @@ class DialogRender : public QDialog
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
explicit DialogRender(QWidget *parent, SoftwareRenderer* renderer);
|
explicit DialogRender(QWidget *parent, SoftwareCanvasRenderer *renderer);
|
||||||
~DialogRender();
|
~DialogRender();
|
||||||
|
|
||||||
void tellRenderSize(int width, int height);
|
|
||||||
void tellProgressChange(double value);
|
|
||||||
void tellRenderEnded();
|
void tellRenderEnded();
|
||||||
void startRender(RenderArea::RenderParams params);
|
void startRender();
|
||||||
void loadLastRender();
|
void loadLastRender();
|
||||||
|
|
||||||
|
virtual void timerEvent(QTimerEvent *event) override;
|
||||||
|
|
||||||
QImage* pixbuf;
|
QImage* pixbuf;
|
||||||
QMutex* pixbuf_lock;
|
QMutex* pixbuf_lock;
|
||||||
QWidget* area;
|
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void applyRenderSize(int width, int height);
|
|
||||||
void applyProgress(double value);
|
|
||||||
void saveRender();
|
void saveRender();
|
||||||
void applyRenderEnded();
|
void applyRenderEnded();
|
||||||
void toneMappingChanged();
|
void toneMappingChanged();
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void renderSizeChanged(int width, int height);
|
|
||||||
void progressChanged(double value);
|
|
||||||
void renderEnded();
|
void renderEnded();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QScrollArea* _scroll;
|
SoftwareCanvasRenderer* canvas_renderer;
|
||||||
|
WidgetPreviewCanvas* canvas_preview;
|
||||||
|
|
||||||
QWidget* _info;
|
QWidget* _info;
|
||||||
QWidget* _actions;
|
QWidget* _actions;
|
||||||
QComboBox* _tonemapping_control;
|
QComboBox* _tonemapping_control;
|
||||||
|
@ -53,7 +49,6 @@ private:
|
||||||
QPushButton* _save_button;
|
QPushButton* _save_button;
|
||||||
QThread* _render_thread;
|
QThread* _render_thread;
|
||||||
QLabel* _timer;
|
QLabel* _timer;
|
||||||
SoftwareRenderer* _renderer;
|
|
||||||
QProgressBar* _progress;
|
QProgressBar* _progress;
|
||||||
time_t _started;
|
time_t _started;
|
||||||
};
|
};
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
#include "tools.h"
|
#include "tools.h"
|
||||||
#include "DesktopScenery.h"
|
#include "DesktopScenery.h"
|
||||||
#include "PackStream.h"
|
#include "PackStream.h"
|
||||||
#include "SoftwareRenderer.h"
|
#include "SoftwareCanvasRenderer.h"
|
||||||
#include "BasePreview.h"
|
#include "BasePreview.h"
|
||||||
#include "CloudsDefinition.h"
|
#include "CloudsDefinition.h"
|
||||||
#include "CameraDefinition.h"
|
#include "CameraDefinition.h"
|
||||||
|
@ -36,8 +36,8 @@ BaseForm(parent, true)
|
||||||
|
|
||||||
addInput(new InputCamera(this, tr("Camera"), _camera));
|
addInput(new InputCamera(this, tr("Camera"), _camera));
|
||||||
addInputInt(tr("Quality"), &_params.quality, 1, 10, 1, 1);
|
addInputInt(tr("Quality"), &_params.quality, 1, 10, 1, 1);
|
||||||
addInputInt(tr("Image width"), &_params.width, 100, 2000, 10, 100);
|
addInputInt(tr("Image width"), &_params.width, 100, 4000, 10, 100);
|
||||||
addInputInt(tr("Image height"), &_params.height, 100, 1200, 10, 100);
|
addInputInt(tr("Image height"), &_params.height, 100, 3000, 10, 100);
|
||||||
addInputInt(tr("Anti aliasing"), &_params.antialias, 1, 4, 1, 1);
|
addInputInt(tr("Anti aliasing"), &_params.antialias, 1, 4, 1, 1);
|
||||||
|
|
||||||
button = addButton(tr("Start new render"));
|
button = addButton(tr("Start new render"));
|
||||||
|
@ -103,14 +103,16 @@ void FormRender::startQuickRender()
|
||||||
{
|
{
|
||||||
delete _renderer;
|
delete _renderer;
|
||||||
}
|
}
|
||||||
_renderer = new SoftwareRenderer(DesktopScenery::getCurrent());
|
|
||||||
|
RenderConfig config(400, 300, 1, 3);
|
||||||
|
|
||||||
|
_renderer = new SoftwareCanvasRenderer();
|
||||||
|
_renderer->setScenery(DesktopScenery::getCurrent());
|
||||||
|
_renderer->setConfig(config);
|
||||||
_renderer_inited = true;
|
_renderer_inited = true;
|
||||||
|
|
||||||
DialogRender* dialog = new DialogRender(this, _renderer);
|
DialogRender dialog(this, _renderer);
|
||||||
RenderArea::RenderParams params = {400, 300, 1, 3};
|
dialog.startRender();
|
||||||
dialog->startRender(params);
|
|
||||||
|
|
||||||
delete dialog;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void FormRender::startRender()
|
void FormRender::startRender()
|
||||||
|
@ -119,22 +121,20 @@ void FormRender::startRender()
|
||||||
{
|
{
|
||||||
delete _renderer;
|
delete _renderer;
|
||||||
}
|
}
|
||||||
_renderer = new SoftwareRenderer(DesktopScenery::getCurrent());
|
_renderer = new SoftwareCanvasRenderer();
|
||||||
|
_renderer->setScenery(DesktopScenery::getCurrent());
|
||||||
|
_renderer->setConfig(_params);
|
||||||
_renderer_inited = true;
|
_renderer_inited = true;
|
||||||
|
|
||||||
DialogRender* dialog = new DialogRender(this, _renderer);
|
DialogRender dialog(this, _renderer);
|
||||||
dialog->startRender(_params);
|
dialog.startRender();
|
||||||
|
|
||||||
delete dialog;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void FormRender::showRender()
|
void FormRender::showRender()
|
||||||
{
|
{
|
||||||
if (_renderer_inited)
|
if (_renderer_inited)
|
||||||
{
|
{
|
||||||
DialogRender* dialog = new DialogRender(this, _renderer);
|
DialogRender dialog(this, _renderer);
|
||||||
dialog->loadLastRender();
|
dialog.loadLastRender();
|
||||||
|
|
||||||
delete dialog;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
|
|
||||||
#include "baseform.h"
|
#include "baseform.h"
|
||||||
|
|
||||||
#include "RenderArea.h"
|
#include "RenderConfig.h"
|
||||||
|
|
||||||
class FormRender : public BaseForm
|
class FormRender : public BaseForm
|
||||||
{
|
{
|
||||||
|
@ -29,9 +29,9 @@ protected slots:
|
||||||
virtual void configChangeEvent();
|
virtual void configChangeEvent();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
RenderArea::RenderParams _params;
|
RenderConfig _params;
|
||||||
CameraDefinition* _camera;
|
CameraDefinition* _camera;
|
||||||
SoftwareRenderer* _renderer;
|
SoftwareCanvasRenderer* _renderer;
|
||||||
bool _renderer_inited;
|
bool _renderer_inited;
|
||||||
BasePreview* _preview_landscape;
|
BasePreview* _preview_landscape;
|
||||||
Base2dPreviewRenderer* _preview_landscape_renderer;
|
Base2dPreviewRenderer* _preview_landscape_renderer;
|
||||||
|
|
112
src/render/software/Canvas.cpp
Normal file
112
src/render/software/Canvas.cpp
Normal file
|
@ -0,0 +1,112 @@
|
||||||
|
#include "Canvas.h"
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
|
|
||||||
|
#include "CanvasPortion.h"
|
||||||
|
#include "CanvasPreview.h"
|
||||||
|
#include "CanvasPictureWriter.h"
|
||||||
|
|
||||||
|
#define CUTTER_SIZE 800
|
||||||
|
|
||||||
|
Canvas::Canvas()
|
||||||
|
{
|
||||||
|
horizontal_portion_count = 1;
|
||||||
|
vertical_portion_count = 1;
|
||||||
|
width = 1;
|
||||||
|
height = 1;
|
||||||
|
portions.push_back(new CanvasPortion);
|
||||||
|
|
||||||
|
preview = new CanvasPreview;
|
||||||
|
}
|
||||||
|
|
||||||
|
Canvas::~Canvas()
|
||||||
|
{
|
||||||
|
for (auto portion: portions)
|
||||||
|
{
|
||||||
|
delete portion;
|
||||||
|
}
|
||||||
|
delete preview;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Canvas::setSize(int width, int height)
|
||||||
|
{
|
||||||
|
horizontal_portion_count = 1 + (width - 1) / CUTTER_SIZE;
|
||||||
|
vertical_portion_count = 1 + (height - 1) / CUTTER_SIZE;
|
||||||
|
|
||||||
|
int portion_width = width / horizontal_portion_count;
|
||||||
|
int portion_height = height / vertical_portion_count;
|
||||||
|
|
||||||
|
for (auto portion: portions)
|
||||||
|
{
|
||||||
|
delete portion;
|
||||||
|
}
|
||||||
|
portions.clear();
|
||||||
|
|
||||||
|
int done_width = 0;
|
||||||
|
int done_height = 0;
|
||||||
|
int index = 0;
|
||||||
|
|
||||||
|
for (int y = 0; y < vertical_portion_count; y++)
|
||||||
|
{
|
||||||
|
done_width = 0;
|
||||||
|
for (int x = 0; x < horizontal_portion_count; x++)
|
||||||
|
{
|
||||||
|
CanvasPortion *portion = new CanvasPortion(index++, preview);
|
||||||
|
|
||||||
|
portion->setSize((x == horizontal_portion_count - 1) ? width - done_width : portion_width,
|
||||||
|
(y == vertical_portion_count - 1) ? height - done_height : portion_height,
|
||||||
|
done_width,
|
||||||
|
done_height);
|
||||||
|
|
||||||
|
done_width += portion->getWidth();
|
||||||
|
if (x == horizontal_portion_count - 1)
|
||||||
|
{
|
||||||
|
done_height += portion->getHeight();
|
||||||
|
}
|
||||||
|
|
||||||
|
portions.push_back(portion);
|
||||||
|
}
|
||||||
|
assert(done_width == width);
|
||||||
|
}
|
||||||
|
assert(done_height == height);
|
||||||
|
|
||||||
|
this->width = width;
|
||||||
|
this->height = height;
|
||||||
|
|
||||||
|
// Smaller preview
|
||||||
|
while (width > 1000 or height > 700)
|
||||||
|
{
|
||||||
|
width = width / 2;
|
||||||
|
height = height / 2;
|
||||||
|
}
|
||||||
|
preview->setSize(this->width, this->height, width, height);
|
||||||
|
}
|
||||||
|
|
||||||
|
CanvasPortion *Canvas::at(int x, int y) const
|
||||||
|
{
|
||||||
|
assert(x >= 0 && x < horizontal_portion_count);
|
||||||
|
assert(y >= 0 && y < vertical_portion_count);
|
||||||
|
|
||||||
|
return portions[y * horizontal_portion_count + x];
|
||||||
|
}
|
||||||
|
|
||||||
|
CanvasPortion *Canvas::atPixel(int x, int y) const
|
||||||
|
{
|
||||||
|
assert(x >= 0 && x < width);
|
||||||
|
assert(y >= 0 && y < height);
|
||||||
|
|
||||||
|
int pwidth = portions[0]->getWidth();
|
||||||
|
int pheight = portions[0]->getHeight();
|
||||||
|
|
||||||
|
return at(x / pwidth, y / pheight);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Canvas::saveToDisk(const std::string &filepath, const ColorProfile &profile, int antialias) const
|
||||||
|
{
|
||||||
|
assert(antialias >= 1);
|
||||||
|
|
||||||
|
CanvasPictureWriter writer(this);
|
||||||
|
writer.setColorProfile(profile);
|
||||||
|
writer.setAntialias(antialias);
|
||||||
|
return writer.saveCanvas(filepath);
|
||||||
|
}
|
53
src/render/software/Canvas.h
Normal file
53
src/render/software/Canvas.h
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
#ifndef CANVAS_H
|
||||||
|
#define CANVAS_H
|
||||||
|
|
||||||
|
#include "software_global.h"
|
||||||
|
|
||||||
|
namespace paysages {
|
||||||
|
namespace software {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Graphics area to draw and do compositing.
|
||||||
|
*
|
||||||
|
* Software rendering is done in portions of Canvas (in CanvasPortion class).
|
||||||
|
* This splitting in portions allows to keep memory consumption low.
|
||||||
|
*/
|
||||||
|
class SOFTWARESHARED_EXPORT Canvas
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Canvas();
|
||||||
|
~Canvas();
|
||||||
|
|
||||||
|
void setSize(int width, int height);
|
||||||
|
|
||||||
|
inline int getHorizontalPortionCount() const {return horizontal_portion_count;}
|
||||||
|
inline int getVerticalPortionCount() const {return vertical_portion_count;}
|
||||||
|
|
||||||
|
CanvasPortion *at(int x, int y) const;
|
||||||
|
CanvasPortion *atPixel(int x, int y) const;
|
||||||
|
|
||||||
|
inline int getWidth() const {return width;}
|
||||||
|
inline int getHeight() const {return height;}
|
||||||
|
inline CanvasPreview *getPreview() const {return preview;}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Save the canvas to a picture file on disk.
|
||||||
|
*
|
||||||
|
* Returns true if the save was successful.
|
||||||
|
*/
|
||||||
|
bool saveToDisk(const std::string &filepath, const ColorProfile &profile, int antialias) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::vector<CanvasPortion*> portions;
|
||||||
|
int horizontal_portion_count;
|
||||||
|
int vertical_portion_count;
|
||||||
|
int width;
|
||||||
|
int height;
|
||||||
|
|
||||||
|
CanvasPreview *preview;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // CANVAS_H
|
16
src/render/software/CanvasFragment.cpp
Normal file
16
src/render/software/CanvasFragment.cpp
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
#include "CanvasFragment.h"
|
||||||
|
|
||||||
|
CanvasFragment::CanvasFragment()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
CanvasFragment::CanvasFragment(double z, const Vector3 &location, int client, bool opaque):
|
||||||
|
opaque(opaque), z(z), location(location), client(client)
|
||||||
|
{
|
||||||
|
color = COLOR_WHITE;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CanvasFragment::setColor(const Color &col)
|
||||||
|
{
|
||||||
|
color = col;
|
||||||
|
}
|
40
src/render/software/CanvasFragment.h
Normal file
40
src/render/software/CanvasFragment.h
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
#ifndef CANVASFRAGMENT_H
|
||||||
|
#define CANVASFRAGMENT_H
|
||||||
|
|
||||||
|
#include "software_global.h"
|
||||||
|
|
||||||
|
#include "Color.h"
|
||||||
|
#include "Vector3.h"
|
||||||
|
|
||||||
|
namespace paysages {
|
||||||
|
namespace software {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Representation of world coordinates projected in a canvas pixel.
|
||||||
|
*/
|
||||||
|
class SOFTWARESHARED_EXPORT CanvasFragment
|
||||||
|
{
|
||||||
|
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;}
|
||||||
|
inline int getClient() const {return client;}
|
||||||
|
inline const Color &getColor() const {return color;}
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool opaque;
|
||||||
|
double z;
|
||||||
|
Vector3 location;
|
||||||
|
int client;
|
||||||
|
Color color;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // CANVASFRAGMENT_H
|
17
src/render/software/CanvasLiveClient.cpp
Normal file
17
src/render/software/CanvasLiveClient.cpp
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
#include "CanvasLiveClient.h"
|
||||||
|
|
||||||
|
CanvasLiveClient::CanvasLiveClient()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void CanvasLiveClient::canvasResized(int, int)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void CanvasLiveClient::canvasCleared(const Color &)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void CanvasLiveClient::canvasPainted(int, int, const Color &)
|
||||||
|
{
|
||||||
|
}
|
25
src/render/software/CanvasLiveClient.h
Normal file
25
src/render/software/CanvasLiveClient.h
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
#ifndef CANVASLIVECLIENT_H
|
||||||
|
#define CANVASLIVECLIENT_H
|
||||||
|
|
||||||
|
#include "software_global.h"
|
||||||
|
|
||||||
|
namespace paysages {
|
||||||
|
namespace software {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Abstract class to receive live modifications from canvas preview.
|
||||||
|
*/
|
||||||
|
class SOFTWARESHARED_EXPORT CanvasLiveClient
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CanvasLiveClient();
|
||||||
|
|
||||||
|
virtual void canvasResized(int width, int height);
|
||||||
|
virtual void canvasCleared(const Color &col);
|
||||||
|
virtual void canvasPainted(int x, int y, const Color &col);
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // CANVASLIVECLIENT_H
|
116
src/render/software/CanvasPictureWriter.cpp
Normal file
116
src/render/software/CanvasPictureWriter.cpp
Normal file
|
@ -0,0 +1,116 @@
|
||||||
|
#include "CanvasPictureWriter.h"
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
|
|
||||||
|
#include "Canvas.h"
|
||||||
|
#include "CanvasPortion.h"
|
||||||
|
#include "ColorProfile.h"
|
||||||
|
#include "PackStream.h"
|
||||||
|
|
||||||
|
CanvasPictureWriter::CanvasPictureWriter(const Canvas *canvas):
|
||||||
|
canvas(canvas)
|
||||||
|
{
|
||||||
|
profile = new ColorProfile();
|
||||||
|
antialias = 1;
|
||||||
|
width = canvas->getWidth();
|
||||||
|
height = canvas->getHeight();
|
||||||
|
|
||||||
|
last_portion = NULL;
|
||||||
|
last_stream = NULL;
|
||||||
|
last_y = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
CanvasPictureWriter::~CanvasPictureWriter()
|
||||||
|
{
|
||||||
|
delete profile;
|
||||||
|
if (last_stream)
|
||||||
|
{
|
||||||
|
delete last_stream;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CanvasPictureWriter::setAntialias(int antialias)
|
||||||
|
{
|
||||||
|
assert(antialias >= 1);
|
||||||
|
assert(canvas->getWidth() % antialias == 0);
|
||||||
|
assert(canvas->getHeight() % antialias == 0);
|
||||||
|
|
||||||
|
this->antialias = antialias;
|
||||||
|
this->width = canvas->getWidth() / antialias;
|
||||||
|
this->height = canvas->getHeight() / antialias;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CanvasPictureWriter::setColorProfile(const ColorProfile &profile)
|
||||||
|
{
|
||||||
|
profile.copy(this->profile);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CanvasPictureWriter::saveCanvas(const std::string &filepath)
|
||||||
|
{
|
||||||
|
return save(filepath, width, height);
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int CanvasPictureWriter::getPixel(int x, int y)
|
||||||
|
{
|
||||||
|
Color comp;
|
||||||
|
|
||||||
|
if (antialias > 1)
|
||||||
|
{
|
||||||
|
int basex = x * antialias;
|
||||||
|
int basey = y * antialias;
|
||||||
|
double factor = 1.0 / (antialias * antialias);
|
||||||
|
|
||||||
|
comp = COLOR_BLACK;
|
||||||
|
|
||||||
|
for (int iy = 0; iy < antialias; iy++)
|
||||||
|
{
|
||||||
|
for (int ix = 0; ix < antialias; ix++)
|
||||||
|
{
|
||||||
|
Color col = getRawPixel(basex + ix, basey + iy);
|
||||||
|
comp.r += col.r * factor;
|
||||||
|
comp.g += col.g * factor;
|
||||||
|
comp.b += col.b * factor;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
comp = getRawPixel(x, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
comp = profile->apply(comp);
|
||||||
|
comp.normalize();
|
||||||
|
return comp.to32BitBGRA();
|
||||||
|
}
|
||||||
|
|
||||||
|
Color CanvasPictureWriter::getRawPixel(int x, int y)
|
||||||
|
{
|
||||||
|
// Get the portion this pixel is in
|
||||||
|
CanvasPortion *portion = canvas->atPixel(x, y);
|
||||||
|
|
||||||
|
// While we stay in the same portion line, read is sequential in the stream
|
||||||
|
if (portion != last_portion or last_y != y)
|
||||||
|
{
|
||||||
|
// Get the pack stream positioned at the pixel
|
||||||
|
if (last_stream)
|
||||||
|
{
|
||||||
|
delete last_stream;
|
||||||
|
}
|
||||||
|
last_stream = new PackStream;
|
||||||
|
if (portion->getReadStream(*last_stream, x - portion->getXOffset(), y - portion->getYOffset()))
|
||||||
|
{
|
||||||
|
last_portion = portion;
|
||||||
|
last_y = y;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Portion has no stream
|
||||||
|
return COLOR_BLACK;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load the pixel
|
||||||
|
Color col;
|
||||||
|
col.load(last_stream);
|
||||||
|
return col;
|
||||||
|
}
|
58
src/render/software/CanvasPictureWriter.h
Normal file
58
src/render/software/CanvasPictureWriter.h
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
#ifndef CANVASPICTUREWRITER_H
|
||||||
|
#define CANVASPICTUREWRITER_H
|
||||||
|
|
||||||
|
#include "software_global.h"
|
||||||
|
|
||||||
|
#include "PictureWriter.h"
|
||||||
|
|
||||||
|
namespace paysages {
|
||||||
|
namespace software {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Picture writer to create the final image from canvas portions.
|
||||||
|
*/
|
||||||
|
class SOFTWARESHARED_EXPORT CanvasPictureWriter: public PictureWriter
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CanvasPictureWriter(const Canvas *canvas);
|
||||||
|
virtual ~CanvasPictureWriter();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the antialias factor, 1 for no antialiasing.
|
||||||
|
*/
|
||||||
|
void setAntialias(int antialias);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the color profile to apply to final pixels.
|
||||||
|
*/
|
||||||
|
void setColorProfile(const ColorProfile &profile);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start the saving process.
|
||||||
|
*
|
||||||
|
* Returns true if saving was successful.
|
||||||
|
*/
|
||||||
|
bool saveCanvas(const std::string &filepath);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual unsigned int getPixel(int x, int y) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
Color getRawPixel(int x, int y);
|
||||||
|
|
||||||
|
private:
|
||||||
|
const Canvas *canvas;
|
||||||
|
int antialias;
|
||||||
|
int width;
|
||||||
|
int height;
|
||||||
|
ColorProfile *profile;
|
||||||
|
|
||||||
|
CanvasPortion *last_portion;
|
||||||
|
int last_y;
|
||||||
|
PackStream *last_stream;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // CANVASPICTUREWRITER_H
|
106
src/render/software/CanvasPixel.cpp
Normal file
106
src/render/software/CanvasPixel.cpp
Normal file
|
@ -0,0 +1,106 @@
|
||||||
|
#include "CanvasPixel.h"
|
||||||
|
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
|
CanvasPixel::CanvasPixel()
|
||||||
|
{
|
||||||
|
count = 0;
|
||||||
|
composite = COLOR_BLACK;
|
||||||
|
}
|
||||||
|
|
||||||
|
const CanvasFragment *CanvasPixel::getFrontFragment() const
|
||||||
|
{
|
||||||
|
if (count == 0)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return fragments + (count - 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CanvasPixel::reset()
|
||||||
|
{
|
||||||
|
count = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CanvasPixel::pushFragment(const CanvasFragment &fragment)
|
||||||
|
{
|
||||||
|
if (count == 0)
|
||||||
|
{
|
||||||
|
fragments[0] = fragment;
|
||||||
|
count = 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (fragments[0].getOpaque() and fragment.getZ() < fragments[0].getZ())
|
||||||
|
{
|
||||||
|
// behind opaque fragment, don't bother
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// find expected position
|
||||||
|
int i = 0;
|
||||||
|
while (i < count and fragment.getZ() > fragments[i].getZ())
|
||||||
|
{
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fragment.getOpaque())
|
||||||
|
{
|
||||||
|
// Discard fragments masked by the incoming opaque one
|
||||||
|
if (i < count)
|
||||||
|
{
|
||||||
|
memmove(fragments + 1, fragments + i, sizeof(CanvasFragment) * (count - i));
|
||||||
|
count -= i;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
count = 1;
|
||||||
|
}
|
||||||
|
fragments[0] = fragment;
|
||||||
|
}
|
||||||
|
else if (i < count)
|
||||||
|
{
|
||||||
|
// Need to make room for the incoming fragment
|
||||||
|
if (count < MAX_FRAGMENT_COUNT)
|
||||||
|
{
|
||||||
|
memmove(fragments + i + 1, fragments + i, sizeof(CanvasFragment) * (count - i));
|
||||||
|
fragments[i] = fragment;
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (count == MAX_FRAGMENT_COUNT)
|
||||||
|
{
|
||||||
|
// Replace nearest fragment
|
||||||
|
fragments[count - 1] = fragment;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Append
|
||||||
|
fragments[count] = fragment;
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CanvasPixel::setComposite(const Color &color)
|
||||||
|
{
|
||||||
|
composite = color;
|
||||||
|
}
|
42
src/render/software/CanvasPixel.h
Normal file
42
src/render/software/CanvasPixel.h
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
#ifndef CANVASPIXEL_H
|
||||||
|
#define CANVASPIXEL_H
|
||||||
|
|
||||||
|
#include "software_global.h"
|
||||||
|
|
||||||
|
#include "CanvasFragment.h"
|
||||||
|
|
||||||
|
const int MAX_FRAGMENT_COUNT = 7;
|
||||||
|
|
||||||
|
namespace paysages {
|
||||||
|
namespace software {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief One pixel of a Canvas.
|
||||||
|
*
|
||||||
|
* A pixel stores superimposed fragments (CanvasFragment), sorted by their distance to camera.
|
||||||
|
*/
|
||||||
|
class SOFTWARESHARED_EXPORT CanvasPixel
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CanvasPixel();
|
||||||
|
|
||||||
|
inline int getFragmentCount() const {return count;}
|
||||||
|
inline const Color &getComposite() const {return composite;}
|
||||||
|
inline const CanvasFragment &getFragment(int position) const {return fragments[position];}
|
||||||
|
const CanvasFragment *getFrontFragment() const;
|
||||||
|
|
||||||
|
void reset();
|
||||||
|
void pushFragment(const CanvasFragment &fragment);
|
||||||
|
void updateComposite();
|
||||||
|
void setComposite(const Color &color);
|
||||||
|
|
||||||
|
private:
|
||||||
|
int count;
|
||||||
|
CanvasFragment fragments[MAX_FRAGMENT_COUNT];
|
||||||
|
Color composite;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // CANVASPIXEL_H
|
63
src/render/software/CanvasPixelShader.cpp
Normal file
63
src/render/software/CanvasPixelShader.cpp
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
#include "CanvasPixelShader.h"
|
||||||
|
|
||||||
|
#include "Color.h"
|
||||||
|
#include "SoftwareCanvasRenderer.h"
|
||||||
|
#include "CanvasPortion.h"
|
||||||
|
#include "CanvasPixel.h"
|
||||||
|
#include "CanvasFragment.h"
|
||||||
|
#include "Rasterizer.h"
|
||||||
|
|
||||||
|
CanvasPixelShader::CanvasPixelShader(const SoftwareCanvasRenderer &renderer, CanvasPortion *portion, int chunk_size, int sub_chunk_size, int chunks_x, int chunks_y):
|
||||||
|
renderer(renderer), portion(portion), chunk_size(chunk_size), sub_chunk_size(sub_chunk_size), chunks_x(chunks_x), chunks_y(chunks_y)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void CanvasPixelShader::processParallelUnit(int unit)
|
||||||
|
{
|
||||||
|
// Locate the chunk we work on
|
||||||
|
int prev_sub_chunk_size = chunk_size * 2;
|
||||||
|
int chunk_x = unit / chunks_y;
|
||||||
|
int chunk_y = unit % chunks_y;
|
||||||
|
int base_x = chunk_x * chunk_size;
|
||||||
|
int base_y = chunk_y * chunk_size;
|
||||||
|
int limit_x = portion->getWidth() - base_x;
|
||||||
|
int limit_y = portion->getHeight() - base_y;
|
||||||
|
|
||||||
|
limit_x = (limit_x > chunk_size) ? chunk_size : limit_x;
|
||||||
|
limit_y = (limit_y > chunk_size) ? chunk_size : limit_y;
|
||||||
|
|
||||||
|
// Iterate on sub-chunks
|
||||||
|
for (int x = 0; x < limit_x; x += sub_chunk_size)
|
||||||
|
{
|
||||||
|
for (int y = 0; y < limit_y; y += sub_chunk_size)
|
||||||
|
{
|
||||||
|
if (interrupted)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sub_chunk_size == chunk_size or x % prev_sub_chunk_size != 0 or y % prev_sub_chunk_size != 0)
|
||||||
|
{
|
||||||
|
// Resolve the pixel color
|
||||||
|
const CanvasPixel &pixel = portion->at(base_x + x, base_y + y);
|
||||||
|
int n = pixel.getFragmentCount();
|
||||||
|
Color composite = COLOR_BLACK;
|
||||||
|
for (int i = 0; i < n; i++)
|
||||||
|
{
|
||||||
|
const CanvasFragment &fragment = pixel.getFragment(i);
|
||||||
|
const Rasterizer &rasterizer = renderer.getRasterizer(fragment.getClient());
|
||||||
|
composite.mask(rasterizer.shadeFragment(fragment));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fill the square area
|
||||||
|
for (int fx = 0; fx + x < limit_x and fx < sub_chunk_size; fx++)
|
||||||
|
{
|
||||||
|
for (int fy = 0; fy + y < limit_y and fy < sub_chunk_size; fy++)
|
||||||
|
{
|
||||||
|
portion->setColor(base_x + x + fx, base_y + y + fy, composite);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
37
src/render/software/CanvasPixelShader.h
Normal file
37
src/render/software/CanvasPixelShader.h
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
#ifndef CANVASPIXELSHADER_H
|
||||||
|
#define CANVASPIXELSHADER_H
|
||||||
|
|
||||||
|
#include "software_global.h"
|
||||||
|
|
||||||
|
#include "ParallelWorker.h"
|
||||||
|
|
||||||
|
namespace paysages {
|
||||||
|
namespace software {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Parallel worker that can work on canvas portion to resolve pixel colors.
|
||||||
|
*
|
||||||
|
* This is used after the rasterization phase to compute pixel colors from the fragments stored in them.
|
||||||
|
*
|
||||||
|
* This worker will be set to work on a given chunk of a canvas portion.
|
||||||
|
*/
|
||||||
|
class CanvasPixelShader: public ParallelWorker
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CanvasPixelShader(const SoftwareCanvasRenderer &renderer, CanvasPortion *portion, int chunk_size, int sub_chunk_size, int chunks_x, int chunks_y);
|
||||||
|
|
||||||
|
virtual void processParallelUnit(int unit) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
const SoftwareCanvasRenderer &renderer;
|
||||||
|
CanvasPortion *portion;
|
||||||
|
int chunk_size;
|
||||||
|
int sub_chunk_size;
|
||||||
|
int chunks_x;
|
||||||
|
int chunks_y;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // CANVASPIXELSHADER_H
|
168
src/render/software/CanvasPortion.cpp
Normal file
168
src/render/software/CanvasPortion.cpp
Normal file
|
@ -0,0 +1,168 @@
|
||||||
|
#include "CanvasPortion.h"
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
|
|
||||||
|
#include "CanvasPixel.h"
|
||||||
|
#include "CanvasPreview.h"
|
||||||
|
#include "PackStream.h"
|
||||||
|
#include "FileSystem.h"
|
||||||
|
|
||||||
|
#define CHECK_COORDINATES() assert(x >= 0); \
|
||||||
|
assert(x < width); \
|
||||||
|
assert(y >= 0); \
|
||||||
|
assert(y < height); \
|
||||||
|
assert(pixels != NULL)
|
||||||
|
|
||||||
|
CanvasPortion::CanvasPortion(int index, CanvasPreview* preview):
|
||||||
|
index(index), preview(preview)
|
||||||
|
{
|
||||||
|
width = 1;
|
||||||
|
height = 1;
|
||||||
|
xoffset = 0;
|
||||||
|
yoffset = 0;
|
||||||
|
pixels = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
CanvasPortion::~CanvasPortion()
|
||||||
|
{
|
||||||
|
if (pixels)
|
||||||
|
{
|
||||||
|
delete[] pixels;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int CanvasPortion::getFragmentCount(int x, int y) const
|
||||||
|
{
|
||||||
|
CHECK_COORDINATES();
|
||||||
|
|
||||||
|
return pixels[y * width + x].getFragmentCount();
|
||||||
|
}
|
||||||
|
|
||||||
|
const CanvasFragment *CanvasPortion::getFrontFragment(int x, int y) const
|
||||||
|
{
|
||||||
|
CHECK_COORDINATES();
|
||||||
|
|
||||||
|
return pixels[y * width + x].getFrontFragment();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CanvasPortion::clear()
|
||||||
|
{
|
||||||
|
int n = width * height;
|
||||||
|
for (int i = 0; i < n; i++)
|
||||||
|
{
|
||||||
|
pixels[i].reset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CanvasPortion::setSize(int width, int height, int xoffset, int yoffset)
|
||||||
|
{
|
||||||
|
this->width = width;
|
||||||
|
this->height = height;
|
||||||
|
this->xoffset = xoffset;
|
||||||
|
this->yoffset = yoffset;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CanvasPortion::preparePixels()
|
||||||
|
{
|
||||||
|
if (pixels)
|
||||||
|
{
|
||||||
|
delete[] pixels;
|
||||||
|
}
|
||||||
|
pixels = new CanvasPixel[width * height];
|
||||||
|
clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CanvasPortion::discardPixels(bool save)
|
||||||
|
{
|
||||||
|
if (pixels)
|
||||||
|
{
|
||||||
|
if (save)
|
||||||
|
{
|
||||||
|
saveToDisk();
|
||||||
|
}
|
||||||
|
delete[] pixels;
|
||||||
|
pixels = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CanvasPortion::saveToDisk()
|
||||||
|
{
|
||||||
|
if (pixels)
|
||||||
|
{
|
||||||
|
filepath = FileSystem::getTempFile("paysages_portion_" + std::to_string(index) + ".dat");
|
||||||
|
PackStream stream;
|
||||||
|
stream.bindToFile(filepath, true);
|
||||||
|
stream.write(&width);
|
||||||
|
stream.write(&height);
|
||||||
|
for (int y = 0; y < height; y++)
|
||||||
|
{
|
||||||
|
for (int x = 0; x < width; x++)
|
||||||
|
{
|
||||||
|
pixels[y * width + x].getComposite().save(&stream);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CanvasPortion::getReadStream(PackStream &stream, int x, int y)
|
||||||
|
{
|
||||||
|
if (FileSystem::isFile(filepath))
|
||||||
|
{
|
||||||
|
if (not stream.bindToFile(filepath))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int unused_i;
|
||||||
|
stream.skip(unused_i, 2);
|
||||||
|
|
||||||
|
if (x > 0 or y > 0)
|
||||||
|
{
|
||||||
|
double unused_d;
|
||||||
|
stream.skip(unused_d, (y * width + x) * 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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(xoffset + x, yoffset + y, old_color, pixel.getComposite());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const CanvasPixel &CanvasPortion::at(int x, int y)
|
||||||
|
{
|
||||||
|
CHECK_COORDINATES();
|
||||||
|
|
||||||
|
return pixels[y * width + x];
|
||||||
|
}
|
||||||
|
|
||||||
|
void CanvasPortion::setColor(int x, int y, const Color &color)
|
||||||
|
{
|
||||||
|
CHECK_COORDINATES();
|
||||||
|
|
||||||
|
CanvasPixel &pixel = pixels[y * width + x];
|
||||||
|
Color old_color = pixel.getComposite();
|
||||||
|
|
||||||
|
pixel.setComposite(color);
|
||||||
|
|
||||||
|
if (preview)
|
||||||
|
{
|
||||||
|
preview->pushPixel(xoffset + x, yoffset + y, old_color, pixel.getComposite());
|
||||||
|
}
|
||||||
|
}
|
91
src/render/software/CanvasPortion.h
Normal file
91
src/render/software/CanvasPortion.h
Normal file
|
@ -0,0 +1,91 @@
|
||||||
|
#ifndef CANVASPORTION_H
|
||||||
|
#define CANVASPORTION_H
|
||||||
|
|
||||||
|
#include "software_global.h"
|
||||||
|
|
||||||
|
namespace paysages {
|
||||||
|
namespace software {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rectangular portion of a Canvas.
|
||||||
|
*
|
||||||
|
* Contains the pixels of a canvas region (CanvasPixel).
|
||||||
|
*
|
||||||
|
* Pixels are not allocated until preparePixels is called.
|
||||||
|
*/
|
||||||
|
class SOFTWARESHARED_EXPORT CanvasPortion
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CanvasPortion(int index=0, CanvasPreview *preview=NULL);
|
||||||
|
virtual ~CanvasPortion();
|
||||||
|
|
||||||
|
inline int getWidth() const {return width;}
|
||||||
|
inline int getHeight() const {return height;}
|
||||||
|
inline int getXOffset() const {return xoffset;}
|
||||||
|
inline int getYOffset() const {return yoffset;}
|
||||||
|
int getFragmentCount(int x, int y) const;
|
||||||
|
const CanvasFragment *getFrontFragment(int x, int y) const;
|
||||||
|
|
||||||
|
void clear();
|
||||||
|
void setSize(int width, int height, int xoffset=0, int yoffset=0);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prepare (allocate in memory) the pixels area.
|
||||||
|
*/
|
||||||
|
void preparePixels();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Discard the memory used by pixels.
|
||||||
|
*
|
||||||
|
* If save is true, the portion will be saved to disk before.
|
||||||
|
*/
|
||||||
|
void discardPixels(bool save=true);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Save the portion to a picture file on disk.
|
||||||
|
*/
|
||||||
|
void saveToDisk();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Bind a stream to pixel data, and position it on a given pixel.
|
||||||
|
*
|
||||||
|
* Returns true if the stream was successfully located, false if it was not possible.
|
||||||
|
*/
|
||||||
|
bool getReadStream(PackStream &stream, int x=0, int y=0);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a fragment to the pixel located at (x, y).
|
||||||
|
*
|
||||||
|
* Checking x and y coordinates to be in the canvas portion should be done before this call.
|
||||||
|
*/
|
||||||
|
void pushFragment(int x, int y, const CanvasFragment &fragment);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the CanvasPixel at a given coordinates.
|
||||||
|
*
|
||||||
|
* Checking x and y coordinates to be in the canvas portion should be done before this call.
|
||||||
|
*/
|
||||||
|
const CanvasPixel &at(int x, int y);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Change the final color of the pixel.
|
||||||
|
*
|
||||||
|
* Checking x and y coordinates to be in the canvas portion should be done before this call.
|
||||||
|
*/
|
||||||
|
void setColor(int x, int y, const Color &color);
|
||||||
|
|
||||||
|
private:
|
||||||
|
int index;
|
||||||
|
int width;
|
||||||
|
int height;
|
||||||
|
int xoffset;
|
||||||
|
int yoffset;
|
||||||
|
CanvasPixel *pixels;
|
||||||
|
CanvasPreview *preview;
|
||||||
|
std::string filepath;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // CANVASPORTION_H
|
182
src/render/software/CanvasPreview.cpp
Normal file
182
src/render/software/CanvasPreview.cpp
Normal file
|
@ -0,0 +1,182 @@
|
||||||
|
#include "CanvasPreview.h"
|
||||||
|
|
||||||
|
#include "Color.h"
|
||||||
|
#include "CanvasLiveClient.h"
|
||||||
|
#include "Mutex.h"
|
||||||
|
#include "ColorProfile.h"
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
|
|
||||||
|
#define CHECK_COORDINATES(_x_, _y_) \
|
||||||
|
assert(_x_ >= 0); \
|
||||||
|
assert(_y_ >= 0); \
|
||||||
|
assert(_x_ < this->width); \
|
||||||
|
assert(_y_ < this->height) \
|
||||||
|
|
||||||
|
CanvasPreview::CanvasPreview()
|
||||||
|
{
|
||||||
|
width = 1;
|
||||||
|
height = 1;
|
||||||
|
pixels = new Color[1];
|
||||||
|
|
||||||
|
dirty_left = 1;
|
||||||
|
dirty_right = -1;
|
||||||
|
dirty_down = 1;
|
||||||
|
dirty_up = -1;
|
||||||
|
|
||||||
|
scaled = false;
|
||||||
|
factor = 1.0;
|
||||||
|
factor_x = 1.0;
|
||||||
|
factor_y = 1.0;
|
||||||
|
|
||||||
|
lock = new Mutex();
|
||||||
|
profile = new ColorProfile();
|
||||||
|
}
|
||||||
|
|
||||||
|
CanvasPreview::~CanvasPreview()
|
||||||
|
{
|
||||||
|
delete [] pixels;
|
||||||
|
delete lock;
|
||||||
|
delete profile;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Color &CanvasPreview::getFinalPixel(int x, int y) const
|
||||||
|
{
|
||||||
|
CHECK_COORDINATES(x, y);
|
||||||
|
|
||||||
|
return pixels[y * width + x];
|
||||||
|
}
|
||||||
|
|
||||||
|
void CanvasPreview::setSize(int real_width, int real_height, int preview_width, int preview_height)
|
||||||
|
{
|
||||||
|
lock->acquire();
|
||||||
|
|
||||||
|
delete [] pixels;
|
||||||
|
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;
|
||||||
|
|
||||||
|
scaled = (real_width != preview_height or real_height != preview_height);
|
||||||
|
factor_x = (double)preview_width / (double)real_width;
|
||||||
|
factor_y = (double)preview_height / (double)real_height;
|
||||||
|
factor = factor_x * factor_y;
|
||||||
|
|
||||||
|
lock->release();
|
||||||
|
|
||||||
|
reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CanvasPreview::setToneMapping(const ColorProfile &profile)
|
||||||
|
{
|
||||||
|
profile.copy(this->profile);
|
||||||
|
setAllDirty();
|
||||||
|
}
|
||||||
|
|
||||||
|
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, profile->apply(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)
|
||||||
|
{
|
||||||
|
int x, y;
|
||||||
|
|
||||||
|
if (scaled)
|
||||||
|
{
|
||||||
|
x = int(real_x * factor_x);
|
||||||
|
y = int(real_y * factor_y);
|
||||||
|
|
||||||
|
x = (x >= width) ? width - 1 : x;
|
||||||
|
y = (y >= height) ? height - 1 : y;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
x = real_x;
|
||||||
|
y = real_y;
|
||||||
|
}
|
||||||
|
|
||||||
|
CHECK_COORDINATES(x, y);
|
||||||
|
|
||||||
|
lock->acquire();
|
||||||
|
|
||||||
|
Color* pixel = pixels + (y * width + x);
|
||||||
|
pixel->r = pixel->r - old_color.r * factor + new_color.r * factor;
|
||||||
|
pixel->g = pixel->g - old_color.g * factor + new_color.g * factor;
|
||||||
|
pixel->b = pixel->b - old_color.b * factor + new_color.b * factor;
|
||||||
|
|
||||||
|
// 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();
|
||||||
|
}
|
59
src/render/software/CanvasPreview.h
Normal file
59
src/render/software/CanvasPreview.h
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
#ifndef CANVASPREVIEW_H
|
||||||
|
#define CANVASPREVIEW_H
|
||||||
|
|
||||||
|
#include "software_global.h"
|
||||||
|
|
||||||
|
namespace paysages {
|
||||||
|
namespace software {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Smaller preview of a Canvas rendering, that can be watched live.
|
||||||
|
*/
|
||||||
|
class SOFTWARESHARED_EXPORT CanvasPreview
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CanvasPreview();
|
||||||
|
~CanvasPreview();
|
||||||
|
|
||||||
|
inline int getWidth() const {return width;}
|
||||||
|
inline int getHeight() const {return height;}
|
||||||
|
inline const ColorProfile *getToneMapping() const {return profile;}
|
||||||
|
|
||||||
|
const Color &getFinalPixel(int x, int y) const;
|
||||||
|
|
||||||
|
void setSize(int real_width, int real_height, int preview_width, int preview_height);
|
||||||
|
void setToneMapping(const ColorProfile &profile);
|
||||||
|
void reset();
|
||||||
|
|
||||||
|
void initLive(CanvasLiveClient *client);
|
||||||
|
void updateLive(CanvasLiveClient *client);
|
||||||
|
|
||||||
|
void pushPixel(int real_x, int real_y, const Color &old_color, const Color &new_color);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void setAllDirty();
|
||||||
|
|
||||||
|
private:
|
||||||
|
Mutex *lock;
|
||||||
|
|
||||||
|
Color *pixels;
|
||||||
|
int width;
|
||||||
|
int height;
|
||||||
|
|
||||||
|
ColorProfile *profile;
|
||||||
|
|
||||||
|
int dirty_left;
|
||||||
|
int dirty_right;
|
||||||
|
int dirty_down;
|
||||||
|
int dirty_up;
|
||||||
|
|
||||||
|
bool scaled;
|
||||||
|
double factor;
|
||||||
|
double factor_x;
|
||||||
|
double factor_y;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // CANVASPREVIEW_H
|
373
src/render/software/Rasterizer.cpp
Normal file
373
src/render/software/Rasterizer.cpp
Normal file
|
@ -0,0 +1,373 @@
|
||||||
|
#include "Rasterizer.h"
|
||||||
|
|
||||||
|
#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, const Color &color):
|
||||||
|
renderer(renderer), client_id(client_id)
|
||||||
|
{
|
||||||
|
this->color = new Color(color);
|
||||||
|
|
||||||
|
interrupted = false;
|
||||||
|
predicted_poly_count = 0;
|
||||||
|
done_poly_count = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
Rasterizer::~Rasterizer()
|
||||||
|
{
|
||||||
|
delete color;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Rasterizer::interrupt()
|
||||||
|
{
|
||||||
|
interrupted = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Rasterizer::addPredictedPolys(int count)
|
||||||
|
{
|
||||||
|
predicted_poly_count += count;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Rasterizer::addDonePolys(int count)
|
||||||
|
{
|
||||||
|
done_poly_count += count;
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
Vector3 canvas_offset(canvas->getXOffset(), canvas->getYOffset(), 0.0);
|
||||||
|
Vector3 dpixel1 = pixel1.sub(canvas_offset);
|
||||||
|
Vector3 dpixel2 = pixel2.sub(canvas_offset);
|
||||||
|
Vector3 dpixel3 = pixel3.sub(canvas_offset);
|
||||||
|
|
||||||
|
/* Filter if outside screen */
|
||||||
|
if (dpixel1.z < 1.0 || dpixel2.z < 1.0 || dpixel3.z < 1.0 || (dpixel1.x < 0.0 && dpixel2.x < 0.0 && dpixel3.x < 0.0) || (dpixel1.y < 0.0 && dpixel2.y < 0.0 && dpixel3.y < 0.0) || (dpixel1.x > limit_width && dpixel2.x > limit_width && dpixel3.x > limit_width) || (dpixel1.y > limit_height && dpixel2.y > limit_height && dpixel3.y > limit_height))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Prepare vertices */
|
||||||
|
point1.pixel.x = dpixel1.x;
|
||||||
|
point1.pixel.y = dpixel1.y;
|
||||||
|
point1.pixel.z = dpixel1.z;
|
||||||
|
point1.location.x = location1.x;
|
||||||
|
point1.location.y = location1.y;
|
||||||
|
point1.location.z = location1.z;
|
||||||
|
point1.client = client_id;
|
||||||
|
|
||||||
|
point2.pixel.x = dpixel2.x;
|
||||||
|
point2.pixel.y = dpixel2.y;
|
||||||
|
point2.pixel.z = dpixel2.z;
|
||||||
|
point2.location.x = location2.x;
|
||||||
|
point2.location.y = location2.y;
|
||||||
|
point2.location.z = location2.z;
|
||||||
|
point2.client = client_id;
|
||||||
|
|
||||||
|
point3.pixel.x = dpixel3.x;
|
||||||
|
point3.pixel.y = dpixel3.y;
|
||||||
|
point3.pixel.z = dpixel3.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::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 = 1.0 / camera->getRealDepth(vec1);
|
||||||
|
double v2depth = 1.0 / camera->getRealDepth(vec1.add(vecdiff));
|
||||||
|
double factor = 1.0 / ((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++)
|
||||||
|
{
|
||||||
|
if (dy == 0)
|
||||||
|
{
|
||||||
|
// Down and up are the same
|
||||||
|
current = down;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
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);
|
||||||
|
|
||||||
|
Color frag_color = *color;
|
||||||
|
if (cury == starty || cury == endy)
|
||||||
|
{
|
||||||
|
frag_color.mask(Color(0.0, 0.0, 0.0, 0.3));
|
||||||
|
}
|
||||||
|
fragment.setColor(frag_color);
|
||||||
|
|
||||||
|
canvas->pushFragment(current.x, current.y, fragment);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
57
src/render/software/Rasterizer.h
Normal file
57
src/render/software/Rasterizer.h
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
#ifndef RASTERIZER_H
|
||||||
|
#define RASTERIZER_H
|
||||||
|
|
||||||
|
#include "software_global.h"
|
||||||
|
|
||||||
|
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(SoftwareRenderer *renderer, int client_id, const Color &color);
|
||||||
|
virtual ~Rasterizer();
|
||||||
|
|
||||||
|
inline SoftwareRenderer *getRenderer() const {return renderer;}
|
||||||
|
|
||||||
|
virtual void rasterizeToCanvas(CanvasPortion* canvas) = 0;
|
||||||
|
virtual Color shadeFragment(const CanvasFragment &fragment) const = 0;
|
||||||
|
virtual void interrupt();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void addPredictedPolys(int count=1);
|
||||||
|
void addDonePolys(int count=1);
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
Color* color;
|
||||||
|
SoftwareRenderer *renderer;
|
||||||
|
int client_id;
|
||||||
|
bool interrupted;
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
int predicted_poly_count;
|
||||||
|
int done_poly_count;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // RASTERIZER_H
|
|
@ -1,792 +0,0 @@
|
||||||
#include "RenderArea.h"
|
|
||||||
|
|
||||||
#include "Vector3.h"
|
|
||||||
#include "ColorProfile.h"
|
|
||||||
#include "Mutex.h"
|
|
||||||
#include "CameraDefinition.h"
|
|
||||||
#include "SoftwareRenderer.h"
|
|
||||||
#include "Thread.h"
|
|
||||||
#include "PictureWriter.h"
|
|
||||||
|
|
||||||
struct RenderFragment
|
|
||||||
{
|
|
||||||
struct
|
|
||||||
{
|
|
||||||
unsigned char dirty : 1;
|
|
||||||
unsigned char edge : 1;
|
|
||||||
unsigned char callback : 6;
|
|
||||||
} flags;
|
|
||||||
union
|
|
||||||
{
|
|
||||||
struct {
|
|
||||||
double x;
|
|
||||||
double y;
|
|
||||||
double z;
|
|
||||||
} location;
|
|
||||||
struct {
|
|
||||||
double r;
|
|
||||||
double g;
|
|
||||||
double b;
|
|
||||||
} color;
|
|
||||||
} data;
|
|
||||||
double z;
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef struct
|
|
||||||
{
|
|
||||||
int x;
|
|
||||||
int y;
|
|
||||||
struct {
|
|
||||||
double x;
|
|
||||||
double y;
|
|
||||||
double z;
|
|
||||||
} pixel;
|
|
||||||
struct {
|
|
||||||
double x;
|
|
||||||
double y;
|
|
||||||
double z;
|
|
||||||
} location;
|
|
||||||
int callback;
|
|
||||||
} ScanPoint;
|
|
||||||
|
|
||||||
struct FragmentCallback
|
|
||||||
{
|
|
||||||
RenderArea::f_RenderFragmentCallback function;
|
|
||||||
void* data;
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef struct
|
|
||||||
{
|
|
||||||
ScanPoint* up;
|
|
||||||
ScanPoint* down;
|
|
||||||
int left;
|
|
||||||
int right;
|
|
||||||
} RenderScanlines;
|
|
||||||
|
|
||||||
typedef struct
|
|
||||||
{
|
|
||||||
int startx;
|
|
||||||
int endx;
|
|
||||||
int starty;
|
|
||||||
int endy;
|
|
||||||
int finished;
|
|
||||||
int interrupt;
|
|
||||||
int pixel_done;
|
|
||||||
Thread* thread;
|
|
||||||
RenderArea* area;
|
|
||||||
} RenderChunk;
|
|
||||||
|
|
||||||
static void _callbackStart(int, int, const Color&) {}
|
|
||||||
static void _callbackDraw(int, int, const Color&) {}
|
|
||||||
static void _callbackUpdate(double) {}
|
|
||||||
|
|
||||||
RenderArea::RenderArea(SoftwareRenderer* renderer)
|
|
||||||
{
|
|
||||||
this->renderer = renderer;
|
|
||||||
this->hdr_mapping = new ColorProfile;
|
|
||||||
this->params.width = 1;
|
|
||||||
this->params.height = 1;
|
|
||||||
this->params.antialias = 1;
|
|
||||||
this->params.quality = 5;
|
|
||||||
this->pixel_count = 1;
|
|
||||||
this->pixels = new RenderFragment[1];
|
|
||||||
this->fragment_callbacks_count = 0;
|
|
||||||
this->fragment_callbacks = new FragmentCallback[64];
|
|
||||||
this->background_color = COLOR_TRANSPARENT;
|
|
||||||
this->dirty_left = 1;
|
|
||||||
this->dirty_right = -1;
|
|
||||||
this->dirty_down = 1;
|
|
||||||
this->dirty_up = -1;
|
|
||||||
this->dirty_count = 0;
|
|
||||||
this->lock = new Mutex();
|
|
||||||
this->callback_start = _callbackStart;
|
|
||||||
this->callback_draw = _callbackDraw;
|
|
||||||
this->callback_update = _callbackUpdate;
|
|
||||||
}
|
|
||||||
|
|
||||||
RenderArea::~RenderArea()
|
|
||||||
{
|
|
||||||
delete hdr_mapping;
|
|
||||||
delete lock;
|
|
||||||
delete[] fragment_callbacks;
|
|
||||||
delete[] pixels;
|
|
||||||
}
|
|
||||||
|
|
||||||
void RenderArea::setAllDirty()
|
|
||||||
{
|
|
||||||
dirty_left = 0;
|
|
||||||
dirty_right = params.width * params.antialias - 1;
|
|
||||||
dirty_down = 0;
|
|
||||||
dirty_up = params.height * params.antialias - 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
void RenderArea::setParams(RenderParams params)
|
|
||||||
{
|
|
||||||
int width, height;
|
|
||||||
|
|
||||||
width = params.width * params.antialias;
|
|
||||||
height = params.height * params.antialias;
|
|
||||||
|
|
||||||
this->params = params;
|
|
||||||
delete[] pixels;
|
|
||||||
pixels = new RenderFragment[width * height];
|
|
||||||
pixel_count = width * height;
|
|
||||||
|
|
||||||
dirty_left = width;
|
|
||||||
dirty_right = -1;
|
|
||||||
dirty_down = height;
|
|
||||||
dirty_up = -1;
|
|
||||||
dirty_count = 0;
|
|
||||||
|
|
||||||
clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
void RenderArea::setToneMapping(const ColorProfile &profile)
|
|
||||||
{
|
|
||||||
profile.copy(hdr_mapping);
|
|
||||||
setAllDirty();
|
|
||||||
update();
|
|
||||||
}
|
|
||||||
|
|
||||||
void RenderArea::setBackgroundColor(const Color &col)
|
|
||||||
{
|
|
||||||
background_color = col;
|
|
||||||
}
|
|
||||||
|
|
||||||
void RenderArea::clear()
|
|
||||||
{
|
|
||||||
RenderFragment* pixel;
|
|
||||||
int x;
|
|
||||||
int y;
|
|
||||||
|
|
||||||
fragment_callbacks_count = 1;
|
|
||||||
fragment_callbacks[0].function = NULL;
|
|
||||||
fragment_callbacks[0].data = NULL;
|
|
||||||
|
|
||||||
for (x = 0; x < params.width * params.antialias; x++)
|
|
||||||
{
|
|
||||||
for (y = 0; y < params.height * params.antialias; y++)
|
|
||||||
{
|
|
||||||
pixel = pixels + (y * params.width * params.antialias + x);
|
|
||||||
pixel->z = -100000000.0;
|
|
||||||
pixel->flags.dirty = 0;
|
|
||||||
pixel->flags.callback = 0;
|
|
||||||
pixel->data.color.r = background_color.r;
|
|
||||||
pixel->data.color.g = background_color.g;
|
|
||||||
pixel->data.color.b = background_color.b;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
callback_start(params.width, params.height, background_color);
|
|
||||||
|
|
||||||
dirty_left = params.width * params.antialias;
|
|
||||||
dirty_right = -1;
|
|
||||||
dirty_down = params.height * params.antialias;
|
|
||||||
dirty_up = -1;
|
|
||||||
dirty_count = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void _setDirtyPixel(RenderArea* area, int x, int y)
|
|
||||||
{
|
|
||||||
if (x < area->dirty_left)
|
|
||||||
{
|
|
||||||
area->dirty_left = x;
|
|
||||||
}
|
|
||||||
if (x > area->dirty_right)
|
|
||||||
{
|
|
||||||
area->dirty_right = x;
|
|
||||||
}
|
|
||||||
if (y < area->dirty_down)
|
|
||||||
{
|
|
||||||
area->dirty_down = y;
|
|
||||||
}
|
|
||||||
if (y > area->dirty_up)
|
|
||||||
{
|
|
||||||
area->dirty_up = y;
|
|
||||||
}
|
|
||||||
|
|
||||||
area->dirty_count++;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline Color _getFinalPixel(RenderArea* area, int x, int y)
|
|
||||||
{
|
|
||||||
Color result, col;
|
|
||||||
int sx, sy;
|
|
||||||
double factor = 1.0 / (double)(area->params.antialias * area->params.antialias);
|
|
||||||
RenderFragment* pixel_data;
|
|
||||||
|
|
||||||
result.r = result.g = result.b = 0.0;
|
|
||||||
result.a = 1.0;
|
|
||||||
for (sx = 0; sx < area->params.antialias; sx++)
|
|
||||||
{
|
|
||||||
for (sy = 0; sy < area->params.antialias; sy++)
|
|
||||||
{
|
|
||||||
pixel_data = area->pixels + (y * area->params.antialias + sy) * area->params.width * area->params.antialias + (x * area->params.antialias + sx);
|
|
||||||
if (pixel_data->flags.dirty)
|
|
||||||
{
|
|
||||||
if (pixel_data->flags.edge)
|
|
||||||
{
|
|
||||||
col = COLOR_GREY;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
col = COLOR_WHITE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
col.r = pixel_data->data.color.r;
|
|
||||||
col.g = pixel_data->data.color.g;
|
|
||||||
col.b = pixel_data->data.color.b;
|
|
||||||
}
|
|
||||||
result.r += col.r * factor;
|
|
||||||
result.g += col.g * factor;
|
|
||||||
result.b += col.b * factor;
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return area->hdr_mapping->apply(result);
|
|
||||||
}
|
|
||||||
|
|
||||||
void RenderArea::processDirtyPixels()
|
|
||||||
{
|
|
||||||
int x, y;
|
|
||||||
int down, up, left, right;
|
|
||||||
|
|
||||||
down = dirty_down / params.antialias;
|
|
||||||
up = dirty_up / params.antialias;
|
|
||||||
left = dirty_left / params.antialias;
|
|
||||||
right = dirty_right / params.antialias;
|
|
||||||
|
|
||||||
for (y = down; y <= up; y++)
|
|
||||||
{
|
|
||||||
for (x = left; x <= right; x++)
|
|
||||||
{
|
|
||||||
callback_draw(x, y, _getFinalPixel(this, x, y));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
callback_update(renderer->render_progress);
|
|
||||||
|
|
||||||
dirty_left = params.width * params.antialias;
|
|
||||||
dirty_right = -1;
|
|
||||||
dirty_down = params.height * params.antialias;
|
|
||||||
dirty_up = -1;
|
|
||||||
dirty_count = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void RenderArea::update()
|
|
||||||
{
|
|
||||||
lock->acquire();
|
|
||||||
processDirtyPixels();
|
|
||||||
lock->release();
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline unsigned int _pushCallback(RenderArea* area, FragmentCallback callback)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
for (i = 0; i < area->fragment_callbacks_count; i++)
|
|
||||||
{
|
|
||||||
if (area->fragment_callbacks[i].function == callback.function && area->fragment_callbacks[i].data == callback.data)
|
|
||||||
{
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (area->fragment_callbacks_count >= 64)
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
area->fragment_callbacks[area->fragment_callbacks_count].function = callback.function;
|
|
||||||
area->fragment_callbacks[area->fragment_callbacks_count].data = callback.data;
|
|
||||||
return area->fragment_callbacks_count++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void RenderArea::pushFragment(int x, int y, double z, int edge, const Vector3 &location, int callback)
|
|
||||||
{
|
|
||||||
RenderFragment* pixel_data;
|
|
||||||
|
|
||||||
if (x >= 0 && x < params.width * params.antialias && y >= 0 && y < params.height * params.antialias && z > 1.0)
|
|
||||||
{
|
|
||||||
pixel_data = pixels + (y * params.width * params.antialias + x);
|
|
||||||
|
|
||||||
if (z > pixel_data->z)
|
|
||||||
{
|
|
||||||
pixel_data->flags.dirty = (unsigned char)1;
|
|
||||||
pixel_data->flags.edge = (unsigned char)edge;
|
|
||||||
pixel_data->flags.callback = (unsigned char)callback;
|
|
||||||
pixel_data->data.location.x = location.x;
|
|
||||||
pixel_data->data.location.y = location.y;
|
|
||||||
pixel_data->data.location.z = location.z;
|
|
||||||
pixel_data->z = z;
|
|
||||||
_setDirtyPixel(this, x, y);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void _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->callback = v1->callback;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void _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->callback = v1->callback;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void _pushScanPoint(RenderArea* area, RenderScanlines* scanlines, ScanPoint* point)
|
|
||||||
{
|
|
||||||
point->x = (int)floor(point->pixel.x);
|
|
||||||
point->y = (int)floor(point->pixel.y);
|
|
||||||
|
|
||||||
if (point->x < 0 || point->x >= area->params.width * area->params.antialias)
|
|
||||||
{
|
|
||||||
// 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 = area->params.height * area->params.antialias;
|
|
||||||
}
|
|
||||||
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 = area->params.height * area->params.antialias;
|
|
||||||
}
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void _pushScanLineEdge(RenderArea* area, 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(area, scanlines, point2, point1);
|
|
||||||
}
|
|
||||||
else if (endx < 0 || startx >= area->params.width * area->params.antialias)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
else if (startx == endx)
|
|
||||||
{
|
|
||||||
_pushScanPoint(area, scanlines, point1);
|
|
||||||
_pushScanPoint(area, scanlines, point2);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (startx < 0)
|
|
||||||
{
|
|
||||||
startx = 0;
|
|
||||||
}
|
|
||||||
if (endx >= area->params.width * area->params.antialias)
|
|
||||||
{
|
|
||||||
endx = area->params.width * area->params.antialias - 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(area->renderer->render_camera, point1, &diff, fx / dx, &point);
|
|
||||||
|
|
||||||
/*point.pixel.x = (double)curx;*/
|
|
||||||
|
|
||||||
_pushScanPoint(area, scanlines, &point);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void _renderScanLines(RenderArea* area, 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 >= area->params.height * area->params.antialias)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (starty < 0)
|
|
||||||
{
|
|
||||||
starty = 0;
|
|
||||||
}
|
|
||||||
if (endy >= area->params.height * area->params.antialias)
|
|
||||||
{
|
|
||||||
endy = area->params.height * area->params.antialias - 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(area->renderer->render_camera, &down, &diff, fy / dy, ¤t);
|
|
||||||
|
|
||||||
Vector3 veclocation = Vector3(current.location.x, current.location.y, current.location.z);
|
|
||||||
area->pushFragment(current.x, current.y, current.pixel.z, (cury == starty || cury == endy), veclocation, current.callback);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void RenderArea::pushTriangle(const Vector3 &pixel1, const Vector3 &pixel2, const Vector3 &pixel3, const Vector3 &location1, const Vector3 &location2, const Vector3 &location3, f_RenderFragmentCallback callback, void* callback_data)
|
|
||||||
{
|
|
||||||
FragmentCallback fragment_callback = {callback, callback_data};
|
|
||||||
ScanPoint point1, point2, point3;
|
|
||||||
double limit_width = (double)(params.width * params.antialias - 1);
|
|
||||||
double limit_height = (double)(params.height * params.antialias - 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 fragment callback */
|
|
||||||
lock->acquire();
|
|
||||||
point1.callback = _pushCallback(this, fragment_callback);
|
|
||||||
lock->release();
|
|
||||||
|
|
||||||
/* Prepare vertices */
|
|
||||||
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;
|
|
||||||
|
|
||||||
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.callback = point1.callback;
|
|
||||||
|
|
||||||
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.callback = point1.callback;
|
|
||||||
|
|
||||||
/* Prepare scanlines */
|
|
||||||
// TODO Don't create scanlines for each triangles (one by thread is more appropriate)
|
|
||||||
RenderScanlines scanlines;
|
|
||||||
int width = params.width * params.antialias;
|
|
||||||
scanlines.left = width;
|
|
||||||
scanlines.right = -1;
|
|
||||||
scanlines.up = new ScanPoint[width];
|
|
||||||
scanlines.down = new ScanPoint[width];
|
|
||||||
|
|
||||||
/* Render edges in scanlines */
|
|
||||||
_pushScanLineEdge(this, &scanlines, &point1, &point2);
|
|
||||||
_pushScanLineEdge(this, &scanlines, &point2, &point3);
|
|
||||||
_pushScanLineEdge(this, &scanlines, &point3, &point1);
|
|
||||||
|
|
||||||
/* Commit scanlines to area */
|
|
||||||
lock->acquire();
|
|
||||||
_renderScanLines(this, &scanlines);
|
|
||||||
lock->release();
|
|
||||||
|
|
||||||
/* Free scalines */
|
|
||||||
delete[] scanlines.up;
|
|
||||||
delete[] scanlines.down;
|
|
||||||
}
|
|
||||||
|
|
||||||
Color RenderArea::getPixel(int x, int y)
|
|
||||||
{
|
|
||||||
Color result;
|
|
||||||
|
|
||||||
lock->acquire();
|
|
||||||
result = _getFinalPixel(this, x, y);
|
|
||||||
lock->release();
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
void* _renderPostProcessChunk(void* data)
|
|
||||||
{
|
|
||||||
int x, y;
|
|
||||||
RenderFragment* fragment;
|
|
||||||
RenderChunk* chunk = (RenderChunk*)data;
|
|
||||||
|
|
||||||
for (x = chunk->startx; x <= chunk->endx; x++)
|
|
||||||
{
|
|
||||||
for (y = chunk->starty; y <= chunk->endy; y++)
|
|
||||||
{
|
|
||||||
fragment = chunk->area->pixels + (y * chunk->area->params.width * chunk->area->params.antialias + x);
|
|
||||||
if (fragment->flags.dirty)
|
|
||||||
{
|
|
||||||
FragmentCallback callback;
|
|
||||||
Color col;
|
|
||||||
|
|
||||||
callback = chunk->area->fragment_callbacks[fragment->flags.callback];
|
|
||||||
if (callback.function)
|
|
||||||
{
|
|
||||||
Vector3 location(fragment->data.location.x, fragment->data.location.y, fragment->data.location.z);
|
|
||||||
col = callback.function(chunk->area->renderer, location, callback.data);
|
|
||||||
/*colorNormalize(&col);*/
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
col = COLOR_BLACK;
|
|
||||||
}
|
|
||||||
|
|
||||||
fragment->data.color.r = col.r;
|
|
||||||
fragment->data.color.g = col.g;
|
|
||||||
fragment->data.color.b = col.b;
|
|
||||||
|
|
||||||
chunk->area->lock->acquire();
|
|
||||||
fragment->flags.dirty = 0;
|
|
||||||
_setDirtyPixel(chunk->area, x, y);
|
|
||||||
chunk->area->lock->release();
|
|
||||||
}
|
|
||||||
chunk->area->pixel_done++;
|
|
||||||
}
|
|
||||||
if (chunk->interrupt)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
chunk->finished = 1;
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
#define MAX_CHUNKS 8
|
|
||||||
void RenderArea::postProcess(int nbchunks)
|
|
||||||
{
|
|
||||||
volatile RenderChunk chunks[MAX_CHUNKS];
|
|
||||||
int i;
|
|
||||||
int x, y, dx, dy, nx, ny;
|
|
||||||
int loops, running;
|
|
||||||
|
|
||||||
if (nbchunks > MAX_CHUNKS)
|
|
||||||
{
|
|
||||||
nbchunks = MAX_CHUNKS;
|
|
||||||
}
|
|
||||||
if (nbchunks < 1)
|
|
||||||
{
|
|
||||||
nbchunks = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
nx = 10;
|
|
||||||
ny = 10;
|
|
||||||
dx = params.width * params.antialias / nx;
|
|
||||||
dy = params.height * params.antialias / ny;
|
|
||||||
x = 0;
|
|
||||||
y = 0;
|
|
||||||
pixel_done = 0;
|
|
||||||
|
|
||||||
for (i = 0; i < nbchunks; i++)
|
|
||||||
{
|
|
||||||
chunks[i].thread = NULL;
|
|
||||||
chunks[i].area = this;
|
|
||||||
}
|
|
||||||
|
|
||||||
running = 0;
|
|
||||||
loops = 0;
|
|
||||||
while ((x < nx && !renderer->render_interrupt) || running > 0)
|
|
||||||
{
|
|
||||||
Thread::timeSleepMs(50);
|
|
||||||
|
|
||||||
for (i = 0; i < nbchunks; i++)
|
|
||||||
{
|
|
||||||
if (chunks[i].thread)
|
|
||||||
{
|
|
||||||
if (chunks[i].finished)
|
|
||||||
{
|
|
||||||
chunks[i].thread->join();
|
|
||||||
delete chunks[i].thread;
|
|
||||||
chunks[i].thread = NULL;
|
|
||||||
running--;
|
|
||||||
}
|
|
||||||
else if (renderer->render_interrupt)
|
|
||||||
{
|
|
||||||
chunks[i].interrupt = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
renderer->render_progress = 0.1 + ((double)pixel_done / (double)pixel_count) * 0.9;
|
|
||||||
|
|
||||||
if (x < nx && !chunks[i].thread && !renderer->render_interrupt)
|
|
||||||
{
|
|
||||||
chunks[i].finished = 0;
|
|
||||||
chunks[i].interrupt = 0;
|
|
||||||
chunks[i].startx = x * dx;
|
|
||||||
if (x == nx - 1)
|
|
||||||
{
|
|
||||||
chunks[i].endx = params.width * params.antialias - 1;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
chunks[i].endx = (x + 1) * dx - 1;
|
|
||||||
}
|
|
||||||
chunks[i].starty = y * dy;
|
|
||||||
if (y == ny - 1)
|
|
||||||
{
|
|
||||||
chunks[i].endy = params.height * params.antialias - 1;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
chunks[i].endy = (y + 1) * dy - 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
chunks[i].thread = new Thread(_renderPostProcessChunk);
|
|
||||||
chunks[i].thread->start((void*)(chunks + i));
|
|
||||||
running++;
|
|
||||||
|
|
||||||
if (++y >= ny)
|
|
||||||
{
|
|
||||||
x++;
|
|
||||||
y = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (++loops >= 10)
|
|
||||||
{
|
|
||||||
lock->acquire();
|
|
||||||
processDirtyPixels();
|
|
||||||
lock->release();
|
|
||||||
|
|
||||||
loops = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
processDirtyPixels();
|
|
||||||
callback_update(1.0);
|
|
||||||
}
|
|
||||||
|
|
||||||
class RenderWriter:public PictureWriter
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
RenderWriter(RenderArea *area): area(area) {}
|
|
||||||
|
|
||||||
virtual unsigned int getPixel(int x, int y) override
|
|
||||||
{
|
|
||||||
Color result = _getFinalPixel(area, x, y);
|
|
||||||
result.normalize();
|
|
||||||
return result.to32BitBGRA();
|
|
||||||
}
|
|
||||||
private:
|
|
||||||
RenderArea *area;
|
|
||||||
};
|
|
||||||
|
|
||||||
int RenderArea::saveToFile(const std::string &path)
|
|
||||||
{
|
|
||||||
RenderWriter writer(this);
|
|
||||||
return writer.save(path, params.width, params.height);
|
|
||||||
}
|
|
||||||
|
|
||||||
void RenderArea::setPreviewCallbacks(RenderCallbackStart start, RenderCallbackDraw draw, RenderCallbackUpdate update)
|
|
||||||
{
|
|
||||||
callback_start = start ? start : _callbackStart;
|
|
||||||
callback_draw = draw ? draw : _callbackDraw;
|
|
||||||
callback_update = update ? update : _callbackUpdate;
|
|
||||||
|
|
||||||
callback_start(params.width, params.height, background_color);
|
|
||||||
|
|
||||||
setAllDirty();
|
|
||||||
processDirtyPixels();
|
|
||||||
|
|
||||||
callback_update(0.0);
|
|
||||||
}
|
|
|
@ -1,76 +0,0 @@
|
||||||
#ifndef RENDERAREA_H
|
|
||||||
#define RENDERAREA_H
|
|
||||||
|
|
||||||
#include "software_global.h"
|
|
||||||
|
|
||||||
#include "Color.h"
|
|
||||||
|
|
||||||
typedef struct RenderFragment RenderFragment;
|
|
||||||
typedef struct FragmentCallback FragmentCallback;
|
|
||||||
|
|
||||||
namespace paysages {
|
|
||||||
namespace software {
|
|
||||||
|
|
||||||
class SOFTWARESHARED_EXPORT RenderArea
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
typedef struct
|
|
||||||
{
|
|
||||||
int width;
|
|
||||||
int height;
|
|
||||||
int antialias;
|
|
||||||
int quality;
|
|
||||||
} RenderParams;
|
|
||||||
|
|
||||||
typedef Color (*f_RenderFragmentCallback)(SoftwareRenderer* renderer, const Vector3 &location, void* data);
|
|
||||||
typedef void (*RenderCallbackStart)(int width, int height, const Color &background);
|
|
||||||
typedef void (*RenderCallbackDraw)(int x, int y, const Color &col);
|
|
||||||
typedef void (*RenderCallbackUpdate)(double progress);
|
|
||||||
|
|
||||||
public:
|
|
||||||
RenderArea(SoftwareRenderer* parent);
|
|
||||||
~RenderArea();
|
|
||||||
|
|
||||||
void setParams(RenderParams params);
|
|
||||||
void setToneMapping(const ColorProfile &profile);
|
|
||||||
void setBackgroundColor(const Color& col);
|
|
||||||
void clear();
|
|
||||||
void update();
|
|
||||||
|
|
||||||
void pushTriangle(const Vector3 &pixel1, const Vector3 &pixel2, const Vector3 &pixel3, const Vector3 &location1, const Vector3 &location2, const Vector3 &location3, f_RenderFragmentCallback callback, void* callback_data);
|
|
||||||
|
|
||||||
Color getPixel(int x, int y);
|
|
||||||
|
|
||||||
void postProcess(int nbchunks);
|
|
||||||
int saveToFile(const std::string &path);
|
|
||||||
void setPreviewCallbacks(RenderCallbackStart start, RenderCallbackDraw draw, RenderCallbackUpdate update);
|
|
||||||
|
|
||||||
void setAllDirty();
|
|
||||||
void processDirtyPixels();
|
|
||||||
void pushFragment(int x, int y, double z, int edge, const Vector3 &location, int callback);
|
|
||||||
|
|
||||||
public:
|
|
||||||
ColorProfile* hdr_mapping;
|
|
||||||
SoftwareRenderer* renderer;
|
|
||||||
RenderParams params;
|
|
||||||
int pixel_count;
|
|
||||||
int pixel_done;
|
|
||||||
RenderFragment* pixels;
|
|
||||||
int fragment_callbacks_count;
|
|
||||||
FragmentCallback* fragment_callbacks;
|
|
||||||
Color background_color;
|
|
||||||
volatile int dirty_left;
|
|
||||||
volatile int dirty_right;
|
|
||||||
volatile int dirty_up;
|
|
||||||
volatile int dirty_down;
|
|
||||||
volatile int dirty_count;
|
|
||||||
Mutex* lock;
|
|
||||||
RenderCallbackStart callback_start;
|
|
||||||
RenderCallbackDraw callback_draw;
|
|
||||||
RenderCallbackUpdate callback_update;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // RENDERAREA_H
|
|
30
src/render/software/RenderConfig.cpp
Normal file
30
src/render/software/RenderConfig.cpp
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
#include "RenderConfig.h"
|
||||||
|
|
||||||
|
RenderConfig::RenderConfig(int width, int height, int antialias, int quality):
|
||||||
|
width(width), height(height), antialias(antialias), quality(quality)
|
||||||
|
{
|
||||||
|
if (this->width <= 0)
|
||||||
|
{
|
||||||
|
this->width = 400;
|
||||||
|
}
|
||||||
|
if (this->height <= 0)
|
||||||
|
{
|
||||||
|
this->height = this->width * 4 / 3;
|
||||||
|
}
|
||||||
|
if (this->antialias < 1)
|
||||||
|
{
|
||||||
|
this->antialias = 1;
|
||||||
|
}
|
||||||
|
if (this->antialias > 4)
|
||||||
|
{
|
||||||
|
this->antialias = 4;
|
||||||
|
}
|
||||||
|
if (this->quality < 1)
|
||||||
|
{
|
||||||
|
this->quality = 1;
|
||||||
|
}
|
||||||
|
if (this->quality > 10)
|
||||||
|
{
|
||||||
|
this->quality = 10;
|
||||||
|
}
|
||||||
|
}
|
23
src/render/software/RenderConfig.h
Normal file
23
src/render/software/RenderConfig.h
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
#ifndef RENDERCONFIG_H
|
||||||
|
#define RENDERCONFIG_H
|
||||||
|
|
||||||
|
#include "software_global.h"
|
||||||
|
|
||||||
|
namespace paysages {
|
||||||
|
namespace software {
|
||||||
|
|
||||||
|
class SOFTWARESHARED_EXPORT RenderConfig
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
RenderConfig(int width=0, int height=0, int antialias=1, int quality=5);
|
||||||
|
|
||||||
|
int width;
|
||||||
|
int height;
|
||||||
|
int antialias;
|
||||||
|
int quality;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // RENDERCONFIG_H
|
|
@ -6,30 +6,17 @@
|
||||||
#include "AtmosphereRenderer.h"
|
#include "AtmosphereRenderer.h"
|
||||||
#include "AtmosphereResult.h"
|
#include "AtmosphereResult.h"
|
||||||
#include "CloudsRenderer.h"
|
#include "CloudsRenderer.h"
|
||||||
|
#include "Rasterizer.h"
|
||||||
|
#include "CanvasFragment.h"
|
||||||
|
|
||||||
#define SPHERE_SIZE 20000.0
|
#define SPHERE_SIZE 20000.0
|
||||||
|
|
||||||
SkyRasterizer::SkyRasterizer(SoftwareRenderer* renderer):
|
SkyRasterizer::SkyRasterizer(SoftwareRenderer* renderer, int client_id):
|
||||||
renderer(renderer)
|
Rasterizer(renderer, client_id, Color(0.9, 0.9, 1.0))
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
static Color _postProcessFragment(SoftwareRenderer* renderer, const Vector3 &location, void*)
|
void SkyRasterizer::rasterizeToCanvas(CanvasPortion* canvas)
|
||||||
{
|
|
||||||
Vector3 camera_location, direction;
|
|
||||||
Color result;
|
|
||||||
|
|
||||||
camera_location = renderer->getCameraLocation(location);
|
|
||||||
direction = location.sub(camera_location);
|
|
||||||
|
|
||||||
/* TODO Don't compute result->color if it's fully covered by clouds */
|
|
||||||
result = renderer->getAtmosphereRenderer()->getSkyColor(direction.normalize()).final;
|
|
||||||
result = renderer->getCloudsRenderer()->getColor(camera_location, camera_location.add(direction.scale(10.0)), result);
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SkyRasterizer::rasterize()
|
|
||||||
{
|
{
|
||||||
int res_i, res_j;
|
int res_i, res_j;
|
||||||
int i, j;
|
int i, j;
|
||||||
|
@ -47,7 +34,7 @@ void SkyRasterizer::rasterize()
|
||||||
|
|
||||||
for (j = 0; j < res_j; j++)
|
for (j = 0; j < res_j; j++)
|
||||||
{
|
{
|
||||||
if (!renderer->addRenderProgress(0.0))
|
if (interrupted)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -79,7 +66,23 @@ void SkyRasterizer::rasterize()
|
||||||
vertex4 = camera_location.add(direction);
|
vertex4 = camera_location.add(direction);
|
||||||
|
|
||||||
/* TODO Triangles at poles */
|
/* TODO Triangles at poles */
|
||||||
renderer->pushQuad(vertex1, vertex4, vertex3, vertex2, _postProcessFragment, NULL);
|
pushQuad(canvas, vertex1, vertex4, vertex3, vertex2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Color SkyRasterizer::shadeFragment(const CanvasFragment &fragment) const
|
||||||
|
{
|
||||||
|
Vector3 location = fragment.getLocation();
|
||||||
|
Vector3 camera_location, direction;
|
||||||
|
Color result;
|
||||||
|
|
||||||
|
camera_location = renderer->getCameraLocation(location);
|
||||||
|
direction = location.sub(camera_location);
|
||||||
|
|
||||||
|
/* TODO Don't compute result->color if it's fully covered by clouds */
|
||||||
|
result = renderer->getAtmosphereRenderer()->getSkyColor(direction.normalize()).final;
|
||||||
|
result = renderer->getCloudsRenderer()->getColor(camera_location, camera_location.add(direction.scale(10.0)), result);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
|
@ -3,17 +3,18 @@
|
||||||
|
|
||||||
#include "software_global.h"
|
#include "software_global.h"
|
||||||
|
|
||||||
|
#include "Rasterizer.h"
|
||||||
|
|
||||||
namespace paysages {
|
namespace paysages {
|
||||||
namespace software {
|
namespace software {
|
||||||
|
|
||||||
class SOFTWARESHARED_EXPORT SkyRasterizer
|
class SOFTWARESHARED_EXPORT SkyRasterizer: public Rasterizer
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
SkyRasterizer(SoftwareRenderer* renderer);
|
SkyRasterizer(SoftwareRenderer* renderer, int client_id);
|
||||||
void rasterize();
|
|
||||||
|
|
||||||
private:
|
virtual void rasterizeToCanvas(CanvasPortion* canvas) override;
|
||||||
SoftwareRenderer* renderer;
|
virtual Color shadeFragment(const CanvasFragment &fragment) const override;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
164
src/render/software/SoftwareCanvasRenderer.cpp
Normal file
164
src/render/software/SoftwareCanvasRenderer.cpp
Normal file
|
@ -0,0 +1,164 @@
|
||||||
|
#include "SoftwareCanvasRenderer.h"
|
||||||
|
|
||||||
|
#include "Rasterizer.h"
|
||||||
|
#include "SoftwareRenderer.h"
|
||||||
|
#include "Canvas.h"
|
||||||
|
#include "TerrainRasterizer.h"
|
||||||
|
#include "WaterRasterizer.h"
|
||||||
|
#include "SkyRasterizer.h"
|
||||||
|
#include "CameraDefinition.h"
|
||||||
|
#include "ParallelWork.h"
|
||||||
|
#include "CanvasPortion.h"
|
||||||
|
#include "CanvasPixelShader.h"
|
||||||
|
#include "RenderConfig.h"
|
||||||
|
#include "ColorProfile.h"
|
||||||
|
#include "CanvasPreview.h"
|
||||||
|
|
||||||
|
SoftwareCanvasRenderer::SoftwareCanvasRenderer()
|
||||||
|
{
|
||||||
|
started = false;
|
||||||
|
interrupted = false;
|
||||||
|
canvas = new Canvas();
|
||||||
|
progress = 0.0;
|
||||||
|
samples = 1;
|
||||||
|
|
||||||
|
rasterizers.push_back(new SkyRasterizer(this, 0));
|
||||||
|
rasterizers.push_back(new WaterRasterizer(this, 1));
|
||||||
|
rasterizers.push_back(new TerrainRasterizer(this, 2));
|
||||||
|
|
||||||
|
current_work = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
SoftwareCanvasRenderer::~SoftwareCanvasRenderer()
|
||||||
|
{
|
||||||
|
delete canvas;
|
||||||
|
|
||||||
|
for (auto &rasterizer: rasterizers)
|
||||||
|
{
|
||||||
|
delete rasterizer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SoftwareCanvasRenderer::setConfig(const RenderConfig &config)
|
||||||
|
{
|
||||||
|
if (not started)
|
||||||
|
{
|
||||||
|
setSize(config.width, config.height, config.antialias);
|
||||||
|
render_quality = config.quality;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SoftwareCanvasRenderer::setSize(int width, int height, int samples)
|
||||||
|
{
|
||||||
|
if (not started)
|
||||||
|
{
|
||||||
|
canvas->setSize(width * samples, height * samples);
|
||||||
|
this->samples = samples;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SoftwareCanvasRenderer::render()
|
||||||
|
{
|
||||||
|
started = true;
|
||||||
|
progress = 0.0;
|
||||||
|
|
||||||
|
render_camera->setRenderSize(canvas->getWidth(), canvas->getHeight());
|
||||||
|
|
||||||
|
prepare();
|
||||||
|
|
||||||
|
// Iterate portions
|
||||||
|
int nx = canvas->getHorizontalPortionCount();
|
||||||
|
int ny = canvas->getVerticalPortionCount();
|
||||||
|
int i = 0;
|
||||||
|
int n = nx * ny;
|
||||||
|
for (int y = 0; y < ny; y++)
|
||||||
|
{
|
||||||
|
for (int x = 0; x < nx; x++)
|
||||||
|
{
|
||||||
|
CanvasPortion *portion = canvas->at(x, y);
|
||||||
|
|
||||||
|
if (not interrupted)
|
||||||
|
{
|
||||||
|
progress_segment = 0.2 / (double)n;
|
||||||
|
portion->preparePixels();
|
||||||
|
rasterize(portion);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (not interrupted)
|
||||||
|
{
|
||||||
|
progress_segment = 0.8 / (double)n;
|
||||||
|
applyPixelShader(portion);
|
||||||
|
}
|
||||||
|
|
||||||
|
portion->discardPixels();
|
||||||
|
i++;
|
||||||
|
progress = (double)i / (double)n;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SoftwareCanvasRenderer::interrupt()
|
||||||
|
{
|
||||||
|
interrupted = true;
|
||||||
|
|
||||||
|
if (current_work)
|
||||||
|
{
|
||||||
|
current_work->interrupt();
|
||||||
|
}
|
||||||
|
for (auto &rasterizer:rasterizers)
|
||||||
|
{
|
||||||
|
rasterizer->interrupt();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const Rasterizer &SoftwareCanvasRenderer::getRasterizer(int client_id) const
|
||||||
|
{
|
||||||
|
return *(rasterizers[client_id]);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SoftwareCanvasRenderer::saveToDisk(const std::string &filepath) const
|
||||||
|
{
|
||||||
|
return getCanvas()->saveToDisk(filepath, *getCanvas()->getPreview()->getToneMapping(), samples);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SoftwareCanvasRenderer::rasterize(CanvasPortion *portion)
|
||||||
|
{
|
||||||
|
for (auto &rasterizer:rasterizers)
|
||||||
|
{
|
||||||
|
rasterizer->rasterizeToCanvas(portion);
|
||||||
|
progress += progress_segment / (double)rasterizers.size();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SoftwareCanvasRenderer::applyPixelShader(CanvasPortion *portion)
|
||||||
|
{
|
||||||
|
// Subdivide in chunks
|
||||||
|
int chunk_size = 64;
|
||||||
|
int chunks_x = (portion->getWidth() - 1) / chunk_size + 1;
|
||||||
|
int chunks_y = (portion->getHeight() - 1) / chunk_size + 1;
|
||||||
|
int units = chunks_x * chunks_y;
|
||||||
|
|
||||||
|
// Estimate chunks
|
||||||
|
int n = 0;
|
||||||
|
for (int sub_chunk_size = chunk_size; sub_chunk_size >= 1; sub_chunk_size /= 2)
|
||||||
|
{
|
||||||
|
n += chunk_size / sub_chunk_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Render chunks in parallel
|
||||||
|
for (int sub_chunk_size = chunk_size; sub_chunk_size >= 1; sub_chunk_size /= 2)
|
||||||
|
{
|
||||||
|
if (interrupted)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
CanvasPixelShader shader(*this, portion, chunk_size, sub_chunk_size, chunks_x, chunks_y);
|
||||||
|
ParallelWork work(&shader, units);
|
||||||
|
|
||||||
|
current_work = &work;
|
||||||
|
work.perform();
|
||||||
|
current_work = NULL;
|
||||||
|
progress += progress_segment * (double)(chunk_size / sub_chunk_size) / (double)n;
|
||||||
|
}
|
||||||
|
}
|
89
src/render/software/SoftwareCanvasRenderer.h
Normal file
89
src/render/software/SoftwareCanvasRenderer.h
Normal file
|
@ -0,0 +1,89 @@
|
||||||
|
#ifndef SOFTWARECANVASRENDERER_H
|
||||||
|
#define SOFTWARECANVASRENDERER_H
|
||||||
|
|
||||||
|
#include "software_global.h"
|
||||||
|
|
||||||
|
#include "SoftwareRenderer.h"
|
||||||
|
|
||||||
|
namespace paysages {
|
||||||
|
namespace software {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Software rendering inside a Canvas surface.
|
||||||
|
*
|
||||||
|
* This class launches the rasterization process into canvas portions and
|
||||||
|
* redirects post processing to the software renderer.
|
||||||
|
*
|
||||||
|
* It tries to keep a canvas portion rasterized ahead of the post processing.
|
||||||
|
*/
|
||||||
|
class SOFTWARESHARED_EXPORT SoftwareCanvasRenderer: public SoftwareRenderer
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
SoftwareCanvasRenderer();
|
||||||
|
virtual ~SoftwareCanvasRenderer();
|
||||||
|
|
||||||
|
inline const Canvas *getCanvas() const {return canvas;}
|
||||||
|
inline double getProgress() const {return progress;}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the renderer configuration.
|
||||||
|
*/
|
||||||
|
void setConfig(const RenderConfig &config);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Set the rendering size in pixels.
|
||||||
|
*
|
||||||
|
* Set 'samples' to something bigger than 1 to allow for the multi-sampling of pixels.
|
||||||
|
*/
|
||||||
|
void setSize(int width, int height, int samples=1);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Start the two-pass render process.
|
||||||
|
*/
|
||||||
|
void render();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Interrupt the render process.
|
||||||
|
*/
|
||||||
|
void interrupt();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a rasterizer by its client id.
|
||||||
|
*/
|
||||||
|
const Rasterizer &getRasterizer(int client_id) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Save the rendered canvas to a picture file on disk.
|
||||||
|
*
|
||||||
|
* Returns true if the save was successful.
|
||||||
|
*/
|
||||||
|
bool saveToDisk(const std::string &filepath) const;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
/**
|
||||||
|
* @brief Rasterize the scenery into a canvas portion.
|
||||||
|
*/
|
||||||
|
void rasterize(CanvasPortion *portion);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Apply pixel shader to fragments stored in the CanvasPortion.
|
||||||
|
*/
|
||||||
|
void applyPixelShader(CanvasPortion *portion);
|
||||||
|
|
||||||
|
private:
|
||||||
|
double progress;
|
||||||
|
double progress_segment;
|
||||||
|
|
||||||
|
Canvas *canvas;
|
||||||
|
int samples;
|
||||||
|
std::vector<Rasterizer*> rasterizers;
|
||||||
|
bool started;
|
||||||
|
bool interrupted;
|
||||||
|
|
||||||
|
ParallelWork *current_work;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // SOFTWARECANVASRENDERER_H
|
|
@ -22,17 +22,8 @@
|
||||||
|
|
||||||
SoftwareRenderer::SoftwareRenderer(Scenery* scenery)
|
SoftwareRenderer::SoftwareRenderer(Scenery* scenery)
|
||||||
{
|
{
|
||||||
RenderArea::RenderParams params = {1, 1, 1, 5};
|
|
||||||
|
|
||||||
render_quality = 5;
|
render_quality = 5;
|
||||||
render_width = 1;
|
|
||||||
render_height = 1;
|
|
||||||
render_interrupt = 0;
|
|
||||||
render_progress = 0.0;
|
|
||||||
is_rendering = 0;
|
|
||||||
render_camera = new CameraDefinition;
|
render_camera = new CameraDefinition;
|
||||||
render_area = new RenderArea(this);
|
|
||||||
render_area->setParams(params);
|
|
||||||
|
|
||||||
atmosphere_renderer = new BaseAtmosphereRenderer(this);
|
atmosphere_renderer = new BaseAtmosphereRenderer(this);
|
||||||
clouds_renderer = new CloudsRenderer(this);
|
clouds_renderer = new CloudsRenderer(this);
|
||||||
|
@ -59,7 +50,6 @@ SoftwareRenderer::SoftwareRenderer(Scenery* scenery)
|
||||||
SoftwareRenderer::~SoftwareRenderer()
|
SoftwareRenderer::~SoftwareRenderer()
|
||||||
{
|
{
|
||||||
delete render_camera;
|
delete render_camera;
|
||||||
delete render_area;
|
|
||||||
|
|
||||||
delete fluid_medium;
|
delete fluid_medium;
|
||||||
delete lighting;
|
delete lighting;
|
||||||
|
@ -108,18 +98,6 @@ void SoftwareRenderer::prepare()
|
||||||
//fluid_medium->registerMedium(water_renderer);
|
//fluid_medium->registerMedium(water_renderer);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SoftwareRenderer::rasterize()
|
|
||||||
{
|
|
||||||
TerrainRasterizer terrain(this);
|
|
||||||
terrain.renderSurface();
|
|
||||||
|
|
||||||
WaterRasterizer water(this);
|
|
||||||
water.renderSurface();
|
|
||||||
|
|
||||||
SkyRasterizer sky(this);
|
|
||||||
sky.rasterize();
|
|
||||||
}
|
|
||||||
|
|
||||||
void SoftwareRenderer::disableClouds()
|
void SoftwareRenderer::disableClouds()
|
||||||
{
|
{
|
||||||
scenery->getClouds()->clear();
|
scenery->getClouds()->clear();
|
||||||
|
@ -164,68 +142,6 @@ void SoftwareRenderer::disableAtmosphere(const std::vector<LightComponent> &ligh
|
||||||
atmosphere_renderer->setStaticLights(lights);
|
atmosphere_renderer->setStaticLights(lights);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SoftwareRenderer::setPreviewCallbacks(RenderArea::RenderCallbackStart start, RenderArea::RenderCallbackDraw draw, RenderArea::RenderCallbackUpdate update)
|
|
||||||
{
|
|
||||||
render_area->setPreviewCallbacks(start, draw, update);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void* _renderFirstPass(void* data)
|
|
||||||
{
|
|
||||||
SoftwareRenderer* renderer = (SoftwareRenderer*)data;
|
|
||||||
renderer->rasterize();
|
|
||||||
renderer->is_rendering = 0;
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SoftwareRenderer::start(RenderArea::RenderParams params)
|
|
||||||
{
|
|
||||||
Thread thread(_renderFirstPass);
|
|
||||||
int loops;
|
|
||||||
int core_count = System::getCoreCount();
|
|
||||||
|
|
||||||
params.antialias = (params.antialias < 1) ? 1 : params.antialias;
|
|
||||||
params.antialias = (params.antialias > 4) ? 4 : params.antialias;
|
|
||||||
|
|
||||||
render_quality = params.quality;
|
|
||||||
render_width = params.width * params.antialias;
|
|
||||||
render_height = params.height * params.antialias;
|
|
||||||
render_interrupt = 0;
|
|
||||||
render_progress = 0.0;
|
|
||||||
|
|
||||||
prepare();
|
|
||||||
|
|
||||||
render_camera->setRenderSize(render_width, render_height);
|
|
||||||
|
|
||||||
render_area->setBackgroundColor(COLOR_BLACK);
|
|
||||||
render_area->setParams(params);
|
|
||||||
render_area->clear();
|
|
||||||
|
|
||||||
is_rendering = 1;
|
|
||||||
thread.start(this);
|
|
||||||
loops = 0;
|
|
||||||
|
|
||||||
while (is_rendering)
|
|
||||||
{
|
|
||||||
Thread::timeSleepMs(100);
|
|
||||||
|
|
||||||
if (++loops >= 10)
|
|
||||||
{
|
|
||||||
render_area->update();
|
|
||||||
loops = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
thread.join();
|
|
||||||
|
|
||||||
is_rendering = 1;
|
|
||||||
render_area->postProcess(core_count);
|
|
||||||
is_rendering = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SoftwareRenderer::interrupt()
|
|
||||||
{
|
|
||||||
render_interrupt = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
Color SoftwareRenderer::applyLightingToSurface(const Vector3 &location, const Vector3 &normal, const SurfaceMaterial &material)
|
Color SoftwareRenderer::applyLightingToSurface(const Vector3 &location, const Vector3 &normal, const SurfaceMaterial &material)
|
||||||
{
|
{
|
||||||
LightStatus status(lighting, location, getCameraLocation(location));
|
LightStatus status(lighting, location, getCameraLocation(location));
|
||||||
|
@ -261,11 +177,6 @@ RayCastingResult SoftwareRenderer::rayWalking(const Vector3 &location, const Vec
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
int SoftwareRenderer::addRenderProgress(double)
|
|
||||||
{
|
|
||||||
return not render_interrupt;
|
|
||||||
}
|
|
||||||
|
|
||||||
Vector3 SoftwareRenderer::getCameraLocation(const Vector3 &)
|
Vector3 SoftwareRenderer::getCameraLocation(const Vector3 &)
|
||||||
{
|
{
|
||||||
return render_camera->getLocation();
|
return render_camera->getLocation();
|
||||||
|
@ -296,37 +207,3 @@ Vector3 SoftwareRenderer::unprojectPoint(const Vector3 &point)
|
||||||
{
|
{
|
||||||
return render_camera->unproject(point);
|
return render_camera->unproject(point);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SoftwareRenderer::pushTriangle(const Vector3 &v1, const Vector3 &v2, const Vector3 &v3, RenderArea::f_RenderFragmentCallback callback, void* callback_data)
|
|
||||||
{
|
|
||||||
Vector3 p1, p2, p3;
|
|
||||||
|
|
||||||
p1 = projectPoint(v1);
|
|
||||||
p2 = projectPoint(v2);
|
|
||||||
p3 = projectPoint(v3);
|
|
||||||
|
|
||||||
render_area->pushTriangle(p1, p2, p3, v1, v2, v3, callback, callback_data);
|
|
||||||
}
|
|
||||||
|
|
||||||
void SoftwareRenderer::pushQuad(const Vector3 &v1, const Vector3 &v2, const Vector3 &v3, const Vector3 &v4, RenderArea::f_RenderFragmentCallback callback, void* callback_data)
|
|
||||||
{
|
|
||||||
pushTriangle(v2, v3, v1, callback, callback_data);
|
|
||||||
pushTriangle(v4, v1, v3, callback, callback_data);
|
|
||||||
}
|
|
||||||
|
|
||||||
void SoftwareRenderer::pushDisplacedTriangle(const Vector3 &v1, const Vector3 &v2, const Vector3 &v3, const Vector3 &ov1, const Vector3 &ov2, const Vector3 &ov3, RenderArea::f_RenderFragmentCallback callback, void* callback_data)
|
|
||||||
{
|
|
||||||
Vector3 p1, p2, p3;
|
|
||||||
|
|
||||||
p1 = projectPoint(v1);
|
|
||||||
p2 = projectPoint(v2);
|
|
||||||
p3 = projectPoint(v3);
|
|
||||||
|
|
||||||
render_area->pushTriangle(p1, p2, p3, ov1, ov2, ov3, callback, callback_data);
|
|
||||||
}
|
|
||||||
|
|
||||||
void SoftwareRenderer::pushDisplacedQuad(const Vector3 &v1, const Vector3 &v2, const Vector3 &v3, const Vector3 &v4, const Vector3 &ov1, const Vector3 &ov2, const Vector3 &ov3, const Vector3 &ov4, RenderArea::f_RenderFragmentCallback callback, void* callback_data)
|
|
||||||
{
|
|
||||||
pushDisplacedTriangle(v2, v3, v1, ov2, ov3, ov1, callback, callback_data);
|
|
||||||
pushDisplacedTriangle(v4, v1, v3, ov4, ov1, ov3, callback, callback_data);
|
|
||||||
}
|
|
||||||
|
|
|
@ -3,7 +3,6 @@
|
||||||
|
|
||||||
#include "software_global.h"
|
#include "software_global.h"
|
||||||
|
|
||||||
#include "RenderArea.h"
|
|
||||||
#include "RayCastingManager.h"
|
#include "RayCastingManager.h"
|
||||||
|
|
||||||
namespace paysages {
|
namespace paysages {
|
||||||
|
@ -21,16 +20,8 @@ public:
|
||||||
|
|
||||||
/* Render base configuration */
|
/* Render base configuration */
|
||||||
int render_quality;
|
int render_quality;
|
||||||
int render_width;
|
|
||||||
int render_height;
|
|
||||||
CameraDefinition* render_camera;
|
CameraDefinition* render_camera;
|
||||||
|
|
||||||
/* Render related */
|
|
||||||
RenderArea* render_area;
|
|
||||||
double render_progress;
|
|
||||||
int render_interrupt;
|
|
||||||
int is_rendering;
|
|
||||||
|
|
||||||
void* customData[10];
|
void* customData[10];
|
||||||
|
|
||||||
virtual Vector3 getCameraLocation(const Vector3 &target);
|
virtual Vector3 getCameraLocation(const Vector3 &target);
|
||||||
|
@ -38,11 +29,6 @@ public:
|
||||||
virtual double getPrecision(const Vector3 &location);
|
virtual double getPrecision(const Vector3 &location);
|
||||||
virtual Vector3 projectPoint(const Vector3 &point);
|
virtual Vector3 projectPoint(const Vector3 &point);
|
||||||
virtual Vector3 unprojectPoint(const Vector3 &point);
|
virtual Vector3 unprojectPoint(const Vector3 &point);
|
||||||
virtual int addRenderProgress(double progress);
|
|
||||||
virtual void pushTriangle(const Vector3 &v1, const Vector3 &v2, const Vector3 &v3, RenderArea::f_RenderFragmentCallback callback, void* callback_data);
|
|
||||||
virtual void pushQuad(const Vector3 &v1, const Vector3 &v2, const Vector3 &v3, const Vector3 &v4, RenderArea::f_RenderFragmentCallback callback, void* callback_data);
|
|
||||||
virtual void pushDisplacedTriangle(const Vector3 &v1, const Vector3 &v2, const Vector3 &v3, const Vector3 &ov1, const Vector3 &ov2, const Vector3 &ov3, RenderArea::f_RenderFragmentCallback callback, void* callback_data);
|
|
||||||
virtual void pushDisplacedQuad(const Vector3 &v1, const Vector3 &v2, const Vector3 &v3, const Vector3 &v4, const Vector3 &ov1, const Vector3 &ov2, const Vector3 &ov3, const Vector3 &ov4, RenderArea::f_RenderFragmentCallback callback, void* callback_data);
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* \brief Set the scenery to render.
|
* \brief Set the scenery to render.
|
||||||
|
@ -59,11 +45,6 @@ public:
|
||||||
*/
|
*/
|
||||||
virtual void prepare();
|
virtual void prepare();
|
||||||
|
|
||||||
/*!
|
|
||||||
* \brief Start the rasterization process.
|
|
||||||
*/
|
|
||||||
virtual void rasterize();
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* \brief Disable the clouds feature.
|
* \brief Disable the clouds feature.
|
||||||
*
|
*
|
||||||
|
@ -78,10 +59,6 @@ public:
|
||||||
void disableAtmosphere();
|
void disableAtmosphere();
|
||||||
void disableAtmosphere(const std::vector<LightComponent> &lights);
|
void disableAtmosphere(const std::vector<LightComponent> &lights);
|
||||||
|
|
||||||
void setPreviewCallbacks(RenderArea::RenderCallbackStart start, RenderArea::RenderCallbackDraw draw, RenderArea::RenderCallbackUpdate update);
|
|
||||||
void start(RenderArea::RenderParams params);
|
|
||||||
void interrupt();
|
|
||||||
|
|
||||||
inline Scenery* getScenery() const {return scenery;}
|
inline Scenery* getScenery() const {return scenery;}
|
||||||
|
|
||||||
inline BaseAtmosphereRenderer* getAtmosphereRenderer() const {return atmosphere_renderer;}
|
inline BaseAtmosphereRenderer* getAtmosphereRenderer() const {return atmosphere_renderer;}
|
||||||
|
|
|
@ -7,10 +7,11 @@
|
||||||
#include "WaterRenderer.h"
|
#include "WaterRenderer.h"
|
||||||
#include "TexturesRenderer.h"
|
#include "TexturesRenderer.h"
|
||||||
#include "Scenery.h"
|
#include "Scenery.h"
|
||||||
#include "ParallelQueue.h"
|
#include "CanvasPortion.h"
|
||||||
|
#include "CanvasFragment.h"
|
||||||
|
|
||||||
TerrainRasterizer::TerrainRasterizer(SoftwareRenderer* renderer):
|
TerrainRasterizer::TerrainRasterizer(SoftwareRenderer* renderer, int client_id):
|
||||||
renderer(renderer)
|
Rasterizer(renderer, client_id, Color(1.0, 0.9, 0.9))
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -19,13 +20,30 @@ static inline Vector3 _getPoint(SoftwareRenderer* renderer, double x, double z)
|
||||||
return Vector3(x, renderer->getTerrainRenderer()->getHeight(x, z, 1), z);
|
return Vector3(x, renderer->getTerrainRenderer()->getHeight(x, z, 1), z);
|
||||||
}
|
}
|
||||||
|
|
||||||
static Color _postProcessFragment(SoftwareRenderer* renderer, const Vector3 &point, void*)
|
void TerrainRasterizer::tessellateChunk(CanvasPortion* canvas, TerrainChunkInfo* chunk, int detail)
|
||||||
{
|
{
|
||||||
double precision = renderer->getPrecision(_getPoint(renderer, point.x, point.z));
|
if (detail < 1)
|
||||||
return renderer->getTerrainRenderer()->getFinalColor(point, precision);
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
double water_height = renderer->getWaterRenderer()->getHeightInfo().min_height;
|
||||||
|
|
||||||
|
double startx = chunk->point_nw.x;
|
||||||
|
double startz = chunk->point_nw.z;
|
||||||
|
double size = (chunk->point_ne.x - chunk->point_nw.x) / (double)detail;
|
||||||
|
int i, j;
|
||||||
|
|
||||||
|
for (i = 0; i < detail; i++)
|
||||||
|
{
|
||||||
|
for (j = 0; j < detail; j++)
|
||||||
|
{
|
||||||
|
renderQuad(canvas, startx + (double)i * size, startz + (double)j * size, size, water_height);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void _renderQuad(SoftwareRenderer* renderer, double x, double z, double size, double water_height)
|
void TerrainRasterizer::renderQuad(CanvasPortion *canvas, double x, double z, double size, double water_height)
|
||||||
{
|
{
|
||||||
Vector3 ov1, ov2, ov3, ov4;
|
Vector3 ov1, ov2, ov3, ov4;
|
||||||
Vector3 dv1, dv2, dv3, dv4;
|
Vector3 dv1, dv2, dv3, dv4;
|
||||||
|
@ -50,30 +68,7 @@ static void _renderQuad(SoftwareRenderer* renderer, double x, double z, double s
|
||||||
|
|
||||||
if (dv1.y > water_height || dv2.y > water_height || dv3.y > water_height || dv4.y > water_height)
|
if (dv1.y > water_height || dv2.y > water_height || dv3.y > water_height || dv4.y > water_height)
|
||||||
{
|
{
|
||||||
renderer->pushDisplacedQuad(dv1, dv2, dv3, dv4, ov1, ov2, ov3, ov4, _postProcessFragment, NULL);
|
pushDisplacedQuad(canvas, dv1, dv2, dv3, dv4, ov1, ov2, ov3, ov4);
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void TerrainRasterizer::tessellateChunk(TerrainChunkInfo* chunk, int detail)
|
|
||||||
{
|
|
||||||
if (detail < 1)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
double water_height = renderer->getWaterRenderer()->getHeightInfo().min_height;
|
|
||||||
|
|
||||||
double startx = chunk->point_nw.x;
|
|
||||||
double startz = chunk->point_nw.z;
|
|
||||||
double size = (chunk->point_ne.x - chunk->point_nw.x) / (double)detail;
|
|
||||||
int i, j;
|
|
||||||
|
|
||||||
for (i = 0; i < detail; i++)
|
|
||||||
{
|
|
||||||
for (j = 0; j < detail; j++)
|
|
||||||
{
|
|
||||||
_renderQuad(renderer, startx + (double)i * size, startz + (double)j * size, size, water_height);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -130,7 +125,7 @@ static void _getChunk(SoftwareRenderer* renderer, TerrainRasterizer::TerrainChun
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void TerrainRasterizer::getTessellationInfo(int displaced)
|
void TerrainRasterizer::getTessellationInfo(CanvasPortion* canvas, int displaced)
|
||||||
{
|
{
|
||||||
TerrainChunkInfo chunk;
|
TerrainChunkInfo chunk;
|
||||||
int chunk_factor, chunk_count, i;
|
int chunk_factor, chunk_count, i;
|
||||||
|
@ -157,25 +152,29 @@ void TerrainRasterizer::getTessellationInfo(int displaced)
|
||||||
for (i = 0; i < chunk_count - 1; i++)
|
for (i = 0; i < chunk_count - 1; i++)
|
||||||
{
|
{
|
||||||
_getChunk(renderer, &chunk, cx - radius_ext + chunk_size * i, cz - radius_ext, chunk_size, displaced);
|
_getChunk(renderer, &chunk, cx - radius_ext + chunk_size * i, cz - radius_ext, chunk_size, displaced);
|
||||||
if (!processChunk(&chunk, progress))
|
processChunk(canvas, &chunk, progress);
|
||||||
|
if (interrupted)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
_getChunk(renderer, &chunk, cx + radius_int, cz - radius_ext + chunk_size * i, chunk_size, displaced);
|
_getChunk(renderer, &chunk, cx + radius_int, cz - radius_ext + chunk_size * i, chunk_size, displaced);
|
||||||
if (!processChunk(&chunk, progress))
|
processChunk(canvas, &chunk, progress);
|
||||||
|
if (interrupted)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
_getChunk(renderer, &chunk, cx + radius_int - chunk_size * i, cz + radius_int, chunk_size, displaced);
|
_getChunk(renderer, &chunk, cx + radius_int - chunk_size * i, cz + radius_int, chunk_size, displaced);
|
||||||
if (!processChunk(&chunk, progress))
|
processChunk(canvas, &chunk, progress);
|
||||||
|
if (interrupted)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
_getChunk(renderer, &chunk, cx - radius_ext, cz + radius_int - chunk_size * i, chunk_size, displaced);
|
_getChunk(renderer, &chunk, cx - radius_ext, cz + radius_int - chunk_size * i, chunk_size, displaced);
|
||||||
if (!processChunk(&chunk, progress))
|
processChunk(canvas, &chunk, progress);
|
||||||
|
if (interrupted)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -193,48 +192,19 @@ void TerrainRasterizer::getTessellationInfo(int displaced)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef struct
|
void TerrainRasterizer::processChunk(CanvasPortion* canvas, TerrainChunkInfo* chunk, double progress)
|
||||||
{
|
{
|
||||||
TerrainRasterizer* rasterizer;
|
tessellateChunk(canvas, chunk, chunk->detail_hint);
|
||||||
TerrainRasterizer::TerrainChunkInfo chunk;
|
|
||||||
} ParallelRasterInfo;
|
|
||||||
|
|
||||||
static int _parallelJobCallback(ParallelQueue*, int, void* data, int stopping)
|
|
||||||
{
|
|
||||||
ParallelRasterInfo* info = (ParallelRasterInfo*)data;
|
|
||||||
|
|
||||||
if (!stopping)
|
|
||||||
{
|
|
||||||
info->rasterizer->tessellateChunk(&info->chunk, info->chunk.detail_hint);
|
|
||||||
}
|
|
||||||
|
|
||||||
delete info;
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int TerrainRasterizer::processChunk(TerrainChunkInfo* chunk, double progress)
|
void TerrainRasterizer::rasterizeToCanvas(CanvasPortion *canvas)
|
||||||
{
|
{
|
||||||
ParallelRasterInfo* info = new ParallelRasterInfo;
|
getTessellationInfo(canvas, 0);
|
||||||
|
|
||||||
info->rasterizer = this;
|
|
||||||
info->chunk = *chunk;
|
|
||||||
|
|
||||||
if (!queue->addJob(_parallelJobCallback, info))
|
|
||||||
{
|
|
||||||
delete info;
|
|
||||||
}
|
|
||||||
|
|
||||||
renderer->render_progress = 0.05 * progress;
|
|
||||||
return !renderer->render_interrupt;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void TerrainRasterizer::renderSurface()
|
Color TerrainRasterizer::shadeFragment(const CanvasFragment &fragment) const
|
||||||
{
|
{
|
||||||
queue = new ParallelQueue();
|
Vector3 point = fragment.getLocation();
|
||||||
|
double precision = renderer->getPrecision(_getPoint(renderer, point.x, point.z));
|
||||||
renderer->render_progress = 0.0;
|
return renderer->getTerrainRenderer()->getFinalColor(point, precision);
|
||||||
getTessellationInfo(0);
|
|
||||||
renderer->render_progress = 0.05;
|
|
||||||
|
|
||||||
queue->wait();
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,12 +3,13 @@
|
||||||
|
|
||||||
#include "software_global.h"
|
#include "software_global.h"
|
||||||
|
|
||||||
|
#include "Rasterizer.h"
|
||||||
#include "Vector3.h"
|
#include "Vector3.h"
|
||||||
|
|
||||||
namespace paysages {
|
namespace paysages {
|
||||||
namespace software {
|
namespace software {
|
||||||
|
|
||||||
class SOFTWARESHARED_EXPORT TerrainRasterizer
|
class SOFTWARESHARED_EXPORT TerrainRasterizer: public Rasterizer
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
typedef struct
|
typedef struct
|
||||||
|
@ -21,35 +22,29 @@ public:
|
||||||
} TerrainChunkInfo;
|
} TerrainChunkInfo;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
TerrainRasterizer(SoftwareRenderer* renderer);
|
TerrainRasterizer(SoftwareRenderer* renderer, int client_id);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Method called for each chunk tessellated by getTessellationInfo.
|
* Method called for each chunk tessellated by getTessellationInfo.
|
||||||
*/
|
*/
|
||||||
int processChunk(TerrainChunkInfo* chunk, double progress);
|
void processChunk(CanvasPortion* canvas, TerrainChunkInfo* chunk, double progress);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tessellate the terrain, calling processChunk for each chunk.
|
* Tessellate the terrain, calling processChunk for each chunk.
|
||||||
*
|
*
|
||||||
* The terrain will be broken in chunks, most detailed near the camera.
|
* The terrain will be broken in chunks, most detailed near the camera.
|
||||||
*/
|
*/
|
||||||
void getTessellationInfo(int displaced);
|
void getTessellationInfo(CanvasPortion* canvas, int displaced);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tessellate a terrain chunk, pushing the quads in the render area.
|
* Tessellate a terrain chunk, pushing the quads in the render area.
|
||||||
*/
|
*/
|
||||||
void tessellateChunk(TerrainChunkInfo* chunk, int detail);
|
void tessellateChunk(CanvasPortion* canvas, TerrainChunkInfo* chunk, int detail);
|
||||||
|
|
||||||
/**
|
void renderQuad(CanvasPortion* canvas, double x, double z, double size, double water_height);
|
||||||
* Start the final rasterization of terrain.
|
|
||||||
*
|
|
||||||
* This will push the rasterized quads in the render area, waiting for post process.
|
|
||||||
*/
|
|
||||||
void renderSurface();
|
|
||||||
|
|
||||||
private:
|
virtual void rasterizeToCanvas(CanvasPortion* canvas) override;
|
||||||
SoftwareRenderer* renderer;
|
virtual Color shadeFragment(const CanvasFragment &fragment) const override;
|
||||||
ParallelQueue* queue;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,10 +40,10 @@ double TexturesRenderer::getTriplanarNoise(NoiseGenerator *noise, const Vector3
|
||||||
double mXY = fabs(normal.z);
|
double mXY = fabs(normal.z);
|
||||||
double mXZ = fabs(normal.y);
|
double mXZ = fabs(normal.y);
|
||||||
double mYZ = fabs(normal.x);
|
double mYZ = fabs(normal.x);
|
||||||
double total = mXY + mXZ + mYZ;
|
double total = 1.0 / (mXY + mXZ + mYZ);
|
||||||
mXY /= total;
|
mXY *= total;
|
||||||
mXZ /= total;
|
mXZ *= total;
|
||||||
mYZ /= total;
|
mYZ *= total;
|
||||||
|
|
||||||
return noiseXY * mXY + noiseXZ * mXZ + noiseYZ * mYZ;
|
return noiseXY * mXY + noiseXZ * mXZ + noiseYZ * mYZ;
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,19 +2,14 @@
|
||||||
|
|
||||||
#include "SoftwareRenderer.h"
|
#include "SoftwareRenderer.h"
|
||||||
#include "WaterRenderer.h"
|
#include "WaterRenderer.h"
|
||||||
#include "ParallelQueue.h"
|
#include "CanvasFragment.h"
|
||||||
|
|
||||||
WaterRasterizer::WaterRasterizer(SoftwareRenderer* renderer):
|
WaterRasterizer::WaterRasterizer(SoftwareRenderer* renderer, int client_id):
|
||||||
renderer(renderer)
|
Rasterizer(renderer, client_id, Color(0.9, 0.95, 1.0))
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
static Color _postProcessFragment(SoftwareRenderer* renderer, const Vector3 &location, void*)
|
static inline Vector3 _getFirstPassVertex(SoftwareRenderer* renderer, double x, double z)
|
||||||
{
|
|
||||||
return renderer->getWaterRenderer()->getResult(location.x, location.z).final;
|
|
||||||
}
|
|
||||||
|
|
||||||
static Vector3 _getFirstPassVertex(SoftwareRenderer* renderer, double x, double z)
|
|
||||||
{
|
{
|
||||||
Vector3 result;
|
Vector3 result;
|
||||||
|
|
||||||
|
@ -25,7 +20,7 @@ static Vector3 _getFirstPassVertex(SoftwareRenderer* renderer, double x, double
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void _renderQuad(SoftwareRenderer* renderer, double x, double z, double size)
|
void WaterRasterizer::rasterizeQuad(CanvasPortion* canvas, double x, double z, double size)
|
||||||
{
|
{
|
||||||
Vector3 v1, v2, v3, v4;
|
Vector3 v1, v2, v3, v4;
|
||||||
|
|
||||||
|
@ -34,42 +29,11 @@ static void _renderQuad(SoftwareRenderer* renderer, double x, double z, double s
|
||||||
v3 = _getFirstPassVertex(renderer, x + size, z + size);
|
v3 = _getFirstPassVertex(renderer, x + size, z + size);
|
||||||
v4 = _getFirstPassVertex(renderer, x + size, z);
|
v4 = _getFirstPassVertex(renderer, x + size, z);
|
||||||
|
|
||||||
renderer->pushQuad(v1, v2, v3, v4, _postProcessFragment, NULL);
|
pushQuad(canvas, v1, v2, v3, v4);
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef struct
|
void WaterRasterizer::rasterizeToCanvas(CanvasPortion *canvas)
|
||||||
{
|
{
|
||||||
SoftwareRenderer* renderer;
|
|
||||||
int i;
|
|
||||||
double cx;
|
|
||||||
double cz;
|
|
||||||
double radius_int;
|
|
||||||
double chunk_size;
|
|
||||||
double radius_ext;
|
|
||||||
} ParallelRasterInfo;
|
|
||||||
|
|
||||||
static int _parallelJobCallback(ParallelQueue*, int, void* data, int stopping)
|
|
||||||
{
|
|
||||||
ParallelRasterInfo* info = (ParallelRasterInfo*)data;
|
|
||||||
|
|
||||||
if (!stopping)
|
|
||||||
{
|
|
||||||
_renderQuad(info->renderer, info->cx - info->radius_ext + info->chunk_size * info->i, info->cz - info->radius_ext, info->chunk_size);
|
|
||||||
_renderQuad(info->renderer, info->cx + info->radius_int, info->cz - info->radius_ext + info->chunk_size * info->i, info->chunk_size);
|
|
||||||
_renderQuad(info->renderer, info->cx + info->radius_int - info->chunk_size * info->i, info->cz + info->radius_int, info->chunk_size);
|
|
||||||
_renderQuad(info->renderer, info->cx - info->radius_ext, info->cz + info->radius_int - info->chunk_size * info->i, info->chunk_size);
|
|
||||||
}
|
|
||||||
|
|
||||||
delete info;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void WaterRasterizer::renderSurface()
|
|
||||||
{
|
|
||||||
ParallelRasterInfo* info;
|
|
||||||
ParallelQueue* queue;
|
|
||||||
queue = new ParallelQueue();
|
|
||||||
|
|
||||||
int chunk_factor, chunk_count, i;
|
int chunk_factor, chunk_count, i;
|
||||||
Vector3 cam = renderer->getCameraLocation(VECTOR_ZERO);
|
Vector3 cam = renderer->getCameraLocation(VECTOR_ZERO);
|
||||||
double radius_int, radius_ext, base_chunk_size, chunk_size;
|
double radius_int, radius_ext, base_chunk_size, chunk_size;
|
||||||
|
@ -91,27 +55,17 @@ void WaterRasterizer::renderSurface()
|
||||||
|
|
||||||
while (radius_int < 20000.0)
|
while (radius_int < 20000.0)
|
||||||
{
|
{
|
||||||
if (!renderer->addRenderProgress(0.0))
|
if (interrupted)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i = 0; i < chunk_count - 1; i++)
|
for (i = 0; i < chunk_count - 1; i++)
|
||||||
{
|
{
|
||||||
info = new ParallelRasterInfo;
|
rasterizeQuad(canvas, cx - radius_ext + chunk_size * i, cz - radius_ext, chunk_size);
|
||||||
|
rasterizeQuad(canvas, cx + radius_int, cz - radius_ext + chunk_size * i, chunk_size);
|
||||||
info->renderer = renderer;
|
rasterizeQuad(canvas, cx + radius_int - chunk_size * i, cz + radius_int, chunk_size);
|
||||||
info->cx = cx;
|
rasterizeQuad(canvas, cx - radius_ext, cz + radius_int - chunk_size * i, chunk_size);
|
||||||
info->cz = cz;
|
|
||||||
info->i = i;
|
|
||||||
info->radius_int = radius_int;
|
|
||||||
info->radius_ext = radius_ext;
|
|
||||||
info->chunk_size = chunk_size;
|
|
||||||
|
|
||||||
if (!queue->addJob(_parallelJobCallback, info))
|
|
||||||
{
|
|
||||||
delete info;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (radius_int > 20.0 && chunk_count % 64 == 0 && (double)chunk_factor < radius_int / 20.0)
|
if (radius_int > 20.0 && chunk_count % 64 == 0 && (double)chunk_factor < radius_int / 20.0)
|
||||||
|
@ -124,7 +78,10 @@ void WaterRasterizer::renderSurface()
|
||||||
radius_int = radius_ext;
|
radius_int = radius_ext;
|
||||||
radius_ext += chunk_size;
|
radius_ext += chunk_size;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
queue->wait();
|
|
||||||
delete queue;
|
Color WaterRasterizer::shadeFragment(const CanvasFragment &fragment) const
|
||||||
|
{
|
||||||
|
Vector3 location = fragment.getLocation();
|
||||||
|
return renderer->getWaterRenderer()->getResult(location.x, location.z).final;
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,18 +3,20 @@
|
||||||
|
|
||||||
#include "software_global.h"
|
#include "software_global.h"
|
||||||
|
|
||||||
|
#include "Rasterizer.h"
|
||||||
|
|
||||||
namespace paysages {
|
namespace paysages {
|
||||||
namespace software {
|
namespace software {
|
||||||
|
|
||||||
class WaterRasterizer
|
class WaterRasterizer: public Rasterizer
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
WaterRasterizer(SoftwareRenderer* renderer);
|
WaterRasterizer(SoftwareRenderer* renderer, int client_id);
|
||||||
|
|
||||||
void renderSurface();
|
void rasterizeQuad(CanvasPortion* canvas, double x, double z, double size);
|
||||||
|
|
||||||
private:
|
virtual void rasterizeToCanvas(CanvasPortion* canvas) override;
|
||||||
SoftwareRenderer* renderer;
|
virtual Color shadeFragment(const CanvasFragment &fragment) const override;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,10 +34,20 @@ SOURCES += SoftwareRenderer.cpp \
|
||||||
TerrainRenderer.cpp \
|
TerrainRenderer.cpp \
|
||||||
TexturesRenderer.cpp \
|
TexturesRenderer.cpp \
|
||||||
WaterRenderer.cpp \
|
WaterRenderer.cpp \
|
||||||
RenderArea.cpp \
|
|
||||||
RayCastingManager.cpp \
|
RayCastingManager.cpp \
|
||||||
NightSky.cpp \
|
NightSky.cpp \
|
||||||
TerrainRayWalker.cpp
|
TerrainRayWalker.cpp \
|
||||||
|
Canvas.cpp \
|
||||||
|
CanvasPortion.cpp \
|
||||||
|
CanvasPixel.cpp \
|
||||||
|
CanvasFragment.cpp \
|
||||||
|
SoftwareCanvasRenderer.cpp \
|
||||||
|
Rasterizer.cpp \
|
||||||
|
CanvasLiveClient.cpp \
|
||||||
|
CanvasPreview.cpp \
|
||||||
|
RenderConfig.cpp \
|
||||||
|
CanvasPixelShader.cpp \
|
||||||
|
CanvasPictureWriter.cpp
|
||||||
|
|
||||||
HEADERS += SoftwareRenderer.h\
|
HEADERS += SoftwareRenderer.h\
|
||||||
software_global.h \
|
software_global.h \
|
||||||
|
@ -61,10 +71,20 @@ HEADERS += SoftwareRenderer.h\
|
||||||
TerrainRenderer.h \
|
TerrainRenderer.h \
|
||||||
TexturesRenderer.h \
|
TexturesRenderer.h \
|
||||||
WaterRenderer.h \
|
WaterRenderer.h \
|
||||||
RenderArea.h \
|
|
||||||
RayCastingManager.h \
|
RayCastingManager.h \
|
||||||
NightSky.h \
|
NightSky.h \
|
||||||
TerrainRayWalker.h
|
TerrainRayWalker.h \
|
||||||
|
Canvas.h \
|
||||||
|
CanvasPortion.h \
|
||||||
|
CanvasPixel.h \
|
||||||
|
CanvasFragment.h \
|
||||||
|
SoftwareCanvasRenderer.h \
|
||||||
|
Rasterizer.h \
|
||||||
|
CanvasLiveClient.h \
|
||||||
|
CanvasPreview.h \
|
||||||
|
RenderConfig.h \
|
||||||
|
CanvasPixelShader.h \
|
||||||
|
CanvasPictureWriter.h
|
||||||
|
|
||||||
unix:!symbian {
|
unix:!symbian {
|
||||||
maemo5 {
|
maemo5 {
|
||||||
|
|
|
@ -14,7 +14,8 @@
|
||||||
namespace paysages {
|
namespace paysages {
|
||||||
namespace software {
|
namespace software {
|
||||||
class SoftwareRenderer;
|
class SoftwareRenderer;
|
||||||
class RenderArea;
|
class SoftwareCanvasRenderer;
|
||||||
|
class RenderConfig;
|
||||||
|
|
||||||
class FluidMediumManager;
|
class FluidMediumManager;
|
||||||
class FluidMediumInterface;
|
class FluidMediumInterface;
|
||||||
|
@ -33,6 +34,7 @@ namespace software {
|
||||||
class TexturesRenderer;
|
class TexturesRenderer;
|
||||||
class WaterRenderer;
|
class WaterRenderer;
|
||||||
|
|
||||||
|
class Rasterizer;
|
||||||
class SkyRasterizer;
|
class SkyRasterizer;
|
||||||
class TerrainRasterizer;
|
class TerrainRasterizer;
|
||||||
|
|
||||||
|
@ -44,6 +46,15 @@ namespace software {
|
||||||
class NightSky;
|
class NightSky;
|
||||||
|
|
||||||
class TerrainRayWalker;
|
class TerrainRayWalker;
|
||||||
|
|
||||||
|
class Canvas;
|
||||||
|
class CanvasPortion;
|
||||||
|
class CanvasPixel;
|
||||||
|
class CanvasFragment;
|
||||||
|
class CanvasLiveClient;
|
||||||
|
class CanvasPreview;
|
||||||
|
class CanvasPixelShader;
|
||||||
|
class CanvasPictureWriter;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
14
src/system/FileSystem.cpp
Normal file
14
src/system/FileSystem.cpp
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
#include "FileSystem.h"
|
||||||
|
|
||||||
|
#include <QDir>
|
||||||
|
#include <QFileInfo>
|
||||||
|
|
||||||
|
std::string FileSystem::getTempFile(const std::string &filename)
|
||||||
|
{
|
||||||
|
return QDir::temp().filePath(QString::fromStdString(filename)).toStdString();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FileSystem::isFile(const std::string &filepath)
|
||||||
|
{
|
||||||
|
return QFileInfo(QString::fromStdString(filepath)).exists();
|
||||||
|
}
|
28
src/system/FileSystem.h
Normal file
28
src/system/FileSystem.h
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
#ifndef FILESYSTEM_H
|
||||||
|
#define FILESYSTEM_H
|
||||||
|
|
||||||
|
#include "system_global.h"
|
||||||
|
|
||||||
|
namespace paysages {
|
||||||
|
namespace system {
|
||||||
|
|
||||||
|
class SYSTEMSHARED_EXPORT FileSystem
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Get an absolute path to a temporary file.
|
||||||
|
*
|
||||||
|
* filename must not contain directory separators.
|
||||||
|
*/
|
||||||
|
static std::string getTempFile(const std::string &filename);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if the given path points to a file.
|
||||||
|
*/
|
||||||
|
static bool isFile(const std::string &filepath);
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // FILESYSTEM_H
|
|
@ -114,3 +114,13 @@ std::string PackStream::readString()
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void PackStream::skip(const int &value, int count)
|
||||||
|
{
|
||||||
|
stream->skipRawData(sizeof(value) * count);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PackStream::skip(const double &value, int count)
|
||||||
|
{
|
||||||
|
stream->skipRawData(sizeof(value) * count);
|
||||||
|
}
|
||||||
|
|
|
@ -32,6 +32,9 @@ public:
|
||||||
void read(char* value, int max_length);
|
void read(char* value, int max_length);
|
||||||
std::string readString();
|
std::string readString();
|
||||||
|
|
||||||
|
void skip(const int &value, int count=1);
|
||||||
|
void skip(const double &value, int count=1);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QFile* file;
|
QFile* file;
|
||||||
QDataStream* stream;
|
QDataStream* stream;
|
||||||
|
|
|
@ -1,181 +0,0 @@
|
||||||
#include "ParallelQueue.h"
|
|
||||||
|
|
||||||
#include "Mutex.h"
|
|
||||||
#include "Thread.h"
|
|
||||||
#include "System.h"
|
|
||||||
#include <cassert>
|
|
||||||
|
|
||||||
#define QUEUE_SIZE 1000
|
|
||||||
|
|
||||||
static void* _queueThreadCallback(ParallelQueue* queue)
|
|
||||||
{
|
|
||||||
ParallelQueue::ParallelJob* job;
|
|
||||||
|
|
||||||
while (!queue->stopping)
|
|
||||||
{
|
|
||||||
/* Try to take a job */
|
|
||||||
queue->lock->acquire();
|
|
||||||
job = queue->jobs + queue->jobs_index_pending;
|
|
||||||
if (job->state == ParallelQueue::JOB_STATE_PENDING)
|
|
||||||
{
|
|
||||||
if (queue->jobs_index_pending >= QUEUE_SIZE - 1)
|
|
||||||
{
|
|
||||||
queue->jobs_index_pending = 0;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
queue->jobs_index_pending++;
|
|
||||||
}
|
|
||||||
job->state = ParallelQueue::JOB_STATE_PROCESSING;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
job = NULL;
|
|
||||||
}
|
|
||||||
queue->lock->release();
|
|
||||||
|
|
||||||
if (job)
|
|
||||||
{
|
|
||||||
/* Process the job */
|
|
||||||
job->process(queue, job->id, job->data, 0);
|
|
||||||
|
|
||||||
queue->lock->acquire();
|
|
||||||
if (queue->collect)
|
|
||||||
{
|
|
||||||
job->state = ParallelQueue::JOB_STATE_TOCOLLECT;
|
|
||||||
/* TODO jobs_index_collect ? */
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
job->state = ParallelQueue::JOB_STATE_FREE;
|
|
||||||
queue->jobs_count--;
|
|
||||||
}
|
|
||||||
queue->lock->release();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Thread::timeSleepMs(50);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
ParallelQueue::ParallelQueue(int collect)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
|
|
||||||
assert(!collect); /* Not fully implemented yet ! */
|
|
||||||
|
|
||||||
this->collect = collect;
|
|
||||||
this->stopping = 0;
|
|
||||||
this->lock = new Mutex();
|
|
||||||
|
|
||||||
this->jobs = new ParallelJob[QUEUE_SIZE];
|
|
||||||
for (i = 0; i < QUEUE_SIZE; i++)
|
|
||||||
{
|
|
||||||
this->jobs[i].state = JOB_STATE_FREE;
|
|
||||||
}
|
|
||||||
this->jobs_count = 0;
|
|
||||||
this->jobs_index_free = 0;
|
|
||||||
this->jobs_index_collect = 0;
|
|
||||||
this->jobs_index_pending = 0;
|
|
||||||
this->jobs_next_id = 1;
|
|
||||||
|
|
||||||
/* Start workers */
|
|
||||||
this->workers_count = System::getCoreCount();
|
|
||||||
this->workers = new Thread*[this->workers_count];
|
|
||||||
for (i = 0; i < this->workers_count; i++)
|
|
||||||
{
|
|
||||||
this->workers[i] = new Thread((ThreadFunction)_queueThreadCallback);
|
|
||||||
this->workers[i]->start(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ParallelQueue::~ParallelQueue()
|
|
||||||
{
|
|
||||||
interrupt();
|
|
||||||
|
|
||||||
assert(not collect or jobs[jobs_index_collect].state != JOB_STATE_TOCOLLECT);
|
|
||||||
assert(jobs_count == 0);
|
|
||||||
|
|
||||||
delete lock;
|
|
||||||
delete[] jobs;
|
|
||||||
delete[] workers;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ParallelQueue::interrupt()
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
|
|
||||||
if (not stopping)
|
|
||||||
{
|
|
||||||
stopping = 1;
|
|
||||||
|
|
||||||
for (i = 0; i < workers_count; i++)
|
|
||||||
{
|
|
||||||
workers[i]->join();
|
|
||||||
delete workers[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void ParallelQueue::wait()
|
|
||||||
{
|
|
||||||
while (jobs_count > 0)
|
|
||||||
{
|
|
||||||
Thread::timeSleepMs(100);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int ParallelQueue::addJob(FuncParallelJob func_process, void* data)
|
|
||||||
{
|
|
||||||
if (stopping)
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Wait for a free slot */
|
|
||||||
while (jobs[jobs_index_free].state != JOB_STATE_FREE)
|
|
||||||
{
|
|
||||||
Thread::timeSleepMs(50);
|
|
||||||
if (stopping)
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Prepare the job */
|
|
||||||
ParallelJob job;
|
|
||||||
job.state = JOB_STATE_PENDING;
|
|
||||||
job.id = jobs_next_id++;
|
|
||||||
job.process = func_process;
|
|
||||||
job.data = data;
|
|
||||||
|
|
||||||
/* Add the job to the queue */
|
|
||||||
lock->acquire();
|
|
||||||
if (stopping)
|
|
||||||
{
|
|
||||||
lock->release();
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
jobs[jobs_index_free] = job;
|
|
||||||
if (jobs_index_free >= QUEUE_SIZE - 1)
|
|
||||||
{
|
|
||||||
jobs_index_free = 0;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
jobs_index_free++;
|
|
||||||
}
|
|
||||||
jobs_count++;
|
|
||||||
assert(jobs_count <= QUEUE_SIZE);
|
|
||||||
lock->release();
|
|
||||||
|
|
||||||
return job.id;
|
|
||||||
}
|
|
||||||
|
|
||||||
int ParallelQueue::collectJobs(FuncParallelJob)
|
|
||||||
{
|
|
||||||
/* TODO */
|
|
||||||
return 0;
|
|
||||||
}
|
|
|
@ -1,94 +0,0 @@
|
||||||
#ifndef PARALLELQUEUE_H
|
|
||||||
#define PARALLELQUEUE_H
|
|
||||||
|
|
||||||
#include "system_global.h"
|
|
||||||
|
|
||||||
namespace paysages {
|
|
||||||
namespace system {
|
|
||||||
|
|
||||||
class SYSTEMSHARED_EXPORT ParallelQueue
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
typedef int (*FuncParallelJob)(ParallelQueue* queue, int job_id, void* data, int stopping);
|
|
||||||
|
|
||||||
typedef enum
|
|
||||||
{
|
|
||||||
JOB_STATE_FREE,
|
|
||||||
JOB_STATE_PENDING,
|
|
||||||
JOB_STATE_PROCESSING,
|
|
||||||
JOB_STATE_TOCOLLECT
|
|
||||||
} EnumJobState;
|
|
||||||
|
|
||||||
typedef struct
|
|
||||||
{
|
|
||||||
EnumJobState state;
|
|
||||||
int id;
|
|
||||||
FuncParallelJob process;
|
|
||||||
void* data;
|
|
||||||
} ParallelJob;
|
|
||||||
|
|
||||||
public:
|
|
||||||
/**
|
|
||||||
* Create a parallel processing queue.
|
|
||||||
*
|
|
||||||
* This queue will use parallel workers to process jobs added to it.
|
|
||||||
* @param collect True to collect finished jobs and wait for a call to collectJobs, False to discard finished jobs.
|
|
||||||
* @return The newly allocated queue.
|
|
||||||
*/
|
|
||||||
ParallelQueue(int collect=0);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Delete a parallel queue.
|
|
||||||
*
|
|
||||||
* This will interrupt the queue.
|
|
||||||
* If the queue is in collect mode, you should call interrupt, then collectJobs, before calling this.
|
|
||||||
*/
|
|
||||||
~ParallelQueue();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Interrupt the queue processing.
|
|
||||||
*
|
|
||||||
* This will wait for running jobs to end, cancel pending jobs (still calling their callbacks with stopping=1) and
|
|
||||||
* refuse future jobs.
|
|
||||||
*/
|
|
||||||
void interrupt();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Wait for all jobs to finish.
|
|
||||||
*
|
|
||||||
* This function will return as soon as there is no pending jobs. It is recommended to stop feeding the queue, or this
|
|
||||||
* function may never return.
|
|
||||||
*/
|
|
||||||
void wait();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add a job to the queue.
|
|
||||||
*
|
|
||||||
* Don't call this method concurrently from several threads.
|
|
||||||
* @param func_process The function that will be called for the job processing.
|
|
||||||
* @param data The data that will be passed to the callback.
|
|
||||||
* @return The job ID, 0 if the queue doesn't accept jobs.
|
|
||||||
*/
|
|
||||||
int addJob(FuncParallelJob func_process, void* data);
|
|
||||||
|
|
||||||
int collectJobs(FuncParallelJob func_collect);
|
|
||||||
|
|
||||||
int collect;
|
|
||||||
volatile int stopping;
|
|
||||||
Mutex* lock;
|
|
||||||
|
|
||||||
int workers_count;
|
|
||||||
Thread** workers;
|
|
||||||
|
|
||||||
ParallelJob* jobs;
|
|
||||||
int jobs_count; /** Number of jobs in queue (all status except JOB_STATE_FREE) */
|
|
||||||
int jobs_index_free; /** Index of next free position */
|
|
||||||
int jobs_index_collect; /** Index of first job to collect */
|
|
||||||
int jobs_index_pending; /** Index of first pending job to process */
|
|
||||||
int jobs_next_id;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // PARALLELQUEUE_H
|
|
|
@ -2,113 +2,186 @@
|
||||||
|
|
||||||
#include "Thread.h"
|
#include "Thread.h"
|
||||||
#include "System.h"
|
#include "System.h"
|
||||||
|
#include "Semaphore.h"
|
||||||
|
#include "Mutex.h"
|
||||||
|
#include "ParallelWorker.h"
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compatibility class for code that uses ParallelUnitFunction.
|
||||||
|
*/
|
||||||
|
class ParallelWorkerCompat:public ParallelWorker
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ParallelWorkerCompat(ParallelWork *work, ParallelWork::ParallelUnitFunction func, void* data):
|
||||||
|
ParallelWorker(), work(work), func(func), data(data)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void processParallelUnit(int unit)
|
||||||
|
{
|
||||||
|
func(work, unit, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
ParallelWork* work;
|
||||||
|
ParallelWork::ParallelUnitFunction func;
|
||||||
|
void* data;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Thread sub-class to perform units of work
|
||||||
|
*/
|
||||||
|
class ParallelWork::ParallelThread:public Thread
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ParallelThread(ParallelWork *work):
|
||||||
|
Thread(), work(work), sem(0)
|
||||||
|
{
|
||||||
|
interrupted = false;
|
||||||
|
unit = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void feedUnit(int unit)
|
||||||
|
{
|
||||||
|
this->unit = unit;
|
||||||
|
sem.release();
|
||||||
|
}
|
||||||
|
|
||||||
|
void interrupt()
|
||||||
|
{
|
||||||
|
interrupted = true;
|
||||||
|
sem.release();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual void run() override
|
||||||
|
{
|
||||||
|
while (unit >= 0 or not interrupted)
|
||||||
|
{
|
||||||
|
// Wait for a unit (or interrupt)
|
||||||
|
sem.acquire();
|
||||||
|
|
||||||
|
// Process the unit
|
||||||
|
if (unit >= 0)
|
||||||
|
{
|
||||||
|
work->worker->processParallelUnit(unit);
|
||||||
|
unit = -1;
|
||||||
|
work->returnThread(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
ParallelWork* work;
|
||||||
|
Semaphore sem;
|
||||||
|
int unit;
|
||||||
|
bool interrupted;
|
||||||
|
};
|
||||||
|
|
||||||
|
ParallelWork::ParallelWork(ParallelWorker *worker, int units)
|
||||||
|
{
|
||||||
|
this->units = units;
|
||||||
|
this->running = 0;
|
||||||
|
this->worker = worker;
|
||||||
|
this->worker_compat = false;
|
||||||
|
}
|
||||||
|
|
||||||
ParallelWork::ParallelWork(ParallelUnitFunction func, int units, void* data)
|
ParallelWork::ParallelWork(ParallelUnitFunction func, int units, void* data)
|
||||||
{
|
{
|
||||||
this->units = units;
|
this->units = units;
|
||||||
this->running = 0;
|
this->running = 0;
|
||||||
this->unit_function = func;
|
this->worker = new ParallelWorkerCompat(this, func, data);
|
||||||
this->data = data;
|
this->worker_compat = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
ParallelWork::~ParallelWork()
|
ParallelWork::~ParallelWork()
|
||||||
{
|
{
|
||||||
assert(not running);
|
assert(not running);
|
||||||
}
|
if (worker_compat)
|
||||||
|
|
||||||
static void* _workerThreadCallback(ParallelWork::ParallelWorker* worker)
|
|
||||||
{
|
|
||||||
worker->result = worker->work->unit_function(worker->work, worker->unit, worker->work->data);
|
|
||||||
worker->status = ParallelWork::PARALLEL_WORKER_STATUS_DONE;
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int _runNextWorker(ParallelWork::ParallelWorker workers[], int worker_count, int unit)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
|
|
||||||
while (1)
|
|
||||||
{
|
{
|
||||||
for (i = 0; i < worker_count; i++)
|
delete worker;
|
||||||
{
|
|
||||||
ParallelWork::ParallelWorker* worker = workers + i;
|
|
||||||
if (worker->status == ParallelWork::PARALLEL_WORKER_STATUS_VOID)
|
|
||||||
{
|
|
||||||
worker->status = ParallelWork::PARALLEL_WORKER_STATUS_RUNNING;
|
|
||||||
worker->result = 0;
|
|
||||||
worker->unit = unit;
|
|
||||||
worker->thread = new Thread((ThreadFunction)_workerThreadCallback);
|
|
||||||
worker->thread->start(worker);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
else if (worker->status == ParallelWork::PARALLEL_WORKER_STATUS_DONE)
|
|
||||||
{
|
|
||||||
int result = worker->result;
|
|
||||||
|
|
||||||
worker->status = ParallelWork::PARALLEL_WORKER_STATUS_RUNNING;
|
|
||||||
worker->result = 0;
|
|
||||||
worker->unit = unit;
|
|
||||||
worker->thread->join();
|
|
||||||
delete worker->thread;
|
|
||||||
worker->thread = new Thread((ThreadFunction)_workerThreadCallback);
|
|
||||||
worker->thread->start(worker);
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Thread::timeSleepMs(50);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int ParallelWork::perform(int nbworkers)
|
int ParallelWork::perform(int thread_count)
|
||||||
{
|
{
|
||||||
int i, done, result;
|
int i, done;
|
||||||
assert(not running);
|
assert(not running);
|
||||||
|
|
||||||
result = 0;
|
// Get thread count
|
||||||
|
if (thread_count <= 0)
|
||||||
if (nbworkers <= 0)
|
|
||||||
{
|
{
|
||||||
nbworkers = System::getCoreCount();
|
thread_count = System::getCoreCount();
|
||||||
}
|
}
|
||||||
if (nbworkers > PARALLEL_MAX_THREADS)
|
if (thread_count > PARALLEL_MAX_THREADS)
|
||||||
{
|
{
|
||||||
nbworkers = PARALLEL_MAX_THREADS;
|
thread_count = PARALLEL_MAX_THREADS;
|
||||||
}
|
}
|
||||||
|
this->thread_count = thread_count;
|
||||||
running = 1;
|
running = 1;
|
||||||
|
|
||||||
/* Init workers */
|
// Init threads
|
||||||
for (i = 0; i < nbworkers; i++)
|
semaphore = new Semaphore(thread_count);
|
||||||
|
mutex = new Mutex();
|
||||||
|
threads = new ParallelThread*[thread_count];
|
||||||
|
available = new ParallelThread*[thread_count];
|
||||||
|
available_offset = 0;
|
||||||
|
available_length = thread_count;
|
||||||
|
for (i = 0; i < thread_count; i++)
|
||||||
{
|
{
|
||||||
workers[i].status = PARALLEL_WORKER_STATUS_VOID;
|
threads[i] = available[i] = new ParallelThread(this);
|
||||||
workers[i].work = this;
|
threads[i]->start();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Perform run */
|
// Perform all units
|
||||||
for (done = 0; done < units; done++)
|
for (done = 0; done < units; done++)
|
||||||
{
|
{
|
||||||
if (_runNextWorker(workers, nbworkers, done))
|
// Take first available thread
|
||||||
|
semaphore->acquire();
|
||||||
|
mutex->acquire();
|
||||||
|
ParallelThread *thread = available[available_offset];
|
||||||
|
available_offset++;
|
||||||
|
if (available_offset >= thread_count)
|
||||||
{
|
{
|
||||||
result++;
|
available_offset = 0;
|
||||||
}
|
}
|
||||||
|
available_length--;
|
||||||
|
mutex->release();
|
||||||
|
|
||||||
|
// Feed the unit to it
|
||||||
|
thread->feedUnit(done);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Wait and clean up workers */
|
// Wait for all threads to end, then cleanup
|
||||||
for (i = 0; i < nbworkers; i++)
|
for (i = 0; i < thread_count; i++)
|
||||||
{
|
{
|
||||||
if (workers[i].status != PARALLEL_WORKER_STATUS_VOID)
|
threads[i]->interrupt();
|
||||||
{
|
|
||||||
workers[i].thread->join();
|
|
||||||
delete workers[i].thread;
|
|
||||||
if (workers[i].result)
|
|
||||||
{
|
|
||||||
result++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
for (i = 0; i < thread_count; i++)
|
||||||
|
{
|
||||||
|
threads[i]->join();
|
||||||
|
delete threads[i];
|
||||||
|
}
|
||||||
|
delete[] threads;
|
||||||
|
delete[] available;
|
||||||
|
delete semaphore;
|
||||||
|
delete mutex;
|
||||||
|
|
||||||
running = 0;
|
running = 0;
|
||||||
return result;
|
return done;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ParallelWork::interrupt()
|
||||||
|
{
|
||||||
|
worker->interrupt();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ParallelWork::returnThread(ParallelWork::ParallelThread *thread)
|
||||||
|
{
|
||||||
|
mutex->acquire();
|
||||||
|
available[(available_offset + available_length) % thread_count] = thread;
|
||||||
|
available_length++;
|
||||||
|
semaphore->release();
|
||||||
|
mutex->release();
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,32 +13,24 @@ class SYSTEMSHARED_EXPORT ParallelWork
|
||||||
public:
|
public:
|
||||||
typedef int (*ParallelUnitFunction)(ParallelWork* work, int unit, void* data);
|
typedef int (*ParallelUnitFunction)(ParallelWork* work, int unit, void* data);
|
||||||
|
|
||||||
typedef enum
|
/**
|
||||||
{
|
* Obscure thread class.
|
||||||
PARALLEL_WORKER_STATUS_VOID,
|
*/
|
||||||
PARALLEL_WORKER_STATUS_RUNNING,
|
class ParallelThread;
|
||||||
PARALLEL_WORKER_STATUS_DONE
|
friend class ParallelThread;
|
||||||
} ParallelWorkerStatus;
|
|
||||||
|
|
||||||
typedef struct
|
|
||||||
{
|
|
||||||
Thread* thread;
|
|
||||||
ParallelWork* work;
|
|
||||||
ParallelWorkerStatus status;
|
|
||||||
int unit;
|
|
||||||
int result;
|
|
||||||
} ParallelWorker;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/**
|
/**
|
||||||
* Create a parallel work handler.
|
* Create a parallel work handler.
|
||||||
*
|
*
|
||||||
* This will spawn an optimal number of threads to process a given number of work units.
|
* This will spawn a number of threads.
|
||||||
|
*/
|
||||||
|
ParallelWork(ParallelWorker *worker, int units);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a parallel work handler.
|
||||||
*
|
*
|
||||||
* @param func The callback that will be called from threads to process one unit.
|
* This is a compatibility constructor for older code, use the constructor with ParallelWorker instead.
|
||||||
* @param units Number of units to handle.
|
|
||||||
* @param data Custom data that will be passed to the callback.
|
|
||||||
* @return The newly allocated handler.
|
|
||||||
*/
|
*/
|
||||||
ParallelWork(ParallelUnitFunction func, int units, void* data);
|
ParallelWork(ParallelUnitFunction func, int units, void* data);
|
||||||
|
|
||||||
|
@ -52,15 +44,33 @@ public:
|
||||||
/**
|
/**
|
||||||
* Start working on the units.
|
* Start working on the units.
|
||||||
*
|
*
|
||||||
* @param workers Number of threads to spaws, -1 for an optimal number.
|
* @param threads Number of threads to spaws, -1 for an optimal number.
|
||||||
*/
|
*/
|
||||||
int perform(int workers=-1);
|
int perform(int thread_count=-1);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tell the threads to interrupt what they are doing.
|
||||||
|
*
|
||||||
|
* This will also call interrupt() on the worker.
|
||||||
|
*/
|
||||||
|
void interrupt();
|
||||||
|
|
||||||
|
private:
|
||||||
|
void returnThread(ParallelThread *thread);
|
||||||
|
|
||||||
|
private:
|
||||||
int units;
|
int units;
|
||||||
int running;
|
int running;
|
||||||
ParallelUnitFunction unit_function;
|
ParallelWorker *worker;
|
||||||
ParallelWorker workers[PARALLEL_MAX_THREADS];
|
bool worker_compat;
|
||||||
void* data;
|
|
||||||
|
int thread_count;
|
||||||
|
Mutex* mutex;
|
||||||
|
Semaphore* semaphore;
|
||||||
|
ParallelThread** threads;
|
||||||
|
ParallelThread** available;
|
||||||
|
int available_offset;
|
||||||
|
int available_length;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
15
src/system/ParallelWorker.cpp
Normal file
15
src/system/ParallelWorker.cpp
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
#include "ParallelWorker.h"
|
||||||
|
|
||||||
|
ParallelWorker::ParallelWorker()
|
||||||
|
{
|
||||||
|
interrupted = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ParallelWorker::~ParallelWorker()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void ParallelWorker::interrupt()
|
||||||
|
{
|
||||||
|
interrupted = true;
|
||||||
|
}
|
37
src/system/ParallelWorker.h
Normal file
37
src/system/ParallelWorker.h
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
#ifndef PARALLELWORKER_H
|
||||||
|
#define PARALLELWORKER_H
|
||||||
|
|
||||||
|
#include "system_global.h"
|
||||||
|
|
||||||
|
namespace paysages {
|
||||||
|
namespace system {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Worker that can be used by the ParallelWork object to perform tasks in several threads.
|
||||||
|
*/
|
||||||
|
class SYSTEMSHARED_EXPORT ParallelWorker
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ParallelWorker();
|
||||||
|
virtual ~ParallelWorker();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Abstract method to reimplement to process a work unit.
|
||||||
|
*
|
||||||
|
* This method will be called from any thread in the thread pool used by the ParallelWork.
|
||||||
|
*/
|
||||||
|
virtual void processParallelUnit(int unit) = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Method to reimplement to know when to interrupt the processing of units.
|
||||||
|
*/
|
||||||
|
virtual void interrupt();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
bool interrupted;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // PARALLELWORKER_H
|
6
src/system/Semaphore.cpp
Normal file
6
src/system/Semaphore.cpp
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
#include "Semaphore.h"
|
||||||
|
|
||||||
|
Semaphore::Semaphore(int resources):
|
||||||
|
QSemaphore(resources)
|
||||||
|
{
|
||||||
|
}
|
25
src/system/Semaphore.h
Normal file
25
src/system/Semaphore.h
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
#ifndef SEMAPHORE_H
|
||||||
|
#define SEMAPHORE_H
|
||||||
|
|
||||||
|
#include "system_global.h"
|
||||||
|
|
||||||
|
#include <QSemaphore>
|
||||||
|
|
||||||
|
namespace paysages
|
||||||
|
{
|
||||||
|
namespace system
|
||||||
|
{
|
||||||
|
|
||||||
|
class Semaphore: private QSemaphore
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Semaphore(int resources);
|
||||||
|
|
||||||
|
inline void acquire() {QSemaphore::acquire();}
|
||||||
|
inline void release() {QSemaphore::release();}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // SEMAPHORE_H
|
|
@ -29,7 +29,7 @@ public:
|
||||||
* \brief Start the thread
|
* \brief Start the thread
|
||||||
* \param data User data to pass to the threaded function
|
* \param data User data to pass to the threaded function
|
||||||
*/
|
*/
|
||||||
void start(void* data);
|
void start(void* data=0);
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* \brief Wait for the thread to end, and collect its result.
|
* \brief Wait for the thread to end, and collect its result.
|
||||||
|
|
|
@ -21,11 +21,13 @@ SOURCES += \
|
||||||
RandomGenerator.cpp \
|
RandomGenerator.cpp \
|
||||||
Memory.cpp \
|
Memory.cpp \
|
||||||
ParallelWork.cpp \
|
ParallelWork.cpp \
|
||||||
ParallelQueue.cpp \
|
|
||||||
CacheFile.cpp \
|
CacheFile.cpp \
|
||||||
PictureWriter.cpp \
|
PictureWriter.cpp \
|
||||||
Logs.cpp \
|
Logs.cpp \
|
||||||
ParallelPool.cpp
|
ParallelPool.cpp \
|
||||||
|
ParallelWorker.cpp \
|
||||||
|
Semaphore.cpp \
|
||||||
|
FileSystem.cpp
|
||||||
|
|
||||||
HEADERS += \
|
HEADERS += \
|
||||||
system_global.h \
|
system_global.h \
|
||||||
|
@ -36,11 +38,13 @@ HEADERS += \
|
||||||
RandomGenerator.h \
|
RandomGenerator.h \
|
||||||
Memory.h \
|
Memory.h \
|
||||||
ParallelWork.h \
|
ParallelWork.h \
|
||||||
ParallelQueue.h \
|
|
||||||
CacheFile.h \
|
CacheFile.h \
|
||||||
PictureWriter.h \
|
PictureWriter.h \
|
||||||
Logs.h \
|
Logs.h \
|
||||||
ParallelPool.h
|
ParallelPool.h \
|
||||||
|
ParallelWorker.h \
|
||||||
|
Semaphore.h \
|
||||||
|
FileSystem.h
|
||||||
|
|
||||||
unix:!symbian {
|
unix:!symbian {
|
||||||
maemo5 {
|
maemo5 {
|
||||||
|
|
|
@ -15,11 +15,13 @@
|
||||||
namespace paysages {
|
namespace paysages {
|
||||||
namespace system {
|
namespace system {
|
||||||
class PackStream;
|
class PackStream;
|
||||||
class ParallelQueue;
|
|
||||||
class ParallelWork;
|
class ParallelWork;
|
||||||
class ParallelPool;
|
class ParallelPool;
|
||||||
|
class ParallelWorker;
|
||||||
class Thread;
|
class Thread;
|
||||||
class Mutex;
|
class Mutex;
|
||||||
|
class Semaphore;
|
||||||
|
class PictureWriter;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
using namespace paysages::system;
|
using namespace paysages::system;
|
||||||
|
|
|
@ -1,83 +0,0 @@
|
||||||
#include "BaseTestCase.h"
|
|
||||||
|
|
||||||
#include "CameraDefinition.h"
|
|
||||||
#include "SoftwareRenderer.h"
|
|
||||||
#include "AtmosphereDefinition.h"
|
|
||||||
#include "AtmosphereRenderer.h"
|
|
||||||
#include "AtmosphereResult.h"
|
|
||||||
#include "Scenery.h"
|
|
||||||
#include "System.h"
|
|
||||||
|
|
||||||
#define OUTPUT_WIDTH 400
|
|
||||||
#define OUTPUT_HEIGHT 300
|
|
||||||
|
|
||||||
static Color _postProcessFragment(SoftwareRenderer* renderer, const Vector3 &location, void*)
|
|
||||||
{
|
|
||||||
return renderer->getAtmosphereRenderer()->applyAerialPerspective(location, COLOR_BLACK).final;
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(Bruneton, AerialPerspective1)
|
|
||||||
{
|
|
||||||
#ifndef TESTS_FULL
|
|
||||||
return;
|
|
||||||
#endif
|
|
||||||
Scenery scenery;
|
|
||||||
SoftwareRenderer renderer(&scenery);
|
|
||||||
renderer.render_width = 800;
|
|
||||||
renderer.render_height = 600;
|
|
||||||
renderer.render_quality = 1;
|
|
||||||
|
|
||||||
renderer.render_camera->setLocation(VECTOR_ZERO);
|
|
||||||
renderer.render_camera->setTarget(VECTOR_EAST);
|
|
||||||
renderer.render_camera->setRenderSize(renderer.render_width, renderer.render_height);
|
|
||||||
|
|
||||||
RenderArea::RenderParams params = {renderer.render_width, renderer.render_height, 1, 1};
|
|
||||||
renderer.render_area->setParams(params);
|
|
||||||
renderer.render_area->setBackgroundColor(COLOR_BLACK);
|
|
||||||
renderer.render_area->clear();
|
|
||||||
|
|
||||||
renderer.pushQuad(Vector3(50.0, -10.0, -50.0), Vector3(1.0, -10.0, -50.0), Vector3(1.0, -10.0, 50.0), Vector3(50.0, -10.0, 50.0), _postProcessFragment, NULL);
|
|
||||||
renderer.pushQuad(Vector3(10.0, -10.0, -10.0), Vector3(10.0, -10.0, -5.0), Vector3(10.0, 50.0, -5.0), Vector3(10.0, 50.0, -10.0), _postProcessFragment, NULL);
|
|
||||||
renderer.pushQuad(Vector3(15.0, -10.0, -5.0), Vector3(15.0, -10.0, 0.0), Vector3(15.0, 50.0, 0.0), Vector3(15.0, 50.0, -5.0), _postProcessFragment, NULL);
|
|
||||||
renderer.pushQuad(Vector3(20.0, -10.0, 5.0), Vector3(20.0, -10.0, 10.0), Vector3(20.0, 50.0, 10.0), Vector3(20.0, 50.0, 5.0), _postProcessFragment, NULL);
|
|
||||||
renderer.pushQuad(Vector3(30.0, -10.0, 25.0), Vector3(30.0, -10.0, 30.0), Vector3(30.0, 50.0, 30.0), Vector3(30.0, 50.0, 25.0), _postProcessFragment, NULL);
|
|
||||||
renderer.render_area->postProcess(System::getCoreCount());
|
|
||||||
|
|
||||||
renderer.render_area->saveToFile("./output/test_bruneton_perspective.png");
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(Bruneton, AerialPerspective2)
|
|
||||||
{
|
|
||||||
#ifndef TESTS_FULL
|
|
||||||
return;
|
|
||||||
#endif
|
|
||||||
Scenery scenery;
|
|
||||||
|
|
||||||
AtmosphereDefinition* atmo = scenery.getAtmosphere();
|
|
||||||
atmo->hour = 6;
|
|
||||||
atmo->minute = 30;
|
|
||||||
atmo->validate();
|
|
||||||
|
|
||||||
SoftwareRenderer renderer(&scenery);
|
|
||||||
renderer.render_width = 800;
|
|
||||||
renderer.render_height = 600;
|
|
||||||
renderer.render_quality = 1;
|
|
||||||
|
|
||||||
renderer.render_camera->setLocation(VECTOR_ZERO);
|
|
||||||
renderer.render_camera->setTarget(VECTOR_EAST);
|
|
||||||
renderer.render_camera->setRenderSize(renderer.render_width, renderer.render_height);
|
|
||||||
|
|
||||||
RenderArea::RenderParams params = {renderer.render_width, renderer.render_height, 1, 1};
|
|
||||||
renderer.render_area->setParams(params);
|
|
||||||
renderer.render_area->setBackgroundColor(COLOR_BLACK);
|
|
||||||
renderer.render_area->clear();
|
|
||||||
|
|
||||||
renderer.pushQuad(Vector3(50.0, -10.0, -50.0), Vector3(1.0, -10.0, -50.0), Vector3(1.0, -10.0, 50.0), Vector3(50.0, -10.0, 50.0), _postProcessFragment, NULL);
|
|
||||||
renderer.pushQuad(Vector3(10.0, -10.0, -10.0), Vector3(10.0, -10.0, -5.0), Vector3(10.0, 50.0, -5.0), Vector3(10.0, 50.0, -10.0), _postProcessFragment, NULL);
|
|
||||||
renderer.pushQuad(Vector3(15.0, -10.0, -5.0), Vector3(15.0, -10.0, 0.0), Vector3(15.0, 50.0, 0.0), Vector3(15.0, 50.0, -5.0), _postProcessFragment, NULL);
|
|
||||||
renderer.pushQuad(Vector3(20.0, -10.0, 5.0), Vector3(20.0, -10.0, 10.0), Vector3(20.0, 50.0, 10.0), Vector3(20.0, 50.0, 5.0), _postProcessFragment, NULL);
|
|
||||||
renderer.pushQuad(Vector3(30.0, -10.0, 25.0), Vector3(30.0, -10.0, 30.0), Vector3(30.0, 50.0, 30.0), Vector3(30.0, 50.0, 25.0), _postProcessFragment, NULL);
|
|
||||||
renderer.render_area->postProcess(System::getCoreCount());
|
|
||||||
|
|
||||||
renderer.render_area->saveToFile("./output/test_bruneton_perspective1.png");
|
|
||||||
}
|
|
110
src/tests/CanvasPortion_Test.cpp
Normal file
110
src/tests/CanvasPortion_Test.cpp
Normal file
|
@ -0,0 +1,110 @@
|
||||||
|
#include "BaseTestCase.h"
|
||||||
|
|
||||||
|
#include "CanvasPortion.h"
|
||||||
|
#include "CanvasFragment.h"
|
||||||
|
|
||||||
|
TEST(CanvasPortion, setSize)
|
||||||
|
{
|
||||||
|
CanvasPortion portion;
|
||||||
|
|
||||||
|
portion.setSize(150, 30);
|
||||||
|
|
||||||
|
EXPECT_EQ(150, portion.getWidth());
|
||||||
|
EXPECT_EQ(30, portion.getHeight());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(CanvasPortion, pushFragment)
|
||||||
|
{
|
||||||
|
CanvasPortion portion;
|
||||||
|
CanvasFragment pushed;
|
||||||
|
const CanvasFragment *got;
|
||||||
|
int count;
|
||||||
|
|
||||||
|
portion.setSize(50, 50);
|
||||||
|
portion.preparePixels();
|
||||||
|
|
||||||
|
portion.pushFragment(10, 15, pushed);
|
||||||
|
|
||||||
|
count = portion.getFragmentCount(10, 14);
|
||||||
|
ASSERT_EQ(0, count);
|
||||||
|
got = portion.getFrontFragment(10, 14);
|
||||||
|
ASSERT_FALSE(got);
|
||||||
|
|
||||||
|
count = portion.getFragmentCount(10, 15);
|
||||||
|
ASSERT_EQ(1, count);
|
||||||
|
got = portion.getFrontFragment(10, 15);
|
||||||
|
ASSERT_TRUE(got);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(CanvasPortion, pushFragment_opaque)
|
||||||
|
{
|
||||||
|
CanvasPortion portion;
|
||||||
|
CanvasFragment pushed;
|
||||||
|
|
||||||
|
portion.setSize(10, 10);
|
||||||
|
portion.preparePixels();
|
||||||
|
|
||||||
|
pushed = CanvasFragment(2.0, VECTOR_ZERO, 0);
|
||||||
|
portion.pushFragment(2, 2, pushed);
|
||||||
|
|
||||||
|
ASSERT_EQ(1, portion.getFragmentCount(2, 2));
|
||||||
|
EXPECT_DOUBLE_EQ(2.0, portion.getFrontFragment(2, 2)->getZ());
|
||||||
|
|
||||||
|
pushed = CanvasFragment(1.0, VECTOR_ZERO, 0);
|
||||||
|
portion.pushFragment(2, 2, pushed);
|
||||||
|
|
||||||
|
ASSERT_EQ(1, portion.getFragmentCount(2, 2));
|
||||||
|
EXPECT_DOUBLE_EQ(2.0, portion.getFrontFragment(2, 2)->getZ());
|
||||||
|
|
||||||
|
pushed = CanvasFragment(4.0, VECTOR_ZERO, 0);
|
||||||
|
portion.pushFragment(2, 2, pushed);
|
||||||
|
|
||||||
|
ASSERT_EQ(1, portion.getFragmentCount(2, 2));
|
||||||
|
EXPECT_DOUBLE_EQ(4.0, portion.getFrontFragment(2, 2)->getZ());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(CanvasPortion, pushFragment_transparent)
|
||||||
|
{
|
||||||
|
CanvasPortion portion;
|
||||||
|
CanvasFragment pushed;
|
||||||
|
|
||||||
|
portion.setSize(10, 10);
|
||||||
|
portion.preparePixels();
|
||||||
|
|
||||||
|
pushed = CanvasFragment(2.0, VECTOR_ZERO, 0, false);
|
||||||
|
portion.pushFragment(2, 2, pushed);
|
||||||
|
|
||||||
|
ASSERT_EQ(1, portion.getFragmentCount(2, 2));
|
||||||
|
EXPECT_DOUBLE_EQ(2.0, portion.getFrontFragment(2, 2)->getZ());
|
||||||
|
|
||||||
|
pushed = CanvasFragment(3.0, VECTOR_ZERO, 0, true);
|
||||||
|
portion.pushFragment(2, 2, pushed);
|
||||||
|
|
||||||
|
ASSERT_EQ(1, portion.getFragmentCount(2, 2));
|
||||||
|
EXPECT_DOUBLE_EQ(3.0, portion.getFrontFragment(2, 2)->getZ());
|
||||||
|
|
||||||
|
pushed = CanvasFragment(4.0, VECTOR_ZERO, 0, false);
|
||||||
|
portion.pushFragment(2, 2, pushed);
|
||||||
|
|
||||||
|
ASSERT_EQ(2, portion.getFragmentCount(2, 2));
|
||||||
|
EXPECT_DOUBLE_EQ(4.0, portion.getFrontFragment(2, 2)->getZ());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(CanvasPortion, clear)
|
||||||
|
{
|
||||||
|
CanvasPortion portion;
|
||||||
|
CanvasFragment fragment;
|
||||||
|
|
||||||
|
portion.setSize(10, 10);
|
||||||
|
portion.preparePixels();
|
||||||
|
|
||||||
|
portion.pushFragment(5, 5, fragment);
|
||||||
|
|
||||||
|
EXPECT_EQ(0, portion.getFragmentCount(4, 5));
|
||||||
|
EXPECT_EQ(1, portion.getFragmentCount(5, 5));
|
||||||
|
|
||||||
|
portion.clear();
|
||||||
|
|
||||||
|
EXPECT_EQ(0, portion.getFragmentCount(4, 5));
|
||||||
|
EXPECT_EQ(0, portion.getFragmentCount(5, 5));
|
||||||
|
}
|
50
src/tests/CanvasPreview_Test.cpp
Normal file
50
src/tests/CanvasPreview_Test.cpp
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
#include "BaseTestCase.h"
|
||||||
|
|
||||||
|
#include "CanvasPreview.h"
|
||||||
|
#include "Color.h"
|
||||||
|
|
||||||
|
TEST(CanvasPreview, setSize)
|
||||||
|
{
|
||||||
|
CanvasPreview preview;
|
||||||
|
|
||||||
|
preview.setSize(800, 600, 400, 300);
|
||||||
|
|
||||||
|
EXPECT_EQ(400, preview.getWidth());
|
||||||
|
EXPECT_EQ(300, preview.getHeight());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(CanvasPreview, pushPixel_accumulate)
|
||||||
|
{
|
||||||
|
CanvasPreview preview;
|
||||||
|
Color col;
|
||||||
|
preview.setSize(800, 600, 400, 300);
|
||||||
|
|
||||||
|
col = preview.getFinalPixel(0, 0);
|
||||||
|
EXPECT_COLOR_RGBA(col, 0.0, 0.0, 0.0, 1.0);
|
||||||
|
|
||||||
|
preview.pushPixel(0, 0, COLOR_BLACK, COLOR_RED);
|
||||||
|
col = preview.getFinalPixel(0, 0);
|
||||||
|
EXPECT_COLOR_RGBA(col, 0.25, 0.0, 0.0, 1.0);
|
||||||
|
|
||||||
|
preview.pushPixel(0, 1, COLOR_BLACK, COLOR_BLUE);
|
||||||
|
col = preview.getFinalPixel(0, 0);
|
||||||
|
EXPECT_COLOR_RGBA(col, 0.25, 0.0, 0.25, 1.0);
|
||||||
|
|
||||||
|
preview.pushPixel(0, 2, COLOR_BLACK, COLOR_BLUE);
|
||||||
|
col = preview.getFinalPixel(0, 0);
|
||||||
|
EXPECT_COLOR_RGBA(col, 0.25, 0.0, 0.25, 1.0);
|
||||||
|
col = preview.getFinalPixel(0, 1);
|
||||||
|
EXPECT_COLOR_RGBA(col, 0.0, 0.0, 0.25, 1.0);
|
||||||
|
|
||||||
|
preview.pushPixel(0, 1, COLOR_BLUE, COLOR_GREEN);
|
||||||
|
col = preview.getFinalPixel(0, 0);
|
||||||
|
EXPECT_COLOR_RGBA(col, 0.25, 0.25, 0.0, 1.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(CanvasPreview, pushPixel_border)
|
||||||
|
{
|
||||||
|
CanvasPreview preview;
|
||||||
|
preview.setSize(759, 237, 9, 14);
|
||||||
|
|
||||||
|
preview.pushPixel(759, 237, COLOR_BLACK, COLOR_RED);
|
||||||
|
}
|
42
src/tests/Canvas_Test.cpp
Normal file
42
src/tests/Canvas_Test.cpp
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
#include "BaseTestCase.h"
|
||||||
|
|
||||||
|
#include "Canvas.h"
|
||||||
|
#include "CanvasPortion.h"
|
||||||
|
#include "CanvasPreview.h"
|
||||||
|
|
||||||
|
static void checkPortion(Canvas &canvas, int x, int y, int width, int height)
|
||||||
|
{
|
||||||
|
ASSERT_LT(x, canvas.getHorizontalPortionCount());
|
||||||
|
ASSERT_LT(y, canvas.getVerticalPortionCount());
|
||||||
|
|
||||||
|
CanvasPortion* portion = canvas.at(x, y);
|
||||||
|
|
||||||
|
EXPECT_EQ(width, portion->getWidth());
|
||||||
|
EXPECT_EQ(height, portion->getHeight());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Canvas, SizingAndCutting)
|
||||||
|
{
|
||||||
|
Canvas canvas;
|
||||||
|
|
||||||
|
canvas.setSize(200, 100);
|
||||||
|
EXPECT_EQ(200, canvas.getWidth());
|
||||||
|
EXPECT_EQ(100, canvas.getHeight());
|
||||||
|
EXPECT_EQ(200, canvas.getPreview()->getWidth());
|
||||||
|
EXPECT_EQ(100, canvas.getPreview()->getHeight());
|
||||||
|
ASSERT_EQ(1, canvas.getHorizontalPortionCount());
|
||||||
|
ASSERT_EQ(1, canvas.getVerticalPortionCount());
|
||||||
|
checkPortion(canvas, 0, 0, 200, 100);
|
||||||
|
|
||||||
|
canvas.setSize(1100, 901);
|
||||||
|
EXPECT_EQ(1100, canvas.getWidth());
|
||||||
|
EXPECT_EQ(901, canvas.getHeight());
|
||||||
|
EXPECT_EQ(550, canvas.getPreview()->getWidth());
|
||||||
|
EXPECT_EQ(450, canvas.getPreview()->getHeight());
|
||||||
|
ASSERT_EQ(2, canvas.getHorizontalPortionCount());
|
||||||
|
ASSERT_EQ(2, canvas.getVerticalPortionCount());
|
||||||
|
checkPortion(canvas, 0, 0, 550, 450);
|
||||||
|
checkPortion(canvas, 0, 1, 550, 451);
|
||||||
|
checkPortion(canvas, 1, 0, 550, 450);
|
||||||
|
checkPortion(canvas, 1, 1, 550, 451);
|
||||||
|
}
|
|
@ -49,3 +49,46 @@ TEST(PackStream, All)
|
||||||
}
|
}
|
||||||
delete stream;
|
delete stream;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(PackStream, Skip)
|
||||||
|
{
|
||||||
|
PackStream* stream;
|
||||||
|
int i1=1, i2=2, i3=3;
|
||||||
|
double d1=1.1, d2=2.2;
|
||||||
|
|
||||||
|
stream = new PackStream();
|
||||||
|
stream->bindToFile("/tmp/test_paysages_pack", true);
|
||||||
|
|
||||||
|
stream->write(&i1);
|
||||||
|
stream->write(&i2);
|
||||||
|
stream->write(&d1);
|
||||||
|
stream->write(&d2);
|
||||||
|
stream->write(&i3);
|
||||||
|
|
||||||
|
delete stream;
|
||||||
|
|
||||||
|
int resi;
|
||||||
|
double resd;
|
||||||
|
|
||||||
|
stream = new PackStream();
|
||||||
|
stream->bindToFile("/tmp/test_paysages_pack");
|
||||||
|
stream->skip(i1, 1);
|
||||||
|
stream->read(&resi);
|
||||||
|
EXPECT_EQ(2, resi);
|
||||||
|
delete stream;
|
||||||
|
|
||||||
|
stream = new PackStream();
|
||||||
|
stream->bindToFile("/tmp/test_paysages_pack");
|
||||||
|
stream->skip(i1, 2);
|
||||||
|
stream->read(&resd);
|
||||||
|
EXPECT_DOUBLE_EQ(1.1, resd);
|
||||||
|
delete stream;
|
||||||
|
|
||||||
|
stream = new PackStream();
|
||||||
|
stream->bindToFile("/tmp/test_paysages_pack");
|
||||||
|
stream->skip(i1, 2);
|
||||||
|
stream->skip(d1, 2);
|
||||||
|
stream->read(&resi);
|
||||||
|
EXPECT_EQ(3, resi);
|
||||||
|
delete stream;
|
||||||
|
}
|
||||||
|
|
|
@ -1,90 +0,0 @@
|
||||||
#include "BaseTestCase.h"
|
|
||||||
|
|
||||||
#include <cmath>
|
|
||||||
#include "SoftwareRenderer.h"
|
|
||||||
#include "CameraDefinition.h"
|
|
||||||
#include "ColorProfile.h"
|
|
||||||
#include "System.h"
|
|
||||||
|
|
||||||
static Color _postProcessFragment(SoftwareRenderer*, const Vector3 &location, void*)
|
|
||||||
{
|
|
||||||
/* Checker-board */
|
|
||||||
double x = fmod(location.x, 0.2);
|
|
||||||
double z = fmod(location.z, 0.2);
|
|
||||||
if (x < 0.0)
|
|
||||||
{
|
|
||||||
x = 0.2 + x;
|
|
||||||
}
|
|
||||||
if (z < 0.0)
|
|
||||||
{
|
|
||||||
z = 0.2 + z;
|
|
||||||
}
|
|
||||||
if ((x > 0.1) ^ (z > 0.1))
|
|
||||||
{
|
|
||||||
return COLOR_WHITE;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return COLOR_BLACK;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void _render_quad_checker(SoftwareRenderer &renderer)
|
|
||||||
{
|
|
||||||
renderer.render_width = 800;
|
|
||||||
renderer.render_height = 600;
|
|
||||||
renderer.render_quality = 1;
|
|
||||||
renderer.render_area->setToneMapping(ColorProfile(ColorProfile::TONE_MAPPING_CLAMP, 0.0));
|
|
||||||
|
|
||||||
renderer.render_camera->setRenderSize(renderer.render_width, renderer.render_height);
|
|
||||||
renderer.render_camera->setFov(1.57);
|
|
||||||
|
|
||||||
RenderArea::RenderParams params = {renderer.render_width, renderer.render_height, 1, 1};
|
|
||||||
renderer.render_area->setParams(params);
|
|
||||||
|
|
||||||
renderer.render_area->setBackgroundColor(COLOR_BLUE);
|
|
||||||
renderer.render_area->clear();
|
|
||||||
|
|
||||||
renderer.pushQuad(Vector3(-1.0, 0.0, 1.0), Vector3(-1.0, 0.0, -1.0), Vector3(1.0, 0.0, -1.0), Vector3(1.0, 0.0, 1.0), _postProcessFragment, NULL);
|
|
||||||
renderer.render_area->postProcess(System::getCoreCount());
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(Render, quad)
|
|
||||||
{
|
|
||||||
#ifndef TESTS_FULL
|
|
||||||
return;
|
|
||||||
#endif
|
|
||||||
SoftwareRenderer renderer;
|
|
||||||
|
|
||||||
renderer.render_camera->setLocationCoords(0.0, 0.5, 2.0);
|
|
||||||
renderer.render_camera->setTargetCoords(0.0, 0.5, 0.0);
|
|
||||||
|
|
||||||
_render_quad_checker(renderer);
|
|
||||||
|
|
||||||
Color col;
|
|
||||||
col = renderer.render_area->getPixel(399, 599 - 435);
|
|
||||||
ASSERT_COLOR_RGBA(col, 1.0, 1.0, 1.0, 1.0);
|
|
||||||
col = renderer.render_area->getPixel(399, 599 - 436);
|
|
||||||
ASSERT_COLOR_RGBA(col, 0.0, 0.0, 0.0, 1.0);
|
|
||||||
col = renderer.render_area->getPixel(400, 599 - 435);
|
|
||||||
ASSERT_COLOR_RGBA(col, 0.0, 0.0, 0.0, 1.0);
|
|
||||||
col = renderer.render_area->getPixel(400, 599 - 436);
|
|
||||||
ASSERT_COLOR_RGBA(col, 1.0, 1.0, 1.0, 1.0);
|
|
||||||
|
|
||||||
renderer.render_area->saveToFile("./output/test_render_quad.png");
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(Render, quad_cut)
|
|
||||||
{
|
|
||||||
#ifndef TESTS_FULL
|
|
||||||
return;
|
|
||||||
#endif
|
|
||||||
SoftwareRenderer renderer;
|
|
||||||
|
|
||||||
renderer.render_camera->setLocationCoords(0.8, 0.7, 1.0);
|
|
||||||
renderer.render_camera->setTargetCoords(0.0, 0.0, -0.5);
|
|
||||||
|
|
||||||
_render_quad_checker(renderer);
|
|
||||||
|
|
||||||
renderer.render_area->saveToFile("./output/test_render_quad_cut.png");
|
|
||||||
}
|
|
|
@ -10,16 +10,17 @@ SOURCES += main.cpp \
|
||||||
Layers_Test.cpp \
|
Layers_Test.cpp \
|
||||||
PackStream_Test.cpp \
|
PackStream_Test.cpp \
|
||||||
NoiseGenerator_Test.cpp \
|
NoiseGenerator_Test.cpp \
|
||||||
Render_Test.cpp \
|
|
||||||
TerrainPainting_Test.cpp \
|
TerrainPainting_Test.cpp \
|
||||||
Zone_Test.cpp \
|
Zone_Test.cpp \
|
||||||
Euclid_Test.cpp \
|
Euclid_Test.cpp \
|
||||||
Bruneton_Test.cpp \
|
|
||||||
Camera_Test.cpp \
|
Camera_Test.cpp \
|
||||||
Clouds_Test.cpp \
|
Clouds_Test.cpp \
|
||||||
FluidMediumManager_Test.cpp \
|
FluidMediumManager_Test.cpp \
|
||||||
VertexArray_Test.cpp \
|
VertexArray_Test.cpp \
|
||||||
FractalNoise_Test.cpp
|
FractalNoise_Test.cpp \
|
||||||
|
Canvas_Test.cpp \
|
||||||
|
CanvasPortion_Test.cpp \
|
||||||
|
CanvasPreview_Test.cpp
|
||||||
|
|
||||||
HEADERS += \
|
HEADERS += \
|
||||||
BaseTestCase.h
|
BaseTestCase.h
|
||||||
|
|
Loading…
Reference in a new issue