mirror of
https://github.com/lubosz/overte.git
synced 2025-04-17 02:16:42 +02:00
starfield and numerous utility components, initial checkin
This commit is contained in:
parent
f8f098cf2c
commit
4444bcf26e
10 changed files with 1603 additions and 0 deletions
20
interface/resources/gen_stars.py
Normal file
20
interface/resources/gen_stars.py
Normal file
|
@ -0,0 +1,20 @@
|
|||
|
||||
from random import random,randint
|
||||
from sys import argv
|
||||
|
||||
n = 1000
|
||||
|
||||
if len(argv) > 1:
|
||||
n = int(argv[1])
|
||||
|
||||
for i in range(n):
|
||||
# color
|
||||
w = randint(30,randint(40,255))
|
||||
r = max(0,min(255,w + randint(-10,70)))
|
||||
g = max(0,min(255,w + randint(-20,60)))
|
||||
b = max(0,min(255,w + randint(-10,100)))
|
||||
# position
|
||||
azi = random() * 360
|
||||
alt = random() * 90
|
||||
print "%f %f #%02x%02x%02x" % (azi,alt,r,g,b)
|
||||
|
130
interface/src/FieldOfView.cpp
Normal file
130
interface/src/FieldOfView.cpp
Normal file
|
@ -0,0 +1,130 @@
|
|||
//
|
||||
// FieldOfView.cpp
|
||||
// interface
|
||||
//
|
||||
// Created by Tobias Schwinger on 3/21/13.
|
||||
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#include "FieldOfView.h"
|
||||
|
||||
#include <math.h>
|
||||
#include <algorithm>
|
||||
#include <glm/gtc/matrix_transform.hpp>
|
||||
#include <glm/gtc/matrix_inverse.hpp>
|
||||
|
||||
using namespace glm;
|
||||
|
||||
FieldOfView::FieldOfView()
|
||||
: mat_orientation(mat4(1.0f)),
|
||||
vec_frustum_low(vec3(-1.0f,-1.0f,-1.0f)),
|
||||
vec_frustum_high(vec3(1.0f,1.0f,1.0f)),
|
||||
val_width(256.0f),
|
||||
val_height(256.0f),
|
||||
val_angle(0.61),
|
||||
val_zoom(1.0f),
|
||||
enm_aspect_balancing(expose_less)
|
||||
{
|
||||
}
|
||||
|
||||
mat4 FieldOfView::getViewerScreenXform() const
|
||||
{
|
||||
mat4 projection;
|
||||
vec3 low, high;
|
||||
calcGlFrustum(low, high);
|
||||
|
||||
// perspective projection? determine correct near distance
|
||||
if (val_angle != 0.0f)
|
||||
{
|
||||
projection = translate(
|
||||
frustum(low.x, high.x, low.y, high.y, low.z, high.z),
|
||||
vec3(0.f, 0.f, -low.z) );
|
||||
}
|
||||
else
|
||||
{
|
||||
projection = ortho(low.x, high.x, low.y, high.y, low.z, high.z);
|
||||
}
|
||||
|
||||
return projection;
|
||||
}
|
||||
|
||||
mat4 FieldOfView::getWorldViewerXform() const
|
||||
{
|
||||
return translate(affineInverse(mat_orientation),
|
||||
vec3(0.0f, 0.0f, -vec_frustum_high.z) );
|
||||
}
|
||||
|
||||
mat4 FieldOfView::getWorldScreenXform() const
|
||||
{
|
||||
return translate(
|
||||
getViewerScreenXform() * affineInverse(mat_orientation),
|
||||
vec3(0.0f, 0.0f, -vec_frustum_high.z) );
|
||||
}
|
||||
|
||||
mat4 FieldOfView::getViewerWorldXform() const
|
||||
{
|
||||
vec3 n_translate = vec3(0.0f, 0.0f, vec_frustum_high.z);
|
||||
|
||||
return translate(
|
||||
translate(mat4(1.0f), n_translate)
|
||||
* mat_orientation, -n_translate );
|
||||
}
|
||||
|
||||
float FieldOfView::getPixelSize() const
|
||||
{
|
||||
vec3 low, high;
|
||||
calcGlFrustum(low, high);
|
||||
|
||||
return std::min(
|
||||
abs(high.x - low.x) / val_width,
|
||||
abs(high.y - low.y) / val_height);
|
||||
}
|
||||
|
||||
void FieldOfView::calcGlFrustum(vec3& low, vec3& high) const
|
||||
{
|
||||
low = vec_frustum_low;
|
||||
high = vec_frustum_high;
|
||||
|
||||
// apply zoom
|
||||
float inv_zoom = 1.0f / val_zoom;
|
||||
float ax = (low.x + high.x) / 2.0f, ay = (low.y + high.y) / 2.0f;
|
||||
low.x = (low.x - ax) * inv_zoom + ax;
|
||||
high.x = (high.x - ax) * inv_zoom + ax;
|
||||
low.y = (low.y - ay) * inv_zoom + ay;
|
||||
high.y = (high.y - ay) * inv_zoom + ay;
|
||||
low.z = (low.z - high.z) * inv_zoom + high.z;
|
||||
|
||||
// balance aspect
|
||||
if (enm_aspect_balancing != stretch)
|
||||
{
|
||||
float f_aspect = (high.x - low.x) / (high.y - low.y);
|
||||
float vp_aspect = val_width / val_height;
|
||||
|
||||
float adj;
|
||||
if ((enm_aspect_balancing == expose_more)
|
||||
!= (f_aspect > vp_aspect))
|
||||
{
|
||||
// expose_more -> f_aspect <= vp_aspect <=> adj >= 1
|
||||
// expose_less -> f_aspect > vp_aspect <=> adj < 1
|
||||
adj = vp_aspect / f_aspect;
|
||||
low.x *= adj;
|
||||
high.x *= adj;
|
||||
}
|
||||
else
|
||||
{
|
||||
// expose_more -> f_aspect > vp_aspect <=> adj > 1
|
||||
// expose_less -> f_aspect <= vp_aspect <=> adj <= 1
|
||||
adj = f_aspect / vp_aspect;
|
||||
low.y *= adj;
|
||||
high.y *= adj;
|
||||
}
|
||||
}
|
||||
|
||||
float w = high.x - low.x, h = high.y - low.y;
|
||||
|
||||
high.z -= low.z;
|
||||
low.z = val_angle == 0.0f ? 0.0f :
|
||||
sqrt(w*w+h*h) * 0.5f / tan(val_angle * 0.5f);
|
||||
high.z += low.z;
|
||||
}
|
||||
|
115
interface/src/FieldOfView.h
Normal file
115
interface/src/FieldOfView.h
Normal file
|
@ -0,0 +1,115 @@
|
|||
//
|
||||
// FieldOfView.cpp
|
||||
// interface
|
||||
//
|
||||
// Created by Tobias Schwinger on 3/21/13.
|
||||
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef __interface__FieldOfView__
|
||||
#define __interface__FieldOfView__
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
/**
|
||||
* Viewing parameter encapsulation.
|
||||
*/
|
||||
class FieldOfView
|
||||
{
|
||||
glm::mat4 mat_orientation;
|
||||
glm::vec3 vec_frustum_low;
|
||||
glm::vec3 vec_frustum_high;
|
||||
float val_width;
|
||||
float val_height;
|
||||
float val_angle;
|
||||
float val_zoom;
|
||||
int enm_aspect_balancing;
|
||||
public:
|
||||
|
||||
FieldOfView();
|
||||
|
||||
// mutators
|
||||
|
||||
FieldOfView& setFrustum(glm::vec3 const& low, glm::vec3 const& high)
|
||||
{ vec_frustum_low = low; vec_frustum_high = high; return *this; }
|
||||
|
||||
FieldOfView& setOrientation(glm::mat4 const& matrix)
|
||||
{ mat_orientation = matrix; return *this; }
|
||||
|
||||
FieldOfView& setPerspective(float angle)
|
||||
{ val_angle = angle; return *this; }
|
||||
|
||||
FieldOfView& setResolution(unsigned width, unsigned height)
|
||||
{ val_width = width; val_height = height; return *this; }
|
||||
|
||||
FieldOfView& setZoom(float factor)
|
||||
{ val_zoom = factor; return *this; }
|
||||
|
||||
enum aspect_balancing
|
||||
{
|
||||
expose_more,
|
||||
expose_less,
|
||||
stretch
|
||||
};
|
||||
|
||||
FieldOfView& setAspectBalancing(aspect_balancing v)
|
||||
{ enm_aspect_balancing = v; return *this; }
|
||||
|
||||
// dumb accessors
|
||||
|
||||
glm::vec3 const& getFrustumLow() const { return vec_frustum_low; }
|
||||
glm::vec3 const& getFrustumHigh() const { return vec_frustum_high; }
|
||||
glm::mat4 const& getOrientation() const { return mat_orientation; }
|
||||
float getWidthInPixels() const { return val_width; }
|
||||
float getHeightInPixels() const { return val_height; }
|
||||
float getPerspective() const { return val_angle; }
|
||||
|
||||
// matrices
|
||||
|
||||
/**
|
||||
* Returns a full transformation matrix to project world coordinates
|
||||
* onto the screen.
|
||||
*/
|
||||
glm::mat4 getWorldScreenXform() const;
|
||||
|
||||
/**
|
||||
* Transforms world coordinates to viewer-relative coordinates.
|
||||
*
|
||||
* This matrix can be used as the modelview matrix in legacy GL code
|
||||
* where the projection matrix is kept separately.
|
||||
*/
|
||||
glm::mat4 getWorldViewerXform() const;
|
||||
|
||||
/**
|
||||
* Returns the transformation to of viewer-relative coordinates back
|
||||
* to world space.
|
||||
*
|
||||
* This matrix can be used to set up a coordinate system for avatar
|
||||
* rendering.
|
||||
*/
|
||||
glm::mat4 getViewerWorldXform() const;
|
||||
|
||||
/**
|
||||
* Returns the transformation of viewer-relative coordinates to the
|
||||
* screen.
|
||||
*
|
||||
* This matrix can be used as the projection matrix in legacy GL code.
|
||||
*/
|
||||
glm::mat4 getViewerScreenXform() const;
|
||||
|
||||
|
||||
// other useful information
|
||||
|
||||
/**
|
||||
* Returns the size of a pixel in world space, that is the minimum
|
||||
* in respect to x/y screen directions.
|
||||
*/
|
||||
float getPixelSize() const;
|
||||
|
||||
private:
|
||||
|
||||
void calcGlFrustum(glm::vec3& low, glm::vec3& high) const;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
679
interface/src/Stars.cpp
Normal file
679
interface/src/Stars.cpp
Normal file
|
@ -0,0 +1,679 @@
|
|||
//
|
||||
// Stars.cpp
|
||||
// interface
|
||||
//
|
||||
// Created by Tobias Schwinger on 3/22/13.
|
||||
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#include "InterfaceConfig.h"
|
||||
|
||||
#include "Stars.h"
|
||||
#include "UrlReader.h"
|
||||
#include "FieldOfView.h"
|
||||
#include "AngleUtils.h"
|
||||
#include "Radix2InplaceSort.h"
|
||||
#include "Radix2IntegerScanner.h"
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <assert.h>
|
||||
#include <math.h>
|
||||
#include <stdio.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#include <new>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
#include <glm/gtc/type_ptr.hpp>
|
||||
#include <glm/gtc/matrix_inverse.hpp>
|
||||
|
||||
#define DEG2RAD 0.017453292519f
|
||||
|
||||
/* Data pipeline
|
||||
* -------------
|
||||
*
|
||||
* ->> readInput -(load)--+---- (get brightness & sort) ---> brightness LUT
|
||||
* | |
|
||||
* ->> setResolution --+ | >extractBrightnessLevels<
|
||||
* V |
|
||||
* (sort by (tile,brightness))
|
||||
* | |
|
||||
* ->> setLOD ---+ | >retile< ->> setLOD --> (just parameterize
|
||||
* V V renderer when on-GPU
|
||||
* (filter by max-LOD brightness, data suffices)
|
||||
* build tile info for rendering)
|
||||
* | |
|
||||
* V >recreateRenderer<
|
||||
* (set new renderer)/
|
||||
*
|
||||
*
|
||||
* (process), ->> entry point, ---> data flow, >internal routine<
|
||||
*
|
||||
*
|
||||
* Open issues
|
||||
* -----------
|
||||
*
|
||||
* o FOV culling is too eager - gotta revisit
|
||||
* o LOD adjustment in a living renderer still needs to be coded (planned)
|
||||
* o input limit (while keeping the brightest) needs to be coded (planned)
|
||||
* o atomics/mutexes need to be added as annotated in the source to allow
|
||||
* concurrent threads to pull the strings to e.g. have a low priority
|
||||
* thread run the data pipeline for update -- rendering is wait-free
|
||||
*/
|
||||
|
||||
namespace
|
||||
{
|
||||
using std::swap;
|
||||
using std::min;
|
||||
using std::max;
|
||||
|
||||
using glm::mat4;
|
||||
using glm::value_ptr;
|
||||
|
||||
class InputVertex
|
||||
{
|
||||
unsigned val_color;
|
||||
float val_azimuth;
|
||||
float val_altitude;
|
||||
public:
|
||||
|
||||
InputVertex(float azimuth, float altitude, unsigned color)
|
||||
{
|
||||
val_color = color >> 16 & 0xffu | color & 0xff00u |
|
||||
color << 16 & 0xff0000u | 0xff000000u;
|
||||
|
||||
angleHorizontalPolar<Degrees>(azimuth, altitude);
|
||||
|
||||
val_azimuth = azimuth;
|
||||
val_altitude = altitude;
|
||||
}
|
||||
|
||||
float getAzimuth() const { return val_azimuth; }
|
||||
float getAltitude() const { return val_altitude; }
|
||||
unsigned getColor() const { return val_color; }
|
||||
};
|
||||
|
||||
typedef std::vector<InputVertex> InputVertices;
|
||||
|
||||
class Loader : UrlReader
|
||||
{
|
||||
InputVertices* ptr_vertices;
|
||||
unsigned val_limit;
|
||||
|
||||
unsigned val_lineno;
|
||||
char const* str_actual_url;
|
||||
public:
|
||||
|
||||
bool loadVertices(
|
||||
InputVertices& destination, char const* url, unsigned limit)
|
||||
{
|
||||
ptr_vertices = & destination;
|
||||
val_limit = limit;
|
||||
str_actual_url = url; // in case we fail early
|
||||
|
||||
if (! UrlReader::readUrl(url, *this))
|
||||
{
|
||||
fprintf(stderr, "%s:%d: %s\n",
|
||||
str_actual_url, val_lineno, getError());
|
||||
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
friend class UrlReader;
|
||||
|
||||
void begin(char const* url,
|
||||
char const* type, int64_t size, int64_t stardate)
|
||||
{
|
||||
val_lineno = 0u;
|
||||
str_actual_url = url; // new value in http redirect
|
||||
|
||||
ptr_vertices->clear();
|
||||
ptr_vertices->reserve(val_limit);
|
||||
}
|
||||
|
||||
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;
|
||||
++val_lineno;
|
||||
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 (ptr_vertices->size() < val_limit)
|
||||
ptr_vertices->push_back( InputVertex(azi, alt, c) );
|
||||
|
||||
// TODO handle limit by switching to a minheap when
|
||||
// buffer is full
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf(stderr, "Stars.cpp:%d: Bad input from %s\n",
|
||||
val_lineno, str_actual_url);
|
||||
}
|
||||
|
||||
}
|
||||
return consumed;
|
||||
}
|
||||
|
||||
void end(bool ok)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
typedef uint16_t BrightnessLevel;
|
||||
typedef std::vector<BrightnessLevel> BrightnessLevels;
|
||||
const unsigned BrightnessBits = 16u;
|
||||
|
||||
template< class Vertex >
|
||||
BrightnessLevel getBrightness(Vertex const& v)
|
||||
{
|
||||
unsigned c = v.getColor();
|
||||
unsigned r = (c >> 16) & 0xff;
|
||||
unsigned g = (c >> 8) & 0xff;
|
||||
unsigned b = c & 0xff;
|
||||
return BrightnessLevel((r*r+g*g+b*b) >> 1);
|
||||
}
|
||||
|
||||
struct BrightnessSortScanner : Radix2IntegerScanner<BrightnessLevel>
|
||||
{
|
||||
typedef Radix2IntegerScanner<BrightnessLevel> Base;
|
||||
BrightnessSortScanner() : Base(BrightnessBits) { }
|
||||
bool bit(BrightnessLevel const& k, state_type& s)
|
||||
{ return ! Base::bit(k,s); }
|
||||
};
|
||||
|
||||
void extractBrightnessLevels(BrightnessLevels& dst, InputVertices const& src)
|
||||
{
|
||||
dst.clear();
|
||||
dst.reserve(src.size());
|
||||
for (InputVertices::const_iterator i =
|
||||
src.begin(), e = src.end(); i != e; ++i)
|
||||
dst.push_back( getBrightness(*i) );
|
||||
|
||||
radix2InplaceSort(dst.begin(), dst.end(), BrightnessSortScanner());
|
||||
}
|
||||
|
||||
|
||||
template< class Unit >
|
||||
class HorizontalTiling
|
||||
{
|
||||
unsigned val_k;
|
||||
float val_rcp_slice;
|
||||
unsigned val_bits;
|
||||
public:
|
||||
|
||||
HorizontalTiling(unsigned k)
|
||||
: val_k(k), val_rcp_slice(k / Unit::twice_pi())
|
||||
{
|
||||
val_bits = ceil(log2(getTileCount() ));
|
||||
}
|
||||
|
||||
unsigned getAzimuthalTiles() const { return val_k; }
|
||||
unsigned getAltitudinalTiles() const { return val_k / 2 + 1; }
|
||||
unsigned getTileIndexBits() const { return val_bits; }
|
||||
|
||||
unsigned getTileCount() const
|
||||
{
|
||||
return getAzimuthalTiles() * getAltitudinalTiles();
|
||||
}
|
||||
|
||||
unsigned getTileIndex(float azimuth, float altitude) const
|
||||
{
|
||||
unsigned result;
|
||||
return discreteAngle(azimuth) % val_k +
|
||||
discreteAngle(altitude + Unit::half_pi()) * val_k;
|
||||
}
|
||||
|
||||
unsigned getTileIndex(InputVertex const& v) const
|
||||
{
|
||||
return getTileIndex(v.getAzimuth(), v.getAltitude());
|
||||
}
|
||||
|
||||
unsigned discreteAngle(float unsigned_angle) const
|
||||
{
|
||||
return unsigned(round(unsigned_angle * val_rcp_slice));
|
||||
}
|
||||
};
|
||||
|
||||
class TileSortScanner : public Radix2IntegerScanner<unsigned>
|
||||
{
|
||||
HorizontalTiling<Degrees> obj_tiling;
|
||||
|
||||
typedef Radix2IntegerScanner<unsigned> Base;
|
||||
public:
|
||||
|
||||
explicit TileSortScanner(HorizontalTiling<Degrees> const& tiling)
|
||||
: Base(tiling.getTileIndexBits() + BrightnessBits),
|
||||
obj_tiling(tiling)
|
||||
{ }
|
||||
|
||||
bool bit(InputVertex const& v, state_type const& s) const
|
||||
{
|
||||
// inspect (tile_index, brightness) tuples
|
||||
unsigned key = getBrightness(v);
|
||||
key |= obj_tiling.getTileIndex(v) << BrightnessBits;
|
||||
return Base::bit(key, s);
|
||||
}
|
||||
};
|
||||
|
||||
struct Tile
|
||||
{
|
||||
uint16_t offset;
|
||||
uint16_t count; // according to previous lod setting
|
||||
};
|
||||
|
||||
|
||||
struct GpuVertex
|
||||
{
|
||||
unsigned val_color;
|
||||
float val_x;
|
||||
float val_y;
|
||||
float val_z;
|
||||
//
|
||||
|
||||
GpuVertex() { }
|
||||
|
||||
GpuVertex(InputVertex const& in)
|
||||
{
|
||||
val_color = in.getColor();
|
||||
float azimuth = in.getAzimuth() * DEG2RAD;
|
||||
float altitude = in.getAltitude() * DEG2RAD;
|
||||
|
||||
// ground vector in x/z plane...
|
||||
float gx = sin(azimuth);
|
||||
float gz = -cos(azimuth);
|
||||
|
||||
// ...elevated in y direction by altitude
|
||||
float exz = cos(altitude);
|
||||
val_x = gx * exz;
|
||||
val_y = sin(altitude);
|
||||
val_z = gz * exz;
|
||||
|
||||
//fprintf(stderr, "Stars.cpp: GpuVertex created (%x,%f,%f,%f)\n", val_color, val_x, val_y, val_z);
|
||||
}
|
||||
|
||||
unsigned getColor() const { return val_color; }
|
||||
};
|
||||
|
||||
class Renderer
|
||||
{
|
||||
GpuVertex* ptr_data;
|
||||
Tile* ptr_tiles;
|
||||
BrightnessLevel val_brightness;
|
||||
unsigned val_tile_resolution;
|
||||
GLint* ptr_batch_offs;
|
||||
GLsizei* ptr_batch_count;
|
||||
GLuint hnd_vao;
|
||||
public:
|
||||
|
||||
Renderer(InputVertices const& src, size_t n,
|
||||
unsigned k, BrightnessLevel b, BrightnessLevel b_max)
|
||||
: ptr_data(0l), ptr_tiles(0l),
|
||||
val_brightness(b), val_tile_resolution(k)
|
||||
{
|
||||
|
||||
HorizontalTiling<Degrees> tiling(k);
|
||||
size_t n_tiles = tiling.getTileCount();
|
||||
|
||||
ptr_data = new GpuVertex[n];
|
||||
ptr_tiles = new Tile[n_tiles];
|
||||
// TODO tighten bounds and save some memory
|
||||
ptr_batch_offs = new GLint[n_tiles];
|
||||
ptr_batch_count = new GLsizei[n_tiles];
|
||||
|
||||
size_t vertex_index = 0u, curr_tile_index = 0u, count_active = 0u;
|
||||
|
||||
for (InputVertices::const_iterator i =
|
||||
src.begin(), e = src.end(); i != e; ++i)
|
||||
{
|
||||
BrightnessLevel bv = getBrightness(*i);
|
||||
// filter by alloc brightness
|
||||
if (bv >= b_max)
|
||||
{
|
||||
size_t tile_index = tiling.getTileIndex(*i);
|
||||
|
||||
assert(tile_index >= curr_tile_index);
|
||||
|
||||
// moved to another tile?
|
||||
if (tile_index != curr_tile_index)
|
||||
{
|
||||
Tile* t = ptr_tiles + curr_tile_index;
|
||||
Tile* t_last = ptr_tiles + tile_index;
|
||||
|
||||
// set count of active vertices (upcoming lod)
|
||||
t->count = count_active;
|
||||
|
||||
// generate skipped entries
|
||||
for(size_t offs = t_last->offset; ++t != t_last ;)
|
||||
t->offset = offs, t->count = 0u;
|
||||
|
||||
// set offset of the beginning tile`
|
||||
t_last->offset = vertex_index;
|
||||
|
||||
curr_tile_index = tile_index;
|
||||
count_active = 0u;
|
||||
}
|
||||
|
||||
if (bv >= b)
|
||||
++count_active;
|
||||
|
||||
//fprintf(stderr, "Stars.cpp: Vertex %d on tile #%d\n", vertex_index, tile_index);
|
||||
|
||||
// write converted vertex
|
||||
ptr_data[vertex_index++] = *i;
|
||||
}
|
||||
}
|
||||
assert(vertex_index == n);
|
||||
// finish last tile (see above)
|
||||
Tile* t = ptr_tiles + curr_tile_index;
|
||||
Tile* t_last = ptr_tiles + n_tiles;
|
||||
t->count = count_active;
|
||||
for(; ++t != t_last ;)
|
||||
t->offset = vertex_index, t->count = 0u;
|
||||
|
||||
// OpenGL upload
|
||||
|
||||
GLuint vbo;
|
||||
glGenBuffers(1, & vbo);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, vbo);
|
||||
glBufferData(GL_ARRAY_BUFFER,
|
||||
n * sizeof(GpuVertex), ptr_data, GL_STATIC_DRAW);
|
||||
|
||||
glGenVertexArrays(1, & hnd_vao);
|
||||
glBindVertexArray(hnd_vao);
|
||||
glInterleavedArrays(GL_C4UB_V3F, sizeof(GpuVertex), 0l);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, vbo);
|
||||
|
||||
glBindVertexArray(0);
|
||||
|
||||
}
|
||||
|
||||
~Renderer()
|
||||
{
|
||||
delete[] ptr_data;
|
||||
delete[] ptr_tiles;
|
||||
delete[] ptr_batch_count;
|
||||
delete[] ptr_batch_offs;
|
||||
glDeleteVertexArrays(1, & hnd_vao);
|
||||
}
|
||||
|
||||
void render(FieldOfView fov, BrightnessLevel lod)
|
||||
{
|
||||
mat4 local_space = fov.getOrientation();
|
||||
HorizontalTiling<Radians> tiling(val_tile_resolution);
|
||||
|
||||
// get z direction
|
||||
float x = local_space[2][0];
|
||||
float y = local_space[2][1];
|
||||
float z = local_space[2][2];
|
||||
|
||||
// to polar
|
||||
float azimuth = atan2(x,-z) + Radians::pi();
|
||||
float altitude = atan2(y, sqrt(x*x+z*z));
|
||||
|
||||
fprintf(stderr, "Stars.cpp: viewer azimuth = %f, altitude = %f\n", azimuth, altitude);
|
||||
|
||||
// half diagonal perspective angle
|
||||
float hd_pers = fov.getPerspective() * 0.5f;
|
||||
|
||||
unsigned azi_dim = tiling.getAzimuthalTiles();
|
||||
unsigned alt_dim = tiling.getAltitudinalTiles();
|
||||
|
||||
// determine tile range in azimuthal direction (modulated)
|
||||
unsigned azi_from = tiling.discreteAngle(
|
||||
angleUnsignedNormal<Radians>(azimuth - hd_pers) ) % azi_dim;
|
||||
unsigned azi_to = (1 + tiling.discreteAngle(
|
||||
angleUnsignedNormal<Radians>(azimuth + hd_pers) )) % azi_dim;
|
||||
|
||||
// determine tile range in altitudinal direction (clamped)
|
||||
unsigned alt_from = tiling.discreteAngle(
|
||||
max(-Radians::half_pi(),min(Radians::half_pi(),
|
||||
altitude - hd_pers)) + Radians::half_pi() );
|
||||
unsigned alt_to = tiling.discreteAngle(
|
||||
max(-Radians::half_pi(),min(Radians::half_pi(),
|
||||
altitude + hd_pers)) + Radians::half_pi() );
|
||||
|
||||
// iterate the grid...
|
||||
unsigned n_batches = 0u;
|
||||
|
||||
fprintf(stderr, "Stars.cpp: grid dimensions: %d x %d\n", azi_dim, alt_dim);
|
||||
fprintf(stderr, "Stars.cpp: grid range: [%d;%d) [%d;%d]\n", azi_from, azi_to, alt_from, alt_to);
|
||||
|
||||
GLint* offs = ptr_batch_offs, * count = ptr_batch_count;
|
||||
for (unsigned alt = alt_from; alt <= alt_to; ++alt)
|
||||
{
|
||||
for (unsigned azi = azi_from;
|
||||
azi != azi_to; azi = (azi + 1) % azi_dim)
|
||||
{
|
||||
unsigned tile_index = azi + alt * azi_dim;
|
||||
Tile& t = ptr_tiles[tile_index];
|
||||
|
||||
// TODO handle LOD changes by performing a binary
|
||||
// search for the new brightness, if any
|
||||
|
||||
if (! t.count)
|
||||
continue;
|
||||
|
||||
fprintf(stderr, "Stars.cpp: tile %d selected (%d vertices at offset %d)\n", tile_index, t.count, t.offset);
|
||||
|
||||
*offs++ = t.offset;
|
||||
*count++ = t.count;
|
||||
|
||||
++n_batches;
|
||||
}
|
||||
}
|
||||
|
||||
fprintf(stderr, "Stars.cpp: rendering %d-multibatch\n", n_batches);
|
||||
|
||||
// cancel translation
|
||||
local_space[3][0] = 0.0f;
|
||||
local_space[3][1] = 0.0f;
|
||||
local_space[3][2] = 0.0f;
|
||||
|
||||
// and setup modelview matrix
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
glPushMatrix();
|
||||
glLoadMatrixf(glm::value_ptr(
|
||||
fov.setOrientation(local_space).getWorldViewerXform()));
|
||||
|
||||
// render
|
||||
glBindVertexArray(hnd_vao);
|
||||
glPointSize(1.0);
|
||||
glMultiDrawArrays(GL_POINTS,
|
||||
ptr_batch_offs, ptr_batch_count, n_batches);
|
||||
|
||||
// restore state state
|
||||
glBindVertexArray(0);
|
||||
glPopMatrix();
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
struct Stars::body
|
||||
{
|
||||
InputVertices vec_input;
|
||||
unsigned val_tile_resolution;
|
||||
|
||||
BrightnessLevels vec_lod_brightness;
|
||||
BrightnessLevel val_lod_brightness;
|
||||
BrightnessLevel val_lod_max_brightness;
|
||||
float val_lod_current_alloc;
|
||||
float val_lod_low_water_mark;
|
||||
float val_lod_high_water_mark;
|
||||
|
||||
Renderer* ptr_renderer;
|
||||
|
||||
|
||||
body()
|
||||
: val_tile_resolution(12), val_lod_brightness(0),
|
||||
val_lod_max_brightness(0), val_lod_current_alloc(1.0f),
|
||||
val_lod_low_water_mark(0.99f), val_lod_high_water_mark(1.0f),
|
||||
ptr_renderer(0l)
|
||||
{ }
|
||||
|
||||
bool readInput(const char* url, unsigned limit)
|
||||
{
|
||||
InputVertices new_vertices;
|
||||
|
||||
if (! Loader().loadVertices(new_vertices, url, limit))
|
||||
return false;
|
||||
|
||||
BrightnessLevels new_brightness;
|
||||
extractBrightnessLevels(new_brightness, new_vertices);
|
||||
|
||||
{
|
||||
// TODO input mutex
|
||||
|
||||
vec_input.swap(new_vertices);
|
||||
|
||||
try
|
||||
{
|
||||
retile(val_tile_resolution);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
// rollback transaction
|
||||
new_vertices.swap(vec_input);
|
||||
throw;
|
||||
}
|
||||
|
||||
{
|
||||
// TODO lod mutex
|
||||
vec_lod_brightness.swap(new_brightness);
|
||||
}
|
||||
}
|
||||
new_vertices.clear();
|
||||
new_brightness.clear();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void setResolution(unsigned k)
|
||||
{
|
||||
if (k != val_tile_resolution)
|
||||
{
|
||||
// TODO input mutex
|
||||
retile(k);
|
||||
}
|
||||
}
|
||||
|
||||
void retile(unsigned k)
|
||||
{
|
||||
HorizontalTiling<Degrees> tiling(k);
|
||||
TileSortScanner scanner(tiling);
|
||||
radix2InplaceSort(vec_input.begin(), vec_input.end(), scanner);
|
||||
|
||||
recreateRenderer(vec_input.size(), k,
|
||||
val_lod_brightness, val_lod_max_brightness);
|
||||
|
||||
val_tile_resolution = k;
|
||||
}
|
||||
|
||||
void setLOD(float fraction, float overalloc, float realloc)
|
||||
{
|
||||
assert(fraction >= 0.0f && fraction <= 0.0f);
|
||||
assert(overalloc >= realloc && realloc >= 0.0f);
|
||||
assert(overalloc <= 1.0f && realloc <= 1.0f);
|
||||
|
||||
float lwm, hwm;
|
||||
float oa_fraction = min(fraction * (1.0f + oa_fraction), 1.0f);
|
||||
size_t oa_new_size;
|
||||
BrightnessLevel b, b_max;
|
||||
{
|
||||
// TODO lod mutex
|
||||
// Or... There is just one write access, here - so LOD state
|
||||
// could be CMPed as well...
|
||||
lwm = val_lod_low_water_mark;
|
||||
hwm = val_lod_high_water_mark;
|
||||
size_t last = vec_lod_brightness.size() - 1;
|
||||
val_lod_brightness = b =
|
||||
vec_lod_brightness[ size_t(fraction * last) ];
|
||||
oa_new_size = size_t(oa_fraction * last);
|
||||
b_max = vec_lod_brightness[oa_new_size++];
|
||||
}
|
||||
|
||||
// have to reallocate?
|
||||
if (fraction < lwm || fraction > hwm)
|
||||
{
|
||||
// TODO input mutex
|
||||
recreateRenderer(oa_new_size, val_tile_resolution, b, b_max);
|
||||
|
||||
{
|
||||
// TODO lod mutex
|
||||
val_lod_current_alloc = fraction;
|
||||
val_lod_low_water_mark = fraction * (1.0f - realloc);
|
||||
val_lod_high_water_mark = fraction * (1.0f + realloc);
|
||||
val_lod_max_brightness = b_max;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void recreateRenderer(
|
||||
size_t n, unsigned k, BrightnessLevel b, BrightnessLevel b_max)
|
||||
{
|
||||
Renderer* renderer = new Renderer(vec_input, n, k, b, b_max);
|
||||
swap(ptr_renderer, renderer); // TODO make atomic
|
||||
delete renderer; // will be NULL when was in use
|
||||
}
|
||||
|
||||
void render(FieldOfView const& fov)
|
||||
{
|
||||
// check out renderer
|
||||
Renderer* renderer = 0l;
|
||||
swap(ptr_renderer, renderer); // TODO make atomic
|
||||
float new_brightness = val_lod_brightness; // make atomic
|
||||
|
||||
// have it render
|
||||
renderer->render(fov, new_brightness);
|
||||
|
||||
// check in - or dispose if there is a new one
|
||||
// TODO make atomic (CAS)
|
||||
if (! ptr_renderer)
|
||||
ptr_renderer = renderer;
|
||||
else delete renderer;
|
||||
}
|
||||
};
|
||||
|
||||
Stars::Stars() : ptr_body(0l) { ptr_body = new body; }
|
||||
Stars::~Stars() { delete ptr_body; }
|
||||
|
||||
bool Stars::readInput(const char* url, unsigned limit)
|
||||
{ return ptr_body->readInput(url, limit); }
|
||||
|
||||
void Stars::setResolution(unsigned k)
|
||||
{ ptr_body->setResolution(k); }
|
||||
|
||||
void Stars::setLOD(float fraction, float overalloc, float realloc)
|
||||
{ ptr_body->setLOD(fraction, 0.0f, 0.0f); } // TODO enable once implemented
|
||||
|
||||
void Stars::render(FieldOfView const& fov)
|
||||
{ ptr_body->render(fov); }
|
||||
|
||||
|
76
interface/src/Stars.h
Normal file
76
interface/src/Stars.h
Normal file
|
@ -0,0 +1,76 @@
|
|||
//
|
||||
// Stars.h
|
||||
// interface
|
||||
//
|
||||
// Created by Tobias Schwinger on 3/22/13.
|
||||
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef __interface__Stars__
|
||||
#define __interface__Stars__
|
||||
|
||||
#include "FieldOfView.h"
|
||||
|
||||
/**
|
||||
* Starfield rendering component.
|
||||
*/
|
||||
class Stars
|
||||
{
|
||||
struct body;
|
||||
body* ptr_body;
|
||||
|
||||
public:
|
||||
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, unsigned limit = 50000);
|
||||
|
||||
/**
|
||||
* Renders the starfield from a local viewer's perspective.
|
||||
* The parameter specifies the field of view.
|
||||
*/
|
||||
void render(FieldOfView const& fov);
|
||||
|
||||
/**
|
||||
* Sets the resolution for FOV culling.
|
||||
*
|
||||
* The parameter determines the number of tiles in azimuthal
|
||||
* and altitudinal directions.
|
||||
*
|
||||
* GPU resources are updated upon change.
|
||||
*/
|
||||
void setResolution(unsigned k);
|
||||
|
||||
/**
|
||||
* Allows to reduce the number of stars to be rendered given a
|
||||
* fractional LOD value. 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'.
|
||||
*/
|
||||
void setLOD(float fraction,
|
||||
float overalloc = 0.25, float realloc = 0.15);
|
||||
|
||||
private:
|
||||
// don't copy/assign
|
||||
Stars(Stars const&); // = delete;
|
||||
Stars& operator=(Stars const&); // delete;
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
|
82
shared/src/AngleUtils.h
Normal file
82
shared/src/AngleUtils.h
Normal file
|
@ -0,0 +1,82 @@
|
|||
//
|
||||
// AngleUtils.h
|
||||
// hifi
|
||||
//
|
||||
// Created by Tobias Schwinger on 3/23/13.
|
||||
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef __hifi__AngleUtils__
|
||||
#define __hifi__AngleUtils__
|
||||
|
||||
#include <math.h>
|
||||
|
||||
struct Degrees
|
||||
{
|
||||
static float pi() { return 180.0f; }
|
||||
static float twice_pi() { return 360.0f; }
|
||||
static float half_pi() { return 90.0f; }
|
||||
};
|
||||
|
||||
struct Radians
|
||||
{
|
||||
static float pi() { return 3.141592653589793f; }
|
||||
static float twice_pi() { return 6.283185307179586f; }
|
||||
static float half_pi() { return 1.5707963267948966; }
|
||||
};
|
||||
|
||||
struct Rotations
|
||||
{
|
||||
static float pi() { return 0.5f; }
|
||||
static float twice_pi() { return 1.0f; }
|
||||
static float half_pi() { return 0.25f; }
|
||||
};
|
||||
|
||||
/**
|
||||
* Clamps an angle to the range of [-180; 180) degrees.
|
||||
*/
|
||||
template< class Unit >
|
||||
float angleSignedNormal(float a)
|
||||
{
|
||||
float result = remainder(a, Unit::twice_pi());
|
||||
if (result == Unit::pi())
|
||||
result = -Unit::pi();
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clamps an angle to the range of [0; 360) degrees.
|
||||
*/
|
||||
template< class Unit >
|
||||
float angleUnsignedNormal(float a)
|
||||
{
|
||||
return angleSignedNormal<Unit>(a - Unit::pi()) + Unit::pi();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Clamps a polar direction so that azimuth is in the range of [0; 360)
|
||||
* degrees and altitude is in the range of [-90; 90] degrees.
|
||||
*
|
||||
* The so normalized angle still contains ambiguity due to gimbal lock:
|
||||
* Both poles can be reached from any azimuthal direction.
|
||||
*/
|
||||
template< class Unit >
|
||||
void angleHorizontalPolar(float& azimuth, float& altitude)
|
||||
{
|
||||
altitude = angleSignedNormal<Unit>(altitude);
|
||||
if (altitude > Unit::half_pi())
|
||||
{
|
||||
altitude = Unit::pi() - altitude;
|
||||
azimuth = -azimuth;
|
||||
}
|
||||
else if (altitude < -Unit::half_pi())
|
||||
{
|
||||
altitude = -Unit::pi() - altitude;
|
||||
azimuth = -azimuth;
|
||||
}
|
||||
azimuth = angleUnsignedNormal<Unit>(azimuth);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
92
shared/src/Radix2InplaceSort.h
Normal file
92
shared/src/Radix2InplaceSort.h
Normal file
|
@ -0,0 +1,92 @@
|
|||
//
|
||||
// Radix2InplaceSort.h
|
||||
// hifi
|
||||
//
|
||||
// Created by Tobias Schwinger on 3/22/13.
|
||||
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef __hifi__Radix2InplaceSort__
|
||||
#define __hifi__Radix2InplaceSort__
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
|
||||
/**
|
||||
* Sorts the range between two iterators in linear time.
|
||||
*
|
||||
* A Radix2Scanner must be provided to decompose the sorting
|
||||
* criterion into a fixed number of bits.
|
||||
*/
|
||||
template< class Radix2Scanner, typename BidiIterator >
|
||||
void radix2InplaceSort( BidiIterator from, BidiIterator to,
|
||||
Radix2Scanner const& scanner = Radix2Scanner() );
|
||||
|
||||
|
||||
|
||||
template< class S, typename I > struct radix2InplaceSort_impl : private S
|
||||
{
|
||||
radix2InplaceSort_impl(S const& scanner) : S(scanner) { }
|
||||
|
||||
void go(I& from, I& to, typename S::state_type s)
|
||||
{
|
||||
I l(from), r(to);
|
||||
unsigned cl, cr;
|
||||
|
||||
using std::swap;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
// scan from left for set bit
|
||||
for (cl = cr = 0u; l != r ; ++l, ++cl)
|
||||
if (S::bit(*l, s))
|
||||
{
|
||||
// scan from the right for unset bit
|
||||
for (++cr; --r != l ;++cr)
|
||||
if (! S::bit(*r, s))
|
||||
{
|
||||
// swap, continue scanning from left
|
||||
swap(*l, *r);
|
||||
break;
|
||||
}
|
||||
if (l == r)
|
||||
break;
|
||||
}
|
||||
|
||||
// on to the next digit, if any
|
||||
if (! S::advance(s))
|
||||
return;
|
||||
|
||||
// recurse into smaller branch and prepare iterative
|
||||
// processing of the other
|
||||
if (cl < cr)
|
||||
{
|
||||
if (cl > 1u) go(from, l, s);
|
||||
else if (cr <= 1u)
|
||||
return;
|
||||
|
||||
l = from = r;
|
||||
r = to;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (cr > 1u) go(r, to, s);
|
||||
else if (cl <= 1u)
|
||||
return;
|
||||
|
||||
r = to = l;
|
||||
l = from;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template< class Radix2Scanner, typename BidiIterator >
|
||||
void radix2InplaceSort( BidiIterator from, BidiIterator to,
|
||||
Radix2Scanner const& scanner = Radix2Scanner() )
|
||||
{
|
||||
radix2InplaceSort_impl<Radix2Scanner, BidiIterator>(scanner)
|
||||
.go(from, to, scanner.initial_state());
|
||||
}
|
||||
|
||||
#endif /* defined(__hifi__Radix2InplaceSort__) */
|
82
shared/src/Radix2IntegerScanner.h
Normal file
82
shared/src/Radix2IntegerScanner.h
Normal file
|
@ -0,0 +1,82 @@
|
|||
//
|
||||
// Radix2IntegerScanner.h
|
||||
// hifi
|
||||
//
|
||||
// Created by Tobias Schwinger on 3/23/13.
|
||||
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef __hifi__Radix2IntegerScanner__
|
||||
#define __hifi__Radix2IntegerScanner__
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
namespace type_traits // those are needed for the declaration, see below
|
||||
{
|
||||
// Note: There are better / more generally appicable implementations
|
||||
// in C++11, make_signed is missing in TR1 too - so I just use C++98
|
||||
// hacks that get the job done...
|
||||
|
||||
template< typename T > struct is_signed
|
||||
{ static bool const value = T(-1) < T(0); };
|
||||
|
||||
template< typename T, size_t S = sizeof(T) > struct make_unsigned;
|
||||
template< typename T > struct make_unsigned< T, 1 > { typedef uint8_t type; };
|
||||
template< typename T > struct make_unsigned< T, 2 > { typedef uint16_t type; };
|
||||
template< typename T > struct make_unsigned< T, 4 > { typedef uint32_t type; };
|
||||
template< typename T > struct make_unsigned< T, 8 > { typedef uint64_t type; };
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Bit decomposition facility for integers.
|
||||
*/
|
||||
template< typename T,
|
||||
bool _Signed = type_traits::is_signed<T>::value >
|
||||
class Radix2IntegerScanner;
|
||||
|
||||
|
||||
|
||||
template< typename UInt >
|
||||
class Radix2IntegerScanner< UInt, false >
|
||||
{
|
||||
UInt msb;
|
||||
public:
|
||||
|
||||
Radix2IntegerScanner()
|
||||
: msb(~UInt(0) &~ (~UInt(0) >> 1)) { }
|
||||
|
||||
explicit Radix2IntegerScanner(int bits) : msb(1u << (bits - 1)) { }
|
||||
|
||||
|
||||
typedef UInt state_type;
|
||||
|
||||
state_type initial_state() const { return msb; }
|
||||
bool advance(state_type& s) const { return (s >>= 1) != 0u; }
|
||||
|
||||
bool bit(UInt const& v, state_type const& s) const { return !!(v & s); }
|
||||
};
|
||||
|
||||
template< typename Int >
|
||||
class Radix2IntegerScanner< Int, true >
|
||||
{
|
||||
typename type_traits::make_unsigned<Int>::type msb;
|
||||
public:
|
||||
|
||||
Radix2IntegerScanner()
|
||||
: msb(~state_type(0) &~ (~state_type(0) >> 1)) { }
|
||||
|
||||
explicit Radix2IntegerScanner(int bits) : msb(1u << (bits - 1)) { }
|
||||
|
||||
|
||||
typedef typename type_traits::make_unsigned<Int>::type state_type;
|
||||
|
||||
state_type initial_state() const { return msb; }
|
||||
bool advance(state_type& s) const { return (s >>= 1) != 0u; }
|
||||
|
||||
bool bit(Int const& v, state_type const& s) const { return !!((v-msb) & s); }
|
||||
};
|
||||
|
||||
#endif /* defined(__hifi__Radix2IntegerScanner__) */
|
||||
|
77
shared/src/UrlReader.cpp
Normal file
77
shared/src/UrlReader.cpp
Normal file
|
@ -0,0 +1,77 @@
|
|||
//
|
||||
// UrlReader.cpp
|
||||
// hifi
|
||||
//
|
||||
// Created by Tobias Schwinger on 3/21/13.
|
||||
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
|
||||
#include "UrlReader.h"
|
||||
|
||||
#include <new>
|
||||
#include <curl/curl.h>
|
||||
|
||||
size_t const UrlReader::max_read_ahead = CURL_MAX_WRITE_SIZE;
|
||||
|
||||
char const* const UrlReader::success = "UrlReader: Success!";
|
||||
char const* const UrlReader::error_init_failed = "UrlReader: Initialization failed.";
|
||||
char const* const UrlReader::error_aborted = "UrlReader: Processing error.";
|
||||
char const* const UrlReader::error_buffer_overflow = "UrlReader: Buffer overflow.";
|
||||
char const* const UrlReader::error_leftover_input = "UrlReader: Incomplete processing.";
|
||||
|
||||
#define hnd_curl static_cast<CURL*>(ptr_impl)
|
||||
|
||||
UrlReader::UrlReader()
|
||||
: ptr_impl(0l), ptr_ra(0l), str_error(0l)
|
||||
{
|
||||
ptr_ra = new(std::nothrow) char[max_read_ahead];
|
||||
if (! ptr_ra) { str_error = error_init_failed; return; }
|
||||
ptr_impl = curl_easy_init();
|
||||
if (! ptr_impl) { str_error = error_init_failed; return; }
|
||||
curl_easy_setopt(hnd_curl, CURLOPT_NOSIGNAL, 1l);
|
||||
curl_easy_setopt(hnd_curl, CURLOPT_FAILONERROR, 1l);
|
||||
curl_easy_setopt(hnd_curl, CURLOPT_FILETIME, 1l);
|
||||
}
|
||||
|
||||
UrlReader::~UrlReader()
|
||||
{
|
||||
delete ptr_ra;
|
||||
if (! hnd_curl) return;
|
||||
curl_easy_cleanup(hnd_curl);
|
||||
}
|
||||
|
||||
bool UrlReader::perform(char const* url, transfer_callback* cb)
|
||||
{
|
||||
curl_easy_setopt(hnd_curl, CURLOPT_URL, url);
|
||||
curl_easy_setopt(hnd_curl, CURLOPT_WRITEFUNCTION, cb);
|
||||
curl_easy_setopt(hnd_curl, CURLOPT_WRITEDATA, this);
|
||||
|
||||
CURLcode rc = curl_easy_perform(hnd_curl);
|
||||
|
||||
if (rc == CURLE_OK)
|
||||
{
|
||||
while (val_ra_size > 0 && str_error == success)
|
||||
cb(0l, 0, 0, this);
|
||||
}
|
||||
else if (str_error != success)
|
||||
str_error = curl_easy_strerror(rc);
|
||||
|
||||
return rc == CURLE_OK;
|
||||
}
|
||||
|
||||
void UrlReader::getinfo(char const*& url,
|
||||
char const*& type, int64_t& length, int64_t& stardate)
|
||||
{
|
||||
curl_easy_getinfo(hnd_curl, CURLINFO_EFFECTIVE_URL, & url);
|
||||
curl_easy_getinfo(hnd_curl, CURLINFO_CONTENT_TYPE, & type);
|
||||
|
||||
double clen;
|
||||
curl_easy_getinfo(hnd_curl, CURLINFO_CONTENT_LENGTH_DOWNLOAD, & clen);
|
||||
length = static_cast<int64_t>(clen);
|
||||
|
||||
long time;
|
||||
curl_easy_getinfo(hnd_curl, CURLINFO_FILETIME, & time);
|
||||
stardate = time;
|
||||
}
|
||||
|
250
shared/src/UrlReader.h
Normal file
250
shared/src/UrlReader.h
Normal file
|
@ -0,0 +1,250 @@
|
|||
//
|
||||
// UrlReader.h
|
||||
// hifi
|
||||
//
|
||||
// Created by Tobias Schwinger on 3/21/13.
|
||||
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef __hifi__UrlReader__
|
||||
#define __hifi__UrlReader__
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
/**
|
||||
* UrlReader class that encapsulates a context for sequential data retrieval
|
||||
* via URLs. Use one per thread.
|
||||
*/
|
||||
class UrlReader
|
||||
{
|
||||
void* ptr_impl;
|
||||
char* ptr_ra;
|
||||
char const* str_error;
|
||||
void* ptr_stream;
|
||||
size_t val_ra_size;
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* Constructor - performs initialization, never throws.
|
||||
*/
|
||||
UrlReader();
|
||||
|
||||
/**
|
||||
* Destructor - frees resources, never throws.
|
||||
*/
|
||||
~UrlReader();
|
||||
|
||||
/**
|
||||
* Reads data from an URL and forwards it to the instance of a class
|
||||
* fulfilling the ContentStream concept.
|
||||
*
|
||||
* The call protocol on the ContentStream is detailed as follows:
|
||||
*
|
||||
* 1. begin(char const* url,
|
||||
* char const* content_type, uint64_t bytes, uint64_t stardate)
|
||||
*
|
||||
* All information except 'url' is optional; 'content_type' can
|
||||
* be a null pointer - 'bytes' and 'stardate' can be equal to
|
||||
* to 'unavailable'.
|
||||
*
|
||||
* 2. transfer(char* buffer, size_t bytes)
|
||||
*
|
||||
* Called until all data has been received. The number of bytes
|
||||
* actually processed should be returned.
|
||||
* Unprocessed data is stored in an extra buffer whose size is
|
||||
* given by the constant UrlReader::max_read_ahead - it can be
|
||||
* assumed to be reasonably large for on-the-fly parsing.
|
||||
*
|
||||
* 3. end(bool ok)
|
||||
*
|
||||
* Called at the end of the transfer.
|
||||
*
|
||||
* Returns the same success code
|
||||
*/
|
||||
template< class ContentStream >
|
||||
bool readUrl(char const* url, ContentStream& s);
|
||||
|
||||
/**
|
||||
* Returns a pointer to a static C-string that describes the error
|
||||
* condition.
|
||||
*/
|
||||
inline char const* getError() const;
|
||||
|
||||
/**
|
||||
* Can be called by the stream to set a user-defined error string.
|
||||
*/
|
||||
inline void setError(char const* static_c_string);
|
||||
|
||||
/**
|
||||
* Pointer to the C-string returned by a call to 'readUrl' when no
|
||||
* error occurred.
|
||||
*/
|
||||
static char const* const success;
|
||||
|
||||
/**
|
||||
* Pointer to the C-string returned by a call to 'readUrl' when the
|
||||
* initialization has failed.
|
||||
*/
|
||||
static char const* const error_init_failed;
|
||||
|
||||
/**
|
||||
* Pointer to the C-string returned by a call to 'readUrl' when the
|
||||
* transfer has been aborted by the client.
|
||||
*/
|
||||
static char const* const error_aborted;
|
||||
|
||||
/**
|
||||
* Pointer to the C-string returned by a call to 'readUrl' when
|
||||
* leftover input from incomplete processing caused a buffer
|
||||
* overflow.
|
||||
*/
|
||||
static char const* const error_buffer_overflow;
|
||||
|
||||
/**
|
||||
* Pointer to the C-string return by a call to 'readUrl' when the
|
||||
* input provided was not completely consumed.
|
||||
*/
|
||||
static char const* const error_leftover_input;
|
||||
|
||||
/**
|
||||
* Constant of the maximum number of bytes that are buffered
|
||||
* between invocations of 'transfer'.
|
||||
*/
|
||||
static size_t const max_read_ahead;
|
||||
|
||||
/**
|
||||
* Constant representing absent information in the call to the
|
||||
* 'begin' member function of the target stream.
|
||||
*/
|
||||
static int const unavailable = -1;
|
||||
|
||||
/**
|
||||
* Constant for requesting to abort the current transfer when
|
||||
* returned by the 'transfer' member function of the target stream.
|
||||
*/
|
||||
static size_t const abort = ~0u;
|
||||
|
||||
private:
|
||||
// instances of this class shall not be copied
|
||||
UrlReader(UrlReader const&); // = delete;
|
||||
UrlReader& operator=(UrlReader const&); // = delete;
|
||||
|
||||
// entrypoints to compiled code
|
||||
|
||||
typedef size_t transfer_callback(char*, size_t, size_t, void*);
|
||||
|
||||
bool perform(char const* url, transfer_callback* transfer);
|
||||
|
||||
void getinfo(char const*& url,
|
||||
char const*& type, int64_t& length, int64_t& stardate);
|
||||
|
||||
// synthesized callback
|
||||
|
||||
template< class Stream >
|
||||
static size_t callback_template(
|
||||
char *input, size_t size, size_t nmemb, void* thiz);
|
||||
};
|
||||
|
||||
template< class ContentStream >
|
||||
bool UrlReader::readUrl(char const* url, ContentStream& s)
|
||||
{
|
||||
if (! ptr_impl) return false;
|
||||
str_error = success;
|
||||
ptr_stream = & s;
|
||||
val_ra_size = ~size_t(0);
|
||||
this->perform(url, & callback_template<ContentStream>);
|
||||
s.end(str_error == success);
|
||||
return str_error == success;
|
||||
}
|
||||
|
||||
inline char const* UrlReader::getError() const { return this->str_error; }
|
||||
|
||||
inline void UrlReader::setError(char const* static_c_string)
|
||||
{
|
||||
if (this->str_error != success)
|
||||
this->str_error = static_c_string;
|
||||
}
|
||||
|
||||
template< class Stream >
|
||||
size_t UrlReader::callback_template(
|
||||
char *input, size_t size, size_t nmemb, void* thiz)
|
||||
{
|
||||
size *= nmemb;
|
||||
|
||||
UrlReader* me = static_cast<UrlReader*>(thiz);
|
||||
Stream* stream = static_cast<Stream*>(me->ptr_stream);
|
||||
|
||||
// first call?
|
||||
if (me->val_ra_size == ~size_t(0))
|
||||
{
|
||||
me->val_ra_size = 0u;
|
||||
// extract meta information and call 'begin'
|
||||
char const* url, * type;
|
||||
int64_t length, stardate;
|
||||
me->getinfo(url, type, length, stardate);
|
||||
stream->begin(url, type, length, stardate);
|
||||
}
|
||||
|
||||
size_t input_offset = 0u;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
char* buffer = input + input_offset;
|
||||
size_t bytes = size - input_offset;
|
||||
|
||||
// data in extra buffer?
|
||||
if (me->val_ra_size > 0)
|
||||
{
|
||||
// fill extra buffer with beginning of input
|
||||
size_t fill = max_read_ahead - me->val_ra_size;
|
||||
if (bytes < fill) fill = bytes;
|
||||
memcpy(me->ptr_ra + me->val_ra_size, buffer, fill);
|
||||
// use extra buffer for next transfer
|
||||
buffer = me->ptr_ra;
|
||||
bytes = me->val_ra_size + fill;
|
||||
input_offset += fill;
|
||||
}
|
||||
|
||||
// call 'transfer'
|
||||
size_t processed = stream->transfer(buffer, bytes);
|
||||
if (processed == abort)
|
||||
{
|
||||
me->setError(error_aborted);
|
||||
return 0u;
|
||||
}
|
||||
else if (! processed && ! input)
|
||||
{
|
||||
me->setError(error_leftover_input);
|
||||
return 0u;
|
||||
}
|
||||
size_t unprocessed = bytes - processed;
|
||||
|
||||
// can switch to input buffer, now?
|
||||
if (buffer == me->ptr_ra && unprocessed <= input_offset)
|
||||
{
|
||||
me->val_ra_size = 0u;
|
||||
input_offset -= unprocessed;
|
||||
}
|
||||
else // no? unprocessed data -> extra buffer
|
||||
{
|
||||
if (unprocessed > max_read_ahead)
|
||||
{
|
||||
me->setError(error_buffer_overflow);
|
||||
return 0;
|
||||
}
|
||||
me->val_ra_size = unprocessed;
|
||||
memmove(me->ptr_ra, buffer + processed, unprocessed);
|
||||
|
||||
if (input_offset == size || buffer != me->ptr_ra)
|
||||
{
|
||||
return size;
|
||||
}
|
||||
}
|
||||
} // for
|
||||
}
|
||||
|
||||
#endif /* defined(__hifi__UrlReader__) */
|
||||
|
Loading…
Reference in a new issue