diff --git a/interface/src/FieldOfView.cpp b/interface/src/FieldOfView.cpp deleted file mode 100644 index c255d7232f..0000000000 --- a/interface/src/FieldOfView.cpp +++ /dev/null @@ -1,126 +0,0 @@ -// -// FieldOfView.cpp -// interface -// -// Created by Tobias Schwinger on 3/21/13. -// Copyright (c) 2013 High Fidelity, Inc. All rights reserved. -// - -#include "FieldOfView.h" - -#include -#include -#include -#include - -using namespace glm; - -FieldOfView::FieldOfView() : - _matOrientation(mat4(1.0f)), - _vecBoundsLow(vec3(-1.0f,-1.0f,-1.0f)), - _vecBoundsHigh(vec3(1.0f,1.0f,1.0f)), - _valWidth(256.0f), - _valHeight(256.0f), - _valAngle(0.61), - _valZoom(1.0f), - _enmAspectBalancing(expose_less) { -} - -mat4 FieldOfView::getViewerScreenXform() const { - - mat4 projection; - vec3 low, high; - getFrustum(low, high); - - // perspective projection? determine correct near distance - if (_valAngle != 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(_matOrientation), - vec3(0.0f, 0.0f, -_vecBoundsHigh.z) ); -} - -mat4 FieldOfView::getWorldScreenXform() const { - - return translate( - getViewerScreenXform() * affineInverse(_matOrientation), - vec3(0.0f, 0.0f, -_vecBoundsHigh.z) ); -} - -mat4 FieldOfView::getViewerWorldXform() const { - - vec3 n_translate = vec3(0.0f, 0.0f, _vecBoundsHigh.z); - - return translate( - translate(mat4(1.0f), n_translate) - * _matOrientation, -n_translate ); -} - -float FieldOfView::getPixelSize() const { - - vec3 low, high; - getFrustum(low, high); - - return std::min( - abs(high.x - low.x) / _valWidth, - abs(high.y - low.y) / _valHeight); -} - -void FieldOfView::getFrustum(vec3& low, vec3& high) const { - - low = _vecBoundsLow; - high = _vecBoundsHigh; - - // start with uniform zoom - float inv_zoom = 1.0f / _valZoom; - float adj_x = inv_zoom, adj_y = inv_zoom; - - // balance aspect - if (_enmAspectBalancing != stretch) { - - float f_aspect = (high.x - low.x) / (high.y - low.y); - float vp_aspect = _valWidth / _valHeight; - - if ((_enmAspectBalancing == 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 = _valAngle == 0.0f ? 0.0f : - sqrt(w*w+h*h) * 0.5f / tan(_valAngle * 0.5f); - high.z += low.z; -} - diff --git a/interface/src/FieldOfView.h b/interface/src/FieldOfView.h deleted file mode 100644 index f4caf582c2..0000000000 --- a/interface/src/FieldOfView.h +++ /dev/null @@ -1,123 +0,0 @@ -// -// 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 - -// -// Viewing parameter encapsulation. -// -class FieldOfView { - - glm::mat4 _matOrientation; - glm::vec3 _vecBoundsLow; - glm::vec3 _vecBoundsHigh; - float _valWidth; - float _valHeight; - float _valAngle; - float _valZoom; - int _enmAspectBalancing; - public: - - FieldOfView(); - - // mutators - - FieldOfView& setBounds(glm::vec3 const& low, glm::vec3 const& high) { - _vecBoundsLow = low; _vecBoundsHigh = high; return *this; } - - FieldOfView& setOrientation(glm::mat4 const& matrix) { _matOrientation = matrix; return *this; } - - FieldOfView& setPerspective(float angle) { _valAngle = angle; return *this; } - - FieldOfView& setResolution(unsigned width, unsigned height) { _valWidth = width; _valHeight = height; return *this; } - - FieldOfView& setZoom(float factor) { _valZoom = factor; return *this; } - - enum aspect_balancing - { - expose_more, - expose_less, - stretch - }; - - FieldOfView& setAspectBalancing(aspect_balancing v) { _enmAspectBalancing = v; return *this; } - - // dumb accessors - - glm::mat4 const& getOrientation() const { return _matOrientation; } - float getWidthInPixels() const { return _valWidth; } - float getHeightInPixels() const { return _valHeight; } - float getPerspective() const { return _valAngle; } - - // 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 _vecBoundsHigh.z; } - - // - // Returns the aspect ratio. - // - float getAspectRatio() const { return _valHeight / _valWidth; } -}; - -#endif - diff --git a/interface/src/Stars.cpp b/interface/src/Stars.cpp index 95a0af3654..97d68bc61d 100644 --- a/interface/src/Stars.cpp +++ b/interface/src/Stars.cpp @@ -7,7 +7,6 @@ // #include "InterfaceConfig.h" -#include "FieldOfView.h" #include "Stars.h" #define __interface__Starfield_impl__ @@ -23,8 +22,8 @@ Stars::~Stars() { delete _ptrController; } -bool Stars::readInput(const char* url, unsigned limit) { - return _ptrController->readInput(url, limit); +bool Stars::readInput(const char* url, const char* cacheFile, unsigned limit) { + return _ptrController->readInput(url, cacheFile, limit); } bool Stars::setResolution(unsigned k) { @@ -35,8 +34,9 @@ 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()); +void Stars::render(float fovDiagonal, float aspect, glm::mat4 const& view) { + + _ptrController->render(fovDiagonal, aspect, glm::affineInverse(view)); } diff --git a/interface/src/Stars.h b/interface/src/Stars.h index 90c52e64ef..92c7b0f806 100644 --- a/interface/src/Stars.h +++ b/interface/src/Stars.h @@ -9,7 +9,7 @@ #ifndef __interface__Stars__ #define __interface__Stars__ -#include "FieldOfView.h" +#include namespace starfield { class Controller; } @@ -31,13 +31,13 @@ class Stars { * 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); + bool readInput(const char* url, const char* cacheFile = 0l, unsigned limit = 200000); /** * Renders the starfield from a local viewer's perspective. * The parameter specifies the field of view. */ - void render(FieldOfView const& fov); + void render(float fovDiagonal, float aspect, glm::mat4 const& view); /** * Sets the resolution for FOV culling. diff --git a/interface/src/Util.cpp b/interface/src/Util.cpp index 5c41318ec0..591cc5024d 100644 --- a/interface/src/Util.cpp +++ b/interface/src/Util.cpp @@ -11,12 +11,44 @@ #include #include +#include #include #include "world.h" #include "Util.h" + +void eulerToOrthonormals(glm::vec3 * angles, glm::vec3 * fwd, glm::vec3 * left, glm::vec3 * up) { + // + // Converts from three euler angles to the associated orthonormal vectors + // + // Angles contains (pitch, yaw, roll) in radians + // + + // First, create the quaternion associated with these euler angles + glm::quat q(glm::vec3(angles->x, angles->y, angles->z)); + + // Next, create a rotation matrix from that quaternion + glm::mat4 rotation; + rotation = glm::mat4_cast(q); + + // Transform the original vectors by the rotation matrix to get the new vectors + glm::vec4 u(0,1,0,0); + glm::vec4 l(1,0,0,0); + glm::vec4 f(0,0,1,0); + glm::vec4 uNew = u*rotation; + glm::vec4 lNew = l*rotation; + glm::vec4 fNew = f*rotation; + + // Copy the answers to output vectors + up->x = uNew.x; up->y = uNew.y; up->z = uNew.z; + left->x = lNew.x; left->y = lNew.y; left->z = lNew.z; + fwd->x = fNew.x; fwd->y = fNew.y; fwd->z = fNew.z; + +} + + // Return the azimuth angle in degrees between two points. float azimuth_to(glm::vec3 head_pos, glm::vec3 source_pos) { return atan2(head_pos.x - source_pos.x, head_pos.z - source_pos.z) * 180.0f / PIf; @@ -227,6 +259,13 @@ void renderOrientationDirections( glm::vec3 position, Orientation orientation, f glEnd(); } +bool closeEnoughForGovernmentWork(float a, float b) { + float distance = std::abs(a-b); + //printf("closeEnoughForGovernmentWork() a=%1.10f b=%1.10f distance=%1.10f\n",a,b,distance); + return (distance < 0.00001f); +} + + void testOrientationClass() { printf("\n----------\ntestOrientationClass()\n----------\n\n"); @@ -236,14 +275,45 @@ void testOrientationClass() { // ( yaw , pitch, roll , front.x , front.y , front.z , up.x , up.y , up.z , right.x , right.y , right.z ) // simple yaw tests - oTestCase( 0.f , 0.f , 0.f , 0.f , 0.f , 1.0f , 0.f , 1.0f , 0.f , -1.0f , 0.f , 0.f ), - oTestCase( 90.0f, 0.f , 0.f , 1.0f , 0.f , 0.f , 0.f , 1.0f , 0.f , 0.0f , 0.f , 1.0f ), - oTestCase(180.0f, 0.f , 0.f , 0.f , 0.f , -1.0f , 0.f , 1.0f , 0.f , 1.0f , 0.f , 0.f ), - oTestCase(270.0f, 0.f , 0.f , -1.0f , 0.f , 0.f , 0.f , 1.0f , 0.f , 0.0f , 0.f , -1.0f ), + oTestCase( 0.f , 0.f , 0.f , 0.f , 0.f , 1.0f , 0.f , 1.0f , 0.f , -1.0f , 0.f , 0.f ), + oTestCase(45.0f , 0.f , 0.f , 0.707107f , 0.f , 0.707107f , 0.f , 1.0f , 0.f , -0.707107f, 0.f , 0.707107f), + oTestCase( 90.0f, 0.f , 0.f , 1.0f , 0.f , 0.f , 0.f , 1.0f , 0.f , 0.0f , 0.f , 1.0f ), + oTestCase(135.0f, 0.f , 0.f , 0.707107f , 0.f ,-0.707107f , 0.f , 1.0f , 0.f , 0.707107f, 0.f , 0.707107f), + oTestCase(180.0f, 0.f , 0.f , 0.f , 0.f , -1.0f , 0.f , 1.0f , 0.f , 1.0f , 0.f , 0.f ), + oTestCase(225.0f, 0.f , 0.f , -0.707107f , 0.f ,-0.707107f , 0.f , 1.0f , 0.f , 0.707107f, 0.f , -0.707107f), + oTestCase(270.0f, 0.f , 0.f , -1.0f , 0.f , 0.f , 0.f , 1.0f , 0.f , 0.0f , 0.f , -1.0f ), + oTestCase(315.0f, 0.f , 0.f , -0.707107f , 0.f , 0.707107f , 0.f , 1.0f , 0.f , -0.707107f, 0.f , -0.707107f), + oTestCase(-45.0f, 0.f , 0.f , -0.707107f , 0.f , 0.707107f , 0.f , 1.0f , 0.f , -0.707107f, 0.f , -0.707107f), + oTestCase(-90.0f, 0.f , 0.f , -1.0f , 0.f , 0.f , 0.f , 1.0f , 0.f , 0.0f , 0.f , -1.0f ), + oTestCase(-135.0f,0.f , 0.f , -0.707107f , 0.f ,-0.707107f , 0.f , 1.0f , 0.f , 0.707107f, 0.f , -0.707107f), + oTestCase(-180.0f,0.f , 0.f , 0.f , 0.f , -1.0f , 0.f , 1.0f , 0.f , 1.0f , 0.f , 0.f ), + oTestCase(-225.0f,0.f , 0.f , 0.707107f , 0.f ,-0.707107f , 0.f , 1.0f , 0.f , 0.707107f, 0.f , 0.707107f), + oTestCase(-270.0f,0.f , 0.f , 1.0f , 0.f , 0.f , 0.f , 1.0f , 0.f , 0.0f , 0.f , 1.0f ), + oTestCase(-315.0f,0.f , 0.f , 0.707107f , 0.f , 0.707107f , 0.f , 1.0f , 0.f , -0.707107f, 0.f , 0.707107f), // simple pitch tests - oTestCase( 0.f ,90.f , 0.f , 0.f , 1.0f , 0.0f , 0.f , 0.0f , -1.0f, -1.0f , 0.f , 0.f ), + oTestCase( 0.f , 0.f , 0.f , 0.f, 0.f , 1.0f , 0.f , 1.0f , 0.f , -1.0f , 0.f , 0.f ), + oTestCase( 0.f ,45.0f , 0.f , 0.f, 0.707107f , 0.707107f, 0.f ,0.707107f, -0.707107f, -1.0f , 0.f , 0.f ), + oTestCase( 0.f ,90.f , 0.f , 0.f, 1.0f , 0.0f , 0.f ,0.0f , -1.0f , -1.0f , 0.f , 0.f ), + oTestCase( 0.f ,135.0f, 0.f , 0.f, 0.707107f , -0.707107f, 0.f ,-0.707107f, -0.707107f, -1.0f , 0.f , 0.f ), + oTestCase( 0.f ,180.f , 0.f , 0.f, 0.0f ,-1.0f , 0.f ,-1.0f , 0.f , -1.0f , 0.f , 0.f ), + oTestCase( 0.f ,225.0f, 0.f , 0.f,-0.707107f , -0.707107f, 0.f ,-0.707107f, 0.707107f, -1.0f , 0.f , 0.f ), + oTestCase( 0.f ,270.f , 0.f , 0.f,-1.0f , 0.0f , 0.f ,0.0f , 1.0f , -1.0f , 0.f , 0.f ), + oTestCase( 0.f ,315.0f, 0.f , 0.f,-0.707107f , 0.707107f, 0.f , 0.707107f, 0.707107f, -1.0f , 0.f , 0.f ), + // simple roll tests + oTestCase( 0.f , 0.f , 0.f , 0.f , 0.f , 1.0f , 0.f , 1.0f ,0.0f , -1.0f , 0.f , 0.0f ), + oTestCase( 0.f , 0.f ,45.0f , 0.f , 0.f , 1.0f , 0.707107f , 0.707107f ,0.0f , -0.707107f, 0.707107f, 0.0f ), + oTestCase( 0.f , 0.f ,90.f , 0.f , 0.f , 1.0f , 1.0f , 0.0f ,0.0f , 0.0f , 1.0f , 0.0f ), + oTestCase( 0.f , 0.f ,135.0f , 0.f , 0.f , 1.0f , 0.707107f , -0.707107f,0.0f , 0.707107f , 0.707107f, 0.0f ), + oTestCase( 0.f , 0.f ,180.f , 0.f , 0.f , 1.0f , 0.0f , -1.0f ,0.0f , 1.0f , 0.0f , 0.0f ), + oTestCase( 0.f , 0.f ,225.0f , 0.f , 0.f , 1.0f , -0.707107f, -0.707107f,0.0f , 0.707107f ,-0.707107f, 0.0f ), + oTestCase( 0.f , 0.f ,270.f , 0.f , 0.f , 1.0f , -1.0f , 0.0f ,0.0f , 0.0f , -1.0f , 0.0f ), + oTestCase( 0.f , 0.f ,315.0f , 0.f , 0.f , 1.0f , -0.707107f, 0.707107f ,0.0f , -0.707107f,-0.707107f, 0.0f ), + + // yaw combo tests + oTestCase( 90.f , 90.f , 0.f , 0.f , 1.0f , 0.0f , -1.0f , 0.0f , 0.f , 0.0f , 0.f , 1.0f ), + oTestCase( 90.f , 0.f , 90.f , 1.0f , 0.0f, 0.f , 0.0f , 0.0f , -1.f , 0.0f , 1.0f , 0.0f ), }; int failedCount = 0; @@ -267,33 +337,43 @@ void testOrientationClass() { glm::vec3 up = o1.getUp(); glm::vec3 right = o1.getRight(); - printf("\n-----\nTest: %d - yaw=%f , pitch=%f , roll=%f \n\n",i+1,yaw,pitch,roll); + printf("\n-----\nTest: %d - yaw=%f , pitch=%f , roll=%f \n",i+1,yaw,pitch,roll); - printf(" +front.x=%f, front.y=%f, front.z=%f\n",front.x,front.y,front.z); - if (front.x == tests[i].frontX && front.y == tests[i].frontY && front.z == tests[i].frontZ) { + printf("\nFRONT\n"); + printf(" + received: front.x=%f, front.y=%f, front.z=%f\n",front.x,front.y,front.z); + + if (closeEnoughForGovernmentWork(front.x, tests[i].frontX) + && closeEnoughForGovernmentWork(front.y, tests[i].frontY) + && closeEnoughForGovernmentWork(front.z, tests[i].frontZ)) { printf(" front vector PASSES!\n"); } else { - printf(" front vector FAILED! expected: \n"); - printf(" front.x=%f, front.y=%f, front.z=%f\n",tests[i].frontX,tests[i].frontY,tests[i].frontZ); + printf(" expected: front.x=%f, front.y=%f, front.z=%f\n",tests[i].frontX,tests[i].frontY,tests[i].frontZ); + printf(" front vector FAILED! \n"); passed = false; } - printf(" +up.x=%f, up.y=%f, up.z=%f\n",up.x,up.y,up.z); - if (up.x == tests[i].upX && up.y == tests[i].upY && up.z == tests[i].upZ) { + printf("\nUP\n"); + printf(" + received: up.x=%f, up.y=%f, up.z=%f\n",up.x,up.y,up.z); + if (closeEnoughForGovernmentWork(up.x, tests[i].upX) + && closeEnoughForGovernmentWork(up.y, tests[i].upY) + && closeEnoughForGovernmentWork(up.z, tests[i].upZ)) { printf(" up vector PASSES!\n"); } else { - printf(" up vector FAILED! expected: \n"); - printf(" up.x=%f, up.y=%f, up.z=%f\n",tests[i].upX,tests[i].upY,tests[i].upZ); + printf(" expected: up.x=%f, up.y=%f, up.z=%f\n",tests[i].upX,tests[i].upY,tests[i].upZ); + printf(" up vector FAILED!\n"); passed = false; } - printf(" +right.x=%f, right.y=%f, right.z=%f\n",right.x,right.y,right.z); - if (right.x == tests[i].rightX && right.y == tests[i].rightY && right.z == tests[i].rightZ) { + printf("\nRIGHT\n"); + printf(" + received: right.x=%f, right.y=%f, right.z=%f\n",right.x,right.y,right.z); + if (closeEnoughForGovernmentWork(right.x, tests[i].rightX) + && closeEnoughForGovernmentWork(right.y, tests[i].rightY) + && closeEnoughForGovernmentWork(right.z, tests[i].rightZ)) { printf(" right vector PASSES!\n"); } else { - printf(" right vector FAILED! expected: \n"); - printf(" right.x=%f, right.y=%f, right.z=%f\n",tests[i].rightX,tests[i].rightY,tests[i].rightZ); + printf(" expected: right.x=%f, right.y=%f, right.z=%f\n",tests[i].rightX,tests[i].rightY,tests[i].rightZ); + printf(" right vector FAILED!\n"); passed = false; } @@ -309,5 +389,3 @@ void testOrientationClass() { - - diff --git a/interface/src/Util.h b/interface/src/Util.h index f2adc918be..5a42884317 100644 --- a/interface/src/Util.h +++ b/interface/src/Util.h @@ -19,6 +19,7 @@ #include +void eulerToOrthonormals(glm::vec3 * angles, glm::vec3 * fwd, glm::vec3 * left, glm::vec3 * up); float azimuth_to(glm::vec3 head_pos, glm::vec3 source_pos); float angle_to(glm::vec3 head_pos, glm::vec3 source_pos, float render_yaw, float head_yaw); diff --git a/interface/src/main.cpp b/interface/src/main.cpp index 2f75f03297..9a7ec2440e 100644 --- a/interface/src/main.cpp +++ b/interface/src/main.cpp @@ -54,7 +54,7 @@ #include "Audio.h" #endif -#include "FieldOfView.h" +#include "AngleUtil.h" #include "Stars.h" #include "MenuRow.h" @@ -96,6 +96,7 @@ int headMirror = 1; // Whether to mirror own head when viewing int WIDTH = 1200; // Window size int HEIGHT = 800; int fullscreen = 0; +float aspectRatio = 1.0f; bool wantColorRandomizer = true; // for addSphere and load file @@ -109,12 +110,8 @@ Camera viewFrustumOffsetCamera; // The camera we use to sometimes show the v // Starfield information char starFile[] = "https://s3-us-west-1.amazonaws.com/highfidelity/stars.txt"; -FieldOfView fov; +char starCacheFile[] = "cachedStars.txt"; Stars stars; -#ifdef STARFIELD_KEYS -int starsTiles = 20; -double starsLod = 1.0; -#endif bool showingVoxels = true; @@ -302,7 +299,7 @@ void init(void) headMouseX = WIDTH/2; headMouseY = HEIGHT/2; - stars.readInput(starFile, 0); + stars.readInput(starFile, starCacheFile, 0); // Initialize Field values field = Field(); @@ -765,18 +762,12 @@ void display(void) glRotatef ( whichCamera.getYaw(), 0, 1, 0 ); glTranslatef( -whichCamera.getPosition().x, -whichCamera.getPosition().y, -whichCamera.getPosition().z ); - - - - //quick test for camera ortho-normal sanity check... - - - - - if (::starsOn) { // should be the first rendering pass - w/o depth buffer / lighting - stars.render(fov); + + glm::mat4 view; + glGetFloatv(GL_MODELVIEW_MATRIX, glm::value_ptr(view)); + stars.render(angleConvert(whichCamera.getFieldOfView()), aspectRatio, view); } glEnable(GL_LIGHTING); @@ -1277,13 +1268,6 @@ void key(unsigned char k, int x, int y) 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(starFile, 0); -#endif if (k == 'a') myAvatar.setDriveKeys(ROT_LEFT, 1); if (k == 'd') myAvatar.setDriveKeys(ROT_RIGHT, 1); } @@ -1401,7 +1385,7 @@ void reshape(int width, int height) { WIDTH = width; HEIGHT = height; - float aspectRatio = ((float)width/(float)height); // based on screen resize + aspectRatio = ((float)width/(float)height); // based on screen resize float fov; float nearClip; @@ -1429,14 +1413,6 @@ void reshape(int width, int height) glMatrixMode(GL_PROJECTION); //hello - // XXXBHG - Note: this is Tobias's code for loading the perspective matrix. At Philip's suggestion, I'm removing - // it and putting back our old code that simply loaded the fov, ratio, and near/far clips. But I'm keeping this here - // for reference for now. - //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())); - glLoadIdentity(); // XXXBHG - If we're in view frustum mode, then we need to do this little bit of hackery so that @@ -1507,7 +1483,20 @@ void audioMixerUpdate(in_addr_t newMixerAddress, in_port_t newMixerPort) { int main(int argc, const char * argv[]) { // Quick test of the Orientation class on startup! - testOrientationClass(); + //testOrientationClass(); // PER - commented out to test orthonormal code + + // + // For Brad: Demo of function to test conversion of euler angles to orthonormals + // (Note that the euler angles order is Pitch, Yaw, Roll, and in radians) + // + glm::vec3 angles(0, PI/4, 0); + glm::vec3 fwd, left, up; + + eulerToOrthonormals(&angles, &fwd, &left, &up); + + printf("fwd: %4.2f, %4.2f, %4.2f\n", fwd.x, fwd.y, fwd.z); + printf("left: %4.2f, %4.2f, %4.2f\n", left.x, left.y, left.z); + printf("up: %4.2f, %4.2f, %4.2f\n", up.x, up.y, up.z); AgentList::createInstance(AGENT_TYPE_INTERFACE); diff --git a/interface/src/starfield/Config.h b/interface/src/starfield/Config.h index 001af23a5f..5717fa22ee 100644 --- a/interface/src/starfield/Config.h +++ b/interface/src/starfield/Config.h @@ -25,8 +25,8 @@ #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 +#ifndef STARFIELD_DEBUG_CULLING +#define STARFIELD_DEBUG_CULLING 0 // set to 1 to peek behind the scenes #endif #ifndef STARFIELD_MULTITHREADING @@ -64,7 +64,6 @@ #include #include #include -#include #include "UrlReader.h" #include "AngleUtil.h" @@ -80,13 +79,11 @@ namespace starfield { 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; diff --git a/interface/src/starfield/Controller.h b/interface/src/starfield/Controller.h index 329119bbc4..f5680f881a 100644 --- a/interface/src/starfield/Controller.h +++ b/interface/src/starfield/Controller.h @@ -109,11 +109,11 @@ namespace starfield { _ptrRenderer(0l) { } - bool readInput(const char* url, unsigned limit) + bool readInput(const char* url, const char* cacheFile, unsigned limit) { InputVertices vertices; - if (! Loader().loadVertices(vertices, url, limit)) + if (! Loader().loadVertices(vertices, url, cacheFile, limit)) return false; BrightnessLevels brightness; diff --git a/interface/src/starfield/Loader.h b/interface/src/starfield/Loader.h index 03dfd18a53..00210e5e8a 100644 --- a/interface/src/starfield/Loader.h +++ b/interface/src/starfield/Loader.h @@ -33,7 +33,7 @@ namespace starfield { public: bool loadVertices( - InputVertices& destination, char const* url, unsigned limit) + InputVertices& destination, char const* url, char const* cacheFile, unsigned limit) { _ptrVertices = & destination; _valLimit = limit; @@ -43,7 +43,7 @@ namespace starfield { #endif _strUrl = url; // in case we fail early - if (! UrlReader::readUrl(url, *this)) + if (! UrlReader::readUrl(url, *this, cacheFile)) { fprintf(stderr, "%s:%d: %s\n", _strUrl, _valLineNo, getError()); diff --git a/interface/src/starfield/renderer/Renderer.h b/interface/src/starfield/renderer/Renderer.h index 03bc0b5763..a09226ed85 100644 --- a/interface/src/starfield/renderer/Renderer.h +++ b/interface/src/starfield/renderer/Renderer.h @@ -149,7 +149,7 @@ namespace starfield { matrix[3][2] = 0.0f; // extract local z vector - vec3 ahead = swizzle( column(matrix, 2) ); + vec3 ahead = vec3(matrix[2]); float azimuth = atan2(ahead.x,-ahead.z) + Radians::pi(); float altitude = atan2(-ahead.y, hypotf(ahead.x, ahead.z)); @@ -163,7 +163,7 @@ namespace starfield { // fprintf(stderr, "Stars.cpp: starting on tile #%d\n", tileIndex); -#if STARFIELD_DEBUG_LOD +#if STARFIELD_DEBUG_CULLING mat4 matrix_debug = glm::translate( glm::frustum(-hw, hw, -hh, hh, nearClip, 10.0f), vec3(0.0f, 0.0f, -4.0f)) * glm::affineInverse(matrix); @@ -173,7 +173,7 @@ namespace starfield { * glm::affineInverse(matrix); this->_itrOutIndex = (unsigned*) _arrBatchOffs; - this->_vecWxform = swizzle(row(matrix, 3)); + this->_vecWxform = vec3(row(matrix, 3)); this->_valHalfPersp = halfPersp; this->_valMinBright = minBright; @@ -181,13 +181,13 @@ namespace starfield { _arrTile, _arrTile + _objTiling.getTileCount(), (Tile**) _arrBatchCount)); -#if STARFIELD_DEBUG_LOD +#if STARFIELD_DEBUG_CULLING # define matrix matrix_debug #endif this->glBatch(glm::value_ptr(matrix), prepareBatch( (unsigned*) _arrBatchOffs, _itrOutIndex) ); -#if STARFIELD_DEBUG_LOD +#if STARFIELD_DEBUG_CULLING # undef matrix #endif } diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index 4c9164de0b..ac1a27d9f2 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -82,7 +82,10 @@ void AvatarData::parseData(unsigned char* sourceBuffer, int numBytes) { memcpy(&_handPosition, sourceBuffer, sizeof(float) * 3); sourceBuffer += sizeof(float) * 3; + + //printf( "_bodyYaw = %f", _bodyYaw ); + //std::cout << _handPosition.x << ", " << _handPosition.y << ", " << _handPosition.z << "\n"; //std::cout << _bodyPosition.x << ", " << _bodyPosition.y << ", " << _bodyPosition.z << "\n"; diff --git a/libraries/shared/src/UrlReader.cpp b/libraries/shared/src/UrlReader.cpp index f8fb707375..750ff1a821 100644 --- a/libraries/shared/src/UrlReader.cpp +++ b/libraries/shared/src/UrlReader.cpp @@ -6,13 +6,17 @@ // Copyright (c) 2013 High Fidelity, Inc. All rights reserved. // - #include "UrlReader.h" #include + #ifdef _WIN32 #define NOCURL_IN_WINDOWS #endif + +#include +#include + #ifndef NOCURL_IN_WINDOWS #include size_t const UrlReader::max_read_ahead = CURL_MAX_WRITE_SIZE; @@ -21,6 +25,7 @@ size_t const UrlReader::max_read_ahead = 0; #endif char const* const UrlReader::success = "UrlReader: Success!"; +char const* const UrlReader::success_cached = "UrlReader:: Using local file."; 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."; @@ -29,7 +34,7 @@ char const* const UrlReader::error_leftover_input = "UrlReader: Incomplete pro #define hnd_curl static_cast(_ptrImpl) UrlReader::UrlReader() - : _ptrImpl(0l), _arrXtra(0l), _strError(0l) { + : _ptrImpl(0l), _arrXtra(0l), _strError(0l), _arrCacheRdBuf(0l) { _arrXtra = new(std::nothrow) char[max_read_ahead]; if (! _arrXtra) { _strError = error_init_failed; return; } @@ -39,12 +44,14 @@ UrlReader::UrlReader() curl_easy_setopt(hnd_curl, CURLOPT_NOSIGNAL, 1l); curl_easy_setopt(hnd_curl, CURLOPT_FAILONERROR, 1l); curl_easy_setopt(hnd_curl, CURLOPT_FILETIME, 1l); + curl_easy_setopt(hnd_curl, CURLOPT_ENCODING, ""); #endif } UrlReader::~UrlReader() { - delete _arrXtra; + delete[] _arrXtra; + delete[] _arrCacheRdBuf; #ifndef NOCURL_IN_WINDOWS if (! hnd_curl) return; curl_easy_cleanup(hnd_curl); @@ -78,14 +85,42 @@ void UrlReader::getinfo(char const*& url, char const*& type, int64_t& length, int64_t& stardate) { #ifndef NOCURL_IN_WINDOWS + double clen; + long time; + curl_easy_getinfo(hnd_curl, CURLINFO_FILETIME, & time); + + // check caching file time whether we actually want to download anything + if (_strCacheFile != 0l) { + struct stat s; + stat(_strCacheFile, & s); + if (time > s.st_mtime) { + // file on server is newer -> update cache file + _ptrCacheFile = fopen(_strCacheFile, "wb"); + if (_ptrCacheFile != 0l) { + _valCacheMode = cache_write; + } + } else { + // file on server is older -> use cache file + if (! _arrCacheRdBuf) { + _arrCacheRdBuf = new (std::nothrow) char[max_read_ahead]; + if (! _arrCacheRdBuf) { + _valCacheMode = no_cache; + } + } + _ptrCacheFile = fopen(_strCacheFile, "rb"); + if (_ptrCacheFile != 0l) { + _valCacheMode = cache_read; + } + _strError = success_cached; + } + } + 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(clen); - long time; curl_easy_getinfo(hnd_curl, CURLINFO_FILETIME, & time); stardate = time; #endif diff --git a/libraries/shared/src/UrlReader.h b/libraries/shared/src/UrlReader.h index c50fe88d5b..9ad77d27ff 100644 --- a/libraries/shared/src/UrlReader.h +++ b/libraries/shared/src/UrlReader.h @@ -12,119 +12,132 @@ #include #include #include - -/** - * UrlReader class that encapsulates a context for sequential data retrieval - * via URLs. Use one per thread. - */ +#include + +// +// UrlReader class that encapsulates a context for sequential data retrieval +// via URLs. Use one per thread. +// class UrlReader { + enum CacheMode { no_cache, cache_write, cache_read }; + void* _ptrImpl; char* _arrXtra; char const* _strError; void* _ptrStream; + char const* _strCacheFile; + FILE* _ptrCacheFile; + char* _arrCacheRdBuf; + CacheMode _valCacheMode; size_t _valXtraSize; - public: - /** - * Constructor - performs initialization, never throws. - */ + // + // Constructor - performs initialization, never throws. + // UrlReader(); - /** - * Destructor - frees resources, never throws. - */ + // + // 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 - */ + // + // 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); + bool readUrl(char const* url, ContentStream& s, char const* cacheFile = 0l); - /** - * Returns a pointer to a static C-string that describes the error - * condition. - */ + // + // 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. - */ + // + // 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. - */ + // + // 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. - */ + // + // Pointer to the C-string returned by a call to 'readUrl' when no + // error occurred and a local file has been read instead of the + // network stream. + // + static char const* const success_cached; + + // + // 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. - */ + // + // 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. - */ + // + // 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. - */ + // + // 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'. - */ + // + // 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. - */ + // + // 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. - */ + // + // 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: @@ -143,38 +156,105 @@ class UrlReader { // synthesized callback - template< class Stream > - static size_t callback_template( - char *input, size_t size, size_t nmemb, void* thiz); + template< class Stream > static size_t callback_template(char *input, size_t size, + size_t nmemb, void* thiz); + + template< class Stream > size_t feedBuffered(Stream* stream, + char* input, size_t size); }; template< class ContentStream > -bool UrlReader::readUrl(char const* url, ContentStream& s) { +bool UrlReader::readUrl(char const* url, ContentStream& s, char const* cacheFile) { if (! _ptrImpl) return false; + _strCacheFile = cacheFile; + _ptrCacheFile = 0l; + _valCacheMode = no_cache; // eventually set later _strError = success; _ptrStream = & s; _valXtraSize = ~size_t(0); this->perform(url, & callback_template); s.end(_strError == success); - return _strError == success; + if (_ptrCacheFile != 0l) { + fclose(_ptrCacheFile); + } + return _strError == success || _strError == success_cached; } inline char const* UrlReader::getError() const { return this->_strError; } -inline void UrlReader::setError(char const* static_c_string) { +inline void UrlReader::setError(char const* staticCstring) { - if (this->_strError == success) - this->_strError = static_c_string; + if (this->_strError == success || this->_strError == success_cached) + this->_strError = staticCstring; } template< class Stream > -size_t UrlReader::callback_template( - char *input, size_t size, size_t nmemb, void* thiz) { +size_t UrlReader::feedBuffered(Stream* stream, char* input, size_t size) { + size_t inputOffset = 0u; - size *= nmemb; + while (true) { + char* buffer = input + inputOffset; + size_t bytes = size - inputOffset; + + // data in extra buffer? + if (_valXtraSize > 0) { + + // fill extra buffer with beginning of input + size_t fill = max_read_ahead - _valXtraSize; + if (bytes < fill) fill = bytes; + memcpy(_arrXtra + _valXtraSize, buffer, fill); + // use extra buffer for next transfer + buffer = _arrXtra; + bytes = _valXtraSize + fill; + inputOffset += fill; + } + + // call 'transfer' + size_t processed = stream->transfer(buffer, bytes); + if (processed == abort) { + + setError(error_aborted); + return 0u; + + } else if (! processed && ! input) { + + setError(error_leftover_input); + return 0u; + } + size_t unprocessed = bytes - processed; + + // can switch to input buffer, now? + if (buffer == _arrXtra && unprocessed <= inputOffset) { + + _valXtraSize = 0u; + inputOffset -= unprocessed; + + } else { // no? unprocessed data -> extra buffer + + if (unprocessed > max_read_ahead) { + + setError(error_buffer_overflow); + return 0; + } + _valXtraSize = unprocessed; + memmove(_arrXtra, buffer + processed, unprocessed); + + if (inputOffset == size || buffer != _arrXtra) { + + return size; + } + } + } // while +} + +template< class Stream > +size_t UrlReader::callback_template(char *input, size_t size, size_t nmemb, void* thiz) { + + size_t result = 0u; UrlReader* me = static_cast(thiz); Stream* stream = static_cast(me->_ptrStream); + size *= nmemb; // first call? if (me->_valXtraSize == ~size_t(0)) { @@ -184,65 +264,28 @@ size_t UrlReader::callback_template( char const* url, * type; int64_t length, stardate; me->getinfo(url, type, length, stardate); - stream->begin(url, type, length, stardate); + if (me->_valCacheMode != cache_read) { + stream->begin(url, type, length, stardate); + } } + do { + // will have to repeat from here when reading a local file - size_t input_offset = 0u; - - while (true) { - - 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; + // read from cache file? + if (me->_valCacheMode == cache_read) { + // change input buffer and start + input = me->_arrCacheRdBuf; + size = fread(input, 1, max_read_ahead, me->_ptrCacheFile); + nmemb = 1; + } else if (me->_valCacheMode == cache_write) { + fwrite(input, 1, size, me->_ptrCacheFile); } - // call 'transfer' - size_t processed = stream->transfer(buffer, bytes); - if (processed == abort) { + result = me->feedBuffered(stream, input, size); - me->setError(error_aborted); - return 0u; + } while (me->_valCacheMode == cache_read && result != 0 && ! feof(me->_ptrCacheFile)); - } 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 + return me->_valCacheMode != cache_read ? result : 0; } #endif /* defined(__hifi__UrlReader__) */