343 lines
8.4 KiB
C++
343 lines
8.4 KiB
C++
|
#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, ¥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;
|
||
|
}
|