mirror of
https://github.com/overte-org/overte.git
synced 2025-08-07 19:30:41 +02:00
splits up Stars.cpp into a module of multiple files
This commit is contained in:
parent
8300646b33
commit
2b3263e019
12 changed files with 1499 additions and 1221 deletions
File diff suppressed because it is too large
Load diff
|
@ -11,15 +11,17 @@
|
||||||
|
|
||||||
#include "FieldOfView.h"
|
#include "FieldOfView.h"
|
||||||
|
|
||||||
|
namespace starfield { class Controller; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Starfield rendering component.
|
* Starfield rendering component.
|
||||||
*/
|
*/
|
||||||
class Stars
|
class Stars {
|
||||||
{
|
|
||||||
struct body;
|
starfield::Controller* _ptrController;
|
||||||
body* ptr_body;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
Stars();
|
Stars();
|
||||||
~Stars();
|
~Stars();
|
||||||
|
|
||||||
|
|
96
interface/src/starfield/Config.h
Normal file
96
interface/src/starfield/Config.h
Normal file
|
@ -0,0 +1,96 @@
|
||||||
|
//
|
||||||
|
// starfield/Config.h
|
||||||
|
// interface
|
||||||
|
//
|
||||||
|
// Created by Tobias Schwinger on 3/29/13.
|
||||||
|
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#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_LOD
|
||||||
|
#define STARFIELD_DEBUG_LOD 0 // set to 1 to peek behind the scenes
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//
|
||||||
|
// Dependencies:
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "InterfaceConfig.h"
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
|
#include <cfloat>
|
||||||
|
#include <cassert>
|
||||||
|
#include <cmath>
|
||||||
|
#include <cstdio>
|
||||||
|
#include <cctype>
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include <new>
|
||||||
|
#include <vector>
|
||||||
|
#include <memory>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
#include <glm/glm.hpp>
|
||||||
|
#include <glm/gtc/type_ptr.hpp>
|
||||||
|
#include <glm/gtc/matrix_inverse.hpp>
|
||||||
|
#include <glm/gtc/matrix_transform.hpp>
|
||||||
|
#include <glm/gtc/matrix_access.hpp>
|
||||||
|
#include <glm/gtc/swizzle.hpp>
|
||||||
|
|
||||||
|
#include "UrlReader.h"
|
||||||
|
#include "AngleUtils.h"
|
||||||
|
#include "Radix2InplaceSort.h"
|
||||||
|
#include "Radix2IntegerScanner.h"
|
||||||
|
#include "FloodFill.h"
|
||||||
|
|
||||||
|
// Namespace configuration:
|
||||||
|
|
||||||
|
namespace starfield {
|
||||||
|
|
||||||
|
using glm::vec3;
|
||||||
|
using glm::vec4;
|
||||||
|
using glm::dot;
|
||||||
|
using glm::normalize;
|
||||||
|
using glm::swizzle;
|
||||||
|
using glm::X;
|
||||||
|
using glm::Y;
|
||||||
|
using glm::Z;
|
||||||
|
using glm::W;
|
||||||
|
using glm::mat4;
|
||||||
|
using glm::column;
|
||||||
|
using glm::row;
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
382
interface/src/starfield/Controller.h
Normal file
382
interface/src/starfield/Controller.h
Normal file
|
@ -0,0 +1,382 @@
|
||||||
|
//
|
||||||
|
// starfield/Controller.h
|
||||||
|
// interface
|
||||||
|
//
|
||||||
|
// Created by Tobias Schwinger on 3/29/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 "starfield/data/InputVertex.h"
|
||||||
|
#include "starfield/data/BrightnessLevel.h"
|
||||||
|
#include "starfield/Loader.h"
|
||||||
|
|
||||||
|
#include "starfield/renderer/Renderer.h"
|
||||||
|
#include "starfield/renderer/VertexOrder.h"
|
||||||
|
|
||||||
|
namespace starfield {
|
||||||
|
|
||||||
|
class Controller {
|
||||||
|
|
||||||
|
InputVertices _seqInput;
|
||||||
|
unsigned _valTileResolution;
|
||||||
|
|
||||||
|
double _valLodFraction;
|
||||||
|
double _valLodLowWaterMark;
|
||||||
|
double _valLodHighWaterMark;
|
||||||
|
double _valLodOveralloc;
|
||||||
|
size_t _valLodNalloc;
|
||||||
|
size_t _valLodNrender;
|
||||||
|
BrightnessLevels _seqLodBrightness;
|
||||||
|
BrightnessLevel _valLodBrightness;
|
||||||
|
BrightnessLevel _valLodAllocBrightness;
|
||||||
|
|
||||||
|
Renderer* _ptrRenderer;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
Controller() :
|
||||||
|
_valTileResolution(20),
|
||||||
|
_valLodFraction(1.0),
|
||||||
|
_valLodLowWaterMark(0.8),
|
||||||
|
_valLodHighWaterMark(1.0),
|
||||||
|
_valLodOveralloc(1.2),
|
||||||
|
_valLodNalloc(0),
|
||||||
|
_valLodNrender(0),
|
||||||
|
_valLodBrightness(0),
|
||||||
|
_valLodAllocBrightness(0),
|
||||||
|
_ptrRenderer(0l) {
|
||||||
|
}
|
||||||
|
|
||||||
|
bool readInput(const char* url, unsigned limit)
|
||||||
|
{
|
||||||
|
InputVertices vertices;
|
||||||
|
|
||||||
|
if (! Loader().loadVertices(vertices, url, limit))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
BrightnessLevels brightness;
|
||||||
|
extractBrightnessLevels(brightness, vertices);
|
||||||
|
|
||||||
|
assert(brightness.size() == vertices.size());
|
||||||
|
|
||||||
|
for (BrightnessLevels::iterator i =
|
||||||
|
brightness.begin(); i != brightness.end() - 1; ++i)
|
||||||
|
{
|
||||||
|
BrightnessLevels::iterator next = i + 1;
|
||||||
|
if (next != brightness.end())
|
||||||
|
assert( *i >= *next );
|
||||||
|
}
|
||||||
|
|
||||||
|
// input is read, now run the entire data pipeline on the new input
|
||||||
|
|
||||||
|
{
|
||||||
|
// TODO input mutex
|
||||||
|
|
||||||
|
_seqInput.swap(vertices);
|
||||||
|
|
||||||
|
unsigned k = _valTileResolution;
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
{ // TODO lod mutex
|
||||||
|
|
||||||
|
size_t newLast = _seqInput.size() - 1;
|
||||||
|
|
||||||
|
// reciprocal change N_old/N_new tells us how to scale
|
||||||
|
// the fractions
|
||||||
|
rcpChange = min(1.0, double(vertices.size()) / _seqInput.size());
|
||||||
|
|
||||||
|
// initialization? use defaults / previously set values
|
||||||
|
if (rcpChange == 0.0) {
|
||||||
|
|
||||||
|
rcpChange = 1.0;
|
||||||
|
|
||||||
|
nRender = lrint(_valLodFraction * newLast);
|
||||||
|
n = min(newLast, size_t(lrint(_valLodOveralloc * nRender)));
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
// cannot allocate or render more than we have
|
||||||
|
n = min(newLast, _valLodNalloc);
|
||||||
|
nRender = min(newLast, _valLodNrender);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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(_seqInput);
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
|
||||||
|
// finally publish the new LOD state
|
||||||
|
|
||||||
|
{ // TODO lod mutex
|
||||||
|
|
||||||
|
_seqLodBrightness.swap(brightness);
|
||||||
|
_valLodFraction *= rcpChange;
|
||||||
|
_valLodLowWaterMark *= rcpChange;
|
||||||
|
_valLodHighWaterMark *= rcpChange;
|
||||||
|
_valLodOveralloc *= rcpChange;
|
||||||
|
_valLodNalloc = n;
|
||||||
|
_valLodNrender = nRender;
|
||||||
|
_valLodAllocBrightness = bMin;
|
||||||
|
// keep last, it's accessed asynchronously
|
||||||
|
_valLodBrightness = b;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool setResolution(unsigned k) {
|
||||||
|
|
||||||
|
if (k <= 3) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// fprintf(stderr, "Stars.cpp: setResolution(%d)\n", k);
|
||||||
|
|
||||||
|
if (k != _valTileResolution) { // TODO make atomic
|
||||||
|
|
||||||
|
// TODO input mutex
|
||||||
|
|
||||||
|
unsigned n;
|
||||||
|
BrightnessLevel b, bMin;
|
||||||
|
|
||||||
|
{ // TODO lod mutex
|
||||||
|
|
||||||
|
n = _valLodNalloc;
|
||||||
|
b = _valLodBrightness;
|
||||||
|
bMin = _valLodAllocBrightness;
|
||||||
|
}
|
||||||
|
|
||||||
|
this->retile(n, k, b, bMin);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
void retile(size_t n, unsigned k,
|
||||||
|
BrightnessLevel b, BrightnessLevel bMin) {
|
||||||
|
|
||||||
|
Tiling tiling(k);
|
||||||
|
VertexOrder scanner(tiling);
|
||||||
|
radix2InplaceSort(_seqInput.begin(), _seqInput.end(), scanner);
|
||||||
|
|
||||||
|
// fprintf(stderr,
|
||||||
|
// "Stars.cpp: recreateRenderer(%d, %d, %d, %d)\n", n, k, b, bMin);
|
||||||
|
|
||||||
|
recreateRenderer(n, k, b, bMin);
|
||||||
|
|
||||||
|
_valTileResolution = k;
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
double changeLOD(double factor, double overalloc, double realloc) {
|
||||||
|
|
||||||
|
assert(overalloc >= realloc && realloc >= 0.0);
|
||||||
|
assert(overalloc <= 1.0 && realloc <= 1.0);
|
||||||
|
|
||||||
|
// fprintf(stderr,
|
||||||
|
// "Stars.cpp: changeLOD(%lf, %lf, %lf)\n", factor, overalloc, realloc);
|
||||||
|
|
||||||
|
size_t n, nRender;
|
||||||
|
BrightnessLevel bMin, b;
|
||||||
|
double fraction, lwm, hwm;
|
||||||
|
|
||||||
|
{ // TODO lod mutex
|
||||||
|
|
||||||
|
// acuire a consistent copy of the current LOD state
|
||||||
|
fraction = _valLodFraction;
|
||||||
|
lwm = _valLodLowWaterMark;
|
||||||
|
hwm = _valLodHighWaterMark;
|
||||||
|
size_t last = _seqLodBrightness.size() - 1;
|
||||||
|
|
||||||
|
// apply factor
|
||||||
|
fraction = max(0.0, min(1.0, fraction * factor));
|
||||||
|
|
||||||
|
// calculate allocation size and corresponding brightness
|
||||||
|
// threshold
|
||||||
|
double oaFract = std::min(fraction * (1.0 + overalloc), 1.0);
|
||||||
|
n = lrint(oaFract * last);
|
||||||
|
bMin = _seqLodBrightness[n];
|
||||||
|
n = std::upper_bound(
|
||||||
|
_seqLodBrightness.begin() + n - 1,
|
||||||
|
_seqLodBrightness.end(),
|
||||||
|
bMin, GreaterBrightness() ) - _seqLodBrightness.begin();
|
||||||
|
|
||||||
|
// also determine number of vertices to render and brightness
|
||||||
|
nRender = lrint(fraction * last);
|
||||||
|
// Note: nRender does not have to be accurate
|
||||||
|
b = _seqLodBrightness[nRender];
|
||||||
|
// this setting controls the renderer, also keep b as the
|
||||||
|
// brightness becomes volatile as soon as the mutex is
|
||||||
|
// released
|
||||||
|
_valLodBrightness = b; // TODO make atomic
|
||||||
|
|
||||||
|
// fprintf(stderr, "Stars.cpp: "
|
||||||
|
// "fraction = %lf, oaFract = %lf, n = %d, n' = %d, bMin = %d, b = %d\n",
|
||||||
|
// fraction, oaFract, lrint(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 >= _valLodLowWaterMark
|
||||||
|
&& fraction <= _valLodHighWaterMark) {
|
||||||
|
|
||||||
|
_valLodFraction = fraction;
|
||||||
|
return fraction;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// reallocate
|
||||||
|
{ // TODO input mutex
|
||||||
|
recreateRenderer(n, _valTileResolution, b, bMin);
|
||||||
|
|
||||||
|
// fprintf(stderr, "Stars.cpp: LOD reallocation\n");
|
||||||
|
|
||||||
|
// publish new lod state
|
||||||
|
{ // TODO lod mutex
|
||||||
|
_valLodNalloc = n;
|
||||||
|
_valLodNrender = nRender;
|
||||||
|
|
||||||
|
_valLodFraction = fraction;
|
||||||
|
_valLodLowWaterMark = fraction * (1.0 - realloc);
|
||||||
|
_valLodHighWaterMark = fraction * (1.0 + realloc);
|
||||||
|
_valLodOveralloc = fraction * (1.0 + overalloc);
|
||||||
|
_valLodAllocBrightness = bMin;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return fraction;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
void recreateRenderer(size_t n, unsigned k,
|
||||||
|
BrightnessLevel b, BrightnessLevel bMin) {
|
||||||
|
|
||||||
|
Renderer* renderer = new Renderer(_seqInput, n, k, b, bMin);
|
||||||
|
swap(_ptrRenderer, renderer); // TODO make atomic
|
||||||
|
delete renderer; // will be NULL when was in use
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
void render(float perspective, float angle, mat4 const& orientation) {
|
||||||
|
|
||||||
|
// check out renderer
|
||||||
|
Renderer* renderer = 0l;
|
||||||
|
swap(_ptrRenderer, renderer); // TODO make atomic
|
||||||
|
|
||||||
|
// have it render
|
||||||
|
if (renderer != 0l) {
|
||||||
|
|
||||||
|
BrightnessLevel b = _valLodBrightness; // make atomic
|
||||||
|
|
||||||
|
renderer->render(perspective, angle, orientation, b);
|
||||||
|
}
|
||||||
|
|
||||||
|
// check in - or dispose if there is a new one
|
||||||
|
// TODO make atomic (CAS)
|
||||||
|
if (! _ptrRenderer) {
|
||||||
|
_ptrRenderer = renderer;
|
||||||
|
} else {
|
||||||
|
delete renderer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
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());
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
180
interface/src/starfield/Loader.h
Normal file
180
interface/src/starfield/Loader.h
Normal file
|
@ -0,0 +1,180 @@
|
||||||
|
//
|
||||||
|
// starfield/Loader.h
|
||||||
|
// interface
|
||||||
|
//
|
||||||
|
// Created by Tobias Schwinger on 3/29/13.
|
||||||
|
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef __interface__starfield__Loader__
|
||||||
|
#define __interface__starfield__Loader__
|
||||||
|
|
||||||
|
#ifndef __interface__Starfield_impl__
|
||||||
|
#error "This is an implementation file - not intended for direct inclusion."
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "Config.h"
|
||||||
|
|
||||||
|
#include "starfield/data/InputVertex.h"
|
||||||
|
#include "starfield/data/BrightnessLevel.h"
|
||||||
|
|
||||||
|
namespace starfield {
|
||||||
|
|
||||||
|
class Loader : UrlReader {
|
||||||
|
|
||||||
|
InputVertices* _ptrVertices;
|
||||||
|
unsigned _valLimit;
|
||||||
|
|
||||||
|
unsigned _valLineNo;
|
||||||
|
char const* _strUrl;
|
||||||
|
|
||||||
|
unsigned _valRecordsRead;
|
||||||
|
BrightnessLevel _valMinBrightness;
|
||||||
|
public:
|
||||||
|
|
||||||
|
bool loadVertices(
|
||||||
|
InputVertices& destination, char const* url, unsigned limit)
|
||||||
|
{
|
||||||
|
_ptrVertices = & destination;
|
||||||
|
_valLimit = limit;
|
||||||
|
#if STARFIELD_SAVE_MEMORY
|
||||||
|
if (_valLimit == 0 || _valLimit > 60000u)
|
||||||
|
_valLimit = 60000u;
|
||||||
|
#endif
|
||||||
|
_strUrl = url; // in case we fail early
|
||||||
|
|
||||||
|
if (! UrlReader::readUrl(url, *this))
|
||||||
|
{
|
||||||
|
fprintf(stderr, "%s:%d: %s\n",
|
||||||
|
_strUrl, _valLineNo, getError());
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
fprintf(stderr, "Stars.cpp: read %d vertices, using %d\n",
|
||||||
|
_valRecordsRead, _ptrVertices->size());
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
|
||||||
|
friend class UrlReader;
|
||||||
|
|
||||||
|
void begin(char const* url,
|
||||||
|
char const* type,
|
||||||
|
int64_t size,
|
||||||
|
int64_t stardate) {
|
||||||
|
|
||||||
|
_valLineNo = 0u;
|
||||||
|
_strUrl = url; // new value in http redirect
|
||||||
|
|
||||||
|
_valRecordsRead = 0u;
|
||||||
|
|
||||||
|
_ptrVertices->clear();
|
||||||
|
_ptrVertices->reserve(_valLimit);
|
||||||
|
// fprintf(stderr, "Stars.cpp: loader begin %s\n", url);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t transfer(char* input, size_t bytes) {
|
||||||
|
|
||||||
|
size_t consumed = 0u;
|
||||||
|
char const* end = input + bytes;
|
||||||
|
char* line, * next = input;
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
|
||||||
|
// advance to next line
|
||||||
|
for (; next != end && isspace(*next); ++next);
|
||||||
|
consumed = next - input;
|
||||||
|
line = next;
|
||||||
|
++_valLineNo;
|
||||||
|
for (; next != end && *next != '\n' && *next != '\r'; ++next);
|
||||||
|
if (next == end)
|
||||||
|
return consumed;
|
||||||
|
*next++ = '\0';
|
||||||
|
|
||||||
|
// skip comments
|
||||||
|
if (*line == '\\' || *line == '/' || *line == ';')
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// parse
|
||||||
|
float azi, alt;
|
||||||
|
unsigned c;
|
||||||
|
if (sscanf(line, " %f %f #%x", & azi, & alt, & c) == 3) {
|
||||||
|
|
||||||
|
if (spaceFor( getBrightness(c) )) {
|
||||||
|
|
||||||
|
storeVertex(azi, alt, c);
|
||||||
|
}
|
||||||
|
|
||||||
|
++_valRecordsRead;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
fprintf(stderr, "Stars.cpp:%d: Bad input from %s\n",
|
||||||
|
_valLineNo, _strUrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
return consumed;
|
||||||
|
}
|
||||||
|
|
||||||
|
void end(bool ok)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
bool atLimit() { return _valLimit > 0u && _valRecordsRead >= _valLimit; }
|
||||||
|
|
||||||
|
bool spaceFor(BrightnessLevel b) {
|
||||||
|
|
||||||
|
if (! atLimit()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// just reached the limit? -> establish a minimum heap and
|
||||||
|
// remember the brightness at its top
|
||||||
|
if (_valRecordsRead == _valLimit) {
|
||||||
|
|
||||||
|
// fprintf(stderr, "Stars.cpp: vertex limit reached -> heap mode\n");
|
||||||
|
|
||||||
|
make_heap(
|
||||||
|
_ptrVertices->begin(), _ptrVertices->end(),
|
||||||
|
GreaterBrightness() );
|
||||||
|
|
||||||
|
_valMinBrightness = getBrightness(
|
||||||
|
_ptrVertices->begin()->getColor() );
|
||||||
|
}
|
||||||
|
|
||||||
|
// not interested? say so
|
||||||
|
if (_valMinBrightness >= b)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// otherwise free up space for the new vertex
|
||||||
|
pop_heap(
|
||||||
|
_ptrVertices->begin(), _ptrVertices->end(),
|
||||||
|
GreaterBrightness() );
|
||||||
|
_ptrVertices->pop_back();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void storeVertex(float azi, float alt, unsigned color) {
|
||||||
|
|
||||||
|
_ptrVertices->push_back(InputVertex(azi, alt, color));
|
||||||
|
|
||||||
|
if (atLimit()) {
|
||||||
|
|
||||||
|
push_heap(
|
||||||
|
_ptrVertices->begin(), _ptrVertices->end(),
|
||||||
|
GreaterBrightness() );
|
||||||
|
|
||||||
|
_valMinBrightness = getBrightness(
|
||||||
|
_ptrVertices->begin()->getColor() );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // anonymous namespace
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
64
interface/src/starfield/data/BrightnessLevel.h
Normal file
64
interface/src/starfield/data/BrightnessLevel.h
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
//
|
||||||
|
// 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
|
||||||
|
|
53
interface/src/starfield/data/GpuVertex.h
Normal file
53
interface/src/starfield/data/GpuVertex.h
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
//
|
||||||
|
// starfield/data/GpuVertex.h
|
||||||
|
// interface
|
||||||
|
//
|
||||||
|
// Created by Tobias Schwinger on 3/29/13.
|
||||||
|
// 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 {
|
||||||
|
|
||||||
|
class GpuVertex {
|
||||||
|
|
||||||
|
unsigned _valColor;
|
||||||
|
float _valX;
|
||||||
|
float _valY;
|
||||||
|
float _valZ;
|
||||||
|
public:
|
||||||
|
|
||||||
|
GpuVertex() { }
|
||||||
|
|
||||||
|
GpuVertex(InputVertex const& in) {
|
||||||
|
|
||||||
|
_valColor = 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned getColor() const { return _valColor; }
|
||||||
|
};
|
||||||
|
|
||||||
|
} // anonymous namespace
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
51
interface/src/starfield/data/InputVertex.h
Normal file
51
interface/src/starfield/data/InputVertex.h
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
//
|
||||||
|
// starfield/data/InputVertex.h
|
||||||
|
// interface
|
||||||
|
//
|
||||||
|
// Created by Tobias Schwinger on 3/29/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 {
|
||||||
|
|
||||||
|
class InputVertex {
|
||||||
|
|
||||||
|
unsigned _valColor;
|
||||||
|
float _valAzimuth;
|
||||||
|
float _valAltitude;
|
||||||
|
public:
|
||||||
|
|
||||||
|
InputVertex(float azimuth, float altitude, unsigned color) {
|
||||||
|
|
||||||
|
_valColor = color >> 16 & 0xffu | color & 0xff00u |
|
||||||
|
color << 16 & 0xff0000u | 0xff000000u;
|
||||||
|
|
||||||
|
azimuth = angleConvert<Degrees,Radians>(azimuth);
|
||||||
|
altitude = angleConvert<Degrees,Radians>(altitude);
|
||||||
|
|
||||||
|
angleHorizontalPolar<Radians>(azimuth, altitude);
|
||||||
|
|
||||||
|
_valAzimuth = azimuth;
|
||||||
|
_valAltitude = altitude;
|
||||||
|
}
|
||||||
|
|
||||||
|
float getAzimuth() const { return _valAzimuth; }
|
||||||
|
float getAltitude() const { return _valAltitude; }
|
||||||
|
unsigned getColor() const { return _valColor; }
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef std::vector<InputVertex> InputVertices;
|
||||||
|
|
||||||
|
} // anonymous namespace
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
37
interface/src/starfield/data/Tile.h
Normal file
37
interface/src/starfield/data/Tile.h
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
//
|
||||||
|
// starfield/data/Tile.h
|
||||||
|
// interface
|
||||||
|
//
|
||||||
|
// Created by Tobias Schwinger on 3/22/13.
|
||||||
|
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#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;
|
||||||
|
|
||||||
|
static uint16_t const checked = 1;
|
||||||
|
static uint16_t const visited = 2;
|
||||||
|
static uint16_t const render = 4;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
} // anonymous namespace
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
495
interface/src/starfield/renderer/Renderer.h
Normal file
495
interface/src/starfield/renderer/Renderer.h
Normal file
|
@ -0,0 +1,495 @@
|
||||||
|
//
|
||||||
|
// starfield/renderer/Renderer.h
|
||||||
|
// interface
|
||||||
|
//
|
||||||
|
// Created by Tobias Schwinger on 3/22/13.
|
||||||
|
// 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"
|
||||||
|
|
||||||
|
//
|
||||||
|
// FOV culling
|
||||||
|
// ===========
|
||||||
|
//
|
||||||
|
// As stars can be thought of as at infinity distance, the field of view only
|
||||||
|
// depends on perspective and rotation:
|
||||||
|
//
|
||||||
|
// _----_ <-- visible stars
|
||||||
|
// from above +-near-+ - -
|
||||||
|
// \ / |
|
||||||
|
// near width: \ / | cos(p/2)
|
||||||
|
// 2sin(p/2) \/ _
|
||||||
|
// center
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// Now it is important to note that a change in altitude maps uniformly to a
|
||||||
|
// distance on a sphere. This is NOT the case for azimuthal angles: In this
|
||||||
|
// case a factor of 'cos(alt)' (the orbital radius) applies:
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// |<-cos alt ->| | |<-|<----->|->| d_azi cos(alt)
|
||||||
|
// |
|
||||||
|
// __--* | --------- -
|
||||||
|
// __-- * | | | ^ d_alt
|
||||||
|
// __-- alt) * | | | v
|
||||||
|
// --------------*- | ------------- -
|
||||||
|
// |
|
||||||
|
// side view | tile on sphere
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// This lets us find a worst-case (Eigen) angle from the center to the edge
|
||||||
|
// of a tile as
|
||||||
|
//
|
||||||
|
// hypot( 0.5 d_alt, 0.5 d_azi cos(alt_absmin) ).
|
||||||
|
//
|
||||||
|
// This angle must be added to 'p' (the perspective angle) in order to find
|
||||||
|
// an altered near plane for the culling decision.
|
||||||
|
//
|
||||||
|
|
||||||
|
namespace starfield {
|
||||||
|
|
||||||
|
class Renderer {
|
||||||
|
|
||||||
|
GpuVertex* _arrData;
|
||||||
|
Tile* _arrTile;
|
||||||
|
GLint* _arrBatchOffs;
|
||||||
|
GLsizei* _arrBatchCount;
|
||||||
|
GLuint _hndVertexArray;
|
||||||
|
Tiling _objTiling;
|
||||||
|
|
||||||
|
unsigned* _itrOutIndex;
|
||||||
|
vec3 _vecWxform;
|
||||||
|
float _valHalfPersp;
|
||||||
|
BrightnessLevel _valMinBright;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
Renderer(InputVertices const& src,
|
||||||
|
size_t n,
|
||||||
|
unsigned k,
|
||||||
|
BrightnessLevel b,
|
||||||
|
BrightnessLevel bMin) :
|
||||||
|
|
||||||
|
_arrData(0l),
|
||||||
|
_arrTile(0l),
|
||||||
|
_objTiling(k) {
|
||||||
|
|
||||||
|
this->glAlloc();
|
||||||
|
|
||||||
|
Tiling tiling(k);
|
||||||
|
size_t nTiles = tiling.getTileCount();
|
||||||
|
|
||||||
|
_arrData = new GpuVertex[n];
|
||||||
|
_arrTile = new Tile[nTiles + 1];
|
||||||
|
_arrBatchOffs = new GLint[nTiles];
|
||||||
|
_arrBatchCount = new GLsizei[nTiles];
|
||||||
|
|
||||||
|
prepareVertexData(src, n, tiling, b, bMin);
|
||||||
|
|
||||||
|
this->glUpload(n);
|
||||||
|
}
|
||||||
|
|
||||||
|
~Renderer()
|
||||||
|
{
|
||||||
|
delete[] _arrData;
|
||||||
|
delete[] _arrTile;
|
||||||
|
delete[] _arrBatchCount;
|
||||||
|
delete[] _arrBatchOffs;
|
||||||
|
|
||||||
|
this->glFree();
|
||||||
|
}
|
||||||
|
|
||||||
|
void render(float perspective,
|
||||||
|
float aspect,
|
||||||
|
mat4 const& orientation,
|
||||||
|
BrightnessLevel minBright)
|
||||||
|
{
|
||||||
|
|
||||||
|
// fprintf(stderr, "
|
||||||
|
// Stars.cpp: rendering at minimal brightness %d\n", minBright);
|
||||||
|
|
||||||
|
float halfPersp = perspective * 0.5f;
|
||||||
|
|
||||||
|
// determine dimensions based on a sought screen diagonal
|
||||||
|
//
|
||||||
|
// ww + hh = dd
|
||||||
|
// a = h / w => h = wa
|
||||||
|
// ww + ww aa = dd
|
||||||
|
// ww = dd / (1 + aa)
|
||||||
|
float diag = 2.0f * std::sin(halfPersp);
|
||||||
|
float near = std::cos(halfPersp);
|
||||||
|
|
||||||
|
float hw = 0.5f * sqrt(diag * diag / (1.0f + aspect * aspect));
|
||||||
|
float hh = hw * aspect;
|
||||||
|
|
||||||
|
// 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 = swizzle<X,Y,Z>( column(matrix, 2) );
|
||||||
|
|
||||||
|
float azimuth = atan2(ahead.x,-ahead.z) + Radians::pi();
|
||||||
|
float altitude = atan2(-ahead.y, hypot(ahead.x, ahead.z));
|
||||||
|
angleHorizontalPolar<Radians>(azimuth, altitude);
|
||||||
|
#if STARFIELD_HEMISPHERE_ONLY
|
||||||
|
altitude = std::max(0.0f, altitude);
|
||||||
|
#endif
|
||||||
|
unsigned tileIndex =
|
||||||
|
_objTiling.getTileIndex(azimuth, altitude);
|
||||||
|
|
||||||
|
// fprintf(stderr, "Stars.cpp: starting on tile #%d\n", tileIndex);
|
||||||
|
|
||||||
|
|
||||||
|
#if STARFIELD_DEBUG_LOD
|
||||||
|
mat4 matrix_debug = glm::translate(
|
||||||
|
glm::frustum(-hw,hw, -hh,hh, near,10.0f),
|
||||||
|
vec3(0.0f, 0.0f, -4.0f)) * glm::affineInverse(matrix);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
matrix = glm::frustum(-hw,hw, -hh,hh, near,10.0f)
|
||||||
|
* glm::affineInverse(matrix);
|
||||||
|
|
||||||
|
this->_itrOutIndex = (unsigned*) _arrBatchOffs;
|
||||||
|
this->_vecWxform = swizzle<X,Y,Z>(row(matrix, 3));
|
||||||
|
this->_valHalfPersp = halfPersp;
|
||||||
|
this->_valMinBright = minBright;
|
||||||
|
|
||||||
|
floodFill(_arrTile + tileIndex, TileSelection(*this,
|
||||||
|
_arrTile, _arrTile + _objTiling.getTileCount(),
|
||||||
|
(Tile**) _arrBatchCount));
|
||||||
|
|
||||||
|
#if STARFIELD_DEBUG_LOD
|
||||||
|
# define matrix matrix_debug
|
||||||
|
#endif
|
||||||
|
this->glBatch(glm::value_ptr(matrix), prepareBatch(
|
||||||
|
(unsigned*) _arrBatchOffs, _itrOutIndex) );
|
||||||
|
|
||||||
|
#if STARFIELD_DEBUG_LOD
|
||||||
|
# undef matrix
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
_arrTile[0].offset = 0u;
|
||||||
|
_arrTile[0].lod = b;
|
||||||
|
_arrTile[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 = _arrTile + currTileIndex;
|
||||||
|
Tile* tLast = _arrTile + 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;
|
||||||
|
|
||||||
|
// fprintf(stderr, "Stars.cpp: Vertex %d on tile #%d\n", vertexIndex, tileIndex);
|
||||||
|
|
||||||
|
// write converted vertex
|
||||||
|
_arrData[vertexIndex++] = *i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assert(vertexIndex == n);
|
||||||
|
// flush last tile (see above)
|
||||||
|
Tile* t = _arrTile + currTileIndex;
|
||||||
|
t->count = count_active;
|
||||||
|
for (Tile* e = _arrTile + nTiles + 1; ++t != e;) {
|
||||||
|
t->offset = vertexIndex, t->count = 0u,
|
||||||
|
t->lod = b, t->flags = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private: // FOV culling / LOD
|
||||||
|
|
||||||
|
class TileSelection;
|
||||||
|
friend class Renderer::TileSelection;
|
||||||
|
|
||||||
|
class TileSelection {
|
||||||
|
|
||||||
|
Renderer& _refRenderer;
|
||||||
|
Tile** const _arrStack;
|
||||||
|
Tile** _itrStack;
|
||||||
|
Tile const* const _arrTile;
|
||||||
|
Tile const* const _itrTilesEnd;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
TileSelection(Renderer& renderer, Tile const* tiles,
|
||||||
|
Tile const* tiles_end, Tile** stack) :
|
||||||
|
|
||||||
|
_refRenderer(renderer),
|
||||||
|
_arrStack(stack),
|
||||||
|
_itrStack(stack),
|
||||||
|
_arrTile(tiles),
|
||||||
|
_itrTilesEnd(tiles_end) {
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
|
||||||
|
// flood fill strategy
|
||||||
|
|
||||||
|
bool select(Tile* t) {
|
||||||
|
|
||||||
|
if (t < _arrTile || t >= _itrTilesEnd ||
|
||||||
|
!! (t->flags & Tile::visited)) {
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (! (t->flags & Tile::checked)) {
|
||||||
|
|
||||||
|
if (_refRenderer.visitTile(t))
|
||||||
|
t->flags |= Tile::render;
|
||||||
|
}
|
||||||
|
return !! (t->flags & Tile::render);
|
||||||
|
}
|
||||||
|
|
||||||
|
void process(Tile* t) {
|
||||||
|
|
||||||
|
t->flags |= Tile::visited;
|
||||||
|
}
|
||||||
|
|
||||||
|
void right(Tile*& cursor) const { cursor += 1; }
|
||||||
|
void left(Tile*& cursor) const { cursor -= 1; }
|
||||||
|
void up(Tile*& cursor) const { cursor += yStride(); }
|
||||||
|
void down(Tile*& cursor) const { cursor -= yStride(); }
|
||||||
|
|
||||||
|
void defer(Tile* t) { *_itrStack++ = t; }
|
||||||
|
|
||||||
|
bool deferred(Tile*& cursor) {
|
||||||
|
|
||||||
|
if (_itrStack != _arrStack) {
|
||||||
|
cursor = *--_itrStack;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
unsigned yStride() const {
|
||||||
|
|
||||||
|
return _refRenderer._objTiling.getAzimuthalTiles();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
bool visitTile(Tile* t) {
|
||||||
|
|
||||||
|
unsigned index = t - _arrTile;
|
||||||
|
*_itrOutIndex++ = index;
|
||||||
|
|
||||||
|
if (! tileVisible(t, index)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (t->lod != _valMinBright) {
|
||||||
|
updateVertexCount(t, _valMinBright);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool tileVisible(Tile* t, unsigned i) {
|
||||||
|
|
||||||
|
float slice = _objTiling.getSliceAngle();
|
||||||
|
unsigned stride = _objTiling.getAzimuthalTiles();
|
||||||
|
float azimuth = (i % stride) * slice;
|
||||||
|
float altitude = (i / stride) * slice - Radians::half_pi();
|
||||||
|
float gx = sin(azimuth);
|
||||||
|
float gz = -cos(azimuth);
|
||||||
|
float exz = cos(altitude);
|
||||||
|
vec3 tileCenter = vec3(gx * exz, sin(altitude), gz * exz);
|
||||||
|
float w = dot(_vecWxform, tileCenter);
|
||||||
|
|
||||||
|
float halfSlice = 0.5f * slice;
|
||||||
|
float daz = halfSlice * cos(abs(altitude) - halfSlice);
|
||||||
|
float dal = halfSlice;
|
||||||
|
float near = cos(_valHalfPersp + sqrt(daz*daz+dal*dal));
|
||||||
|
|
||||||
|
// fprintf(stderr, "Stars.cpp: checking tile #%d, w = %f, near = %f\n", i, w, near);
|
||||||
|
|
||||||
|
return w > near;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 = _arrData + t[0].offset;
|
||||||
|
GpuVertex const* end = _arrData + 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 >= _arrData + t[0].offset);
|
||||||
|
|
||||||
|
t->count = end - _arrData - t[0].offset;
|
||||||
|
t->lod = minBright;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned prepareBatch(unsigned const* indices,
|
||||||
|
unsigned const* indicesEnd) {
|
||||||
|
|
||||||
|
unsigned nRanges = 0u;
|
||||||
|
GLint* offs = _arrBatchOffs;
|
||||||
|
GLsizei* count = _arrBatchCount;
|
||||||
|
|
||||||
|
for (unsigned* i = (unsigned*) _arrBatchOffs;
|
||||||
|
i != indicesEnd; ++i) {
|
||||||
|
|
||||||
|
Tile* t = _arrTile + *i;
|
||||||
|
if ((t->flags & Tile::render) > 0u && t->count > 0u) {
|
||||||
|
|
||||||
|
*offs++ = t->offset;
|
||||||
|
*count++ = t->count;
|
||||||
|
++nRanges;
|
||||||
|
}
|
||||||
|
t->flags = 0;
|
||||||
|
}
|
||||||
|
return nRanges;
|
||||||
|
}
|
||||||
|
|
||||||
|
private: // gl API handling
|
||||||
|
|
||||||
|
#ifdef __APPLE__
|
||||||
|
# define glBindVertexArray glBindVertexArrayAPPLE
|
||||||
|
# define glGenVertexArrays glGenVertexArraysAPPLE
|
||||||
|
# define glDeleteVertexArrays glDeleteVertexArraysAPPLE
|
||||||
|
#endif
|
||||||
|
void glAlloc() {
|
||||||
|
|
||||||
|
glGenVertexArrays(1, & _hndVertexArray);
|
||||||
|
}
|
||||||
|
|
||||||
|
void glFree() {
|
||||||
|
|
||||||
|
glDeleteVertexArrays(1, & _hndVertexArray);
|
||||||
|
}
|
||||||
|
|
||||||
|
void glUpload(GLsizei n) {
|
||||||
|
|
||||||
|
GLuint vbo;
|
||||||
|
glGenBuffers(1, & vbo);
|
||||||
|
|
||||||
|
glBindVertexArray(_hndVertexArray);
|
||||||
|
glBindBuffer(GL_ARRAY_BUFFER, vbo);
|
||||||
|
glBufferData(GL_ARRAY_BUFFER,
|
||||||
|
n * sizeof(GpuVertex), _arrData, GL_STATIC_DRAW);
|
||||||
|
glInterleavedArrays(GL_C4UB_V3F, sizeof(GpuVertex), 0l);
|
||||||
|
|
||||||
|
glBindVertexArray(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void glBatch(GLfloat const* matrix, GLsizei n_ranges) {
|
||||||
|
|
||||||
|
// fprintf(stderr, "Stars.cpp: rendering %d-multibatch\n", n_ranges);
|
||||||
|
|
||||||
|
// for (int i = 0; i < n_ranges; ++i)
|
||||||
|
// fprintf(stderr, "Stars.cpp: Batch #%d - %d stars @ %d\n", i,
|
||||||
|
// _arrBatchOffs[i], _arrBatchCount[i]);
|
||||||
|
|
||||||
|
// setup modelview matrix (identity)
|
||||||
|
glMatrixMode(GL_MODELVIEW);
|
||||||
|
glPushMatrix();
|
||||||
|
glLoadIdentity();
|
||||||
|
|
||||||
|
// set projection matrix
|
||||||
|
glMatrixMode(GL_PROJECTION);
|
||||||
|
glPushMatrix();
|
||||||
|
glLoadMatrixf(matrix);
|
||||||
|
|
||||||
|
// render
|
||||||
|
glBindVertexArray(_hndVertexArray);
|
||||||
|
|
||||||
|
glEnable(GL_POINT_SMOOTH);
|
||||||
|
glHint(GL_POINT_SMOOTH_HINT, GL_NICEST);
|
||||||
|
glPointSize(1.42f);
|
||||||
|
|
||||||
|
glMultiDrawArrays(GL_POINTS,
|
||||||
|
_arrBatchOffs, _arrBatchCount, n_ranges);
|
||||||
|
|
||||||
|
// restore state
|
||||||
|
glBindVertexArray(0);
|
||||||
|
glPopMatrix();
|
||||||
|
glMatrixMode(GL_MODELVIEW);
|
||||||
|
glPopMatrix();
|
||||||
|
}
|
||||||
|
#ifdef __APPLE__
|
||||||
|
# undef glBindVertexArray
|
||||||
|
# undef glGenVertexArrays
|
||||||
|
# undef glDeleteVertexArrays
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
} // anonymous namespace
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
71
interface/src/starfield/renderer/Tiling.h
Normal file
71
interface/src/starfield/renderer/Tiling.h
Normal file
|
@ -0,0 +1,71 @@
|
||||||
|
//
|
||||||
|
// starfield/renderer/Tiling.h
|
||||||
|
// interface
|
||||||
|
//
|
||||||
|
// Created by Tobias Schwinger on 3/22/13.
|
||||||
|
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#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 {
|
||||||
|
|
||||||
|
class Tiling {
|
||||||
|
|
||||||
|
unsigned _valK;
|
||||||
|
float _valRcpSlice;
|
||||||
|
unsigned _valBits;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
Tiling(unsigned k) :
|
||||||
|
_valK(k),
|
||||||
|
_valRcpSlice(k / Radians::twice_pi()) {
|
||||||
|
_valBits = ceil(log2(getTileCount()));
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned getAzimuthalTiles() const { return _valK; }
|
||||||
|
unsigned getAltitudinalTiles() const { return _valK / 2 + 1; }
|
||||||
|
unsigned getTileIndexBits() const { return _valBits; }
|
||||||
|
|
||||||
|
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 / _valRcpSlice;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
unsigned discreteAngle(float unsigned_angle) const {
|
||||||
|
return unsigned(round(unsigned_angle * _valRcpSlice));
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned discreteAzimuth(float a) const {
|
||||||
|
return discreteAngle(a) % _valK;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned discreteAltitude(float a) const {
|
||||||
|
return min(getAltitudinalTiles() - 1,
|
||||||
|
discreteAngle(a + Radians::half_pi()) );
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
} // anonymous namespace
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
52
interface/src/starfield/renderer/VertexOrder.h
Normal file
52
interface/src/starfield/renderer/VertexOrder.h
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
//
|
||||||
|
// starfield/renderer/VertexOrder.h
|
||||||
|
// interface
|
||||||
|
//
|
||||||
|
// Created by Tobias Schwinger on 3/22/13.
|
||||||
|
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#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.
|
||||||
|
*/
|
||||||
|
class VertexOrder : public Radix2IntegerScanner<unsigned>
|
||||||
|
{
|
||||||
|
Tiling _objTiling;
|
||||||
|
|
||||||
|
typedef Radix2IntegerScanner<unsigned> base;
|
||||||
|
public:
|
||||||
|
|
||||||
|
explicit VertexOrder(Tiling const& tiling) :
|
||||||
|
|
||||||
|
base(tiling.getTileIndexBits() + BrightnessBits),
|
||||||
|
_objTiling(tiling) {
|
||||||
|
}
|
||||||
|
|
||||||
|
bool bit(InputVertex const& v, state_type const& s) const {
|
||||||
|
|
||||||
|
// inspect (tile_index, brightness) tuples
|
||||||
|
unsigned key = getBrightness(v.getColor()) ^ BrightnessMask;
|
||||||
|
key |= _objTiling.getTileIndex(
|
||||||
|
v.getAzimuth(), v.getAltitude()) << BrightnessBits;
|
||||||
|
return base::bit(key, s);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // anonymous namespace
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
Loading…
Reference in a new issue