Merge branch 'master' into quick_ui

Conflicts:
	src/paysages.pro
This commit is contained in:
Michaël Lemaire 2014-08-27 13:55:51 +02:00
commit 078bab62fc
255 changed files with 3295 additions and 70841 deletions

3
.gitmodules vendored Normal file
View file

@ -0,0 +1,3 @@
[submodule "src/tests/googletest/sources"]
path = src/tests/googletest/sources
url = https://github.com/thunderk/googletest.git

View file

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

View file

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

View file

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

View file

@ -28,7 +28,7 @@ public:
typedef struct
{
double wavelength;
double frequency;
double amplitude;
double minvalue;
} NoiseLevel;

View file

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

View file

@ -85,6 +85,8 @@ private:
Matrix4 projector;
Matrix4 unprojector;
Matrix4 unperspective;
double inv_x_factor;
double inv_y_factor;
};
}

View file

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

View file

@ -0,0 +1,6 @@
#include "WidgetCanvas.h"
WidgetCanvas::WidgetCanvas(QWidget *parent) :
QWidget(parent)
{
}

View 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

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

View 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

View file

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

View file

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

View file

@ -10,6 +10,9 @@ namespace paysages {
namespace desktop {
class BaseInput;
class BaseForm;
class WidgetCanvas;
class WidgetPreviewCanvas;
}
}

View file

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

View file

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

View file

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

View file

@ -7,7 +7,7 @@
#include "tools.h"
#include "DesktopScenery.h"
#include "PackStream.h"
#include "SoftwareRenderer.h"
#include "SoftwareCanvasRenderer.h"
#include "BasePreview.h"
#include "CloudsDefinition.h"
#include "CameraDefinition.h"
@ -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();
}
}

View file

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

View file

@ -10,6 +10,10 @@ SUBDIRS = \
render/opengl \
interface/commandline \
interface/desktop \
interface/quick \
tests/googletest \
tests
interface/quick
exists( tests/googletest/sources/src/gtest-all.cc ) {
SUBDIRS += \
tests/googletest \
tests
}

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

View 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

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

View 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

View 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 &)
{
}

View 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

View 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];
}

View 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

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

View 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

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

View 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

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

View 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

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

View 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

View file

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

View file

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

View file

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

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

View 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

View file

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

View file

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

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

View 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

View file

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

View file

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

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

View 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

View file

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

View file

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

View file

@ -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;
}
double water_height = renderer->getWaterRenderer()->getHeightInfo().min_height;
double startx = chunk->point_nw.x;
double startz = chunk->point_nw.z;
double size = (chunk->point_ne.x - chunk->point_nw.x) / (double)detail;
int i, j;
for (i = 0; i < detail; i++)
{
for (j = 0; j < detail; j++)
{
renderQuad(canvas, startx + (double)i * size, startz + (double)j * size, size, water_height);
}
}
}
static void _renderQuad(SoftwareRenderer* renderer, double x, double z, double size, double water_height)
void TerrainRasterizer::renderQuad(CanvasPortion *canvas, double x, double z, double size, double water_height)
{
Vector3 ov1, ov2, ov3, ov4;
Vector3 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);
}
delete info;
return 0;
tessellateChunk(canvas, chunk, chunk->detail_hint);
}
int TerrainRasterizer::processChunk(TerrainChunkInfo* chunk, double progress)
void TerrainRasterizer::rasterizeToCanvas(CanvasPortion *canvas)
{
ParallelRasterInfo* info = new ParallelRasterInfo;
info->rasterizer = this;
info->chunk = *chunk;
if (!queue->addJob(_parallelJobCallback, info))
{
delete info;
}
renderer->render_progress = 0.05 * progress;
return !renderer->render_interrupt;
getTessellationInfo(canvas, 0);
}
void TerrainRasterizer::renderSurface()
Color TerrainRasterizer::shadeFragment(const CanvasFragment &fragment) const
{
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);
}

View file

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

View file

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

View file

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

View file

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

View file

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

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

View 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

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

View 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

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

View 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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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)
{
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)
if (worker_compat)
{
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)
{
workers[i].thread->join();
delete workers[i].thread;
if (workers[i].result)
{
result++;
}
}
threads[i]->interrupt();
}
for (i = 0; i < thread_count; i++)
{
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();
}

View file

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

View file

@ -0,0 +1,15 @@
#include "ParallelWorker.h"
ParallelWorker::ParallelWorker()
{
interrupted = false;
}
ParallelWorker::~ParallelWorker()
{
}
void ParallelWorker::interrupt()
{
interrupted = true;
}

View 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
View file

@ -0,0 +1,6 @@
#include "Semaphore.h"
Semaphore::Semaphore(int resources):
QSemaphore(resources)
{
}

25
src/system/Semaphore.h Normal file
View 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

View file

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

View file

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

View file

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

View file

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

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

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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