mirror of
https://github.com/overte-org/overte.git
synced 2025-08-09 11:07:07 +02:00
implements live LOD and input limit
This commit is contained in:
parent
2c40673e04
commit
e965b88c88
1 changed files with 219 additions and 157 deletions
|
@ -25,6 +25,7 @@
|
||||||
#include <new>
|
#include <new>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
#include <glm/glm.hpp>
|
#include <glm/glm.hpp>
|
||||||
#include <glm/gtc/type_ptr.hpp>
|
#include <glm/gtc/type_ptr.hpp>
|
||||||
|
@ -41,7 +42,7 @@
|
||||||
* V |
|
* V |
|
||||||
* (sort by (tile,brightness))
|
* (sort by (tile,brightness))
|
||||||
* | |
|
* | |
|
||||||
* ->> setLOD ---+ | >retile< ->> setLOD --> (just parameterize
|
* ->> setLOD ---+ | >retile< ->> setLOD --> (just parameterizes
|
||||||
* V V renderer when on-GPU
|
* V V renderer when on-GPU
|
||||||
* (filter by max-LOD brightness, data suffices)
|
* (filter by max-LOD brightness, data suffices)
|
||||||
* build tile info for rendering)
|
* build tile info for rendering)
|
||||||
|
@ -57,8 +58,6 @@
|
||||||
* -----------
|
* -----------
|
||||||
*
|
*
|
||||||
* o FOV culling is too eager - gotta revisit
|
* o FOV culling is too eager - gotta revisit
|
||||||
* o LOD adjustment in a living renderer still needs to be coded (planned)
|
|
||||||
* o input limit (while keeping the brightest) needs to be coded (planned)
|
|
||||||
* o atomics/mutexes need to be added as annotated in the source to allow
|
* o atomics/mutexes need to be added as annotated in the source to allow
|
||||||
* concurrent threads to pull the strings to e.g. have a low priority
|
* concurrent threads to pull the strings to e.g. have a low priority
|
||||||
* thread run the data pipeline for update -- rendering is wait-free
|
* thread run the data pipeline for update -- rendering is wait-free
|
||||||
|
@ -98,6 +97,151 @@ namespace
|
||||||
|
|
||||||
typedef std::vector<InputVertex> InputVertices;
|
typedef std::vector<InputVertex> InputVertices;
|
||||||
|
|
||||||
|
typedef uint16_t BrightnessLevel;
|
||||||
|
typedef std::vector<BrightnessLevel> BrightnessLevels;
|
||||||
|
const unsigned BrightnessBits = 16u;
|
||||||
|
|
||||||
|
BrightnessLevel getBrightness(unsigned c)
|
||||||
|
{
|
||||||
|
unsigned r = (c >> 16) & 0xff;
|
||||||
|
unsigned g = (c >> 8) & 0xff;
|
||||||
|
unsigned b = c & 0xff;
|
||||||
|
return BrightnessLevel((r*r+g*g+b*b) >> 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct BrightnessSortScanner : Radix2IntegerScanner<BrightnessLevel>
|
||||||
|
{
|
||||||
|
typedef Radix2IntegerScanner<BrightnessLevel> Base;
|
||||||
|
BrightnessSortScanner() : Base(BrightnessBits) { }
|
||||||
|
bool bit(BrightnessLevel const& k, state_type& s)
|
||||||
|
{ return ! Base::bit(k,s); }
|
||||||
|
};
|
||||||
|
|
||||||
|
void extractBrightnessLevels(BrightnessLevels& dst, InputVertices const& src)
|
||||||
|
{
|
||||||
|
dst.clear();
|
||||||
|
dst.reserve(src.size());
|
||||||
|
for (InputVertices::const_iterator i =
|
||||||
|
src.begin(), e = src.end(); i != e; ++i)
|
||||||
|
dst.push_back( getBrightness(i->getColor()) );
|
||||||
|
|
||||||
|
radix2InplaceSort(dst.begin(), dst.end(), BrightnessSortScanner());
|
||||||
|
}
|
||||||
|
|
||||||
|
template< class Unit >
|
||||||
|
class HorizontalTiling
|
||||||
|
{
|
||||||
|
unsigned val_k;
|
||||||
|
float val_rcp_slice;
|
||||||
|
unsigned val_bits;
|
||||||
|
public:
|
||||||
|
|
||||||
|
HorizontalTiling(unsigned k)
|
||||||
|
: val_k(k), val_rcp_slice(k / Unit::twice_pi())
|
||||||
|
{
|
||||||
|
val_bits = ceil(log2(getTileCount() ));
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned getAzimuthalTiles() const { return val_k; }
|
||||||
|
unsigned getAltitudinalTiles() const { return val_k / 2 + 1; }
|
||||||
|
unsigned getTileIndexBits() const { return val_bits; }
|
||||||
|
|
||||||
|
unsigned getTileCount() const
|
||||||
|
{
|
||||||
|
return getAzimuthalTiles() * getAltitudinalTiles();
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned getTileIndex(float azimuth, float altitude) const
|
||||||
|
{
|
||||||
|
unsigned result;
|
||||||
|
return discreteAngle(azimuth) % val_k +
|
||||||
|
discreteAngle(altitude + Unit::half_pi()) * val_k;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned getTileIndex(InputVertex const& v) const
|
||||||
|
{
|
||||||
|
return getTileIndex(v.getAzimuth(), v.getAltitude());
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned discreteAngle(float unsigned_angle) const
|
||||||
|
{
|
||||||
|
return unsigned(round(unsigned_angle * val_rcp_slice));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class TileSortScanner : public Radix2IntegerScanner<unsigned>
|
||||||
|
{
|
||||||
|
HorizontalTiling<Degrees> obj_tiling;
|
||||||
|
|
||||||
|
typedef Radix2IntegerScanner<unsigned> Base;
|
||||||
|
public:
|
||||||
|
|
||||||
|
explicit TileSortScanner(HorizontalTiling<Degrees> const& tiling)
|
||||||
|
: Base(tiling.getTileIndexBits() + BrightnessBits),
|
||||||
|
obj_tiling(tiling)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
bool bit(InputVertex const& v, state_type const& s) const
|
||||||
|
{
|
||||||
|
// inspect (tile_index, brightness) tuples
|
||||||
|
unsigned key = getBrightness(v.getColor());
|
||||||
|
key |= obj_tiling.getTileIndex(v) << BrightnessBits;
|
||||||
|
return Base::bit(key, s);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Tile
|
||||||
|
{
|
||||||
|
uint16_t offset;
|
||||||
|
uint16_t count; // according to previous lod setting
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
struct GpuVertex
|
||||||
|
{
|
||||||
|
unsigned val_color;
|
||||||
|
float val_x;
|
||||||
|
float val_y;
|
||||||
|
float val_z;
|
||||||
|
//
|
||||||
|
|
||||||
|
GpuVertex() { }
|
||||||
|
|
||||||
|
GpuVertex(InputVertex const& in)
|
||||||
|
{
|
||||||
|
val_color = in.getColor();
|
||||||
|
float azimuth = in.getAzimuth() * DEG2RAD;
|
||||||
|
float altitude = in.getAltitude() * DEG2RAD;
|
||||||
|
|
||||||
|
// ground vector in x/z plane...
|
||||||
|
float gx = sin(azimuth);
|
||||||
|
float gz = -cos(azimuth);
|
||||||
|
|
||||||
|
// ...elevated in y direction by altitude
|
||||||
|
float exz = cos(altitude);
|
||||||
|
val_x = gx * exz;
|
||||||
|
val_y = sin(altitude);
|
||||||
|
val_z = gz * exz;
|
||||||
|
|
||||||
|
//fprintf(stderr, "Stars.cpp: GpuVertex created (%x,%f,%f,%f)\n", val_color, val_x, val_y, val_z);
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned getColor() const { return val_color; }
|
||||||
|
};
|
||||||
|
|
||||||
|
struct GreaterBrightness
|
||||||
|
{
|
||||||
|
bool operator()(InputVertex const& lhs, InputVertex const& rhs) const
|
||||||
|
{
|
||||||
|
return getBrightness(lhs.getColor())
|
||||||
|
> getBrightness(rhs.getColor());
|
||||||
|
}
|
||||||
|
bool operator()(BrightnessLevel lhs, GpuVertex const& rhs) const
|
||||||
|
{
|
||||||
|
return lhs > getBrightness(rhs.getColor());;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
class Loader : UrlReader
|
class Loader : UrlReader
|
||||||
{
|
{
|
||||||
InputVertices* ptr_vertices;
|
InputVertices* ptr_vertices;
|
||||||
|
@ -105,6 +249,9 @@ namespace
|
||||||
|
|
||||||
unsigned val_lineno;
|
unsigned val_lineno;
|
||||||
char const* str_actual_url;
|
char const* str_actual_url;
|
||||||
|
|
||||||
|
unsigned val_records_read;
|
||||||
|
BrightnessLevel val_min_brightness;
|
||||||
public:
|
public:
|
||||||
|
|
||||||
bool loadVertices(
|
bool loadVertices(
|
||||||
|
@ -163,13 +310,40 @@ namespace
|
||||||
// parse
|
// parse
|
||||||
float azi, alt;
|
float azi, alt;
|
||||||
unsigned c;
|
unsigned c;
|
||||||
if (sscanf(line, "%f %f #%x", & azi, & alt, & c) == 3)
|
if (sscanf(line, " %f %f #%x", & azi, & alt, & c) == 3)
|
||||||
{
|
{
|
||||||
if (ptr_vertices->size() < val_limit)
|
if (val_records_read++ == val_limit)
|
||||||
ptr_vertices->push_back( InputVertex(azi, alt, c) );
|
{
|
||||||
|
std::make_heap(
|
||||||
|
ptr_vertices->begin(), ptr_vertices->end(),
|
||||||
|
GreaterBrightness() );
|
||||||
|
|
||||||
// TODO handle limit by switching to a minheap when
|
val_min_brightness = getBrightness(
|
||||||
// buffer is full
|
ptr_vertices->begin()->getColor() );
|
||||||
|
}
|
||||||
|
if (ptr_vertices->size() == val_limit)
|
||||||
|
{
|
||||||
|
if (val_min_brightness >= getBrightness(c))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
std::pop_heap(
|
||||||
|
ptr_vertices->begin(), ptr_vertices->end(),
|
||||||
|
GreaterBrightness() );
|
||||||
|
ptr_vertices->pop_back();
|
||||||
|
}
|
||||||
|
|
||||||
|
ptr_vertices->push_back( InputVertex(azi, alt, c) );
|
||||||
|
|
||||||
|
if (val_records_read > val_limit)
|
||||||
|
{
|
||||||
|
std::push_heap(
|
||||||
|
ptr_vertices->begin(), ptr_vertices->end(),
|
||||||
|
GreaterBrightness() );
|
||||||
|
ptr_vertices->pop_back();
|
||||||
|
|
||||||
|
val_min_brightness = getBrightness(
|
||||||
|
ptr_vertices->begin()->getColor() );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -186,146 +360,11 @@ namespace
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef uint16_t BrightnessLevel;
|
|
||||||
typedef std::vector<BrightnessLevel> BrightnessLevels;
|
|
||||||
const unsigned BrightnessBits = 16u;
|
|
||||||
|
|
||||||
template< class Vertex >
|
|
||||||
BrightnessLevel getBrightness(Vertex const& v)
|
|
||||||
{
|
|
||||||
unsigned c = v.getColor();
|
|
||||||
unsigned r = (c >> 16) & 0xff;
|
|
||||||
unsigned g = (c >> 8) & 0xff;
|
|
||||||
unsigned b = c & 0xff;
|
|
||||||
return BrightnessLevel((r*r+g*g+b*b) >> 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
struct BrightnessSortScanner : Radix2IntegerScanner<BrightnessLevel>
|
|
||||||
{
|
|
||||||
typedef Radix2IntegerScanner<BrightnessLevel> Base;
|
|
||||||
BrightnessSortScanner() : Base(BrightnessBits) { }
|
|
||||||
bool bit(BrightnessLevel const& k, state_type& s)
|
|
||||||
{ return ! Base::bit(k,s); }
|
|
||||||
};
|
|
||||||
|
|
||||||
void extractBrightnessLevels(BrightnessLevels& dst, InputVertices const& src)
|
|
||||||
{
|
|
||||||
dst.clear();
|
|
||||||
dst.reserve(src.size());
|
|
||||||
for (InputVertices::const_iterator i =
|
|
||||||
src.begin(), e = src.end(); i != e; ++i)
|
|
||||||
dst.push_back( getBrightness(*i) );
|
|
||||||
|
|
||||||
radix2InplaceSort(dst.begin(), dst.end(), BrightnessSortScanner());
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
template< class Unit >
|
|
||||||
class HorizontalTiling
|
|
||||||
{
|
|
||||||
unsigned val_k;
|
|
||||||
float val_rcp_slice;
|
|
||||||
unsigned val_bits;
|
|
||||||
public:
|
|
||||||
|
|
||||||
HorizontalTiling(unsigned k)
|
|
||||||
: val_k(k), val_rcp_slice(k / Unit::twice_pi())
|
|
||||||
{
|
|
||||||
val_bits = ceil(log2(getTileCount() ));
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned getAzimuthalTiles() const { return val_k; }
|
|
||||||
unsigned getAltitudinalTiles() const { return val_k / 2 + 1; }
|
|
||||||
unsigned getTileIndexBits() const { return val_bits; }
|
|
||||||
|
|
||||||
unsigned getTileCount() const
|
|
||||||
{
|
|
||||||
return getAzimuthalTiles() * getAltitudinalTiles();
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned getTileIndex(float azimuth, float altitude) const
|
|
||||||
{
|
|
||||||
unsigned result;
|
|
||||||
return discreteAngle(azimuth) % val_k +
|
|
||||||
discreteAngle(altitude + Unit::half_pi()) * val_k;
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned getTileIndex(InputVertex const& v) const
|
|
||||||
{
|
|
||||||
return getTileIndex(v.getAzimuth(), v.getAltitude());
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned discreteAngle(float unsigned_angle) const
|
|
||||||
{
|
|
||||||
return unsigned(round(unsigned_angle * val_rcp_slice));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class TileSortScanner : public Radix2IntegerScanner<unsigned>
|
|
||||||
{
|
|
||||||
HorizontalTiling<Degrees> obj_tiling;
|
|
||||||
|
|
||||||
typedef Radix2IntegerScanner<unsigned> Base;
|
|
||||||
public:
|
|
||||||
|
|
||||||
explicit TileSortScanner(HorizontalTiling<Degrees> const& tiling)
|
|
||||||
: Base(tiling.getTileIndexBits() + BrightnessBits),
|
|
||||||
obj_tiling(tiling)
|
|
||||||
{ }
|
|
||||||
|
|
||||||
bool bit(InputVertex const& v, state_type const& s) const
|
|
||||||
{
|
|
||||||
// inspect (tile_index, brightness) tuples
|
|
||||||
unsigned key = getBrightness(v);
|
|
||||||
key |= obj_tiling.getTileIndex(v) << BrightnessBits;
|
|
||||||
return Base::bit(key, s);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Tile
|
|
||||||
{
|
|
||||||
uint16_t offset;
|
|
||||||
uint16_t count; // according to previous lod setting
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
struct GpuVertex
|
|
||||||
{
|
|
||||||
unsigned val_color;
|
|
||||||
float val_x;
|
|
||||||
float val_y;
|
|
||||||
float val_z;
|
|
||||||
//
|
|
||||||
|
|
||||||
GpuVertex() { }
|
|
||||||
|
|
||||||
GpuVertex(InputVertex const& in)
|
|
||||||
{
|
|
||||||
val_color = in.getColor();
|
|
||||||
float azimuth = in.getAzimuth() * DEG2RAD;
|
|
||||||
float altitude = in.getAltitude() * DEG2RAD;
|
|
||||||
|
|
||||||
// ground vector in x/z plane...
|
|
||||||
float gx = sin(azimuth);
|
|
||||||
float gz = -cos(azimuth);
|
|
||||||
|
|
||||||
// ...elevated in y direction by altitude
|
|
||||||
float exz = cos(altitude);
|
|
||||||
val_x = gx * exz;
|
|
||||||
val_y = sin(altitude);
|
|
||||||
val_z = gz * exz;
|
|
||||||
|
|
||||||
//fprintf(stderr, "Stars.cpp: GpuVertex created (%x,%f,%f,%f)\n", val_color, val_x, val_y, val_z);
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned getColor() const { return val_color; }
|
|
||||||
};
|
|
||||||
|
|
||||||
class Renderer
|
class Renderer
|
||||||
{
|
{
|
||||||
GpuVertex* ptr_data;
|
GpuVertex* ptr_data;
|
||||||
Tile* ptr_tiles;
|
Tile* ptr_tiles;
|
||||||
BrightnessLevel val_brightness;
|
BrightnessLevel val_lod;
|
||||||
unsigned val_tile_resolution;
|
unsigned val_tile_resolution;
|
||||||
GLint* ptr_batch_offs;
|
GLint* ptr_batch_offs;
|
||||||
GLsizei* ptr_batch_count;
|
GLsizei* ptr_batch_count;
|
||||||
|
@ -335,14 +374,14 @@ namespace
|
||||||
Renderer(InputVertices const& src, size_t n,
|
Renderer(InputVertices const& src, size_t n,
|
||||||
unsigned k, BrightnessLevel b, BrightnessLevel b_max)
|
unsigned k, BrightnessLevel b, BrightnessLevel b_max)
|
||||||
: ptr_data(0l), ptr_tiles(0l),
|
: ptr_data(0l), ptr_tiles(0l),
|
||||||
val_brightness(b), val_tile_resolution(k)
|
val_lod(b), val_tile_resolution(k)
|
||||||
{
|
{
|
||||||
|
|
||||||
HorizontalTiling<Degrees> tiling(k);
|
HorizontalTiling<Degrees> tiling(k);
|
||||||
size_t n_tiles = tiling.getTileCount();
|
size_t n_tiles = tiling.getTileCount();
|
||||||
|
|
||||||
ptr_data = new GpuVertex[n];
|
ptr_data = new GpuVertex[n];
|
||||||
ptr_tiles = new Tile[n_tiles];
|
ptr_tiles = new Tile[n_tiles + 1];
|
||||||
// TODO tighten bounds and save some memory
|
// TODO tighten bounds and save some memory
|
||||||
ptr_batch_offs = new GLint[n_tiles];
|
ptr_batch_offs = new GLint[n_tiles];
|
||||||
ptr_batch_count = new GLsizei[n_tiles];
|
ptr_batch_count = new GLsizei[n_tiles];
|
||||||
|
@ -352,7 +391,7 @@ namespace
|
||||||
for (InputVertices::const_iterator i =
|
for (InputVertices::const_iterator i =
|
||||||
src.begin(), e = src.end(); i != e; ++i)
|
src.begin(), e = src.end(); i != e; ++i)
|
||||||
{
|
{
|
||||||
BrightnessLevel bv = getBrightness(*i);
|
BrightnessLevel bv = getBrightness(i->getColor());
|
||||||
// filter by alloc brightness
|
// filter by alloc brightness
|
||||||
if (bv >= b_max)
|
if (bv >= b_max)
|
||||||
{
|
{
|
||||||
|
@ -391,10 +430,9 @@ namespace
|
||||||
}
|
}
|
||||||
assert(vertex_index == n);
|
assert(vertex_index == n);
|
||||||
// finish last tile (see above)
|
// finish last tile (see above)
|
||||||
Tile* t = ptr_tiles + curr_tile_index;
|
Tile* t = ptr_tiles + curr_tile_index;
|
||||||
Tile* t_last = ptr_tiles + n_tiles;
|
|
||||||
t->count = count_active;
|
t->count = count_active;
|
||||||
for(; ++t != t_last ;)
|
for (Tile* e = ptr_tiles + n_tiles + 1; ++t != e ;)
|
||||||
t->offset = vertex_index, t->count = 0u;
|
t->offset = vertex_index, t->count = 0u;
|
||||||
|
|
||||||
// OpenGL upload
|
// OpenGL upload
|
||||||
|
@ -472,18 +510,42 @@ fprintf(stderr, "Stars.cpp: grid range: [%d;%d) [%d;%d]\n", azi_from, azi_to, al
|
||||||
azi != azi_to; azi = (azi + 1) % azi_dim)
|
azi != azi_to; azi = (azi + 1) % azi_dim)
|
||||||
{
|
{
|
||||||
unsigned tile_index = azi + alt * azi_dim;
|
unsigned tile_index = azi + alt * azi_dim;
|
||||||
Tile& t = ptr_tiles[tile_index];
|
Tile* t = ptr_tiles + tile_index;
|
||||||
|
|
||||||
// TODO handle LOD changes by performing a binary
|
// LOD brightness changed?
|
||||||
// search for the new brightness, if any
|
if (lod != val_lod)
|
||||||
|
{
|
||||||
|
// determine bounds for binary search
|
||||||
|
//
|
||||||
|
// a growing number of stars needs to be rendereed
|
||||||
|
// at decreasing minimum brightness - perform a
|
||||||
|
// binary search in the so found partition for the
|
||||||
|
// new vertex count in this tile
|
||||||
|
|
||||||
if (! t.count)
|
GpuVertex const* start = ptr_data + t[0].offset;
|
||||||
|
GpuVertex const* end = ptr_data + t[1].offset;
|
||||||
|
|
||||||
|
if (start == end)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (lod < val_lod)
|
||||||
|
start += (t->count > 0 ? t->count - 1 : 0);
|
||||||
|
else
|
||||||
|
end = start + t->count;
|
||||||
|
|
||||||
|
end = std::upper_bound(
|
||||||
|
start, end, lod, GreaterBrightness());
|
||||||
|
|
||||||
|
t->count = end - ptr_data + t[0].offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (! t->count)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
fprintf(stderr, "Stars.cpp: tile %d selected (%d vertices at offset %d)\n", tile_index, t.count, t.offset);
|
fprintf(stderr, "Stars.cpp: tile %d selected (%d vertices at offset %d)\n", tile_index, t->count, t->offset);
|
||||||
|
|
||||||
*offs++ = t.offset;
|
*offs++ = t->offset;
|
||||||
*count++ = t.count;
|
*count++ = t->count;
|
||||||
|
|
||||||
++n_batches;
|
++n_batches;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue