2014-09-15 10:32:27 +00:00
|
|
|
#include "PaintedGrid.h"
|
|
|
|
|
|
|
|
#include <cassert>
|
|
|
|
#include "Memory.h"
|
|
|
|
#include "Interpolation.h"
|
|
|
|
#include "PaintedGridData.h"
|
|
|
|
#include "PaintedGridBrush.h"
|
|
|
|
|
2015-11-09 21:30:46 +00:00
|
|
|
PaintedGrid::PaintedGrid(DefinitionNode *parent) : DefinitionNode(parent, "grid", "grid") {
|
2014-09-15 10:32:27 +00:00
|
|
|
merged_data = new PaintedGridData;
|
|
|
|
brush_data = new PaintedGridData;
|
|
|
|
}
|
|
|
|
|
2015-11-09 21:30:46 +00:00
|
|
|
PaintedGrid::~PaintedGrid() {
|
2014-09-15 10:32:27 +00:00
|
|
|
delete merged_data;
|
|
|
|
delete brush_data;
|
|
|
|
}
|
|
|
|
|
2015-11-09 21:30:46 +00:00
|
|
|
void PaintedGrid::copy(DefinitionNode *_destination) const {
|
|
|
|
PaintedGrid *destination = (PaintedGrid *)_destination;
|
2014-09-15 10:32:27 +00:00
|
|
|
|
|
|
|
merged_data->copy(destination->merged_data);
|
|
|
|
destination->brush_data->clear();
|
|
|
|
}
|
|
|
|
|
2015-11-09 21:30:46 +00:00
|
|
|
void PaintedGrid::save(PackStream *stream) const {
|
2014-09-15 10:32:27 +00:00
|
|
|
merged_data->save(stream);
|
|
|
|
}
|
|
|
|
|
2015-11-09 21:30:46 +00:00
|
|
|
void PaintedGrid::load(PackStream *stream) {
|
2014-09-15 10:32:27 +00:00
|
|
|
merged_data->load(stream);
|
|
|
|
brush_data->clear();
|
|
|
|
}
|
|
|
|
|
2015-11-09 21:30:46 +00:00
|
|
|
bool PaintedGrid::getInterpolatedValue(double x, double y, double *result) const {
|
2014-09-15 10:32:27 +00:00
|
|
|
int ix, iy;
|
|
|
|
int xlow;
|
|
|
|
int ylow;
|
|
|
|
|
|
|
|
xlow = floor(x);
|
|
|
|
ylow = floor(y);
|
|
|
|
|
|
|
|
int hit = 0;
|
2015-11-09 21:30:46 +00:00
|
|
|
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)) {
|
2014-09-15 10:32:27 +00:00
|
|
|
hit = 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-11-09 21:30:46 +00:00
|
|
|
if (hit && result) {
|
2014-09-15 10:32:27 +00:00
|
|
|
double stencil[16];
|
|
|
|
double value;
|
2015-11-09 21:30:46 +00:00
|
|
|
for (ix = xlow - 1; ix <= xlow + 2; ix++) {
|
|
|
|
for (iy = ylow - 1; iy <= ylow + 2; iy++) {
|
|
|
|
if (!getGridValue(ix, iy, &value)) {
|
2014-09-15 10:32:27 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2015-11-09 21:30:46 +00:00
|
|
|
bool PaintedGrid::getGridValue(int x, int y, double *result) const {
|
|
|
|
double *dpointer;
|
2014-09-15 10:32:27 +00:00
|
|
|
dpointer = getDataPointer(brush_data, x, y, NULL, false);
|
2015-11-09 21:30:46 +00:00
|
|
|
if (dpointer) {
|
2014-09-15 10:32:27 +00:00
|
|
|
*result = *dpointer;
|
|
|
|
return true;
|
2015-11-09 21:30:46 +00:00
|
|
|
} else {
|
2014-09-15 10:32:27 +00:00
|
|
|
dpointer = getDataPointer(merged_data, x, y, NULL, false);
|
2015-11-09 21:30:46 +00:00
|
|
|
if (dpointer) {
|
2014-09-15 10:32:27 +00:00
|
|
|
*result = *dpointer;
|
|
|
|
return true;
|
2015-11-09 21:30:46 +00:00
|
|
|
} else {
|
2014-09-15 10:32:27 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-11-09 21:30:46 +00:00
|
|
|
double PaintedGrid::getFinalValue(double x, double y) const {
|
2014-09-15 10:32:27 +00:00
|
|
|
double result;
|
|
|
|
|
2015-11-09 21:30:46 +00:00
|
|
|
if (getInterpolatedValue(x, y, &result)) {
|
2014-09-15 10:32:27 +00:00
|
|
|
return result;
|
2015-11-09 21:30:46 +00:00
|
|
|
} else {
|
2014-09-15 10:32:27 +00:00
|
|
|
return getInitialValue(x, y);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-11-09 21:30:46 +00:00
|
|
|
bool PaintedGrid::hasPainting() const {
|
2015-09-20 23:36:03 +00:00
|
|
|
return merged_data->hasData() || brush_data->hasData();
|
|
|
|
}
|
|
|
|
|
2015-11-09 21:30:46 +00:00
|
|
|
bool PaintedGrid::isPainted(int x, int y) const {
|
2014-09-15 10:32:27 +00:00
|
|
|
return getDataPointer(brush_data, x, y, NULL, false) || getDataPointer(merged_data, x, y, NULL, false);
|
|
|
|
}
|
|
|
|
|
2015-11-09 21:30:46 +00:00
|
|
|
unsigned long PaintedGrid::getMemoryStats() const {
|
2014-09-15 10:32:27 +00:00
|
|
|
return merged_data->memsize + brush_data->memsize;
|
|
|
|
}
|
|
|
|
|
2015-11-09 21:30:46 +00:00
|
|
|
void PaintedGrid::clearPainting() {
|
2014-09-15 10:32:27 +00:00
|
|
|
merged_data->clear();
|
|
|
|
brush_data->clear();
|
|
|
|
}
|
|
|
|
|
2015-11-09 21:30:46 +00:00
|
|
|
void PaintedGrid::applyBrush(const PaintedGridBrush &brush, double x, double y, double force, bool commit) {
|
2014-09-15 10:32:27 +00:00
|
|
|
int xstart, xend, ystart, yend;
|
|
|
|
|
|
|
|
brush.getArea(x, y, &xstart, &ystart, &xend, ¥d);
|
|
|
|
|
|
|
|
int ix, iy;
|
|
|
|
double dx, dy, influence;
|
|
|
|
|
2015-11-09 21:30:46 +00:00
|
|
|
for (ix = xstart; ix <= xend; ix++) {
|
2014-09-15 10:32:27 +00:00
|
|
|
dx = (double)ix;
|
2015-11-09 21:30:46 +00:00
|
|
|
for (iy = ystart; iy <= yend; iy++) {
|
2014-09-15 10:32:27 +00:00
|
|
|
dy = (double)iy;
|
|
|
|
|
|
|
|
influence = brush.getInfluence(x - dx, y - dy);
|
|
|
|
|
2015-11-09 21:30:46 +00:00
|
|
|
if (influence > 0.0) {
|
|
|
|
double *dpointer = getDataPointer(brush_data, ix, iy, merged_data, true);
|
2014-09-15 10:32:27 +00:00
|
|
|
*dpointer = brush.getValue(this, dx, dy, *dpointer, influence, force);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2015-09-21 17:37:17 +00:00
|
|
|
|
2015-11-09 21:30:46 +00:00
|
|
|
if (commit) {
|
2015-09-21 17:37:17 +00:00
|
|
|
endBrushStroke();
|
|
|
|
}
|
2014-09-15 10:32:27 +00:00
|
|
|
}
|
|
|
|
|
2015-11-09 21:30:46 +00:00
|
|
|
void PaintedGrid::endBrushStroke() {
|
2014-09-15 10:32:27 +00:00
|
|
|
int i, j, k;
|
2015-11-09 21:30:46 +00:00
|
|
|
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);
|
2014-09-15 10:32:27 +00:00
|
|
|
*dpointer = data->rows[i].pixel_groups[j].height[k];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
brush_data->clear();
|
|
|
|
}
|
|
|
|
|
2015-11-09 21:30:46 +00:00
|
|
|
double PaintedGrid::getInitialValue(double, double) const {
|
2014-09-15 10:32:27 +00:00
|
|
|
return 0.0;
|
|
|
|
}
|
|
|
|
|
2015-11-09 21:30:46 +00:00
|
|
|
double *PaintedGrid::getDataPointer(PaintedGridData *data, int x, int y, PaintedGridData *fallback, bool grow) const {
|
2014-09-15 10:32:27 +00:00
|
|
|
int i;
|
|
|
|
|
|
|
|
/* Find row */
|
|
|
|
/* TODO Dichotomic search */
|
2015-11-09 21:30:46 +00:00
|
|
|
PaintedGridData::HeightMapRow *row;
|
2014-09-15 10:32:27 +00:00
|
|
|
i = 0;
|
2015-11-09 21:30:46 +00:00
|
|
|
while (i < data->rows_count && data->rows[i].y < y) {
|
2014-09-15 10:32:27 +00:00
|
|
|
i++;
|
|
|
|
}
|
2015-11-09 21:30:46 +00:00
|
|
|
if (i < data->rows_count && data->rows[i].y == y) {
|
2014-09-15 10:32:27 +00:00
|
|
|
row = data->rows + i;
|
2015-11-09 21:30:46 +00:00
|
|
|
} else if (grow) {
|
|
|
|
row = (PaintedGridData::HeightMapRow *)Memory::naiveArrayInsert(
|
|
|
|
(void **)&data->rows, sizeof(PaintedGridData::HeightMapRow), data->rows_count, i);
|
2014-09-15 10:32:27 +00:00
|
|
|
|
|
|
|
row->y = y;
|
|
|
|
row->pixel_groups_count = 0;
|
2015-11-09 21:30:46 +00:00
|
|
|
row->pixel_groups = (PaintedGridData::HeightMapPixelGroup *)malloc(1);
|
2014-09-15 10:32:27 +00:00
|
|
|
|
|
|
|
data->rows_count++;
|
|
|
|
data->memsize += sizeof(PaintedGridData::HeightMapRow);
|
2015-11-09 21:30:46 +00:00
|
|
|
} else {
|
2014-09-15 10:32:27 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifndef NDEBUG
|
|
|
|
/* Check rows */
|
2015-11-09 21:30:46 +00:00
|
|
|
for (i = 1; i < data->rows_count; i++) {
|
2014-09-18 08:09:25 +00:00
|
|
|
assert(data->rows[i].y > data->rows[i - 1].y);
|
2014-09-15 10:32:27 +00:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/* Find pixel group */
|
2015-11-09 21:30:46 +00:00
|
|
|
PaintedGridData::HeightMapPixelGroup *pixel_group = NULL;
|
|
|
|
for (i = 0; i < row->pixel_groups_count; i++) {
|
|
|
|
if (x < row->pixel_groups[i].xstart - 1) {
|
2014-09-15 10:32:27 +00:00
|
|
|
break;
|
2015-11-09 21:30:46 +00:00
|
|
|
} 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) {
|
2014-09-15 10:32:27 +00:00
|
|
|
/* Choose next group if it already includes the pixel */
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
pixel_group = row->pixel_groups + i;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Alter pixel group */
|
2015-11-09 21:30:46 +00:00
|
|
|
double *pixel;
|
2014-09-15 10:32:27 +00:00
|
|
|
int added = 1;
|
2015-11-09 21:30:46 +00:00
|
|
|
if (!pixel_group) {
|
|
|
|
if (!grow) {
|
2014-09-15 10:32:27 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Create the pixel group with one pixel */
|
2015-11-09 21:30:46 +00:00
|
|
|
pixel_group = (PaintedGridData::HeightMapPixelGroup *)Memory::naiveArrayInsert(
|
|
|
|
(void **)&row->pixel_groups, sizeof(PaintedGridData::HeightMapPixelGroup), row->pixel_groups_count, i);
|
2014-09-15 10:32:27 +00:00
|
|
|
|
|
|
|
pixel_group->xstart = x;
|
|
|
|
pixel_group->xend = x;
|
2015-11-09 21:30:46 +00:00
|
|
|
pixel_group->height = (double *)malloc(sizeof(double));
|
2014-09-15 10:32:27 +00:00
|
|
|
|
|
|
|
pixel = pixel_group->height;
|
|
|
|
|
|
|
|
row->pixel_groups_count++;
|
|
|
|
data->memsize += sizeof(PaintedGridData::HeightMapPixelGroup) + sizeof(double);
|
2015-11-09 21:30:46 +00:00
|
|
|
} else if (x == pixel_group->xstart - 1) {
|
|
|
|
if (!grow) {
|
2014-09-15 10:32:27 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Extend the rowgroup at start */
|
|
|
|
pixel_group->xstart--;
|
2015-11-09 21:30:46 +00:00
|
|
|
pixel = (double *)Memory::naiveArrayInsert((void **)&pixel_group->height, sizeof(double),
|
|
|
|
pixel_group->xend - pixel_group->xstart, 0);
|
2014-09-15 10:32:27 +00:00
|
|
|
data->memsize += sizeof(double);
|
2015-11-09 21:30:46 +00:00
|
|
|
} else if (x == pixel_group->xend + 1) {
|
|
|
|
if (!grow) {
|
2014-09-15 10:32:27 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Extend the rowgroup at end */
|
|
|
|
pixel_group->xend++;
|
2015-11-09 21:30:46 +00:00
|
|
|
pixel = (double *)Memory::naiveArrayInsert((void **)&pixel_group->height, sizeof(double),
|
|
|
|
pixel_group->xend - pixel_group->xstart,
|
|
|
|
pixel_group->xend - pixel_group->xstart);
|
2014-09-15 10:32:27 +00:00
|
|
|
data->memsize += sizeof(double);
|
2015-11-09 21:30:46 +00:00
|
|
|
} else {
|
2014-09-15 10:32:27 +00:00
|
|
|
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 */
|
2015-11-09 21:30:46 +00:00
|
|
|
for (i = 0; i < row->pixel_groups_count; i++) {
|
|
|
|
if (i > 0) {
|
2014-09-15 10:32:27 +00:00
|
|
|
assert(row->pixel_groups[i].xstart > row->pixel_groups[i - 1].xend);
|
|
|
|
}
|
2015-11-09 21:30:46 +00:00
|
|
|
if (i < row->pixel_groups_count - 1) {
|
2014-09-15 10:32:27 +00:00
|
|
|
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 */
|
2015-11-09 21:30:46 +00:00
|
|
|
if (added) {
|
|
|
|
if (fallback) {
|
|
|
|
double *dpointer = getDataPointer(fallback, x, y, NULL, false);
|
|
|
|
if (dpointer) {
|
2014-09-15 10:32:27 +00:00
|
|
|
*pixel = *dpointer;
|
2015-11-09 21:30:46 +00:00
|
|
|
} else {
|
2014-09-15 10:32:27 +00:00
|
|
|
*pixel = getInitialValue(x, y);
|
|
|
|
}
|
2015-11-09 21:30:46 +00:00
|
|
|
} else {
|
2014-09-15 10:32:27 +00:00
|
|
|
*pixel = getInitialValue(x, y);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return pixel;
|
|
|
|
}
|