mirror of
https://github.com/overte-org/overte.git
synced 2025-04-20 12:04:18 +02:00
commit
412dcc3b87
31 changed files with 107915 additions and 21 deletions
|
@ -8,6 +8,7 @@ set(PORTAUDIO_DIR ${CMAKE_CURRENT_SOURCE_DIR}/external/portaudio)
|
|||
|
||||
project(interface)
|
||||
|
||||
|
||||
if (APPLE)
|
||||
set(GL_HEADERS "#include <GLUT/glut.h>\n#include <OpenGL/glext.h>")
|
||||
else (APPLE)
|
||||
|
@ -27,6 +28,9 @@ configure_file(InterfaceConfig.h.in ${PROJECT_BINARY_DIR}/includes/InterfaceConf
|
|||
# grab the implementation and header files from src dir
|
||||
file(GLOB INTERFACE_SRCS src/*.cpp src/*.h)
|
||||
|
||||
# project subdirectories
|
||||
add_subdirectory(src/starfield)
|
||||
|
||||
if (APPLE)
|
||||
# set how the icon shows up in the Info.plist file
|
||||
SET(MACOSX_BUNDLE_ICON_FILE interface.icns)
|
||||
|
@ -51,12 +55,20 @@ link_hifi_shared_library(interface)
|
|||
# find required libraries
|
||||
find_package(GLM REQUIRED)
|
||||
find_package(LodePNG REQUIRED)
|
||||
find_package(CURL REQUIRED)
|
||||
|
||||
# include headers for external libraries and InterfaceConfig.
|
||||
include_directories(
|
||||
${PROJECT_SOURCE_DIR}/src
|
||||
${PROJECT_BINARY_DIR}/includes
|
||||
${GLM_INCLUDE_DIRS}
|
||||
${LODEPNG_INCLUDE_DIRS}
|
||||
${CURL_INCLUDE_DIRS}
|
||||
)
|
||||
|
||||
# link target to common, external libraries
|
||||
target_link_libraries(interface
|
||||
${CURL_LIBRARY}
|
||||
)
|
||||
|
||||
if (NOT APPLE)
|
||||
|
@ -88,7 +100,7 @@ if (WIN32)
|
|||
)
|
||||
else (WIN32)
|
||||
target_link_libraries(interface ${LODEPNG_LIBRARY})
|
||||
|
||||
|
||||
# include PortAudio as external project
|
||||
include(ExternalProject)
|
||||
set(PORTAUDIO_PROJ_DIR external/portaudio)
|
||||
|
@ -133,4 +145,4 @@ endif (WIN32)
|
|||
INSTALL(TARGETS interface
|
||||
BUNDLE DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/install COMPONENT Runtime
|
||||
RUNTIME DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/install COMPONENT Runtime
|
||||
)
|
||||
)
|
||||
|
|
72
interface/resources/gen_stars.py
Normal file
72
interface/resources/gen_stars.py
Normal file
|
@ -0,0 +1,72 @@
|
|||
#
|
||||
# gen_stars.py
|
||||
# interface
|
||||
#
|
||||
# Created by Tobias Schwinger on 3/22/13.
|
||||
# Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
|
||||
#
|
||||
|
||||
# Input file generator for the starfield.
|
||||
|
||||
from random import random,randint
|
||||
from math import sqrt, hypot, atan2, pi, fmod, degrees
|
||||
from sys import argv,stderr
|
||||
|
||||
hemisphere_only, equator, meridians= False, 1000, 1000
|
||||
|
||||
n_random = 100000
|
||||
if len(argv) > 1:
|
||||
n_random = int(argv[1])
|
||||
|
||||
bars_total, bars_prev = 77, 0
|
||||
|
||||
def meridian(azimuth,n,(r0,g0,b0),(r1,g1,b1)):
|
||||
alts = 180.0/n
|
||||
for alti in range(n):
|
||||
# color
|
||||
altj = n-alti-1
|
||||
r = (r0 *altj+alti* r1)/n
|
||||
g = (g0 *altj+alti* g1)/n
|
||||
b = (b0 *altj+alti* b1)/n
|
||||
# position
|
||||
altitude = alts*alti
|
||||
print "%f %f #%02x%02x%02x" % (azimuth,altitude,r,g,b)
|
||||
print "%f %f #%02x%02x%02x" % (azimuth,-altitude,r,g,b)
|
||||
|
||||
if meridians:
|
||||
meridian( 0,meridians,(255,255,255), (180, 60,255)) # N->S
|
||||
meridian(90,meridians,( 80,255, 80), (255,240, 40)) # E->W
|
||||
|
||||
if equator:
|
||||
azis = 360.0/equator
|
||||
for azii in range(equator):
|
||||
azimuth = azis*azii
|
||||
print "%f %f #%02x%02x%02x" % (azimuth,0,255,255,255)
|
||||
|
||||
for i in range(n_random):
|
||||
# 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
|
||||
x,y,z = random()*2-1,random(),random()*2-1
|
||||
if not hemisphere_only:
|
||||
y = y*2-1
|
||||
l = sqrt(x*x + y*y + z*z)
|
||||
x /= l; y /= l; z /= l
|
||||
xz = hypot(x,z)
|
||||
|
||||
azimuth = degrees(fmod(atan2(x,z)+pi,2*pi))
|
||||
altitude = degrees(atan2(y,xz))
|
||||
|
||||
bars = round(bars_total*i/n_random)
|
||||
if bars != bars_prev:
|
||||
bars_prev = bars
|
||||
bars = int(bars)
|
||||
stderr.write('\r[%s%s]' % ('#' * bars, '-' * (bars_total-bars)))
|
||||
|
||||
print "%f %f #%02x%02x%02x" % (azimuth,altitude,r,g,b)
|
||||
|
||||
stderr.write('\r[%s]\n' % ('#' * bars_total,))
|
||||
|
105000
interface/resources/stars.txt
Normal file
105000
interface/resources/stars.txt
Normal file
File diff suppressed because it is too large
Load diff
128
interface/src/FieldOfView.cpp
Normal file
128
interface/src/FieldOfView.cpp
Normal file
|
@ -0,0 +1,128 @@
|
|||
//
|
||||
// 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_bounds_low(vec3(-1.0f,-1.0f,-1.0f)),
|
||||
vec_bounds_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;
|
||||
getFrustum(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_bounds_high.z) );
|
||||
}
|
||||
|
||||
mat4 FieldOfView::getWorldScreenXform() const
|
||||
{
|
||||
return translate(
|
||||
getViewerScreenXform() * affineInverse(mat_orientation),
|
||||
vec3(0.0f, 0.0f, -vec_bounds_high.z) );
|
||||
}
|
||||
|
||||
mat4 FieldOfView::getViewerWorldXform() const
|
||||
{
|
||||
vec3 n_translate = vec3(0.0f, 0.0f, vec_bounds_high.z);
|
||||
|
||||
return translate(
|
||||
translate(mat4(1.0f), n_translate)
|
||||
* mat_orientation, -n_translate );
|
||||
}
|
||||
|
||||
float FieldOfView::getPixelSize() const
|
||||
{
|
||||
vec3 low, high;
|
||||
getFrustum(low, high);
|
||||
|
||||
return std::min(
|
||||
abs(high.x - low.x) / val_width,
|
||||
abs(high.y - low.y) / val_height);
|
||||
}
|
||||
|
||||
void FieldOfView::getFrustum(vec3& low, vec3& high) const
|
||||
{
|
||||
low = vec_bounds_low;
|
||||
high = vec_bounds_high;
|
||||
|
||||
// start with uniform zoom
|
||||
float inv_zoom = 1.0f / val_zoom;
|
||||
float adj_x = inv_zoom, adj_y = inv_zoom;
|
||||
|
||||
// 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;
|
||||
|
||||
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_x = vp_aspect / f_aspect;
|
||||
}
|
||||
else
|
||||
{
|
||||
// expose_more -> f_aspect > vp_aspect <=> adj > 1
|
||||
// expose_less -> f_aspect <= vp_aspect <=> adj <= 1
|
||||
adj_y = f_aspect / vp_aspect;
|
||||
}
|
||||
}
|
||||
|
||||
// scale according to zoom / aspect correction
|
||||
float ax = (low.x + high.x) / 2.0f, ay = (low.y + high.y) / 2.0f;
|
||||
low.x = (low.x - ax) * adj_x + ax;
|
||||
high.x = (high.x - ax) * adj_x + ax;
|
||||
low.y = (low.y - ay) * adj_y + ay;
|
||||
high.y = (high.y - ay) * adj_y + ay;
|
||||
low.z = (low.z - high.z) * inv_zoom + high.z;
|
||||
|
||||
// calc and apply near distance based on near diagonal and perspective
|
||||
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;
|
||||
}
|
||||
|
128
interface/src/FieldOfView.h
Normal file
128
interface/src/FieldOfView.h
Normal file
|
@ -0,0 +1,128 @@
|
|||
//
|
||||
// FieldOfView.h
|
||||
// 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_bounds_low;
|
||||
glm::vec3 vec_bounds_high;
|
||||
float val_width;
|
||||
float val_height;
|
||||
float val_angle;
|
||||
float val_zoom;
|
||||
int enm_aspect_balancing;
|
||||
public:
|
||||
|
||||
FieldOfView();
|
||||
|
||||
// mutators
|
||||
|
||||
FieldOfView& setBounds(glm::vec3 const& low, glm::vec3 const& high)
|
||||
{ vec_bounds_low = low; vec_bounds_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::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;
|
||||
|
||||
/**
|
||||
* Returns the frustum as used for the projection matrices.
|
||||
* The result depdends on the bounds, eventually aspect correction
|
||||
* for the current resolution, the perspective angle (specified in
|
||||
* respect to diagonal) and zoom.
|
||||
*/
|
||||
void getFrustum(glm::vec3& low, glm::vec3& high) const;
|
||||
|
||||
/**
|
||||
* Returns the z-offset from the origin to where orientation ia
|
||||
* applied.
|
||||
*/
|
||||
float getTransformOffset() const { return vec_bounds_high.z; }
|
||||
|
||||
/**
|
||||
* Returns the aspect ratio.
|
||||
*/
|
||||
float getAspectRatio() const { return val_height / val_width; }
|
||||
};
|
||||
|
||||
#endif
|
||||
|
130
interface/src/OGlProgram.h
Normal file
130
interface/src/OGlProgram.h
Normal file
|
@ -0,0 +1,130 @@
|
|||
#ifndef __interface__OpenGlSupport__
|
||||
#define __interface__OpenGlSupport__
|
||||
|
||||
#include "InterfaceConfig.h"
|
||||
|
||||
/**
|
||||
* Macro to log OpenGl errors to stderr.
|
||||
* Example: oglLog( glPushMatrix() );
|
||||
*/
|
||||
#define oGlLog(stmt) \
|
||||
stmt; \
|
||||
{ \
|
||||
GLenum e = glGetError(); \
|
||||
if (e != GL_NO_ERROR) { \
|
||||
fprintf(stderr, __FILE__ ":" oGlLog_stringize(__LINE__) \
|
||||
" [OpenGL] %s\n", gluErrorString(e)); \
|
||||
} \
|
||||
} \
|
||||
(void) 0
|
||||
|
||||
#define oGlLog_stringize(x) oGlLog_stringize_i(x)
|
||||
#define oGlLog_stringize_i(x) # x
|
||||
|
||||
/**
|
||||
* Encapsulation of the otherwise lengthy call sequence to compile
|
||||
* and link shading pipelines.
|
||||
*/
|
||||
class OGlProgram {
|
||||
|
||||
GLuint _hndProg;
|
||||
|
||||
public:
|
||||
|
||||
OGlProgram() : _hndProg(0) { }
|
||||
|
||||
~OGlProgram() { if (_hndProg != 0u) { glDeleteProgram(_hndProg); } }
|
||||
|
||||
// no copy/assign
|
||||
OGlProgram(OGlProgram const&); // = delete;
|
||||
OGlProgram& operator=(OGlProgram const&); // = delete;
|
||||
|
||||
#if 0 // let's keep this commented, for now (C++11)
|
||||
|
||||
OGlProgram(OGlProgram&& disposable) : _hndProg(disposable._hndProg) {
|
||||
|
||||
disposable._hndProg = 0;
|
||||
}
|
||||
|
||||
OGlProgram& operator=(OGlProgram&& disposable) {
|
||||
|
||||
GLuint tmp = _hndProg;
|
||||
_hndProg = disposable._hndProg;
|
||||
disposable._hndProg = tmp;
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Activates the executable for rendering.
|
||||
* Shaders must be added and linked before this will work.
|
||||
*/
|
||||
void activate() const {
|
||||
|
||||
if (_hndProg != 0u)
|
||||
oGlLog( glUseProgram(_hndProg) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a shader to the program.
|
||||
*/
|
||||
bool addShader(GLenum type, GLchar const* cString) {
|
||||
|
||||
addShader(type, 1, & cString);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a shader to the program and logs to stderr.
|
||||
*/
|
||||
bool addShader(GLenum type, GLsizei nStrings, GLchar const** strings) {
|
||||
|
||||
if (! _hndProg) { _hndProg = glCreateProgram(); }
|
||||
|
||||
GLuint s = glCreateShader(type);
|
||||
glShaderSource(s, nStrings, strings, 0l);
|
||||
glCompileShader(s);
|
||||
GLint status;
|
||||
glGetShaderiv(s, GL_COMPILE_STATUS, & status);
|
||||
if (!! status)
|
||||
glAttachShader(_hndProg, s);
|
||||
#ifdef NDEBUG
|
||||
else
|
||||
#endif
|
||||
fetchLog(s, glGetShaderiv, glGetShaderInfoLog);
|
||||
glDeleteShader(s);
|
||||
return !! status;
|
||||
}
|
||||
|
||||
/**
|
||||
* Links the program and logs to stderr.
|
||||
*/
|
||||
bool link() {
|
||||
|
||||
if (! _hndProg) { return false; }
|
||||
|
||||
glLinkProgram(_hndProg);
|
||||
GLint status;
|
||||
glGetProgramiv(_hndProg, GL_LINK_STATUS, & status);
|
||||
#ifdef NDEBUG
|
||||
if (status == 0)
|
||||
#endif
|
||||
fetchLog(_hndProg, glGetProgramiv, glGetProgramInfoLog);
|
||||
|
||||
return status != 0;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
template< typename ParamFunc, typename GetLogFunc >
|
||||
void fetchLog(GLint handle, ParamFunc getParam, GetLogFunc getLog) {
|
||||
GLint logLength = 0;
|
||||
getParam(handle, GL_INFO_LOG_LENGTH, &logLength);
|
||||
if (!! logLength) {
|
||||
GLchar* message = new GLchar[logLength];
|
||||
getLog(handle, logLength, 0l, message);
|
||||
fprintf(stderr, "%s\n", message);
|
||||
delete[] message;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
42
interface/src/Stars.cpp
Normal file
42
interface/src/Stars.cpp
Normal file
|
@ -0,0 +1,42 @@
|
|||
//
|
||||
// Stars.cpp
|
||||
// interface
|
||||
//
|
||||
// Created by Tobias Schwinger on 3/22/13.
|
||||
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#include "InterfaceConfig.h"
|
||||
#include "FieldOfView.h"
|
||||
#include "Stars.h"
|
||||
|
||||
#define __interface__Starfield_impl__
|
||||
#include "starfield/Controller.h"
|
||||
#undef __interface__Starfield_impl__
|
||||
|
||||
Stars::Stars() :
|
||||
_ptrController(0l) {
|
||||
_ptrController = new starfield::Controller;
|
||||
}
|
||||
|
||||
Stars::~Stars() {
|
||||
delete _ptrController;
|
||||
}
|
||||
|
||||
bool Stars::readInput(const char* url, unsigned limit) {
|
||||
return _ptrController->readInput(url, limit);
|
||||
}
|
||||
|
||||
bool Stars::setResolution(unsigned k) {
|
||||
return _ptrController->setResolution(k);
|
||||
}
|
||||
|
||||
float Stars::changeLOD(float fraction, float overalloc, float realloc) {
|
||||
return float(_ptrController->changeLOD(fraction, overalloc, realloc));
|
||||
}
|
||||
|
||||
void Stars::render(FieldOfView const& fov) {
|
||||
_ptrController->render(fov.getPerspective(), fov.getAspectRatio(), fov.getOrientation());
|
||||
}
|
||||
|
||||
|
80
interface/src/Stars.h
Normal file
80
interface/src/Stars.h
Normal file
|
@ -0,0 +1,80 @@
|
|||
//
|
||||
// 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"
|
||||
|
||||
namespace starfield { class Controller; }
|
||||
|
||||
/**
|
||||
* Starfield rendering component.
|
||||
*/
|
||||
class Stars {
|
||||
|
||||
starfield::Controller* _ptrController;
|
||||
|
||||
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 = 200000);
|
||||
|
||||
/**
|
||||
* 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 in which case 'true'
|
||||
* is returned.
|
||||
*/
|
||||
bool setResolution(unsigned k);
|
||||
|
||||
/**
|
||||
* Allows to alter the number of stars to be rendered given a
|
||||
* factor. The least brightest ones are omitted first.
|
||||
*
|
||||
* The further parameters determine when GPU resources should
|
||||
* be reallocated. Its value is fractional in respect to the
|
||||
* last number of stars 'n' that caused 'n * (1+overalloc)' to
|
||||
* be allocated. When the next call to setLOD causes the total
|
||||
* number of stars that could be rendered to drop below 'n *
|
||||
* (1-realloc)' or rises above 'n * (1+realloc)' GPU resources
|
||||
* are updated. Note that all parameters must be fractions,
|
||||
* that is within the range [0;1] and that 'overalloc' must be
|
||||
* greater than or equal to 'realloc'.
|
||||
*
|
||||
* The current level of detail is returned as a float in [0;1].
|
||||
*/
|
||||
float changeLOD(float factor,
|
||||
float overalloc = 0.25, float realloc = 0.15);
|
||||
|
||||
private:
|
||||
// don't copy/assign
|
||||
Stars(Stars const&); // = delete;
|
||||
Stars& operator=(Stars const&); // delete;
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
|
|
@ -35,12 +35,19 @@
|
|||
#include <pthread.h>
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
#include <glm/gtc/matrix_transform.hpp>
|
||||
#include <glm/gtc/type_ptr.hpp>
|
||||
|
||||
#include "Field.h"
|
||||
#include "world.h"
|
||||
#include "Util.h"
|
||||
#ifndef _WIN32
|
||||
#include "Audio.h"
|
||||
#endif
|
||||
|
||||
#include "FieldOfView.h"
|
||||
#include "Stars.h"
|
||||
|
||||
#include "Head.h"
|
||||
#include "Hand.h"
|
||||
#include "Particle.h"
|
||||
|
@ -58,7 +65,7 @@
|
|||
|
||||
using namespace std;
|
||||
|
||||
int audio_on = 1; // Whether to turn on the audio support
|
||||
int audio_on = 0; // Whether to turn on the audio support
|
||||
int simulate_on = 1;
|
||||
|
||||
AgentList agentList('I');
|
||||
|
@ -89,6 +96,13 @@ Oscilloscope audioScope(256,200,true);
|
|||
#define HAND_RADIUS 0.25 // Radius of in-world 'hand' of you
|
||||
Head myHead; // The rendered head of oneself
|
||||
|
||||
FieldOfView fov;
|
||||
Stars stars;
|
||||
#ifdef STARFIELD_KEYS
|
||||
int starsTiles = 20;
|
||||
double starsLod = 1.0;
|
||||
#endif
|
||||
|
||||
glm::vec3 box(WORLD_SIZE,WORLD_SIZE,WORLD_SIZE);
|
||||
ParticleSystem balls(0,
|
||||
box,
|
||||
|
@ -312,7 +326,9 @@ void init(void)
|
|||
head_mouse_y = HEIGHT/2;
|
||||
head_lean_x = WIDTH/2;
|
||||
head_lean_y = HEIGHT/2;
|
||||
|
||||
|
||||
stars.readInput("file://stars.txt", 0);
|
||||
|
||||
// Initialize Field values
|
||||
field = Field();
|
||||
printf( "Field Initialized.\n" );
|
||||
|
@ -503,10 +519,21 @@ void display(void)
|
|||
glMateriali(GL_FRONT, GL_SHININESS, 96);
|
||||
|
||||
// Rotate, translate to camera location
|
||||
fov.setOrientation(
|
||||
glm::rotate(glm::rotate(glm::translate(glm::mat4(1.0f), -myHead.getPos()),
|
||||
-myHead.getRenderYaw(), glm::vec3(0.0f,1.0f,0.0f)),
|
||||
-myHead.getRenderPitch(), glm::vec3(1.0f,0.0f,0.0f)) );
|
||||
|
||||
glLoadMatrixf( glm::value_ptr(fov.getWorldViewerXform()) );
|
||||
glRotatef(myHead.getRenderPitch(), 1, 0, 0);
|
||||
glRotatef(myHead.getRenderYaw(), 0, 1, 0);
|
||||
glTranslatef(myHead.getPos().x, myHead.getPos().y, myHead.getPos().z);
|
||||
|
||||
|
||||
glDisable(GL_LIGHTING);
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
stars.render(fov);
|
||||
glEnable(GL_LIGHTING);
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
|
||||
glColor3f(1,0,0);
|
||||
glutSolidSphere(0.25, 15, 15);
|
||||
|
||||
|
@ -661,6 +688,7 @@ void addRandomSphere(bool wantColorRandomizer)
|
|||
|
||||
|
||||
const float KEYBOARD_YAW_RATE = 0.8;
|
||||
const float KEYBOARD_PITCH_RATE = 0.6;
|
||||
const float KEYBOARD_STRAFE_RATE = 0.03;
|
||||
const float KEYBOARD_FLY_RATE = 0.08;
|
||||
|
||||
|
@ -756,6 +784,15 @@ void key(unsigned char k, int x, int y)
|
|||
if (k == 'w') myHead.setDriveKeys(FWD, 1);
|
||||
if (k == 's') myHead.setDriveKeys(BACK, 1);
|
||||
if (k == ' ') reset_sensors();
|
||||
if (k == 't') renderPitchRate -= KEYBOARD_PITCH_RATE;
|
||||
if (k == 'g') renderPitchRate += KEYBOARD_PITCH_RATE;
|
||||
#ifdef STARFIELD_KEYS
|
||||
if (k == 'u') stars.setResolution(starsTiles += 1);
|
||||
if (k == 'j') stars.setResolution(starsTiles = max(starsTiles-1,1));
|
||||
if (k == 'i') if (starsLod < 1.0) starsLod = stars.changeLOD(1.01);
|
||||
if (k == 'k') if (starsLod > 0.01) starsLod = stars.changeLOD(0.99);
|
||||
if (k == 'r') stars.readInput("file://stars.txt", 0);
|
||||
#endif
|
||||
if (k == 'a') myHead.setDriveKeys(ROT_LEFT, 1);
|
||||
if (k == 'd') myHead.setDriveKeys(ROT_RIGHT, 1);
|
||||
if (k == 'o') simulate_on = !simulate_on;
|
||||
|
@ -845,19 +882,17 @@ void reshape(int width, int height)
|
|||
{
|
||||
WIDTH = width;
|
||||
HEIGHT = height;
|
||||
|
||||
glViewport(0, 0, width, height);
|
||||
|
||||
glMatrixMode(GL_PROJECTION); //hello
|
||||
glLoadIdentity();
|
||||
gluPerspective(45, //view angle
|
||||
1.0, //aspect ratio
|
||||
0.1, //near clip
|
||||
500.0);//far clip
|
||||
fov.setResolution(width, height)
|
||||
.setBounds(glm::vec3(-0.5f,-0.5f,-500.0f), glm::vec3(0.5f, 0.5f, 0.1f) )
|
||||
.setPerspective(0.7854f);
|
||||
glLoadMatrixf(glm::value_ptr(fov.getViewerScreenXform()));
|
||||
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
glLoadIdentity();
|
||||
|
||||
|
||||
glViewport(0, 0, width, height);
|
||||
}
|
||||
|
||||
void mouseFunc( int button, int state, int x, int y )
|
||||
|
|
9
interface/src/starfield/CMakeLists.txt
Normal file
9
interface/src/starfield/CMakeLists.txt
Normal file
|
@ -0,0 +1,9 @@
|
|||
|
||||
project(starfield)
|
||||
|
||||
# Only headers (that are facaded by the Stars.cpp file) here -
|
||||
# hence declared as custom target.
|
||||
|
||||
file(GLOB_RECURSE STARFIELD_SRCS *.h)
|
||||
add_custom_target("starfield" SOURCES ${STARFIELD_SRCS})
|
||||
|
106
interface/src/starfield/Config.h
Normal file
106
interface/src/starfield/Config.h
Normal file
|
@ -0,0 +1,106 @@
|
|||
//
|
||||
// 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
|
||||
|
||||
#ifndef STARFIELD_MULTITHREADING
|
||||
#define STARFIELD_MULTITHREADING 0
|
||||
#endif
|
||||
|
||||
//
|
||||
// Dependencies:
|
||||
//
|
||||
|
||||
#include "InterfaceConfig.h"
|
||||
#include "OGlProgram.h"
|
||||
|
||||
#include <cstddef>
|
||||
#include <cfloat>
|
||||
#include <cassert>
|
||||
#include <cmath>
|
||||
#include <cstdio>
|
||||
#include <cctype>
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#if STARFIELD_MULTITHREADING
|
||||
#include <mutex>
|
||||
#include <atomic>
|
||||
#endif
|
||||
|
||||
#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
|
||||
|
422
interface/src/starfield/Controller.h
Normal file
422
interface/src/starfield/Controller.h
Normal file
|
@ -0,0 +1,422 @@
|
|||
//
|
||||
// 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;
|
||||
#if STARFIELD_MULTITHREADING
|
||||
mutex _mtxInput;
|
||||
atomic<unsigned> _valTileResolution;
|
||||
|
||||
mutex _mtxLodState;
|
||||
#else
|
||||
unsigned _valTileResolution;
|
||||
#endif
|
||||
double _valLodFraction;
|
||||
double _valLodLowWaterMark;
|
||||
double _valLodHighWaterMark;
|
||||
double _valLodOveralloc;
|
||||
size_t _valLodNalloc;
|
||||
size_t _valLodNrender;
|
||||
BrightnessLevels _seqLodBrightness;
|
||||
BrightnessLevel _valLodAllocBrightness;
|
||||
|
||||
#if STARFIELD_MULTITHREADING
|
||||
atomic<BrightnessLevel> _valLodBrightness;
|
||||
|
||||
atomic<Renderer*> _ptrRenderer;
|
||||
|
||||
typedef lock_guard<mutex> lock;
|
||||
#else
|
||||
BrightnessLevel _valLodBrightness;
|
||||
|
||||
Renderer* _ptrRenderer;
|
||||
|
||||
#define lock
|
||||
#define _(x)
|
||||
#endif
|
||||
|
||||
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);
|
||||
|
||||
// input is read, now run the entire data pipeline on the new input
|
||||
|
||||
{ lock _(_mtxInput);
|
||||
|
||||
_seqInput.swap(vertices);
|
||||
#if STARFIELD_MULTITHREADING
|
||||
unsigned k = _valTileResolution.load(memory_order_relaxed);
|
||||
#else
|
||||
unsigned k = _valTileResolution;
|
||||
#endif
|
||||
size_t n, nRender;
|
||||
BrightnessLevel bMin, b;
|
||||
double rcpChange;
|
||||
|
||||
// we'll have to build a new LOD state for a new total N,
|
||||
// ideally keeping allocation size and number of vertices
|
||||
|
||||
{ lock _(_mtxLodState);
|
||||
|
||||
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
|
||||
|
||||
{ lock _(_mtxLodState);
|
||||
|
||||
_seqLodBrightness.swap(brightness);
|
||||
_valLodFraction *= rcpChange;
|
||||
_valLodLowWaterMark *= rcpChange;
|
||||
_valLodHighWaterMark *= rcpChange;
|
||||
_valLodOveralloc *= rcpChange;
|
||||
_valLodNalloc = n;
|
||||
_valLodNrender = nRender;
|
||||
_valLodAllocBrightness = bMin;
|
||||
#if STARFIELD_MULTITHREADING
|
||||
_valLodBrightness.store(b, memory_order_relaxed);
|
||||
#else
|
||||
_valLodBrightness = b;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool setResolution(unsigned k) {
|
||||
|
||||
if (k <= 3) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// fprintf(stderr, "Stars.cpp: setResolution(%d)\n", k);
|
||||
|
||||
#if STARFIELD_MULTITHREADING
|
||||
if (k != _valTileResolution.load(memory_order_relaxed))
|
||||
#else
|
||||
if (k != _valTileResolution)
|
||||
#endif
|
||||
{ lock _(_mtxInput);
|
||||
|
||||
unsigned n;
|
||||
BrightnessLevel b, bMin;
|
||||
|
||||
{ lock _(_mtxLodState);
|
||||
|
||||
n = _valLodNalloc;
|
||||
#if STARFIELD_MULTITHREADING
|
||||
b = _valLodBrightness.load(memory_order_relaxed);
|
||||
#else
|
||||
b = _valLodBrightness;
|
||||
#endif
|
||||
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;
|
||||
|
||||
{ lock _(_mtxLodState);
|
||||
|
||||
// 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, so keep b
|
||||
#if STARFIELD_MULTITHREADING
|
||||
_valLodBrightness.store(b, memory_order_relaxed);
|
||||
#else
|
||||
_valLodBrightness = b;
|
||||
#endif
|
||||
|
||||
// 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
|
||||
|
||||
{ lock _(_mtxInput);
|
||||
|
||||
recreateRenderer(n, _valTileResolution, b, bMin);
|
||||
|
||||
// fprintf(stderr, "Stars.cpp: LOD reallocation\n");
|
||||
|
||||
// publish new lod state
|
||||
|
||||
{ lock _(_mtxLodState);
|
||||
|
||||
_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) {
|
||||
|
||||
#if STARFIELD_MULTITHREADING
|
||||
delete _ptrRenderer.exchange(new Renderer(_seqInput, n, k, b, bMin) );
|
||||
#else
|
||||
delete _ptrRenderer;
|
||||
_ptrRenderer = new Renderer(_seqInput, n, k, b, bMin);
|
||||
#endif
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
void render(float perspective, float angle, mat4 const& orientation) {
|
||||
|
||||
#if STARFIELD_MULTITHREADING
|
||||
// check out renderer
|
||||
Renderer* renderer = _ptrRenderer.exchange(0l);
|
||||
#else
|
||||
Renderer* renderer = _ptrRenderer;
|
||||
#endif
|
||||
|
||||
// have it render
|
||||
if (renderer != 0l) {
|
||||
#if STARFIELD_MULTITHREADING
|
||||
BrightnessLevel b = _valLodBrightness.load(memory_order_relaxed);
|
||||
#else
|
||||
BrightnessLevel b = _valLodBrightness;
|
||||
#endif
|
||||
renderer->render(perspective, angle, orientation, b);
|
||||
}
|
||||
|
||||
#if STARFIELD_MULTITHREADING
|
||||
// check in - or dispose if there is a new one
|
||||
Renderer* newOne = 0l;
|
||||
if (! _ptrRenderer.compare_exchange_strong(newOne, renderer)) {
|
||||
|
||||
assert(!! newOne);
|
||||
delete renderer;
|
||||
}
|
||||
#else
|
||||
# undef lock
|
||||
# undef _
|
||||
#endif
|
||||
}
|
||||
|
||||
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
|
||||
|
524
interface/src/starfield/renderer/Renderer.h
Normal file
524
interface/src/starfield/renderer/Renderer.h
Normal file
|
@ -0,0 +1,524 @@
|
|||
//
|
||||
// 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;
|
||||
OGlProgram _objProgram;
|
||||
|
||||
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, hypotf(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::halfPi();
|
||||
float gx = sin(azimuth);
|
||||
float gz = -cos(azimuth);
|
||||
float exz = cos(altitude);
|
||||
vec3 tileCenter = vec3(gx * exz, sin(altitude), gz * exz);
|
||||
float w = dot(_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() {
|
||||
|
||||
GLchar const* const VERTEX_SHADER =
|
||||
"#version 120\n"
|
||||
"void main(void) {\n"
|
||||
|
||||
" vec3 c = gl_Color.rgb * 1.0125;\n"
|
||||
" float s = max(1.0, dot(c, c) * 0.7);\n"
|
||||
|
||||
" gl_Position = ftransform();\n"
|
||||
" gl_FrontColor= gl_Color;\n"
|
||||
" gl_PointSize = s;\n"
|
||||
"}\n";
|
||||
|
||||
_objProgram.addShader(GL_VERTEX_SHADER, VERTEX_SHADER);
|
||||
GLchar const* const FRAGMENT_SHADER =
|
||||
"#version 120\n"
|
||||
"void main(void) {\n"
|
||||
" gl_FragColor = gl_Color;\n"
|
||||
"}\n";
|
||||
_objProgram.addShader(GL_FRAGMENT_SHADER, FRAGMENT_SHADER);
|
||||
_objProgram.link();
|
||||
|
||||
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);
|
||||
|
||||
// set point size and smoothing + shader control
|
||||
glPointSize(1.0f);
|
||||
glEnable(GL_POINT_SMOOTH);
|
||||
glHint(GL_POINT_SMOOTH_HINT, GL_NICEST);
|
||||
glEnable(GL_VERTEX_PROGRAM_POINT_SIZE);
|
||||
|
||||
// select shader and vertex array
|
||||
_objProgram.activate();
|
||||
glBindVertexArray(_hndVertexArray);
|
||||
|
||||
// render
|
||||
glMultiDrawArrays(GL_POINTS,
|
||||
_arrBatchOffs, _arrBatchCount, n_ranges);
|
||||
|
||||
// restore state
|
||||
glBindVertexArray(0);
|
||||
glUseProgram(0);
|
||||
glDisable(GL_VERTEX_PROGRAM_POINT_SIZE);
|
||||
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::twicePi()) {
|
||||
_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::halfPi()) );
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
} // 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
|
||||
|
|
@ -8,7 +8,8 @@
|
|||
|
||||
#include <pthread.h>
|
||||
#include <cstring>
|
||||
#include <stdlib.h>
|
||||
#include <cstdlib>
|
||||
#include <cstdio>
|
||||
#include "AgentList.h"
|
||||
#include "SharedUtil.h"
|
||||
|
||||
|
@ -355,4 +356,4 @@ void AgentList::startDomainServerCheckInThread() {
|
|||
void AgentList::stopDomainServerCheckInThread() {
|
||||
domainServerCheckinStopFlag = true;
|
||||
pthread_join(checkInWithDomainServerThread, NULL);
|
||||
}
|
||||
}
|
||||
|
|
92
shared/src/AngleUtils.h
Normal file
92
shared/src/AngleUtils.h
Normal file
|
@ -0,0 +1,92 @@
|
|||
//
|
||||
// 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 twicePi() { return 360.0f; }
|
||||
static float halfPi() { return 90.0f; }
|
||||
};
|
||||
|
||||
struct Radians
|
||||
{
|
||||
static float pi() { return 3.141592653589793f; }
|
||||
static float twicePi() { return 6.283185307179586f; }
|
||||
static float halfPi() { return 1.5707963267948966; }
|
||||
};
|
||||
|
||||
struct Rotations
|
||||
{
|
||||
static float pi() { return 0.5f; }
|
||||
static float twicePi() { return 1.0f; }
|
||||
static float halfPi() { return 0.25f; }
|
||||
};
|
||||
|
||||
/**
|
||||
* Converts an angle from one unit to another.
|
||||
*/
|
||||
template< class UnitFrom, class UnitTo >
|
||||
float angleConvert(float a)
|
||||
{
|
||||
return a * (UnitTo::halfPi() / UnitFrom::halfPi());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Clamps an angle to the range of [-180; 180) degrees.
|
||||
*/
|
||||
template< class Unit >
|
||||
float angleSignedNormal(float a)
|
||||
{
|
||||
float result = remainder(a, Unit::twicePi());
|
||||
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::halfPi())
|
||||
{
|
||||
altitude = Unit::pi() - altitude;
|
||||
azimuth += Unit::pi();
|
||||
}
|
||||
else if (altitude < -Unit::halfPi())
|
||||
{
|
||||
altitude = -Unit::pi() - altitude;
|
||||
azimuth += Unit::pi();
|
||||
}
|
||||
azimuth = angleUnsignedNormal<Unit>(azimuth);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
95
shared/src/FloodFill.h
Normal file
95
shared/src/FloodFill.h
Normal file
|
@ -0,0 +1,95 @@
|
|||
//
|
||||
// FloodFill.h
|
||||
// hifi
|
||||
//
|
||||
// Created by Tobias Schwinger 3/26/13.
|
||||
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef __hifi__FloodFill__
|
||||
#define __hifi__FloodFill__
|
||||
|
||||
/**
|
||||
* Line scanning, iterative flood fill algorithm.
|
||||
*/
|
||||
template< class Strategy, typename Cursor >
|
||||
void floodFill(Cursor const& position,
|
||||
Strategy const& strategy = Strategy());
|
||||
|
||||
|
||||
template< class Strategy, typename Cursor >
|
||||
struct floodFill_impl : Strategy
|
||||
{
|
||||
floodFill_impl(Strategy const& s) : Strategy(s) { }
|
||||
|
||||
using Strategy::select;
|
||||
using Strategy::process;
|
||||
|
||||
using Strategy::left;
|
||||
using Strategy::right;
|
||||
using Strategy::up;
|
||||
using Strategy::down;
|
||||
|
||||
using Strategy::defer;
|
||||
using Strategy::deferred;
|
||||
|
||||
void go(Cursor position)
|
||||
{
|
||||
Cursor higher, lower, h,l, i;
|
||||
bool higherFound, lowerFound, hf, lf;
|
||||
do
|
||||
{
|
||||
if (! select(position))
|
||||
continue;
|
||||
|
||||
process(position);
|
||||
|
||||
higher = position; higherFound = false;
|
||||
up(higher); yTest(higher, higherFound);
|
||||
lower = position; lowerFound = false;
|
||||
down(lower); yTest(lower, lowerFound);
|
||||
|
||||
i = position, h = higher, l = lower;
|
||||
hf = higherFound, lf = lowerFound;
|
||||
do { right(i), right(h), right(l); yTest(h,hf); yTest(l,lf); }
|
||||
while (selectAndProcess(i));
|
||||
|
||||
i = position, h = higher, l = lower;
|
||||
hf = higherFound, lf = lowerFound;
|
||||
do { left(i); left(h); left(l); yTest(h,hf); yTest(l,lf); }
|
||||
while (selectAndProcess(i));
|
||||
}
|
||||
while (deferred(position));
|
||||
}
|
||||
|
||||
bool selectAndProcess(Cursor const& i)
|
||||
{
|
||||
if (select(i))
|
||||
{
|
||||
process(i);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void yTest(Cursor const& i, bool& state)
|
||||
{
|
||||
if (! select(i))
|
||||
state = false;
|
||||
else if (! state)
|
||||
{
|
||||
state = true;
|
||||
defer(i);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template< class Strategy, typename Cursor >
|
||||
void floodFill(Cursor const& p, Strategy const& s)
|
||||
{
|
||||
floodFill_impl<Strategy,Cursor>(s).go(p);
|
||||
}
|
||||
|
||||
|
||||
#endif /* defined(__hifi__FloodFill__) */
|
||||
|
|
@ -9,7 +9,7 @@
|
|||
#ifndef __hifi__OctalCode__
|
||||
#define __hifi__OctalCode__
|
||||
|
||||
#include <iostream>
|
||||
#include <string.h>
|
||||
|
||||
void printOctalCode(unsigned char * octalCode);
|
||||
int bytesRequiredForCodeLength(unsigned char threeBitCodes);
|
||||
|
|
96
shared/src/Radix2InplaceSort.h
Normal file
96
shared/src/Radix2InplaceSort.h
Normal file
|
@ -0,0 +1,96 @@
|
|||
//
|
||||
// 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 Scanner, typename Iterator >
|
||||
struct radix2InplaceSort_impl : Scanner
|
||||
{
|
||||
radix2InplaceSort_impl(Scanner const& s) : Scanner(s) { }
|
||||
|
||||
using Scanner::advance;
|
||||
using Scanner::bit;
|
||||
|
||||
void go(Iterator& from, Iterator& to, typename Scanner::state_type s)
|
||||
{
|
||||
Iterator 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 (bit(*l, s))
|
||||
{
|
||||
// scan from the right for unset bit
|
||||
for (++cr; --r != l ;++cr)
|
||||
if (! bit(*r, s))
|
||||
{
|
||||
// swap, continue scanning from left
|
||||
swap(*l, *r);
|
||||
break;
|
||||
}
|
||||
if (l == r)
|
||||
break;
|
||||
}
|
||||
|
||||
// on to the next digit, if any
|
||||
if (! 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)
|
||||
{
|
||||
radix2InplaceSort_impl<Radix2Scanner, BidiIterator>(scanner)
|
||||
.go(from, to, scanner.initial_state());
|
||||
}
|
||||
|
||||
#endif /* defined(__hifi__Radix2InplaceSort__) */
|
87
shared/src/Radix2IntegerScanner.h
Normal file
87
shared/src/Radix2IntegerScanner.h
Normal file
|
@ -0,0 +1,87 @@
|
|||
//
|
||||
// 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 valMsb;
|
||||
public:
|
||||
|
||||
Radix2IntegerScanner()
|
||||
: valMsb(~UInt(0) &~ (~UInt(0) >> 1)) { }
|
||||
|
||||
explicit Radix2IntegerScanner(int bits)
|
||||
: valMsb(UInt(1u) << (bits - 1))
|
||||
{ }
|
||||
|
||||
|
||||
typedef UInt state_type;
|
||||
|
||||
state_type initial_state() const { return valMsb; }
|
||||
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 valMsb;
|
||||
public:
|
||||
|
||||
Radix2IntegerScanner()
|
||||
: valMsb(~state_type(0u) &~ (~state_type(0u) >> 1))
|
||||
{ }
|
||||
|
||||
explicit Radix2IntegerScanner(int bits)
|
||||
: valMsb(state_type(1u) << (bits - 1))
|
||||
{ }
|
||||
|
||||
|
||||
typedef typename type_traits::make_unsigned<Int>::type state_type;
|
||||
|
||||
state_type initial_state() const { return valMsb; }
|
||||
bool advance(state_type& s) const { return (s >>= 1) != 0u; }
|
||||
|
||||
bool bit(Int const& v, state_type const& s) const { return !!((v-valMsb) & s); }
|
||||
};
|
||||
|
||||
#endif /* defined(__hifi__Radix2IntegerScanner__) */
|
||||
|
|
@ -9,8 +9,7 @@
|
|||
#ifndef __hifi__SharedUtil__
|
||||
#define __hifi__SharedUtil__
|
||||
|
||||
#include <iostream>
|
||||
#include <cstdio>
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include "Systime.h"
|
||||
|
|
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*>(_ptrImpl)
|
||||
|
||||
UrlReader::UrlReader()
|
||||
: _ptrImpl(0l), _arrXtra(0l), _strError(0l)
|
||||
{
|
||||
_arrXtra = new(std::nothrow) char[max_read_ahead];
|
||||
if (! _arrXtra) { _strError = error_init_failed; return; }
|
||||
_ptrImpl = curl_easy_init();
|
||||
if (! _ptrImpl) { _strError = 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 _arrXtra;
|
||||
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 (_valXtraSize > 0 && _strError == success)
|
||||
cb(0l, 0, 0, this);
|
||||
}
|
||||
else if (_strError == success)
|
||||
_strError = 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* _ptrImpl;
|
||||
char* _arrXtra;
|
||||
char const* _strError;
|
||||
void* _ptrStream;
|
||||
size_t _valXtraSize;
|
||||
|
||||
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 (! _ptrImpl) return false;
|
||||
_strError = success;
|
||||
_ptrStream = & s;
|
||||
_valXtraSize = ~size_t(0);
|
||||
this->perform(url, & callback_template<ContentStream>);
|
||||
s.end(_strError == success);
|
||||
return _strError == success;
|
||||
}
|
||||
|
||||
inline char const* UrlReader::getError() const { return this->_strError; }
|
||||
|
||||
inline void UrlReader::setError(char const* static_c_string)
|
||||
{
|
||||
if (this->_strError == success)
|
||||
this->_strError = 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->_ptrStream);
|
||||
|
||||
// first call?
|
||||
if (me->_valXtraSize == ~size_t(0))
|
||||
{
|
||||
me->_valXtraSize = 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->_valXtraSize > 0)
|
||||
{
|
||||
// fill extra buffer with beginning of input
|
||||
size_t fill = max_read_ahead - me->_valXtraSize;
|
||||
if (bytes < fill) fill = bytes;
|
||||
memcpy(me->_arrXtra + me->_valXtraSize, buffer, fill);
|
||||
// use extra buffer for next transfer
|
||||
buffer = me->_arrXtra;
|
||||
bytes = me->_valXtraSize + 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->_arrXtra && unprocessed <= input_offset)
|
||||
{
|
||||
me->_valXtraSize = 0u;
|
||||
input_offset -= unprocessed;
|
||||
}
|
||||
else // no? unprocessed data -> extra buffer
|
||||
{
|
||||
if (unprocessed > max_read_ahead)
|
||||
{
|
||||
me->setError(error_buffer_overflow);
|
||||
return 0;
|
||||
}
|
||||
me->_valXtraSize = unprocessed;
|
||||
memmove(me->_arrXtra, buffer + processed, unprocessed);
|
||||
|
||||
if (input_offset == size || buffer != me->_arrXtra)
|
||||
{
|
||||
return size;
|
||||
}
|
||||
}
|
||||
} // for
|
||||
}
|
||||
|
||||
#endif /* defined(__hifi__UrlReader__) */
|
||||
|
|
@ -7,6 +7,7 @@
|
|||
//
|
||||
|
||||
#include <cstring>
|
||||
#include <cstdio>
|
||||
#include <cmath>
|
||||
#include "SharedUtil.h"
|
||||
#include "OctalCode.h"
|
||||
|
@ -14,6 +15,7 @@
|
|||
#include <iostream> // to load voxels from file
|
||||
#include <fstream> // to load voxels from file
|
||||
|
||||
|
||||
int boundaryDistanceForRenderLevel(unsigned int renderLevel) {
|
||||
switch (renderLevel) {
|
||||
case 1:
|
||||
|
@ -612,4 +614,4 @@ void VoxelTree::createSphere(float r,float xc, float yc, float zc, float s, bool
|
|||
}
|
||||
}
|
||||
this->reaverageVoxelColors(this->rootNode);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,7 +9,6 @@
|
|||
#ifndef __hifi__VoxelTree__
|
||||
#define __hifi__VoxelTree__
|
||||
|
||||
#include <iostream>
|
||||
#include "VoxelNode.h"
|
||||
#include "MarkerNode.h"
|
||||
|
||||
|
|
Loading…
Reference in a new issue