From 4a01a4314a5c7d8379d91fc45c61301bfaa3dfcf Mon Sep 17 00:00:00 2001 From: Michael Lemaire Date: Mon, 15 Sep 2014 12:32:27 +0200 Subject: [PATCH] Refactored TerrainHeightMap to PaintedGrid for future reuse --- src/definition/PaintedGrid.cpp | 342 +++++++++++++ src/definition/PaintedGrid.h | 90 ++++ src/definition/PaintedGridBrush.cpp | 84 ++++ src/definition/PaintedGridBrush.h | 95 ++++ src/definition/PaintedGridData.cpp | 125 +++++ src/definition/PaintedGridData.h | 52 ++ src/definition/TerrainDefinition.cpp | 4 +- src/definition/TerrainHeightMap.cpp | 472 +----------------- src/definition/TerrainHeightMap.h | 39 +- src/definition/TerrainHeightMapBrush.cpp | 119 ----- src/definition/TerrainHeightMapBrush.h | 78 --- src/definition/definition.pro | 12 +- src/definition/definition_global.h | 14 +- .../desktop/terrain/paintingbrush.cpp | 23 +- src/tests/TerrainPainting_Test.cpp | 48 +- 15 files changed, 871 insertions(+), 726 deletions(-) create mode 100644 src/definition/PaintedGrid.cpp create mode 100644 src/definition/PaintedGrid.h create mode 100644 src/definition/PaintedGridBrush.cpp create mode 100644 src/definition/PaintedGridBrush.h create mode 100644 src/definition/PaintedGridData.cpp create mode 100644 src/definition/PaintedGridData.h delete mode 100644 src/definition/TerrainHeightMapBrush.cpp delete mode 100644 src/definition/TerrainHeightMapBrush.h diff --git a/src/definition/PaintedGrid.cpp b/src/definition/PaintedGrid.cpp new file mode 100644 index 0000000..527fef8 --- /dev/null +++ b/src/definition/PaintedGrid.cpp @@ -0,0 +1,342 @@ +#include "PaintedGrid.h" + +#include +#include "Memory.h" +#include "Interpolation.h" +#include "PaintedGridData.h" +#include "PaintedGridBrush.h" + +PaintedGrid::PaintedGrid(BaseDefinition *parent): + BaseDefinition(parent) +{ + merged_data = new PaintedGridData; + brush_data = new PaintedGridData; +} + +PaintedGrid::~PaintedGrid() +{ + delete merged_data; + delete brush_data; +} + +void PaintedGrid::copy(BaseDefinition *_destination) const +{ + PaintedGrid* destination = (PaintedGrid *)_destination; + + merged_data->copy(destination->merged_data); + destination->brush_data->clear(); +} + +void PaintedGrid::save(PackStream *stream) const +{ + merged_data->save(stream); +} + +void PaintedGrid::load(PackStream *stream) +{ + merged_data->load(stream); + brush_data->clear(); +} + +bool PaintedGrid::getInterpolatedValue(double x, double y, double *result) const +{ + int ix, iy; + int xlow; + int ylow; + + xlow = floor(x); + ylow = floor(y); + + int hit = 0; + for (ix = xlow - 1; ix <= xlow + 2 && !hit; ix++) + { + for (iy = ylow - 1; iy <= ylow + 2 && !hit; iy++) + { + if (getDataPointer(brush_data, x, y, NULL, false) || getDataPointer(merged_data, x, y, NULL, false)) + { + hit = 1; + } + } + } + + if (hit && result) + { + double stencil[16]; + double value; + for (ix = xlow - 1; ix <= xlow + 2; ix++) + { + for (iy = ylow - 1; iy <= ylow + 2; iy++) + { + if (!getGridValue(ix, iy, &value)) + { + value = getInitialValue(ix, iy); + } + stencil[(iy - (ylow - 1)) * 4 + ix - (xlow - 1)] = value; + } + } + + *result = Interpolation::bicubic(stencil, x - (double)xlow, y - (double)ylow); + } + + return hit; +} + +bool PaintedGrid::getGridValue(int x, int y, double *result) const +{ + double* dpointer; + dpointer = getDataPointer(brush_data, x, y, NULL, false); + if (dpointer) + { + *result = *dpointer; + return true; + } + else + { + dpointer = getDataPointer(merged_data, x, y, NULL, false); + if (dpointer) + { + *result = *dpointer; + return true; + } + else + { + return false; + } + } +} + +double PaintedGrid::getFinalValue(double x, double y) const +{ + double result; + + if (getInterpolatedValue(x, y, &result)) + { + return result; + } + else + { + return getInitialValue(x, y); + } +} + +bool PaintedGrid::isPainted(int x, int y) const +{ + return getDataPointer(brush_data, x, y, NULL, false) || getDataPointer(merged_data, x, y, NULL, false); +} + +unsigned long PaintedGrid::getMemoryStats() const +{ + return merged_data->memsize + brush_data->memsize; +} + +void PaintedGrid::clearPainting() +{ + merged_data->clear(); + brush_data->clear(); +} + +void PaintedGrid::applyBrush(const PaintedGridBrush &brush, double x, double y, double force) +{ + int xstart, xend, ystart, yend; + + brush.getArea(x, y, &xstart, &ystart, &xend, ¥d); + + int ix, iy; + double dx, dy, influence; + + for (ix = xstart; ix <= xend; ix++) + { + dx = (double)ix; + for (iy = ystart; iy <= yend; iy++) + { + dy = (double)iy; + + influence = brush.getInfluence(x - dx, y - dy); + + if (influence > 0.0) + { + double* dpointer = getDataPointer(brush_data, ix, iy, merged_data, true); + *dpointer = brush.getValue(this, dx, dy, *dpointer, influence, force); + } + } + } +} + +void PaintedGrid::endBrushStroke() +{ + int i, j, k; + PaintedGridData* data = brush_data; + + for (i = 0; i < data->rows_count; i++) + { + for (j = 0; j < data->rows[i].pixel_groups_count; j++) + { + for (k = 0; k < data->rows[i].pixel_groups[j].xend - data->rows[i].pixel_groups[j].xstart + 1; k++) + { + double* dpointer = getDataPointer(merged_data, data->rows[i].pixel_groups[j].xstart + k, data->rows[i].y, NULL, true); + *dpointer = data->rows[i].pixel_groups[j].height[k]; + } + } + } + + brush_data->clear(); +} + +double PaintedGrid::getInitialValue(double, double) const +{ + return 0.0; +} + +double *PaintedGrid::getDataPointer(PaintedGridData *data, int x, int y, PaintedGridData *fallback, bool grow) const +{ + int i; + + /* Find row */ + /* TODO Dichotomic search */ + PaintedGridData::HeightMapRow* row; + i = 0; + while (i < data->rows_count && data->rows[i].y < y) + { + i++; + } + if (i < data->rows_count && data->rows[i].y == y) + { + row = data->rows + i; + } + else if (grow) + { + row = (PaintedGridData::HeightMapRow*)Memory::naiveArrayInsert((void**)&data->rows, sizeof(PaintedGridData::HeightMapRow), data->rows_count, i); + + row->y = y; + row->pixel_groups_count = 0; + row->pixel_groups = (PaintedGridData::HeightMapPixelGroup*)malloc(1); + + data->rows_count++; + data->memsize += sizeof(PaintedGridData::HeightMapRow); + } + else + { + return NULL; + } + +#ifndef NDEBUG + /* Check rows */ + for (i = 1; i < data->rows_count; i++) + { + assert(data->rows[i].z > data->rows[i - 1].z); + } +#endif + + /* Find pixel group */ + PaintedGridData::HeightMapPixelGroup* pixel_group = NULL; + for (i = 0; i < row->pixel_groups_count; i++) + { + if (x < row->pixel_groups[i].xstart - 1) + { + break; + } + else if (x <= row->pixel_groups[i].xend + 1) + { + if (x == row->pixel_groups[i].xend + 1 && i < row->pixel_groups_count - 1 && x == row->pixel_groups[i + 1].xstart) + { + /* Choose next group if it already includes the pixel */ + i++; + } + pixel_group = row->pixel_groups + i; + break; + } + } + + /* Alter pixel group */ + double* pixel; + int added = 1; + if (!pixel_group) + { + if (!grow) + { + return NULL; + } + + /* Create the pixel group with one pixel */ + pixel_group = (PaintedGridData::HeightMapPixelGroup*)Memory::naiveArrayInsert((void**)&row->pixel_groups, sizeof(PaintedGridData::HeightMapPixelGroup), row->pixel_groups_count, i); + + pixel_group->xstart = x; + pixel_group->xend = x; + pixel_group->height = (double*)malloc(sizeof(double)); + + pixel = pixel_group->height; + + row->pixel_groups_count++; + data->memsize += sizeof(PaintedGridData::HeightMapPixelGroup) + sizeof(double); + } + else if (x == pixel_group->xstart - 1) + { + if (!grow) + { + return NULL; + } + + /* Extend the rowgroup at start */ + pixel_group->xstart--; + pixel = (double*)Memory::naiveArrayInsert((void**)&pixel_group->height, sizeof(double), pixel_group->xend - pixel_group->xstart, 0); + data->memsize += sizeof(double); + } + else if (x == pixel_group->xend + 1) + { + if (!grow) + { + return NULL; + } + + /* Extend the rowgroup at end */ + pixel_group->xend++; + pixel = (double*)Memory::naiveArrayInsert((void**)&pixel_group->height, sizeof(double), pixel_group->xend - pixel_group->xstart, pixel_group->xend - pixel_group->xstart); + data->memsize += sizeof(double); + } + else + { + assert(x >= pixel_group->xstart); + assert(x <= pixel_group->xend); + pixel = pixel_group->height + x - pixel_group->xstart; + added = 0; + } + +#ifndef NDEBUG + /* Check pixel groups */ + for (i = 0; i < row->pixel_groups_count; i++) + { + if (i > 0) + { + assert(row->pixel_groups[i].xstart > row->pixel_groups[i - 1].xend); + } + if (i < row->pixel_groups_count - 1) + { + assert(row->pixel_groups[i].xend < row->pixel_groups[i + 1].xstart); + } + assert(row->pixel_groups[i].xend >= row->pixel_groups[i].xstart); + } +#endif + + /* Reset pixel if it had been added */ + if (added) + { + if (fallback) + { + double* dpointer = getDataPointer(fallback, x, y, NULL, false); + if (dpointer) + { + *pixel = *dpointer; + } + else + { + *pixel = getInitialValue(x, y); + } + } + else + { + *pixel = getInitialValue(x, y); + } + } + + return pixel; +} diff --git a/src/definition/PaintedGrid.h b/src/definition/PaintedGrid.h new file mode 100644 index 0000000..49dad40 --- /dev/null +++ b/src/definition/PaintedGrid.h @@ -0,0 +1,90 @@ +#ifndef PAINTEDGRID_H +#define PAINTEDGRID_H + +#include "definition_global.h" + +#include "BaseDefinition.h" + +namespace paysages { +namespace definition { + +/** + * Grid of values that can be painted using a brush. + * + * This may be useful for terrain altitude, texture presence... + * + * Grid cells are considered to be 1.0-sized. + */ +class DEFINITIONSHARED_EXPORT PaintedGrid: public BaseDefinition +{ +public: + PaintedGrid(BaseDefinition *parent=0); + virtual ~PaintedGrid(); + + virtual void copy(BaseDefinition *destination) const override; + virtual void save(PackStream *stream) const override; + virtual void load(PackStream *stream) override; + + /** + * Get the interpolated value at a given location. + * + * Returns true if the value is user-specified in the grid, false otherwise. + */ + bool getInterpolatedValue(double x, double y, double *result) const; + + /** + * Get the grid-stored value at a grid point. + * + * Returns true if the value is user-specified in the grid, false otherwise. + */ + bool getGridValue(int x, int y, double *result) const; + + /** + * Get the final interpolated value, using initial value as fallback. + */ + double getFinalValue(double x, double y) const; + + /** + * Returns true if a grid point is user-specified, false if it's the initial value. + */ + bool isPainted(int x, int y) const; + + /** + * Get the number of bytes used by this grid. + */ + unsigned long getMemoryStats() const; + + /** + * Clear all painting and reset the initial value everywhere. + */ + void clearPainting(); + + /** + * Apply a brush stroke at a grid location (locating the brush center). + */ + void applyBrush(const PaintedGridBrush &brush, double x, double y, double force); + + /** + * Commit previous brush strokes. + */ + void endBrushStroke(); + + /** + * Virtual method that can be reimplemented to provide the initial value at a grid location. + * + * By default, the initial value will be 0.0. + */ + virtual double getInitialValue(double x, double y) const; + +private: + double *getDataPointer(PaintedGridData *data, int x, int y, PaintedGridData *fallback, bool grow) const; + +private: + PaintedGridData *merged_data; + PaintedGridData *brush_data; +}; + +} +} + +#endif // PAINTEDGRID_H diff --git a/src/definition/PaintedGridBrush.cpp b/src/definition/PaintedGridBrush.cpp new file mode 100644 index 0000000..ffba79b --- /dev/null +++ b/src/definition/PaintedGridBrush.cpp @@ -0,0 +1,84 @@ +#include "PaintedGridBrush.h" + +#include +#include "NoiseGenerator.h" +#include "PaintedGrid.h" + +PaintedGridBrush::PaintedGridBrush(double hard_radius, double smoothed_size, double total_radius): + hard_radius(hard_radius), smoothed_size(smoothed_size), total_radius(total_radius) +{ +} + +void PaintedGridBrush::getArea(double x, double y, int *xstart, int *ystart, int *xend, int *yend) const +{ + double s = smoothed_size + hard_radius; + + *xstart = (int)floor(x - s); + *xend = (int)ceil(x + s); + *ystart = (int)floor(y - s); + *yend = (int)ceil(y + s); +} + +double PaintedGridBrush::getInfluence(double dx, double dy) const +{ + double distance = sqrt(dx * dx + dy * dy); + + if (distance > hard_radius) + { + if (distance <= hard_radius + smoothed_size) + { + return 1.0 - (distance - hard_radius) / smoothed_size; + } + else + { + return 0.0; + } + } + else + { + return 1.0; + } +} + +double PaintedGridBrush::getValue(const PaintedGrid *, double, double, double basevalue, double, double) const +{ + return basevalue; +} + +double PaintedGridBrushRaiseLower::getValue(const PaintedGrid *, double, double, double basevalue, double influence, double force) const +{ + return basevalue + influence * force; +} + +double PaintedGridBrushSmooth::getValue(const PaintedGrid *grid, double x, double y, double basevalue, double influence, double force) const +{ + double ideal, factor; + ideal = grid->getFinalValue((x + total_radius * 0.5), y); + ideal += grid->getFinalValue((x - total_radius * 0.5), y); + ideal += grid->getFinalValue(x, (y - total_radius * 0.5)); + ideal += grid->getFinalValue(x, (y + total_radius * 0.5)); + ideal /= 4.0; + factor = influence * force; + if (factor > 1.0) + { + factor = 0.0; + } + return basevalue + (ideal - basevalue) * factor; +} + +double PaintedGridBrushAddNoise::getValue(const PaintedGrid *, double x, double y, double basevalue, double influence, double force) const +{ + return basevalue + generator->get2DTotal(x / total_radius, y / total_radius) * influence * force * total_radius; +} + +double PaintedGridBrushReset::getValue(const PaintedGrid *grid, double x, double y, double basevalue, double influence, double force) const +{ + double ideal = grid->getInitialValue(x, y); + return basevalue + (ideal - basevalue) * influence * force; +} + +double PaintedGridBrushFlatten::getValue(const PaintedGrid *, double, double, double basevalue, double influence, double force) const +{ + double ideal = target; + return basevalue + (ideal - basevalue) * influence * force; +} diff --git a/src/definition/PaintedGridBrush.h b/src/definition/PaintedGridBrush.h new file mode 100644 index 0000000..764d21d --- /dev/null +++ b/src/definition/PaintedGridBrush.h @@ -0,0 +1,95 @@ +#ifndef PAINTEDGRIDBRUSH_H +#define PAINTEDGRIDBRUSH_H + +#include "definition_global.h" + +namespace paysages { +namespace definition { + +/** + * Base class for brushes that can be used to paint values on a PaintedGrid. + */ +class DEFINITIONSHARED_EXPORT PaintedGridBrush +{ +public: + PaintedGridBrush(double hard_radius, double smoothed_size, double total_radius); + + /** + * Get the rectangle area potentially covered by this brush. + */ + void getArea(double x, double y, int *xstart, int *ystart, int *xend, int *yend) const; + + /** + * Get the brush influence for coordinates relative to its center. + */ + double getInfluence(double dx, double dy) const; + + /** + * Abstract method to reimplement to get the final value of a brush stroke at a given point. + */ + virtual double getValue(const PaintedGrid *grid, double x, double y, double basevalue, double influence, double force) const; + +protected: + double hard_radius; + double smoothed_size; + double total_radius; +}; + +/** + * Brush able to raise or lower the grid value. + */ +class DEFINITIONSHARED_EXPORT PaintedGridBrushRaiseLower: public PaintedGridBrush +{ +public: + PaintedGridBrushRaiseLower(const PaintedGridBrush &brush) : PaintedGridBrush(brush) {} + double getValue(const PaintedGrid *grid, double x, double z, double basevalue, double influence, double force) const override; +}; + +/** + * Brush able to smooth the value in an area. + */ +class DEFINITIONSHARED_EXPORT PaintedGridBrushSmooth: public PaintedGridBrush +{ +public: + PaintedGridBrushSmooth(const PaintedGridBrush &brush) : PaintedGridBrush(brush) {} + double getValue(const PaintedGrid *grid, double x, double z, double basevalue, double influence, double force) const override; +}; + +/** + * Brush able to add random fractal noise. + */ +class DEFINITIONSHARED_EXPORT PaintedGridBrushAddNoise: public PaintedGridBrush +{ +public: + PaintedGridBrushAddNoise(const PaintedGridBrush &brush, NoiseGenerator *generator) : PaintedGridBrush(brush), generator(generator) {} + double getValue(const PaintedGrid *grid, double x, double z, double basevalue, double influence, double force) const override; +private: + NoiseGenerator *generator; +}; + +/** + * Brush able to reset to initial value. + */ +class DEFINITIONSHARED_EXPORT PaintedGridBrushReset: public PaintedGridBrush +{ +public: + PaintedGridBrushReset(const PaintedGridBrush &brush) : PaintedGridBrush(brush) {} + double getValue(const PaintedGrid *grid, double x, double z, double basevalue, double influence, double force) const override; +}; + +/** + * Brush able to flatten to a specific value. + */ +class DEFINITIONSHARED_EXPORT PaintedGridBrushFlatten: public PaintedGridBrush +{ +public: + PaintedGridBrushFlatten(const PaintedGridBrush &brush, double target) : PaintedGridBrush(brush), target(target) {} + double getValue(const PaintedGrid *grid, double x, double z, double basevalue, double influence, double force) const override; +private: + double target; +}; + +} +} + +#endif // PAINTEDGRIDBRUSH_H diff --git a/src/definition/PaintedGridData.cpp b/src/definition/PaintedGridData.cpp new file mode 100644 index 0000000..f0359a1 --- /dev/null +++ b/src/definition/PaintedGridData.cpp @@ -0,0 +1,125 @@ +#include "PaintedGridData.h" + +#include +#include "PackStream.h" + +PaintedGridData::PaintedGridData() +{ + rows_count = 0; + rows = new HeightMapRow[1]; + memsize = 0; +} + +PaintedGridData::~PaintedGridData() +{ + clear(); + delete[] rows; +} + +void PaintedGridData::copy(PaintedGridData *destination) const +{ + int i, j, n; + size_t size; + + destination->clear(); + + destination->rows_count = this->rows_count; + if (destination->rows_count > 0) + { + size = sizeof(HeightMapRow) * destination->rows_count; + destination->rows = (HeightMapRow*)realloc(destination->rows, size); + destination->memsize += size; + for (i = 0; i < destination->rows_count; i++) + { + destination->rows[i].y = this->rows[i].y; + destination->rows[i].pixel_groups_count = this->rows[i].pixel_groups_count; + size = sizeof(HeightMapPixelGroup) * destination->rows[i].pixel_groups_count; + destination->rows[i].pixel_groups = (HeightMapPixelGroup*)malloc(size); + destination->memsize += size; + for (j = 0; j < destination->rows[i].pixel_groups_count; j++) + { + destination->rows[i].pixel_groups[j].xstart = this->rows[i].pixel_groups[j].xstart; + destination->rows[i].pixel_groups[j].xend = this->rows[i].pixel_groups[j].xend; + n = destination->rows[i].pixel_groups[j].xend - destination->rows[i].pixel_groups[j].xstart + 1; + size = sizeof(double) * n; + destination->rows[i].pixel_groups[j].height = (double*)malloc(size); + destination->memsize += size; + memcpy(destination->rows[i].pixel_groups[j].height, this->rows[i].pixel_groups[j].height, size); + } + } + } +} + +void PaintedGridData::save(PackStream *stream) const +{ + int i, j, k; + stream->write(&rows_count); + for (i = 0; i < rows_count; i++) + { + stream->write(&rows[i].y); + stream->write(&rows[i].pixel_groups_count); + for (j = 0; j < rows[i].pixel_groups_count; j++) + { + stream->write(&rows[i].pixel_groups[j].xstart); + stream->write(&rows[i].pixel_groups[j].xend); + for (k = 0; k < rows[i].pixel_groups[j].xend - rows[i].pixel_groups[j].xstart; k++) + { + stream->write(&rows[i].pixel_groups[j].height[k]); + } + } + } +} + +void PaintedGridData::load(PackStream *stream) +{ + int i, j, k, n; + size_t size; + + clear(); + + stream->read(&rows_count); + if (rows_count > 0) + { + size = sizeof(HeightMapRow) * rows_count; + rows = (HeightMapRow*)realloc(rows, size); + memsize += size; + for (i = 0; i < rows_count; i++) + { + stream->read(&rows[i].y); + stream->read(&rows[i].pixel_groups_count); + size = sizeof(HeightMapPixelGroup) * rows[i].pixel_groups_count; + rows[i].pixel_groups = (HeightMapPixelGroup*)malloc(size); + memsize += size; + for (j = 0; j < rows[i].pixel_groups_count; j++) + { + stream->read(&rows[i].pixel_groups[j].xstart); + stream->read(&rows[i].pixel_groups[j].xend); + n = rows[i].pixel_groups[j].xend - rows[i].pixel_groups[j].xstart; + size = sizeof(double) * n; + rows[i].pixel_groups[j].height = (double*)malloc(size); + memsize += size; + for (k = 0; k < n; k++) + { + stream->read(&rows[i].pixel_groups[j].height[k]); + } + } + } + } +} + +void PaintedGridData::clear() +{ + int i, j; + for (i = 0; i < rows_count; i++) + { + for (j = 0; j < rows[i].pixel_groups_count; j++) + { + free(rows[i].pixel_groups[j].height); + } + free(rows[i].pixel_groups); + } + rows_count = 0; + delete[] rows; + rows = new HeightMapRow[1]; + memsize = 0; +} diff --git a/src/definition/PaintedGridData.h b/src/definition/PaintedGridData.h new file mode 100644 index 0000000..fe55562 --- /dev/null +++ b/src/definition/PaintedGridData.h @@ -0,0 +1,52 @@ +#ifndef PAINTEDGRIDDATA_H +#define PAINTEDGRIDDATA_H + +#include "definition_global.h" + +namespace paysages { +namespace definition { + +/** + * Internal storage class to hold data for a PaintedGrid. + */ +class PaintedGridData +{ + friend class PaintedGrid; + +public: + PaintedGridData(); + ~PaintedGridData(); + + void copy(PaintedGridData *destination) const; + void save(PackStream *stream) const; + void load(PackStream *stream); + + /** + * Clear all stored data. + */ + void clear(); + +private: + typedef struct + { + int xstart; + int xend; + double* height; + } HeightMapPixelGroup; + + typedef struct + { + int y; + int pixel_groups_count; + HeightMapPixelGroup* pixel_groups; + } HeightMapRow; + + int memsize; + int rows_count; + HeightMapRow* rows; +}; + +} +} + +#endif // PAINTEDGRIDDATA_H diff --git a/src/definition/TerrainDefinition.cpp b/src/definition/TerrainDefinition.cpp index 2d74702..8ec30a3 100644 --- a/src/definition/TerrainDefinition.cpp +++ b/src/definition/TerrainDefinition.cpp @@ -86,7 +86,7 @@ double TerrainDefinition::getGridHeight(int x, int z, int with_painting) { double h; - if (!with_painting || !height_map->getGridHeight(x, z, &h)) + if (!with_painting || !height_map->getGridValue(x, z, &h)) { h = _height_noise->get2DTotal((double)x, (double)z); } @@ -100,7 +100,7 @@ double TerrainDefinition::getInterpolatedHeight(double x, double z, int scaled, x /= scaling; z /= scaling; - if (!with_painting || !height_map->getInterpolatedHeight(x, z, &h)) + if (!with_painting || !height_map->getInterpolatedValue(x, z, &h)) { h = _height_noise->get2DTotal(x, z); } diff --git a/src/definition/TerrainHeightMap.cpp b/src/definition/TerrainHeightMap.cpp index 2d366b5..a00e809 100644 --- a/src/definition/TerrainHeightMap.cpp +++ b/src/definition/TerrainHeightMap.cpp @@ -1,333 +1,11 @@ #include "TerrainHeightMap.h" -#include -#include -#include "PackStream.h" -#include "Memory.h" #include "TerrainDefinition.h" -#include "TerrainHeightMapBrush.h" -#include "Interpolation.h" -#include "NoiseGenerator.h" - -typedef struct -{ - int xstart; - int xend; - double* height; -} HeightMapPixelGroup; - -typedef struct -{ - int z; - int pixel_groups_count; - HeightMapPixelGroup* pixel_groups; -} HeightMapRow; - -namespace paysages { -namespace definition { -class TerrainHeightMapData -{ -public: - int memsize; - int rows_count; - HeightMapRow* rows; -}; -} -} - -static void _initData(TerrainHeightMapData* data) -{ - data->rows_count = 0; - data->rows = new HeightMapRow[1]; - data->memsize = 0; -} - -static void _clearData(TerrainHeightMapData* data) -{ - int i, j; - for (i = 0; i < data->rows_count; i++) - { - for (j = 0; j < data->rows[i].pixel_groups_count; j++) - { - free(data->rows[i].pixel_groups[j].height); - } - free(data->rows[i].pixel_groups); - } - data->rows_count = 0; - delete[] data->rows; - data->rows = new HeightMapRow[1]; - data->memsize = 0; -} - -static void _deleteData(TerrainHeightMapData* data) -{ - _clearData(data); - delete[] data->rows; - delete data; -} - -static void _copyData(TerrainHeightMapData* source, TerrainHeightMapData* destination) -{ - int i, j, n; - size_t size; - - _clearData(destination); - - destination->rows_count = source->rows_count; - if (destination->rows_count > 0) - { - size = sizeof(HeightMapRow) * destination->rows_count; - destination->rows = (HeightMapRow*)realloc(destination->rows, size); - destination->memsize += size; - for (i = 0; i < destination->rows_count; i++) - { - destination->rows[i].z = source->rows[i].z; - destination->rows[i].pixel_groups_count = source->rows[i].pixel_groups_count; - size = sizeof(HeightMapPixelGroup) * destination->rows[i].pixel_groups_count; - destination->rows[i].pixel_groups = (HeightMapPixelGroup*)malloc(size); - destination->memsize += size; - for (j = 0; j < destination->rows[i].pixel_groups_count; j++) - { - destination->rows[i].pixel_groups[j].xstart = source->rows[i].pixel_groups[j].xstart; - destination->rows[i].pixel_groups[j].xend = source->rows[i].pixel_groups[j].xend; - n = destination->rows[i].pixel_groups[j].xend - destination->rows[i].pixel_groups[j].xstart + 1; - size = sizeof(double) * n; - destination->rows[i].pixel_groups[j].height = (double*)malloc(size); - destination->memsize += size; - memcpy(destination->rows[i].pixel_groups[j].height, source->rows[i].pixel_groups[j].height, size); - } - } - } -} - -static void _saveData(PackStream* stream, TerrainHeightMapData* data) -{ - int i, j, k; - stream->write(&data->rows_count); - for (i = 0; i < data->rows_count; i++) - { - stream->write(&data->rows[i].z); - stream->write(&data->rows[i].pixel_groups_count); - for (j = 0; j < data->rows[i].pixel_groups_count; j++) - { - stream->write(&data->rows[i].pixel_groups[j].xstart); - stream->write(&data->rows[i].pixel_groups[j].xend); - for (k = 0; k < data->rows[i].pixel_groups[j].xend - data->rows[i].pixel_groups[j].xstart; k++) - { - stream->write(&data->rows[i].pixel_groups[j].height[k]); - } - } - } -} - -static void _loadData(PackStream* stream, TerrainHeightMapData* data) -{ - int i, j, k, n; - size_t size; - - _clearData(data); - - stream->read(&data->rows_count); - if (data->rows_count > 0) - { - size = sizeof(HeightMapRow) * data->rows_count; - data->rows = (HeightMapRow*)realloc(data->rows, size); - data->memsize += size; - for (i = 0; i < data->rows_count; i++) - { - stream->read(&data->rows[i].z); - stream->read(&data->rows[i].pixel_groups_count); - size = sizeof(HeightMapPixelGroup) * data->rows[i].pixel_groups_count; - data->rows[i].pixel_groups = (HeightMapPixelGroup*)malloc(size); - data->memsize += size; - for (j = 0; j < data->rows[i].pixel_groups_count; j++) - { - stream->read(&data->rows[i].pixel_groups[j].xstart); - stream->read(&data->rows[i].pixel_groups[j].xend); - n = data->rows[i].pixel_groups[j].xend - data->rows[i].pixel_groups[j].xstart; - size = sizeof(double) * n; - data->rows[i].pixel_groups[j].height = (double*)malloc(size); - data->memsize += size; - for (k = 0; k < n; k++) - { - stream->read(&data->rows[i].pixel_groups[j].height[k]); - } - } - } - } -} - -/* - * Get a pointer to the data in a heightmap, to a certain location. - * If the location is not already in the heightmap, it is initialized with the terrain height. - * This method will grow the heightmap as necessary (if 'grow' is set to false, NULL will be returned on missing pixels). - */ -inline double* TerrainHeightMap::getDataPointer(TerrainHeightMapData* data, int x, int z, TerrainHeightMapData* fallback, TerrainDefinition* terrain, int grow) -{ - int i; - - /* Find row */ - /* TODO Dichotomic search */ - HeightMapRow* row; - i = 0; - while (i < data->rows_count && data->rows[i].z < z) - { - i++; - } - if (i < data->rows_count && data->rows[i].z == z) - { - row = data->rows + i; - } - else if (grow) - { - row = (HeightMapRow*)Memory::naiveArrayInsert((void**)&data->rows, sizeof(HeightMapRow), data->rows_count, i); - - row->z = z; - row->pixel_groups_count = 0; - row->pixel_groups = (HeightMapPixelGroup*)malloc(1); - - data->rows_count++; - data->memsize += sizeof(HeightMapRow); - } - else - { - return NULL; - } - -#ifndef NDEBUG - /* Check rows */ - for (i = 1; i < data->rows_count; i++) - { - assert(data->rows[i].z > data->rows[i - 1].z); - } -#endif - - /* Find pixel group */ - HeightMapPixelGroup* pixel_group = NULL; - for (i = 0; i < row->pixel_groups_count; i++) - { - if (x < row->pixel_groups[i].xstart - 1) - { - break; - } - else if (x <= row->pixel_groups[i].xend + 1) - { - if (x == row->pixel_groups[i].xend + 1 && i < row->pixel_groups_count - 1 && x == row->pixel_groups[i + 1].xstart) - { - /* Choose next group if it already includes the pixel */ - i++; - } - pixel_group = row->pixel_groups + i; - break; - } - } - - /* Alter pixel group */ - double* pixel; - int added = 1; - if (!pixel_group) - { - if (!grow) - { - return NULL; - } - - /* Create the pixel group with one pixel */ - pixel_group = (HeightMapPixelGroup*)Memory::naiveArrayInsert((void**)&row->pixel_groups, sizeof(HeightMapPixelGroup), row->pixel_groups_count, i); - - pixel_group->xstart = x; - pixel_group->xend = x; - pixel_group->height = (double*)malloc(sizeof(double)); - - pixel = pixel_group->height; - - row->pixel_groups_count++; - data->memsize += sizeof(HeightMapPixelGroup) + sizeof(double); - } - else if (x == pixel_group->xstart - 1) - { - if (!grow) - { - return NULL; - } - - /* Extend the rowgroup at start */ - pixel_group->xstart--; - pixel = (double*)Memory::naiveArrayInsert((void**)&pixel_group->height, sizeof(double), pixel_group->xend - pixel_group->xstart, 0); - data->memsize += sizeof(double); - } - else if (x == pixel_group->xend + 1) - { - if (!grow) - { - return NULL; - } - - /* Extend the rowgroup at end */ - pixel_group->xend++; - pixel = (double*)Memory::naiveArrayInsert((void**)&pixel_group->height, sizeof(double), pixel_group->xend - pixel_group->xstart, pixel_group->xend - pixel_group->xstart); - data->memsize += sizeof(double); - } - else - { - assert(x >= pixel_group->xstart); - assert(x <= pixel_group->xend); - pixel = pixel_group->height + x - pixel_group->xstart; - added = 0; - } - -#ifndef NDEBUG - /* Check pixel groups */ - for (i = 0; i < row->pixel_groups_count; i++) - { - if (i > 0) - { - assert(row->pixel_groups[i].xstart > row->pixel_groups[i - 1].xend); - } - if (i < row->pixel_groups_count - 1) - { - assert(row->pixel_groups[i].xend < row->pixel_groups[i + 1].xstart); - } - assert(row->pixel_groups[i].xend >= row->pixel_groups[i].xstart); - } -#endif - - /* Reset pixel if it had been added */ - if (added && (terrain || fallback)) - { - if (fallback) - { - double* dpointer = getDataPointer(fallback, x, z, NULL, terrain, 0); - if (dpointer) - { - *pixel = *dpointer; - } - else if (terrain) - { - *pixel = terrain->getGridHeight(x, z, 0); - } - } - else if (terrain) - { - *pixel = terrain->getGridHeight(x, z, 0); - } - } - return pixel; -} +#include "PaintedGridBrush.h" TerrainHeightMap::TerrainHeightMap(TerrainDefinition* terrain): - BaseDefinition(terrain), terrain(terrain) + PaintedGrid(terrain), terrain(terrain) { - merged_data = new TerrainHeightMapData; - brush_data = new TerrainHeightMapData; - _initData(merged_data); - _initData(brush_data); -} - -TerrainHeightMap::~TerrainHeightMap() -{ - _deleteData(merged_data); - _deleteData(brush_data); } void TerrainHeightMap::copy(BaseDefinition* _destination) const @@ -336,150 +14,40 @@ void TerrainHeightMap::copy(BaseDefinition* _destination) const destination->terrain = terrain; - _copyData(merged_data, destination->merged_data); - _clearData(destination->brush_data); + PaintedGrid::copy(destination); } -void TerrainHeightMap::save(PackStream* stream) const +double TerrainHeightMap::getInitialValue(double x, double y) const { - _saveData(stream, merged_data); + return terrain->getInterpolatedHeight(x * terrain->scaling, y * terrain->scaling, false, false); } -void TerrainHeightMap::load(PackStream* stream) +void TerrainHeightMap::brushElevation(const PaintedGridBrush &brush, double x, double y, double value) { - _loadData(stream, merged_data); - _clearData(brush_data); + PaintedGridBrushRaiseLower sbrush(brush); + applyBrush(sbrush, x, y, value / terrain->height); } -int TerrainHeightMap::getGridHeight(int x, int z, double* result) +void TerrainHeightMap::brushFlatten(const PaintedGridBrush &brush, double x, double y, double height, double force) { - double* dpointer; - dpointer = getDataPointer(brush_data, x, z, NULL, NULL, 0); - if (dpointer) - { - *result = *dpointer; - return 1; - } - else - { - dpointer = getDataPointer(merged_data, x, z, NULL, NULL, 0); - if (dpointer) - { - *result = *dpointer; - return 1; - } - else - { - return 0; - } - } + PaintedGridBrushFlatten sbrush(brush, height); + applyBrush(sbrush, x, y, force / terrain->height); } -int TerrainHeightMap::getInterpolatedHeight(double x, double z, double* result) +void TerrainHeightMap::brushSmooth(const PaintedGridBrush &brush, double x, double y, double value) { - int ix, iz; - int xlow; - int zlow; - - xlow = floor(x); - zlow = floor(z); - - int hit = 0; - for (ix = xlow - 1; ix <= xlow + 2 && !hit; ix++) - { - for (iz = zlow - 1; iz <= zlow + 2 && !hit; iz++) - { - if (getDataPointer(brush_data, x, z, NULL, NULL, 0) || getDataPointer(merged_data, x, z, NULL, NULL, 0)) - { - hit = 1; - } - } - } - - if (hit && result) - { - double stencil[16]; - double value; - for (ix = xlow - 1; ix <= xlow + 2; ix++) - { - for (iz = zlow - 1; iz <= zlow + 2; iz++) - { - if (!getGridHeight(ix, iz, &value)) - { - value = terrain->getGridHeight(ix, iz, 0); - } - stencil[(iz - (zlow - 1)) * 4 + ix - (xlow - 1)] = value; - } - } - - *result = Interpolation::bicubic(stencil, x - (double)xlow, z - (double)zlow); - } - - return hit; + PaintedGridBrushSmooth sbrush(brush); + applyBrush(sbrush, x, y, value / terrain->height); } -unsigned long TerrainHeightMap::getMemoryStats() const +void TerrainHeightMap::brushAddNoise(const PaintedGridBrush &brush, double x, double y, NoiseGenerator* generator, double value) { - return merged_data->memsize + brush_data->memsize; + PaintedGridBrushAddNoise sbrush(brush, generator); + applyBrush(sbrush, x, y, value / terrain->height); } -bool TerrainHeightMap::isPainted(int x, int z) +void TerrainHeightMap::brushReset(const PaintedGridBrush &brush, double x, double y, double value) { - return getDataPointer(brush_data, x, z, NULL, NULL, 0) || getDataPointer(merged_data, x, z, NULL, NULL, 0); -} - -void TerrainHeightMap::clearPainting() -{ - _clearData(merged_data); - _clearData(brush_data); -} - -void TerrainHeightMap::brushElevation(const TerrainHeightMapBrush &brush, double value) -{ - TerrainHeightMapBrushElevation sbrush(brush); - sbrush.apply(this, value); -} - -void TerrainHeightMap::brushFlatten(const TerrainHeightMapBrush &brush, double height, double force) -{ - TerrainHeightMapBrushFlatten sbrush(brush, height); - sbrush.apply(this, force); -} - -void TerrainHeightMap::brushSmooth(const TerrainHeightMapBrush &brush, double value) -{ - TerrainHeightMapBrushSmooth sbrush(brush); - sbrush.apply(this, value); -} - -void TerrainHeightMap::brushAddNoise(const TerrainHeightMapBrush &brush, NoiseGenerator* generator, double value) -{ - TerrainHeightMapBrushAddNoise sbrush(brush, generator); - sbrush.apply(this, value); -} - -void TerrainHeightMap::brushReset(const TerrainHeightMapBrush &brush, double value) -{ - TerrainHeightMapBrushReset sbrush(brush); - sbrush.apply(this, value); -} - -void TerrainHeightMap::endBrushStroke() -{ - int i, j, k; - TerrainHeightMapData* data = brush_data; - - for (i = 0; i < data->rows_count; i++) - { - for (j = 0; j < data->rows[i].pixel_groups_count; j++) - { - for (k = 0; k < data->rows[i].pixel_groups[j].xend - data->rows[i].pixel_groups[j].xstart + 1; k++) - { - double* dpointer = getDataPointer(merged_data, data->rows[i].pixel_groups[j].xstart + k, data->rows[i].z, NULL, NULL, 1); - *dpointer = data->rows[i].pixel_groups[j].height[k]; - } - } - } - - _clearData(brush_data); + PaintedGridBrushReset sbrush(brush); + applyBrush(sbrush, x, y, value / terrain->height); } diff --git a/src/definition/TerrainHeightMap.h b/src/definition/TerrainHeightMap.h index 495e8b5..035bf37 100644 --- a/src/definition/TerrainHeightMap.h +++ b/src/definition/TerrainHeightMap.h @@ -3,49 +3,30 @@ #include "definition_global.h" -#include "BaseDefinition.h" +#include "PaintedGrid.h" namespace paysages { namespace definition { -class TerrainHeightMapData; - -class DEFINITIONSHARED_EXPORT TerrainHeightMap : public BaseDefinition +class DEFINITIONSHARED_EXPORT TerrainHeightMap : public PaintedGrid { public: - TerrainHeightMap(TerrainDefinition* terrain); - virtual ~TerrainHeightMap(); + TerrainHeightMap(TerrainDefinition *terrain); - virtual void copy(BaseDefinition* destination) const override; - virtual void save(PackStream* stream) const override; - virtual void load(PackStream* stream) override; + virtual void copy(BaseDefinition *destination) const override; inline TerrainDefinition* getTerrain() const {return terrain;} - int getInterpolatedHeight(double x, double z, double* result); - int getGridHeight(int x, int z, double* result); - bool isPainted(int x, int z); - unsigned long getMemoryStats() const; + virtual double getInitialValue(double x, double y) const override; - void clearPainting(); - void brushElevation(const TerrainHeightMapBrush &brush, double value); - void brushSmooth(const TerrainHeightMapBrush &brush, double value); - void brushAddNoise(const TerrainHeightMapBrush &brush, NoiseGenerator* generator, double value); - void brushReset(const TerrainHeightMapBrush &brush, double value); - void brushFlatten(const TerrainHeightMapBrush &brush, double height, double force); - void endBrushStroke(); - - friend class TerrainHeightMapBrush; - friend class TerrainHeightMapBrushSmooth; - friend class TerrainHeightMapBrushReset; - -protected: - double* getDataPointer(TerrainHeightMapData* data, int x, int z, TerrainHeightMapData* fallback, TerrainDefinition* terrain, int grow); + void brushElevation(const PaintedGridBrush &brush, double x, double y, double value); + void brushSmooth(const PaintedGridBrush &brush, double x, double y, double value); + void brushAddNoise(const PaintedGridBrush &brush, double x, double y, NoiseGenerator* generator, double value); + void brushReset(const PaintedGridBrush &brush, double x, double y, double value); + void brushFlatten(const PaintedGridBrush &brush, double x, double y, double height, double force); private: TerrainDefinition* terrain; - TerrainHeightMapData* merged_data; - TerrainHeightMapData* brush_data; }; } diff --git a/src/definition/TerrainHeightMapBrush.cpp b/src/definition/TerrainHeightMapBrush.cpp deleted file mode 100644 index af16d8a..0000000 --- a/src/definition/TerrainHeightMapBrush.cpp +++ /dev/null @@ -1,119 +0,0 @@ -#include "TerrainHeightMapBrush.h" - -#include "TerrainDefinition.h" -#include "TerrainHeightMap.h" -#include "NoiseGenerator.h" - -typedef struct -{ - int xstart; - int xend; - int xsize; - int zstart; - int zend; - int zsize; -} IntegerRect; - -static inline IntegerRect _getBrushRect(TerrainHeightMapBrush* brush) -{ - IntegerRect result; - double s = brush->smoothed_size + brush->hard_radius; - - result.xstart = (int)floor(brush->relative_x - s); - result.xend = (int)ceil(brush->relative_x + s); - result.zstart = (int)floor(brush->relative_z - s); - result.zend = (int)ceil(brush->relative_z + s); - - result.xsize = result.xend - result.xstart + 1; - result.zsize = result.zend - result.zstart + 1; - - return result; -} - -static inline int _isInRect(const IntegerRect &rect, int x, int z) -{ - return (x >= rect.xstart && x <= rect.xend && z >= rect.zstart && z <= rect.zend); -} - -void TerrainHeightMapBrush::apply(TerrainHeightMap* heightmap, double force) -{ - IntegerRect brush_rect = _getBrushRect(this); - int x, z; - double dx, dz, distance, influence; - - force /= heightmap->terrain->height; - - for (x = brush_rect.xstart; x <= brush_rect.xend; x++) - { - dx = (double)x; - for (z = brush_rect.zstart; z <= brush_rect.zend; z++) - { - dz = (double)z; - distance = sqrt((relative_x - dx) * (relative_x - dx) + (relative_z - dz) * (relative_z - dz)); - - if (distance > hard_radius) - { - if (distance <= hard_radius + smoothed_size) - { - influence = (1.0 - (distance - hard_radius) / smoothed_size); - } - else - { - continue; - } - } - else - { - influence = 1.0; - } - - double* dpointer = heightmap->getDataPointer(heightmap->brush_data, x, z, heightmap->merged_data, heightmap->terrain, 1); - *dpointer = getBrushValue(heightmap, dx, dz, *dpointer, influence, force); - } - } -} - -double TerrainHeightMapBrush::getBrushValue(TerrainHeightMap*, double, double, double basevalue, double, double) const -{ - return basevalue; -} - -double TerrainHeightMapBrushElevation::getBrushValue(TerrainHeightMap*, double, double, double basevalue, double influence, double force) const -{ - return basevalue + influence * force; -} - -double TerrainHeightMapBrushSmooth::getBrushValue(TerrainHeightMap* heightmap, double x, double z, double basevalue, double influence, double force) const -{ - TerrainDefinition* terrain = heightmap->terrain; - double ideal, factor; - ideal = terrain->getInterpolatedHeight((x + total_radius * 0.5) * terrain->scaling, z * terrain->scaling, 0, 1); - ideal += terrain->getInterpolatedHeight((x - total_radius * 0.5) * terrain->scaling, z * terrain->scaling, 0, 1); - ideal += terrain->getInterpolatedHeight(x * terrain->scaling, (z - total_radius * 0.5) * terrain->scaling, 0, 1); - ideal += terrain->getInterpolatedHeight(x * terrain->scaling, (z + total_radius * 0.5) * terrain->scaling, 0, 1); - ideal /= 4.0; - factor = influence * force; - if (factor > 1.0) - { - factor = 0.0; - } - return basevalue + (ideal - basevalue) * factor; -} - -double TerrainHeightMapBrushAddNoise::getBrushValue(TerrainHeightMap*, double x, double z, double basevalue, double influence, double force) const -{ - return basevalue + generator->get2DTotal(x / total_radius, z / total_radius) * influence * force * total_radius; -} - -double TerrainHeightMapBrushReset::getBrushValue(TerrainHeightMap* heightmap, double x, double z, double basevalue, double influence, double force) const -{ - TerrainDefinition* terrain = heightmap->terrain; - double ideal = terrain->getInterpolatedHeight(x * terrain->scaling, z * terrain->scaling, 0, 0); - return basevalue + (ideal - basevalue) * influence * force; -} - -double TerrainHeightMapBrushFlatten::getBrushValue(TerrainHeightMap*, double, double, double basevalue, double influence, double force) const -{ - double ideal = height; - return basevalue + (ideal - basevalue) * influence * force; -} diff --git a/src/definition/TerrainHeightMapBrush.h b/src/definition/TerrainHeightMapBrush.h deleted file mode 100644 index 4161965..0000000 --- a/src/definition/TerrainHeightMapBrush.h +++ /dev/null @@ -1,78 +0,0 @@ -#ifndef TERRAINHEIGHTMAPBRUSH_H -#define TERRAINHEIGHTMAPBRUSH_H - -#include "definition_global.h" - -namespace paysages { -namespace definition { - -class DEFINITIONSHARED_EXPORT TerrainHeightMapBrush -{ -public: - TerrainHeightMapBrush() {} - TerrainHeightMapBrush(double relative_x, double relative_z, double hard_radius, double smoothed_size, double total_radius): - relative_x(relative_x), relative_z(relative_z), hard_radius(hard_radius), smoothed_size(smoothed_size), total_radius(total_radius) {} - - void apply(TerrainHeightMap* heightmap, double force); - -protected: - virtual double getBrushValue(TerrainHeightMap* heightmap, double x, double z, double basevalue, double influence, double force) const; - -public: - double relative_x; - double relative_z; - double hard_radius; - double smoothed_size; - double total_radius; -}; - -class DEFINITIONSHARED_EXPORT TerrainHeightMapBrushElevation: public TerrainHeightMapBrush -{ -public: - TerrainHeightMapBrushElevation(const TerrainHeightMapBrush& brush) : TerrainHeightMapBrush(brush) {} -protected: - double getBrushValue(TerrainHeightMap* heightmap, double x, double z, double basevalue, double influence, double force) const override; -}; - -class DEFINITIONSHARED_EXPORT TerrainHeightMapBrushSmooth: public TerrainHeightMapBrush -{ -public: - TerrainHeightMapBrushSmooth(const TerrainHeightMapBrush& brush) : TerrainHeightMapBrush(brush) {} -protected: - double getBrushValue(TerrainHeightMap* heightmap, double x, double z, double basevalue, double influence, double force) const override; -}; - -class DEFINITIONSHARED_EXPORT TerrainHeightMapBrushAddNoise: public TerrainHeightMapBrush -{ -public: - TerrainHeightMapBrushAddNoise(NoiseGenerator* generator) : generator(generator) {} - TerrainHeightMapBrushAddNoise(const TerrainHeightMapBrush& brush, NoiseGenerator* generator) : TerrainHeightMapBrush(brush), generator(generator) {} -protected: - double getBrushValue(TerrainHeightMap* heightmap, double x, double z, double basevalue, double influence, double force) const override; -private: - NoiseGenerator* generator; -}; - -class DEFINITIONSHARED_EXPORT TerrainHeightMapBrushReset: public TerrainHeightMapBrush -{ -public: - TerrainHeightMapBrushReset(const TerrainHeightMapBrush& brush) : TerrainHeightMapBrush(brush) {} -protected: - double getBrushValue(TerrainHeightMap* heightmap, double x, double z, double basevalue, double influence, double force) const override; -}; - -class DEFINITIONSHARED_EXPORT TerrainHeightMapBrushFlatten: public TerrainHeightMapBrush -{ -public: - TerrainHeightMapBrushFlatten(double height) : height(height) {} - TerrainHeightMapBrushFlatten(const TerrainHeightMapBrush& brush, double height) : TerrainHeightMapBrush(brush), height(height) {} -protected: - double getBrushValue(TerrainHeightMap* heightmap, double x, double z, double basevalue, double influence, double force) const override; -private: - double height; -}; - -} -} - -#endif // TERRAINHEIGHTMAPBRUSH_H diff --git a/src/definition/definition.pro b/src/definition/definition.pro index 21b6b85..b56b5fb 100644 --- a/src/definition/definition.pro +++ b/src/definition/definition.pro @@ -27,8 +27,10 @@ SOURCES += \ Zone.cpp \ TerrainDefinition.cpp \ TerrainHeightMap.cpp \ - TerrainHeightMapBrush.cpp \ - Scenery.cpp + Scenery.cpp \ + PaintedGrid.cpp \ + PaintedGridBrush.cpp \ + PaintedGridData.cpp HEADERS +=\ definition_global.h \ @@ -45,8 +47,10 @@ HEADERS +=\ Zone.h \ TerrainDefinition.h \ TerrainHeightMap.h \ - TerrainHeightMapBrush.h \ - Scenery.h + Scenery.h \ + PaintedGrid.h \ + PaintedGridBrush.h \ + PaintedGridData.h unix:!symbian { maemo5 { diff --git a/src/definition/definition_global.h b/src/definition/definition_global.h index 78585e3..c120893 100644 --- a/src/definition/definition_global.h +++ b/src/definition/definition_global.h @@ -27,12 +27,14 @@ namespace definition { class TextureLayerDefinition; class TerrainDefinition; class TerrainHeightMap; - class TerrainHeightMapBrush; - class TerrainHeightMapBrushElevation; - class TerrainHeightMapBrushSmooth; - class TerrainHeightMapBrushAddNoise; - class TerrainHeightMapBrushReset; - class TerrainHeightMapBrushFlatten; + class PaintedGrid; + class PaintedGridData; + class PaintedGridBrush; + class PaintedGridBrushRaiseLower; + class PaintedGridBrushSmooth; + class PaintedGridBrushAddNoise; + class PaintedGridBrushReset; + class PaintedGridBrushFlatten; } } using namespace paysages::definition; diff --git a/src/interface/desktop/terrain/paintingbrush.cpp b/src/interface/desktop/terrain/paintingbrush.cpp index bd017f5..a6e8c7c 100644 --- a/src/interface/desktop/terrain/paintingbrush.cpp +++ b/src/interface/desktop/terrain/paintingbrush.cpp @@ -5,7 +5,7 @@ #include "NoiseGenerator.h" #include "TerrainDefinition.h" #include "TerrainHeightMap.h" -#include "TerrainHeightMapBrush.h" +#include "PaintedGridBrush.h" PaintingBrush::PaintingBrush() { @@ -104,13 +104,12 @@ QString PaintingBrush::getHelpText() void PaintingBrush::applyToTerrain(TerrainDefinition* terrain, double x, double z, double duration, bool reverse) { double brush_strength; - TerrainHeightMapBrush brush; - brush.relative_x = x; - brush.relative_z = z; - brush.hard_radius = _size * (1.0 - _smoothing); - brush.smoothed_size = _size * _smoothing; - brush.total_radius = brush.hard_radius + brush.smoothed_size; + double hard_radius = _size * (1.0 - _smoothing); + double smoothed_size = _size * _smoothing; + double total_radius = hard_radius + smoothed_size; + + PaintedGridBrush brush(hard_radius, smoothed_size, total_radius); brush_strength = 0.5 * _strength * duration / 0.1; @@ -121,16 +120,16 @@ void PaintingBrush::applyToTerrain(TerrainDefinition* terrain, double x, double { brush_strength = -brush_strength; } - terrain->height_map->brushElevation(brush, brush_strength * 2.0); + terrain->height_map->brushElevation(brush, x, z, brush_strength * 2.0); break; case PAINTING_BRUSH_SMOOTH: if (reverse) { - terrain->height_map->brushSmooth(brush, brush_strength * 30.0); + terrain->height_map->brushSmooth(brush, x, z, brush_strength * 30.0); } else { - terrain->height_map->brushAddNoise(brush, _noise, brush_strength * 0.3); + terrain->height_map->brushAddNoise(brush, x, z, _noise, brush_strength * 0.3); } break; case PAINTING_BRUSH_FLATTEN: @@ -140,11 +139,11 @@ void PaintingBrush::applyToTerrain(TerrainDefinition* terrain, double x, double } else { - terrain->height_map->brushFlatten(brush, _height, brush_strength * 30.0); + terrain->height_map->brushFlatten(brush, x, z, _height, brush_strength * 30.0); } break; case PAINTING_BRUSH_RESTORE: - terrain->height_map->brushReset(brush, brush_strength * 30.0); + terrain->height_map->brushReset(brush, x, z, brush_strength * 30.0); break; default: return; diff --git a/src/tests/TerrainPainting_Test.cpp b/src/tests/TerrainPainting_Test.cpp index 6f4f542..1df9b3f 100644 --- a/src/tests/TerrainPainting_Test.cpp +++ b/src/tests/TerrainPainting_Test.cpp @@ -4,7 +4,7 @@ #include "NoiseGenerator.h" #include "TerrainDefinition.h" #include "TerrainHeightMap.h" -#include "TerrainHeightMapBrush.h" +#include "PaintedGridBrush.h" /* Noise sin period is defined at 20.0 */ #define X_FACTOR (M_PI / 10.0) @@ -90,7 +90,7 @@ TEST_F(TerrainPainting_Test, grid) EXPECT_DOUBLE_EQ(terrain->getInterpolatedHeight(3, 0, 1, 0), 6.0 * sin(1.5 * X_FACTOR)); } -static void _checkBrushResultSides(TerrainDefinition* terrain, TerrainHeightMapBrush*, double center, double midhard, double hard, double midsoft, double soft, double exter, double neg_midhard, double neg_hard, double neg_midsoft, double neg_soft, double neg_exter) +static void _checkBrushResultSides(TerrainDefinition* terrain, PaintedGridBrush*, double center, double midhard, double hard, double midsoft, double soft, double exter, double neg_midhard, double neg_hard, double neg_midsoft, double neg_soft, double neg_exter) { EXPECT_DOUBLE_EQ(terrain->getGridHeight(0, 0, 1), center); @@ -107,7 +107,7 @@ static void _checkBrushResultSides(TerrainDefinition* terrain, TerrainHeightMapB EXPECT_DOUBLE_EQ(terrain->getGridHeight(-5, 0, 1), neg_exter); } -static void _checkBrushResult(TerrainDefinition* terrain, TerrainHeightMapBrush* brush, double center, double midhard, double hard, double midsoft, double soft, double exter, int mirror) +static void _checkBrushResult(TerrainDefinition* terrain, PaintedGridBrush* brush, double center, double midhard, double hard, double midsoft, double soft, double exter, int mirror) { if (mirror) { @@ -122,31 +122,31 @@ static void _checkBrushResult(TerrainDefinition* terrain, TerrainHeightMapBrush* TEST_F(TerrainPainting_Test, brush_flatten) { /* Set up */ - TerrainHeightMapBrush brush(0.0, 0.0, 2.0, 2.0, 4.0); + PaintedGridBrush brush(2.0, 2.0, 4.0); terrain->height = 1.0; terrain->scaling = 1.0; terrain->_height_noise->forceValue(0.0); /* Test flattening center at 0.5 */ _checkBrushResult(terrain, &brush, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0); - terrain->height_map->brushFlatten(brush, 0.5, 1.0); + terrain->height_map->brushFlatten(brush, 0.0, 0.0, 0.5, 1.0); _checkBrushResult(terrain, &brush, 0.5, 0.5, 0.5, 0.25, 0.0, 0.0, 0); /* Test brush strength */ terrain->height_map->clearPainting(); _checkBrushResult(terrain, &brush, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0); - terrain->height_map->brushFlatten(brush, 0.5, 0.01); + terrain->height_map->brushFlatten(brush, 0.0, 0.0, 0.5, 0.01); _checkBrushResult(terrain, &brush, 0.005, 0.005, 0.005, 0.0025, 0.0, 0.0, 0); /* Test cumulative effect */ - terrain->height_map->brushFlatten(brush, 0.5, 0.01); + terrain->height_map->brushFlatten(brush, 0.0, 0.0, 0.5, 0.01); _checkBrushResult(terrain, &brush, 0.00995, 0.00995, 0.00995, 0.0049875, 0.0, 0.0, 0); /* Test with height modifier */ terrain->height = 10.0; terrain->height_map->clearPainting(); _checkBrushResult(terrain, &brush, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0); - terrain->height_map->brushFlatten(brush, 0.5, 1.0); + terrain->height_map->brushFlatten(brush, 0.0, 0.0, 0.5, 1.0); _checkBrushResult(terrain, &brush, 0.05, 0.05, 0.05, 0.025, 0.0, 0.0, 0); /* Test with scaling modifier */ @@ -154,45 +154,45 @@ TEST_F(TerrainPainting_Test, brush_flatten) terrain->scaling = 2.0; terrain->height_map->clearPainting(); _checkBrushResult(terrain, &brush, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0); - terrain->height_map->brushFlatten(brush, 0.5, 1.0); + terrain->height_map->brushFlatten(brush, 0.0, 0.0, 0.5, 1.0); _checkBrushResult(terrain, &brush, 0.05, 0.05, 0.05, 0.025, 0.0, 0.0, 0); } TEST_F(TerrainPainting_Test, brush_reset) { /* Set up */ - TerrainHeightMapBrush brush(0.0, 0.0, 2.0, 2.0, 4.0); - TerrainHeightMapBrush brush_full(0.0, 0.0, 4.0, 0.0, 4.0); + PaintedGridBrush brush(2.0, 2.0, 4.0); + PaintedGridBrush brush_full(4.0, 0.0, 4.0); terrain->height = 1.0; terrain->scaling = 1.0; terrain->_height_noise->forceValue(1.0); /* Test resetting at center */ _checkBrushResult(terrain, &brush, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 0); - terrain->height_map->brushFlatten(brush_full, 2.0, 1.0); + terrain->height_map->brushFlatten(brush_full, 0.0, 0.0, 2.0, 1.0); _checkBrushResult(terrain, &brush, 2.0, 2.0, 2.0, 2.0, 2.0, 1.0, 0); - terrain->height_map->brushReset(brush, 1.0); + terrain->height_map->brushReset(brush, 0.0, 0.0, 1.0); _checkBrushResult(terrain, &brush, 1.0, 1.0, 1.0, 1.5, 2.0, 1.0, 0); /* Test brush strength */ terrain->height_map->clearPainting(); _checkBrushResult(terrain, &brush, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 0); - terrain->height_map->brushFlatten(brush_full, 2.0, 1.0); + terrain->height_map->brushFlatten(brush_full, 0.0, 0.0, 2.0, 1.0); _checkBrushResult(terrain, &brush, 2.0, 2.0, 2.0, 2.0, 2.0, 1.0, 0); - terrain->height_map->brushReset(brush, 0.1); + terrain->height_map->brushReset(brush, 0.0, 0.0, 0.1); _checkBrushResult(terrain, &brush, 1.9, 1.9, 1.9, 1.95, 2.0, 1.0, 0); /* Test cumulative effect */ - terrain->height_map->brushReset(brush, 0.1); + terrain->height_map->brushReset(brush, 0.0, 0.0, 0.1); _checkBrushResult(terrain, &brush, 1.81, 1.81, 1.81, 1.9025, 2.0, 1.0, 0); /* Test with height modifier */ terrain->height = 10.0; terrain->height_map->clearPainting(); _checkBrushResult(terrain, &brush, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 0); - terrain->height_map->brushFlatten(brush_full, 2.0, 1.0); + terrain->height_map->brushFlatten(brush_full, 0.0, 0.0, 2.0, 1.0); _checkBrushResult(terrain, &brush, 1.1, 1.1, 1.1, 1.1, 1.1, 1.0, 0); - terrain->height_map->brushReset(brush, 0.1); + terrain->height_map->brushReset(brush, 0.0, 0.0, 0.1); _checkBrushResult(terrain, &brush, 1.099, 1.099, 1.099, 1.0995, 1.1, 1.0, 0); /* Test with scaling modifier */ @@ -200,17 +200,17 @@ TEST_F(TerrainPainting_Test, brush_reset) terrain->scaling = 2.0; terrain->height_map->clearPainting(); _checkBrushResult(terrain, &brush, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 0); - terrain->height_map->brushFlatten(brush_full, 2.0, 1.0); + terrain->height_map->brushFlatten(brush_full, 0.0, 0.0, 2.0, 1.0); _checkBrushResult(terrain, &brush, 1.1, 1.1, 1.1, 1.1, 1.1, 1.0, 0); - terrain->height_map->brushReset(brush, 0.1); + terrain->height_map->brushReset(brush, 0.0, 0.0, 0.1); _checkBrushResult(terrain, &brush, 1.099, 1.099, 1.099, 1.0995, 1.1, 1.0, 0); } TEST_F(TerrainPainting_Test, brush_reset_basevalue) { /* Set up */ - TerrainHeightMapBrush brush(0.0, 0.0, 2.0, 2.0, 4.0); - TerrainHeightMapBrush brush_full(0.0, 0.0, 4.0, 0.0, 4.0); + PaintedGridBrush brush(2.0, 2.0, 4.0); + PaintedGridBrush brush_full(4.0, 0.0, 4.0); terrain->height = 1.0; terrain->scaling = 1.0; @@ -218,8 +218,8 @@ TEST_F(TerrainPainting_Test, brush_reset_basevalue) terrain->height = 1.0; terrain->scaling = 2.0; _checkBrushResult(terrain, &brush, 0.0, 0.309016994375, 0.587785252292, 0.809016994375, 0.951056516295, 1.0, 1); - terrain->height_map->brushFlatten(brush_full, 2.0, 1.0); + terrain->height_map->brushFlatten(brush_full, 0.0, 0.0, 2.0, 1.0); _checkBrushResultSides(terrain, &brush, 2.0, 2.0, 2.0, 2.0, 2.0, 1.0, 2.0, 2.0, 2.0, 2.0, -1.0); - terrain->height_map->brushReset(brush, 1.0); + terrain->height_map->brushReset(brush, 0.0, 0.0, 1.0); _checkBrushResultSides(terrain, &brush, 0.0, 0.309016994375, 0.587785252292, 2.0 - (2.0 - 0.809016994375) * 0.5, 2.0, 1.0, -0.309016994375, -0.587785252292, 2.0 - (2.0 + 0.809016994375) * 0.5, 2.0, -1.0); }