Merge branch 'master' into quick_ui
Conflicts: src/paysages.pro
This commit is contained in:
commit
078bab62fc
255 changed files with 3295 additions and 70841 deletions
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
|
@ -0,0 +1,3 @@
|
|||
[submodule "src/tests/googletest/sources"]
|
||||
path = src/tests/googletest/sources
|
||||
url = https://github.com/thunderk/googletest.git
|
8
Makefile
8
Makefile
|
@ -43,17 +43,17 @@ else
|
|||
endif
|
||||
|
||||
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
|
||||
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
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
package:build
|
||||
|
|
56
README.md
56
README.md
|
@ -1,35 +1,61 @@
|
|||
Paysages 3D
|
||||
===========
|
||||
# Paysages 3D
|
||||
|
||||
[![Build Status](https://travis-ci.org/thunderk/paysages3d.png)](https://travis-ci.org/thunderk/paysages3d)
|
||||
|
||||
About
|
||||
-----
|
||||
|
||||
## About
|
||||
|
||||
[Paysages 3D](http://www.paysages3d.com) is a work-in-progress landscape generator, modeler and renderer software.
|
||||
|
||||
It is written in C++ 11, using Qt 5.2 as only dependency.
|
||||
It is written in C++ 11, using Qt 5.2 as only dependency (and Google Test for unit testing).
|
||||
|
||||
[![Screenshot 1](data/screenshots/small1.jpg)](data/screenshots/large1.jpg) [![Screenshot 2](data/screenshots/small2.jpg)](data/screenshots/large2.jpg) [![Screenshot 3](data/screenshots/small3.jpg)](data/screenshots/large3.jpg)
|
||||
|
||||
Build/Run
|
||||
---------
|
||||
|
||||
At the moment, the build system is only well tested using the latest [QtSDK](http://qt-project.org/downloads) installation, and [QtCreator](http://qt-project.org/downloads#qt-creator). Later work will remove this dependency.
|
||||
## Build/Run
|
||||
|
||||
Simply open the *src/paysages.pro* file in QtCreator, and make a shadow build.
|
||||
### Using QtSDK (All platforms)
|
||||
|
||||
You can then run the compiled program **from the top project directory** (the one that contains *src* and *data*).
|
||||
Download and install the latest [QtSDK](http://qt-project.org/downloads).
|
||||
|
||||
Licensing
|
||||
---------
|
||||
Launch QtCreator, and open the project file *src/paysages.pro*.
|
||||
|
||||
Go to the "Projects" tab to edit the directory in which the executable will be run, and set **the top project directory** (the one that contains *src* and *data*). If you don't do this, some data files will not be found.
|
||||
|
||||
### Using a packaged Qt (Linux)
|
||||
|
||||
You need at least the 5.2 packaged version of Qt, with qMake and QtCore, QtGui and QtOpengl modules.
|
||||
|
||||
On Ubuntu/Mint:
|
||||
|
||||
sudo apt-get install qt5-qmake libqt5core5a libqt5gui5 libqt5widgets5 libqt5opengl5-dev
|
||||
|
||||
Then to build and run the software:
|
||||
|
||||
make run
|
||||
|
||||
### Unit tests
|
||||
|
||||
Running the unit tests needs the Google Test framework:
|
||||
|
||||
git submodule init
|
||||
git submodule update
|
||||
|
||||
Then build and run the tests:
|
||||
|
||||
make tests
|
||||
|
||||
If you're using QtCreator, you can run the "tests" executable, not forgetting to change the directory in which the executable is run.
|
||||
|
||||
|
||||
## Licensing
|
||||
|
||||
The source code is subject to the terms of the [Mozilla Public License, v. 2.0](http://mozilla.org/MPL/2.0/). Read the full terms in the LICENSE file.
|
||||
|
||||
Credits
|
||||
-------
|
||||
|
||||
## Credits
|
||||
|
||||
* [Michaël Lemaire](http://thunderk.net) - Main developer
|
||||
* [Eric Bruneton and Fabrice Neyet](http://www-evasion.imag.fr/Membres/Eric.Bruneton/) - Publication and source code from *Precomputed Atmospheric Scattering (2008)*
|
||||
* [Qt](http://qt-project.org/) - Library in use
|
||||
* [GoogleTest](https://code.google.com/p/googletest/) - Included library
|
||||
* [GoogleTest](https://code.google.com/p/googletest/) - Used for unit testing
|
||||
|
|
|
@ -45,7 +45,7 @@ void NoiseGenerator::save(PackStream* stream)
|
|||
{
|
||||
NoiseLevel* level = levels + x;
|
||||
|
||||
stream->write(&level->wavelength);
|
||||
stream->write(&level->frequency);
|
||||
stream->write(&level->amplitude);
|
||||
stream->write(&level->minvalue);
|
||||
}
|
||||
|
@ -69,7 +69,7 @@ void NoiseGenerator::load(PackStream* stream)
|
|||
{
|
||||
NoiseLevel* level = levels + x;
|
||||
|
||||
stream->read(&level->wavelength);
|
||||
stream->read(&level->frequency);
|
||||
stream->read(&level->amplitude);
|
||||
stream->read(&level->minvalue);
|
||||
}
|
||||
|
@ -215,7 +215,7 @@ void NoiseGenerator::addLevelSimple(double scaling, double minvalue, double maxv
|
|||
{
|
||||
NoiseLevel level;
|
||||
|
||||
level.wavelength = scaling;
|
||||
level.frequency = 1.0 / scaling;
|
||||
level.minvalue = minvalue;
|
||||
level.amplitude = maxvalue - minvalue;
|
||||
|
||||
|
@ -230,7 +230,7 @@ void NoiseGenerator::addLevels(int level_count, NoiseLevel start_level, double s
|
|||
{
|
||||
addLevel(start_level);
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
@ -239,7 +239,7 @@ void NoiseGenerator::addLevelsSimple(int level_count, double scaling, double min
|
|||
{
|
||||
NoiseLevel level;
|
||||
|
||||
level.wavelength = scaling;
|
||||
level.frequency = 1.0 / scaling;
|
||||
level.minvalue = minvalue;
|
||||
level.amplitude = maxvalue - minvalue;
|
||||
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;
|
||||
|
||||
level.wavelength = scaling;
|
||||
level.frequency = 1.0 / scaling;
|
||||
level.minvalue = minvalue;
|
||||
level.amplitude = maxvalue - minvalue;
|
||||
|
||||
|
@ -313,7 +313,7 @@ void NoiseGenerator::normalizeAmplitude(double minvalue, double maxvalue, int ad
|
|||
levels[level].amplitude *= factor;
|
||||
if (adjust_scaling)
|
||||
{
|
||||
levels[level].wavelength *= factor;
|
||||
levels[level].frequency /= 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)
|
||||
{
|
||||
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)
|
||||
|
@ -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)
|
||||
{
|
||||
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)
|
||||
|
@ -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)
|
||||
{
|
||||
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)
|
||||
|
|
|
@ -28,7 +28,7 @@ public:
|
|||
|
||||
typedef struct
|
||||
{
|
||||
double wavelength;
|
||||
double frequency;
|
||||
double amplitude;
|
||||
double minvalue;
|
||||
} NoiseLevel;
|
||||
|
|
|
@ -90,11 +90,14 @@ void CameraDefinition::validate()
|
|||
|
||||
projector = mperspective.mult(Matrix4::newLookAt(location, target, up));
|
||||
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
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
|
@ -85,6 +85,8 @@ private:
|
|||
Matrix4 projector;
|
||||
Matrix4 unprojector;
|
||||
Matrix4 unperspective;
|
||||
double inv_x_factor;
|
||||
double inv_y_factor;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -5,16 +5,18 @@
|
|||
|
||||
#include "CameraDefinition.h"
|
||||
#include "AtmosphereDefinition.h"
|
||||
#include "SoftwareRenderer.h"
|
||||
#include "SoftwareCanvasRenderer.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);
|
||||
renderer->start(params);
|
||||
renderer->render();
|
||||
printf("\rSaving %s ... \n", outputpath);
|
||||
remove(outputpath);
|
||||
renderer->render_area->saveToFile(outputpath);
|
||||
renderer->saveToDisk(outputpath);
|
||||
}
|
||||
|
||||
void displayHelp()
|
||||
|
@ -43,9 +45,9 @@ void _previewUpdate(double progress)
|
|||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
SoftwareRenderer* renderer;
|
||||
SoftwareCanvasRenderer* renderer;
|
||||
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_nb_pictures = 1;
|
||||
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};
|
||||
camera->setLocation(camera->getLocation().add(step));
|
||||
|
||||
renderer = new SoftwareRenderer(scenery);
|
||||
renderer->setPreviewCallbacks(NULL, NULL, _previewUpdate);
|
||||
renderer = new SoftwareCanvasRenderer();
|
||||
renderer->setConfig(conf_render_params);
|
||||
renderer->setScenery(scenery);
|
||||
|
||||
if (outputcount >= conf_first_picture)
|
||||
{
|
||||
sprintf(outputpath, "output/pic%05d.png", outputcount);
|
||||
startRender(renderer, outputpath, conf_render_params);
|
||||
startRender(renderer, outputpath);
|
||||
}
|
||||
|
||||
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 "dialogrender.h"
|
||||
#include "dialogexplorer.h"
|
||||
#include "RenderConfig.h"
|
||||
#include "DesktopScenery.h"
|
||||
#include "BasePreview.h"
|
||||
#include "SoftwareRenderer.h"
|
||||
#include "SoftwareCanvasRenderer.h"
|
||||
#include "CameraDefinition.h"
|
||||
#include "tools.h"
|
||||
|
||||
|
@ -246,15 +247,16 @@ void FreeFormHelper::processExploreClicked()
|
|||
|
||||
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);
|
||||
|
||||
DialogRender* dialog = new DialogRender(_form_widget, &renderer);
|
||||
RenderArea::RenderParams params = {400, 300, 1, 3};
|
||||
dialog->startRender(params);
|
||||
|
||||
delete dialog;
|
||||
DialogRender dialog(_form_widget, &renderer);
|
||||
dialog.startRender();
|
||||
}
|
||||
|
||||
void FreeFormHelper::processDecimalChange(double value)
|
||||
|
|
|
@ -52,7 +52,9 @@ HEADERS += \
|
|||
lighting/SmallPreviewHues.h \
|
||||
textures/DialogTexturesLayer.h \
|
||||
desktop_global.h \
|
||||
DesktopScenery.h
|
||||
DesktopScenery.h \
|
||||
WidgetCanvas.h \
|
||||
WidgetPreviewCanvas.h
|
||||
|
||||
SOURCES += \
|
||||
terrain/widgetheightmap.cpp \
|
||||
|
@ -96,7 +98,9 @@ SOURCES += \
|
|||
lighting/SmallPreviewColor.cpp \
|
||||
lighting/SmallPreviewHues.cpp \
|
||||
textures/DialogTexturesLayer.cpp \
|
||||
DesktopScenery.cpp
|
||||
DesktopScenery.cpp \
|
||||
WidgetCanvas.cpp \
|
||||
WidgetPreviewCanvas.cpp
|
||||
|
||||
FORMS += \
|
||||
terrain/dialogterrainpainting.ui \
|
||||
|
|
|
@ -10,6 +10,9 @@ namespace paysages {
|
|||
namespace desktop {
|
||||
class BaseInput;
|
||||
class BaseForm;
|
||||
|
||||
class WidgetCanvas;
|
||||
class WidgetPreviewCanvas;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -282,7 +282,7 @@ void DialogNoise::addLevel()
|
|||
NoiseGenerator::NoiseLevel level;
|
||||
|
||||
level.amplitude = 0.1;
|
||||
level.wavelength = 0.1;
|
||||
level.frequency = 0.1;
|
||||
|
||||
_current->addLevel(level);
|
||||
|
||||
|
@ -330,7 +330,7 @@ void DialogNoise::levelChanged(int row)
|
|||
((PreviewLevel*)previewLevel)->setLevel(row);
|
||||
|
||||
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 ...
|
||||
}
|
||||
|
@ -345,7 +345,7 @@ void DialogNoise::heightChanged(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);
|
||||
previewLevel->redraw();
|
||||
previewTotal->redraw();
|
||||
|
|
|
@ -18,90 +18,45 @@
|
|||
#include <QComboBox>
|
||||
#include "tools.h"
|
||||
|
||||
#include "SoftwareRenderer.h"
|
||||
#include "Scenery.h"
|
||||
#include "ColorProfile.h"
|
||||
|
||||
static DialogRender* _current_dialog;
|
||||
|
||||
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);
|
||||
}
|
||||
#include "SoftwareCanvasRenderer.h"
|
||||
#include "WidgetPreviewCanvas.h"
|
||||
#include "Canvas.h"
|
||||
|
||||
class RenderThread:public QThread
|
||||
{
|
||||
public:
|
||||
RenderThread(DialogRender* dialog, SoftwareRenderer* renderer, RenderArea::RenderParams params):QThread()
|
||||
RenderThread(DialogRender* dialog, SoftwareCanvasRenderer* renderer):QThread()
|
||||
{
|
||||
_dialog = dialog;
|
||||
_renderer = renderer;
|
||||
_params = params;
|
||||
}
|
||||
void run()
|
||||
{
|
||||
_renderer->start(_params);
|
||||
_renderer->render();
|
||||
_dialog->tellRenderEnded();
|
||||
}
|
||||
private:
|
||||
DialogRender* _dialog;
|
||||
SoftwareRenderer* _renderer;
|
||||
RenderArea::RenderParams _params;
|
||||
SoftwareCanvasRenderer* _renderer;
|
||||
};
|
||||
|
||||
class _RenderArea:public QWidget
|
||||
{
|
||||
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):
|
||||
DialogRender::DialogRender(QWidget *parent, SoftwareCanvasRenderer* renderer):
|
||||
QDialog(parent, Qt::WindowTitleHint | Qt::WindowMaximizeButtonHint | Qt::WindowMinimizeButtonHint | Qt::WindowCloseButtonHint)
|
||||
{
|
||||
pixbuf_lock = new QMutex();
|
||||
pixbuf = new QImage(1, 1, QImage::Format_ARGB32);
|
||||
_current_dialog = this;
|
||||
_render_thread = NULL;
|
||||
_renderer = renderer;
|
||||
canvas_renderer = renderer;
|
||||
|
||||
setModal(true);
|
||||
setWindowTitle(tr("Paysages 3D - Render"));
|
||||
setLayout(new QVBoxLayout());
|
||||
|
||||
_scroll = new QScrollArea(this);
|
||||
_scroll->setAlignment(Qt::AlignCenter);
|
||||
area = new _RenderArea(_scroll);
|
||||
_scroll->setWidget(area);
|
||||
layout()->addWidget(_scroll);
|
||||
canvas_preview = new WidgetPreviewCanvas(this);
|
||||
canvas_preview->setCanvas(canvas_renderer->getCanvas());
|
||||
layout()->addWidget(canvas_preview);
|
||||
|
||||
// Status bar
|
||||
_info = new QWidget(this);
|
||||
|
@ -140,19 +95,19 @@ DialogRender::DialogRender(QWidget *parent, SoftwareRenderer* renderer):
|
|||
_actions->layout()->addWidget(_save_button);
|
||||
|
||||
// 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(_save_button, SIGNAL(clicked()), this, SLOT(saveRender()));
|
||||
connect(_tonemapping_control, SIGNAL(currentIndexChanged(int)), this, SLOT(toneMappingChanged()));
|
||||
connect(_exposure_control, SIGNAL(valueChanged(int)), this, SLOT(toneMappingChanged()));
|
||||
|
||||
toneMappingChanged();
|
||||
}
|
||||
|
||||
DialogRender::~DialogRender()
|
||||
{
|
||||
if (_render_thread)
|
||||
{
|
||||
_renderer->interrupt();
|
||||
canvas_renderer->interrupt();
|
||||
_render_thread->wait();
|
||||
|
||||
delete _render_thread;
|
||||
|
@ -161,31 +116,20 @@ DialogRender::~DialogRender()
|
|||
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()
|
||||
{
|
||||
emit renderEnded();
|
||||
}
|
||||
|
||||
void DialogRender::startRender(RenderArea::RenderParams params)
|
||||
void DialogRender::startRender()
|
||||
{
|
||||
_started = time(NULL);
|
||||
|
||||
applyRenderSize(params.width, params.height);
|
||||
_renderer->setPreviewCallbacks(_renderStart, _renderDraw, _renderUpdate);
|
||||
|
||||
_render_thread = new RenderThread(this, _renderer, params);
|
||||
_render_thread = new RenderThread(this, canvas_renderer);
|
||||
_render_thread->start();
|
||||
|
||||
startTimer(100);
|
||||
|
||||
exec();
|
||||
}
|
||||
|
||||
|
@ -193,8 +137,6 @@ void DialogRender::applyRenderEnded()
|
|||
{
|
||||
_info->hide();
|
||||
_actions->show();
|
||||
|
||||
area->update();
|
||||
}
|
||||
|
||||
void DialogRender::saveRender()
|
||||
|
@ -208,8 +150,7 @@ void DialogRender::saveRender()
|
|||
{
|
||||
filepath = filepath.append(".png");
|
||||
}
|
||||
std::string filepathstr = filepath.toStdString();
|
||||
if (_renderer->render_area->saveToFile((char*)filepathstr.c_str()))
|
||||
if (canvas_renderer->saveToDisk(filepath.toStdString()))
|
||||
{
|
||||
QMessageBox::information(this, "Message", QString(tr("The picture %1 has been saved.")).arg(filepath));
|
||||
}
|
||||
|
@ -223,35 +164,26 @@ void DialogRender::saveRender()
|
|||
void DialogRender::toneMappingChanged()
|
||||
{
|
||||
ColorProfile profile((ColorProfile::ToneMappingOperator)_tonemapping_control->currentIndex(), ((double)_exposure_control->value()) * 0.01);
|
||||
_renderer->render_area->setToneMapping(profile);
|
||||
canvas_preview->setToneMapping(profile);
|
||||
}
|
||||
|
||||
void DialogRender::loadLastRender()
|
||||
{
|
||||
applyRenderSize(_renderer->render_width, _renderer->render_height);
|
||||
_renderer->setPreviewCallbacks(_renderStart, _renderDraw, _renderUpdate);
|
||||
renderEnded();
|
||||
toneMappingChanged();
|
||||
|
||||
exec();
|
||||
}
|
||||
|
||||
void DialogRender::applyRenderSize(int width, int height)
|
||||
{
|
||||
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)
|
||||
void DialogRender::timerEvent(QTimerEvent *)
|
||||
{
|
||||
double diff = difftime(time(NULL), _started);
|
||||
int hours = (int)floor(diff / 3600.0);
|
||||
int minutes = (int)floor((diff - 3600.0 * hours) / 60.0);
|
||||
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')));
|
||||
_progress->setValue((int)(value * 1000.0));
|
||||
|
||||
_progress->setValue((int)(canvas_renderer->getProgress() * 1000.0));
|
||||
_progress->update();
|
||||
}
|
||||
|
||||
|
|
|
@ -5,7 +5,6 @@
|
|||
|
||||
#include <ctime>
|
||||
#include <QDialog>
|
||||
#include "RenderArea.h"
|
||||
|
||||
class QThread;
|
||||
class QProgressBar;
|
||||
|
@ -19,33 +18,30 @@ class DialogRender : public QDialog
|
|||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit DialogRender(QWidget *parent, SoftwareRenderer* renderer);
|
||||
explicit DialogRender(QWidget *parent, SoftwareCanvasRenderer *renderer);
|
||||
~DialogRender();
|
||||
|
||||
void tellRenderSize(int width, int height);
|
||||
void tellProgressChange(double value);
|
||||
void tellRenderEnded();
|
||||
void startRender(RenderArea::RenderParams params);
|
||||
void startRender();
|
||||
void loadLastRender();
|
||||
|
||||
virtual void timerEvent(QTimerEvent *event) override;
|
||||
|
||||
QImage* pixbuf;
|
||||
QMutex* pixbuf_lock;
|
||||
QWidget* area;
|
||||
|
||||
private slots:
|
||||
void applyRenderSize(int width, int height);
|
||||
void applyProgress(double value);
|
||||
void saveRender();
|
||||
void applyRenderEnded();
|
||||
void toneMappingChanged();
|
||||
|
||||
signals:
|
||||
void renderSizeChanged(int width, int height);
|
||||
void progressChanged(double value);
|
||||
void renderEnded();
|
||||
|
||||
private:
|
||||
QScrollArea* _scroll;
|
||||
SoftwareCanvasRenderer* canvas_renderer;
|
||||
WidgetPreviewCanvas* canvas_preview;
|
||||
|
||||
QWidget* _info;
|
||||
QWidget* _actions;
|
||||
QComboBox* _tonemapping_control;
|
||||
|
@ -53,7 +49,6 @@ private:
|
|||
QPushButton* _save_button;
|
||||
QThread* _render_thread;
|
||||
QLabel* _timer;
|
||||
SoftwareRenderer* _renderer;
|
||||
QProgressBar* _progress;
|
||||
time_t _started;
|
||||
};
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
#include "tools.h"
|
||||
#include "DesktopScenery.h"
|
||||
#include "PackStream.h"
|
||||
#include "SoftwareRenderer.h"
|
||||
#include "SoftwareCanvasRenderer.h"
|
||||
#include "BasePreview.h"
|
||||
#include "CloudsDefinition.h"
|
||||
#include "CameraDefinition.h"
|
||||
|
@ -36,8 +36,8 @@ BaseForm(parent, true)
|
|||
|
||||
addInput(new InputCamera(this, tr("Camera"), _camera));
|
||||
addInputInt(tr("Quality"), &_params.quality, 1, 10, 1, 1);
|
||||
addInputInt(tr("Image width"), &_params.width, 100, 2000, 10, 100);
|
||||
addInputInt(tr("Image height"), &_params.height, 100, 1200, 10, 100);
|
||||
addInputInt(tr("Image width"), &_params.width, 100, 4000, 10, 100);
|
||||
addInputInt(tr("Image height"), &_params.height, 100, 3000, 10, 100);
|
||||
addInputInt(tr("Anti aliasing"), &_params.antialias, 1, 4, 1, 1);
|
||||
|
||||
button = addButton(tr("Start new render"));
|
||||
|
@ -103,14 +103,16 @@ void FormRender::startQuickRender()
|
|||
{
|
||||
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;
|
||||
|
||||
DialogRender* dialog = new DialogRender(this, _renderer);
|
||||
RenderArea::RenderParams params = {400, 300, 1, 3};
|
||||
dialog->startRender(params);
|
||||
|
||||
delete dialog;
|
||||
DialogRender dialog(this, _renderer);
|
||||
dialog.startRender();
|
||||
}
|
||||
|
||||
void FormRender::startRender()
|
||||
|
@ -119,22 +121,20 @@ void FormRender::startRender()
|
|||
{
|
||||
delete _renderer;
|
||||
}
|
||||
_renderer = new SoftwareRenderer(DesktopScenery::getCurrent());
|
||||
_renderer = new SoftwareCanvasRenderer();
|
||||
_renderer->setScenery(DesktopScenery::getCurrent());
|
||||
_renderer->setConfig(_params);
|
||||
_renderer_inited = true;
|
||||
|
||||
DialogRender* dialog = new DialogRender(this, _renderer);
|
||||
dialog->startRender(_params);
|
||||
|
||||
delete dialog;
|
||||
DialogRender dialog(this, _renderer);
|
||||
dialog.startRender();
|
||||
}
|
||||
|
||||
void FormRender::showRender()
|
||||
{
|
||||
if (_renderer_inited)
|
||||
{
|
||||
DialogRender* dialog = new DialogRender(this, _renderer);
|
||||
dialog->loadLastRender();
|
||||
|
||||
delete dialog;
|
||||
DialogRender dialog(this, _renderer);
|
||||
dialog.loadLastRender();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
#include "baseform.h"
|
||||
|
||||
#include "RenderArea.h"
|
||||
#include "RenderConfig.h"
|
||||
|
||||
class FormRender : public BaseForm
|
||||
{
|
||||
|
@ -29,9 +29,9 @@ protected slots:
|
|||
virtual void configChangeEvent();
|
||||
|
||||
private:
|
||||
RenderArea::RenderParams _params;
|
||||
RenderConfig _params;
|
||||
CameraDefinition* _camera;
|
||||
SoftwareRenderer* _renderer;
|
||||
SoftwareCanvasRenderer* _renderer;
|
||||
bool _renderer_inited;
|
||||
BasePreview* _preview_landscape;
|
||||
Base2dPreviewRenderer* _preview_landscape_renderer;
|
||||
|
|
|
@ -10,6 +10,10 @@ SUBDIRS = \
|
|||
render/opengl \
|
||||
interface/commandline \
|
||||
interface/desktop \
|
||||
interface/quick \
|
||||
interface/quick
|
||||
|
||||
exists( tests/googletest/sources/src/gtest-all.cc ) {
|
||||
SUBDIRS += \
|
||||
tests/googletest \
|
||||
tests
|
||||
}
|
||||
|
|
124
src/render/software/Canvas.cpp
Normal file
124
src/render/software/Canvas.cpp
Normal file
|
@ -0,0 +1,124 @@
|
|||
#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();
|
||||
|
||||
int px = x / pwidth;
|
||||
int py = y / pheight;
|
||||
|
||||
if (px >= horizontal_portion_count)
|
||||
{
|
||||
px = horizontal_portion_count - 1;
|
||||
}
|
||||
if (py >= vertical_portion_count)
|
||||
{
|
||||
py = vertical_portion_count - 1;
|
||||
}
|
||||
|
||||
return at(px, py);
|
||||
}
|
||||
|
||||
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
|
124
src/render/software/CanvasPictureWriter.cpp
Normal file
124
src/render/software/CanvasPictureWriter.cpp
Normal file
|
@ -0,0 +1,124 @@
|
|||
#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();
|
||||
|
||||
cache = new Color[1];
|
||||
cache_y = -antialias;
|
||||
cache_width = 0;
|
||||
}
|
||||
|
||||
CanvasPictureWriter::~CanvasPictureWriter()
|
||||
{
|
||||
delete profile;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
if (not (y >= cache_y && y < cache_y + antialias))
|
||||
{
|
||||
// Load rows into cache
|
||||
delete [] cache;
|
||||
cache_y = y;
|
||||
cache_width = canvas->getWidth();
|
||||
cache = new Color[cache_width * antialias];
|
||||
|
||||
CanvasPortion *portion = NULL;
|
||||
PackStream *stream = new PackStream;
|
||||
|
||||
Color* itcolor = cache;
|
||||
bool has_pixels = false;
|
||||
for (int cy = cache_y; cy < cache_y + antialias; cy++)
|
||||
{
|
||||
for (int cx = 0; cx < cache_width; cx++)
|
||||
{
|
||||
CanvasPortion *nportion = canvas->atPixel(cx, cy);
|
||||
if (nportion != portion)
|
||||
{
|
||||
portion = nportion;
|
||||
delete stream;
|
||||
stream = new PackStream;
|
||||
has_pixels = portion->getReadStream(*stream, cx - portion->getXOffset(), cy - portion->getYOffset());
|
||||
}
|
||||
if (has_pixels)
|
||||
{
|
||||
itcolor->load(stream);
|
||||
}
|
||||
else
|
||||
{
|
||||
*itcolor = COLOR_BLACK;
|
||||
}
|
||||
itcolor++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Hit the cache
|
||||
return cache[(y - cache_y) * cache_width + x];
|
||||
}
|
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;
|
||||
|
||||
int cache_y;
|
||||
int cache_width;
|
||||
Color *cache;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#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
|
|
@ -9,6 +9,9 @@
|
|||
#include "CameraDefinition.h"
|
||||
|
||||
#include "clouds/BaseCloudsModel.h"
|
||||
#include "clouds/CloudModelAltoCumulus.h"
|
||||
#include "clouds/CloudModelCirrus.h"
|
||||
#include "clouds/CloudModelCumuloNimbus.h"
|
||||
#include "clouds/CloudModelStratoCumulus.h"
|
||||
|
||||
CloudsRenderer::CloudsRenderer(SoftwareRenderer* parent):
|
||||
|
@ -60,20 +63,24 @@ void CloudsRenderer::update()
|
|||
BaseCloudsModel* model;
|
||||
switch (layer->type)
|
||||
{
|
||||
case CloudLayerDefinition::STRATUS:
|
||||
case CloudLayerDefinition::NIMBOSTRATUS:
|
||||
case CloudLayerDefinition::CUMULUS:
|
||||
model = new BaseCloudsModel(layer);
|
||||
break;
|
||||
case CloudLayerDefinition::STRATOCUMULUS:
|
||||
model = new CloudModelStratoCumulus(layer);
|
||||
break;
|
||||
case CloudLayerDefinition::ALTOCUMULUS:
|
||||
case CloudLayerDefinition::ALTOSTRATUS:
|
||||
model = new CloudModelAltoCumulus(layer);
|
||||
break;
|
||||
case CloudLayerDefinition::CIRRUS:
|
||||
model = new CloudModelCirrus(layer);
|
||||
break;
|
||||
case CloudLayerDefinition::CUMULONIMBUS:
|
||||
model = new CloudModelCumuloNimbus(layer);
|
||||
break;
|
||||
case CloudLayerDefinition::STRATUS:
|
||||
case CloudLayerDefinition::NIMBOSTRATUS:
|
||||
case CloudLayerDefinition::CUMULUS:
|
||||
case CloudLayerDefinition::ALTOSTRATUS:
|
||||
case CloudLayerDefinition::CIRROCUMULUS:
|
||||
case CloudLayerDefinition::CIRROSTRATUS:
|
||||
case CloudLayerDefinition::CIRRUS:
|
||||
model = new BaseCloudsModel(layer);
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -12,7 +12,7 @@ LightingManager::LightingManager()
|
|||
|
||||
void LightingManager::registerFilter(LightFilter* filter)
|
||||
{
|
||||
filters.insert(filter);
|
||||
filters.push_back(filter);
|
||||
}
|
||||
|
||||
bool LightingManager::alterLight(LightComponent &component, const Vector3 &location)
|
||||
|
|
|
@ -3,8 +3,6 @@
|
|||
|
||||
#include "software_global.h"
|
||||
|
||||
#include <set>
|
||||
|
||||
namespace paysages {
|
||||
namespace software {
|
||||
|
||||
|
@ -42,7 +40,7 @@ public:
|
|||
private:
|
||||
int specularity;
|
||||
|
||||
std::set<LightFilter*> filters;
|
||||
std::vector<LightFilter*> filters;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
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 "AtmosphereResult.h"
|
||||
#include "CloudsRenderer.h"
|
||||
#include "Rasterizer.h"
|
||||
#include "CanvasFragment.h"
|
||||
|
||||
#define SPHERE_SIZE 20000.0
|
||||
|
||||
SkyRasterizer::SkyRasterizer(SoftwareRenderer* renderer):
|
||||
renderer(renderer)
|
||||
SkyRasterizer::SkyRasterizer(SoftwareRenderer* renderer, int client_id):
|
||||
Rasterizer(renderer, client_id, Color(0.9, 0.9, 1.0))
|
||||
{
|
||||
}
|
||||
|
||||
static Color _postProcessFragment(SoftwareRenderer* renderer, const Vector3 &location, void*)
|
||||
{
|
||||
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()
|
||||
void SkyRasterizer::rasterizeToCanvas(CanvasPortion* canvas)
|
||||
{
|
||||
int res_i, res_j;
|
||||
int i, j;
|
||||
|
@ -47,7 +34,7 @@ void SkyRasterizer::rasterize()
|
|||
|
||||
for (j = 0; j < res_j; j++)
|
||||
{
|
||||
if (!renderer->addRenderProgress(0.0))
|
||||
if (interrupted)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
@ -79,7 +66,23 @@ void SkyRasterizer::rasterize()
|
|||
vertex4 = camera_location.add(direction);
|
||||
|
||||
/* 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 "Rasterizer.h"
|
||||
|
||||
namespace paysages {
|
||||
namespace software {
|
||||
|
||||
class SOFTWARESHARED_EXPORT SkyRasterizer
|
||||
class SOFTWARESHARED_EXPORT SkyRasterizer: public Rasterizer
|
||||
{
|
||||
public:
|
||||
SkyRasterizer(SoftwareRenderer* renderer);
|
||||
void rasterize();
|
||||
SkyRasterizer(SoftwareRenderer* renderer, int client_id);
|
||||
|
||||
private:
|
||||
SoftwareRenderer* renderer;
|
||||
virtual void rasterizeToCanvas(CanvasPortion* canvas) override;
|
||||
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)
|
||||
{
|
||||
RenderArea::RenderParams params = {1, 1, 1, 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_area = new RenderArea(this);
|
||||
render_area->setParams(params);
|
||||
|
||||
atmosphere_renderer = new BaseAtmosphereRenderer(this);
|
||||
clouds_renderer = new CloudsRenderer(this);
|
||||
|
@ -45,8 +36,8 @@ SoftwareRenderer::SoftwareRenderer(Scenery* scenery)
|
|||
fluid_medium = new FluidMediumManager(this);
|
||||
lighting = new LightingManager();
|
||||
|
||||
lighting->registerFilter(terrain_renderer);
|
||||
lighting->registerFilter(water_renderer);
|
||||
lighting->registerFilter(terrain_renderer);
|
||||
lighting->registerFilter(clouds_renderer);
|
||||
|
||||
this->scenery = new Scenery;
|
||||
|
@ -59,7 +50,6 @@ SoftwareRenderer::SoftwareRenderer(Scenery* scenery)
|
|||
SoftwareRenderer::~SoftwareRenderer()
|
||||
{
|
||||
delete render_camera;
|
||||
delete render_area;
|
||||
|
||||
delete fluid_medium;
|
||||
delete lighting;
|
||||
|
@ -108,18 +98,6 @@ void SoftwareRenderer::prepare()
|
|||
//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()
|
||||
{
|
||||
scenery->getClouds()->clear();
|
||||
|
@ -164,68 +142,6 @@ void SoftwareRenderer::disableAtmosphere(const std::vector<LightComponent> &ligh
|
|||
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)
|
||||
{
|
||||
LightStatus status(lighting, location, getCameraLocation(location));
|
||||
|
@ -261,11 +177,6 @@ RayCastingResult SoftwareRenderer::rayWalking(const Vector3 &location, const Vec
|
|||
return result;
|
||||
}
|
||||
|
||||
int SoftwareRenderer::addRenderProgress(double)
|
||||
{
|
||||
return not render_interrupt;
|
||||
}
|
||||
|
||||
Vector3 SoftwareRenderer::getCameraLocation(const Vector3 &)
|
||||
{
|
||||
return render_camera->getLocation();
|
||||
|
@ -296,37 +207,3 @@ Vector3 SoftwareRenderer::unprojectPoint(const Vector3 &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 "RenderArea.h"
|
||||
#include "RayCastingManager.h"
|
||||
|
||||
namespace paysages {
|
||||
|
@ -21,16 +20,8 @@ public:
|
|||
|
||||
/* Render base configuration */
|
||||
int render_quality;
|
||||
int render_width;
|
||||
int render_height;
|
||||
CameraDefinition* render_camera;
|
||||
|
||||
/* Render related */
|
||||
RenderArea* render_area;
|
||||
double render_progress;
|
||||
int render_interrupt;
|
||||
int is_rendering;
|
||||
|
||||
void* customData[10];
|
||||
|
||||
virtual Vector3 getCameraLocation(const Vector3 &target);
|
||||
|
@ -38,11 +29,6 @@ public:
|
|||
virtual double getPrecision(const Vector3 &location);
|
||||
virtual Vector3 projectPoint(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.
|
||||
|
@ -59,11 +45,6 @@ public:
|
|||
*/
|
||||
virtual void prepare();
|
||||
|
||||
/*!
|
||||
* \brief Start the rasterization process.
|
||||
*/
|
||||
virtual void rasterize();
|
||||
|
||||
/*!
|
||||
* \brief Disable the clouds feature.
|
||||
*
|
||||
|
@ -78,10 +59,6 @@ public:
|
|||
void disableAtmosphere();
|
||||
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 BaseAtmosphereRenderer* getAtmosphereRenderer() const {return atmosphere_renderer;}
|
||||
|
|
|
@ -7,10 +7,11 @@
|
|||
#include "WaterRenderer.h"
|
||||
#include "TexturesRenderer.h"
|
||||
#include "Scenery.h"
|
||||
#include "ParallelQueue.h"
|
||||
#include "CanvasPortion.h"
|
||||
#include "CanvasFragment.h"
|
||||
|
||||
TerrainRasterizer::TerrainRasterizer(SoftwareRenderer* renderer):
|
||||
renderer(renderer)
|
||||
TerrainRasterizer::TerrainRasterizer(SoftwareRenderer* renderer, int client_id):
|
||||
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);
|
||||
}
|
||||
|
||||
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));
|
||||
return renderer->getTerrainRenderer()->getFinalColor(point, precision);
|
||||
if (detail < 1)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
static void _renderQuad(SoftwareRenderer* renderer, double x, double z, double size, double water_height)
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TerrainRasterizer::renderQuad(CanvasPortion *canvas, double x, double z, double size, double water_height)
|
||||
{
|
||||
Vector3 ov1, ov2, ov3, ov4;
|
||||
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)
|
||||
{
|
||||
renderer->pushDisplacedQuad(dv1, dv2, dv3, dv4, ov1, ov2, ov3, ov4, _postProcessFragment, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
pushDisplacedQuad(canvas, dv1, dv2, dv3, dv4, ov1, ov2, ov3, ov4);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
int chunk_factor, chunk_count, i;
|
||||
|
@ -157,25 +152,29 @@ void TerrainRasterizer::getTessellationInfo(int displaced)
|
|||
for (i = 0; i < chunk_count - 1; i++)
|
||||
{
|
||||
_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;
|
||||
}
|
||||
|
||||
_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;
|
||||
}
|
||||
|
||||
_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;
|
||||
}
|
||||
|
||||
_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;
|
||||
}
|
||||
|
@ -193,48 +192,19 @@ void TerrainRasterizer::getTessellationInfo(int displaced)
|
|||
}
|
||||
}
|
||||
|
||||
typedef struct
|
||||
void TerrainRasterizer::processChunk(CanvasPortion* canvas, TerrainChunkInfo* chunk, double progress)
|
||||
{
|
||||
TerrainRasterizer* rasterizer;
|
||||
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);
|
||||
tessellateChunk(canvas, chunk, 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))
|
||||
Color TerrainRasterizer::shadeFragment(const CanvasFragment &fragment) const
|
||||
{
|
||||
delete info;
|
||||
}
|
||||
|
||||
renderer->render_progress = 0.05 * progress;
|
||||
return !renderer->render_interrupt;
|
||||
}
|
||||
|
||||
void TerrainRasterizer::renderSurface()
|
||||
{
|
||||
queue = new ParallelQueue();
|
||||
|
||||
renderer->render_progress = 0.0;
|
||||
getTessellationInfo(0);
|
||||
renderer->render_progress = 0.05;
|
||||
|
||||
queue->wait();
|
||||
Vector3 point = fragment.getLocation();
|
||||
double precision = renderer->getPrecision(_getPoint(renderer, point.x, point.z));
|
||||
return renderer->getTerrainRenderer()->getFinalColor(point, precision);
|
||||
}
|
||||
|
|
|
@ -3,12 +3,13 @@
|
|||
|
||||
#include "software_global.h"
|
||||
|
||||
#include "Rasterizer.h"
|
||||
#include "Vector3.h"
|
||||
|
||||
namespace paysages {
|
||||
namespace software {
|
||||
|
||||
class SOFTWARESHARED_EXPORT TerrainRasterizer
|
||||
class SOFTWARESHARED_EXPORT TerrainRasterizer: public Rasterizer
|
||||
{
|
||||
public:
|
||||
typedef struct
|
||||
|
@ -21,35 +22,29 @@ public:
|
|||
} TerrainChunkInfo;
|
||||
|
||||
public:
|
||||
TerrainRasterizer(SoftwareRenderer* renderer);
|
||||
TerrainRasterizer(SoftwareRenderer* renderer, int client_id);
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
void tessellateChunk(TerrainChunkInfo* chunk, int detail);
|
||||
void tessellateChunk(CanvasPortion* canvas, TerrainChunkInfo* chunk, int detail);
|
||||
|
||||
/**
|
||||
* Start the final rasterization of terrain.
|
||||
*
|
||||
* This will push the rasterized quads in the render area, waiting for post process.
|
||||
*/
|
||||
void renderSurface();
|
||||
void renderQuad(CanvasPortion* canvas, double x, double z, double size, double water_height);
|
||||
|
||||
private:
|
||||
SoftwareRenderer* renderer;
|
||||
ParallelQueue* queue;
|
||||
virtual void rasterizeToCanvas(CanvasPortion* canvas) override;
|
||||
virtual Color shadeFragment(const CanvasFragment &fragment) const override;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -40,10 +40,10 @@ double TexturesRenderer::getTriplanarNoise(NoiseGenerator *noise, const Vector3
|
|||
double mXY = fabs(normal.z);
|
||||
double mXZ = fabs(normal.y);
|
||||
double mYZ = fabs(normal.x);
|
||||
double total = mXY + mXZ + mYZ;
|
||||
mXY /= total;
|
||||
mXZ /= total;
|
||||
mYZ /= total;
|
||||
double total = 1.0 / (mXY + mXZ + mYZ);
|
||||
mXY *= total;
|
||||
mXZ *= total;
|
||||
mYZ *= total;
|
||||
|
||||
return noiseXY * mXY + noiseXZ * mXZ + noiseYZ * mYZ;
|
||||
}
|
||||
|
|
|
@ -2,19 +2,14 @@
|
|||
|
||||
#include "SoftwareRenderer.h"
|
||||
#include "WaterRenderer.h"
|
||||
#include "ParallelQueue.h"
|
||||
#include "CanvasFragment.h"
|
||||
|
||||
WaterRasterizer::WaterRasterizer(SoftwareRenderer* renderer):
|
||||
renderer(renderer)
|
||||
WaterRasterizer::WaterRasterizer(SoftwareRenderer* renderer, int client_id):
|
||||
Rasterizer(renderer, client_id, Color(0.9, 0.95, 1.0))
|
||||
{
|
||||
}
|
||||
|
||||
static Color _postProcessFragment(SoftwareRenderer* renderer, const Vector3 &location, void*)
|
||||
{
|
||||
return renderer->getWaterRenderer()->getResult(location.x, location.z).final;
|
||||
}
|
||||
|
||||
static Vector3 _getFirstPassVertex(SoftwareRenderer* renderer, double x, double z)
|
||||
static inline Vector3 _getFirstPassVertex(SoftwareRenderer* renderer, double x, double z)
|
||||
{
|
||||
Vector3 result;
|
||||
|
||||
|
@ -25,7 +20,7 @@ static Vector3 _getFirstPassVertex(SoftwareRenderer* renderer, double x, double
|
|||
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;
|
||||
|
||||
|
@ -34,42 +29,11 @@ static void _renderQuad(SoftwareRenderer* renderer, double x, double z, double s
|
|||
v3 = _getFirstPassVertex(renderer, x + size, z + size);
|
||||
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;
|
||||
Vector3 cam = renderer->getCameraLocation(VECTOR_ZERO);
|
||||
double radius_int, radius_ext, base_chunk_size, chunk_size;
|
||||
|
@ -91,27 +55,17 @@ void WaterRasterizer::renderSurface()
|
|||
|
||||
while (radius_int < 20000.0)
|
||||
{
|
||||
if (!renderer->addRenderProgress(0.0))
|
||||
if (interrupted)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0; i < chunk_count - 1; i++)
|
||||
{
|
||||
info = new ParallelRasterInfo;
|
||||
|
||||
info->renderer = renderer;
|
||||
info->cx = cx;
|
||||
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;
|
||||
}
|
||||
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);
|
||||
rasterizeQuad(canvas, cx + radius_int - chunk_size * i, cz + radius_int, chunk_size);
|
||||
rasterizeQuad(canvas, cx - radius_ext, cz + radius_int - chunk_size * i, chunk_size);
|
||||
}
|
||||
|
||||
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_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 "Rasterizer.h"
|
||||
|
||||
namespace paysages {
|
||||
namespace software {
|
||||
|
||||
class WaterRasterizer
|
||||
class WaterRasterizer: public Rasterizer
|
||||
{
|
||||
public:
|
||||
WaterRasterizer(SoftwareRenderer* renderer);
|
||||
WaterRasterizer(SoftwareRenderer* renderer, int client_id);
|
||||
|
||||
void renderSurface();
|
||||
void rasterizeQuad(CanvasPortion* canvas, double x, double z, double size);
|
||||
|
||||
private:
|
||||
SoftwareRenderer* renderer;
|
||||
virtual void rasterizeToCanvas(CanvasPortion* canvas) override;
|
||||
virtual Color shadeFragment(const CanvasFragment &fragment) const override;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -263,7 +263,7 @@ bool WaterRenderer::applyLightFilter(LightComponent &light, const Vector3 &at)
|
|||
light.color.b *= factor;
|
||||
light.reflection *= factor;
|
||||
|
||||
return true;
|
||||
return factor > 0.0;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
64
src/render/software/clouds/CloudModelAltoCumulus.cpp
Normal file
64
src/render/software/clouds/CloudModelAltoCumulus.cpp
Normal file
|
@ -0,0 +1,64 @@
|
|||
#include "CloudModelAltoCumulus.h"
|
||||
|
||||
#include "NoiseGenerator.h"
|
||||
#include "Vector3.h"
|
||||
#include "CloudLayerDefinition.h"
|
||||
|
||||
CloudModelAltoCumulus::CloudModelAltoCumulus(CloudLayerDefinition* layer):
|
||||
BaseCloudsModel(layer)
|
||||
{
|
||||
noise = new NoiseGenerator();
|
||||
}
|
||||
|
||||
CloudModelAltoCumulus::~CloudModelAltoCumulus()
|
||||
{
|
||||
delete noise;
|
||||
}
|
||||
|
||||
void CloudModelAltoCumulus::update()
|
||||
{
|
||||
noise->clearLevels();
|
||||
noise->addLevelSimple(4.0, -1.0, 1.0);
|
||||
noise->addLevelSimple(1.0 / 2.0, -0.6, 0.6);
|
||||
noise->addLevelSimple(1.0 / 4.0, -0.3, 0.3);
|
||||
noise->addLevelSimple(1.0 / 10.0, -0.15, 0.15);
|
||||
noise->addLevelSimple(1.0 / 20.0, -0.09, 0.09);
|
||||
noise->addLevelSimple(1.0 / 40.0, -0.06, 0.06);
|
||||
noise->addLevelSimple(1.0 / 60.0, -0.03, 0.03);
|
||||
noise->addLevelSimple(1.0 / 80.0, -0.015, 0.015);
|
||||
noise->addLevelSimple(1.0 / 100.0, -0.06, 0.06);
|
||||
noise->normalizeAmplitude(-4.0, 3.0, 0);
|
||||
noise->setState(layer->getNoiseState());
|
||||
}
|
||||
|
||||
void CloudModelAltoCumulus::getAltitudeRange(double *min_altitude, double *max_altitude) const
|
||||
{
|
||||
*min_altitude = 15.0 + 10.0 * layer->altitude;
|
||||
*max_altitude = *min_altitude + 18.0 * layer->scaling;
|
||||
}
|
||||
|
||||
double CloudModelAltoCumulus::getDensity(const Vector3 &location) const
|
||||
{
|
||||
double val;
|
||||
double min_altitude, max_altitude;
|
||||
double noise_scaling = 18.0 * layer->scaling;
|
||||
|
||||
getAltitudeRange(&min_altitude, &max_altitude);
|
||||
|
||||
if (location.y < min_altitude || location.y > max_altitude)
|
||||
{
|
||||
return 0.0;
|
||||
}
|
||||
else
|
||||
{
|
||||
double x = 0.6 * location.x / noise_scaling;
|
||||
double y = (location.y - min_altitude) / noise_scaling;
|
||||
double z = 0.6 * location.z / noise_scaling;
|
||||
|
||||
//double coverage = layer->coverage * layer->_coverage_by_altitude->getValue((position.y - layer->altitude) / layer->scaling);
|
||||
double coverage = layer->coverage;
|
||||
|
||||
val = 0.5 * noise->get3DTotal(x, y, z);
|
||||
return val - 1.1 + coverage;
|
||||
}
|
||||
}
|
29
src/render/software/clouds/CloudModelAltoCumulus.h
Normal file
29
src/render/software/clouds/CloudModelAltoCumulus.h
Normal file
|
@ -0,0 +1,29 @@
|
|||
#ifndef CLOUDMODELALTOCUMULUS_H
|
||||
#define CLOUDMODELALTOCUMULUS_H
|
||||
|
||||
#include "../software_global.h"
|
||||
|
||||
#include "BaseCloudsModel.h"
|
||||
|
||||
namespace paysages {
|
||||
namespace software {
|
||||
|
||||
class CloudModelAltoCumulus : public BaseCloudsModel
|
||||
{
|
||||
public:
|
||||
CloudModelAltoCumulus(CloudLayerDefinition* layer);
|
||||
virtual ~CloudModelAltoCumulus();
|
||||
|
||||
virtual void update() override;
|
||||
|
||||
virtual void getAltitudeRange(double *min_altitude, double *max_altitude) const override;
|
||||
virtual double getDensity(const Vector3 &location) const override;
|
||||
|
||||
private:
|
||||
NoiseGenerator* noise;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif // CLOUDMODELALTOCUMULUS_H
|
62
src/render/software/clouds/CloudModelCirrus.cpp
Normal file
62
src/render/software/clouds/CloudModelCirrus.cpp
Normal file
|
@ -0,0 +1,62 @@
|
|||
#include "CloudModelCirrus.h"
|
||||
|
||||
#include "NoiseGenerator.h"
|
||||
#include "Vector3.h"
|
||||
#include "CloudLayerDefinition.h"
|
||||
|
||||
CloudModelCirrus::CloudModelCirrus(CloudLayerDefinition* layer):
|
||||
BaseCloudsModel(layer)
|
||||
{
|
||||
noise = new NoiseGenerator();
|
||||
}
|
||||
|
||||
CloudModelCirrus::~CloudModelCirrus()
|
||||
{
|
||||
delete noise;
|
||||
}
|
||||
|
||||
void CloudModelCirrus::update()
|
||||
{
|
||||
noise->clearLevels();
|
||||
noise->addLevelSimple(1.0, -1.0, 1.0);
|
||||
noise->addLevelSimple(1.0 / 6.0, -0.6, 0.6);
|
||||
noise->addLevelSimple(1.0 / 10.0, -0.15, 0.15);
|
||||
noise->addLevelSimple(1.0 / 20.0, -0.09, 0.09);
|
||||
noise->addLevelSimple(1.0 / 40.0, -0.06, 0.06);
|
||||
noise->addLevelSimple(1.0 / 120.0, -0.03, 0.03);
|
||||
noise->addLevelSimple(1.0 / 300.0, -0.01, 0.01);
|
||||
noise->normalizeAmplitude(-4.0, 3.0, 0);
|
||||
noise->setState(layer->getNoiseState());
|
||||
}
|
||||
|
||||
void CloudModelCirrus::getAltitudeRange(double *min_altitude, double *max_altitude) const
|
||||
{
|
||||
*min_altitude = 45.0 + 20.0 * layer->altitude;
|
||||
*max_altitude = *min_altitude + 20.0 * layer->scaling;
|
||||
}
|
||||
|
||||
double CloudModelCirrus::getDensity(const Vector3 &location) const
|
||||
{
|
||||
double val;
|
||||
double min_altitude, max_altitude;
|
||||
double noise_scaling = 30.0 * layer->scaling;
|
||||
|
||||
getAltitudeRange(&min_altitude, &max_altitude);
|
||||
|
||||
if (location.y < min_altitude || location.y > max_altitude)
|
||||
{
|
||||
return 0.0;
|
||||
}
|
||||
else
|
||||
{
|
||||
double x = 0.03 * location.x / noise_scaling;
|
||||
double y = (location.y - min_altitude) / noise_scaling;
|
||||
double z = 0.03 * location.z / noise_scaling;
|
||||
|
||||
//double coverage = layer->coverage * layer->_coverage_by_altitude->getValue((position.y - layer->altitude) / layer->scaling);
|
||||
double coverage = layer->coverage;
|
||||
|
||||
val = 0.6 * noise->get3DTotal(x, y, z);
|
||||
return val - 1.1 + coverage;
|
||||
}
|
||||
}
|
29
src/render/software/clouds/CloudModelCirrus.h
Normal file
29
src/render/software/clouds/CloudModelCirrus.h
Normal file
|
@ -0,0 +1,29 @@
|
|||
#ifndef CLOUDMODELCIRRUS_H
|
||||
#define CLOUDMODELCIRRUS_H
|
||||
|
||||
#include "../software_global.h"
|
||||
|
||||
#include "BaseCloudsModel.h"
|
||||
|
||||
namespace paysages {
|
||||
namespace software {
|
||||
|
||||
class CloudModelCirrus : public BaseCloudsModel
|
||||
{
|
||||
public:
|
||||
CloudModelCirrus(CloudLayerDefinition* layer);
|
||||
virtual ~CloudModelCirrus();
|
||||
|
||||
virtual void update() override;
|
||||
|
||||
virtual void getAltitudeRange(double *min_altitude, double *max_altitude) const override;
|
||||
virtual double getDensity(const Vector3 &location) const override;
|
||||
|
||||
private:
|
||||
NoiseGenerator* noise;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif // CLOUDMODELCIRRUS_H
|
69
src/render/software/clouds/CloudModelCumuloNimbus.cpp
Normal file
69
src/render/software/clouds/CloudModelCumuloNimbus.cpp
Normal file
|
@ -0,0 +1,69 @@
|
|||
#include "CloudModelCumuloNimbus.h"
|
||||
|
||||
#include "NoiseGenerator.h"
|
||||
#include "Vector3.h"
|
||||
#include "CloudLayerDefinition.h"
|
||||
|
||||
CloudModelCumuloNimbus::CloudModelCumuloNimbus(CloudLayerDefinition* layer):
|
||||
BaseCloudsModel(layer)
|
||||
{
|
||||
noise = new NoiseGenerator();
|
||||
}
|
||||
|
||||
CloudModelCumuloNimbus::~CloudModelCumuloNimbus()
|
||||
{
|
||||
delete noise;
|
||||
}
|
||||
|
||||
void CloudModelCumuloNimbus::update()
|
||||
{
|
||||
noise->clearLevels();
|
||||
noise->addLevelSimple(8.0, -1.0, 1.0);
|
||||
noise->addLevelSimple(7.0 / 2.0, -0.6, 0.6);
|
||||
noise->addLevelSimple(6.0 / 4.0, -0.3, 0.3);
|
||||
noise->addLevelSimple(5.0 / 10.0, -0.15, 0.15);
|
||||
/*noise->addLevelSimple(1.0 / 20.0, -0.09, 0.09);
|
||||
noise->addLevelSimple(1.0 / 40.0, -0.06, 0.06);
|
||||
noise->addLevelSimple(1.0 / 60.0, -0.03, 0.03);*/
|
||||
noise->addLevelSimple(1.0 / 80.0, -0.015, 0.015);
|
||||
noise->addLevelSimple(1.0 / 100.0, -0.06, 0.06);
|
||||
noise->addLevelSimple(1.0 / 150.0, -0.015, 0.015);
|
||||
noise->addLevelSimple(1.0 / 200.0, -0.009, 0.009);
|
||||
noise->addLevelSimple(1.0 / 400.0, -0.024, 0.024);
|
||||
noise->addLevelSimple(1.0 / 800.0, -0.003, 0.003);
|
||||
noise->addLevelSimple(1.0 / 1000.0, -0.0015, 0.0015);
|
||||
noise->normalizeAmplitude(-3.0, 4.0, 0);
|
||||
noise->setState(layer->getNoiseState());
|
||||
}
|
||||
|
||||
void CloudModelCumuloNimbus::getAltitudeRange(double *min_altitude, double *max_altitude) const
|
||||
{
|
||||
*min_altitude = 5.0 + 10.0 * layer->altitude;
|
||||
*max_altitude = *min_altitude + 50.0 + 50.0 * layer->scaling;
|
||||
}
|
||||
|
||||
double CloudModelCumuloNimbus::getDensity(const Vector3 &location) const
|
||||
{
|
||||
double val;
|
||||
double min_altitude, max_altitude;
|
||||
double noise_scaling = 60.0 * layer->scaling;
|
||||
|
||||
getAltitudeRange(&min_altitude, &max_altitude);
|
||||
|
||||
if (location.y < min_altitude || location.y > max_altitude)
|
||||
{
|
||||
return 0.0;
|
||||
}
|
||||
else
|
||||
{
|
||||
double x = 1.5 * location.x / noise_scaling;
|
||||
double y = (location.y - min_altitude) / noise_scaling;
|
||||
double z = 1.5 * location.z / noise_scaling;
|
||||
|
||||
//double coverage = layer->coverage * layer->_coverage_by_altitude->getValue((position.y - layer->altitude) / layer->scaling);
|
||||
double coverage = layer->coverage;
|
||||
|
||||
val = 0.5 * noise->get3DTotal(x, y, z);
|
||||
return val - 1.0 + coverage;
|
||||
}
|
||||
}
|
29
src/render/software/clouds/CloudModelCumuloNimbus.h
Normal file
29
src/render/software/clouds/CloudModelCumuloNimbus.h
Normal file
|
@ -0,0 +1,29 @@
|
|||
#ifndef CLOUDMODELCUMULONIMBUS_H
|
||||
#define CLOUDMODELCUMULONIMBUS_H
|
||||
|
||||
#include "../software_global.h"
|
||||
|
||||
#include "BaseCloudsModel.h"
|
||||
|
||||
namespace paysages {
|
||||
namespace software {
|
||||
|
||||
class CloudModelCumuloNimbus : public BaseCloudsModel
|
||||
{
|
||||
public:
|
||||
CloudModelCumuloNimbus(CloudLayerDefinition* layer);
|
||||
virtual ~CloudModelCumuloNimbus();
|
||||
|
||||
virtual void update() override;
|
||||
|
||||
virtual void getAltitudeRange(double *min_altitude, double *max_altitude) const override;
|
||||
virtual double getDensity(const Vector3 &location) const override;
|
||||
|
||||
private:
|
||||
NoiseGenerator* noise;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif // CLOUDMODELCUMULONIMBUS_H
|
|
@ -34,10 +34,23 @@ SOURCES += SoftwareRenderer.cpp \
|
|||
TerrainRenderer.cpp \
|
||||
TexturesRenderer.cpp \
|
||||
WaterRenderer.cpp \
|
||||
RenderArea.cpp \
|
||||
RayCastingManager.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 \
|
||||
clouds/CloudModelAltoCumulus.cpp \
|
||||
clouds/CloudModelCirrus.cpp \
|
||||
clouds/CloudModelCumuloNimbus.cpp
|
||||
|
||||
HEADERS += SoftwareRenderer.h\
|
||||
software_global.h \
|
||||
|
@ -61,10 +74,23 @@ HEADERS += SoftwareRenderer.h\
|
|||
TerrainRenderer.h \
|
||||
TexturesRenderer.h \
|
||||
WaterRenderer.h \
|
||||
RenderArea.h \
|
||||
RayCastingManager.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 \
|
||||
clouds/CloudModelAltoCumulus.h \
|
||||
clouds/CloudModelCirrus.h \
|
||||
clouds/CloudModelCumuloNimbus.h
|
||||
|
||||
unix:!symbian {
|
||||
maemo5 {
|
||||
|
|
|
@ -14,7 +14,8 @@
|
|||
namespace paysages {
|
||||
namespace software {
|
||||
class SoftwareRenderer;
|
||||
class RenderArea;
|
||||
class SoftwareCanvasRenderer;
|
||||
class RenderConfig;
|
||||
|
||||
class FluidMediumManager;
|
||||
class FluidMediumInterface;
|
||||
|
@ -33,6 +34,7 @@ namespace software {
|
|||
class TexturesRenderer;
|
||||
class WaterRenderer;
|
||||
|
||||
class Rasterizer;
|
||||
class SkyRasterizer;
|
||||
class TerrainRasterizer;
|
||||
|
||||
|
@ -44,6 +46,15 @@ namespace software {
|
|||
class NightSky;
|
||||
|
||||
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 "";
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
std::string readString();
|
||||
|
||||
void skip(const int &value, int count=1);
|
||||
void skip(const double &value, int count=1);
|
||||
|
||||
private:
|
||||
QFile* file;
|
||||
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 "System.h"
|
||||
#include "Semaphore.h"
|
||||
#include "Mutex.h"
|
||||
#include "ParallelWorker.h"
|
||||
#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)
|
||||
{
|
||||
this->units = units;
|
||||
this->running = 0;
|
||||
this->unit_function = func;
|
||||
this->data = data;
|
||||
this->worker = new ParallelWorkerCompat(this, func, data);
|
||||
this->worker_compat = true;
|
||||
}
|
||||
|
||||
ParallelWork::~ParallelWork()
|
||||
{
|
||||
assert(not running);
|
||||
}
|
||||
|
||||
static void* _workerThreadCallback(ParallelWork::ParallelWorker* worker)
|
||||
if (worker_compat)
|
||||
{
|
||||
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++)
|
||||
{
|
||||
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);
|
||||
delete worker;
|
||||
}
|
||||
}
|
||||
|
||||
int ParallelWork::perform(int nbworkers)
|
||||
int ParallelWork::perform(int thread_count)
|
||||
{
|
||||
int i, done, result;
|
||||
int i, done;
|
||||
assert(not running);
|
||||
|
||||
result = 0;
|
||||
|
||||
if (nbworkers <= 0)
|
||||
// Get thread count
|
||||
if (thread_count <= 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;
|
||||
|
||||
/* Init workers */
|
||||
for (i = 0; i < nbworkers; i++)
|
||||
// Init threads
|
||||
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;
|
||||
workers[i].work = this;
|
||||
threads[i] = available[i] = new ParallelThread(this);
|
||||
threads[i]->start();
|
||||
}
|
||||
|
||||
/* Perform run */
|
||||
// Perform all units
|
||||
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 */
|
||||
for (i = 0; i < nbworkers; i++)
|
||||
// Wait for all threads to end, then cleanup
|
||||
for (i = 0; i < thread_count; i++)
|
||||
{
|
||||
if (workers[i].status != PARALLEL_WORKER_STATUS_VOID)
|
||||
threads[i]->interrupt();
|
||||
}
|
||||
for (i = 0; i < thread_count; i++)
|
||||
{
|
||||
workers[i].thread->join();
|
||||
delete workers[i].thread;
|
||||
if (workers[i].result)
|
||||
{
|
||||
result++;
|
||||
}
|
||||
}
|
||||
threads[i]->join();
|
||||
delete threads[i];
|
||||
}
|
||||
delete[] threads;
|
||||
delete[] available;
|
||||
delete semaphore;
|
||||
delete mutex;
|
||||
|
||||
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:
|
||||
typedef int (*ParallelUnitFunction)(ParallelWork* work, int unit, void* data);
|
||||
|
||||
typedef enum
|
||||
{
|
||||
PARALLEL_WORKER_STATUS_VOID,
|
||||
PARALLEL_WORKER_STATUS_RUNNING,
|
||||
PARALLEL_WORKER_STATUS_DONE
|
||||
} ParallelWorkerStatus;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
Thread* thread;
|
||||
ParallelWork* work;
|
||||
ParallelWorkerStatus status;
|
||||
int unit;
|
||||
int result;
|
||||
} ParallelWorker;
|
||||
/**
|
||||
* Obscure thread class.
|
||||
*/
|
||||
class ParallelThread;
|
||||
friend class ParallelThread;
|
||||
|
||||
public:
|
||||
/**
|
||||
* 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.
|
||||
* @param units Number of units to handle.
|
||||
* @param data Custom data that will be passed to the callback.
|
||||
* @return The newly allocated handler.
|
||||
* This is a compatibility constructor for older code, use the constructor with ParallelWorker instead.
|
||||
*/
|
||||
ParallelWork(ParallelUnitFunction func, int units, void* data);
|
||||
|
||||
|
@ -52,15 +44,33 @@ public:
|
|||
/**
|
||||
* 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 running;
|
||||
ParallelUnitFunction unit_function;
|
||||
ParallelWorker workers[PARALLEL_MAX_THREADS];
|
||||
void* data;
|
||||
ParallelWorker *worker;
|
||||
bool worker_compat;
|
||||
|
||||
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
|
||||
* \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.
|
||||
|
|
|
@ -21,11 +21,13 @@ SOURCES += \
|
|||
RandomGenerator.cpp \
|
||||
Memory.cpp \
|
||||
ParallelWork.cpp \
|
||||
ParallelQueue.cpp \
|
||||
CacheFile.cpp \
|
||||
PictureWriter.cpp \
|
||||
Logs.cpp \
|
||||
ParallelPool.cpp
|
||||
ParallelPool.cpp \
|
||||
ParallelWorker.cpp \
|
||||
Semaphore.cpp \
|
||||
FileSystem.cpp
|
||||
|
||||
HEADERS += \
|
||||
system_global.h \
|
||||
|
@ -36,11 +38,13 @@ HEADERS += \
|
|||
RandomGenerator.h \
|
||||
Memory.h \
|
||||
ParallelWork.h \
|
||||
ParallelQueue.h \
|
||||
CacheFile.h \
|
||||
PictureWriter.h \
|
||||
Logs.h \
|
||||
ParallelPool.h
|
||||
ParallelPool.h \
|
||||
ParallelWorker.h \
|
||||
Semaphore.h \
|
||||
FileSystem.h
|
||||
|
||||
unix:!symbian {
|
||||
maemo5 {
|
||||
|
|
|
@ -15,11 +15,13 @@
|
|||
namespace paysages {
|
||||
namespace system {
|
||||
class PackStream;
|
||||
class ParallelQueue;
|
||||
class ParallelWork;
|
||||
class ParallelPool;
|
||||
class ParallelWorker;
|
||||
class Thread;
|
||||
class Mutex;
|
||||
class Semaphore;
|
||||
class PictureWriter;
|
||||
}
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
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");
|
||||
}
|
|
@ -1,157 +0,0 @@
|
|||
Changes for 1.7.0:
|
||||
|
||||
* New feature: death tests are supported on OpenBSD and in iOS
|
||||
simulator now.
|
||||
* New feature: Google Test now implements a protocol to allow
|
||||
a test runner to detect that a test program has exited
|
||||
prematurely and report it as a failure (before it would be
|
||||
falsely reported as a success if the exit code is 0).
|
||||
* New feature: Test::RecordProperty() can now be used outside of the
|
||||
lifespan of a test method, in which case it will be attributed to
|
||||
the current test case or the test program in the XML report.
|
||||
* New feature (potentially breaking): --gtest_list_tests now prints
|
||||
the type parameters and value parameters for each test.
|
||||
* Improvement: char pointers and char arrays are now escaped properly
|
||||
in failure messages.
|
||||
* Improvement: failure summary in XML reports now includes file and
|
||||
line information.
|
||||
* Improvement: the <testsuites> XML element now has a timestamp attribute.
|
||||
* Improvement: When --gtest_filter is specified, XML report now doesn't
|
||||
contain information about tests that are filtered out.
|
||||
* Fixed the bug where long --gtest_filter flag values are truncated in
|
||||
death tests.
|
||||
* Potentially breaking change: RUN_ALL_TESTS() is now implemented as a
|
||||
function instead of a macro in order to work better with Clang.
|
||||
* Compatibility fixes with C++ 11 and various platforms.
|
||||
* Bug/warning fixes.
|
||||
|
||||
Changes for 1.6.0:
|
||||
|
||||
* New feature: ADD_FAILURE_AT() for reporting a test failure at the
|
||||
given source location -- useful for writing testing utilities.
|
||||
* New feature: the universal value printer is moved from Google Mock
|
||||
to Google Test.
|
||||
* New feature: type parameters and value parameters are reported in
|
||||
the XML report now.
|
||||
* A gtest_disable_pthreads CMake option.
|
||||
* Colored output works in GNU Screen sessions now.
|
||||
* Parameters of value-parameterized tests are now printed in the
|
||||
textual output.
|
||||
* Failures from ad hoc test assertions run before RUN_ALL_TESTS() are
|
||||
now correctly reported.
|
||||
* Arguments of ASSERT_XY and EXPECT_XY no longer need to support << to
|
||||
ostream.
|
||||
* More complete handling of exceptions.
|
||||
* GTEST_ASSERT_XY can be used instead of ASSERT_XY in case the latter
|
||||
name is already used by another library.
|
||||
* --gtest_catch_exceptions is now true by default, allowing a test
|
||||
program to continue after an exception is thrown.
|
||||
* Value-parameterized test fixtures can now derive from Test and
|
||||
WithParamInterface<T> separately, easing conversion of legacy tests.
|
||||
* Death test messages are clearly marked to make them more
|
||||
distinguishable from other messages.
|
||||
* Compatibility fixes for Android, Google Native Client, MinGW, HP UX,
|
||||
PowerPC, Lucid autotools, libCStd, Sun C++, Borland C++ Builder (Code Gear),
|
||||
IBM XL C++ (Visual Age C++), and C++0x.
|
||||
* Bug fixes and implementation clean-ups.
|
||||
* Potentially incompatible changes: disables the harmful 'make install'
|
||||
command in autotools.
|
||||
|
||||
Changes for 1.5.0:
|
||||
|
||||
* New feature: assertions can be safely called in multiple threads
|
||||
where the pthreads library is available.
|
||||
* New feature: predicates used inside EXPECT_TRUE() and friends
|
||||
can now generate custom failure messages.
|
||||
* New feature: Google Test can now be compiled as a DLL.
|
||||
* New feature: fused source files are included.
|
||||
* New feature: prints help when encountering unrecognized Google Test flags.
|
||||
* Experimental feature: CMake build script (requires CMake 2.6.4+).
|
||||
* Experimental feature: the Pump script for meta programming.
|
||||
* double values streamed to an assertion are printed with enough precision
|
||||
to differentiate any two different values.
|
||||
* Google Test now works on Solaris and AIX.
|
||||
* Build and test script improvements.
|
||||
* Bug fixes and implementation clean-ups.
|
||||
|
||||
Potentially breaking changes:
|
||||
|
||||
* Stopped supporting VC++ 7.1 with exceptions disabled.
|
||||
* Dropped support for 'make install'.
|
||||
|
||||
Changes for 1.4.0:
|
||||
|
||||
* New feature: the event listener API
|
||||
* New feature: test shuffling
|
||||
* New feature: the XML report format is closer to junitreport and can
|
||||
be parsed by Hudson now.
|
||||
* New feature: when a test runs under Visual Studio, its failures are
|
||||
integrated in the IDE.
|
||||
* New feature: /MD(d) versions of VC++ projects.
|
||||
* New feature: elapsed time for the tests is printed by default.
|
||||
* New feature: comes with a TR1 tuple implementation such that Boost
|
||||
is no longer needed for Combine().
|
||||
* New feature: EXPECT_DEATH_IF_SUPPORTED macro and friends.
|
||||
* New feature: the Xcode project can now produce static gtest
|
||||
libraries in addition to a framework.
|
||||
* Compatibility fixes for Solaris, Cygwin, minGW, Windows Mobile,
|
||||
Symbian, gcc, and C++Builder.
|
||||
* Bug fixes and implementation clean-ups.
|
||||
|
||||
Changes for 1.3.0:
|
||||
|
||||
* New feature: death tests on Windows, Cygwin, and Mac.
|
||||
* New feature: ability to use Google Test assertions in other testing
|
||||
frameworks.
|
||||
* New feature: ability to run disabled test via
|
||||
--gtest_also_run_disabled_tests.
|
||||
* New feature: the --help flag for printing the usage.
|
||||
* New feature: access to Google Test flag values in user code.
|
||||
* New feature: a script that packs Google Test into one .h and one
|
||||
.cc file for easy deployment.
|
||||
* New feature: support for distributing test functions to multiple
|
||||
machines (requires support from the test runner).
|
||||
* Bug fixes and implementation clean-ups.
|
||||
|
||||
Changes for 1.2.1:
|
||||
|
||||
* Compatibility fixes for Linux IA-64 and IBM z/OS.
|
||||
* Added support for using Boost and other TR1 implementations.
|
||||
* Changes to the build scripts to support upcoming release of Google C++
|
||||
Mocking Framework.
|
||||
* Added Makefile to the distribution package.
|
||||
* Improved build instructions in README.
|
||||
|
||||
Changes for 1.2.0:
|
||||
|
||||
* New feature: value-parameterized tests.
|
||||
* New feature: the ASSERT/EXPECT_(NON)FATAL_FAILURE(_ON_ALL_THREADS)
|
||||
macros.
|
||||
* Changed the XML report format to match JUnit/Ant's.
|
||||
* Added tests to the Xcode project.
|
||||
* Added scons/SConscript for building with SCons.
|
||||
* Added src/gtest-all.cc for building Google Test from a single file.
|
||||
* Fixed compatibility with Solaris and z/OS.
|
||||
* Enabled running Python tests on systems with python 2.3 installed,
|
||||
e.g. Mac OS X 10.4.
|
||||
* Bug fixes.
|
||||
|
||||
Changes for 1.1.0:
|
||||
|
||||
* New feature: type-parameterized tests.
|
||||
* New feature: exception assertions.
|
||||
* New feature: printing elapsed time of tests.
|
||||
* Improved the robustness of death tests.
|
||||
* Added an Xcode project and samples.
|
||||
* Adjusted the output format on Windows to be understandable by Visual Studio.
|
||||
* Minor bug fixes.
|
||||
|
||||
Changes for 1.0.1:
|
||||
|
||||
* Added project files for Visual Studio 7.1.
|
||||
* Fixed issues with compiling on Mac OS X.
|
||||
* Fixed issues with compiling on Cygwin.
|
||||
|
||||
Changes for 1.0.0:
|
||||
|
||||
* Initial Open Source release of Google Test
|
|
@ -1,252 +0,0 @@
|
|||
########################################################################
|
||||
# CMake build script for Google Test.
|
||||
#
|
||||
# To run the tests for Google Test itself on Linux, use 'make test' or
|
||||
# ctest. You can select which tests to run using 'ctest -R regex'.
|
||||
# For more options, run 'ctest --help'.
|
||||
|
||||
# BUILD_SHARED_LIBS is a standard CMake variable, but we declare it here to
|
||||
# make it prominent in the GUI.
|
||||
option(BUILD_SHARED_LIBS "Build shared libraries (DLLs)." OFF)
|
||||
|
||||
# When other libraries are using a shared version of runtime libraries,
|
||||
# Google Test also has to use one.
|
||||
option(
|
||||
gtest_force_shared_crt
|
||||
"Use shared (DLL) run-time lib even when Google Test is built as static lib."
|
||||
OFF)
|
||||
|
||||
option(gtest_build_tests "Build all of gtest's own tests." OFF)
|
||||
|
||||
option(gtest_build_samples "Build gtest's sample programs." OFF)
|
||||
|
||||
option(gtest_disable_pthreads "Disable uses of pthreads in gtest." OFF)
|
||||
|
||||
# Defines pre_project_set_up_hermetic_build() and set_up_hermetic_build().
|
||||
include(cmake/hermetic_build.cmake OPTIONAL)
|
||||
|
||||
if (COMMAND pre_project_set_up_hermetic_build)
|
||||
pre_project_set_up_hermetic_build()
|
||||
endif()
|
||||
|
||||
########################################################################
|
||||
#
|
||||
# Project-wide settings
|
||||
|
||||
# Name of the project.
|
||||
#
|
||||
# CMake files in this project can refer to the root source directory
|
||||
# as ${gtest_SOURCE_DIR} and to the root binary directory as
|
||||
# ${gtest_BINARY_DIR}.
|
||||
# Language "C" is required for find_package(Threads).
|
||||
project(gtest CXX C)
|
||||
cmake_minimum_required(VERSION 2.6.2)
|
||||
|
||||
if (COMMAND set_up_hermetic_build)
|
||||
set_up_hermetic_build()
|
||||
endif()
|
||||
|
||||
# Define helper functions and macros used by Google Test.
|
||||
include(cmake/internal_utils.cmake)
|
||||
|
||||
config_compiler_and_linker() # Defined in internal_utils.cmake.
|
||||
|
||||
# Where Google Test's .h files can be found.
|
||||
include_directories(
|
||||
${gtest_SOURCE_DIR}/include
|
||||
${gtest_SOURCE_DIR})
|
||||
|
||||
# Where Google Test's libraries can be found.
|
||||
link_directories(${gtest_BINARY_DIR}/src)
|
||||
|
||||
########################################################################
|
||||
#
|
||||
# Defines the gtest & gtest_main libraries. User tests should link
|
||||
# with one of them.
|
||||
|
||||
# Google Test libraries. We build them using more strict warnings than what
|
||||
# are used for other targets, to ensure that gtest can be compiled by a user
|
||||
# aggressive about warnings.
|
||||
cxx_library(gtest "${cxx_strict}" src/gtest-all.cc)
|
||||
cxx_library(gtest_main "${cxx_strict}" src/gtest_main.cc)
|
||||
target_link_libraries(gtest_main gtest)
|
||||
|
||||
########################################################################
|
||||
#
|
||||
# Samples on how to link user tests with gtest or gtest_main.
|
||||
#
|
||||
# They are not built by default. To build them, set the
|
||||
# gtest_build_samples option to ON. You can do it by running ccmake
|
||||
# or specifying the -Dgtest_build_samples=ON flag when running cmake.
|
||||
|
||||
if (gtest_build_samples)
|
||||
cxx_executable(sample1_unittest samples gtest_main samples/sample1.cc)
|
||||
cxx_executable(sample2_unittest samples gtest_main samples/sample2.cc)
|
||||
cxx_executable(sample3_unittest samples gtest_main)
|
||||
cxx_executable(sample4_unittest samples gtest_main samples/sample4.cc)
|
||||
cxx_executable(sample5_unittest samples gtest_main samples/sample1.cc)
|
||||
cxx_executable(sample6_unittest samples gtest_main)
|
||||
cxx_executable(sample7_unittest samples gtest_main)
|
||||
cxx_executable(sample8_unittest samples gtest_main)
|
||||
cxx_executable(sample9_unittest samples gtest)
|
||||
cxx_executable(sample10_unittest samples gtest)
|
||||
endif()
|
||||
|
||||
########################################################################
|
||||
#
|
||||
# Google Test's own tests.
|
||||
#
|
||||
# You can skip this section if you aren't interested in testing
|
||||
# Google Test itself.
|
||||
#
|
||||
# The tests are not built by default. To build them, set the
|
||||
# gtest_build_tests option to ON. You can do it by running ccmake
|
||||
# or specifying the -Dgtest_build_tests=ON flag when running cmake.
|
||||
|
||||
if (gtest_build_tests)
|
||||
# This must be set in the root directory for the tests to be run by
|
||||
# 'make test' or ctest.
|
||||
enable_testing()
|
||||
|
||||
############################################################
|
||||
# C++ tests built with standard compiler flags.
|
||||
|
||||
cxx_test(gtest-death-test_test gtest_main)
|
||||
cxx_test(gtest_environment_test gtest)
|
||||
cxx_test(gtest-filepath_test gtest_main)
|
||||
cxx_test(gtest-linked_ptr_test gtest_main)
|
||||
cxx_test(gtest-listener_test gtest_main)
|
||||
cxx_test(gtest_main_unittest gtest_main)
|
||||
cxx_test(gtest-message_test gtest_main)
|
||||
cxx_test(gtest_no_test_unittest gtest)
|
||||
cxx_test(gtest-options_test gtest_main)
|
||||
cxx_test(gtest-param-test_test gtest
|
||||
test/gtest-param-test2_test.cc)
|
||||
cxx_test(gtest-port_test gtest_main)
|
||||
cxx_test(gtest_pred_impl_unittest gtest_main)
|
||||
cxx_test(gtest_premature_exit_test gtest
|
||||
test/gtest_premature_exit_test.cc)
|
||||
cxx_test(gtest-printers_test gtest_main)
|
||||
cxx_test(gtest_prod_test gtest_main
|
||||
test/production.cc)
|
||||
cxx_test(gtest_repeat_test gtest)
|
||||
cxx_test(gtest_sole_header_test gtest_main)
|
||||
cxx_test(gtest_stress_test gtest)
|
||||
cxx_test(gtest-test-part_test gtest_main)
|
||||
cxx_test(gtest_throw_on_failure_ex_test gtest)
|
||||
cxx_test(gtest-typed-test_test gtest_main
|
||||
test/gtest-typed-test2_test.cc)
|
||||
cxx_test(gtest_unittest gtest_main)
|
||||
cxx_test(gtest-unittest-api_test gtest)
|
||||
|
||||
############################################################
|
||||
# C++ tests built with non-standard compiler flags.
|
||||
|
||||
# MSVC 7.1 does not support STL with exceptions disabled.
|
||||
if (NOT MSVC OR MSVC_VERSION GREATER 1310)
|
||||
cxx_library(gtest_no_exception "${cxx_no_exception}"
|
||||
src/gtest-all.cc)
|
||||
cxx_library(gtest_main_no_exception "${cxx_no_exception}"
|
||||
src/gtest-all.cc src/gtest_main.cc)
|
||||
endif()
|
||||
cxx_library(gtest_main_no_rtti "${cxx_no_rtti}"
|
||||
src/gtest-all.cc src/gtest_main.cc)
|
||||
|
||||
cxx_test_with_flags(gtest-death-test_ex_nocatch_test
|
||||
"${cxx_exception} -DGTEST_ENABLE_CATCH_EXCEPTIONS_=0"
|
||||
gtest test/gtest-death-test_ex_test.cc)
|
||||
cxx_test_with_flags(gtest-death-test_ex_catch_test
|
||||
"${cxx_exception} -DGTEST_ENABLE_CATCH_EXCEPTIONS_=1"
|
||||
gtest test/gtest-death-test_ex_test.cc)
|
||||
|
||||
cxx_test_with_flags(gtest_no_rtti_unittest "${cxx_no_rtti}"
|
||||
gtest_main_no_rtti test/gtest_unittest.cc)
|
||||
|
||||
cxx_shared_library(gtest_dll "${cxx_default}"
|
||||
src/gtest-all.cc src/gtest_main.cc)
|
||||
|
||||
cxx_executable_with_flags(gtest_dll_test_ "${cxx_default}"
|
||||
gtest_dll test/gtest_all_test.cc)
|
||||
set_target_properties(gtest_dll_test_
|
||||
PROPERTIES
|
||||
COMPILE_DEFINITIONS "GTEST_LINKED_AS_SHARED_LIBRARY=1")
|
||||
|
||||
if (NOT MSVC OR NOT MSVC_VERSION EQUAL 1600)
|
||||
# The C++ Standard specifies tuple_element<int, class>.
|
||||
# Yet MSVC 10's <utility> declares tuple_element<size_t, class>.
|
||||
# That declaration conflicts with our own standard-conforming
|
||||
# tuple implementation. Therefore using our own tuple with
|
||||
# MSVC 10 doesn't compile.
|
||||
cxx_library(gtest_main_use_own_tuple "${cxx_use_own_tuple}"
|
||||
src/gtest-all.cc src/gtest_main.cc)
|
||||
|
||||
cxx_test_with_flags(gtest-tuple_test "${cxx_use_own_tuple}"
|
||||
gtest_main_use_own_tuple test/gtest-tuple_test.cc)
|
||||
|
||||
cxx_test_with_flags(gtest_use_own_tuple_test "${cxx_use_own_tuple}"
|
||||
gtest_main_use_own_tuple
|
||||
test/gtest-param-test_test.cc test/gtest-param-test2_test.cc)
|
||||
endif()
|
||||
|
||||
############################################################
|
||||
# Python tests.
|
||||
|
||||
cxx_executable(gtest_break_on_failure_unittest_ test gtest)
|
||||
py_test(gtest_break_on_failure_unittest)
|
||||
|
||||
# MSVC 7.1 does not support STL with exceptions disabled.
|
||||
if (NOT MSVC OR MSVC_VERSION GREATER 1310)
|
||||
cxx_executable_with_flags(
|
||||
gtest_catch_exceptions_no_ex_test_
|
||||
"${cxx_no_exception}"
|
||||
gtest_main_no_exception
|
||||
test/gtest_catch_exceptions_test_.cc)
|
||||
endif()
|
||||
|
||||
cxx_executable_with_flags(
|
||||
gtest_catch_exceptions_ex_test_
|
||||
"${cxx_exception}"
|
||||
gtest_main
|
||||
test/gtest_catch_exceptions_test_.cc)
|
||||
py_test(gtest_catch_exceptions_test)
|
||||
|
||||
cxx_executable(gtest_color_test_ test gtest)
|
||||
py_test(gtest_color_test)
|
||||
|
||||
cxx_executable(gtest_env_var_test_ test gtest)
|
||||
py_test(gtest_env_var_test)
|
||||
|
||||
cxx_executable(gtest_filter_unittest_ test gtest)
|
||||
py_test(gtest_filter_unittest)
|
||||
|
||||
cxx_executable(gtest_help_test_ test gtest_main)
|
||||
py_test(gtest_help_test)
|
||||
|
||||
cxx_executable(gtest_list_tests_unittest_ test gtest)
|
||||
py_test(gtest_list_tests_unittest)
|
||||
|
||||
cxx_executable(gtest_output_test_ test gtest)
|
||||
py_test(gtest_output_test)
|
||||
|
||||
cxx_executable(gtest_shuffle_test_ test gtest)
|
||||
py_test(gtest_shuffle_test)
|
||||
|
||||
# MSVC 7.1 does not support STL with exceptions disabled.
|
||||
if (NOT MSVC OR MSVC_VERSION GREATER 1310)
|
||||
cxx_executable(gtest_throw_on_failure_test_ test gtest_no_exception)
|
||||
set_target_properties(gtest_throw_on_failure_test_
|
||||
PROPERTIES
|
||||
COMPILE_FLAGS "${cxx_no_exception}")
|
||||
py_test(gtest_throw_on_failure_test)
|
||||
endif()
|
||||
|
||||
cxx_executable(gtest_uninitialized_test_ test gtest)
|
||||
py_test(gtest_uninitialized_test)
|
||||
|
||||
cxx_executable(gtest_xml_outfile1_test_ test gtest_main)
|
||||
cxx_executable(gtest_xml_outfile2_test_ test gtest_main)
|
||||
py_test(gtest_xml_outfiles_test)
|
||||
|
||||
cxx_executable(gtest_xml_output_unittest_ test gtest)
|
||||
py_test(gtest_xml_output_unittest)
|
||||
endif()
|
|
@ -1,37 +0,0 @@
|
|||
# This file contains a list of people who've made non-trivial
|
||||
# contribution to the Google C++ Testing Framework project. People
|
||||
# who commit code to the project are encouraged to add their names
|
||||
# here. Please keep the list sorted by first names.
|
||||
|
||||
Ajay Joshi <jaj@google.com>
|
||||
Balázs Dán <balazs.dan@gmail.com>
|
||||
Bharat Mediratta <bharat@menalto.com>
|
||||
Chandler Carruth <chandlerc@google.com>
|
||||
Chris Prince <cprince@google.com>
|
||||
Chris Taylor <taylorc@google.com>
|
||||
Dan Egnor <egnor@google.com>
|
||||
Eric Roman <eroman@chromium.org>
|
||||
Hady Zalek <hady.zalek@gmail.com>
|
||||
Jeffrey Yasskin <jyasskin@google.com>
|
||||
Jói Sigurðsson <joi@google.com>
|
||||
Keir Mierle <mierle@gmail.com>
|
||||
Keith Ray <keith.ray@gmail.com>
|
||||
Kenton Varda <kenton@google.com>
|
||||
Manuel Klimek <klimek@google.com>
|
||||
Markus Heule <markus.heule@gmail.com>
|
||||
Mika Raento <mikie@iki.fi>
|
||||
Miklós Fazekas <mfazekas@szemafor.com>
|
||||
Pasi Valminen <pasi.valminen@gmail.com>
|
||||
Patrick Hanna <phanna@google.com>
|
||||
Patrick Riley <pfr@google.com>
|
||||
Peter Kaminski <piotrk@google.com>
|
||||
Preston Jackson <preston.a.jackson@gmail.com>
|
||||
Rainer Klaffenboeck <rainer.klaffenboeck@dynatrace.com>
|
||||
Russ Cox <rsc@google.com>
|
||||
Russ Rufer <russ@pentad.com>
|
||||
Sean Mcafee <eefacm@gmail.com>
|
||||
Sigurður Ásgeirsson <siggi@google.com>
|
||||
Tracy Bialik <tracy@pentad.com>
|
||||
Vadim Berman <vadimb@google.com>
|
||||
Vlad Losev <vladl@google.com>
|
||||
Zhanyong Wan <wan@google.com>
|
|
@ -1,28 +0,0 @@
|
|||
Copyright 2008, Google Inc.
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
* Neither the name of Google Inc. nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
@ -1,306 +0,0 @@
|
|||
# Automake file
|
||||
|
||||
ACLOCAL_AMFLAGS = -I m4
|
||||
|
||||
# Nonstandard package files for distribution
|
||||
EXTRA_DIST = \
|
||||
CHANGES \
|
||||
CONTRIBUTORS \
|
||||
LICENSE \
|
||||
include/gtest/gtest-param-test.h.pump \
|
||||
include/gtest/internal/gtest-param-util-generated.h.pump \
|
||||
include/gtest/internal/gtest-tuple.h.pump \
|
||||
include/gtest/internal/gtest-type-util.h.pump \
|
||||
make/Makefile \
|
||||
scripts/fuse_gtest_files.py \
|
||||
scripts/gen_gtest_pred_impl.py \
|
||||
scripts/pump.py \
|
||||
scripts/test/Makefile
|
||||
|
||||
# gtest source files that we don't compile directly. They are
|
||||
# #included by gtest-all.cc.
|
||||
GTEST_SRC = \
|
||||
src/gtest-death-test.cc \
|
||||
src/gtest-filepath.cc \
|
||||
src/gtest-internal-inl.h \
|
||||
src/gtest-port.cc \
|
||||
src/gtest-printers.cc \
|
||||
src/gtest-test-part.cc \
|
||||
src/gtest-typed-test.cc \
|
||||
src/gtest.cc
|
||||
|
||||
EXTRA_DIST += $(GTEST_SRC)
|
||||
|
||||
# Sample files that we don't compile.
|
||||
EXTRA_DIST += \
|
||||
samples/prime_tables.h \
|
||||
samples/sample2_unittest.cc \
|
||||
samples/sample3_unittest.cc \
|
||||
samples/sample4_unittest.cc \
|
||||
samples/sample5_unittest.cc \
|
||||
samples/sample6_unittest.cc \
|
||||
samples/sample7_unittest.cc \
|
||||
samples/sample8_unittest.cc \
|
||||
samples/sample9_unittest.cc
|
||||
|
||||
# C++ test files that we don't compile directly.
|
||||
EXTRA_DIST += \
|
||||
test/gtest-death-test_ex_test.cc \
|
||||
test/gtest-death-test_test.cc \
|
||||
test/gtest-filepath_test.cc \
|
||||
test/gtest-linked_ptr_test.cc \
|
||||
test/gtest-listener_test.cc \
|
||||
test/gtest-message_test.cc \
|
||||
test/gtest-options_test.cc \
|
||||
test/gtest-param-test2_test.cc \
|
||||
test/gtest-param-test2_test.cc \
|
||||
test/gtest-param-test_test.cc \
|
||||
test/gtest-param-test_test.cc \
|
||||
test/gtest-param-test_test.h \
|
||||
test/gtest-port_test.cc \
|
||||
test/gtest_premature_exit_test.cc \
|
||||
test/gtest-printers_test.cc \
|
||||
test/gtest-test-part_test.cc \
|
||||
test/gtest-tuple_test.cc \
|
||||
test/gtest-typed-test2_test.cc \
|
||||
test/gtest-typed-test_test.cc \
|
||||
test/gtest-typed-test_test.h \
|
||||
test/gtest-unittest-api_test.cc \
|
||||
test/gtest_break_on_failure_unittest_.cc \
|
||||
test/gtest_catch_exceptions_test_.cc \
|
||||
test/gtest_color_test_.cc \
|
||||
test/gtest_env_var_test_.cc \
|
||||
test/gtest_environment_test.cc \
|
||||
test/gtest_filter_unittest_.cc \
|
||||
test/gtest_help_test_.cc \
|
||||
test/gtest_list_tests_unittest_.cc \
|
||||
test/gtest_main_unittest.cc \
|
||||
test/gtest_no_test_unittest.cc \
|
||||
test/gtest_output_test_.cc \
|
||||
test/gtest_pred_impl_unittest.cc \
|
||||
test/gtest_prod_test.cc \
|
||||
test/gtest_repeat_test.cc \
|
||||
test/gtest_shuffle_test_.cc \
|
||||
test/gtest_sole_header_test.cc \
|
||||
test/gtest_stress_test.cc \
|
||||
test/gtest_throw_on_failure_ex_test.cc \
|
||||
test/gtest_throw_on_failure_test_.cc \
|
||||
test/gtest_uninitialized_test_.cc \
|
||||
test/gtest_unittest.cc \
|
||||
test/gtest_unittest.cc \
|
||||
test/gtest_xml_outfile1_test_.cc \
|
||||
test/gtest_xml_outfile2_test_.cc \
|
||||
test/gtest_xml_output_unittest_.cc \
|
||||
test/production.cc \
|
||||
test/production.h
|
||||
|
||||
# Python tests that we don't run.
|
||||
EXTRA_DIST += \
|
||||
test/gtest_break_on_failure_unittest.py \
|
||||
test/gtest_catch_exceptions_test.py \
|
||||
test/gtest_color_test.py \
|
||||
test/gtest_env_var_test.py \
|
||||
test/gtest_filter_unittest.py \
|
||||
test/gtest_help_test.py \
|
||||
test/gtest_list_tests_unittest.py \
|
||||
test/gtest_output_test.py \
|
||||
test/gtest_output_test_golden_lin.txt \
|
||||
test/gtest_shuffle_test.py \
|
||||
test/gtest_test_utils.py \
|
||||
test/gtest_throw_on_failure_test.py \
|
||||
test/gtest_uninitialized_test.py \
|
||||
test/gtest_xml_outfiles_test.py \
|
||||
test/gtest_xml_output_unittest.py \
|
||||
test/gtest_xml_test_utils.py
|
||||
|
||||
# CMake script
|
||||
EXTRA_DIST += \
|
||||
CMakeLists.txt \
|
||||
cmake/internal_utils.cmake
|
||||
|
||||
# MSVC project files
|
||||
EXTRA_DIST += \
|
||||
msvc/gtest-md.sln \
|
||||
msvc/gtest-md.vcproj \
|
||||
msvc/gtest.sln \
|
||||
msvc/gtest.vcproj \
|
||||
msvc/gtest_main-md.vcproj \
|
||||
msvc/gtest_main.vcproj \
|
||||
msvc/gtest_prod_test-md.vcproj \
|
||||
msvc/gtest_prod_test.vcproj \
|
||||
msvc/gtest_unittest-md.vcproj \
|
||||
msvc/gtest_unittest.vcproj
|
||||
|
||||
# xcode project files
|
||||
EXTRA_DIST += \
|
||||
xcode/Config/DebugProject.xcconfig \
|
||||
xcode/Config/FrameworkTarget.xcconfig \
|
||||
xcode/Config/General.xcconfig \
|
||||
xcode/Config/ReleaseProject.xcconfig \
|
||||
xcode/Config/StaticLibraryTarget.xcconfig \
|
||||
xcode/Config/TestTarget.xcconfig \
|
||||
xcode/Resources/Info.plist \
|
||||
xcode/Scripts/runtests.sh \
|
||||
xcode/Scripts/versiongenerate.py \
|
||||
xcode/gtest.xcodeproj/project.pbxproj
|
||||
|
||||
# xcode sample files
|
||||
EXTRA_DIST += \
|
||||
xcode/Samples/FrameworkSample/Info.plist \
|
||||
xcode/Samples/FrameworkSample/WidgetFramework.xcodeproj/project.pbxproj \
|
||||
xcode/Samples/FrameworkSample/runtests.sh \
|
||||
xcode/Samples/FrameworkSample/widget.cc \
|
||||
xcode/Samples/FrameworkSample/widget.h \
|
||||
xcode/Samples/FrameworkSample/widget_test.cc
|
||||
|
||||
# C++Builder project files
|
||||
EXTRA_DIST += \
|
||||
codegear/gtest.cbproj \
|
||||
codegear/gtest.groupproj \
|
||||
codegear/gtest_all.cc \
|
||||
codegear/gtest_link.cc \
|
||||
codegear/gtest_main.cbproj \
|
||||
codegear/gtest_unittest.cbproj
|
||||
|
||||
# Distribute and install M4 macro
|
||||
m4datadir = $(datadir)/aclocal
|
||||
m4data_DATA = m4/gtest.m4
|
||||
EXTRA_DIST += $(m4data_DATA)
|
||||
|
||||
# We define the global AM_CPPFLAGS as everything we compile includes from these
|
||||
# directories.
|
||||
AM_CPPFLAGS = -I$(srcdir) -I$(srcdir)/include
|
||||
|
||||
# Modifies compiler and linker flags for pthreads compatibility.
|
||||
if HAVE_PTHREADS
|
||||
AM_CXXFLAGS = @PTHREAD_CFLAGS@ -DGTEST_HAS_PTHREAD=1
|
||||
AM_LIBS = @PTHREAD_LIBS@
|
||||
else
|
||||
AM_CXXFLAGS = -DGTEST_HAS_PTHREAD=0
|
||||
endif
|
||||
|
||||
# Build rules for libraries.
|
||||
lib_LTLIBRARIES = lib/libgtest.la lib/libgtest_main.la
|
||||
|
||||
lib_libgtest_la_SOURCES = src/gtest-all.cc
|
||||
|
||||
pkginclude_HEADERS = \
|
||||
include/gtest/gtest-death-test.h \
|
||||
include/gtest/gtest-message.h \
|
||||
include/gtest/gtest-param-test.h \
|
||||
include/gtest/gtest-printers.h \
|
||||
include/gtest/gtest-spi.h \
|
||||
include/gtest/gtest-test-part.h \
|
||||
include/gtest/gtest-typed-test.h \
|
||||
include/gtest/gtest.h \
|
||||
include/gtest/gtest_pred_impl.h \
|
||||
include/gtest/gtest_prod.h
|
||||
|
||||
pkginclude_internaldir = $(pkgincludedir)/internal
|
||||
pkginclude_internal_HEADERS = \
|
||||
include/gtest/internal/gtest-death-test-internal.h \
|
||||
include/gtest/internal/gtest-filepath.h \
|
||||
include/gtest/internal/gtest-internal.h \
|
||||
include/gtest/internal/gtest-linked_ptr.h \
|
||||
include/gtest/internal/gtest-param-util-generated.h \
|
||||
include/gtest/internal/gtest-param-util.h \
|
||||
include/gtest/internal/gtest-port.h \
|
||||
include/gtest/internal/gtest-string.h \
|
||||
include/gtest/internal/gtest-tuple.h \
|
||||
include/gtest/internal/gtest-type-util.h
|
||||
|
||||
lib_libgtest_main_la_SOURCES = src/gtest_main.cc
|
||||
lib_libgtest_main_la_LIBADD = lib/libgtest.la
|
||||
|
||||
# Bulid rules for samples and tests. Automake's naming for some of
|
||||
# these variables isn't terribly obvious, so this is a brief
|
||||
# reference:
|
||||
#
|
||||
# TESTS -- Programs run automatically by "make check"
|
||||
# check_PROGRAMS -- Programs built by "make check" but not necessarily run
|
||||
|
||||
noinst_LTLIBRARIES = samples/libsamples.la
|
||||
|
||||
samples_libsamples_la_SOURCES = \
|
||||
samples/sample1.cc \
|
||||
samples/sample1.h \
|
||||
samples/sample2.cc \
|
||||
samples/sample2.h \
|
||||
samples/sample3-inl.h \
|
||||
samples/sample4.cc \
|
||||
samples/sample4.h
|
||||
|
||||
TESTS=
|
||||
TESTS_ENVIRONMENT = GTEST_SOURCE_DIR="$(srcdir)/test" \
|
||||
GTEST_BUILD_DIR="$(top_builddir)/test"
|
||||
check_PROGRAMS=
|
||||
|
||||
# A simple sample on using gtest.
|
||||
TESTS += samples/sample1_unittest
|
||||
check_PROGRAMS += samples/sample1_unittest
|
||||
samples_sample1_unittest_SOURCES = samples/sample1_unittest.cc
|
||||
samples_sample1_unittest_LDADD = lib/libgtest_main.la \
|
||||
lib/libgtest.la \
|
||||
samples/libsamples.la
|
||||
|
||||
# Another sample. It also verifies that libgtest works.
|
||||
TESTS += samples/sample10_unittest
|
||||
check_PROGRAMS += samples/sample10_unittest
|
||||
samples_sample10_unittest_SOURCES = samples/sample10_unittest.cc
|
||||
samples_sample10_unittest_LDADD = lib/libgtest.la
|
||||
|
||||
# This tests most constructs of gtest and verifies that libgtest_main
|
||||
# and libgtest work.
|
||||
TESTS += test/gtest_all_test
|
||||
check_PROGRAMS += test/gtest_all_test
|
||||
test_gtest_all_test_SOURCES = test/gtest_all_test.cc
|
||||
test_gtest_all_test_LDADD = lib/libgtest_main.la \
|
||||
lib/libgtest.la
|
||||
|
||||
# Tests that fused gtest files compile and work.
|
||||
FUSED_GTEST_SRC = \
|
||||
fused-src/gtest/gtest-all.cc \
|
||||
fused-src/gtest/gtest.h \
|
||||
fused-src/gtest/gtest_main.cc
|
||||
|
||||
if HAVE_PYTHON
|
||||
TESTS += test/fused_gtest_test
|
||||
check_PROGRAMS += test/fused_gtest_test
|
||||
test_fused_gtest_test_SOURCES = $(FUSED_GTEST_SRC) \
|
||||
samples/sample1.cc samples/sample1_unittest.cc
|
||||
test_fused_gtest_test_CPPFLAGS = -I"$(srcdir)/fused-src"
|
||||
|
||||
# Build rules for putting fused Google Test files into the distribution
|
||||
# package. The user can also create those files by manually running
|
||||
# scripts/fuse_gtest_files.py.
|
||||
$(test_fused_gtest_test_SOURCES): fused-gtest
|
||||
|
||||
fused-gtest: $(pkginclude_HEADERS) $(pkginclude_internal_HEADERS) \
|
||||
$(GTEST_SRC) src/gtest-all.cc src/gtest_main.cc \
|
||||
scripts/fuse_gtest_files.py
|
||||
mkdir -p "$(srcdir)/fused-src"
|
||||
chmod -R u+w "$(srcdir)/fused-src"
|
||||
rm -f "$(srcdir)/fused-src/gtest/gtest-all.cc"
|
||||
rm -f "$(srcdir)/fused-src/gtest/gtest.h"
|
||||
"$(srcdir)/scripts/fuse_gtest_files.py" "$(srcdir)/fused-src"
|
||||
cp -f "$(srcdir)/src/gtest_main.cc" "$(srcdir)/fused-src/gtest/"
|
||||
|
||||
maintainer-clean-local:
|
||||
rm -rf "$(srcdir)/fused-src"
|
||||
endif
|
||||
|
||||
# Death tests may produce core dumps in the build directory. In case
|
||||
# this happens, clean them to keep distcleancheck happy.
|
||||
CLEANFILES = core
|
||||
|
||||
# Disables 'make install' as installing a compiled version of Google
|
||||
# Test can lead to undefined behavior due to violation of the
|
||||
# One-Definition Rule.
|
||||
|
||||
install-exec-local:
|
||||
echo "'make install' is dangerous and not supported. Instead, see README for how to integrate Google Test into your build system."
|
||||
false
|
||||
|
||||
install-data-local:
|
||||
echo "'make install' is dangerous and not supported. Instead, see README for how to integrate Google Test into your build system."
|
||||
false
|
|
@ -1,435 +0,0 @@
|
|||
Google C++ Testing Framework
|
||||
============================
|
||||
|
||||
http://code.google.com/p/googletest/
|
||||
|
||||
Overview
|
||||
--------
|
||||
|
||||
Google's framework for writing C++ tests on a variety of platforms
|
||||
(Linux, Mac OS X, Windows, Windows CE, Symbian, etc). Based on the
|
||||
xUnit architecture. Supports automatic test discovery, a rich set of
|
||||
assertions, user-defined assertions, death tests, fatal and non-fatal
|
||||
failures, various options for running the tests, and XML test report
|
||||
generation.
|
||||
|
||||
Please see the project page above for more information as well as the
|
||||
mailing list for questions, discussions, and development. There is
|
||||
also an IRC channel on OFTC (irc.oftc.net) #gtest available. Please
|
||||
join us!
|
||||
|
||||
Requirements for End Users
|
||||
--------------------------
|
||||
|
||||
Google Test is designed to have fairly minimal requirements to build
|
||||
and use with your projects, but there are some. Currently, we support
|
||||
Linux, Windows, Mac OS X, and Cygwin. We will also make our best
|
||||
effort to support other platforms (e.g. Solaris, AIX, and z/OS).
|
||||
However, since core members of the Google Test project have no access
|
||||
to these platforms, Google Test may have outstanding issues there. If
|
||||
you notice any problems on your platform, please notify
|
||||
googletestframework@googlegroups.com. Patches for fixing them are
|
||||
even more welcome!
|
||||
|
||||
### Linux Requirements ###
|
||||
|
||||
These are the base requirements to build and use Google Test from a source
|
||||
package (as described below):
|
||||
* GNU-compatible Make or gmake
|
||||
* POSIX-standard shell
|
||||
* POSIX(-2) Regular Expressions (regex.h)
|
||||
* A C++98-standard-compliant compiler
|
||||
|
||||
### Windows Requirements ###
|
||||
|
||||
* Microsoft Visual C++ 7.1 or newer
|
||||
|
||||
### Cygwin Requirements ###
|
||||
|
||||
* Cygwin 1.5.25-14 or newer
|
||||
|
||||
### Mac OS X Requirements ###
|
||||
|
||||
* Mac OS X 10.4 Tiger or newer
|
||||
* Developer Tools Installed
|
||||
|
||||
Also, you'll need CMake 2.6.4 or higher if you want to build the
|
||||
samples using the provided CMake script, regardless of the platform.
|
||||
|
||||
Requirements for Contributors
|
||||
-----------------------------
|
||||
|
||||
We welcome patches. If you plan to contribute a patch, you need to
|
||||
build Google Test and its own tests from an SVN checkout (described
|
||||
below), which has further requirements:
|
||||
|
||||
* Python version 2.3 or newer (for running some of the tests and
|
||||
re-generating certain source files from templates)
|
||||
* CMake 2.6.4 or newer
|
||||
|
||||
Getting the Source
|
||||
------------------
|
||||
|
||||
There are two primary ways of getting Google Test's source code: you
|
||||
can download a stable source release in your preferred archive format,
|
||||
or directly check out the source from our Subversion (SVN) repositary.
|
||||
The SVN checkout requires a few extra steps and some extra software
|
||||
packages on your system, but lets you track the latest development and
|
||||
make patches much more easily, so we highly encourage it.
|
||||
|
||||
### Source Package ###
|
||||
|
||||
Google Test is released in versioned source packages which can be
|
||||
downloaded from the download page [1]. Several different archive
|
||||
formats are provided, but the only difference is the tools used to
|
||||
manipulate them, and the size of the resulting file. Download
|
||||
whichever you are most comfortable with.
|
||||
|
||||
[1] http://code.google.com/p/googletest/downloads/list
|
||||
|
||||
Once the package is downloaded, expand it using whichever tools you
|
||||
prefer for that type. This will result in a new directory with the
|
||||
name "gtest-X.Y.Z" which contains all of the source code. Here are
|
||||
some examples on Linux:
|
||||
|
||||
tar -xvzf gtest-X.Y.Z.tar.gz
|
||||
tar -xvjf gtest-X.Y.Z.tar.bz2
|
||||
unzip gtest-X.Y.Z.zip
|
||||
|
||||
### SVN Checkout ###
|
||||
|
||||
To check out the main branch (also known as the "trunk") of Google
|
||||
Test, run the following Subversion command:
|
||||
|
||||
svn checkout http://googletest.googlecode.com/svn/trunk/ gtest-svn
|
||||
|
||||
Setting up the Build
|
||||
--------------------
|
||||
|
||||
To build Google Test and your tests that use it, you need to tell your
|
||||
build system where to find its headers and source files. The exact
|
||||
way to do it depends on which build system you use, and is usually
|
||||
straightforward.
|
||||
|
||||
### Generic Build Instructions ###
|
||||
|
||||
Suppose you put Google Test in directory ${GTEST_DIR}. To build it,
|
||||
create a library build target (or a project as called by Visual Studio
|
||||
and Xcode) to compile
|
||||
|
||||
${GTEST_DIR}/src/gtest-all.cc
|
||||
|
||||
with ${GTEST_DIR}/include in the system header search path and ${GTEST_DIR}
|
||||
in the normal header search path. Assuming a Linux-like system and gcc,
|
||||
something like the following will do:
|
||||
|
||||
g++ -isystem ${GTEST_DIR}/include -I${GTEST_DIR} \
|
||||
-pthread -c ${GTEST_DIR}/src/gtest-all.cc
|
||||
ar -rv libgtest.a gtest-all.o
|
||||
|
||||
(We need -pthread as Google Test uses threads.)
|
||||
|
||||
Next, you should compile your test source file with
|
||||
${GTEST_DIR}/include in the system header search path, and link it
|
||||
with gtest and any other necessary libraries:
|
||||
|
||||
g++ -isystem ${GTEST_DIR}/include -pthread path/to/your_test.cc libgtest.a \
|
||||
-o your_test
|
||||
|
||||
As an example, the make/ directory contains a Makefile that you can
|
||||
use to build Google Test on systems where GNU make is available
|
||||
(e.g. Linux, Mac OS X, and Cygwin). It doesn't try to build Google
|
||||
Test's own tests. Instead, it just builds the Google Test library and
|
||||
a sample test. You can use it as a starting point for your own build
|
||||
script.
|
||||
|
||||
If the default settings are correct for your environment, the
|
||||
following commands should succeed:
|
||||
|
||||
cd ${GTEST_DIR}/make
|
||||
make
|
||||
./sample1_unittest
|
||||
|
||||
If you see errors, try to tweak the contents of make/Makefile to make
|
||||
them go away. There are instructions in make/Makefile on how to do
|
||||
it.
|
||||
|
||||
### Using CMake ###
|
||||
|
||||
Google Test comes with a CMake build script (CMakeLists.txt) that can
|
||||
be used on a wide range of platforms ("C" stands for cross-platofrm.).
|
||||
If you don't have CMake installed already, you can download it for
|
||||
free from http://www.cmake.org/.
|
||||
|
||||
CMake works by generating native makefiles or build projects that can
|
||||
be used in the compiler environment of your choice. The typical
|
||||
workflow starts with:
|
||||
|
||||
mkdir mybuild # Create a directory to hold the build output.
|
||||
cd mybuild
|
||||
cmake ${GTEST_DIR} # Generate native build scripts.
|
||||
|
||||
If you want to build Google Test's samples, you should replace the
|
||||
last command with
|
||||
|
||||
cmake -Dgtest_build_samples=ON ${GTEST_DIR}
|
||||
|
||||
If you are on a *nix system, you should now see a Makefile in the
|
||||
current directory. Just type 'make' to build gtest.
|
||||
|
||||
If you use Windows and have Vistual Studio installed, a gtest.sln file
|
||||
and several .vcproj files will be created. You can then build them
|
||||
using Visual Studio.
|
||||
|
||||
On Mac OS X with Xcode installed, a .xcodeproj file will be generated.
|
||||
|
||||
### Legacy Build Scripts ###
|
||||
|
||||
Before settling on CMake, we have been providing hand-maintained build
|
||||
projects/scripts for Visual Studio, Xcode, and Autotools. While we
|
||||
continue to provide them for convenience, they are not actively
|
||||
maintained any more. We highly recommend that you follow the
|
||||
instructions in the previous two sections to integrate Google Test
|
||||
with your existing build system.
|
||||
|
||||
If you still need to use the legacy build scripts, here's how:
|
||||
|
||||
The msvc\ folder contains two solutions with Visual C++ projects.
|
||||
Open the gtest.sln or gtest-md.sln file using Visual Studio, and you
|
||||
are ready to build Google Test the same way you build any Visual
|
||||
Studio project. Files that have names ending with -md use DLL
|
||||
versions of Microsoft runtime libraries (the /MD or the /MDd compiler
|
||||
option). Files without that suffix use static versions of the runtime
|
||||
libraries (the /MT or the /MTd option). Please note that one must use
|
||||
the same option to compile both gtest and the test code. If you use
|
||||
Visual Studio 2005 or above, we recommend the -md version as /MD is
|
||||
the default for new projects in these versions of Visual Studio.
|
||||
|
||||
On Mac OS X, open the gtest.xcodeproj in the xcode/ folder using
|
||||
Xcode. Build the "gtest" target. The universal binary framework will
|
||||
end up in your selected build directory (selected in the Xcode
|
||||
"Preferences..." -> "Building" pane and defaults to xcode/build).
|
||||
Alternatively, at the command line, enter:
|
||||
|
||||
xcodebuild
|
||||
|
||||
This will build the "Release" configuration of gtest.framework in your
|
||||
default build location. See the "xcodebuild" man page for more
|
||||
information about building different configurations and building in
|
||||
different locations.
|
||||
|
||||
If you wish to use the Google Test Xcode project with Xcode 4.x and
|
||||
above, you need to either:
|
||||
* update the SDK configuration options in xcode/Config/General.xconfig.
|
||||
Comment options SDKROOT, MACOS_DEPLOYMENT_TARGET, and GCC_VERSION. If
|
||||
you choose this route you lose the ability to target earlier versions
|
||||
of MacOS X.
|
||||
* Install an SDK for an earlier version. This doesn't appear to be
|
||||
supported by Apple, but has been reported to work
|
||||
(http://stackoverflow.com/questions/5378518).
|
||||
|
||||
Tweaking Google Test
|
||||
--------------------
|
||||
|
||||
Google Test can be used in diverse environments. The default
|
||||
configuration may not work (or may not work well) out of the box in
|
||||
some environments. However, you can easily tweak Google Test by
|
||||
defining control macros on the compiler command line. Generally,
|
||||
these macros are named like GTEST_XYZ and you define them to either 1
|
||||
or 0 to enable or disable a certain feature.
|
||||
|
||||
We list the most frequently used macros below. For a complete list,
|
||||
see file include/gtest/internal/gtest-port.h.
|
||||
|
||||
### Choosing a TR1 Tuple Library ###
|
||||
|
||||
Some Google Test features require the C++ Technical Report 1 (TR1)
|
||||
tuple library, which is not yet available with all compilers. The
|
||||
good news is that Google Test implements a subset of TR1 tuple that's
|
||||
enough for its own need, and will automatically use this when the
|
||||
compiler doesn't provide TR1 tuple.
|
||||
|
||||
Usually you don't need to care about which tuple library Google Test
|
||||
uses. However, if your project already uses TR1 tuple, you need to
|
||||
tell Google Test to use the same TR1 tuple library the rest of your
|
||||
project uses, or the two tuple implementations will clash. To do
|
||||
that, add
|
||||
|
||||
-DGTEST_USE_OWN_TR1_TUPLE=0
|
||||
|
||||
to the compiler flags while compiling Google Test and your tests. If
|
||||
you want to force Google Test to use its own tuple library, just add
|
||||
|
||||
-DGTEST_USE_OWN_TR1_TUPLE=1
|
||||
|
||||
to the compiler flags instead.
|
||||
|
||||
If you don't want Google Test to use tuple at all, add
|
||||
|
||||
-DGTEST_HAS_TR1_TUPLE=0
|
||||
|
||||
and all features using tuple will be disabled.
|
||||
|
||||
### Multi-threaded Tests ###
|
||||
|
||||
Google Test is thread-safe where the pthread library is available.
|
||||
After #include "gtest/gtest.h", you can check the GTEST_IS_THREADSAFE
|
||||
macro to see whether this is the case (yes if the macro is #defined to
|
||||
1, no if it's undefined.).
|
||||
|
||||
If Google Test doesn't correctly detect whether pthread is available
|
||||
in your environment, you can force it with
|
||||
|
||||
-DGTEST_HAS_PTHREAD=1
|
||||
|
||||
or
|
||||
|
||||
-DGTEST_HAS_PTHREAD=0
|
||||
|
||||
When Google Test uses pthread, you may need to add flags to your
|
||||
compiler and/or linker to select the pthread library, or you'll get
|
||||
link errors. If you use the CMake script or the deprecated Autotools
|
||||
script, this is taken care of for you. If you use your own build
|
||||
script, you'll need to read your compiler and linker's manual to
|
||||
figure out what flags to add.
|
||||
|
||||
### As a Shared Library (DLL) ###
|
||||
|
||||
Google Test is compact, so most users can build and link it as a
|
||||
static library for the simplicity. You can choose to use Google Test
|
||||
as a shared library (known as a DLL on Windows) if you prefer.
|
||||
|
||||
To compile *gtest* as a shared library, add
|
||||
|
||||
-DGTEST_CREATE_SHARED_LIBRARY=1
|
||||
|
||||
to the compiler flags. You'll also need to tell the linker to produce
|
||||
a shared library instead - consult your linker's manual for how to do
|
||||
it.
|
||||
|
||||
To compile your *tests* that use the gtest shared library, add
|
||||
|
||||
-DGTEST_LINKED_AS_SHARED_LIBRARY=1
|
||||
|
||||
to the compiler flags.
|
||||
|
||||
Note: while the above steps aren't technically necessary today when
|
||||
using some compilers (e.g. GCC), they may become necessary in the
|
||||
future, if we decide to improve the speed of loading the library (see
|
||||
http://gcc.gnu.org/wiki/Visibility for details). Therefore you are
|
||||
recommended to always add the above flags when using Google Test as a
|
||||
shared library. Otherwise a future release of Google Test may break
|
||||
your build script.
|
||||
|
||||
### Avoiding Macro Name Clashes ###
|
||||
|
||||
In C++, macros don't obey namespaces. Therefore two libraries that
|
||||
both define a macro of the same name will clash if you #include both
|
||||
definitions. In case a Google Test macro clashes with another
|
||||
library, you can force Google Test to rename its macro to avoid the
|
||||
conflict.
|
||||
|
||||
Specifically, if both Google Test and some other code define macro
|
||||
FOO, you can add
|
||||
|
||||
-DGTEST_DONT_DEFINE_FOO=1
|
||||
|
||||
to the compiler flags to tell Google Test to change the macro's name
|
||||
from FOO to GTEST_FOO. Currently FOO can be FAIL, SUCCEED, or TEST.
|
||||
For example, with -DGTEST_DONT_DEFINE_TEST=1, you'll need to write
|
||||
|
||||
GTEST_TEST(SomeTest, DoesThis) { ... }
|
||||
|
||||
instead of
|
||||
|
||||
TEST(SomeTest, DoesThis) { ... }
|
||||
|
||||
in order to define a test.
|
||||
|
||||
Upgrating from an Earlier Version
|
||||
---------------------------------
|
||||
|
||||
We strive to keep Google Test releases backward compatible.
|
||||
Sometimes, though, we have to make some breaking changes for the
|
||||
users' long-term benefits. This section describes what you'll need to
|
||||
do if you are upgrading from an earlier version of Google Test.
|
||||
|
||||
### Upgrading from 1.3.0 or Earlier ###
|
||||
|
||||
You may need to explicitly enable or disable Google Test's own TR1
|
||||
tuple library. See the instructions in section "Choosing a TR1 Tuple
|
||||
Library".
|
||||
|
||||
### Upgrading from 1.4.0 or Earlier ###
|
||||
|
||||
The Autotools build script (configure + make) is no longer officially
|
||||
supportted. You are encouraged to migrate to your own build system or
|
||||
use CMake. If you still need to use Autotools, you can find
|
||||
instructions in the README file from Google Test 1.4.0.
|
||||
|
||||
On platforms where the pthread library is available, Google Test uses
|
||||
it in order to be thread-safe. See the "Multi-threaded Tests" section
|
||||
for what this means to your build script.
|
||||
|
||||
If you use Microsoft Visual C++ 7.1 with exceptions disabled, Google
|
||||
Test will no longer compile. This should affect very few people, as a
|
||||
large portion of STL (including <string>) doesn't compile in this mode
|
||||
anyway. We decided to stop supporting it in order to greatly simplify
|
||||
Google Test's implementation.
|
||||
|
||||
Developing Google Test
|
||||
----------------------
|
||||
|
||||
This section discusses how to make your own changes to Google Test.
|
||||
|
||||
### Testing Google Test Itself ###
|
||||
|
||||
To make sure your changes work as intended and don't break existing
|
||||
functionality, you'll want to compile and run Google Test's own tests.
|
||||
For that you can use CMake:
|
||||
|
||||
mkdir mybuild
|
||||
cd mybuild
|
||||
cmake -Dgtest_build_tests=ON ${GTEST_DIR}
|
||||
|
||||
Make sure you have Python installed, as some of Google Test's tests
|
||||
are written in Python. If the cmake command complains about not being
|
||||
able to find Python ("Could NOT find PythonInterp (missing:
|
||||
PYTHON_EXECUTABLE)"), try telling it explicitly where your Python
|
||||
executable can be found:
|
||||
|
||||
cmake -DPYTHON_EXECUTABLE=path/to/python -Dgtest_build_tests=ON ${GTEST_DIR}
|
||||
|
||||
Next, you can build Google Test and all of its own tests. On *nix,
|
||||
this is usually done by 'make'. To run the tests, do
|
||||
|
||||
make test
|
||||
|
||||
All tests should pass.
|
||||
|
||||
### Regenerating Source Files ###
|
||||
|
||||
Some of Google Test's source files are generated from templates (not
|
||||
in the C++ sense) using a script. A template file is named FOO.pump,
|
||||
where FOO is the name of the file it will generate. For example, the
|
||||
file include/gtest/internal/gtest-type-util.h.pump is used to generate
|
||||
gtest-type-util.h in the same directory.
|
||||
|
||||
Normally you don't need to worry about regenerating the source files,
|
||||
unless you need to modify them. In that case, you should modify the
|
||||
corresponding .pump files instead and run the pump.py Python script to
|
||||
regenerate them. You can find pump.py in the scripts/ directory.
|
||||
Read the Pump manual [2] for how to use it.
|
||||
|
||||
[2] http://code.google.com/p/googletest/wiki/PumpManual
|
||||
|
||||
### Contributing a Patch ###
|
||||
|
||||
We welcome patches. Please read the Google Test developer's guide [3]
|
||||
for how you can contribute. In particular, make sure you have signed
|
||||
the Contributor License Agreement, or we won't be able to accept the
|
||||
patch.
|
||||
|
||||
[3] http://code.google.com/p/googletest/wiki/GoogleTestDevGuide
|
||||
|
||||
Happy testing!
|
|
@ -1,227 +0,0 @@
|
|||
# Defines functions and macros useful for building Google Test and
|
||||
# Google Mock.
|
||||
#
|
||||
# Note:
|
||||
#
|
||||
# - This file will be run twice when building Google Mock (once via
|
||||
# Google Test's CMakeLists.txt, and once via Google Mock's).
|
||||
# Therefore it shouldn't have any side effects other than defining
|
||||
# the functions and macros.
|
||||
#
|
||||
# - The functions/macros defined in this file may depend on Google
|
||||
# Test and Google Mock's option() definitions, and thus must be
|
||||
# called *after* the options have been defined.
|
||||
|
||||
# Tweaks CMake's default compiler/linker settings to suit Google Test's needs.
|
||||
#
|
||||
# This must be a macro(), as inside a function string() can only
|
||||
# update variables in the function scope.
|
||||
macro(fix_default_compiler_settings_)
|
||||
if (MSVC)
|
||||
# For MSVC, CMake sets certain flags to defaults we want to override.
|
||||
# This replacement code is taken from sample in the CMake Wiki at
|
||||
# http://www.cmake.org/Wiki/CMake_FAQ#Dynamic_Replace.
|
||||
foreach (flag_var
|
||||
CMAKE_CXX_FLAGS CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS_RELEASE
|
||||
CMAKE_CXX_FLAGS_MINSIZEREL CMAKE_CXX_FLAGS_RELWITHDEBINFO)
|
||||
if (NOT BUILD_SHARED_LIBS AND NOT gtest_force_shared_crt)
|
||||
# When Google Test is built as a shared library, it should also use
|
||||
# shared runtime libraries. Otherwise, it may end up with multiple
|
||||
# copies of runtime library data in different modules, resulting in
|
||||
# hard-to-find crashes. When it is built as a static library, it is
|
||||
# preferable to use CRT as static libraries, as we don't have to rely
|
||||
# on CRT DLLs being available. CMake always defaults to using shared
|
||||
# CRT libraries, so we override that default here.
|
||||
string(REPLACE "/MD" "-MT" ${flag_var} "${${flag_var}}")
|
||||
endif()
|
||||
|
||||
# We prefer more strict warning checking for building Google Test.
|
||||
# Replaces /W3 with /W4 in defaults.
|
||||
string(REPLACE "/W3" "-W4" ${flag_var} "${${flag_var}}")
|
||||
endforeach()
|
||||
endif()
|
||||
endmacro()
|
||||
|
||||
# Defines the compiler/linker flags used to build Google Test and
|
||||
# Google Mock. You can tweak these definitions to suit your need. A
|
||||
# variable's value is empty before it's explicitly assigned to.
|
||||
macro(config_compiler_and_linker)
|
||||
if (NOT gtest_disable_pthreads)
|
||||
# Defines CMAKE_USE_PTHREADS_INIT and CMAKE_THREAD_LIBS_INIT.
|
||||
find_package(Threads)
|
||||
endif()
|
||||
|
||||
fix_default_compiler_settings_()
|
||||
if (MSVC)
|
||||
# Newlines inside flags variables break CMake's NMake generator.
|
||||
# TODO(vladl@google.com): Add -RTCs and -RTCu to debug builds.
|
||||
set(cxx_base_flags "-GS -W4 -WX -wd4127 -wd4251 -wd4275 -nologo -J -Zi")
|
||||
if (MSVC_VERSION LESS 1400)
|
||||
# Suppress spurious warnings MSVC 7.1 sometimes issues.
|
||||
# Forcing value to bool.
|
||||
set(cxx_base_flags "${cxx_base_flags} -wd4800")
|
||||
# Copy constructor and assignment operator could not be generated.
|
||||
set(cxx_base_flags "${cxx_base_flags} -wd4511 -wd4512")
|
||||
# Compatibility warnings not applicable to Google Test.
|
||||
# Resolved overload was found by argument-dependent lookup.
|
||||
set(cxx_base_flags "${cxx_base_flags} -wd4675")
|
||||
endif()
|
||||
set(cxx_base_flags "${cxx_base_flags} -D_UNICODE -DUNICODE -DWIN32 -D_WIN32")
|
||||
set(cxx_base_flags "${cxx_base_flags} -DSTRICT -DWIN32_LEAN_AND_MEAN")
|
||||
set(cxx_exception_flags "-EHsc -D_HAS_EXCEPTIONS=1")
|
||||
set(cxx_no_exception_flags "-D_HAS_EXCEPTIONS=0")
|
||||
set(cxx_no_rtti_flags "-GR-")
|
||||
elseif (CMAKE_COMPILER_IS_GNUCXX)
|
||||
set(cxx_base_flags "-Wall -Wshadow")
|
||||
set(cxx_exception_flags "-fexceptions")
|
||||
set(cxx_no_exception_flags "-fno-exceptions")
|
||||
# Until version 4.3.2, GCC doesn't define a macro to indicate
|
||||
# whether RTTI is enabled. Therefore we define GTEST_HAS_RTTI
|
||||
# explicitly.
|
||||
set(cxx_no_rtti_flags "-fno-rtti -DGTEST_HAS_RTTI=0")
|
||||
set(cxx_strict_flags
|
||||
"-Wextra -Wno-unused-parameter -Wno-missing-field-initializers")
|
||||
elseif (CMAKE_CXX_COMPILER_ID STREQUAL "SunPro")
|
||||
set(cxx_exception_flags "-features=except")
|
||||
# Sun Pro doesn't provide macros to indicate whether exceptions and
|
||||
# RTTI are enabled, so we define GTEST_HAS_* explicitly.
|
||||
set(cxx_no_exception_flags "-features=no%except -DGTEST_HAS_EXCEPTIONS=0")
|
||||
set(cxx_no_rtti_flags "-features=no%rtti -DGTEST_HAS_RTTI=0")
|
||||
elseif (CMAKE_CXX_COMPILER_ID STREQUAL "VisualAge" OR
|
||||
CMAKE_CXX_COMPILER_ID STREQUAL "XL")
|
||||
# CMake 2.8 changes Visual Age's compiler ID to "XL".
|
||||
set(cxx_exception_flags "-qeh")
|
||||
set(cxx_no_exception_flags "-qnoeh")
|
||||
# Until version 9.0, Visual Age doesn't define a macro to indicate
|
||||
# whether RTTI is enabled. Therefore we define GTEST_HAS_RTTI
|
||||
# explicitly.
|
||||
set(cxx_no_rtti_flags "-qnortti -DGTEST_HAS_RTTI=0")
|
||||
elseif (CMAKE_CXX_COMPILER_ID STREQUAL "HP")
|
||||
set(cxx_base_flags "-AA -mt")
|
||||
set(cxx_exception_flags "-DGTEST_HAS_EXCEPTIONS=1")
|
||||
set(cxx_no_exception_flags "+noeh -DGTEST_HAS_EXCEPTIONS=0")
|
||||
# RTTI can not be disabled in HP aCC compiler.
|
||||
set(cxx_no_rtti_flags "")
|
||||
endif()
|
||||
|
||||
if (CMAKE_USE_PTHREADS_INIT) # The pthreads library is available and allowed.
|
||||
set(cxx_base_flags "${cxx_base_flags} -DGTEST_HAS_PTHREAD=1")
|
||||
else()
|
||||
set(cxx_base_flags "${cxx_base_flags} -DGTEST_HAS_PTHREAD=0")
|
||||
endif()
|
||||
|
||||
# For building gtest's own tests and samples.
|
||||
set(cxx_exception "${CMAKE_CXX_FLAGS} ${cxx_base_flags} ${cxx_exception_flags}")
|
||||
set(cxx_no_exception
|
||||
"${CMAKE_CXX_FLAGS} ${cxx_base_flags} ${cxx_no_exception_flags}")
|
||||
set(cxx_default "${cxx_exception}")
|
||||
set(cxx_no_rtti "${cxx_default} ${cxx_no_rtti_flags}")
|
||||
set(cxx_use_own_tuple "${cxx_default} -DGTEST_USE_OWN_TR1_TUPLE=1")
|
||||
|
||||
# For building the gtest libraries.
|
||||
set(cxx_strict "${cxx_default} ${cxx_strict_flags}")
|
||||
endmacro()
|
||||
|
||||
# Defines the gtest & gtest_main libraries. User tests should link
|
||||
# with one of them.
|
||||
function(cxx_library_with_type name type cxx_flags)
|
||||
# type can be either STATIC or SHARED to denote a static or shared library.
|
||||
# ARGN refers to additional arguments after 'cxx_flags'.
|
||||
add_library(${name} ${type} ${ARGN})
|
||||
set_target_properties(${name}
|
||||
PROPERTIES
|
||||
COMPILE_FLAGS "${cxx_flags}")
|
||||
if (BUILD_SHARED_LIBS OR type STREQUAL "SHARED")
|
||||
set_target_properties(${name}
|
||||
PROPERTIES
|
||||
COMPILE_DEFINITIONS "GTEST_CREATE_SHARED_LIBRARY=1")
|
||||
endif()
|
||||
if (CMAKE_USE_PTHREADS_INIT)
|
||||
target_link_libraries(${name} ${CMAKE_THREAD_LIBS_INIT})
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
########################################################################
|
||||
#
|
||||
# Helper functions for creating build targets.
|
||||
|
||||
function(cxx_shared_library name cxx_flags)
|
||||
cxx_library_with_type(${name} SHARED "${cxx_flags}" ${ARGN})
|
||||
endfunction()
|
||||
|
||||
function(cxx_library name cxx_flags)
|
||||
cxx_library_with_type(${name} "" "${cxx_flags}" ${ARGN})
|
||||
endfunction()
|
||||
|
||||
# cxx_executable_with_flags(name cxx_flags libs srcs...)
|
||||
#
|
||||
# creates a named C++ executable that depends on the given libraries and
|
||||
# is built from the given source files with the given compiler flags.
|
||||
function(cxx_executable_with_flags name cxx_flags libs)
|
||||
add_executable(${name} ${ARGN})
|
||||
if (cxx_flags)
|
||||
set_target_properties(${name}
|
||||
PROPERTIES
|
||||
COMPILE_FLAGS "${cxx_flags}")
|
||||
endif()
|
||||
if (BUILD_SHARED_LIBS)
|
||||
set_target_properties(${name}
|
||||
PROPERTIES
|
||||
COMPILE_DEFINITIONS "GTEST_LINKED_AS_SHARED_LIBRARY=1")
|
||||
endif()
|
||||
# To support mixing linking in static and dynamic libraries, link each
|
||||
# library in with an extra call to target_link_libraries.
|
||||
foreach (lib "${libs}")
|
||||
target_link_libraries(${name} ${lib})
|
||||
endforeach()
|
||||
endfunction()
|
||||
|
||||
# cxx_executable(name dir lib srcs...)
|
||||
#
|
||||
# creates a named target that depends on the given libs and is built
|
||||
# from the given source files. dir/name.cc is implicitly included in
|
||||
# the source file list.
|
||||
function(cxx_executable name dir libs)
|
||||
cxx_executable_with_flags(
|
||||
${name} "${cxx_default}" "${libs}" "${dir}/${name}.cc" ${ARGN})
|
||||
endfunction()
|
||||
|
||||
# Sets PYTHONINTERP_FOUND and PYTHON_EXECUTABLE.
|
||||
find_package(PythonInterp)
|
||||
|
||||
# cxx_test_with_flags(name cxx_flags libs srcs...)
|
||||
#
|
||||
# creates a named C++ test that depends on the given libs and is built
|
||||
# from the given source files with the given compiler flags.
|
||||
function(cxx_test_with_flags name cxx_flags libs)
|
||||
cxx_executable_with_flags(${name} "${cxx_flags}" "${libs}" ${ARGN})
|
||||
add_test(${name} ${name})
|
||||
endfunction()
|
||||
|
||||
# cxx_test(name libs srcs...)
|
||||
#
|
||||
# creates a named test target that depends on the given libs and is
|
||||
# built from the given source files. Unlike cxx_test_with_flags,
|
||||
# test/name.cc is already implicitly included in the source file list.
|
||||
function(cxx_test name libs)
|
||||
cxx_test_with_flags("${name}" "${cxx_default}" "${libs}"
|
||||
"test/${name}.cc" ${ARGN})
|
||||
endfunction()
|
||||
|
||||
# py_test(name)
|
||||
#
|
||||
# creates a Python test with the given name whose main module is in
|
||||
# test/name.py. It does nothing if Python is not installed.
|
||||
function(py_test name)
|
||||
# We are not supporting Python tests on Linux yet as they consider
|
||||
# all Linux environments to be google3 and try to use google3 features.
|
||||
if (PYTHONINTERP_FOUND)
|
||||
# ${CMAKE_BINARY_DIR} is known at configuration time, so we can
|
||||
# directly bind it from cmake. ${CTEST_CONFIGURATION_TYPE} is known
|
||||
# only at ctest runtime (by calling ctest -c <Configuration>), so
|
||||
# we have to escape $ to delay variable substitution here.
|
||||
add_test(${name}
|
||||
${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/test/${name}.py
|
||||
--build_dir=${CMAKE_CURRENT_BINARY_DIR}/\${CTEST_CONFIGURATION_TYPE})
|
||||
endif()
|
||||
endfunction()
|
|
@ -1,138 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<ProjectGuid>{bca37a72-5b07-46cf-b44e-89f8e06451a2}</ProjectGuid>
|
||||
<Config Condition="'$(Config)'==''">Release</Config>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Config)'=='Base' or '$(Base)'!=''">
|
||||
<Base>true</Base>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Config)'=='Debug' or '$(Cfg_1)'!=''">
|
||||
<Base>true</Base>
|
||||
<Cfg_1>true</Cfg_1>
|
||||
<CfgParent>Base</CfgParent>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Config)'=='Release' or '$(Cfg_2)'!=''">
|
||||
<Base>true</Base>
|
||||
<Cfg_2>true</Cfg_2>
|
||||
<CfgParent>Base</CfgParent>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Base)'!=''">
|
||||
<BCC_OptimizeForSpeed>true</BCC_OptimizeForSpeed>
|
||||
<OutputExt>lib</OutputExt>
|
||||
<DCC_CBuilderOutput>JPHNE</DCC_CBuilderOutput>
|
||||
<Defines>NO_STRICT</Defines>
|
||||
<DynamicRTL>true</DynamicRTL>
|
||||
<UsePackages>true</UsePackages>
|
||||
<ProjectType>CppStaticLibrary</ProjectType>
|
||||
<BCC_CPPCompileAlways>true</BCC_CPPCompileAlways>
|
||||
<PackageImports>rtl.bpi;vcl.bpi;bcbie.bpi;vclx.bpi;vclactnband.bpi;xmlrtl.bpi;bcbsmp.bpi;dbrtl.bpi;vcldb.bpi;bdertl.bpi;vcldbx.bpi;dsnap.bpi;dsnapcon.bpi;vclib.bpi;ibxpress.bpi;adortl.bpi;dbxcds.bpi;dbexpress.bpi;DbxCommonDriver.bpi;websnap.bpi;vclie.bpi;webdsnap.bpi;inet.bpi;inetdbbde.bpi;inetdbxpress.bpi;soaprtl.bpi;Rave75VCL.bpi;teeUI.bpi;tee.bpi;teedb.bpi;IndyCore.bpi;IndySystem.bpi;IndyProtocols.bpi;IntrawebDB_90_100.bpi;Intraweb_90_100.bpi;dclZipForged11.bpi;vclZipForged11.bpi;GR32_BDS2006.bpi;GR32_DSGN_BDS2006.bpi;Jcl.bpi;JclVcl.bpi;JvCoreD11R.bpi;JvSystemD11R.bpi;JvStdCtrlsD11R.bpi;JvAppFrmD11R.bpi;JvBandsD11R.bpi;JvDBD11R.bpi;JvDlgsD11R.bpi;JvBDED11R.bpi;JvCmpD11R.bpi;JvCryptD11R.bpi;JvCtrlsD11R.bpi;JvCustomD11R.bpi;JvDockingD11R.bpi;JvDotNetCtrlsD11R.bpi;JvEDID11R.bpi;JvGlobusD11R.bpi;JvHMID11R.bpi;JvInterpreterD11R.bpi;JvJansD11R.bpi;JvManagedThreadsD11R.bpi;JvMMD11R.bpi;JvNetD11R.bpi;JvPageCompsD11R.bpi;JvPluginD11R.bpi;JvPrintPreviewD11R.bpi;JvRuntimeDesignD11R.bpi;JvTimeFrameworkD11R.bpi;JvValidatorsD11R.bpi;JvWizardD11R.bpi;JvXPCtrlsD11R.bpi;VclSmp.bpi;CExceptionExpert11.bpi</PackageImports>
|
||||
<BCC_wpar>false</BCC_wpar>
|
||||
<IncludePath>$(BDS)\include;$(BDS)\include\dinkumware;$(BDS)\include\vcl;..\src;..\include;..</IncludePath>
|
||||
<AllPackageLibs>rtl.lib;vcl.lib</AllPackageLibs>
|
||||
<TLIB_PageSize>32</TLIB_PageSize>
|
||||
<ILINK_LibraryPath>$(BDS)\lib;$(BDS)\lib\obj;$(BDS)\lib\psdk</ILINK_LibraryPath>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Cfg_1)'!=''">
|
||||
<BCC_OptimizeForSpeed>false</BCC_OptimizeForSpeed>
|
||||
<DCC_Optimize>false</DCC_Optimize>
|
||||
<DCC_DebugInfoInExe>true</DCC_DebugInfoInExe>
|
||||
<Defines>_DEBUG;$(Defines)</Defines>
|
||||
<ILINK_FullDebugInfo>true</ILINK_FullDebugInfo>
|
||||
<BCC_InlineFunctionExpansion>false</BCC_InlineFunctionExpansion>
|
||||
<ILINK_DisableIncrementalLinking>true</ILINK_DisableIncrementalLinking>
|
||||
<BCC_UseRegisterVariables>None</BCC_UseRegisterVariables>
|
||||
<DCC_Define>DEBUG</DCC_Define>
|
||||
<BCC_DebugLineNumbers>true</BCC_DebugLineNumbers>
|
||||
<IntermediateOutputDir>Debug</IntermediateOutputDir>
|
||||
<TASM_DisplaySourceLines>true</TASM_DisplaySourceLines>
|
||||
<BCC_StackFrames>true</BCC_StackFrames>
|
||||
<BCC_DisableOptimizations>true</BCC_DisableOptimizations>
|
||||
<ILINK_LibraryPath>$(BDS)\lib\debug;$(ILINK_LibraryPath)</ILINK_LibraryPath>
|
||||
<TASM_Debugging>Full</TASM_Debugging>
|
||||
<BCC_SourceDebuggingOn>true</BCC_SourceDebuggingOn>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Cfg_2)'!=''">
|
||||
<Defines>NDEBUG;$(Defines)</Defines>
|
||||
<IntermediateOutputDir>Release</IntermediateOutputDir>
|
||||
<ILINK_LibraryPath>$(BDS)\lib\release;$(ILINK_LibraryPath)</ILINK_LibraryPath>
|
||||
<TASM_Debugging>None</TASM_Debugging>
|
||||
</PropertyGroup>
|
||||
<ProjectExtensions>
|
||||
<Borland.Personality>CPlusPlusBuilder.Personality</Borland.Personality>
|
||||
<Borland.ProjectType>CppStaticLibrary</Borland.ProjectType>
|
||||
<BorlandProject>
|
||||
<BorlandProject><CPlusPlusBuilder.Personality><VersionInfo><VersionInfo Name="IncludeVerInfo">False</VersionInfo><VersionInfo Name="AutoIncBuild">False</VersionInfo><VersionInfo Name="MajorVer">1</VersionInfo><VersionInfo Name="MinorVer">0</VersionInfo><VersionInfo Name="Release">0</VersionInfo><VersionInfo Name="Build">0</VersionInfo><VersionInfo Name="Debug">False</VersionInfo><VersionInfo Name="PreRelease">False</VersionInfo><VersionInfo Name="Special">False</VersionInfo><VersionInfo Name="Private">False</VersionInfo><VersionInfo Name="DLL">False</VersionInfo><VersionInfo Name="Locale">1033</VersionInfo><VersionInfo Name="CodePage">1252</VersionInfo></VersionInfo><VersionInfoKeys><VersionInfoKeys Name="CompanyName"></VersionInfoKeys><VersionInfoKeys Name="FileDescription"></VersionInfoKeys><VersionInfoKeys Name="FileVersion">1.0.0.0</VersionInfoKeys><VersionInfoKeys Name="InternalName"></VersionInfoKeys><VersionInfoKeys Name="LegalCopyright"></VersionInfoKeys><VersionInfoKeys Name="LegalTrademarks"></VersionInfoKeys><VersionInfoKeys Name="OriginalFilename"></VersionInfoKeys><VersionInfoKeys Name="ProductName"></VersionInfoKeys><VersionInfoKeys Name="ProductVersion">1.0.0.0</VersionInfoKeys><VersionInfoKeys Name="Comments"></VersionInfoKeys></VersionInfoKeys><Debugging><Debugging Name="DebugSourceDirs"></Debugging></Debugging><Parameters><Parameters Name="RunParams"></Parameters><Parameters Name="Launcher"></Parameters><Parameters Name="UseLauncher">False</Parameters><Parameters Name="DebugCWD"></Parameters><Parameters Name="HostApplication"></Parameters><Parameters Name="RemoteHost"></Parameters><Parameters Name="RemotePath"></Parameters><Parameters Name="RemoteParams"></Parameters><Parameters Name="RemoteLauncher"></Parameters><Parameters Name="UseRemoteLauncher">False</Parameters><Parameters Name="RemoteCWD"></Parameters><Parameters Name="RemoteDebug">False</Parameters><Parameters Name="Debug Symbols Search Path"></Parameters><Parameters Name="LoadAllSymbols">True</Parameters><Parameters Name="LoadUnspecifiedSymbols">False</Parameters></Parameters><Excluded_Packages>
|
||||
|
||||
|
||||
<Excluded_Packages Name="$(BDS)\bin\bcboffice2k100.bpl">CodeGear C++Builder Office 2000 Servers Package</Excluded_Packages>
|
||||
<Excluded_Packages Name="$(BDS)\bin\bcbofficexp100.bpl">CodeGear C++Builder Office XP Servers Package</Excluded_Packages>
|
||||
</Excluded_Packages><Linker><Linker Name="LibPrefix"></Linker><Linker Name="LibSuffix"></Linker><Linker Name="LibVersion"></Linker></Linker><ProjectProperties><ProjectProperties Name="AutoShowDeps">False</ProjectProperties><ProjectProperties Name="ManagePaths">True</ProjectProperties><ProjectProperties Name="VerifyPackages">True</ProjectProperties></ProjectProperties><HistoryLists_hlIncludePath><HistoryLists_hlIncludePath Name="Count">3</HistoryLists_hlIncludePath><HistoryLists_hlIncludePath Name="Item0">$(BDS)\include;$(BDS)\include\dinkumware;$(BDS)\include\vcl;..\src;..\include;..</HistoryLists_hlIncludePath><HistoryLists_hlIncludePath Name="Item1">$(BDS)\include;$(BDS)\include\dinkumware;$(BDS)\include\vcl;..\src;..\include;..</HistoryLists_hlIncludePath><HistoryLists_hlIncludePath Name="Item2">$(BDS)\include;$(BDS)\include\dinkumware;$(BDS)\include\vcl;..\src;..\src;..\include</HistoryLists_hlIncludePath></HistoryLists_hlIncludePath><HistoryLists_hlILINK_LibraryPath><HistoryLists_hlILINK_LibraryPath Name="Count">1</HistoryLists_hlILINK_LibraryPath><HistoryLists_hlILINK_LibraryPath Name="Item0">$(BDS)\lib;$(BDS)\lib\obj;$(BDS)\lib\psdk</HistoryLists_hlILINK_LibraryPath></HistoryLists_hlILINK_LibraryPath><HistoryLists_hlDefines><HistoryLists_hlDefines Name="Count">1</HistoryLists_hlDefines><HistoryLists_hlDefines Name="Item0">NO_STRICT</HistoryLists_hlDefines></HistoryLists_hlDefines><HistoryLists_hlTLIB_PageSize><HistoryLists_hlTLIB_PageSize Name="Count">1</HistoryLists_hlTLIB_PageSize><HistoryLists_hlTLIB_PageSize Name="Item0">32</HistoryLists_hlTLIB_PageSize><HistoryLists_hlTLIB_PageSize Name="Item1">16</HistoryLists_hlTLIB_PageSize></HistoryLists_hlTLIB_PageSize></CPlusPlusBuilder.Personality></BorlandProject></BorlandProject>
|
||||
</ProjectExtensions>
|
||||
<Import Project="$(MSBuildBinPath)\Borland.Cpp.Targets" />
|
||||
<ItemGroup>
|
||||
<None Include="..\include\gtest\gtest-death-test.h">
|
||||
<BuildOrder>3</BuildOrder>
|
||||
</None>
|
||||
<None Include="..\include\gtest\gtest-message.h">
|
||||
<BuildOrder>4</BuildOrder>
|
||||
</None>
|
||||
<None Include="..\include\gtest\gtest-param-test.h">
|
||||
<BuildOrder>5</BuildOrder>
|
||||
</None>
|
||||
<None Include="..\include\gtest\gtest-spi.h">
|
||||
<BuildOrder>6</BuildOrder>
|
||||
</None>
|
||||
<None Include="..\include\gtest\gtest-test-part.h">
|
||||
<BuildOrder>7</BuildOrder>
|
||||
</None>
|
||||
<None Include="..\include\gtest\gtest-typed-test.h">
|
||||
<BuildOrder>8</BuildOrder>
|
||||
</None>
|
||||
<None Include="..\include\gtest\gtest.h">
|
||||
<BuildOrder>0</BuildOrder>
|
||||
</None>
|
||||
<None Include="..\include\gtest\gtest_pred_impl.h">
|
||||
<BuildOrder>1</BuildOrder>
|
||||
</None>
|
||||
<None Include="..\include\gtest\gtest_prod.h">
|
||||
<BuildOrder>2</BuildOrder>
|
||||
</None>
|
||||
<None Include="..\include\gtest\internal\gtest-death-test-internal.h">
|
||||
<BuildOrder>9</BuildOrder>
|
||||
</None>
|
||||
<None Include="..\include\gtest\internal\gtest-filepath.h">
|
||||
<BuildOrder>10</BuildOrder>
|
||||
</None>
|
||||
<None Include="..\include\gtest\internal\gtest-internal.h">
|
||||
<BuildOrder>11</BuildOrder>
|
||||
</None>
|
||||
<None Include="..\include\gtest\internal\gtest-linked_ptr.h">
|
||||
<BuildOrder>12</BuildOrder>
|
||||
</None>
|
||||
<None Include="..\include\gtest\internal\gtest-param-util-generated.h">
|
||||
<BuildOrder>14</BuildOrder>
|
||||
</None>
|
||||
<None Include="..\include\gtest\internal\gtest-param-util.h">
|
||||
<BuildOrder>13</BuildOrder>
|
||||
</None>
|
||||
<None Include="..\include\gtest\internal\gtest-port.h">
|
||||
<BuildOrder>15</BuildOrder>
|
||||
</None>
|
||||
<None Include="..\include\gtest\internal\gtest-string.h">
|
||||
<BuildOrder>16</BuildOrder>
|
||||
</None>
|
||||
<None Include="..\include\gtest\internal\gtest-type-util.h">
|
||||
<BuildOrder>17</BuildOrder>
|
||||
</None>
|
||||
<CppCompile Include="gtest_all.cc">
|
||||
<BuildOrder>18</BuildOrder>
|
||||
</CppCompile>
|
||||
<BuildConfiguration Include="Debug">
|
||||
<Key>Cfg_1</Key>
|
||||
</BuildConfiguration>
|
||||
<BuildConfiguration Include="Release">
|
||||
<Key>Cfg_2</Key>
|
||||
</BuildConfiguration>
|
||||
</ItemGroup>
|
||||
</Project>
|
|
@ -1,54 +0,0 @@
|
|||
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<ProjectGuid>{c1d923e0-6cba-4332-9b6f-3420acbf5091}</ProjectGuid>
|
||||
</PropertyGroup>
|
||||
<ItemGroup />
|
||||
<ItemGroup>
|
||||
<Projects Include="gtest.cbproj" />
|
||||
<Projects Include="gtest_main.cbproj" />
|
||||
<Projects Include="gtest_unittest.cbproj" />
|
||||
</ItemGroup>
|
||||
<ProjectExtensions>
|
||||
<Borland.Personality>Default.Personality</Borland.Personality>
|
||||
<Borland.ProjectType />
|
||||
<BorlandProject>
|
||||
<BorlandProject xmlns=""><Default.Personality></Default.Personality></BorlandProject></BorlandProject>
|
||||
</ProjectExtensions>
|
||||
<Target Name="gtest">
|
||||
<MSBuild Projects="gtest.cbproj" Targets="" />
|
||||
</Target>
|
||||
<Target Name="gtest:Clean">
|
||||
<MSBuild Projects="gtest.cbproj" Targets="Clean" />
|
||||
</Target>
|
||||
<Target Name="gtest:Make">
|
||||
<MSBuild Projects="gtest.cbproj" Targets="Make" />
|
||||
</Target>
|
||||
<Target Name="gtest_main">
|
||||
<MSBuild Projects="gtest_main.cbproj" Targets="" />
|
||||
</Target>
|
||||
<Target Name="gtest_main:Clean">
|
||||
<MSBuild Projects="gtest_main.cbproj" Targets="Clean" />
|
||||
</Target>
|
||||
<Target Name="gtest_main:Make">
|
||||
<MSBuild Projects="gtest_main.cbproj" Targets="Make" />
|
||||
</Target>
|
||||
<Target Name="gtest_unittest">
|
||||
<MSBuild Projects="gtest_unittest.cbproj" Targets="" />
|
||||
</Target>
|
||||
<Target Name="gtest_unittest:Clean">
|
||||
<MSBuild Projects="gtest_unittest.cbproj" Targets="Clean" />
|
||||
</Target>
|
||||
<Target Name="gtest_unittest:Make">
|
||||
<MSBuild Projects="gtest_unittest.cbproj" Targets="Make" />
|
||||
</Target>
|
||||
<Target Name="Build">
|
||||
<CallTarget Targets="gtest;gtest_main;gtest_unittest" />
|
||||
</Target>
|
||||
<Target Name="Clean">
|
||||
<CallTarget Targets="gtest:Clean;gtest_main:Clean;gtest_unittest:Clean" />
|
||||
</Target>
|
||||
<Target Name="Make">
|
||||
<CallTarget Targets="gtest:Make;gtest_main:Make;gtest_unittest:Make" />
|
||||
</Target>
|
||||
<Import Condition="Exists('$(MSBuildBinPath)\Borland.Group.Targets')" Project="$(MSBuildBinPath)\Borland.Group.Targets" />
|
||||
</Project>
|
|
@ -1,38 +0,0 @@
|
|||
// Copyright 2009, Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// Author: Josh Kelley (joshkel@gmail.com)
|
||||
//
|
||||
// Google C++ Testing Framework (Google Test)
|
||||
//
|
||||
// C++Builder's IDE cannot build a static library from files with hyphens
|
||||
// in their name. See http://qc.codegear.com/wc/qcmain.aspx?d=70977 .
|
||||
// This file serves as a workaround.
|
||||
|
||||
#include "src/gtest-all.cc"
|
|
@ -1,40 +0,0 @@
|
|||
// Copyright 2009, Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// Author: Josh Kelley (joshkel@gmail.com)
|
||||
//
|
||||
// Google C++ Testing Framework (Google Test)
|
||||
//
|
||||
// Links gtest.lib and gtest_main.lib into the current project in C++Builder.
|
||||
// This means that these libraries can't be renamed, but it's the only way to
|
||||
// ensure that Debug versus Release test builds are linked against the
|
||||
// appropriate Debug or Release build of the libraries.
|
||||
|
||||
#pragma link "gtest.lib"
|
||||
#pragma link "gtest_main.lib"
|
|
@ -1,82 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<ProjectGuid>{bca37a72-5b07-46cf-b44e-89f8e06451a2}</ProjectGuid>
|
||||
<Config Condition="'$(Config)'==''">Release</Config>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Config)'=='Base' or '$(Base)'!=''">
|
||||
<Base>true</Base>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Config)'=='Debug' or '$(Cfg_1)'!=''">
|
||||
<Base>true</Base>
|
||||
<Cfg_1>true</Cfg_1>
|
||||
<CfgParent>Base</CfgParent>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Config)'=='Release' or '$(Cfg_2)'!=''">
|
||||
<Base>true</Base>
|
||||
<Cfg_2>true</Cfg_2>
|
||||
<CfgParent>Base</CfgParent>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Base)'!=''">
|
||||
<BCC_OptimizeForSpeed>true</BCC_OptimizeForSpeed>
|
||||
<OutputExt>lib</OutputExt>
|
||||
<DCC_CBuilderOutput>JPHNE</DCC_CBuilderOutput>
|
||||
<Defines>NO_STRICT</Defines>
|
||||
<DynamicRTL>true</DynamicRTL>
|
||||
<UsePackages>true</UsePackages>
|
||||
<ProjectType>CppStaticLibrary</ProjectType>
|
||||
<BCC_CPPCompileAlways>true</BCC_CPPCompileAlways>
|
||||
<PackageImports>rtl.bpi;vcl.bpi;bcbie.bpi;vclx.bpi;vclactnband.bpi;xmlrtl.bpi;bcbsmp.bpi;dbrtl.bpi;vcldb.bpi;bdertl.bpi;vcldbx.bpi;dsnap.bpi;dsnapcon.bpi;vclib.bpi;ibxpress.bpi;adortl.bpi;dbxcds.bpi;dbexpress.bpi;DbxCommonDriver.bpi;websnap.bpi;vclie.bpi;webdsnap.bpi;inet.bpi;inetdbbde.bpi;inetdbxpress.bpi;soaprtl.bpi;Rave75VCL.bpi;teeUI.bpi;tee.bpi;teedb.bpi;IndyCore.bpi;IndySystem.bpi;IndyProtocols.bpi;IntrawebDB_90_100.bpi;Intraweb_90_100.bpi;dclZipForged11.bpi;vclZipForged11.bpi;GR32_BDS2006.bpi;GR32_DSGN_BDS2006.bpi;Jcl.bpi;JclVcl.bpi;JvCoreD11R.bpi;JvSystemD11R.bpi;JvStdCtrlsD11R.bpi;JvAppFrmD11R.bpi;JvBandsD11R.bpi;JvDBD11R.bpi;JvDlgsD11R.bpi;JvBDED11R.bpi;JvCmpD11R.bpi;JvCryptD11R.bpi;JvCtrlsD11R.bpi;JvCustomD11R.bpi;JvDockingD11R.bpi;JvDotNetCtrlsD11R.bpi;JvEDID11R.bpi;JvGlobusD11R.bpi;JvHMID11R.bpi;JvInterpreterD11R.bpi;JvJansD11R.bpi;JvManagedThreadsD11R.bpi;JvMMD11R.bpi;JvNetD11R.bpi;JvPageCompsD11R.bpi;JvPluginD11R.bpi;JvPrintPreviewD11R.bpi;JvRuntimeDesignD11R.bpi;JvTimeFrameworkD11R.bpi;JvValidatorsD11R.bpi;JvWizardD11R.bpi;JvXPCtrlsD11R.bpi;VclSmp.bpi;CExceptionExpert11.bpi</PackageImports>
|
||||
<BCC_wpar>false</BCC_wpar>
|
||||
<IncludePath>$(BDS)\include;$(BDS)\include\dinkumware;$(BDS)\include\vcl;..\src;..\include;..</IncludePath>
|
||||
<AllPackageLibs>rtl.lib;vcl.lib</AllPackageLibs>
|
||||
<TLIB_PageSize>32</TLIB_PageSize>
|
||||
<ILINK_LibraryPath>$(BDS)\lib;$(BDS)\lib\obj;$(BDS)\lib\psdk</ILINK_LibraryPath>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Cfg_1)'!=''">
|
||||
<BCC_OptimizeForSpeed>false</BCC_OptimizeForSpeed>
|
||||
<DCC_Optimize>false</DCC_Optimize>
|
||||
<DCC_DebugInfoInExe>true</DCC_DebugInfoInExe>
|
||||
<Defines>_DEBUG;$(Defines)</Defines>
|
||||
<ILINK_FullDebugInfo>true</ILINK_FullDebugInfo>
|
||||
<BCC_InlineFunctionExpansion>false</BCC_InlineFunctionExpansion>
|
||||
<ILINK_DisableIncrementalLinking>true</ILINK_DisableIncrementalLinking>
|
||||
<BCC_UseRegisterVariables>None</BCC_UseRegisterVariables>
|
||||
<DCC_Define>DEBUG</DCC_Define>
|
||||
<BCC_DebugLineNumbers>true</BCC_DebugLineNumbers>
|
||||
<IntermediateOutputDir>Debug</IntermediateOutputDir>
|
||||
<TASM_DisplaySourceLines>true</TASM_DisplaySourceLines>
|
||||
<BCC_StackFrames>true</BCC_StackFrames>
|
||||
<BCC_DisableOptimizations>true</BCC_DisableOptimizations>
|
||||
<ILINK_LibraryPath>$(BDS)\lib\debug;$(ILINK_LibraryPath)</ILINK_LibraryPath>
|
||||
<TASM_Debugging>Full</TASM_Debugging>
|
||||
<BCC_SourceDebuggingOn>true</BCC_SourceDebuggingOn>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Cfg_2)'!=''">
|
||||
<Defines>NDEBUG;$(Defines)</Defines>
|
||||
<IntermediateOutputDir>Release</IntermediateOutputDir>
|
||||
<ILINK_LibraryPath>$(BDS)\lib\release;$(ILINK_LibraryPath)</ILINK_LibraryPath>
|
||||
<TASM_Debugging>None</TASM_Debugging>
|
||||
</PropertyGroup>
|
||||
<ProjectExtensions>
|
||||
<Borland.Personality>CPlusPlusBuilder.Personality</Borland.Personality>
|
||||
<Borland.ProjectType>CppStaticLibrary</Borland.ProjectType>
|
||||
<BorlandProject>
|
||||
<BorlandProject><CPlusPlusBuilder.Personality><VersionInfo><VersionInfo Name="IncludeVerInfo">False</VersionInfo><VersionInfo Name="AutoIncBuild">False</VersionInfo><VersionInfo Name="MajorVer">1</VersionInfo><VersionInfo Name="MinorVer">0</VersionInfo><VersionInfo Name="Release">0</VersionInfo><VersionInfo Name="Build">0</VersionInfo><VersionInfo Name="Debug">False</VersionInfo><VersionInfo Name="PreRelease">False</VersionInfo><VersionInfo Name="Special">False</VersionInfo><VersionInfo Name="Private">False</VersionInfo><VersionInfo Name="DLL">False</VersionInfo><VersionInfo Name="Locale">1033</VersionInfo><VersionInfo Name="CodePage">1252</VersionInfo></VersionInfo><VersionInfoKeys><VersionInfoKeys Name="CompanyName"></VersionInfoKeys><VersionInfoKeys Name="FileDescription"></VersionInfoKeys><VersionInfoKeys Name="FileVersion">1.0.0.0</VersionInfoKeys><VersionInfoKeys Name="InternalName"></VersionInfoKeys><VersionInfoKeys Name="LegalCopyright"></VersionInfoKeys><VersionInfoKeys Name="LegalTrademarks"></VersionInfoKeys><VersionInfoKeys Name="OriginalFilename"></VersionInfoKeys><VersionInfoKeys Name="ProductName"></VersionInfoKeys><VersionInfoKeys Name="ProductVersion">1.0.0.0</VersionInfoKeys><VersionInfoKeys Name="Comments"></VersionInfoKeys></VersionInfoKeys><Debugging><Debugging Name="DebugSourceDirs"></Debugging></Debugging><Parameters><Parameters Name="RunParams"></Parameters><Parameters Name="Launcher"></Parameters><Parameters Name="UseLauncher">False</Parameters><Parameters Name="DebugCWD"></Parameters><Parameters Name="HostApplication"></Parameters><Parameters Name="RemoteHost"></Parameters><Parameters Name="RemotePath"></Parameters><Parameters Name="RemoteParams"></Parameters><Parameters Name="RemoteLauncher"></Parameters><Parameters Name="UseRemoteLauncher">False</Parameters><Parameters Name="RemoteCWD"></Parameters><Parameters Name="RemoteDebug">False</Parameters><Parameters Name="Debug Symbols Search Path"></Parameters><Parameters Name="LoadAllSymbols">True</Parameters><Parameters Name="LoadUnspecifiedSymbols">False</Parameters></Parameters><Excluded_Packages>
|
||||
<Excluded_Packages Name="$(BDS)\bin\bcboffice2k100.bpl">CodeGear C++Builder Office 2000 Servers Package</Excluded_Packages>
|
||||
<Excluded_Packages Name="$(BDS)\bin\bcbofficexp100.bpl">CodeGear C++Builder Office XP Servers Package</Excluded_Packages>
|
||||
</Excluded_Packages><Linker><Linker Name="LibPrefix"></Linker><Linker Name="LibSuffix"></Linker><Linker Name="LibVersion"></Linker></Linker><ProjectProperties><ProjectProperties Name="AutoShowDeps">False</ProjectProperties><ProjectProperties Name="ManagePaths">True</ProjectProperties><ProjectProperties Name="VerifyPackages">True</ProjectProperties></ProjectProperties><HistoryLists_hlIncludePath><HistoryLists_hlIncludePath Name="Count">3</HistoryLists_hlIncludePath><HistoryLists_hlIncludePath Name="Item0">$(BDS)\include;$(BDS)\include\dinkumware;$(BDS)\include\vcl;..\src;..\include;..</HistoryLists_hlIncludePath><HistoryLists_hlIncludePath Name="Item1">$(BDS)\include;$(BDS)\include\dinkumware;$(BDS)\include\vcl;..\src;..\include;..</HistoryLists_hlIncludePath><HistoryLists_hlIncludePath Name="Item2">$(BDS)\include;$(BDS)\include\dinkumware;$(BDS)\include\vcl;..\src;..\src;..\include</HistoryLists_hlIncludePath></HistoryLists_hlIncludePath><HistoryLists_hlILINK_LibraryPath><HistoryLists_hlILINK_LibraryPath Name="Count">1</HistoryLists_hlILINK_LibraryPath><HistoryLists_hlILINK_LibraryPath Name="Item0">$(BDS)\lib;$(BDS)\lib\obj;$(BDS)\lib\psdk</HistoryLists_hlILINK_LibraryPath></HistoryLists_hlILINK_LibraryPath><HistoryLists_hlDefines><HistoryLists_hlDefines Name="Count">1</HistoryLists_hlDefines><HistoryLists_hlDefines Name="Item0">NO_STRICT</HistoryLists_hlDefines></HistoryLists_hlDefines><HistoryLists_hlTLIB_PageSize><HistoryLists_hlTLIB_PageSize Name="Count">1</HistoryLists_hlTLIB_PageSize><HistoryLists_hlTLIB_PageSize Name="Item0">32</HistoryLists_hlTLIB_PageSize><HistoryLists_hlTLIB_PageSize Name="Item1">16</HistoryLists_hlTLIB_PageSize></HistoryLists_hlTLIB_PageSize></CPlusPlusBuilder.Personality></BorlandProject></BorlandProject>
|
||||
</ProjectExtensions>
|
||||
<Import Project="$(MSBuildBinPath)\Borland.Cpp.Targets" />
|
||||
<ItemGroup>
|
||||
<CppCompile Include="..\src\gtest_main.cc">
|
||||
<BuildOrder>0</BuildOrder>
|
||||
</CppCompile>
|
||||
<BuildConfiguration Include="Debug">
|
||||
<Key>Cfg_1</Key>
|
||||
</BuildConfiguration>
|
||||
<BuildConfiguration Include="Release">
|
||||
<Key>Cfg_2</Key>
|
||||
</BuildConfiguration>
|
||||
</ItemGroup>
|
||||
</Project>
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue