Refactored TerrainHeightMap to PaintedGrid for future reuse

This commit is contained in:
Michaël Lemaire 2014-09-15 12:32:27 +02:00
parent 5eed701803
commit 4a01a4314a
15 changed files with 871 additions and 726 deletions

View file

@ -0,0 +1,342 @@
#include "PaintedGrid.h"
#include <cassert>
#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, &yend);
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;
}

View file

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

View file

@ -0,0 +1,84 @@
#include "PaintedGridBrush.h"
#include <cmath>
#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;
}

View file

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

View file

@ -0,0 +1,125 @@
#include "PaintedGridData.h"
#include <cstring>
#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;
}

View file

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

View file

@ -86,7 +86,7 @@ double TerrainDefinition::getGridHeight(int x, int z, int with_painting)
{ {
double h; 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); h = _height_noise->get2DTotal((double)x, (double)z);
} }
@ -100,7 +100,7 @@ double TerrainDefinition::getInterpolatedHeight(double x, double z, int scaled,
x /= scaling; x /= scaling;
z /= 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); h = _height_noise->get2DTotal(x, z);
} }

View file

@ -1,333 +1,11 @@
#include "TerrainHeightMap.h" #include "TerrainHeightMap.h"
#include <cassert>
#include <cstring>
#include "PackStream.h"
#include "Memory.h"
#include "TerrainDefinition.h" #include "TerrainDefinition.h"
#include "TerrainHeightMapBrush.h" #include "PaintedGridBrush.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;
}
TerrainHeightMap::TerrainHeightMap(TerrainDefinition* terrain): 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 void TerrainHeightMap::copy(BaseDefinition* _destination) const
@ -336,150 +14,40 @@ void TerrainHeightMap::copy(BaseDefinition* _destination) const
destination->terrain = terrain; destination->terrain = terrain;
_copyData(merged_data, destination->merged_data); PaintedGrid::copy(destination);
_clearData(destination->brush_data);
} }
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); PaintedGridBrushRaiseLower sbrush(brush);
_clearData(brush_data); 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; PaintedGridBrushFlatten sbrush(brush, height);
dpointer = getDataPointer(brush_data, x, z, NULL, NULL, 0); applyBrush(sbrush, x, y, force / terrain->height);
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;
}
}
} }
int TerrainHeightMap::getInterpolatedHeight(double x, double z, double* result) void TerrainHeightMap::brushSmooth(const PaintedGridBrush &brush, double x, double y, double value)
{ {
int ix, iz; PaintedGridBrushSmooth sbrush(brush);
int xlow; applyBrush(sbrush, x, y, value / terrain->height);
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) void TerrainHeightMap::brushAddNoise(const PaintedGridBrush &brush, double x, double y, NoiseGenerator* generator, double value)
{ {
double stencil[16]; PaintedGridBrushAddNoise sbrush(brush, generator);
double value; applyBrush(sbrush, x, y, value / terrain->height);
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); void TerrainHeightMap::brushReset(const PaintedGridBrush &brush, double x, double y, double value)
}
return hit;
}
unsigned long TerrainHeightMap::getMemoryStats() const
{ {
return merged_data->memsize + brush_data->memsize; PaintedGridBrushReset sbrush(brush);
} applyBrush(sbrush, x, y, value / terrain->height);
bool TerrainHeightMap::isPainted(int x, int z)
{
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);
} }

View file

@ -3,49 +3,30 @@
#include "definition_global.h" #include "definition_global.h"
#include "BaseDefinition.h" #include "PaintedGrid.h"
namespace paysages { namespace paysages {
namespace definition { namespace definition {
class TerrainHeightMapData; class DEFINITIONSHARED_EXPORT TerrainHeightMap : public PaintedGrid
class DEFINITIONSHARED_EXPORT TerrainHeightMap : public BaseDefinition
{ {
public: public:
TerrainHeightMap(TerrainDefinition *terrain); TerrainHeightMap(TerrainDefinition *terrain);
virtual ~TerrainHeightMap();
virtual void copy(BaseDefinition *destination) const override; virtual void copy(BaseDefinition *destination) const override;
virtual void save(PackStream* stream) const override;
virtual void load(PackStream* stream) override;
inline TerrainDefinition* getTerrain() const {return terrain;} 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); virtual double getInitialValue(double x, double y) const override;
unsigned long getMemoryStats() const;
void clearPainting(); void brushElevation(const PaintedGridBrush &brush, double x, double y, double value);
void brushElevation(const TerrainHeightMapBrush &brush, double value); void brushSmooth(const PaintedGridBrush &brush, double x, double y, double value);
void brushSmooth(const TerrainHeightMapBrush &brush, double value); void brushAddNoise(const PaintedGridBrush &brush, double x, double y, NoiseGenerator* generator, double value);
void brushAddNoise(const TerrainHeightMapBrush &brush, NoiseGenerator* generator, double value); void brushReset(const PaintedGridBrush &brush, double x, double y, double value);
void brushReset(const TerrainHeightMapBrush &brush, double value); void brushFlatten(const PaintedGridBrush &brush, double x, double y, double height, double force);
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);
private: private:
TerrainDefinition* terrain; TerrainDefinition* terrain;
TerrainHeightMapData* merged_data;
TerrainHeightMapData* brush_data;
}; };
} }

View file

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

View file

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

View file

@ -27,8 +27,10 @@ SOURCES += \
Zone.cpp \ Zone.cpp \
TerrainDefinition.cpp \ TerrainDefinition.cpp \
TerrainHeightMap.cpp \ TerrainHeightMap.cpp \
TerrainHeightMapBrush.cpp \ Scenery.cpp \
Scenery.cpp PaintedGrid.cpp \
PaintedGridBrush.cpp \
PaintedGridData.cpp
HEADERS +=\ HEADERS +=\
definition_global.h \ definition_global.h \
@ -45,8 +47,10 @@ HEADERS +=\
Zone.h \ Zone.h \
TerrainDefinition.h \ TerrainDefinition.h \
TerrainHeightMap.h \ TerrainHeightMap.h \
TerrainHeightMapBrush.h \ Scenery.h \
Scenery.h PaintedGrid.h \
PaintedGridBrush.h \
PaintedGridData.h
unix:!symbian { unix:!symbian {
maemo5 { maemo5 {

View file

@ -27,12 +27,14 @@ namespace definition {
class TextureLayerDefinition; class TextureLayerDefinition;
class TerrainDefinition; class TerrainDefinition;
class TerrainHeightMap; class TerrainHeightMap;
class TerrainHeightMapBrush; class PaintedGrid;
class TerrainHeightMapBrushElevation; class PaintedGridData;
class TerrainHeightMapBrushSmooth; class PaintedGridBrush;
class TerrainHeightMapBrushAddNoise; class PaintedGridBrushRaiseLower;
class TerrainHeightMapBrushReset; class PaintedGridBrushSmooth;
class TerrainHeightMapBrushFlatten; class PaintedGridBrushAddNoise;
class PaintedGridBrushReset;
class PaintedGridBrushFlatten;
} }
} }
using namespace paysages::definition; using namespace paysages::definition;

View file

@ -5,7 +5,7 @@
#include "NoiseGenerator.h" #include "NoiseGenerator.h"
#include "TerrainDefinition.h" #include "TerrainDefinition.h"
#include "TerrainHeightMap.h" #include "TerrainHeightMap.h"
#include "TerrainHeightMapBrush.h" #include "PaintedGridBrush.h"
PaintingBrush::PaintingBrush() PaintingBrush::PaintingBrush()
{ {
@ -104,13 +104,12 @@ QString PaintingBrush::getHelpText()
void PaintingBrush::applyToTerrain(TerrainDefinition* terrain, double x, double z, double duration, bool reverse) void PaintingBrush::applyToTerrain(TerrainDefinition* terrain, double x, double z, double duration, bool reverse)
{ {
double brush_strength; double brush_strength;
TerrainHeightMapBrush brush;
brush.relative_x = x; double hard_radius = _size * (1.0 - _smoothing);
brush.relative_z = z; double smoothed_size = _size * _smoothing;
brush.hard_radius = _size * (1.0 - _smoothing); double total_radius = hard_radius + smoothed_size;
brush.smoothed_size = _size * _smoothing;
brush.total_radius = brush.hard_radius + brush.smoothed_size; PaintedGridBrush brush(hard_radius, smoothed_size, total_radius);
brush_strength = 0.5 * _strength * duration / 0.1; brush_strength = 0.5 * _strength * duration / 0.1;
@ -121,16 +120,16 @@ void PaintingBrush::applyToTerrain(TerrainDefinition* terrain, double x, double
{ {
brush_strength = -brush_strength; 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; break;
case PAINTING_BRUSH_SMOOTH: case PAINTING_BRUSH_SMOOTH:
if (reverse) if (reverse)
{ {
terrain->height_map->brushSmooth(brush, brush_strength * 30.0); terrain->height_map->brushSmooth(brush, x, z, brush_strength * 30.0);
} }
else else
{ {
terrain->height_map->brushAddNoise(brush, _noise, brush_strength * 0.3); terrain->height_map->brushAddNoise(brush, x, z, _noise, brush_strength * 0.3);
} }
break; break;
case PAINTING_BRUSH_FLATTEN: case PAINTING_BRUSH_FLATTEN:
@ -140,11 +139,11 @@ void PaintingBrush::applyToTerrain(TerrainDefinition* terrain, double x, double
} }
else else
{ {
terrain->height_map->brushFlatten(brush, _height, brush_strength * 30.0); terrain->height_map->brushFlatten(brush, x, z, _height, brush_strength * 30.0);
} }
break; break;
case PAINTING_BRUSH_RESTORE: 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; break;
default: default:
return; return;

View file

@ -4,7 +4,7 @@
#include "NoiseGenerator.h" #include "NoiseGenerator.h"
#include "TerrainDefinition.h" #include "TerrainDefinition.h"
#include "TerrainHeightMap.h" #include "TerrainHeightMap.h"
#include "TerrainHeightMapBrush.h" #include "PaintedGridBrush.h"
/* Noise sin period is defined at 20.0 */ /* Noise sin period is defined at 20.0 */
#define X_FACTOR (M_PI / 10.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)); 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); 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); 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) if (mirror)
{ {
@ -122,31 +122,31 @@ static void _checkBrushResult(TerrainDefinition* terrain, TerrainHeightMapBrush*
TEST_F(TerrainPainting_Test, brush_flatten) TEST_F(TerrainPainting_Test, brush_flatten)
{ {
/* Set up */ /* 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->height = 1.0;
terrain->scaling = 1.0; terrain->scaling = 1.0;
terrain->_height_noise->forceValue(0.0); terrain->_height_noise->forceValue(0.0);
/* Test flattening center at 0.5 */ /* Test flattening center at 0.5 */
_checkBrushResult(terrain, &brush, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0); _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); _checkBrushResult(terrain, &brush, 0.5, 0.5, 0.5, 0.25, 0.0, 0.0, 0);
/* Test brush strength */ /* Test brush strength */
terrain->height_map->clearPainting(); terrain->height_map->clearPainting();
_checkBrushResult(terrain, &brush, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0); _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); _checkBrushResult(terrain, &brush, 0.005, 0.005, 0.005, 0.0025, 0.0, 0.0, 0);
/* Test cumulative effect */ /* 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); _checkBrushResult(terrain, &brush, 0.00995, 0.00995, 0.00995, 0.0049875, 0.0, 0.0, 0);
/* Test with height modifier */ /* Test with height modifier */
terrain->height = 10.0; terrain->height = 10.0;
terrain->height_map->clearPainting(); terrain->height_map->clearPainting();
_checkBrushResult(terrain, &brush, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0); _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); _checkBrushResult(terrain, &brush, 0.05, 0.05, 0.05, 0.025, 0.0, 0.0, 0);
/* Test with scaling modifier */ /* Test with scaling modifier */
@ -154,45 +154,45 @@ TEST_F(TerrainPainting_Test, brush_flatten)
terrain->scaling = 2.0; terrain->scaling = 2.0;
terrain->height_map->clearPainting(); terrain->height_map->clearPainting();
_checkBrushResult(terrain, &brush, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0); _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); _checkBrushResult(terrain, &brush, 0.05, 0.05, 0.05, 0.025, 0.0, 0.0, 0);
} }
TEST_F(TerrainPainting_Test, brush_reset) TEST_F(TerrainPainting_Test, brush_reset)
{ {
/* Set up */ /* Set up */
TerrainHeightMapBrush brush(0.0, 0.0, 2.0, 2.0, 4.0); PaintedGridBrush brush(2.0, 2.0, 4.0);
TerrainHeightMapBrush brush_full(0.0, 0.0, 4.0, 0.0, 4.0); PaintedGridBrush brush_full(4.0, 0.0, 4.0);
terrain->height = 1.0; terrain->height = 1.0;
terrain->scaling = 1.0; terrain->scaling = 1.0;
terrain->_height_noise->forceValue(1.0); terrain->_height_noise->forceValue(1.0);
/* Test resetting at center */ /* Test resetting at center */
_checkBrushResult(terrain, &brush, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 0); _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); _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); _checkBrushResult(terrain, &brush, 1.0, 1.0, 1.0, 1.5, 2.0, 1.0, 0);
/* Test brush strength */ /* Test brush strength */
terrain->height_map->clearPainting(); terrain->height_map->clearPainting();
_checkBrushResult(terrain, &brush, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 0); _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); _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); _checkBrushResult(terrain, &brush, 1.9, 1.9, 1.9, 1.95, 2.0, 1.0, 0);
/* Test cumulative effect */ /* 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); _checkBrushResult(terrain, &brush, 1.81, 1.81, 1.81, 1.9025, 2.0, 1.0, 0);
/* Test with height modifier */ /* Test with height modifier */
terrain->height = 10.0; terrain->height = 10.0;
terrain->height_map->clearPainting(); terrain->height_map->clearPainting();
_checkBrushResult(terrain, &brush, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 0); _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); _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); _checkBrushResult(terrain, &brush, 1.099, 1.099, 1.099, 1.0995, 1.1, 1.0, 0);
/* Test with scaling modifier */ /* Test with scaling modifier */
@ -200,17 +200,17 @@ TEST_F(TerrainPainting_Test, brush_reset)
terrain->scaling = 2.0; terrain->scaling = 2.0;
terrain->height_map->clearPainting(); terrain->height_map->clearPainting();
_checkBrushResult(terrain, &brush, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 0); _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); _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); _checkBrushResult(terrain, &brush, 1.099, 1.099, 1.099, 1.0995, 1.1, 1.0, 0);
} }
TEST_F(TerrainPainting_Test, brush_reset_basevalue) TEST_F(TerrainPainting_Test, brush_reset_basevalue)
{ {
/* Set up */ /* Set up */
TerrainHeightMapBrush brush(0.0, 0.0, 2.0, 2.0, 4.0); PaintedGridBrush brush(2.0, 2.0, 4.0);
TerrainHeightMapBrush brush_full(0.0, 0.0, 4.0, 0.0, 4.0); PaintedGridBrush brush_full(4.0, 0.0, 4.0);
terrain->height = 1.0; terrain->height = 1.0;
terrain->scaling = 1.0; terrain->scaling = 1.0;
@ -218,8 +218,8 @@ TEST_F(TerrainPainting_Test, brush_reset_basevalue)
terrain->height = 1.0; terrain->height = 1.0;
terrain->scaling = 2.0; terrain->scaling = 2.0;
_checkBrushResult(terrain, &brush, 0.0, 0.309016994375, 0.587785252292, 0.809016994375, 0.951056516295, 1.0, 1); _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); _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); _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);
} }