mirror of
https://thingvellir.net/git/overte
synced 2025-03-27 23:52:03 +01:00
Merge pull request #1083 from lordwaym/mod-starfield
Starfield modification to randomly generate stars
This commit is contained in:
commit
53ffff2718
20 changed files with 658 additions and 1102 deletions
|
@ -72,8 +72,8 @@
|
|||
using namespace std;
|
||||
|
||||
// Starfield information
|
||||
static char STAR_FILE[] = "http://s3-us-west-1.amazonaws.com/highfidelity/stars.txt";
|
||||
static char STAR_CACHE_FILE[] = "cachedStars.txt";
|
||||
static unsigned STARFIELD_NUM_STARS = 50000;
|
||||
static unsigned STARFIELD_SEED = 1;
|
||||
|
||||
static const int BANDWIDTH_METER_CLICK_MAX_DRAG_LENGTH = 6; // farther dragged clicks are ignored
|
||||
|
||||
|
@ -2489,8 +2489,8 @@ void Application::displaySide(Camera& whichCamera) {
|
|||
if (Menu::getInstance()->isOptionChecked(MenuOption::Stars)) {
|
||||
PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings),
|
||||
"Application::displaySide() ... stars...");
|
||||
if (!_stars.getFileLoaded()) {
|
||||
_stars.readInput(STAR_FILE, STAR_CACHE_FILE, 0);
|
||||
if (!_stars.isStarsLoaded()) {
|
||||
_stars.generate(STARFIELD_NUM_STARS, STARFIELD_SEED);
|
||||
}
|
||||
// should be the first rendering pass - w/o depth buffer / lighting
|
||||
|
||||
|
|
14
interface/src/Stars.cpp
Normal file → Executable file
14
interface/src/Stars.cpp
Normal file → Executable file
|
@ -14,27 +14,23 @@
|
|||
#undef __interface__Starfield_impl__
|
||||
|
||||
Stars::Stars() :
|
||||
_controller(0l), _fileLoaded(false) {
|
||||
_controller = new starfield::Controller;
|
||||
_controller(0l), _starsLoaded(false) {
|
||||
_controller = new starfield::Controller;
|
||||
}
|
||||
|
||||
Stars::~Stars() {
|
||||
delete _controller;
|
||||
}
|
||||
|
||||
bool Stars::readInput(const char* url, const char* cacheFile, unsigned limit) {
|
||||
_fileLoaded = _controller->readInput(url, cacheFile, limit);
|
||||
return _fileLoaded;
|
||||
bool Stars::generate(unsigned numStars, unsigned seed) {
|
||||
_starsLoaded = _controller->computeStars(numStars, seed);
|
||||
return _starsLoaded;
|
||||
}
|
||||
|
||||
bool Stars::setResolution(unsigned k) {
|
||||
return _controller->setResolution(k);
|
||||
}
|
||||
|
||||
float Stars::changeLOD(float fraction, float overalloc, float realloc) {
|
||||
return float(_controller->changeLOD(fraction, overalloc, realloc));
|
||||
}
|
||||
|
||||
void Stars::render(float fovY, float aspect, float nearZ, float alpha) {
|
||||
|
||||
// determine length of screen diagonal from quadrant height and aspect ratio
|
||||
|
|
36
interface/src/Stars.h
Normal file → Executable file
36
interface/src/Stars.h
Normal file → Executable file
|
@ -21,13 +21,12 @@ class Stars {
|
|||
Stars();
|
||||
~Stars();
|
||||
|
||||
//
|
||||
// Reads input file from URL. Returns true upon success.
|
||||
//
|
||||
// The limit parameter allows to reduce the number of stars
|
||||
// that are loaded, keeping the brightest ones.
|
||||
//
|
||||
bool readInput(const char* url, const char* cacheFile = 0l, unsigned limit = 200000);
|
||||
// Generate stars from random number seed
|
||||
//
|
||||
// The numStars parameter sets the number of stars to generate.
|
||||
//
|
||||
bool generate(unsigned numStars, unsigned seed);
|
||||
|
||||
//
|
||||
// Renders the starfield from a local viewer's perspective.
|
||||
|
@ -45,27 +44,12 @@ class Stars {
|
|||
// is returned.
|
||||
//
|
||||
bool setResolution(unsigned k);
|
||||
|
||||
//
|
||||
// Allows to alter the number of stars to be rendered given a
|
||||
// factor. The least brightest ones are omitted first.
|
||||
|
||||
|
||||
//
|
||||
// The further parameters determine when GPU resources should
|
||||
// be reallocated. Its value is fractional in respect to the
|
||||
// last number of stars 'n' that caused 'n * (1+overalloc)' to
|
||||
// be allocated. When the next call to setLOD causes the total
|
||||
// number of stars that could be rendered to drop below 'n *
|
||||
// (1-realloc)' or rises above 'n * (1+realloc)' GPU resources
|
||||
// are updated. Note that all parameters must be fractions,
|
||||
// that is within the range [0;1] and that 'overalloc' must be
|
||||
// greater than or equal to 'realloc'.
|
||||
// Returns true when stars have been loaded
|
||||
//
|
||||
// The current level of detail is returned as a float in [0;1].
|
||||
//
|
||||
float changeLOD(float factor,
|
||||
float overalloc = 0.25, float realloc = 0.15);
|
||||
|
||||
bool getFileLoaded() const { return _fileLoaded; };
|
||||
bool isStarsLoaded() const { return _starsLoaded; };
|
||||
private:
|
||||
// don't copy/assign
|
||||
Stars(Stars const&); // = delete;
|
||||
|
@ -75,7 +59,7 @@ class Stars {
|
|||
|
||||
starfield::Controller* _controller;
|
||||
|
||||
bool _fileLoaded;
|
||||
bool _starsLoaded;
|
||||
};
|
||||
|
||||
|
||||
|
|
2
interface/src/starfield/CMakeLists.txt
Normal file → Executable file
2
interface/src/starfield/CMakeLists.txt
Normal file → Executable file
|
@ -4,6 +4,6 @@ project(starfield)
|
|||
# Only headers (that are facaded by the Stars.cpp file) here -
|
||||
# hence declared as custom target.
|
||||
|
||||
file(GLOB_RECURSE STARFIELD_SRCS *.h)
|
||||
file(GLOB_RECURSE STARFIELD_SRCS *.cpp *.h)
|
||||
add_custom_target("starfield" SOURCES ${STARFIELD_SRCS})
|
||||
|
||||
|
|
35
interface/src/starfield/Config.h
Normal file → Executable file
35
interface/src/starfield/Config.h
Normal file → Executable file
|
@ -9,30 +9,6 @@
|
|||
#ifndef __interface__starfield__Config__
|
||||
#define __interface__starfield__Config__
|
||||
|
||||
#ifndef __interface__Starfield_impl__
|
||||
#error "This is an implementation file - not intended for direct inclusion."
|
||||
#endif
|
||||
|
||||
//
|
||||
// Compile time configuration:
|
||||
//
|
||||
|
||||
#ifndef STARFIELD_HEMISPHERE_ONLY
|
||||
#define STARFIELD_HEMISPHERE_ONLY 0 // set to 1 for hemisphere only
|
||||
#endif
|
||||
|
||||
#ifndef STARFIELD_LOW_MEMORY
|
||||
#define STARFIELD_LOW_MEMORY 0 // set to 1 not to use 16-bit types
|
||||
#endif
|
||||
|
||||
#ifndef STARFIELD_DEBUG_CULLING
|
||||
#define STARFIELD_DEBUG_CULLING 0 // set to 1 to peek behind the scenes
|
||||
#endif
|
||||
|
||||
#ifndef STARFIELD_MULTITHREADING
|
||||
#define STARFIELD_MULTITHREADING 0
|
||||
#endif
|
||||
|
||||
//
|
||||
// Dependencies:
|
||||
//
|
||||
|
@ -49,11 +25,6 @@
|
|||
|
||||
#include <stdint.h>
|
||||
|
||||
#if STARFIELD_MULTITHREADING
|
||||
#include <mutex>
|
||||
#include <atomic>
|
||||
#endif
|
||||
|
||||
#include <new>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
|
@ -88,14 +59,8 @@ namespace starfield {
|
|||
|
||||
using namespace std;
|
||||
|
||||
|
||||
#if STARFIELD_SAVE_MEMORY
|
||||
typedef uint16_t nuint;
|
||||
typedef uint32_t wuint;
|
||||
#else
|
||||
typedef uint32_t nuint;
|
||||
typedef uint64_t wuint;
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
|
|
63
interface/src/starfield/Controller.cpp
Executable file
63
interface/src/starfield/Controller.cpp
Executable file
|
@ -0,0 +1,63 @@
|
|||
//
|
||||
// starfield/Controller.cpp
|
||||
// interface
|
||||
//
|
||||
// Created by Chris Barnard on 10/16/13
|
||||
// Portions of code based on earlier work by Tobias Schwinger.
|
||||
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#include "starfield/Controller.h"
|
||||
|
||||
using namespace starfield;
|
||||
|
||||
bool Controller::computeStars(unsigned numStars, unsigned seed) {
|
||||
timeval startTime;
|
||||
gettimeofday(&startTime, NULL);
|
||||
|
||||
Generator::computeStarPositions(_inputSequence, numStars, seed);
|
||||
|
||||
this->retile(numStars, _tileResolution);
|
||||
|
||||
qDebug("Total time to generate stars: %llu msec\n", (usecTimestampNow() - usecTimestamp(&startTime)) / 1000);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Controller::setResolution(unsigned tileResolution) {
|
||||
if (tileResolution <= 3) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (tileResolution != _tileResolution) {
|
||||
|
||||
this->retile(_numStars, tileResolution);
|
||||
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void Controller::render(float perspective, float angle, mat4 const& orientation, float alpha) {
|
||||
Renderer* renderer = _renderer;
|
||||
|
||||
if (renderer != 0l) {
|
||||
renderer->render(perspective, angle, orientation, alpha);
|
||||
}
|
||||
}
|
||||
|
||||
void Controller::retile(unsigned numStars, unsigned tileResolution) {
|
||||
Tiling tiling(tileResolution);
|
||||
VertexOrder scanner(tiling);
|
||||
radix2InplaceSort(_inputSequence.begin(), _inputSequence.end(), scanner);
|
||||
|
||||
recreateRenderer(numStars, tileResolution);
|
||||
|
||||
_tileResolution = tileResolution;
|
||||
}
|
||||
|
||||
void Controller::recreateRenderer(unsigned numStars, unsigned tileResolution) {
|
||||
delete _renderer;
|
||||
_renderer = new Renderer(_inputSequence, numStars, tileResolution);
|
||||
}
|
404
interface/src/starfield/Controller.h
Normal file → Executable file
404
interface/src/starfield/Controller.h
Normal file → Executable file
|
@ -3,52 +3,17 @@
|
|||
// interface
|
||||
//
|
||||
// Created by Tobias Schwinger on 3/29/13.
|
||||
// Modified by Chris Barnard 10/16/13.
|
||||
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef __interface__starfield__Controller__
|
||||
#define __interface__starfield__Confroller__
|
||||
|
||||
#ifndef __interface__Starfield_impl__
|
||||
#error "This is an implementation file - not intended for direct inclusion."
|
||||
#endif
|
||||
|
||||
//
|
||||
// Data pipeline
|
||||
// =============
|
||||
//
|
||||
// ->> readInput -(load)--+---- (get brightness & sort) ---> brightness LUT
|
||||
// | |
|
||||
// ->> setResolution --+ | >extractBrightnessLevels<
|
||||
// V |
|
||||
// (sort by (tile,brightness))
|
||||
// | |
|
||||
// ->> setLOD ---+ | >retile< ->> setLOD --> (just parameterize
|
||||
// V V when enough data on-GPU)
|
||||
// (filter by max-LOD brightness,
|
||||
// build tile info for rendering)
|
||||
// | |
|
||||
// V >recreateRenderer<
|
||||
// (set new renderer)/
|
||||
//
|
||||
//
|
||||
// (process), ->> entry point, ---> data flow, >internal routine<
|
||||
//
|
||||
// (member functions are ordered by data flow)
|
||||
|
||||
//
|
||||
// Still open
|
||||
// ==========
|
||||
//
|
||||
// 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
|
||||
// thread run the data pipeline for update -- rendering is wait-free
|
||||
//
|
||||
#include <time.h>
|
||||
|
||||
#include "starfield/Generator.h"
|
||||
#include "starfield/data/InputVertex.h"
|
||||
#include "starfield/data/BrightnessLevel.h"
|
||||
#include "starfield/Loader.h"
|
||||
|
||||
#include "starfield/renderer/Renderer.h"
|
||||
#include "starfield/renderer/VertexOrder.h"
|
||||
|
||||
|
@ -57,371 +22,30 @@ namespace starfield {
|
|||
class Controller {
|
||||
public:
|
||||
|
||||
Controller() :
|
||||
_tileResolution(20),
|
||||
_lodFraction(1.0),
|
||||
_lodLowWaterMark(0.8),
|
||||
_lodHighWaterMark(1.0),
|
||||
_lodOveralloc(1.2),
|
||||
_lodNalloc(0),
|
||||
_lodNRender(0),
|
||||
_lodBrightness(0),
|
||||
_lodAllocBrightness(0),
|
||||
_renderer(0l) {
|
||||
}
|
||||
Controller() : _tileResolution(20), _renderer(0l) { }
|
||||
|
||||
~Controller() {
|
||||
delete _renderer;
|
||||
}
|
||||
|
||||
#if !STARFIELD_MULTITHREADING
|
||||
#define lock
|
||||
#define _(x)
|
||||
#endif
|
||||
|
||||
bool readInput(const char* url, const char* cacheFile, unsigned limit)
|
||||
{
|
||||
InputVertices vertices;
|
||||
|
||||
if (! Loader().loadVertices(vertices, url, cacheFile, limit))
|
||||
return false;
|
||||
|
||||
BrightnessLevels brightness;
|
||||
extractBrightnessLevels(brightness, vertices);
|
||||
|
||||
// input is read, now run the entire data pipeline on the new input
|
||||
|
||||
{ lock _(_inputMutex);
|
||||
|
||||
_inputSequence.swap(vertices);
|
||||
#if STARFIELD_MULTITHREADING
|
||||
unsigned k = _tileResolution.load(memory_order_relaxed);
|
||||
#else
|
||||
unsigned k = _tileResolution;
|
||||
#endif
|
||||
size_t n, nRender;
|
||||
BrightnessLevel bMin, b;
|
||||
double rcpChange;
|
||||
|
||||
// we'll have to build a new LOD state for a new total N,
|
||||
// ideally keeping allocation size and number of vertices
|
||||
|
||||
{ lock _(_lodStateMutex);
|
||||
|
||||
size_t newLast = _inputSequence.size() - 1;
|
||||
|
||||
// reciprocal change N_old/N_new tells us how to scale
|
||||
// the fractions
|
||||
rcpChange = min(1.0, double(vertices.size()) / _inputSequence.size());
|
||||
|
||||
// initialization? use defaults / previously set values
|
||||
if (rcpChange == 0.0) {
|
||||
|
||||
rcpChange = 1.0;
|
||||
|
||||
nRender = toBufSize(_lodFraction * newLast);
|
||||
n = min(newLast, toBufSize(_lodOveralloc * nRender));
|
||||
|
||||
} else {
|
||||
|
||||
// cannot allocate or render more than we have
|
||||
n = min(newLast, _lodNalloc);
|
||||
nRender = min(newLast, _lodNRender);
|
||||
}
|
||||
|
||||
// determine new minimum brightness levels
|
||||
bMin = brightness[n];
|
||||
b = brightness[nRender];
|
||||
|
||||
// adjust n
|
||||
n = std::upper_bound(
|
||||
brightness.begin() + n - 1,
|
||||
brightness.end(),
|
||||
bMin, GreaterBrightness() ) - brightness.begin();
|
||||
}
|
||||
|
||||
// invoke next stage
|
||||
try {
|
||||
|
||||
this->retile(n, k, b, bMin);
|
||||
|
||||
} catch (...) {
|
||||
|
||||
// rollback transaction and rethrow
|
||||
vertices.swap(_inputSequence);
|
||||
throw;
|
||||
}
|
||||
|
||||
// finally publish the new LOD state
|
||||
|
||||
{ lock _(_lodStateMutex);
|
||||
|
||||
_lodBrightnessSequence.swap(brightness);
|
||||
_lodFraction *= rcpChange;
|
||||
_lodLowWaterMark *= rcpChange;
|
||||
_lodHighWaterMark *= rcpChange;
|
||||
_lodOveralloc *= rcpChange;
|
||||
_lodNalloc = n;
|
||||
_lodNRender = nRender;
|
||||
_lodAllocBrightness = bMin;
|
||||
#if STARFIELD_MULTITHREADING
|
||||
_lodBrightness.store(b, memory_order_relaxed);
|
||||
#else
|
||||
_lodBrightness = b;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool setResolution(unsigned k) {
|
||||
|
||||
if (k <= 3) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// printLog("Stars.cpp: setResolution(%d)\n", k);
|
||||
|
||||
#if STARFIELD_MULTITHREADING
|
||||
if (k != _tileResolution.load(memory_order_relaxed))
|
||||
#else
|
||||
if (k != _tileResolution)
|
||||
#endif
|
||||
{ lock _(_inputMutex);
|
||||
|
||||
unsigned n;
|
||||
BrightnessLevel b, bMin;
|
||||
|
||||
{ lock _(_lodStateMutex);
|
||||
|
||||
n = _lodNalloc;
|
||||
#if STARFIELD_MULTITHREADING
|
||||
b = _lodBrightness.load(memory_order_relaxed);
|
||||
#else
|
||||
b = _lodBrightness;
|
||||
#endif
|
||||
bMin = _lodAllocBrightness;
|
||||
}
|
||||
|
||||
this->retile(n, k, b, bMin);
|
||||
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
double changeLOD(double factor, double overalloc, double realloc) {
|
||||
|
||||
assert(overalloc >= realloc && realloc >= 0.0);
|
||||
assert(overalloc <= 1.0 && realloc <= 1.0);
|
||||
|
||||
// printLog(
|
||||
// "Stars.cpp: changeLOD(%lf, %lf, %lf)\n", factor, overalloc, realloc);
|
||||
|
||||
size_t n, nRender;
|
||||
BrightnessLevel bMin, b;
|
||||
double fraction, lwm, hwm;
|
||||
|
||||
{ lock _(_lodStateMutex);
|
||||
|
||||
// acuire a consistent copy of the current LOD state
|
||||
fraction = _lodFraction;
|
||||
lwm = _lodLowWaterMark;
|
||||
hwm = _lodHighWaterMark;
|
||||
size_t last = _lodBrightnessSequence.size() - 1;
|
||||
bool computeStars(unsigned numStars, unsigned seed);
|
||||
|
||||
// apply factor
|
||||
fraction = max(0.0, min(1.0, fraction * factor));
|
||||
bool setResolution(unsigned tileResolution);
|
||||
|
||||
// calculate allocation size and corresponding brightness
|
||||
// threshold
|
||||
double oaFract = std::min(fraction * (1.0 + overalloc), 1.0);
|
||||
n = toBufSize(oaFract * last);
|
||||
bMin = _lodBrightnessSequence[n];
|
||||
n = std::upper_bound(
|
||||
_lodBrightnessSequence.begin() + n - 1,
|
||||
_lodBrightnessSequence.end(),
|
||||
bMin, GreaterBrightness() ) - _lodBrightnessSequence.begin();
|
||||
|
||||
// also determine number of vertices to render and brightness
|
||||
nRender = toBufSize(fraction * last);
|
||||
// Note: nRender does not have to be accurate
|
||||
b = _lodBrightnessSequence[nRender];
|
||||
// this setting controls the renderer, also keep b as the
|
||||
// brightness becomes volatile as soon as the mutex is
|
||||
// released, so keep b
|
||||
#if STARFIELD_MULTITHREADING
|
||||
_lodBrightness.store(b, memory_order_relaxed);
|
||||
#else
|
||||
_lodBrightness = b;
|
||||
#endif
|
||||
|
||||
// printLog("Stars.cpp: "
|
||||
// "fraction = %lf, oaFract = %lf, n = %d, n' = %d, bMin = %d, b = %d\n",
|
||||
// fraction, oaFract, toBufSize(oaFract * last)), n, bMin, b);
|
||||
|
||||
// will not have to reallocate? set new fraction right away
|
||||
// (it is consistent with the rest of the state in this case)
|
||||
if (fraction >= _lodLowWaterMark
|
||||
&& fraction <= _lodHighWaterMark) {
|
||||
|
||||
_lodFraction = fraction;
|
||||
return fraction;
|
||||
}
|
||||
}
|
||||
|
||||
// reallocate
|
||||
|
||||
{ lock _(_inputMutex);
|
||||
|
||||
recreateRenderer(n, _tileResolution, b, bMin);
|
||||
|
||||
// printLog("Stars.cpp: LOD reallocation\n");
|
||||
|
||||
// publish new lod state
|
||||
|
||||
{ lock _(_lodStateMutex);
|
||||
|
||||
_lodNalloc = n;
|
||||
_lodNRender = nRender;
|
||||
|
||||
_lodFraction = fraction;
|
||||
_lodLowWaterMark = fraction * (1.0 - realloc);
|
||||
_lodHighWaterMark = fraction * (1.0 + realloc);
|
||||
_lodOveralloc = fraction * (1.0 + overalloc);
|
||||
_lodAllocBrightness = bMin;
|
||||
}
|
||||
}
|
||||
return fraction;
|
||||
}
|
||||
|
||||
void render(float perspective, float angle, mat4 const& orientation, float alpha) {
|
||||
|
||||
#if STARFIELD_MULTITHREADING
|
||||
// check out renderer
|
||||
Renderer* renderer = _renderer.exchange(0l);
|
||||
#else
|
||||
Renderer* renderer = _renderer;
|
||||
#endif
|
||||
|
||||
// have it render
|
||||
if (renderer != 0l) {
|
||||
#if STARFIELD_MULTITHREADING
|
||||
BrightnessLevel b = _lodBrightness.load(memory_order_relaxed);
|
||||
#else
|
||||
BrightnessLevel b = _lodBrightness;
|
||||
#endif
|
||||
renderer->render(perspective, angle, orientation, b, alpha);
|
||||
}
|
||||
|
||||
#if STARFIELD_MULTITHREADING
|
||||
// check in - or dispose if there is a new one
|
||||
Renderer* newOne = 0l;
|
||||
if (! _renderer.compare_exchange_strong(newOne, renderer)) {
|
||||
|
||||
assert(!! newOne);
|
||||
delete renderer;
|
||||
}
|
||||
#else
|
||||
# undef lock
|
||||
# undef _
|
||||
#endif
|
||||
}
|
||||
void render(float perspective, float angle, mat4 const& orientation, float alpha);
|
||||
|
||||
private:
|
||||
|
||||
void retile(size_t n, unsigned k,
|
||||
BrightnessLevel b, BrightnessLevel bMin) {
|
||||
void retile(unsigned numStars, unsigned tileResolution);
|
||||
|
||||
Tiling tiling(k);
|
||||
VertexOrder scanner(tiling);
|
||||
radix2InplaceSort(_inputSequence.begin(), _inputSequence.end(), scanner);
|
||||
void recreateRenderer(unsigned numStars, unsigned tileResolution);
|
||||
|
||||
// printLog(
|
||||
// "Stars.cpp: recreateRenderer(%d, %d, %d, %d)\n", n, k, b, bMin);
|
||||
|
||||
recreateRenderer(n, k, b, bMin);
|
||||
|
||||
_tileResolution = k;
|
||||
}
|
||||
|
||||
void recreateRenderer(size_t n, unsigned k,
|
||||
BrightnessLevel b, BrightnessLevel bMin) {
|
||||
|
||||
#if STARFIELD_MULTITHREADING
|
||||
delete _renderer.exchange(new Renderer(_inputSequence, n, k, b, bMin) );
|
||||
#else
|
||||
delete _renderer;
|
||||
_renderer = new Renderer(_inputSequence, n, k, b, bMin);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
static inline size_t toBufSize(double f) {
|
||||
return size_t(floor(f + 0.5f));
|
||||
}
|
||||
|
||||
struct BrightnessSortScanner : Radix2IntegerScanner<BrightnessLevel> {
|
||||
|
||||
typedef Radix2IntegerScanner<BrightnessLevel> base;
|
||||
|
||||
BrightnessSortScanner() : base(BrightnessBits) { }
|
||||
|
||||
bool bit(BrightnessLevel const& k, state_type& s) {
|
||||
|
||||
// bit is inverted to achieve descending order
|
||||
return ! base::bit(k,s);
|
||||
}
|
||||
};
|
||||
|
||||
static 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());
|
||||
}
|
||||
|
||||
InputVertices _inputSequence;
|
||||
#if STARFIELD_MULTITHREADING
|
||||
mutex _inputMutex;
|
||||
atomic<unsigned> _tileResolution;
|
||||
|
||||
mutex _lodStateMutex;
|
||||
#else
|
||||
unsigned _tileResolution;
|
||||
#endif
|
||||
double _lodFraction;
|
||||
double _lodLowWaterMark;
|
||||
double _lodHighWaterMark;
|
||||
double _lodOveralloc;
|
||||
size_t _lodNalloc;
|
||||
size_t _lodNRender;
|
||||
BrightnessLevels _lodBrightnessSequence;
|
||||
|
||||
#if STARFIELD_MULTITHREADING
|
||||
atomic<BrightnessLevel> _lodBrightness;
|
||||
BrightnessLevel _lodAllocBrightness;
|
||||
|
||||
atomic<Renderer*> _renderer;
|
||||
|
||||
typedef lock_guard<mutex> lock;
|
||||
#else
|
||||
BrightnessLevel _lodBrightness;
|
||||
BrightnessLevel _lodAllocBrightness;
|
||||
|
||||
Renderer* _renderer;
|
||||
|
||||
#undef lock
|
||||
#undef _
|
||||
#endif
|
||||
InputVertices _inputSequence;
|
||||
unsigned _tileResolution;
|
||||
unsigned _numStars;
|
||||
Renderer* _renderer;
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
|
|
53
interface/src/starfield/Generator.cpp
Normal file
53
interface/src/starfield/Generator.cpp
Normal file
|
@ -0,0 +1,53 @@
|
|||
//
|
||||
// starfield/Generator.cpp
|
||||
// interface
|
||||
//
|
||||
// Created by Chris Barnard on 10/13/13.
|
||||
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#include "starfield/Generator.h"
|
||||
|
||||
using namespace starfield;
|
||||
|
||||
const float Generator::STAR_COLORIZATION = 0.1;
|
||||
|
||||
void Generator::computeStarPositions(InputVertices& destination, unsigned limit, unsigned seed) {
|
||||
InputVertices* vertices = & destination;
|
||||
//_limit = limit;
|
||||
|
||||
timeval startTime;
|
||||
gettimeofday(&startTime, NULL);
|
||||
|
||||
srand(seed);
|
||||
|
||||
vertices->clear();
|
||||
vertices->reserve(limit);
|
||||
|
||||
const unsigned NUM_DEGREES = 360;
|
||||
|
||||
|
||||
for(int star = 0; star < limit; ++star) {
|
||||
float azimuth, altitude;
|
||||
azimuth = ((float)rand() / (float) RAND_MAX) * NUM_DEGREES;
|
||||
altitude = (((float)rand() / (float) RAND_MAX) * NUM_DEGREES / 2) - NUM_DEGREES / 4;
|
||||
|
||||
vertices->push_back(InputVertex(azimuth, altitude, computeStarColor(STAR_COLORIZATION)));
|
||||
}
|
||||
|
||||
qDebug("Took %llu msec to generate stars.\n", (usecTimestampNow() - usecTimestamp(&startTime)) / 1000);
|
||||
}
|
||||
|
||||
// computeStarColor
|
||||
// - Generate a star color.
|
||||
//
|
||||
// colorization can be a value between 0 and 1 specifying how colorful the resulting star color is.
|
||||
//
|
||||
// 0 = completely black & white
|
||||
// 1 = very colorful
|
||||
unsigned Generator::computeStarColor(float colorization) {
|
||||
unsigned char red = rand() % 256;
|
||||
unsigned char green = round((red * (1 - colorization)) + ((rand() % 256) * colorization));
|
||||
unsigned char blue = round((red * (1 - colorization)) + ((rand() % 256) * colorization));
|
||||
return red | green << 8 | blue << 16;
|
||||
}
|
37
interface/src/starfield/Generator.h
Normal file
37
interface/src/starfield/Generator.h
Normal file
|
@ -0,0 +1,37 @@
|
|||
//
|
||||
// starfield/Generator.h
|
||||
// interface
|
||||
//
|
||||
// Created by Chris Barnard on 10/13/13.
|
||||
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef __interface__starfield__Generator__
|
||||
#define __interface__starfield__Generator__
|
||||
|
||||
#include <locale.h>
|
||||
#include <time.h>
|
||||
|
||||
#include "Config.h"
|
||||
#include "SharedUtil.h"
|
||||
|
||||
#include "starfield/data/InputVertex.h"
|
||||
|
||||
namespace starfield {
|
||||
|
||||
class Generator {
|
||||
|
||||
public:
|
||||
Generator() {}
|
||||
~Generator() {}
|
||||
|
||||
static void computeStarPositions(InputVertices& destination, unsigned limit, unsigned seed);
|
||||
static unsigned computeStarColor(float colorization);
|
||||
|
||||
private:
|
||||
static const float STAR_COLORIZATION;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
#endif
|
|
@ -1,64 +0,0 @@
|
|||
//
|
||||
// starfield/data/BrightnessLevel.h
|
||||
// interface
|
||||
//
|
||||
// Created by Tobias Schwinger on 3/29/13.
|
||||
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef __interface__starfield__data__BrightnessLevel__
|
||||
#define __interface__starfield__data__BrightnessLevel__
|
||||
|
||||
#ifndef __interface__Starfield_impl__
|
||||
#error "This is an implementation file - not intended for direct inclusion."
|
||||
#endif
|
||||
|
||||
#include "starfield/Config.h"
|
||||
#include "starfield/data/InputVertex.h"
|
||||
#include "starfield/data/GpuVertex.h"
|
||||
|
||||
namespace starfield {
|
||||
|
||||
typedef nuint BrightnessLevel;
|
||||
|
||||
|
||||
#if STARFIELD_SAVE_MEMORY
|
||||
const unsigned BrightnessBits = 16u;
|
||||
#else
|
||||
const unsigned BrightnessBits = 18u;
|
||||
#endif
|
||||
const BrightnessLevel BrightnessMask = (1u << (BrightnessBits)) - 1u;
|
||||
|
||||
typedef std::vector<BrightnessLevel> BrightnessLevels;
|
||||
|
||||
BrightnessLevel getBrightness(unsigned c) {
|
||||
|
||||
unsigned r = (c >> 16) & 0xff;
|
||||
unsigned g = (c >> 8) & 0xff;
|
||||
unsigned b = c & 0xff;
|
||||
#if STARFIELD_SAVE_MEMORY
|
||||
return BrightnessLevel((r*r+g*g+b*b) >> 2);
|
||||
#else
|
||||
return BrightnessLevel(r*r+g*g+b*b);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
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());;
|
||||
}
|
||||
bool operator()(BrightnessLevel lhs, BrightnessLevel rhs) const {
|
||||
return lhs > rhs;
|
||||
}
|
||||
};
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
#endif
|
||||
|
23
interface/src/starfield/data/GpuVertex.cpp
Executable file
23
interface/src/starfield/data/GpuVertex.cpp
Executable file
|
@ -0,0 +1,23 @@
|
|||
//
|
||||
// starfield/data/GpuVertex.cpp
|
||||
// interface
|
||||
//
|
||||
// Created by Chris Barnard on 10/17/13.
|
||||
// Based on code by Tobias Schwinger on 3/29/13.
|
||||
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
#include "starfield/data/GpuVertex.h"
|
||||
#include "starfield/data/InputVertex.h"
|
||||
|
||||
using namespace starfield;
|
||||
|
||||
GpuVertex::GpuVertex(InputVertex const& inputVertex) {
|
||||
_color = inputVertex.getColor();
|
||||
float azimuth = inputVertex.getAzimuth();
|
||||
float altitude = inputVertex.getAltitude();
|
||||
|
||||
// compute altitude/azimuth into X/Y/Z point on a sphere
|
||||
_valX = sin(azimuth) * cos(altitude);
|
||||
_valY = sin(altitude);
|
||||
_valZ = -cos(azimuth) * cos(altitude);
|
||||
}
|
32
interface/src/starfield/data/GpuVertex.h
Normal file → Executable file
32
interface/src/starfield/data/GpuVertex.h
Normal file → Executable file
|
@ -3,16 +3,13 @@
|
|||
// interface
|
||||
//
|
||||
// Created by Tobias Schwinger on 3/29/13.
|
||||
// Modified 10/17/13 Chris Barnard.
|
||||
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef __interface__starfield__data__GpuVertex__
|
||||
#define __interface__starfield__data__GpuVertex__
|
||||
|
||||
#ifndef __interface__Starfield_impl__
|
||||
#error "This is an implementation file - not intended for direct inclusion."
|
||||
#endif
|
||||
|
||||
#include "starfield/data/InputVertex.h"
|
||||
|
||||
namespace starfield {
|
||||
|
@ -21,33 +18,18 @@ namespace starfield {
|
|||
public:
|
||||
GpuVertex() { }
|
||||
|
||||
GpuVertex(InputVertex const& in) {
|
||||
|
||||
_color = in.getColor();
|
||||
float azi = in.getAzimuth();
|
||||
float alt = in.getAltitude();
|
||||
|
||||
// ground vector in x/z plane...
|
||||
float gx = sin(azi);
|
||||
float gz = -cos(azi);
|
||||
|
||||
// ...elevated in y direction by altitude
|
||||
float exz = cos(alt);
|
||||
_valX = gx * exz;
|
||||
_valY = sin(alt);
|
||||
_valZ = gz * exz;
|
||||
}
|
||||
GpuVertex(InputVertex const& inputVertex);
|
||||
|
||||
unsigned getColor() const { return _color; }
|
||||
|
||||
private:
|
||||
unsigned _color;
|
||||
float _valX;
|
||||
float _valY;
|
||||
float _valZ;
|
||||
unsigned _color;
|
||||
float _valX;
|
||||
float _valY;
|
||||
float _valZ;
|
||||
};
|
||||
|
||||
} // anonymous namespace
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
|
24
interface/src/starfield/data/InputVertex.cpp
Executable file
24
interface/src/starfield/data/InputVertex.cpp
Executable file
|
@ -0,0 +1,24 @@
|
|||
//
|
||||
// starfield/data/InputVertex.cpp
|
||||
// interface
|
||||
//
|
||||
// Created by Chris Barnard on 10/17.13.
|
||||
// Based on code by Tobias Schwinger 3/29/13.
|
||||
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#include "starfield/data/InputVertex.h"
|
||||
|
||||
using namespace starfield;
|
||||
|
||||
InputVertex::InputVertex(float azimuth, float altitude, unsigned color) {
|
||||
_color = color | 0xff000000u;
|
||||
|
||||
azimuth = angleConvert<Degrees,Radians>(azimuth);
|
||||
altitude = angleConvert<Degrees,Radians>(altitude);
|
||||
|
||||
angleHorizontalPolar<Radians>(azimuth, altitude);
|
||||
|
||||
_azimuth = azimuth;
|
||||
_altitude = altitude;
|
||||
}
|
27
interface/src/starfield/data/InputVertex.h
Normal file → Executable file
27
interface/src/starfield/data/InputVertex.h
Normal file → Executable file
|
@ -3,16 +3,13 @@
|
|||
// interface
|
||||
//
|
||||
// Created by Tobias Schwinger on 3/29/13.
|
||||
// Modified by Chris Barnard 10/17/13.
|
||||
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef __interface__starfield__data__InputVertex__
|
||||
#define __interface__starfield__data__InputVertex__
|
||||
|
||||
#ifndef __interface__Starfield_impl__
|
||||
#error "This is an implementation file - not intended for direct inclusion."
|
||||
#endif
|
||||
|
||||
#include "starfield/Config.h"
|
||||
|
||||
namespace starfield {
|
||||
|
@ -20,33 +17,21 @@ namespace starfield {
|
|||
class InputVertex {
|
||||
public:
|
||||
|
||||
InputVertex(float azimuth, float altitude, unsigned color) {
|
||||
|
||||
_color = ((color >> 16) & 0xffu) | (color & 0xff00u) |
|
||||
((color << 16) & 0xff0000u) | 0xff000000u;
|
||||
|
||||
azimuth = angleConvert<Degrees,Radians>(azimuth);
|
||||
altitude = angleConvert<Degrees,Radians>(altitude);
|
||||
|
||||
angleHorizontalPolar<Radians>(azimuth, altitude);
|
||||
|
||||
_azimuth = azimuth;
|
||||
_altitude = altitude;
|
||||
}
|
||||
InputVertex(float azimuth, float altitude, unsigned color);
|
||||
|
||||
float getAzimuth() const { return _azimuth; }
|
||||
float getAltitude() const { return _altitude; }
|
||||
unsigned getColor() const { return _color; }
|
||||
|
||||
private:
|
||||
unsigned _color;
|
||||
float _azimuth;
|
||||
float _altitude;
|
||||
unsigned _color;
|
||||
float _azimuth;
|
||||
float _altitude;
|
||||
};
|
||||
|
||||
typedef std::vector<InputVertex> InputVertices;
|
||||
|
||||
} // anonymous namespace
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
|
18
interface/src/starfield/data/Tile.h
Normal file → Executable file
18
interface/src/starfield/data/Tile.h
Normal file → Executable file
|
@ -9,30 +9,22 @@
|
|||
#ifndef __interface__starfield__data__Tile__
|
||||
#define __interface__starfield__data__Tile__
|
||||
|
||||
#ifndef __interface__Starfield_impl__
|
||||
#error "This is an implementation file - not intended for direct inclusion."
|
||||
#endif
|
||||
|
||||
#include "starfield/Config.h"
|
||||
#include "starfield/data/BrightnessLevel.h"
|
||||
|
||||
namespace starfield {
|
||||
|
||||
struct Tile {
|
||||
|
||||
nuint offset;
|
||||
nuint count;
|
||||
BrightnessLevel lod;
|
||||
nuint flags;
|
||||
nuint offset;
|
||||
nuint count;
|
||||
nuint flags;
|
||||
|
||||
// flags
|
||||
static uint16_t const checked = 1;
|
||||
static uint16_t const visited = 2;
|
||||
static uint16_t const render = 4;
|
||||
};
|
||||
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
|
311
interface/src/starfield/renderer/Renderer.cpp
Executable file
311
interface/src/starfield/renderer/Renderer.cpp
Executable file
|
@ -0,0 +1,311 @@
|
|||
//
|
||||
// starfield/renderer/Renderer.cpp
|
||||
// interface
|
||||
//
|
||||
// Created by Chris Barnard on 10/17/13.
|
||||
// Based on earlier work by Tobias Schwinger 3/22/13.
|
||||
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#include "starfield/renderer/Renderer.h"
|
||||
|
||||
using namespace starfield;
|
||||
|
||||
Renderer::Renderer(InputVertices const& stars, unsigned numStars, unsigned tileResolution) : _dataArray(0l),
|
||||
_tileArray(0l), _tiling(tileResolution) {
|
||||
this->glAlloc();
|
||||
|
||||
Tiling tiling(tileResolution);
|
||||
size_t numTiles = tiling.getTileCount();
|
||||
|
||||
// REVISIT: batch arrays are probably oversized, but - hey - they
|
||||
// are not very large (unless for insane tiling) and we're better
|
||||
// off safe than sorry
|
||||
_dataArray = new GpuVertex[numStars];
|
||||
_tileArray = new Tile[numTiles + 1];
|
||||
_batchOffs = new GLint[numTiles * 2];
|
||||
_batchCountArray = new GLsizei[numTiles * 2];
|
||||
|
||||
prepareVertexData(stars, numStars, tiling);
|
||||
|
||||
this->glUpload(numStars);
|
||||
}
|
||||
|
||||
Renderer::~Renderer() {
|
||||
delete[] _dataArray;
|
||||
delete[] _tileArray;
|
||||
delete[] _batchCountArray;
|
||||
delete[] _batchOffs;
|
||||
|
||||
this->glFree();
|
||||
}
|
||||
|
||||
void Renderer::render(float perspective, float aspect, mat4 const& orientation, float alpha) {
|
||||
float halfPersp = perspective * 0.5f;
|
||||
|
||||
// cancel all translation
|
||||
mat4 matrix = orientation;
|
||||
matrix[3][0] = 0.0f;
|
||||
matrix[3][1] = 0.0f;
|
||||
matrix[3][2] = 0.0f;
|
||||
|
||||
// extract local z vector
|
||||
vec3 ahead = vec3(matrix[2]);
|
||||
|
||||
float azimuth = atan2(ahead.x,-ahead.z) + Radians::pi();
|
||||
float altitude = atan2(-ahead.y, hypotf(ahead.x, ahead.z));
|
||||
angleHorizontalPolar<Radians>(azimuth, altitude);
|
||||
float const eps = 0.002f;
|
||||
altitude = glm::clamp(altitude, -Radians::halfPi() + eps, Radians::halfPi() - eps);
|
||||
|
||||
matrix = glm::affineInverse(matrix);
|
||||
|
||||
this->_outIndexPos = (unsigned*) _batchOffs;
|
||||
this->_wRowVec = -vec3(row(matrix, 2));
|
||||
this->_halfPerspectiveAngle = halfPersp;
|
||||
|
||||
TileSelection::Cursor cursor;
|
||||
cursor.current = _tileArray + _tiling.getTileIndex(azimuth, altitude);
|
||||
cursor.firstInRow = _tileArray + _tiling.getTileIndex(0.0f, altitude);
|
||||
|
||||
floodFill(cursor, TileSelection(*this, _tileArray, _tileArray + _tiling.getTileCount(), (TileSelection::Cursor*) _batchCountArray));
|
||||
|
||||
this->glBatch(glm::value_ptr(matrix), prepareBatch((unsigned*) _batchOffs, _outIndexPos), alpha);
|
||||
}
|
||||
|
||||
// renderer construction
|
||||
|
||||
void Renderer::prepareVertexData(InputVertices const& vertices, unsigned numStars, Tiling const& tiling) {
|
||||
|
||||
size_t nTiles = tiling.getTileCount();
|
||||
size_t vertexIndex = 0u, currTileIndex = 0u, count_active = 0u;
|
||||
|
||||
_tileArray[0].offset = 0u;
|
||||
_tileArray[0].flags = 0u;
|
||||
|
||||
for (InputVertices::const_iterator i = vertices.begin(), e = vertices.end(); i != e; ++i) {
|
||||
size_t tileIndex = tiling.getTileIndex(i->getAzimuth(), i->getAltitude());
|
||||
assert(tileIndex >= currTileIndex);
|
||||
|
||||
// moved on to another tile? -> flush
|
||||
if (tileIndex != currTileIndex) {
|
||||
|
||||
Tile* tile = _tileArray + currTileIndex;
|
||||
Tile* lastTile = _tileArray + tileIndex;
|
||||
|
||||
// set count of active vertices (upcoming lod)
|
||||
tile->count = count_active;
|
||||
// generate skipped, empty tiles
|
||||
for(size_t offset = vertexIndex; ++tile != lastTile ;) {
|
||||
tile->offset = offset, tile->count = 0u, tile->flags = 0u;
|
||||
}
|
||||
|
||||
// initialize next (as far as possible here)
|
||||
lastTile->offset = vertexIndex;
|
||||
lastTile->flags = 0u;
|
||||
|
||||
currTileIndex = tileIndex;
|
||||
count_active = 0u;
|
||||
}
|
||||
|
||||
++count_active;
|
||||
|
||||
// write converted vertex
|
||||
_dataArray[vertexIndex++] = *i;
|
||||
}
|
||||
assert(vertexIndex == numStars);
|
||||
|
||||
// flush last tile (see above)
|
||||
Tile* tile = _tileArray + currTileIndex;
|
||||
tile->count = count_active;
|
||||
for (Tile* e = _tileArray + nTiles + 1; ++tile != e;) {
|
||||
tile->offset = vertexIndex, tile->count = 0u, tile->flags = 0;
|
||||
}
|
||||
}
|
||||
|
||||
bool Renderer::visitTile(Tile* tile) {
|
||||
unsigned index = tile - _tileArray;
|
||||
*_outIndexPos++ = index;
|
||||
|
||||
return isTileVisible(index);
|
||||
}
|
||||
|
||||
bool Renderer::isTileVisible(unsigned index) {
|
||||
|
||||
float slice = _tiling.getSliceAngle();
|
||||
float halfSlice = 0.5f * slice;
|
||||
unsigned stride = _tiling.getAzimuthalTiles();
|
||||
float azimuth = (index % stride) * slice;
|
||||
float altitude = (index / stride) * slice - Radians::halfPi();
|
||||
float groundX = sin(azimuth);
|
||||
float groundZ = -cos(azimuth);
|
||||
float elevation = cos(altitude);
|
||||
vec3 tileCenter = vec3(groundX * elevation, sin(altitude), groundZ * elevation);
|
||||
float w = dot(_wRowVec, tileCenter);
|
||||
|
||||
float daz = halfSlice * cos(std::max(0.0f, abs(altitude) - halfSlice));
|
||||
float dal = halfSlice;
|
||||
float adjustedNear = cos(_halfPerspectiveAngle + sqrt(daz * daz + dal * dal));
|
||||
|
||||
return w >= adjustedNear;
|
||||
}
|
||||
|
||||
unsigned Renderer::prepareBatch(unsigned const* indices, unsigned const* indicesEnd) {
|
||||
unsigned nRanges = 0u;
|
||||
GLint* offs = _batchOffs;
|
||||
GLsizei* count = _batchCountArray;
|
||||
|
||||
for (unsigned* i = (unsigned*) _batchOffs; i != indicesEnd; ++i) {
|
||||
Tile* t = _tileArray + *i;
|
||||
if ((t->flags & Tile::render) > 0u && t->count > 0u) {
|
||||
*offs++ = t->offset;
|
||||
*count++ = t->count;
|
||||
++nRanges;
|
||||
}
|
||||
t->flags = 0;
|
||||
}
|
||||
return nRanges;
|
||||
}
|
||||
|
||||
// GL API handling
|
||||
|
||||
void Renderer::glAlloc() {
|
||||
GLchar const* const VERTEX_SHADER =
|
||||
"#version 120\n"
|
||||
"uniform float alpha;\n"
|
||||
"void main(void) {\n"
|
||||
" vec3 c = gl_Color.rgb * 1.22;\n"
|
||||
" float s = min(max(tan((c.r + c.g + c.b) / 3), 1.0), 3.0);\n"
|
||||
" gl_Position = ftransform();\n"
|
||||
" gl_FrontColor= gl_Color * alpha * 1.5;\n"
|
||||
" gl_PointSize = s;\n"
|
||||
"}\n";
|
||||
|
||||
_program.addShaderFromSourceCode(QGLShader::Vertex, VERTEX_SHADER);
|
||||
|
||||
GLchar const* const FRAGMENT_SHADER =
|
||||
"#version 120\n"
|
||||
"void main(void) {\n"
|
||||
" gl_FragColor = gl_Color;\n"
|
||||
"}\n";
|
||||
|
||||
_program.addShaderFromSourceCode(QGLShader::Fragment, FRAGMENT_SHADER);
|
||||
_program.link();
|
||||
_alphaLocationHandle = _program.uniformLocation("alpha");
|
||||
|
||||
glGenBuffersARB(1, & _vertexArrayHandle);
|
||||
}
|
||||
|
||||
void Renderer::glFree() {
|
||||
glDeleteBuffersARB(1, & _vertexArrayHandle);
|
||||
}
|
||||
|
||||
void Renderer::glUpload(GLsizei numStars) {
|
||||
glBindBufferARB(GL_ARRAY_BUFFER, _vertexArrayHandle);
|
||||
glBufferData(GL_ARRAY_BUFFER, numStars * sizeof(GpuVertex), _dataArray, GL_STATIC_DRAW);
|
||||
glBindBufferARB(GL_ARRAY_BUFFER, 0);
|
||||
}
|
||||
|
||||
void Renderer::glBatch(GLfloat const* matrix, GLsizei n_ranges, float alpha) {
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
glDisable(GL_LIGHTING);
|
||||
|
||||
// setup modelview matrix
|
||||
glPushMatrix();
|
||||
glLoadMatrixf(matrix);
|
||||
|
||||
// set point size and smoothing + shader control
|
||||
glPointSize(1.0f);
|
||||
glEnable(GL_POINT_SMOOTH);
|
||||
glHint(GL_POINT_SMOOTH_HINT, GL_NICEST);
|
||||
glEnable(GL_VERTEX_PROGRAM_POINT_SIZE);
|
||||
|
||||
// select shader and vertex array
|
||||
_program.bind();
|
||||
_program.setUniformValue(_alphaLocationHandle, alpha);
|
||||
glBindBufferARB(GL_ARRAY_BUFFER, _vertexArrayHandle);
|
||||
glInterleavedArrays(GL_C4UB_V3F, sizeof(GpuVertex), 0l);
|
||||
|
||||
// render
|
||||
glMultiDrawArrays(GL_POINTS, _batchOffs, _batchCountArray, n_ranges);
|
||||
|
||||
// restore state
|
||||
glBindBufferARB(GL_ARRAY_BUFFER, 0);
|
||||
_program.release();
|
||||
glDisable(GL_VERTEX_PROGRAM_POINT_SIZE);
|
||||
glDisable(GL_POINT_SMOOTH);
|
||||
glDisableClientState(GL_VERTEX_ARRAY);
|
||||
glDisableClientState(GL_COLOR_ARRAY);
|
||||
|
||||
glPopMatrix();
|
||||
}
|
||||
|
||||
// flood fill strategy
|
||||
|
||||
bool Renderer::TileSelection::select(Renderer::TileSelection::Cursor const& cursor) {
|
||||
Tile* tile = cursor.current;
|
||||
|
||||
if (tile < _tileArray || tile >= _tilesEnd || !! (tile->flags & Tile::checked)) {
|
||||
// out of bounds or been here already
|
||||
return false;
|
||||
}
|
||||
|
||||
// will check now and never again
|
||||
tile->flags |= Tile::checked;
|
||||
if (_rendererRef.visitTile(tile)) {
|
||||
// good one -> remember (for batching) and propagate
|
||||
tile->flags |= Tile::render;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Renderer::TileSelection::process(Renderer::TileSelection::Cursor const& cursor) {
|
||||
Tile* tile = cursor.current;
|
||||
|
||||
if (! (tile->flags & Tile::visited)) {
|
||||
tile->flags |= Tile::visited;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void Renderer::TileSelection::right(Renderer::TileSelection::Cursor& cursor) const {
|
||||
cursor.current += 1;
|
||||
if (cursor.current == cursor.firstInRow + _rendererRef._tiling.getAzimuthalTiles()) {
|
||||
cursor.current = cursor.firstInRow;
|
||||
}
|
||||
}
|
||||
|
||||
void Renderer::TileSelection::left(Renderer::TileSelection::Cursor& cursor) const {
|
||||
if (cursor.current == cursor.firstInRow) {
|
||||
cursor.current = cursor.firstInRow + _rendererRef._tiling.getAzimuthalTiles();
|
||||
}
|
||||
cursor.current -= 1;
|
||||
}
|
||||
|
||||
void Renderer::TileSelection::up(Renderer::TileSelection::Cursor& cursor) const {
|
||||
unsigned numTiles = _rendererRef._tiling.getAzimuthalTiles();
|
||||
cursor.current += numTiles;
|
||||
cursor.firstInRow += numTiles;
|
||||
}
|
||||
|
||||
void Renderer::TileSelection::down(Renderer::TileSelection::Cursor& cursor) const {
|
||||
unsigned numTiles = _rendererRef._tiling.getAzimuthalTiles();
|
||||
cursor.current -= numTiles;
|
||||
cursor.firstInRow -= numTiles;
|
||||
}
|
||||
|
||||
void Renderer::TileSelection::defer(Renderer::TileSelection::Cursor const& cursor) {
|
||||
*_stackPos++ = cursor;
|
||||
}
|
||||
|
||||
bool Renderer::TileSelection::deferred(Renderer::TileSelection::Cursor& cursor) {
|
||||
if (_stackPos != _stackArray) {
|
||||
cursor = *--_stackPos;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
506
interface/src/starfield/renderer/Renderer.h
Normal file → Executable file
506
interface/src/starfield/renderer/Renderer.h
Normal file → Executable file
|
@ -3,24 +3,18 @@
|
|||
// interface
|
||||
//
|
||||
// Created by Tobias Schwinger on 3/22/13.
|
||||
// Modified 10/17/13 Chris Barnard.
|
||||
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef __interface__starfield__renderer__Renderer__
|
||||
#define __interface__starfield__renderer__Renderer__
|
||||
|
||||
#ifndef __interface__Starfield_impl__
|
||||
#error "This is an implementation file - not intended for direct inclusion."
|
||||
#endif
|
||||
|
||||
#include "starfield/Config.h"
|
||||
#include "starfield/data/InputVertex.h"
|
||||
#include "starfield/data/BrightnessLevel.h"
|
||||
|
||||
#include "starfield/data/Tile.h"
|
||||
#include "starfield/data/GpuVertex.h"
|
||||
|
||||
#include "Tiling.h"
|
||||
#include "starfield/renderer/Tiling.h"
|
||||
|
||||
//
|
||||
// FOV culling
|
||||
|
@ -66,174 +60,14 @@ namespace starfield {
|
|||
class Renderer {
|
||||
public:
|
||||
|
||||
Renderer(InputVertices const& src,
|
||||
size_t n,
|
||||
unsigned k,
|
||||
BrightnessLevel b,
|
||||
BrightnessLevel bMin) :
|
||||
|
||||
_dataArray(0l),
|
||||
_tileArray(0l),
|
||||
_tiling(k) {
|
||||
|
||||
this->glAlloc();
|
||||
|
||||
Tiling tiling(k);
|
||||
size_t nTiles = tiling.getTileCount();
|
||||
|
||||
// REVISIT: could coalesce allocation for faster rebuild
|
||||
// REVISIT: batch arrays are probably oversized, but - hey - they
|
||||
// are not very large (unless for insane tiling) and we're better
|
||||
// off safe than sorry
|
||||
_dataArray = new GpuVertex[n];
|
||||
_tileArray = new Tile[nTiles + 1];
|
||||
_batchOffs = new GLint[nTiles * 2];
|
||||
_batchCountArray = new GLsizei[nTiles * 2];
|
||||
|
||||
prepareVertexData(src, n, tiling, b, bMin);
|
||||
|
||||
this->glUpload(n);
|
||||
}
|
||||
|
||||
~Renderer() {
|
||||
|
||||
delete[] _dataArray;
|
||||
delete[] _tileArray;
|
||||
delete[] _batchCountArray;
|
||||
delete[] _batchOffs;
|
||||
|
||||
this->glFree();
|
||||
}
|
||||
|
||||
void render(float perspective,
|
||||
float aspect,
|
||||
mat4 const& orientation,
|
||||
BrightnessLevel minBright,
|
||||
float alpha) {
|
||||
|
||||
// printLog("
|
||||
// Stars.cpp: rendering at minimal brightness %d\n", minBright);
|
||||
|
||||
float halfPersp = perspective * 0.5f;
|
||||
|
||||
// cancel all translation
|
||||
mat4 matrix = orientation;
|
||||
matrix[3][0] = 0.0f;
|
||||
matrix[3][1] = 0.0f;
|
||||
matrix[3][2] = 0.0f;
|
||||
|
||||
// extract local z vector
|
||||
vec3 ahead = vec3(matrix[2]);
|
||||
|
||||
float azimuth = atan2(ahead.x,-ahead.z) + Radians::pi();
|
||||
float altitude = atan2(-ahead.y, hypotf(ahead.x, ahead.z));
|
||||
angleHorizontalPolar<Radians>(azimuth, altitude);
|
||||
float const eps = 0.002f;
|
||||
altitude = glm::clamp(altitude,
|
||||
-Radians::halfPi() + eps, Radians::halfPi() - eps);
|
||||
#if STARFIELD_HEMISPHERE_ONLY
|
||||
altitude = std::max(0.0f, altitude);
|
||||
#endif
|
||||
|
||||
#if STARFIELD_DEBUG_CULLING
|
||||
mat4 matrix_debug = glm::translate(vec3(0.0f, 0.0f, -4.0f)) *
|
||||
glm::affineInverse(matrix);
|
||||
#endif
|
||||
|
||||
matrix = glm::affineInverse(matrix);
|
||||
|
||||
this->_outIndexPos = (unsigned*) _batchOffs;
|
||||
this->_wRowVec = -vec3(row(matrix, 2));
|
||||
this->_halfPerspectiveAngle = halfPersp;
|
||||
this->_minBright = minBright;
|
||||
|
||||
TileSelection::Cursor cursor;
|
||||
cursor.current = _tileArray + _tiling.getTileIndex(azimuth, altitude);
|
||||
cursor.firstInRow = _tileArray + _tiling.getTileIndex(0.0f, altitude);
|
||||
|
||||
floodFill(cursor, TileSelection(*this, _tileArray, _tileArray + _tiling.getTileCount(),
|
||||
(TileSelection::Cursor*) _batchCountArray));
|
||||
|
||||
#if STARFIELD_DEBUG_CULLING
|
||||
# define matrix matrix_debug
|
||||
#endif
|
||||
this->glBatch(glm::value_ptr(matrix), prepareBatch(
|
||||
(unsigned*) _batchOffs, _outIndexPos), alpha);
|
||||
|
||||
#if STARFIELD_DEBUG_CULLING
|
||||
# undef matrix
|
||||
#endif
|
||||
}
|
||||
|
||||
Renderer(InputVertices const& src, unsigned numStars, unsigned tileResolution);
|
||||
~Renderer();
|
||||
void render(float perspective, float aspect, mat4 const& orientation, float alpha);
|
||||
|
||||
private:
|
||||
// renderer construction
|
||||
|
||||
void prepareVertexData(InputVertices const& src,
|
||||
size_t n, // <-- at bMin and brighter
|
||||
Tiling const& tiling,
|
||||
BrightnessLevel b,
|
||||
BrightnessLevel bMin) {
|
||||
|
||||
size_t nTiles = tiling.getTileCount();
|
||||
size_t vertexIndex = 0u, currTileIndex = 0u, count_active = 0u;
|
||||
|
||||
_tileArray[0].offset = 0u;
|
||||
_tileArray[0].lod = b;
|
||||
_tileArray[0].flags = 0u;
|
||||
|
||||
for (InputVertices::const_iterator i =
|
||||
src.begin(), e = src.end(); i != e; ++i) {
|
||||
|
||||
BrightnessLevel bv = getBrightness(i->getColor());
|
||||
// filter by alloc brightness
|
||||
if (bv >= bMin) {
|
||||
|
||||
size_t tileIndex = tiling.getTileIndex(
|
||||
i->getAzimuth(), i->getAltitude());
|
||||
|
||||
assert(tileIndex >= currTileIndex);
|
||||
|
||||
// moved on to another tile? -> flush
|
||||
if (tileIndex != currTileIndex) {
|
||||
|
||||
Tile* t = _tileArray + currTileIndex;
|
||||
Tile* tLast = _tileArray + tileIndex;
|
||||
|
||||
// set count of active vertices (upcoming lod)
|
||||
t->count = count_active;
|
||||
// generate skipped, empty tiles
|
||||
for(size_t offs = vertexIndex; ++t != tLast ;) {
|
||||
t->offset = offs, t->count = 0u,
|
||||
t->lod = b, t->flags = 0u;
|
||||
}
|
||||
|
||||
// initialize next (as far as possible here)
|
||||
tLast->offset = vertexIndex;
|
||||
tLast->lod = b;
|
||||
tLast->flags = 0u;
|
||||
|
||||
currTileIndex = tileIndex;
|
||||
count_active = 0u;
|
||||
}
|
||||
|
||||
if (bv >= b)
|
||||
++count_active;
|
||||
|
||||
// printLog("Stars.cpp: Vertex %d on tile #%d\n", vertexIndex, tileIndex);
|
||||
|
||||
// write converted vertex
|
||||
_dataArray[vertexIndex++] = *i;
|
||||
}
|
||||
}
|
||||
assert(vertexIndex == n);
|
||||
// flush last tile (see above)
|
||||
Tile* t = _tileArray + currTileIndex;
|
||||
t->count = count_active;
|
||||
for (Tile* e = _tileArray + nTiles + 1; ++t != e;) {
|
||||
t->offset = vertexIndex, t->count = 0u,
|
||||
t->lod = b, t->flags = 0;
|
||||
}
|
||||
}
|
||||
void prepareVertexData(InputVertices const& vertices, unsigned numStars, Tiling const& tiling);
|
||||
|
||||
// FOV culling / LOD
|
||||
|
||||
|
@ -242,299 +76,65 @@ namespace starfield {
|
|||
|
||||
class TileSelection {
|
||||
|
||||
public:
|
||||
struct Cursor { Tile* current, * firstInRow; };
|
||||
private:
|
||||
Renderer& _rendererRef;
|
||||
Cursor* const _stackArray;
|
||||
Cursor* _stackPos;
|
||||
Tile const* const _tileArray;
|
||||
Tile const* const _tilesEnd;
|
||||
public:
|
||||
struct Cursor { Tile* current, * firstInRow; };
|
||||
|
||||
private:
|
||||
Renderer& _rendererRef;
|
||||
Cursor* const _stackArray;
|
||||
Cursor* _stackPos;
|
||||
Tile const* const _tileArray;
|
||||
Tile const* const _tilesEnd;
|
||||
|
||||
public:
|
||||
|
||||
TileSelection(Renderer& renderer, Tile const* tiles,
|
||||
Tile const* tiles_end, Cursor* stack) :
|
||||
|
||||
_rendererRef(renderer),
|
||||
_stackArray(stack),
|
||||
_stackPos(stack),
|
||||
_tileArray(tiles),
|
||||
_tilesEnd(tiles_end) {
|
||||
}
|
||||
public:
|
||||
TileSelection(Renderer& renderer, Tile const* tiles, Tile const* tiles_end, Cursor* stack) :
|
||||
_rendererRef(renderer),
|
||||
_stackArray(stack),
|
||||
_stackPos(stack),
|
||||
_tileArray(tiles),
|
||||
_tilesEnd(tiles_end) { }
|
||||
|
||||
protected:
|
||||
|
||||
// flood fill strategy
|
||||
|
||||
bool select(Cursor const& c) {
|
||||
Tile* t = c.current;
|
||||
|
||||
if (t < _tileArray || t >= _tilesEnd ||
|
||||
!! (t->flags & Tile::checked)) {
|
||||
|
||||
// out of bounds or been here already
|
||||
return false;
|
||||
}
|
||||
|
||||
// will check now and never again
|
||||
t->flags |= Tile::checked;
|
||||
if (_rendererRef.visitTile(t)) {
|
||||
|
||||
// good one -> remember (for batching) and propagate
|
||||
t->flags |= Tile::render;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool process(Cursor const& c) {
|
||||
Tile* t = c.current;
|
||||
|
||||
if (! (t->flags & Tile::visited)) {
|
||||
|
||||
t->flags |= Tile::visited;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void right(Cursor& c) const {
|
||||
|
||||
c.current += 1;
|
||||
if (c.current == c.firstInRow + _rendererRef._tiling.getAzimuthalTiles()) {
|
||||
c.current = c.firstInRow;
|
||||
}
|
||||
}
|
||||
void left(Cursor& c) const {
|
||||
|
||||
if (c.current == c.firstInRow) {
|
||||
c.current = c.firstInRow + _rendererRef._tiling.getAzimuthalTiles();
|
||||
}
|
||||
c.current -= 1;
|
||||
}
|
||||
void up(Cursor& c) const {
|
||||
|
||||
unsigned d = _rendererRef._tiling.getAzimuthalTiles();
|
||||
c.current += d;
|
||||
c.firstInRow += d;
|
||||
}
|
||||
void down(Cursor& c) const {
|
||||
|
||||
unsigned d = _rendererRef._tiling.getAzimuthalTiles();
|
||||
c.current -= d;
|
||||
c.firstInRow -= d;
|
||||
}
|
||||
|
||||
void defer(Cursor const& t) {
|
||||
|
||||
*_stackPos++ = t;
|
||||
}
|
||||
|
||||
bool deferred(Cursor& cursor) {
|
||||
|
||||
if (_stackPos != _stackArray) {
|
||||
cursor = *--_stackPos;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
protected:
|
||||
bool select(Cursor const& cursor);
|
||||
bool process(Cursor const& cursor);
|
||||
void right(Cursor& cursor) const;
|
||||
void left(Cursor& cursor) const;
|
||||
void up(Cursor& cursor) const;
|
||||
void down(Cursor& cursor) const;
|
||||
void defer(Cursor const& cursor);
|
||||
bool deferred(Cursor& cursor);
|
||||
};
|
||||
|
||||
bool visitTile(Tile* t) {
|
||||
|
||||
unsigned index = t - _tileArray;
|
||||
*_outIndexPos++ = index;
|
||||
|
||||
if (! tileVisible(t, index)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (t->lod != _minBright) {
|
||||
updateVertexCount(t, _minBright);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool tileVisible(Tile* t, unsigned i) {
|
||||
|
||||
float slice = _tiling.getSliceAngle();
|
||||
float halfSlice = 0.5f * slice;
|
||||
unsigned stride = _tiling.getAzimuthalTiles();
|
||||
float azimuth = (i % stride) * slice;
|
||||
float altitude = (i / stride) * slice - Radians::halfPi();
|
||||
float gx = sin(azimuth);
|
||||
float gz = -cos(azimuth);
|
||||
float exz = cos(altitude);
|
||||
vec3 tileCenter = vec3(gx * exz, sin(altitude), gz * exz);
|
||||
float w = dot(_wRowVec, tileCenter);
|
||||
|
||||
float daz = halfSlice * cos(std::max(0.0f, abs(altitude) - halfSlice));
|
||||
float dal = halfSlice;
|
||||
float adjustedNear = cos(_halfPerspectiveAngle + sqrt(daz * daz + dal * dal));
|
||||
|
||||
// printLog("Stars.cpp: checking tile #%d, w = %f, near = %f\n", i, w, nearClip);
|
||||
|
||||
return w >= adjustedNear;
|
||||
}
|
||||
|
||||
void updateVertexCount(Tile* t, BrightnessLevel minBright) {
|
||||
|
||||
// a growing number of stars needs to be rendereed when the
|
||||
// minimum brightness decreases
|
||||
// perform a binary search in the so found partition for the
|
||||
// new vertex count of this tile
|
||||
|
||||
GpuVertex const* start = _dataArray + t[0].offset;
|
||||
GpuVertex const* end = _dataArray + t[1].offset;
|
||||
|
||||
assert(end >= start);
|
||||
|
||||
if (start == end)
|
||||
return;
|
||||
|
||||
if (t->lod < minBright)
|
||||
end = start + t->count;
|
||||
else
|
||||
start += (t->count > 0 ? t->count - 1 : 0);
|
||||
|
||||
end = std::upper_bound(
|
||||
start, end, minBright, GreaterBrightness());
|
||||
|
||||
assert(end >= _dataArray + t[0].offset);
|
||||
|
||||
t->count = end - _dataArray - t[0].offset;
|
||||
t->lod = minBright;
|
||||
}
|
||||
|
||||
unsigned prepareBatch(unsigned const* indices,
|
||||
unsigned const* indicesEnd) {
|
||||
|
||||
unsigned nRanges = 0u;
|
||||
GLint* offs = _batchOffs;
|
||||
GLsizei* count = _batchCountArray;
|
||||
|
||||
for (unsigned* i = (unsigned*) _batchOffs;
|
||||
i != indicesEnd; ++i) {
|
||||
|
||||
Tile* t = _tileArray + *i;
|
||||
if ((t->flags & Tile::render) > 0u && t->count > 0u) {
|
||||
|
||||
*offs++ = t->offset;
|
||||
*count++ = t->count;
|
||||
++nRanges;
|
||||
}
|
||||
t->flags = 0;
|
||||
}
|
||||
return nRanges;
|
||||
}
|
||||
|
||||
bool visitTile(Tile* tile);
|
||||
bool isTileVisible(unsigned index);
|
||||
unsigned prepareBatch(unsigned const* indices, unsigned const* indicesEnd);
|
||||
|
||||
// GL API handling
|
||||
|
||||
void glAlloc() {
|
||||
|
||||
GLchar const* const VERTEX_SHADER =
|
||||
"#version 120\n"
|
||||
"uniform float alpha;\n"
|
||||
"void main(void) {\n"
|
||||
|
||||
" vec3 c = gl_Color.rgb * 1.0125;\n"
|
||||
" float s = max(1.0, dot(c, c) * 0.7);\n"
|
||||
|
||||
" gl_Position = ftransform();\n"
|
||||
" gl_FrontColor= gl_Color * alpha;\n"
|
||||
" gl_PointSize = s;\n"
|
||||
"}\n";
|
||||
|
||||
_program.addShaderFromSourceCode(QGLShader::Vertex, VERTEX_SHADER);
|
||||
GLchar const* const FRAGMENT_SHADER =
|
||||
"#version 120\n"
|
||||
"void main(void) {\n"
|
||||
" gl_FragColor = gl_Color;\n"
|
||||
"}\n";
|
||||
_program.addShaderFromSourceCode(QGLShader::Fragment, FRAGMENT_SHADER);
|
||||
_program.link();
|
||||
_alphaLocationHandle = _program.uniformLocation("alpha");
|
||||
|
||||
glGenBuffersARB(1, & _vertexArrayHandle);
|
||||
}
|
||||
|
||||
void glFree() {
|
||||
|
||||
glDeleteBuffersARB(1, & _vertexArrayHandle);
|
||||
}
|
||||
|
||||
void glUpload(GLsizei n) {
|
||||
glBindBufferARB(GL_ARRAY_BUFFER, _vertexArrayHandle);
|
||||
glBufferData(GL_ARRAY_BUFFER,
|
||||
n * sizeof(GpuVertex), _dataArray, GL_STATIC_DRAW);
|
||||
//glInterleavedArrays(GL_C4UB_V3F, sizeof(GpuVertex), 0l);
|
||||
|
||||
glBindBufferARB(GL_ARRAY_BUFFER, 0);
|
||||
}
|
||||
|
||||
void glBatch(GLfloat const* matrix, GLsizei n_ranges, float alpha) {
|
||||
|
||||
// printLog("Stars.cpp: rendering %d-multibatch\n", n_ranges);
|
||||
|
||||
// for (int i = 0; i < n_ranges; ++i)
|
||||
// printLog("Stars.cpp: Batch #%d - %d stars @ %d\n", i,
|
||||
// _batchOffs[i], _batchCountArray[i]);
|
||||
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
glDisable(GL_LIGHTING);
|
||||
|
||||
// setup modelview matrix
|
||||
glPushMatrix();
|
||||
glLoadMatrixf(matrix);
|
||||
|
||||
// set point size and smoothing + shader control
|
||||
glPointSize(1.0f);
|
||||
glEnable(GL_POINT_SMOOTH);
|
||||
glHint(GL_POINT_SMOOTH_HINT, GL_NICEST);
|
||||
glEnable(GL_VERTEX_PROGRAM_POINT_SIZE);
|
||||
|
||||
// select shader and vertex array
|
||||
_program.bind();
|
||||
_program.setUniformValue(_alphaLocationHandle, alpha);
|
||||
glBindBufferARB(GL_ARRAY_BUFFER, _vertexArrayHandle);
|
||||
glInterleavedArrays(GL_C4UB_V3F, sizeof(GpuVertex), 0l);
|
||||
|
||||
// render
|
||||
glMultiDrawArrays(GL_POINTS,
|
||||
_batchOffs, _batchCountArray, n_ranges);
|
||||
|
||||
// restore state
|
||||
glBindBufferARB(GL_ARRAY_BUFFER, 0);
|
||||
_program.release();
|
||||
glDisable(GL_VERTEX_PROGRAM_POINT_SIZE);
|
||||
glDisable(GL_POINT_SMOOTH);
|
||||
glDisableClientState(GL_VERTEX_ARRAY);
|
||||
glDisableClientState(GL_COLOR_ARRAY);
|
||||
|
||||
glPopMatrix();
|
||||
}
|
||||
|
||||
void glAlloc();
|
||||
void glFree();
|
||||
void glUpload(GLsizei numStars);
|
||||
void glBatch(GLfloat const* matrix, GLsizei n_ranges, float alpha);
|
||||
|
||||
// variables
|
||||
|
||||
GpuVertex* _dataArray;
|
||||
Tile* _tileArray;
|
||||
GLint* _batchOffs;
|
||||
GLsizei* _batchCountArray;
|
||||
GLuint _vertexArrayHandle;
|
||||
ProgramObject _program;
|
||||
int _alphaLocationHandle;
|
||||
GpuVertex* _dataArray;
|
||||
Tile* _tileArray;
|
||||
GLint* _batchOffs;
|
||||
GLsizei* _batchCountArray;
|
||||
GLuint _vertexArrayHandle;
|
||||
ProgramObject _program;
|
||||
int _alphaLocationHandle;
|
||||
|
||||
Tiling _tiling;
|
||||
Tiling _tiling;
|
||||
|
||||
unsigned* _outIndexPos;
|
||||
vec3 _wRowVec;
|
||||
float _halfPerspectiveAngle;
|
||||
BrightnessLevel _minBright;
|
||||
unsigned* _outIndexPos;
|
||||
vec3 _wRowVec;
|
||||
float _halfPerspectiveAngle;
|
||||
|
||||
};
|
||||
|
||||
} // anonymous namespace
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
|
61
interface/src/starfield/renderer/Tiling.h
Normal file → Executable file
61
interface/src/starfield/renderer/Tiling.h
Normal file → Executable file
|
@ -9,10 +9,6 @@
|
|||
#ifndef __interface__starfield__renderer__Tiling__
|
||||
#define __interface__starfield__renderer__Tiling__
|
||||
|
||||
#ifndef __interface__Starfield_impl__
|
||||
#error "This is an implementation file - not intended for direct inclusion."
|
||||
#endif
|
||||
|
||||
#include "starfield/Config.h"
|
||||
|
||||
namespace starfield {
|
||||
|
@ -20,53 +16,34 @@ namespace starfield {
|
|||
class Tiling {
|
||||
public:
|
||||
|
||||
Tiling(unsigned k) :
|
||||
_valK(k),
|
||||
_rcpSlice(k / Radians::twicePi()) {
|
||||
_nBits = ceil(log(getTileCount()) * 1.4426950408889634); // log2
|
||||
}
|
||||
|
||||
unsigned getAzimuthalTiles() const { return _valK; }
|
||||
unsigned getAltitudinalTiles() const { return _valK / 2 + 1; }
|
||||
Tiling(unsigned tileResolution) : _tileResolution(tileResolution), _rcpSlice(tileResolution / Radians::twicePi()) {
|
||||
_nBits = ceil(log(getTileCount()) * LOG2); }
|
||||
|
||||
unsigned getAzimuthalTiles() const { return _tileResolution; }
|
||||
unsigned getAltitudinalTiles() const { return _tileResolution / 2 + 1; }
|
||||
unsigned getTileIndexBits() const { return _nBits; }
|
||||
|
||||
unsigned getTileCount() const {
|
||||
return getAzimuthalTiles() * getAltitudinalTiles();
|
||||
}
|
||||
|
||||
unsigned getTileIndex(float azimuth, float altitude) const {
|
||||
return discreteAzimuth(azimuth) +
|
||||
_valK * discreteAltitude(altitude);
|
||||
}
|
||||
|
||||
float getSliceAngle() const {
|
||||
return 1.0f / _rcpSlice;
|
||||
}
|
||||
unsigned getTileCount() const { return getAzimuthalTiles() * getAltitudinalTiles(); }
|
||||
unsigned getTileIndex(float azimuth, float altitude) const { return discreteAzimuth(azimuth) +
|
||||
_tileResolution * discreteAltitude(altitude); }
|
||||
float getSliceAngle() const { return 1.0f / _rcpSlice; }
|
||||
|
||||
private:
|
||||
|
||||
unsigned discreteAngle(float unsigned_angle) const {
|
||||
|
||||
return unsigned(floor(unsigned_angle * _rcpSlice + 0.5f));
|
||||
}
|
||||
|
||||
unsigned discreteAzimuth(float a) const {
|
||||
return discreteAngle(a) % _valK;
|
||||
}
|
||||
|
||||
unsigned discreteAltitude(float a) const {
|
||||
return min(getAltitudinalTiles() - 1,
|
||||
discreteAngle(a + Radians::halfPi()) );
|
||||
}
|
||||
unsigned discreteAngle(float unsigned_angle) const { return unsigned(floor(unsigned_angle * _rcpSlice + 0.5f)); }
|
||||
unsigned discreteAzimuth(float angle) const { return discreteAngle(angle) % _tileResolution; }
|
||||
unsigned discreteAltitude(float angle) const { return min( getAltitudinalTiles() - 1,
|
||||
discreteAngle(angle + Radians::halfPi()) ); }
|
||||
|
||||
// variables
|
||||
|
||||
unsigned _valK;
|
||||
float _rcpSlice;
|
||||
unsigned _nBits;
|
||||
unsigned _tileResolution;
|
||||
float _rcpSlice;
|
||||
unsigned _nBits;
|
||||
|
||||
const float LOG2 = 1.4426950408889634;
|
||||
};
|
||||
|
||||
} // anonymous namespace
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
|
18
interface/src/starfield/renderer/VertexOrder.cpp
Executable file
18
interface/src/starfield/renderer/VertexOrder.cpp
Executable file
|
@ -0,0 +1,18 @@
|
|||
//
|
||||
// starfield/renderer/VertexOrder.cpp
|
||||
// interface
|
||||
//
|
||||
// Created by Chris Barnard on 10/17/13.
|
||||
// Based on code by Tobias Schwinger on 3/22/13.
|
||||
//
|
||||
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#include "starfield/renderer/VertexOrder.h"
|
||||
|
||||
using namespace starfield;
|
||||
|
||||
bool VertexOrder::bit(InputVertex const& vertex, state_type const& state) const {
|
||||
unsigned key = _tiling.getTileIndex(vertex.getAzimuth(), vertex.getAltitude());
|
||||
return base::bit(key, state);
|
||||
}
|
24
interface/src/starfield/renderer/VertexOrder.h
Normal file → Executable file
24
interface/src/starfield/renderer/VertexOrder.h
Normal file → Executable file
|
@ -9,37 +9,23 @@
|
|||
#ifndef __interface__starfield__renderer__VertexOrder__
|
||||
#define __interface__starfield__renderer__VertexOrder__
|
||||
|
||||
#ifndef __interface__Starfield_impl__
|
||||
#error "This is an implementation file - not intended for direct inclusion."
|
||||
#endif
|
||||
|
||||
#include "starfield/Config.h"
|
||||
#include "starfield/data/InputVertex.h"
|
||||
#include "starfield/renderer/Tiling.h"
|
||||
|
||||
namespace starfield {
|
||||
|
||||
/**
|
||||
* Defines the vertex order for the renderer as a bit extractor for
|
||||
* binary in-place Radix Sort.
|
||||
*/
|
||||
// Defines the vertex order for the renderer as a bit extractor for
|
||||
//binary in-place Radix Sort.
|
||||
|
||||
class VertexOrder : public Radix2IntegerScanner<unsigned>
|
||||
{
|
||||
public:
|
||||
explicit VertexOrder(Tiling const& tiling) :
|
||||
|
||||
base(tiling.getTileIndexBits() + BrightnessBits),
|
||||
_tiling(tiling) {
|
||||
}
|
||||
base(tiling.getTileIndexBits()), _tiling(tiling) { }
|
||||
|
||||
bool bit(InputVertex const& v, state_type const& s) const {
|
||||
|
||||
// inspect (tile_index, brightness) tuples
|
||||
unsigned key = getBrightness(v.getColor()) ^ BrightnessMask;
|
||||
key |= _tiling.getTileIndex(
|
||||
v.getAzimuth(), v.getAltitude()) << BrightnessBits;
|
||||
return base::bit(key, s);
|
||||
}
|
||||
bool bit(InputVertex const& vertex, state_type const& state) const;
|
||||
|
||||
private:
|
||||
Tiling _tiling;
|
||||
|
|
Loading…
Reference in a new issue