2020-12-26 10:58:16 -08:00
|
|
|
#include "image_range_grid.h"
|
|
|
|
#include "range_utility.h"
|
|
|
|
|
|
|
|
#include <core/image.h>
|
|
|
|
|
|
|
|
ImageRangeGrid::~ImageRangeGrid() {
|
|
|
|
clear();
|
|
|
|
}
|
|
|
|
|
|
|
|
void ImageRangeGrid::clear() {
|
2020-12-26 11:01:49 -08:00
|
|
|
for (int i = 0; i < _lod_count; ++i) {
|
2020-12-26 10:58:16 -08:00
|
|
|
Lod &lod = _lods[i];
|
|
|
|
if (lod.data != nullptr) {
|
|
|
|
memdelete_arr(lod.data);
|
|
|
|
lod.data = nullptr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
_pixels_x = 0;
|
|
|
|
_pixels_y = 0;
|
|
|
|
_lod_base = 0;
|
|
|
|
_lod_count = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void ImageRangeGrid::generate(Image &im) {
|
|
|
|
clear();
|
|
|
|
|
|
|
|
const int lod_base = 4; // Start at 16
|
|
|
|
|
|
|
|
// Compute first lod
|
|
|
|
{
|
|
|
|
const int chunk_size = 1 << lod_base;
|
|
|
|
|
|
|
|
Lod &lod = _lods[0];
|
|
|
|
lod.size_x = (im.get_width() - 1) / chunk_size + 1;
|
|
|
|
lod.size_y = (im.get_height() - 1) / chunk_size + 1;
|
|
|
|
lod.data = memnew_arr(Interval, lod.size_x * lod.size_y);
|
|
|
|
|
|
|
|
for (int cy = 0; cy < lod.size_y; ++cy) {
|
|
|
|
for (int cx = 0; cx < lod.size_x; ++cx) {
|
|
|
|
const int min_x = cx * chunk_size;
|
|
|
|
const int min_y = cy * chunk_size;
|
|
|
|
const int max_x = min(min_x + chunk_size, im.get_width());
|
|
|
|
const int max_y = min(min_y + chunk_size, im.get_height());
|
|
|
|
|
|
|
|
const Interval r = get_heightmap_range(im, Rect2i(min_x, min_y, max_x - min_x, max_y - min_y));
|
|
|
|
|
|
|
|
lod.data[cx + cy * lod.size_x] = r;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int lod_count = 1;
|
|
|
|
|
|
|
|
// Compute next lods based on previous
|
|
|
|
for (int lod_index = 1; lod_index < MAX_LODS; ++lod_index, ++lod_count) {
|
|
|
|
const Lod &prev_lod = _lods[lod_index - 1];
|
|
|
|
|
|
|
|
if (prev_lod.size_x == 1 && prev_lod.size_y == 1) {
|
|
|
|
// Can't downscale further
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
Lod &lod = _lods[lod_index];
|
|
|
|
CRASH_COND(lod.data != nullptr);
|
|
|
|
|
|
|
|
lod.size_x = max(prev_lod.size_x >> 1, 1);
|
|
|
|
lod.size_y = max(prev_lod.size_y >> 1, 1);
|
|
|
|
|
|
|
|
lod.data = memnew_arr(Interval, lod.size_x * lod.size_y);
|
|
|
|
|
|
|
|
int dst_i = 0;
|
|
|
|
|
|
|
|
for (int cy = 0; cy < lod.size_y; ++cy) {
|
|
|
|
const int src_y = min(cy * 2, prev_lod.size_y);
|
|
|
|
|
|
|
|
for (int cx = 0; cx < lod.size_x; ++cx) {
|
|
|
|
const int src_x = min(cx * 2, prev_lod.size_x);
|
|
|
|
const int a = src_x + src_y * prev_lod.size_x;
|
|
|
|
|
|
|
|
Interval r = prev_lod.data[a];
|
|
|
|
|
|
|
|
if (src_x + 1 < prev_lod.size_x) {
|
|
|
|
r.add_interval(prev_lod.data[a + 1]);
|
|
|
|
|
|
|
|
if (src_y + 1 < prev_lod.size_y) {
|
|
|
|
r.add_interval(prev_lod.data[a + prev_lod.size_x]);
|
|
|
|
r.add_interval(prev_lod.data[a + prev_lod.size_x + 1]);
|
|
|
|
}
|
|
|
|
|
|
|
|
} else if (src_y + 1 < prev_lod.size_y) {
|
|
|
|
r.add_interval(prev_lod.data[a + prev_lod.size_x]);
|
|
|
|
}
|
|
|
|
|
|
|
|
lod.data[dst_i++] = r;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
const int last_lod_index = lod_count - 1;
|
|
|
|
CRASH_COND(last_lod_index < 0);
|
|
|
|
const Lod &lod = _lods[last_lod_index];
|
|
|
|
Interval r;
|
|
|
|
for (int cy = 0; cy < lod.size_y; ++cy) {
|
|
|
|
for (int cx = 0; cx < lod.size_x; ++cx) {
|
|
|
|
r.add_interval(lod.data[cx + cy * lod.size_x]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
_total_range = r;
|
|
|
|
}
|
|
|
|
|
|
|
|
_pixels_x = im.get_width();
|
|
|
|
_pixels_y = im.get_height();
|
|
|
|
_lod_base = lod_base;
|
|
|
|
_lod_count = lod_count;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void interval_to_pixels(Interval i, int &out_min, int &out_max, int len) {
|
|
|
|
// Convert range to integer coordinates
|
2021-01-03 12:23:45 -08:00
|
|
|
const int imin = static_cast<int>(Math::floor(i.min));
|
|
|
|
const int imax = static_cast<int>(Math::ceil(i.max));
|
2020-12-26 10:58:16 -08:00
|
|
|
|
|
|
|
// Images are finite, intervals are not.
|
|
|
|
// It's useless to let the range span a potentially infinite area.
|
|
|
|
// The image can repeat, so we clamp to one repetition.
|
|
|
|
out_min = clamp(imin, -len, len);
|
|
|
|
out_max = clamp(imax, -len, len);
|
|
|
|
}
|
|
|
|
|
|
|
|
Interval ImageRangeGrid::get_range(Interval xr, Interval yr) const {
|
|
|
|
CRASH_COND(_lod_count <= 0);
|
|
|
|
|
|
|
|
int pixel_min_x, pixel_max_x, pixel_min_y, pixel_max_y;
|
|
|
|
interval_to_pixels(xr, pixel_min_x, pixel_max_x, _pixels_x);
|
|
|
|
interval_to_pixels(yr, pixel_min_y, pixel_max_y, _pixels_y);
|
|
|
|
|
|
|
|
// Find best LOD to use.
|
|
|
|
// Depending on the length of the largest range, we may evaluate a different LOD to save iterations
|
|
|
|
int pixel_len = max(pixel_max_x - pixel_min_x, pixel_max_y - pixel_min_y);
|
|
|
|
const int cs = 1 << _lod_base;
|
|
|
|
const int max_overlapping_chunks = 2;
|
|
|
|
int lod_index = 0; // relative to _lod_base
|
|
|
|
while (pixel_len > cs * max_overlapping_chunks && lod_index + 1 < _lod_count) {
|
|
|
|
++lod_index;
|
|
|
|
pixel_len >>= 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
CRASH_COND(lod_index >= _lod_count);
|
|
|
|
|
|
|
|
const int absolute_lod = _lod_base + lod_index;
|
|
|
|
const int x_min = pixel_min_x >> absolute_lod;
|
|
|
|
const int x_max = pixel_max_x >> absolute_lod;
|
|
|
|
const int y_min = pixel_min_y >> absolute_lod;
|
|
|
|
const int y_max = pixel_max_y >> absolute_lod;
|
|
|
|
|
|
|
|
const Lod &lod = _lods[lod_index];
|
|
|
|
|
|
|
|
// Accumulate overlapping chunks
|
|
|
|
Interval r;
|
|
|
|
for (int y = y_min; y <= y_max; ++y) {
|
|
|
|
for (int x = x_min; x <= x_max; ++x) {
|
|
|
|
r.add_interval(lod.data[(x % lod.size_x) + (y % lod.size_y) * lod.size_x]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return r;
|
|
|
|
}
|