Merge remote-tracking branch 'upstream/master' into cleanup

This commit is contained in:
Stephen Birarda 2013-12-03 12:21:03 -08:00
commit 3277cb49a6
110 changed files with 3988 additions and 1232 deletions

View file

@ -20,6 +20,13 @@ else (LIBOVR_LIBRARIES AND LIBOVR_INCLUDE_DIRS)
if (APPLE)
find_library(LIBOVR_LIBRARIES libovr.a ${LIBOVR_ROOT_DIR}/Lib/MacOS/)
elseif (UNIX)
find_library(UDEV_LIBRARY libudev.a /usr/lib/x86_64-linux-gnu/)
find_library(XINERAMA_LIBRARY libXinerama.a /usr/lib/x86_64-linux-gnu/)
find_library(OVR_LIBRARY libovr.a ${LIBOVR_ROOT_DIR}/Lib/UNIX/)
if (UDEV_LIBRARY AND XINERAMA_LIBRARY AND OVR_LIBRARY)
set(LIBOVR_LIBRARIES ${OVR_LIBRARY} ${UDEV_LIBRARY} ${XINERAMA_LIBRARY})
endif (UDEV_LIBRARY AND XINERAMA_LIBRARY AND OVR_LIBRARY)
elseif (WIN32)
find_library(LIBOVR_LIBRARIES libovr.lib ${LIBOVR_ROOT_DIR}/Lib/Win32/)
endif ()
@ -41,4 +48,4 @@ else (LIBOVR_LIBRARIES AND LIBOVR_INCLUDE_DIRS)
# show the LIBOVR_INCLUDE_DIRS and LIBOVR_LIBRARIES variables only in the advanced view
mark_as_advanced(LIBOVR_INCLUDE_DIRS LIBOVR_LIBRARIES)
endif (LIBOVR_LIBRARIES AND LIBOVR_INCLUDE_DIRS)
endif (LIBOVR_LIBRARIES AND LIBOVR_INCLUDE_DIRS)

View file

@ -116,6 +116,14 @@ if (SIXENSE_FOUND AND NOT DISABLE_SIXENSE)
target_link_libraries(${TARGET_NAME} ${SIXENSE_LIBRARIES})
endif (SIXENSE_FOUND AND NOT DISABLE_SIXENSE)
# and with LibOVR for Oculus Rift
if (LIBOVR_FOUND AND NOT DISABLE_LIBOVR)
add_definitions(-DHAVE_LIBOVR)
include_directories(SYSTEM ${LIBOVR_INCLUDE_DIRS})
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -isystem ${LIBOVR_INCLUDE_DIRS}")
target_link_libraries(${TARGET_NAME} ${LIBOVR_LIBRARIES})
endif (LIBOVR_FOUND AND NOT DISABLE_LIBOVR)
qt5_use_modules(${TARGET_NAME} Core Gui Multimedia Network OpenGL Svg WebKit WebKitWidgets)
# include headers for interface and InterfaceConfig.
@ -130,7 +138,6 @@ include_directories(
SYSTEM
${FACESHIFT_INCLUDE_DIRS}
${GLM_INCLUDE_DIRS}
${LIBOVR_INCLUDE_DIRS}
${LIBVPX_INCLUDE_DIRS}
${LEAP_INCLUDE_DIRS}
${MOTIONDRIVER_INCLUDE_DIRS}
@ -179,7 +186,6 @@ if (APPLE)
${QTKit}
${QuartzCore}
${UVCCAMERACONTROL_LIBRARIES}
${LIBOVR_LIBRARIES}
)
if (LEAP_FOUND)

View file

@ -25,6 +25,7 @@ otherwise accompanies this software in either electronic or hard copy form.
#include "../Src/OVR_DeviceHandle.h"
#include "../Src/OVR_DeviceMessages.h"
#include "../Src/OVR_SensorFusion.h"
#include "../Src/OVR_Profile.h"
#include "../Src/Util/Util_LatencyTest.h"
#include "../Src/Util/Util_Render_Stereo.h"

View file

@ -16,7 +16,7 @@ otherwise accompanies this software in either electronic or hard copy form.
#define OVR_MAJOR_VERSION 0
#define OVR_MINOR_VERSION 2
#define OVR_BUILD_VERSION 1
#define OVR_VERSION_STRING "0.2.1"
#define OVR_BUILD_VERSION 5
#define OVR_VERSION_STRING "0.2.5"
#endif

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

0
interface/external/LibOVR/Src/Kernel/OVR_Alg.h vendored Executable file → Normal file
View file

0
interface/external/LibOVR/Src/Kernel/OVR_Allocator.h vendored Executable file → Normal file
View file

0
interface/external/LibOVR/Src/Kernel/OVR_Array.h vendored Executable file → Normal file
View file

1
interface/external/LibOVR/Src/Kernel/OVR_Atomic.h vendored Executable file → Normal file
View file

@ -824,6 +824,7 @@ public:
Lock (unsigned dummy = 0)
{
OVR_UNUSED(dummy);
if (!RecursiveAttrInit)
{
pthread_mutexattr_init(&RecursiveAttr);

0
interface/external/LibOVR/Src/Kernel/OVR_Color.h vendored Executable file → Normal file
View file

0
interface/external/LibOVR/Src/Kernel/OVR_ContainerAllocator.h vendored Executable file → Normal file
View file

0
interface/external/LibOVR/Src/Kernel/OVR_File.h vendored Executable file → Normal file
View file

0
interface/external/LibOVR/Src/Kernel/OVR_Hash.h vendored Executable file → Normal file
View file

0
interface/external/LibOVR/Src/Kernel/OVR_KeyCodes.h vendored Executable file → Normal file
View file

2
interface/external/LibOVR/Src/Kernel/OVR_List.h vendored Executable file → Normal file
View file

@ -115,7 +115,7 @@ struct ListNode
// }
//
// List<> represents a doubly-linked list if T, where each T must derive
// List<> represents a doubly-linked list of T, where each T must derive
// from ListNode<B>. B specifies the base class that was directly
// derived from ListNode, and is only necessary if there is an intermediate
// inheritance chain.

0
interface/external/LibOVR/Src/Kernel/OVR_Log.h vendored Executable file → Normal file
View file

596
interface/external/LibOVR/Src/Kernel/OVR_Math.h vendored Executable file → Normal file
View file

@ -4,7 +4,7 @@ PublicHeader: OVR.h
Filename : OVR_Math.h
Content : Implementation of 3D primitives such as vectors, matrices.
Created : September 4, 2012
Authors : Andrew Reisse, Michael Antonov, Steve LaValle, Anna Yershova
Authors : Andrew Reisse, Michael Antonov, Steve LaValle, Anna Yershova, Max Katsev
Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved.
@ -23,11 +23,12 @@ otherwise accompanies this software in either electronic or hard copy form.
#include "OVR_Types.h"
#include "OVR_RefCount.h"
#include "OVR_Std.h"
namespace OVR {
//-------------------------------------------------------------------------------------
// Constants for 3D world/axis definitions.
// ***** Constants for 3D world/axis definitions.
// Definitions of axes for coordinate and rotation conversions.
enum Axis
@ -48,12 +49,13 @@ enum RotateDirection
Rotate_CW = -1
};
// Constants for right handed and left handed coordinate systems
enum HandedSystem
{
Handed_R = 1, Handed_L = -1
};
// AxisDirection describes which way the axis points. Used by WorldAxes.
// AxisDirection describes which way the coordinate axis points. Used by WorldAxes.
enum AxisDirection
{
Axis_Up = 2,
@ -74,9 +76,9 @@ struct WorldAxes
};
//-------------------------------------------------------------------------------------
// ***** Math
//------------------------------------------------------------------------------------//
// ************************************ Math *****************************************//
//
// Math class contains constants and functions. This class is a template specialized
// per type, with Math<float> and Math<double> being distinct.
template<class Type>
@ -95,14 +97,14 @@ public:
static const float PiOver4;
static const float E;
static const float MaxValue; // Largest positive float Value
static const float MinPositiveValue; // Smallest possible positive value
static const float MaxValue; // Largest positive float Value
static const float MinPositiveValue; // Smallest possible positive value
static const float RadToDegreeFactor;
static const float DegreeToRadFactor;
static const float Tolerance; // 0.00001f;
static const float SingularityRadius; //0.00000000001f for Gimbal lock numerical problems
static const float Tolerance; // 0.00001f;
static const float SingularityRadius; // 0.0000001f for Gimbal lock numerical problems
};
// Double-precision Math constants class.
@ -116,33 +118,54 @@ public:
static const double PiOver4;
static const double E;
static const double MaxValue; // Largest positive double Value
static const double MinPositiveValue; // Smallest possible positive value
static const double MaxValue; // Largest positive double Value
static const double MinPositiveValue; // Smallest possible positive value
static const double RadToDegreeFactor;
static const double DegreeToRadFactor;
static const double Tolerance; // 0.00001f;
static const double SingularityRadius; //0.00000000001 for Gimbal lock numerical problems
static const double Tolerance; // 0.00001;
static const double SingularityRadius; // 0.000000000001 for Gimbal lock numerical problems
};
typedef Math<float> Mathf;
typedef Math<double> Mathd;
// Conversion functions between degrees and radians
template<class FT>
FT RadToDegree(FT rads) { return rads * Math<FT>::RadToDegreeFactor; }
template<class FT>
FT DegreeToRad(FT rads) { return rads * Math<FT>::DegreeToRadFactor; }
template<class T>
T RadToDegree(T rads) { return rads * Math<T>::RadToDegreeFactor; }
template<class T>
T DegreeToRad(T rads) { return rads * Math<T>::DegreeToRadFactor; }
// Numerically stable acos function
template<class T>
T Acos(T val) {
if (val > T(1)) return T(0);
else if (val < T(-1)) return Math<T>::Pi;
else return acos(val);
};
// Numerically stable asin function
template<class T>
T Asin(T val) {
if (val > T(1)) return Math<T>::PiOver2;
else if (val < T(-1)) return Math<T>::PiOver2 * T(3);
else return asin(val);
};
#ifdef OVR_CC_MSVC
inline int isnan(double x) { return _isnan(x); };
#endif
template<class T>
class Quat;
//-------------------------------------------------------------------------------------
// ***** Vector2f - 2D Vector2f
// ***** Vector2<>
// Vector2f represents a 2-dimensional vector or point in space,
// consisting of coordinates x and y,
// Vector2f (Vector2d) represents a 2-dimensional vector or point in space,
// consisting of coordinates x and y
template<class T>
class Vector2
@ -174,33 +197,53 @@ public:
return *this; }
// Compare two vectors for equality with tolerance. Returns true if vectors match withing tolerance.
bool Compare(const Vector2&b, T tolerance = Mathf::Tolerance)
bool Compare(const Vector2&b, T tolerance = Mathf::Tolerance)
{
return (fabs(b.x-x) < tolerance) && (fabs(b.y-y) < tolerance);
}
// Dot product overload.
// Entrywise product of two vectors
Vector2 EntrywiseMultiply(const Vector2& b) const { return Vector2(x * b.x, y * b.y);}
// Dot product
// Used to calculate angle q between two vectors among other things,
// as (A dot B) = |a||b|cos(q).
T operator* (const Vector2& b) const { return x*b.x + y*b.y; }
T Dot(const Vector2& b) const { return x*b.x + y*b.y; }
// Returns the angle from this vector to b, in radians.
T Angle(const Vector2& b) const { return acos((*this * b)/(Length()*b.Length())); }
T Angle(const Vector2& b) const
{
T div = LengthSq()*b.LengthSq();
OVR_ASSERT(div != T(0));
T result = Acos((this->Dot(b))/sqrt(div));
return result;
}
// Return Length of the vector squared.
T LengthSq() const { return (x * x + y * y); }
// Return vector length.
T Length() const { return sqrt(LengthSq()); }
// Returns distance between two points represented by vectors.
// Returns distance between two points represented by vectors.
T Distance(Vector2& b) const { return (*this - b).Length(); }
// Determine if this a unit vector.
// Determine if this a unit vector.
bool IsNormalized() const { return fabs(LengthSq() - T(1)) < Math<T>::Tolerance; }
// Normalize, convention vector length to 1.
void Normalize() { *this /= Length(); }
void Normalize()
{
T l = Length();
OVR_ASSERT(l != T(0));
*this /= l;
}
// Returns normalized (unit) version of the vector without modifying itself.
Vector2 Normalized() const { return *this / Length(); }
Vector2 Normalized() const
{
T l = Length();
OVR_ASSERT(l != T(0));
return *this / l;
}
// Linearly interpolates from this vector to another.
// Factor should be between 0.0 and 1.0, with 0 giving full value to this.
@ -208,17 +251,24 @@ public:
// Projects this vector onto the argument; in other words,
// A.Project(B) returns projection of vector A onto B.
Vector2 ProjectTo(const Vector2& b) const { return b * ((*this * b) / b.LengthSq()); }
Vector2 ProjectTo(const Vector2& b) const
{
T l2 = b.LengthSq();
OVR_ASSERT(l2 != T(0));
return b * ( Dot(b) / l2 );
}
};
typedef Vector2<float> Vector2f;
typedef Vector2<double> Vector2d;
//-------------------------------------------------------------------------------------
// ***** Vector3f - 3D Vector3f
// Vector3f represents a 3-dimensional vector or point in space,
//-------------------------------------------------------------------------------------
// ***** Vector3<> - 3D vector of {x, y, z}
//
// Vector3f (Vector3d) represents a 3-dimensional vector or point in space,
// consisting of coordinates x, y and z.
template<class T>
@ -253,13 +303,20 @@ public:
// Compare two vectors for equality with tolerance. Returns true if vectors match withing tolerance.
bool Compare(const Vector3&b, T tolerance = Mathf::Tolerance)
{
return (fabs(b.x-x) < tolerance) && (fabs(b.y-y) < tolerance) && (fabs(b.z-z) < tolerance);
return (fabs(b.x-x) < tolerance) &&
(fabs(b.y-y) < tolerance) &&
(fabs(b.z-z) < tolerance);
}
// Dot product overload.
// Entrywise product of two vectors
Vector3 EntrywiseMultiply(const Vector3& b) const { return Vector3(x * b.x,
y * b.y,
z * b.z);}
// Dot product
// Used to calculate angle q between two vectors among other things,
// as (A dot B) = |a||b|cos(q).
T operator* (const Vector3& b) const { return x*b.x + y*b.y + z*b.z; }
T Dot(const Vector3& b) const { return x*b.x + y*b.y + z*b.z; }
// Compute cross product, which generates a normal vector.
// Direction vector can be determined by right-hand rule: Pointing index finder in
@ -269,7 +326,13 @@ public:
x*b.y - y*b.x); }
// Returns the angle from this vector to b, in radians.
T Angle(const Vector3& b) const { return acos((*this * b)/(Length()*b.Length())); }
T Angle(const Vector3& b) const
{
T div = LengthSq()*b.LengthSq();
OVR_ASSERT(div != T(0));
T result = Acos((this->Dot(b))/sqrt(div));
return result;
}
// Return Length of the vector squared.
T LengthSq() const { return (x * x + y * y + z * z); }
@ -281,10 +344,22 @@ public:
// Determine if this a unit vector.
bool IsNormalized() const { return fabs(LengthSq() - T(1)) < Math<T>::Tolerance; }
// Normalize, convention vector length to 1.
void Normalize() { *this /= Length(); }
void Normalize()
{
T l = Length();
OVR_ASSERT(l != T(0));
*this /= l;
}
// Returns normalized (unit) version of the vector without modifying itself.
Vector3 Normalized() const { return *this / Length(); }
Vector3 Normalized() const
{
T l = Length();
OVR_ASSERT(l != T(0));
return *this / l;
}
// Linearly interpolates from this vector to another.
// Factor should be between 0.0 and 1.0, with 0 giving full value to this.
@ -292,7 +367,15 @@ public:
// Projects this vector onto the argument; in other words,
// A.Project(B) returns projection of vector A onto B.
Vector3 ProjectTo(const Vector3& b) const { return b * ((*this * b) / b.LengthSq()); }
Vector3 ProjectTo(const Vector3& b) const
{
T l2 = b.LengthSq();
OVR_ASSERT(l2 != T(0));
return b * ( Dot(b) / l2 );
}
// Projects this vector onto a plane defined by a normal vector
Vector3 ProjectToPlane(const Vector3& normal) const { return *this - this->ProjectTo(normal); }
};
@ -301,8 +384,55 @@ typedef Vector3<double> Vector3d;
//-------------------------------------------------------------------------------------
// ***** Matrix4f
// ***** Size
// Size class represents 2D size with Width, Height components.
// Used to describe distentions of render targets, etc.
template<class T>
class Size
{
public:
T Width, Height;
Size() : Width(0), Height(0) { }
Size(T w_, T h_) : Width(w_), Height(h_) { }
explicit Size(T s) : Width(s), Height(s) { }
bool operator== (const Size& b) const { return Width == b.Width && Height == b.Height; }
bool operator!= (const Size& b) const { return Width != b.Width || Height != b.Height; }
Size operator+ (const Size& b) const { return Size(Width + b.Width, Height + b.Height); }
Size& operator+= (const Size& b) { Width += b.Width; Height += b.Height; return *this; }
Size operator- (const Size& b) const { return Size(Width - b.Width, Height - b.Height); }
Size& operator-= (const Size& b) { Width -= b.Width; Height -= b.Height; return *this; }
Size operator- () const { return Size(-Width, -Height); }
Size operator* (const Size& b) const { return Size(Width * b.Width, Height * b.Height); }
Size& operator*= (const Size& b) { Width *= b.Width; Height *= b.Height; return *this; }
Size operator/ (const Size& b) const { return Size(Width / b.Width, Height / b.Height); }
Size& operator/= (const Size& b) { Width /= b.Width; Height /= b.Height; return *this; }
// Scalar multiplication/division scales both components.
Size operator* (T s) const { return Size(Width*s, Height*s); }
Size& operator*= (T s) { Width *= s; Height *= s; return *this; }
Size operator/ (T s) const { return Size(Width/s, Height/s); }
Size& operator/= (T s) { Width /= s; Height /= s; return *this; }
T Area() const { return Width * Height; }
inline Vector2<T> ToVector() const { return Vector2<T>(Width, Height); }
};
typedef Size<int> Sizei;
typedef Size<unsigned> Sizeu;
typedef Size<float> Sizef;
typedef Size<double> Sized;
//-------------------------------------------------------------------------------------
// ***** Matrix4f
//
// Matrix4f is a 4x4 matrix used for 3d transformations and projections.
// Translation stored in the last column.
// The matrix is stored in row-major order in memory, meaning that values
@ -367,6 +497,29 @@ public:
M[3][0] = 0; M[3][1] = 0; M[3][2] = 0; M[3][3] = 1;
}
void ToString(char* dest, UPInt destsize)
{
UPInt pos = 0;
for (int r=0; r<4; r++)
for (int c=0; c<4; c++)
pos += OVR_sprintf(dest+pos, destsize-pos, "%g ", M[r][c]);
}
static Matrix4f FromString(const char* src)
{
Matrix4f result;
for (int r=0; r<4; r++)
for (int c=0; c<4; c++)
{
result.M[r][c] = (float)atof(src);
while (src && *src != ' ')
src++;
while (src && *src == ' ')
src++;
}
return result;
}
static const Matrix4f& Identity() { return IdentityValue; }
void SetIdentity()
@ -377,6 +530,36 @@ public:
M[0][3] = M[1][3] = M[2][1] = M[3][0] = 0;
}
Matrix4f operator+ (const Matrix4f& b) const
{
Matrix4f result(*this);
result += b;
return result;
}
Matrix4f& operator+= (const Matrix4f& b)
{
for (int i = 0; i < 4; i++)
for (int j = 0; j < 4; j++)
M[i][j] += b.M[i][j];
return *this;
}
Matrix4f operator- (const Matrix4f& b) const
{
Matrix4f result(*this);
result -= b;
return result;
}
Matrix4f& operator-= (const Matrix4f& b)
{
for (int i = 0; i < 4; i++)
for (int j = 0; j < 4; j++)
M[i][j] -= b.M[i][j];
return *this;
}
// Multiplies two matrices into destination with minimum copying.
static Matrix4f& Multiply(Matrix4f* d, const Matrix4f& a, const Matrix4f& b)
{
@ -406,18 +589,32 @@ public:
Matrix4f operator* (float s) const
{
return Matrix4f(M[0][0] * s, M[0][1] * s, M[0][2] * s, M[0][3] * s,
M[1][0] * s, M[1][1] * s, M[1][2] * s, M[1][3] * s,
M[2][0] * s, M[2][1] * s, M[2][2] * s, M[2][3] * s,
M[3][0] * s, M[3][1] * s, M[3][2] * s, M[3][3] * s);
Matrix4f result(*this);
result *= s;
return result;
}
Matrix4f& operator*= (float s)
{
M[0][0] *= s; M[0][1] *= s; M[0][2] *= s; M[0][3] *= s;
M[1][0] *= s; M[1][1] *= s; M[1][2] *= s; M[1][3] *= s;
M[2][0] *= s; M[2][1] *= s; M[2][2] *= s; M[2][3] *= s;
M[3][0] *= s; M[3][1] *= s; M[3][2] *= s; M[3][3] *= s;
for (int i = 0; i < 4; i++)
for (int j = 0; j < 4; j++)
M[i][j] *= s;
return *this;
}
Matrix4f operator/ (float s) const
{
Matrix4f result(*this);
result /= s;
return result;
}
Matrix4f& operator/= (float s)
{
for (int i = 0; i < 4; i++)
for (int j = 0; j < 4; j++)
M[i][j] /= s;
return *this;
}
@ -442,16 +639,16 @@ public:
}
float SubDet (const int* rows, const int* cols) const
float SubDet (const UPInt* rows, const UPInt* cols) const
{
return M[rows[0]][cols[0]] * (M[rows[1]][cols[1]] * M[rows[2]][cols[2]] - M[rows[1]][cols[2]] * M[rows[2]][cols[1]])
- M[rows[0]][cols[1]] * (M[rows[1]][cols[0]] * M[rows[2]][cols[2]] - M[rows[1]][cols[2]] * M[rows[2]][cols[0]])
+ M[rows[0]][cols[2]] * (M[rows[1]][cols[0]] * M[rows[2]][cols[1]] - M[rows[1]][cols[1]] * M[rows[2]][cols[0]]);
}
float Cofactor(int I, int J) const
float Cofactor(UPInt I, UPInt J) const
{
const int indices[4][3] = {{1,2,3},{0,2,3},{0,1,3},{0,1,2}};
const UPInt indices[4][3] = {{1,2,3},{0,2,3},{0,1,3},{0,1,2}};
return ((I+J)&1) ? -SubDet(indices[I],indices[J]) : SubDet(indices[I],indices[J]);
}
@ -480,7 +677,27 @@ public:
*this = Inverted();
}
//AnnaSteve:
// This is more efficient than general inverse, but ONLY works
// correctly if it is a homogeneous transform matrix (rot + trans)
Matrix4f InvertedHomogeneousTransform() const
{
// Make the inverse rotation matrix
Matrix4f rinv = this->Transposed();
rinv.M[3][0] = rinv.M[3][1] = rinv.M[3][2] = 0.0f;
// Make the inverse translation matrix
Vector3f tvinv = Vector3f(-M[0][3],-M[1][3],-M[2][3]);
Matrix4f tinv = Matrix4f::Translation(tvinv);
return rinv * tinv; // "untranslate", then "unrotate"
}
// This is more efficient than general inverse, but ONLY works
// correctly if it is a homogeneous transform matrix (rot + trans)
void InvertHomogeneousTransform()
{
*this = InvertedHomogeneousTransform();
}
// Matrix to Euler Angles conversion
// a,b,c, are the YawPitchRoll angles to be returned
// rotation a around axis A1
// is followed by rotation b around axis A2
@ -502,7 +719,7 @@ public:
*b = -S*D*Math<float>::PiOver2;
*c = S*D*atan2( psign*M[A2][A1], M[A2][A2] );
}
else if (pm > 1.0 - Math<float>::SingularityRadius)
else if (pm > 1.0f - Math<float>::SingularityRadius)
{ // North pole singularity
*a = 0.0f;
*b = S*D*Math<float>::PiOver2;
@ -518,7 +735,7 @@ public:
return;
}
//AnnaSteve:
// Matrix to Euler Angles conversion
// a,b,c, are the YawPitchRoll angles to be returned
// rotation a around axis A1
// is followed by rotation b around axis A2
@ -537,13 +754,13 @@ public:
psign = 1.0f;
float c2 = M[A1][A1];
if (c2 < -1.0 + Math<float>::SingularityRadius)
if (c2 < -1.0f + Math<float>::SingularityRadius)
{ // South pole singularity
*a = 0.0f;
*b = S*D*Math<float>::Pi;
*c = S*D*atan2( -psign*M[A2][m],M[A2][A2]);
}
else if (c2 > 1.0 - Math<float>::SingularityRadius)
else if (c2 > 1.0f - Math<float>::SingularityRadius)
{ // North pole singularity
*a = 0.0f;
*b = 0.0f;
@ -560,7 +777,6 @@ public:
// Creates a matrix that converts the vertices from one coordinate system
// to another.
//
static Matrix4f AxisConversion(const WorldAxes& to, const WorldAxes& from)
{
// Holds axis values from the 'to' structure
@ -584,7 +800,7 @@ public:
}
// Creates a matrix for translation by vector
static Matrix4f Translation(const Vector3f& v)
{
Matrix4f t;
@ -594,6 +810,7 @@ public:
return t;
}
// Creates a matrix for translation by vector
static Matrix4f Translation(float x, float y, float z = 0.0f)
{
Matrix4f t;
@ -603,6 +820,7 @@ public:
return t;
}
// Creates a matrix for scaling by vector
static Matrix4f Scaling(const Vector3f& v)
{
Matrix4f t;
@ -612,6 +830,7 @@ public:
return t;
}
// Creates a matrix for scaling by vector
static Matrix4f Scaling(float x, float y, float z)
{
Matrix4f t;
@ -621,6 +840,7 @@ public:
return t;
}
// Creates a matrix for scaling by constant
static Matrix4f Scaling(float s)
{
Matrix4f t;
@ -632,7 +852,8 @@ public:
//AnnaSteve : Just for quick testing. Not for final API. Need to remove case.
// Creates a rotation matrix rotating around the X axis by 'angle' radians.
// Just for quick testing. Not for final API. Need to remove case.
static Matrix4f RotationAxis(Axis A, float angle, RotateDirection d, HandedSystem s)
{
float sina = s * d *sin(angle);
@ -658,10 +879,10 @@ public:
// Creates a rotation matrix rotating around the X axis by 'angle' radians.
// Rotation direction is depends on the coordinate system:
// RHS (Oculus default): Positive angle values rotate Counter-clockwise (CCW),
// RHS (Oculus default): Positive angle values rotate Counter-clockwise (CCW),
// while looking in the negative axis direction. This is the
// same as looking down from positive axis values towards origin.
// LHS: Positive angle values rotate clock-wise (CW), while looking in the
// LHS: Positive angle values rotate clock-wise (CW), while looking in the
// negative axis direction.
static Matrix4f RotationX(float angle)
{
@ -743,9 +964,9 @@ public:
};
//-------------------------------------------------------------------------------------
// ***** Quat
//-------------------------------------------------------------------------------------//
// **************************************** Quat **************************************//
//
// Quatf represents a quaternion class used for rotations.
//
// Quaternion multiplications are done in right-to-left order, to match the
@ -763,7 +984,7 @@ public:
Quat(T x_, T y_, T z_, T w_) : x(x_), y(y_), z(z_), w(w_) {}
// Constructs rotation quaternion around the axis.
// Constructs quaternion for rotation around the axis by an angle.
Quat(const Vector3<T>& axis, T angle)
{
Vector3<T> unitAxis = axis.Normalized();
@ -775,33 +996,33 @@ public:
z = unitAxis.z * sinHalfAngle;
}
//AnnaSteve:
// Constructs quaternion for rotation around one of the coordinate axis by an angle.
void AxisAngle(Axis A, T angle, RotateDirection d, HandedSystem s)
{
T sinHalfAngle = s * d *sin(angle * (T)0.5);
T sinHalfAngle = s * d *sin(angle * T(0.5));
T v[3];
v[0] = v[1] = v[2] = (T)0;
v[0] = v[1] = v[2] = T(0);
v[A] = sinHalfAngle;
//return Quat(v[0], v[1], v[2], cos(angle * (T)0.5));
w = cos(angle * (T)0.5);
w = cos(angle * T(0.5));
x = v[0];
y = v[1];
z = v[2];
}
void GetAxisAngle(Vector3<T>* axis, T* angle) const
// Compute axis and angle from quaternion
void GetAxisAngle(Vector3<T>* axis, T* angle) const
{
if (LengthSq() > Math<T>::Tolerance * Math<T>::Tolerance)
{
*axis = Vector3<T>(x, y, z).Normalized();
*angle = 2 * acos(w);
}
else
{
*axis = Vector3<T>(1, 0, 0);
*angle= 0;
}
if ( x*x + y*y + z*z > Math<T>::Tolerance * Math<T>::Tolerance ) {
*axis = Vector3<T>(x, y, z).Normalized();
*angle = T(2) * Acos(w);
}
else
{
*axis = Vector3<T>(1, 0, 0);
*angle= 0;
}
}
bool operator== (const Quat& b) const { return x == b.x && y == b.y && z == b.z && w == b.w; }
@ -817,6 +1038,7 @@ public:
Quat operator/ (T s) const { T rcp = T(1)/s; return Quat(x * rcp, y * rcp, z * rcp, w *rcp); }
Quat& operator/= (T s) { T rcp = T(1)/s; w *= rcp; x *= rcp; y *= rcp; z *= rcp; return *this; }
// Get Imaginary part vector
Vector3<T> Imag() const { return Vector3<T>(x,y,z); }
@ -824,29 +1046,42 @@ public:
T Length() const { return sqrt(x * x + y * y + z * z + w * w); }
// Get quaternion length squared.
T LengthSq() const { return (x * x + y * y + z * z + w * w); }
// Simple Eulidean distance in R^4 (not SLERP distance, but at least respects Haar measure)
T Distance(const Quat& q) const
{
T Distance(const Quat& q) const
{
T d1 = (*this - q).Length();
T d2 = (*this + q).Length(); // Antipoldal point check
T d2 = (*this + q).Length(); // Antipodal point check
return (d1 < d2) ? d1 : d2;
}
}
T DistanceSq(const Quat& q) const
{
T d1 = (*this - q).LengthSq();
T d2 = (*this + q).LengthSq(); // Antipoldal point check
T d2 = (*this + q).LengthSq(); // Antipodal point check
return (d1 < d2) ? d1 : d2;
}
// Normalize
bool IsNormalized() const { return fabs(LengthSq() - 1) < Math<T>::Tolerance; }
void Normalize() { *this /= Length(); }
Quat Normalized() const { return *this / Length(); }
bool IsNormalized() const { return fabs(LengthSq() - T(1)) < Math<T>::Tolerance; }
void Normalize()
{
T l = Length();
OVR_ASSERT(l != T(0));
*this /= l;
}
Quat Normalized() const
{
T l = Length();
OVR_ASSERT(l != T(0));
return *this / l;
}
// Returns conjugate of the quaternion. Produces inverse rotation if quaternion is normalized.
Quat Conj() const { return Quat(-x, -y, -z, w); }
// AnnaSteve fixed: order of quaternion multiplication
// Quaternion multiplication. Combines quaternion rotations, performing the one on the
// right hand side first.
Quat operator* (const Quat& b) const { return Quat(w * b.x + x * b.w + y * b.z - z * b.y,
@ -868,7 +1103,7 @@ public:
// assuming negative direction of the axis). Standard formula: q(t) * V * q(t)^-1.
Vector3<T> Rotate(const Vector3<T>& v) const
{
return ((*this * Quat<T>(v.x, v.y, v.z, 0)) * Inverted()).Imag();
return ((*this * Quat<T>(v.x, v.y, v.z, T(0))) * Inverted()).Imag();
}
@ -879,7 +1114,7 @@ public:
}
// Sets this quaternion to the one rotates in the opposite direction.
void Invert() const
void Invert()
{
*this = Quat(-x, -y, -z, w);
}
@ -897,6 +1132,52 @@ public:
float(T(2) * (x*z - w*y)), float(T(2) * (y*z + w*x)), float(ww - xx - yy + zz) );
}
// Converting matrix to quaternion
static Quat<T> Matrix4fToQuat(const Matrix4f& m)
{
T trace = m.M[0][0] + m.M[1][1] + m.M[2][2];
Quat<T> q;
// In almost all cases, the first part is executed.
// However, if the trace is not positive, the other
// cases arise.
if (trace > T(0))
{
T s = sqrt(trace + T(1)) * T(2); // s=4*qw
q.w = T(0.25) * s;
q.x = (m.M[2][1] - m.M[1][2]) / s;
q.y = (m.M[0][2] - m.M[2][0]) / s;
q.z = (m.M[1][0] - m.M[0][1]) / s;
}
else if ((m.M[0][0] > m.M[1][1])&&(m.M[0][0] > m.M[2][2]))
{
T s = sqrt(T(1) + m.M[0][0] - m.M[1][1] - m.M[2][2]) * T(2);
q.w = (m.M[2][1] - m.M[1][2]) / s;
q.x = T(0.25) * s;
q.y = (m.M[0][1] + m.M[1][0]) / s;
q.z = (m.M[2][0] + m.M[0][2]) / s;
}
else if (m.M[1][1] > m.M[2][2])
{
T s = sqrt(T(1) + m.M[1][1] - m.M[0][0] - m.M[2][2]) * T(2); // S=4*qy
q.w = (m.M[0][2] - m.M[2][0]) / s;
q.x = (m.M[0][1] + m.M[1][0]) / s;
q.y = T(0.25) * s;
q.z = (m.M[1][2] + m.M[2][1]) / s;
}
else
{
T s = sqrt(T(1) + m.M[2][2] - m.M[0][0] - m.M[1][1]) * T(2); // S=4*qz
q.w = (m.M[1][0] - m.M[0][1]) / s;
q.x = (m.M[0][2] + m.M[2][0]) / s;
q.y = (m.M[1][2] + m.M[2][1]) / s;
q.z = T(0.25) * s;
}
return q;
}
// GetEulerAngles extracts Euler angles from the quaternion, in the specified order of
// axis rotations and the specified coordinate system. Right-handed coordinate system
@ -918,33 +1199,33 @@ public:
T Q22 = Q[A2]*Q[A2];
T Q33 = Q[A3]*Q[A3];
T psign = T(-1.0);
T psign = T(-1);
// Determine whether even permutation
if (((A1 + 1) % 3 == A2) && ((A2 + 1) % 3 == A3))
psign = T(1.0);
psign = T(1);
T s2 = psign * T(2.0) * (psign*w*Q[A2] + Q[A1]*Q[A3]);
T s2 = psign * T(2) * (psign*w*Q[A2] + Q[A1]*Q[A3]);
if (s2 < (T)-1.0 + Math<T>::SingularityRadius)
if (s2 < T(-1) + Math<T>::SingularityRadius)
{ // South pole singularity
*a = T(0.0);
*a = T(0);
*b = -S*D*Math<T>::PiOver2;
*c = S*D*atan2((T)2.0*(psign*Q[A1]*Q[A2] + w*Q[A3]),
*c = S*D*atan2(T(2)*(psign*Q[A1]*Q[A2] + w*Q[A3]),
ww + Q22 - Q11 - Q33 );
}
else if (s2 > (T)1.0 - Math<T>::SingularityRadius)
else if (s2 > T(1) - Math<T>::SingularityRadius)
{ // North pole singularity
*a = (T)0.0;
*a = T(0);
*b = S*D*Math<T>::PiOver2;
*c = S*D*atan2((T)2.0*(psign*Q[A1]*Q[A2] + w*Q[A3]),
*c = S*D*atan2(T(2)*(psign*Q[A1]*Q[A2] + w*Q[A3]),
ww + Q22 - Q11 - Q33);
}
else
{
*a = -S*D*atan2((T)-2.0*(w*Q[A1] - psign*Q[A2]*Q[A3]),
*a = -S*D*atan2(T(-2)*(w*Q[A1] - psign*Q[A2]*Q[A3]),
ww + Q33 - Q11 - Q22);
*b = S*D*asin(s2);
*c = S*D*atan2((T)2.0*(w*Q[A3] - psign*Q[A1]*Q[A2]),
*c = S*D*atan2(T(2)*(w*Q[A3] - psign*Q[A1]*Q[A2]),
ww + Q11 - Q22 - Q33);
}
return;
@ -982,25 +1263,25 @@ public:
T Q22 = Q[A2]*Q[A2];
T Qmm = Q[m]*Q[m];
T psign = T(-1.0);
T psign = T(-1);
if ((A1 + 1) % 3 == A2) // Determine whether even permutation
{
psign = (T)1.0;
psign = T(1);
}
T c2 = ww + Q11 - Q22 - Qmm;
if (c2 < (T)-1.0 + Math<T>::SingularityRadius)
if (c2 < T(-1) + Math<T>::SingularityRadius)
{ // South pole singularity
*a = (T)0.0;
*a = T(0);
*b = S*D*Math<T>::Pi;
*c = S*D*atan2( (T)2.0*(w*Q[A1] - psign*Q[A2]*Q[m]),
*c = S*D*atan2( T(2)*(w*Q[A1] - psign*Q[A2]*Q[m]),
ww + Q22 - Q11 - Qmm);
}
else if (c2 > (T)1.0 - Math<T>::SingularityRadius)
else if (c2 > T(1) - Math<T>::SingularityRadius)
{ // North pole singularity
*a = (T)0.0;
*b = (T)0.0;
*c = S*D*atan2( (T)2.0*(w*Q[A1] - psign*Q[A2]*Q[m]),
*a = T(0);
*b = T(0);
*c = S*D*atan2( T(2)*(w*Q[A1] - psign*Q[A2]*Q[m]),
ww + Q22 - Q11 - Qmm);
}
else
@ -1013,12 +1294,93 @@ public:
}
return;
}
};
};
typedef Quat<float> Quatf;
typedef Quat<double> Quatd;
//-------------------------------------------------------------------------------------
// ***** Angle
// Cleanly representing the algebra of 2D rotations.
// The operations maintain the angle between -Pi and Pi, the same range as atan2.
template<class T>
class Angle
{
public:
enum AngularUnits
{
Radians = 0,
Degrees = 1
};
Angle() : a(0) {}
// Fix the range to be between -Pi and Pi
Angle(T a_, AngularUnits u = Radians) : a((u == Radians) ? a_ : a_*Math<T>::DegreeToRadFactor) { FixRange(); }
T Get(AngularUnits u = Radians) const { return (u == Radians) ? a : a*Math<T>::RadToDegreeFactor; }
void Set(const T& x, AngularUnits u = Radians) { a = (u == Radians) ? x : x*Math<T>::DegreeToRadFactor; FixRange(); }
int Sign() const { if (a == 0) return 0; else return (a > 0) ? 1 : -1; }
T Abs() const { return (a > 0) ? a : -a; }
bool operator== (const Angle& b) const { return a == b.a; }
bool operator!= (const Angle& b) const { return a != b.a; }
// bool operator< (const Angle& b) const { return a < a.b; }
// bool operator> (const Angle& b) const { return a > a.b; }
// bool operator<= (const Angle& b) const { return a <= a.b; }
// bool operator>= (const Angle& b) const { return a >= a.b; }
// bool operator= (const T& x) { a = x; FixRange(); }
// These operations assume a is already between -Pi and Pi.
Angle& operator+= (const Angle& b) { a = a + b.a; FastFixRange(); return *this; }
Angle& operator+= (const T& x) { a = a + x; FixRange(); return *this; }
Angle operator+ (const Angle& b) const { Angle res = *this; res += b; return res; }
Angle operator+ (const T& x) const { Angle res = *this; res += x; return res; }
Angle& operator-= (const Angle& b) { a = a - b.a; FastFixRange(); return *this; }
Angle& operator-= (const T& x) { a = a - x; FixRange(); return *this; }
Angle operator- (const Angle& b) const { Angle res = *this; res -= b; return res; }
Angle operator- (const T& x) const { Angle res = *this; res -= x; return res; }
T Distance(const Angle& b) { T c = fabs(a - b.a); return (c <= Math<T>::Pi) ? c : Math<T>::TwoPi - c; }
private:
// The stored angle, which should be maintained between -Pi and Pi
T a;
// Fixes the angle range to [-Pi,Pi], but assumes no more than 2Pi away on either side
inline void FastFixRange()
{
if (a < -Math<T>::Pi)
a += Math<T>::TwoPi;
else if (a > Math<T>::Pi)
a -= Math<T>::TwoPi;
}
// Fixes the angle range to [-Pi,Pi] for any given range, but slower then the fast method
inline void FixRange()
{
// do nothing if the value is already in the correct range, since fmod call is expensive
if (a >= -Math<T>::Pi && a <= Math<T>::Pi)
return;
a = fmod(a,Math<T>::TwoPi);
if (a < -Math<T>::Pi)
a += Math<T>::TwoPi;
else if (a > Math<T>::Pi)
a -= Math<T>::TwoPi;
}
};
typedef Angle<float> Anglef;
typedef Angle<double> Angled;
//-------------------------------------------------------------------------------------
// ***** Plane
@ -1043,7 +1405,7 @@ public:
// Find the point to plane distance. The sign indicates what side of the plane the point is on (0 = point on plane).
T TestSide(const Vector3<T>& p) const
{
return (N * p) + D;
return (N.Dot(p)) + D;
}
Plane<T> Flipped() const
@ -1065,6 +1427,6 @@ public:
typedef Plane<float> Planef;
}
} // Namespace OVR
#endif

0
interface/external/LibOVR/Src/Kernel/OVR_RefCount.h vendored Executable file → Normal file
View file

0
interface/external/LibOVR/Src/Kernel/OVR_Std.h vendored Executable file → Normal file
View file

0
interface/external/LibOVR/Src/Kernel/OVR_String.h vendored Executable file → Normal file
View file

0
interface/external/LibOVR/Src/Kernel/OVR_StringHash.h vendored Executable file → Normal file
View file

2
interface/external/LibOVR/Src/Kernel/OVR_SysFile.h vendored Executable file → Normal file
View file

@ -88,6 +88,6 @@ public:
virtual bool Close();
};
} // Scaleform
} // Namespace OVR
#endif

0
interface/external/LibOVR/Src/Kernel/OVR_System.h vendored Executable file → Normal file
View file

0
interface/external/LibOVR/Src/Kernel/OVR_Threads.h vendored Executable file → Normal file
View file

2
interface/external/LibOVR/Src/Kernel/OVR_Timer.h vendored Executable file → Normal file
View file

@ -95,6 +95,6 @@ private:
};
} // Scaleform::Timer
} // Namespace OVR
#endif

2
interface/external/LibOVR/Src/Kernel/OVR_Types.h vendored Executable file → Normal file
View file

@ -291,7 +291,7 @@ namespace BaseTypes
#if defined(OVR_CC_MSVC)
# define OVR_FORCE_INLINE __forceinline
#elif defined(OVR_CC_GNU)
# define OVR_FORCE_INLINE __attribute__((always_inline))
# define OVR_FORCE_INLINE __attribute__((always_inline)) inline
#else
# define OVR_FORCE_INLINE inline
#endif // OVR_CC_MSVC

0
interface/external/LibOVR/Src/Kernel/OVR_UTF8Util.h vendored Executable file → Normal file
View file

101
interface/external/LibOVR/Src/OVR_Device.h vendored Executable file → Normal file
View file

@ -24,14 +24,19 @@ otherwise accompanies this software in either electronic or hard copy form.
#include "Kernel/OVR_Atomic.h"
#include "Kernel/OVR_RefCount.h"
#include "Kernel/OVR_String.h"
namespace OVR {
// Declared externally
class Profile;
class ProfileManager; // << Should be renamed for consistency
// Forward declarations
class SensorDevice;
class DeviceCommon;
class DeviceManager;
// MessageHandler is a base class from which users derive to receive messages,
// its OnMessage handler will be called for messages once it is installed on
// a device. Same message handler can be installed on multiple devices.
@ -102,6 +107,8 @@ public:
virtual DeviceType GetType() const;
virtual bool GetDeviceInfo(DeviceInfo* info) const;
// returns the MessageHandler's lock
Lock* GetHandlerLock() const;
protected:
// Internal
virtual DeviceCommon* getDeviceCommon() const = 0;
@ -228,6 +235,10 @@ public:
virtual DeviceType GetType() const { return Device_Manager; }
virtual DeviceManager* GetManager() const { return const_cast<DeviceManager*>(this); }
// Every DeviceManager has an associated profile manager, which us used to store
// user settings that may affect device behavior.
virtual ProfileManager* GetProfileManager() const = 0;
// EnumerateDevices enumerates all of the available devices of the specified class,
// returning an enumerator that references the first device. An empty enumerator is
@ -251,14 +262,18 @@ public:
// End users should call DeumerateDevices<>() instead.
virtual DeviceEnumerator<> EnumerateDevicesEx(const DeviceEnumerationArgs& args) = 0;
// Creates a new DeviceManager. Only one instance of DeviceManager should be created at a time.
static DeviceManager* Create();
// Static constant for this device type, used in template cast type checks.
enum { EnumDeviceType = Device_Manager };
// Adds a device (DeviceCreateDesc*) into Devices. Returns NULL,
// if unsuccessful or device is already in the list.
virtual Ptr<DeviceCreateDesc> AddDevice_NeedsLock(const DeviceCreateDesc& createDesc) = 0;
protected:
DeviceEnumerator<> enumeratorFromHandle(const DeviceHandle& h, const DeviceEnumerationArgs& args)
{ return DeviceEnumerator<>(h, args); }
@ -368,6 +383,14 @@ public:
memcpy(DisplayDeviceName, src.DisplayDeviceName, sizeof(DisplayDeviceName));
DisplayId = src.DisplayId;
}
bool IsSameDisplay(const HMDInfo& o) const
{
return DisplayId == o.DisplayId &&
String::CompareNoCase(DisplayDeviceName,
o.DisplayDeviceName) == 0;
}
};
@ -389,10 +412,29 @@ public:
// Static constant for this device type, used in template cast type checks.
enum { EnumDeviceType = Device_HMD };
virtual DeviceType GetType() const { return Device_HMD; }
virtual DeviceType GetType() const { return Device_HMD; }
// Creates a sensor associated with this HMD.
virtual SensorDevice* GetSensor() = 0;
// Requests the currently used profile. This profile affects the
// settings reported by HMDInfo.
virtual Profile* GetProfile() const = 0;
// Obtains the currently used profile name. This is initialized to the default
// profile name, if any; it can then be changed per-device by SetProfileName.
virtual const char* GetProfileName() const = 0;
// Sets the profile user name, changing the data returned by GetProfileInfo.
virtual bool SetProfileName(const char* name) = 0;
// Disconnects from real HMD device. This HMDDevice remains as 'fake' HMD.
// SensorDevice ptr is used to restore the 'fake' HMD (can be NULL).
HMDDevice* Disconnect(SensorDevice*);
// Returns 'true' if HMD device is a 'fake' HMD (was created this way or
// 'Disconnect' method was called).
bool IsDisconnected() const;
};
@ -479,6 +521,17 @@ public:
virtual void SetCoordinateFrame(CoordinateFrame coordframe) = 0;
virtual CoordinateFrame GetCoordinateFrame() const = 0;
// Sets report rate (in Hz) of MessageBodyFrame messages (delivered through MessageHandler::OnMessage call).
// Currently supported maximum rate is 1000Hz. If the rate is set to 500 or 333 Hz then OnMessage will be
// called twice or thrice at the same 'tick'.
// If the rate is < 333 then the OnMessage / MessageBodyFrame will be called three
// times for each 'tick': the first call will contain averaged values, the second
// and third calls will provide with most recent two recorded samples.
virtual void SetReportRate(unsigned rateHz) = 0;
// Returns currently set report rate, in Hz. If 0 - error occurred.
// Note, this value may be different from the one provided for SetReportRate. The return
// value will contain the actual rate.
virtual unsigned GetReportRate() const = 0;
// Sets maximum range settings for the sensor described by SensorRange.
// The function will fail if you try to pass values outside Maximum supported
@ -509,34 +562,6 @@ struct LatencyTestConfiguration
bool SendSamples;
};
//-------------------------------------------------------------------------------------
// ***** LatencyTestCalibrate
// LatencyTestCalibrate specifies colors used for Latency Tester calibration.
struct LatencyTestCalibrate
{
LatencyTestCalibrate(const Color& value)
: Value(value)
{
}
// The color being calibrated to.
Color Value;
};
//-------------------------------------------------------------------------------------
// ***** LatencyTestStartTest
// LatencyTestStartTest specifies values used when starting the Latency Tester test.
struct LatencyTestStartTest
{
LatencyTestStartTest(const Color& targetValue)
: TargetValue(targetValue)
{
}
// The color value that the display is being set to.
Color TargetValue;
};
//-------------------------------------------------------------------------------------
// ***** LatencyTestDisplay
// LatencyTestDisplay sets the mode and contents of the Latency Tester LED display.
@ -574,19 +599,19 @@ public:
// Get configuration information from device.
virtual bool GetConfiguration(LatencyTestConfiguration* configuration) = 0;
// Used to calibrate the latency tester at the start of a test. Calibration information is lost
// Used to calibrate the latency tester at the start of a test. Display the specified color on the screen
// beneath the latency tester and then call this method. Calibration information is lost
// when power is removed from the device.
virtual bool SetCalibrate(const LatencyTestCalibrate& calibrate, bool waitFlag = false) = 0;
// Get calibration information from device.
virtual bool GetCalibrate(LatencyTestCalibrate* calibrate) = 0;
virtual bool SetCalibrate(const Color& calibrationColor, bool waitFlag = false) = 0;
// Triggers the start of a measurement. This starts the millisecond timer on the device and
// causes it to respond with the 'MessageLatencyTestStarted' message.
virtual bool SetStartTest(const LatencyTestStartTest& start, bool waitFlag = false) = 0;
virtual bool SetStartTest(const Color& targetColor, bool waitFlag = false) = 0;
// Used to set the value displayed on the LED display panel.
virtual bool SetDisplay(const LatencyTestDisplay& display, bool waitFlag = false) = 0;
virtual DeviceBase* GetDevice() { return this; }
};
} // namespace OVR

1
interface/external/LibOVR/Src/OVR_DeviceConstants.h vendored Executable file → Normal file
View file

@ -30,6 +30,7 @@ enum DeviceType
Device_HMD = 2,
Device_Sensor = 3,
Device_LatencyTester = 4,
Device_BootLoader = 5,
Device_All = 0xFF // Set for enumeration only, to enumerate all device types.
};

15
interface/external/LibOVR/Src/OVR_DeviceHandle.h vendored Executable file → Normal file
View file

@ -59,15 +59,30 @@ public:
// operator bool() returns true if Handle/Enumerator points to a valid device.
operator bool () const { return GetType() != Device_None; }
// Returns existing device, or NULL if !IsCreated. The returned ptr is
// addref-ed.
DeviceBase* GetDevice_AddRef() const;
DeviceType GetType() const;
bool GetDeviceInfo(DeviceInfo* info) const;
bool IsAvailable() const;
bool IsCreated() const;
// Returns true, if the handle contains the same device ptr
// as specified in the parameter.
bool IsDevice(DeviceBase*) const;
// Creates a device, or returns AddRefed pointer if one is already created.
// New devices start out with RefCount of 1.
DeviceBase* CreateDevice();
// Creates a device, or returns AddRefed pointer if one is already created.
// New devices start out with RefCount of 1. DeviceT is used to cast the
// DeviceBase* to a concreete type.
template <class DeviceT>
DeviceT* CreateDeviceTyped() const
{
return static_cast<DeviceT*>(DeviceHandle(*this).CreateDevice());
}
// Resets the device handle to uninitialized state.
void Clear();

57
interface/external/LibOVR/Src/OVR_DeviceImpl.h vendored Executable file → Normal file
View file

@ -30,12 +30,18 @@ namespace OVR {
class DeviceManagerImpl;
class DeviceFactory;
enum
{
Oculus_VendorId = 0x2833
};
//-------------------------------------------------------------------------------------
// Globally shared Lock implementation used for MessageHandlers.
class SharedLock
{
public:
SharedLock() : UseCount(0) {}
Lock* GetLockAddRef();
void ReleaseLock(Lock* plock);
@ -127,8 +133,8 @@ public:
RemoveNode();
}
DeviceManagerImpl* GetManagerImpl() { return pLock->pManager; }
Lock* GetLock() const { return &pLock->CreateLock; }
DeviceManagerImpl* GetManagerImpl() const { return pLock->pManager; }
Lock* GetLock() const { return &pLock->CreateLock; }
// DeviceCreateDesc reference counting is tied to Devices list management,
// see comments for HandleCount.
@ -160,12 +166,21 @@ public:
// but more searching is necessary. If this is the case UpdateMatchedCandidate will be called.
virtual MatchResult MatchDevice(const DeviceCreateDesc& other,
DeviceCreateDesc** pcandidate) const = 0;
// Called for matched candidate after all potential matches are iterated.
// Used to update HMDevice creation arguments from Sensor.
// Optional return param 'newDeviceFlag' will be set to true if the
// 'desc' refers to a new device; false, otherwise.
// Return 'false' to create new object, 'true' if done with this argument.
virtual bool UpdateMatchedCandidate(const DeviceCreateDesc&) { return false; }
virtual bool UpdateMatchedCandidate(
const DeviceCreateDesc& desc, bool* newDeviceFlag = NULL)
{ OVR_UNUSED2(desc, newDeviceFlag); return false; }
// Matches HID device to the descriptor.
virtual bool MatchHIDDevice(const HIDDeviceDesc&) const { return false; }
// Matches device by path.
virtual bool MatchDevice(const String& /*path*/) { return false; }
//protected:
DeviceFactory* const pFactory;
const DeviceType Type;
@ -292,6 +307,22 @@ public:
// Enumerates factory devices by notifying EnumerateVisitor about every
// device that is present.
virtual void EnumerateDevices(EnumerateVisitor& visitor) = 0;
// Matches vendorId/productId pair with the factory; returns 'true'
// if the factory can handle the device.
virtual bool MatchVendorProduct(UInt16 vendorId, UInt16 productId) const
{
OVR_UNUSED2(vendorId, productId);
return false;
}
// Detects the HID device and adds the DeviceCreateDesc into Devices list, if
// the device belongs to this factory. Returns 'false', if not.
virtual bool DetectHIDDevice(DeviceManager* pdevMgr, const HIDDeviceDesc& desc)
{
OVR_UNUSED2(pdevMgr, desc);
return false;
}
protected:
DeviceManagerImpl* pManager;
@ -318,10 +349,17 @@ public:
virtual bool Initialize(DeviceBase* parent);
virtual void Shutdown();
// Every DeviceManager has an associated profile manager, which is used to store
// user settings that may affect device behavior.
virtual ProfileManager* GetProfileManager() const { return pProfileManager.GetPtr(); }
// Override to return ThreadCommandQueue implementation used to post commands
// to the background device manager thread (that must be created by Initialize).
virtual ThreadCommandQueue* GetThreadQueue() = 0;
// Returns the thread id of the DeviceManager.
virtual ThreadId GetThreadId() const = 0;
virtual DeviceEnumerator<> EnumerateDevicesEx(const DeviceEnumerationArgs& args);
@ -361,11 +399,21 @@ public:
// Enumerates devices for a particular factory.
virtual Void EnumerateFactoryDevices(DeviceFactory* factory);
virtual HIDDeviceManager* GetHIDDeviceManager()
virtual HIDDeviceManager* GetHIDDeviceManager() const
{
return HidDeviceManager;
}
// Adds device (DeviceCreateDesc*) into Devices. Returns NULL,
// if unsuccessful or device is already in the list.
virtual Ptr<DeviceCreateDesc> AddDevice_NeedsLock(const DeviceCreateDesc& createDesc);
// Finds a device descriptor by path and optional type.
Ptr<DeviceCreateDesc> FindDevice(const String& path, DeviceType = Device_None);
// Finds HID device by HIDDeviceDesc.
Ptr<DeviceCreateDesc> FindHIDDevice(const HIDDeviceDesc&);
void DetectHIDDevice(const HIDDeviceDesc&);
// Manager Lock-protected list of devices.
List<DeviceCreateDesc> Devices;
@ -375,6 +423,7 @@ public:
protected:
Ptr<HIDDeviceManager> HidDeviceManager;
Ptr<ProfileManager> pProfileManager;
};

3
interface/external/LibOVR/Src/OVR_DeviceMessages.h vendored Executable file → Normal file
View file

@ -93,7 +93,8 @@ public:
float TimeDelta; // Time passed since last Body Frame, in seconds.
};
// Sent when we receive a device status changes.
// Sent when we receive a device status changes (e.g.:
// Message_DeviceAdded, Message_DeviceRemoved).
class MessageDeviceStatus : public Message
{
public:

4
interface/external/LibOVR/Src/OVR_HIDDevice.h vendored Executable file → Normal file
View file

@ -123,8 +123,8 @@ public:
enum HIDDeviceMessageType
{
HIDDeviceMessage_DeviceAdded = 0,
HIDDeviceMessage_DeviceRemoved = 1,
HIDDeviceMessage_DeviceAdded = 0,
HIDDeviceMessage_DeviceRemoved = 1
};
virtual void OnDeviceMessage(HIDDeviceMessageType messageType)

0
interface/external/LibOVR/Src/OVR_HIDDeviceBase.h vendored Executable file → Normal file
View file

50
interface/external/LibOVR/Src/OVR_HIDDeviceImpl.h vendored Executable file → Normal file
View file

@ -30,6 +30,12 @@ public:
HIDDeviceCreateDesc(const HIDDeviceCreateDesc& other)
: DeviceCreateDesc(other.pFactory, other.Type), HIDDesc(other.HIDDesc) { }
virtual bool MatchDevice(const String& path)
{
// should it be case insensitive?
return HIDDesc.Path.CompareNoCase(path) == 0;
}
HIDDeviceDesc HIDDesc;
};
@ -46,19 +52,18 @@ public:
// HIDDevice::Handler interface.
virtual void OnDeviceMessage(HIDDeviceMessageType messageType)
{
MessageType handlerMessageType = handlerMessageType = Message_DeviceAdded;
if (messageType == HIDDeviceMessage_DeviceAdded)
{
}
else if (messageType == HIDDeviceMessage_DeviceRemoved)
{
handlerMessageType = Message_DeviceRemoved;
}
else
{
OVR_ASSERT(0);
}
MessageType handlerMessageType;
switch (messageType) {
case HIDDeviceMessage_DeviceAdded:
handlerMessageType = Message_DeviceAdded;
break;
case HIDDeviceMessage_DeviceRemoved:
handlerMessageType = Message_DeviceRemoved;
break;
default: OVR_ASSERT(0); return;
}
// Do device notification.
{
@ -71,17 +76,18 @@ public:
}
}
// Do device manager notification.
DeviceManagerImpl* manager = this->GetManagerImpl();
if (handlerMessageType == Message_DeviceAdded)
{
manager->CallOnDeviceAdded(this->pCreateDesc);
}
else if (handlerMessageType == Message_DeviceRemoved)
{
manager->CallOnDeviceRemoved(this->pCreateDesc);
switch (handlerMessageType) {
case Message_DeviceAdded:
manager->CallOnDeviceAdded(this->pCreateDesc);
break;
case Message_DeviceRemoved:
manager->CallOnDeviceRemoved(this->pCreateDesc);
break;
default:;
}
}
@ -177,7 +183,7 @@ public:
}
protected:
HIDDevice* GetInternalDevice()
HIDDevice* GetInternalDevice() const
{
return InternalDevice;
}

143
interface/external/LibOVR/Src/OVR_JSON.h vendored Normal file
View file

@ -0,0 +1,143 @@
/************************************************************************************
PublicHeader: None
Filename : OVR_JSON.h
Content : JSON format reader and writer
Created : April 9, 2013
Author : Brant Lewis
Notes :
Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved.
Use of this software is subject to the terms of the Oculus license
agreement provided at the time of installation or download, or which
otherwise accompanies this software in either electronic or hard copy form.
************************************************************************************/
#ifndef OVR_JSON_H
#define OVR_JSON_H
#include "Kernel/OVR_RefCount.h"
#include "Kernel/OVR_String.h"
#include "Kernel/OVR_List.h"
namespace OVR {
// JSONItemType describes the type of JSON item, specifying the type of
// data that can be obtained from it.
enum JSONItemType
{
JSON_None = 0,
JSON_Null = 1,
JSON_Bool = 2,
JSON_Number = 3,
JSON_String = 4,
JSON_Array = 5,
JSON_Object = 6
};
//-----------------------------------------------------------------------------
// ***** JSON
// JSON object represents a JSON node that can be either a root of the JSON tree
// or a child item. Every node has a type that describes what is is.
// New JSON trees are typically loaded JSON::Load or created with JSON::Parse.
class JSON : public RefCountBase<JSON>, public ListNode<JSON>
{
protected:
List<JSON> Children;
public:
JSONItemType Type; // Type of this JSON node.
String Name; // Name part of the {Name, Value} pair in a parent object.
String Value;
double dValue;
public:
~JSON();
// *** Creation of NEW JSON objects
static JSON* CreateObject() { return new JSON(JSON_Object);}
static JSON* CreateNull() { return new JSON(JSON_Null); }
static JSON* CreateArray() { return new JSON(JSON_Array); }
static JSON* CreateBool(bool b) { return createHelper(JSON_Bool, b ? 1.0 : 0.0); }
static JSON* CreateNumber(double num) { return createHelper(JSON_Number, num); }
static JSON* CreateString(const char *s) { return createHelper(JSON_String, 0.0, s); }
// Creates a new JSON object from parsing string.
// Returns null pointer and fills in *perror in case of parse error.
static JSON* Parse(const char* buff, const char** perror = 0);
// Loads and parses a JSON object from a file.
// Returns 0 and assigns perror with error message on fail.
static JSON* Load(const char* path, const char** perror = 0);
// Saves a JSON object to a file.
bool Save(const char* path);
// *** Object Member Access
// These provide access to child items of the list.
bool HasItems() const { return Children.IsEmpty(); }
// Returns first/last child item, or null if child list is empty
JSON* GetFirstItem() { return (!Children.IsEmpty()) ? Children.GetFirst() : 0; }
JSON* GetLastItem() { return (!Children.IsEmpty()) ? Children.GetLast() : 0; }
// Counts the number of items in the object; these methods are inefficient.
unsigned GetItemCount() const;
JSON* GetItemByIndex(unsigned i);
JSON* GetItemByName(const char* name);
// Returns next item in a list of children; 0 if no more items exist.
JSON* GetNextItem(JSON* item) { return Children.IsNull(item->pNext) ? 0 : item->pNext; }
JSON* GetPrevItem(JSON* item) { return Children.IsNull(item->pPrev) ? 0 : item->pPrev; }
// Child item access functions
void AddItem(const char *string, JSON* item);
void AddNullItem(const char* name) { AddItem(name, CreateNull()); }
void AddBoolItem(const char* name, bool b) { AddItem(name, CreateBool(b)); }
void AddNumberItem(const char* name, double n) { AddItem(name, CreateNumber(n)); }
void AddStringItem(const char* name, const char* s) { AddItem(name, CreateString(s)); }
// void ReplaceItem(unsigned index, JSON* new_item);
// void DeleteItem(unsigned index);
// *** Array Element Access
// Add new elements to the end of array.
void AddArrayElement(JSON *item);
void AddArrayNumber(double n) { AddArrayElement(CreateNumber(n)); }
void AddArrayString(const char* s) { AddArrayElement(CreateString(s)); }
// Accessed array elements; currently inefficient.
int GetArraySize();
double GetArrayNumber(int index);
const char* GetArrayString(int index);
protected:
JSON(JSONItemType itemType = JSON_Object);
static JSON* createHelper(JSONItemType itemType, double dval, const char* strVal = 0);
// JSON Parsing helper functions.
const char* parseValue(const char *buff, const char** perror);
const char* parseNumber(const char *num);
const char* parseArray(const char* value, const char** perror);
const char* parseObject(const char* value, const char** perror);
const char* parseString(const char* str, const char** perror);
char* PrintValue(int depth, bool fmt);
char* PrintObject(int depth, bool fmt);
char* PrintArray(int depth, bool fmt);
};
}
#endif

22
interface/external/LibOVR/Src/OVR_LatencyTestImpl.h vendored Executable file → Normal file
View file

@ -35,6 +35,9 @@ public:
// Enumerates devices, creating and destroying relevant objects in manager.
virtual void EnumerateDevices(EnumerateVisitor& visitor);
virtual bool MatchVendorProduct(UInt16 vendorId, UInt16 productId) const;
virtual bool DetectHIDDevice(DeviceManager* pdevMgr, const HIDDeviceDesc& desc);
protected:
DeviceManager* getManager() const { return (DeviceManager*) pManager; }
};
@ -60,13 +63,18 @@ public:
if ((other.Type == Device_LatencyTester) && (pFactory == other.pFactory))
{
const LatencyTestDeviceCreateDesc& s2 = (const LatencyTestDeviceCreateDesc&) other;
if ((HIDDesc.Path == s2.HIDDesc.Path) &&
(HIDDesc.SerialNumber == s2.HIDDesc.SerialNumber))
if (MatchHIDDevice(s2.HIDDesc))
return Match_Found;
}
return Match_None;
}
virtual bool MatchHIDDevice(const HIDDeviceDesc& hidDesc) const
{
// should paths comparison be case insensitive?
return ((HIDDesc.Path.CompareNoCase(hidDesc.Path) == 0) &&
(HIDDesc.SerialNumber == hidDesc.SerialNumber));
}
virtual bool GetDeviceInfo(DeviceInfo* info) const;
};
@ -93,10 +101,9 @@ public:
virtual bool SetConfiguration(const OVR::LatencyTestConfiguration& configuration, bool waitFlag = false);
virtual bool GetConfiguration(OVR::LatencyTestConfiguration* configuration);
virtual bool SetCalibrate(const OVR::LatencyTestCalibrate& calibrate, bool waitFlag = false);
virtual bool GetCalibrate(OVR::LatencyTestCalibrate* calibrate);
virtual bool SetCalibrate(const Color& calibrationColor, bool waitFlag = false);
virtual bool SetStartTest(const OVR::LatencyTestStartTest& start, bool waitFlag = false);
virtual bool SetStartTest(const Color& targetColor, bool waitFlag = false);
virtual bool SetDisplay(const LatencyTestDisplay& display, bool waitFlag = false);
protected:
@ -109,9 +116,8 @@ protected:
bool setConfiguration(const OVR::LatencyTestConfiguration& configuration);
bool getConfiguration(OVR::LatencyTestConfiguration* configuration);
bool setCalibrate(const OVR::LatencyTestCalibrate& calibrate);
bool getCalibrate(OVR::LatencyTestCalibrate* calibrate);
bool setStartTest(const OVR::LatencyTestStartTest& start);
bool setCalibrate(const Color& calibrationColor);
bool setStartTest(const Color& targetColor);
bool setDisplay(const OVR::LatencyTestDisplay& display);
// Called for decoded messages

View file

@ -0,0 +1,111 @@
/************************************************************************************
Filename : OVR_Linux_DeviceManager.h
Content : Linux-specific DeviceManager header.
Created :
Authors :
Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved.
Use of this software is subject to the terms of the Oculus license
agreement provided at the time of installation or download, or which
otherwise accompanies this software in either electronic or hard copy form.
*************************************************************************************/
#ifndef OVR_Linux_DeviceManager_h
#define OVR_Linux_DeviceManager_h
#include "OVR_DeviceImpl.h"
#include <unistd.h>
#include <sys/poll.h>
namespace OVR { namespace Linux {
class DeviceManagerThread;
//-------------------------------------------------------------------------------------
// ***** Linux DeviceManager
class DeviceManager : public DeviceManagerImpl
{
public:
DeviceManager();
~DeviceManager();
// Initialize/Shutdowncreate and shutdown manger thread.
virtual bool Initialize(DeviceBase* parent);
virtual void Shutdown();
virtual ThreadCommandQueue* GetThreadQueue();
virtual ThreadId GetThreadId() const;
virtual DeviceEnumerator<> EnumerateDevicesEx(const DeviceEnumerationArgs& args);
virtual bool GetDeviceInfo(DeviceInfo* info) const;
Ptr<DeviceManagerThread> pThread;
};
//-------------------------------------------------------------------------------------
// ***** Device Manager Background Thread
class DeviceManagerThread : public Thread, public ThreadCommandQueue
{
friend class DeviceManager;
enum { ThreadStackSize = 64 * 1024 };
public:
DeviceManagerThread();
~DeviceManagerThread();
virtual int Run();
// ThreadCommandQueue notifications for CommandEvent handling.
virtual void OnPushNonEmpty_Locked() { write(CommandFd[1], this, 1); }
virtual void OnPopEmpty_Locked() { }
class Notifier
{
public:
// Called when I/O is received
virtual void OnEvent(int i, int fd) = 0;
// Called when timing ticks are updated.
// Returns the largest number of microseconds this function can
// wait till next call.
virtual UInt64 OnTicks(UInt64 ticksMks)
{
OVR_UNUSED1(ticksMks);
return Timer::MksPerSecond * 1000;
}
};
// Add I/O notifier
bool AddSelectFd(Notifier* notify, int fd);
bool RemoveSelectFd(Notifier* notify, int fd);
// Add notifier that will be called at regular intervals.
bool AddTicksNotifier(Notifier* notify);
bool RemoveTicksNotifier(Notifier* notify);
private:
bool threadInitialized() { return CommandFd[0] != 0; }
// pipe used to signal commands
int CommandFd[2];
Array<struct pollfd> PollFds;
Array<Notifier*> FdNotifiers;
Event StartupEvent;
// Ticks notifiers - used for time-dependent events such as keep-alive.
Array<Notifier*> TicksNotifiers;
};
}} // namespace Linux::OVR
#endif // OVR_Linux_DeviceManager_h

View file

@ -0,0 +1,124 @@
/************************************************************************************
Filename : OVR_Linux_HIDDevice.h
Content : Linux HID device implementation.
Created : June 13, 2013
Authors : Brant Lewis
Copyright : Copyright 2013 Oculus VR, Inc. All Rights reserved.
Use of this software is subject to the terms of the Oculus license
agreement provided at the time of installation or download, or which
otherwise accompanies this software in either electronic or hard copy form.
*************************************************************************************/
#ifndef OVR_LINUX_HIDDevice_h
#define OVR_LINUX_HIDDevice_h
#include "OVR_HIDDevice.h"
#include "OVR_Linux_DeviceManager.h"
#include <libudev.h>
namespace OVR { namespace Linux {
class HIDDeviceManager;
//-------------------------------------------------------------------------------------
// ***** Linux HIDDevice
class HIDDevice : public OVR::HIDDevice, public DeviceManagerThread::Notifier
{
private:
friend class HIDDeviceManager;
public:
HIDDevice(HIDDeviceManager* manager);
// This is a minimal constructor used during enumeration for us to pass
// a HIDDevice to the visit function (so that it can query feature reports).
HIDDevice(HIDDeviceManager* manager, int device_handle);
virtual ~HIDDevice();
bool HIDInitialize(const String& path);
void HIDShutdown();
virtual bool SetFeatureReport(UByte* data, UInt32 length);
virtual bool GetFeatureReport(UByte* data, UInt32 length);
// DeviceManagerThread::Notifier
void OnEvent(int i, int fd);
UInt64 OnTicks(UInt64 ticksMks);
bool OnDeviceNotification(MessageType messageType,
HIDDeviceDesc* device_info,
bool* error);
private:
bool initInfo();
bool openDevice(const char* dev_path);
void closeDevice(bool wasUnplugged);
void closeDeviceOnIOError();
bool setupDevicePluggedInNotification();
bool InMinimalMode;
HIDDeviceManager* HIDManager;
int DeviceHandle; // file handle to the device
HIDDeviceDesc DevDesc;
enum { ReadBufferSize = 96 };
UByte ReadBuffer[ReadBufferSize];
UInt16 InputReportBufferLength;
UInt16 OutputReportBufferLength;
UInt16 FeatureReportBufferLength;
};
//-------------------------------------------------------------------------------------
// ***** Linux HIDDeviceManager
class HIDDeviceManager : public OVR::HIDDeviceManager, public DeviceManagerThread::Notifier
{
friend class HIDDevice;
public:
HIDDeviceManager(Linux::DeviceManager* Manager);
virtual ~HIDDeviceManager();
virtual bool Initialize();
virtual void Shutdown();
virtual bool Enumerate(HIDEnumerateVisitor* enumVisitor);
virtual OVR::HIDDevice* Open(const String& path);
static HIDDeviceManager* CreateInternal(DeviceManager* manager);
void OnEvent(int i, int fd);
private:
bool initializeManager();
bool initVendorProductVersion(udev_device* device, HIDDeviceDesc* pDevDesc);
bool getPath(udev_device* device, String* pPath);
bool getIntProperty(udev_device* device, const char* key, int32_t* pResult);
bool getStringProperty(udev_device* device,
const char* propertyName,
OVR::String* pResult);
bool getFullDesc(udev_device* device, HIDDeviceDesc* desc);
bool GetDescriptorFromPath(const char* dev_path, HIDDeviceDesc* desc);
bool AddNotificationDevice(HIDDevice* device);
bool RemoveNotificationDevice(HIDDevice* device);
DeviceManager* DevManager;
udev* UdevInstance; // a handle to the udev library instance
udev_monitor* HIDMonitor;
int HIDMonHandle; // the udev_monitor file handle
Array<HIDDevice*> NotificationDevices;
};
}} // namespace OVR::Linux
#endif // OVR_Linux_HIDDevice_h

View file

@ -0,0 +1,156 @@
/************************************************************************************
Filename : OVR_Linux_HMDDevice.h
Content : Linux HMDDevice implementation
Created : June 17, 2013
Authors : Brant Lewis
Copyright : Copyright 2013 Oculus VR, Inc. All Rights reserved.
Use of this software is subject to the terms of the Oculus license
agreement provided at the time of installation or download, or which
otherwise accompanies this software in either electronic or hard copy form.
*************************************************************************************/
#ifndef OVR_Linux_HMDDevice_h
#define OVR_Linux_HMDDevice_h
#include "OVR_Linux_DeviceManager.h"
#include "OVR_Profile.h"
namespace OVR { namespace Linux {
class HMDDevice;
//-------------------------------------------------------------------------------------
// HMDDeviceFactory enumerates attached Oculus HMD devices.
//
// This is currently done by matching monitor device strings.
class HMDDeviceFactory : public DeviceFactory
{
public:
static HMDDeviceFactory Instance;
// Enumerates devices, creating and destroying relevant objects in manager.
virtual void EnumerateDevices(EnumerateVisitor& visitor);
protected:
DeviceManager* getManager() const { return (DeviceManager*) pManager; }
};
class HMDDeviceCreateDesc : public DeviceCreateDesc
{
friend class HMDDevice;
protected:
enum
{
Contents_Screen = 1,
Contents_Distortion = 2,
Contents_7Inch = 4,
};
String DeviceId;
String DisplayDeviceName;
int DesktopX, DesktopY;
unsigned Contents;
unsigned HResolution, VResolution;
float HScreenSize, VScreenSize;
long DisplayId;
float DistortionK[4];
public:
HMDDeviceCreateDesc(DeviceFactory* factory, const String& displayDeviceName, long dispId);
HMDDeviceCreateDesc(const HMDDeviceCreateDesc& other);
virtual DeviceCreateDesc* Clone() const
{
return new HMDDeviceCreateDesc(*this);
}
virtual DeviceBase* NewDeviceInstance();
virtual MatchResult MatchDevice(const DeviceCreateDesc& other,
DeviceCreateDesc**) const;
// Matches device by path.
virtual bool MatchDevice(const String& path);
virtual bool UpdateMatchedCandidate(const DeviceCreateDesc&, bool* newDeviceFlag = NULL);
virtual bool GetDeviceInfo(DeviceInfo* info) const;
// Requests the currently used default profile. This profile affects the
// settings reported by HMDInfo.
Profile* GetProfileAddRef() const;
ProfileType GetProfileType() const
{
return (HResolution >= 1920) ? Profile_RiftDKHD : Profile_RiftDK1;
}
void SetScreenParameters(int x, int y, unsigned hres, unsigned vres, float hsize, float vsize)
{
DesktopX = x;
DesktopY = y;
HResolution = hres;
VResolution = vres;
HScreenSize = hsize;
VScreenSize = vsize;
Contents |= Contents_Screen;
}
void SetDistortion(const float* dks)
{
for (int i = 0; i < 4; i++)
DistortionK[i] = dks[i];
Contents |= Contents_Distortion;
}
void Set7Inch() { Contents |= Contents_7Inch; }
bool Is7Inch() const;
};
//-------------------------------------------------------------------------------------
// HMDDevice represents an Oculus HMD device unit. An instance of this class
// is typically created from the DeviceManager.
// After HMD device is created, we its sensor data can be obtained by
// first creating a Sensor object and then wrappig it in SensorFusion.
class HMDDevice : public DeviceImpl<OVR::HMDDevice>
{
public:
HMDDevice(HMDDeviceCreateDesc* createDesc);
~HMDDevice();
virtual bool Initialize(DeviceBase* parent);
virtual void Shutdown();
// Requests the currently used default profile. This profile affects the
// settings reported by HMDInfo.
virtual Profile* GetProfile() const;
virtual const char* GetProfileName() const;
virtual bool SetProfileName(const char* name);
// Query associated sensor.
virtual OVR::SensorDevice* GetSensor();
protected:
HMDDeviceCreateDesc* getDesc() const { return (HMDDeviceCreateDesc*)pCreateDesc.GetPtr(); }
// User name for the profile used with this device.
String ProfileName;
mutable Ptr<Profile> pCachedProfile;
};
}} // namespace OVR::Linux
#endif // OVR_Linux_HMDDevice_h

12
interface/external/LibOVR/Src/OVR_OSX_DeviceManager.h vendored Executable file → Normal file
View file

@ -21,6 +21,8 @@ otherwise accompanies this software in either electronic or hard copy form.
#include "Kernel/OVR_Timer.h"
#include <IOKit/hid/IOHIDManager.h>
#include <CoreGraphics/CGDirectDisplay.h>
#include <CoreGraphics/CGDisplayConfiguration.h>
namespace OVR { namespace OSX {
@ -41,11 +43,18 @@ public:
virtual void Shutdown();
virtual ThreadCommandQueue* GetThreadQueue();
virtual ThreadId GetThreadId() const;
virtual DeviceEnumerator<> EnumerateDevicesEx(const DeviceEnumerationArgs& args);
virtual bool GetDeviceInfo(DeviceInfo* info) const;
protected:
static void displayReconfigurationCallBack (CGDirectDisplayID display,
CGDisplayChangeSummaryFlags flags,
void *userInfo);
public: // data
Ptr<DeviceManagerThread> pThread;
};
@ -90,6 +99,7 @@ public:
CFRunLoopRef GetRunLoop()
{ return RunLoop; }
void Shutdown();
private:
CFRunLoopRef RunLoop;

9
interface/external/LibOVR/Src/OVR_OSX_HIDDevice.h vendored Executable file → Normal file
View file

@ -45,8 +45,8 @@ public:
bool HIDInitialize(const String& path);
void HIDShutdown();
bool SetFeatureReport(UByte* data, UInt32 length);
bool GetFeatureReport(UByte* data, UInt32 length);
virtual bool SetFeatureReport(UByte* data, UInt32 length);
virtual bool GetFeatureReport(UByte* data, UInt32 length);
bool Write(UByte* data, UInt32 length);
@ -134,6 +134,11 @@ private:
bool getStringProperty(IOHIDDeviceRef device, CFStringRef propertyName, String* pResult);
bool getFullDesc(IOHIDDeviceRef device, HIDDeviceDesc* desc);
static void staticDeviceMatchingCallback(void *inContext,
IOReturn inResult,
void *inSender,
IOHIDDeviceRef inIOHIDDeviceRef);
DeviceManager* DevManager;
IOHIDManagerRef HIDManager;

26
interface/external/LibOVR/Src/OVR_OSX_HMDDevice.h vendored Executable file → Normal file
View file

@ -18,6 +18,7 @@ otherwise accompanies this software in either electronic or hard copy form.
#include "OVR_DeviceImpl.h"
#include <Kernel/OVR_String.h>
#include "OVR_Profile.h"
namespace OVR { namespace OSX {
@ -71,10 +72,19 @@ public:
virtual MatchResult MatchDevice(const DeviceCreateDesc& other,
DeviceCreateDesc**) const;
virtual bool UpdateMatchedCandidate(const DeviceCreateDesc&);
virtual bool UpdateMatchedCandidate(const DeviceCreateDesc&, bool* newDeviceFlag = NULL);
virtual bool GetDeviceInfo(DeviceInfo* info) const;
// Requests the currently used default profile. This profile affects the
// settings reported by HMDInfo.
Profile* GetProfileAddRef() const;
ProfileType GetProfileType() const
{
return (HResolution >= 1920) ? Profile_RiftDKHD : Profile_RiftDK1;
}
void SetScreenParameters(int x, int y, unsigned hres, unsigned vres, float hsize, float vsize)
{
DesktopX = x;
@ -125,8 +135,22 @@ public:
virtual bool Initialize(DeviceBase* parent);
virtual void Shutdown();
// Requests the currently used default profile. This profile affects the
// settings reported by HMDInfo.
virtual Profile* GetProfile() const;
virtual const char* GetProfileName() const;
virtual bool SetProfileName(const char* name);
// Query associated sensor.
virtual OVR::SensorDevice* GetSensor();
protected:
HMDDeviceCreateDesc* getDesc() const { return (HMDDeviceCreateDesc*)pCreateDesc.GetPtr(); }
// User name for the profile used with this device.
String ProfileName;
mutable Ptr<Profile> pCachedProfile;
};

View file

@ -0,0 +1,243 @@
/************************************************************************************
PublicHeader: OVR.h
Filename : OVR_Profile.h
Content : Structs and functions for loading and storing device profile settings
Created : February 14, 2013
Notes :
Profiles are used to store per-user settings that can be transferred and used
across multiple applications. For example, player IPD can be configured once
and reused for a unified experience across games. Configuration and saving of profiles
can be accomplished in game via the Profile API or by the official Oculus Configuration
Utility.
Copyright : Copyright 2013 Oculus VR, Inc. All Rights reserved.
Use of this software is subject to the terms of the Oculus license
agreement provided at the time of installation or download, or which
otherwise accompanies this software in either electronic or hard copy form.
************************************************************************************/
#ifndef OVR_Profile_h
#define OVR_Profile_h
#include "Kernel/OVR_String.h"
#include "Kernel/OVR_RefCount.h"
#include "Kernel/OVR_Array.h"
namespace OVR {
// Defines the profile object for each device type
enum ProfileType
{
Profile_Unknown = 0,
Profile_GenericHMD = 10,
Profile_RiftDK1 = 11,
Profile_RiftDKHD = 12,
};
class Profile;
// -----------------------------------------------------------------------------
// ***** ProfileManager
// Profiles are interfaced through a ProfileManager object. Applications should
// create a ProfileManager each time they intend to read or write user profile data.
// The scope of the ProfileManager object defines when disk I/O is performed. Disk
// reads are performed on the first profile access and disk writes are performed when
// the ProfileManager goes out of scope. All profile interactions between these times
// are performed in local memory and are fast. A typical profile interaction might
// look like this:
//
// {
// Ptr<ProfileManager> pm = *ProfileManager::Create();
// Ptr<Profile> profile = pm->LoadProfile(Profile_RiftDK1,
// pm->GetDefaultProfileName(Profile_RiftDK1));
// if (profile)
// { // Retrieve the current profile settings
// }
// } // Profile will be destroyed and any disk I/O completed when going out of scope
class ProfileManager : public RefCountBase<ProfileManager>
{
protected:
// Synchronize ProfileManager access since it may be accessed from multiple threads,
// as it's shared through DeviceManager.
Lock ProfileLock;
Array<Ptr<Profile> > ProfileCache;
ProfileType CacheDevice;
String DefaultProfile;
bool Changed;
char NameBuff[32];
public:
static ProfileManager* Create();
// Static interface functions
int GetProfileCount(ProfileType device);
const char* GetProfileName(ProfileType device, unsigned int index);
bool HasProfile(ProfileType device, const char* name);
Profile* LoadProfile(ProfileType device, unsigned int index);
Profile* LoadProfile(ProfileType device, const char* name);
Profile* GetDeviceDefaultProfile(ProfileType device);
const char* GetDefaultProfileName(ProfileType device);
bool SetDefaultProfileName(ProfileType device, const char* name);
bool Save(const Profile* profile);
bool Delete(const Profile* profile);
protected:
ProfileManager();
~ProfileManager();
void LoadCache(ProfileType device);
void SaveCache();
void ClearCache();
Profile* CreateProfileObject(const char* user,
ProfileType device,
const char** device_name);
};
//-------------------------------------------------------------------
// ***** Profile
// The base profile for all users. This object is not created directly.
// Instead derived device objects provide add specific device members to
// the base profile
class Profile : public RefCountBase<Profile>
{
public:
enum { MaxNameLen = 32 };
enum GenderType
{
Gender_Unspecified = 0,
Gender_Male = 1,
Gender_Female = 2
};
ProfileType Type; // The type of device profile
char Name[MaxNameLen]; // The name given to this profile
protected:
GenderType Gender; // The gender of the user
float PlayerHeight; // The height of the user in meters
float IPD; // Distance between eyes in meters
public:
virtual Profile* Clone() const = 0;
// These are properties which are intrinsic to the user and affect scene setup
GenderType GetGender() { return Gender; };
float GetPlayerHeight() { return PlayerHeight; };
float GetIPD() { return IPD; };
float GetEyeHeight();
void SetGender(GenderType gender) { Gender = gender; };
void SetPlayerHeight(float height) { PlayerHeight = height; };
void SetIPD(float ipd) { IPD = ipd; };
protected:
Profile(ProfileType type, const char* name);
virtual bool ParseProperty(const char* prop, const char* sval);
friend class ProfileManager;
};
//-----------------------------------------------------------------------------
// ***** HMDProfile
// The generic HMD profile is used for properties that are common to all headsets
class HMDProfile : public Profile
{
protected:
// FOV extents in pixels measured by a user
int LL; // left eye outer extent
int LR; // left eye inner extent
int RL; // right eye inner extent
int RR; // right eye outer extent
public:
virtual Profile* Clone() const;
void SetLL(int val) { LL = val; };
void SetLR(int val) { LR = val; };
void SetRL(int val) { RL = val; };
void SetRR(int val) { RR = val; };
int GetLL() { return LL; };
int GetLR() { return LR; };
int GetRL() { return RL; };
int GetRR() { return RR; };
protected:
HMDProfile(ProfileType type, const char* name);
virtual bool ParseProperty(const char* prop, const char* sval);
friend class ProfileManager;
};
// For headsets that use eye cups
enum EyeCupType
{
EyeCup_A = 0,
EyeCup_B = 1,
EyeCup_C = 2
};
//-----------------------------------------------------------------------------
// ***** RiftDK1Profile
// This profile is specific to the Rift Dev Kit 1 and contains overrides specific
// to that device and lens cup settings.
class RiftDK1Profile : public HMDProfile
{
protected:
EyeCupType EyeCups; // Which eye cup does the player use
public:
virtual Profile* Clone() const;
EyeCupType GetEyeCup() { return EyeCups; };
void SetEyeCup(EyeCupType cup) { EyeCups = cup; };
protected:
RiftDK1Profile(const char* name);
virtual bool ParseProperty(const char* prop, const char* sval);
friend class ProfileManager;
};
//-----------------------------------------------------------------------------
// ***** RiftDKHDProfile
// This profile is specific to the Rift HD Dev Kit and contains overrides specific
// to that device and lens cup settings.
class RiftDKHDProfile : public HMDProfile
{
protected:
EyeCupType EyeCups; // Which eye cup does the player use
public:
virtual Profile* Clone() const;
EyeCupType GetEyeCup() { return EyeCups; };
void SetEyeCup(EyeCupType cup) { EyeCups = cup; };
protected:
RiftDKHDProfile(const char* name);
virtual bool ParseProperty(const char* prop, const char* sval);
friend class ProfileManager;
};
String GetBaseOVRPath(bool create_dir);
}
#endif // OVR_Profile_h

203
interface/external/LibOVR/Src/OVR_SensorFilter.h vendored Executable file → Normal file
View file

@ -4,7 +4,7 @@ PublicHeader: OVR.h
Filename : OVR_SensorFilter.h
Content : Basic filtering of sensor data
Created : March 7, 2013
Authors : Steve LaValle, Anna Yershova
Authors : Steve LaValle, Anna Yershova, Max Katsev
Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved.
@ -22,74 +22,179 @@ otherwise accompanies this software in either electronic or hard copy form.
namespace OVR {
// This class maintains a sliding window of sensor data taken over time and implements
// various simple filters, most of which are linear functions of the data history.
class SensorFilter
// A simple circular buffer data structure that stores last N elements in an array
template <typename T>
class CircularBuffer
{
protected:
enum
{
MaxFilterSize = 100,
DefaultFilterSize = 20
DefaultFilterCapacity = 20
};
private:
int LastIdx; // The index of the last element that was added to the array
int Size; // The window size (number of elements)
Vector3f Elements[MaxFilterSize];
int LastIdx; // The index of the last element that was added to the buffer
int Capacity; // The buffer size (maximum number of elements)
int Count; // Number of elements in the filter
T* Elements;
public:
// Create a new filter with default size
SensorFilter()
CircularBuffer(int capacity = DefaultFilterCapacity)
: LastIdx(-1), Capacity(capacity), Count(0)
{
LastIdx = -1;
Size = DefaultFilterSize;
};
Elements = (T*)OVR_ALLOC(capacity * sizeof(T));
for (int i = 0; i < Capacity; i++)
Elements[i] = T();
}
// Create a new filter with size i
SensorFilter(const int &i)
~CircularBuffer() {
OVR_FREE(Elements);
}
private:
// Make the class non-copyable
CircularBuffer(const CircularBuffer& other);
CircularBuffer& operator=(const CircularBuffer& other);
public:
// Add a new element to the filter
void AddElement (const T &e)
{
OVR_ASSERT(i <= MaxFilterSize);
LastIdx = -1;
Size = i;
};
// Create a new element to the filter
void AddElement (const Vector3f &e)
{
if (LastIdx == Size - 1)
LastIdx = 0;
else
LastIdx++;
LastIdx = (LastIdx + 1) % Capacity;
Elements[LastIdx] = e;
};
if (Count < Capacity)
Count++;
}
// Get element i. 0 is the most recent, 1 is one step ago, 2 is two steps ago, ...
Vector3f GetPrev(const int &i)
T GetPrev(int i = 0) const
{
int idx = (LastIdx - i) % Size;
if (idx < 0) // Fix the negative mod problem
idx += Size;
OVR_ASSERT(i >= 0);
if (i >= Count) // return 0 if the filter doesn't have enough elements
return T();
int idx = (LastIdx - i);
if (idx < 0) // Fix the wraparound case
idx += Capacity;
OVR_ASSERT(idx >= 0); // Multiple wraparounds not allowed
return Elements[idx];
};
}
};
// A base class for filters that maintains a buffer of sensor data taken over time and implements
// various simple filters, most of which are linear functions of the data history.
// Maintains the running sum of its elements for better performance on large capacity values
template <typename T>
class SensorFilterBase : public CircularBuffer<T>
{
protected:
T RunningTotal; // Cached sum of the elements
public:
SensorFilterBase(int capacity = CircularBuffer<T>::DefaultFilterCapacity) : CircularBuffer<T>(capacity), RunningTotal() { };
// Add a new element to the filter
// Updates the running sum value
void AddElement (const T &e)
{
int NextIdx = (this->LastIdx + 1) % this->Capacity;
RunningTotal += (e - this->Elements[NextIdx]);
CircularBuffer<T>::AddElement(e);
if (this->LastIdx == 0)
{
// update the cached total to avoid error accumulation
RunningTotal = T();
for (int i = 0; i < this->Count; i++)
RunningTotal += this->Elements[i];
}
}
// Simple statistics
Vector3f Total();
Vector3f Mean();
Vector3f Median();
Vector3f Variance(); // The diagonal of covariance matrix
Matrix4f Covariance();
Vector3f PearsonCoefficient();
T Total() const
{
return RunningTotal;
}
T Mean() const
{
return (this->Count == 0) ? T() : (Total() / (float) this->Count);
}
// A popular family of smoothing filters and smoothed derivatives
Vector3f SavitzkyGolaySmooth8();
Vector3f SavitzkyGolayDerivative4();
Vector3f SavitzkyGolayDerivative5();
Vector3f SavitzkyGolayDerivative12();
Vector3f SavitzkyGolayDerivativeN(const int &n);
T SavitzkyGolaySmooth8() const
{
OVR_ASSERT(this->Capacity >= 8);
return this->GetPrev(0)*0.41667f +
this->GetPrev(1)*0.33333f +
this->GetPrev(2)*0.25f +
this->GetPrev(3)*0.16667f +
this->GetPrev(4)*0.08333f -
this->GetPrev(6)*0.08333f -
this->GetPrev(7)*0.16667f;
}
~SensorFilter() {};
T SavitzkyGolayDerivative4() const
{
OVR_ASSERT(this->Capacity >= 4);
return this->GetPrev(0)*0.3f +
this->GetPrev(1)*0.1f -
this->GetPrev(2)*0.1f -
this->GetPrev(3)*0.3f;
}
T SavitzkyGolayDerivative5() const
{
OVR_ASSERT(this->Capacity >= 5);
return this->GetPrev(0)*0.2f +
this->GetPrev(1)*0.1f -
this->GetPrev(3)*0.1f -
this->GetPrev(4)*0.2f;
}
T SavitzkyGolayDerivative12() const
{
OVR_ASSERT(this->Capacity >= 12);
return this->GetPrev(0)*0.03846f +
this->GetPrev(1)*0.03147f +
this->GetPrev(2)*0.02448f +
this->GetPrev(3)*0.01748f +
this->GetPrev(4)*0.01049f +
this->GetPrev(5)*0.0035f -
this->GetPrev(6)*0.0035f -
this->GetPrev(7)*0.01049f -
this->GetPrev(8)*0.01748f -
this->GetPrev(9)*0.02448f -
this->GetPrev(10)*0.03147f -
this->GetPrev(11)*0.03846f;
}
T SavitzkyGolayDerivativeN(int n) const
{
OVR_ASSERT(this->Capacity >= n);
int m = (n-1)/2;
T result = T();
for (int k = 1; k <= m; k++)
{
int ind1 = m - k;
int ind2 = n - m + k - 1;
result += (this->GetPrev(ind1) - this->GetPrev(ind2)) * (float) k;
}
float coef = 3.0f/(m*(m+1.0f)*(2.0f*m+1.0f));
result = result*coef;
return result;
}
};
// This class maintains a buffer of sensor data taken over time and implements
// various simple filters, most of which are linear functions of the data history.
class SensorFilter : public SensorFilterBase<Vector3f>
{
public:
SensorFilter(int capacity = DefaultFilterCapacity) : SensorFilterBase<Vector3f>(capacity) { };
// Simple statistics
Vector3f Median() const;
Vector3f Variance() const; // The diagonal of covariance matrix
Matrix4f Covariance() const;
Vector3f PearsonCoefficient() const;
};
} //namespace OVR

267
interface/external/LibOVR/Src/OVR_SensorFusion.h vendored Executable file → Normal file
View file

@ -4,7 +4,7 @@ PublicHeader: OVR.h
Filename : OVR_SensorFusion.h
Content : Methods that determine head orientation from sensor data over time
Created : October 9, 2012
Authors : Michael Antonov, Steve LaValle
Authors : Michael Antonov, Steve LaValle, Max Katsev
Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved.
@ -19,6 +19,7 @@ otherwise accompanies this software in either electronic or hard copy form.
#include "OVR_Device.h"
#include "OVR_SensorFilter.h"
#include <time.h>
namespace OVR {
@ -27,6 +28,8 @@ namespace OVR {
// SensorFusion class accumulates Sensor notification messages to keep track of
// orientation, which involves integrating the gyro and doing correction with gravity.
// Magnetometer based yaw drift correction is also supported; it is usually enabled
// automatically based on loaded magnetometer configuration.
// Orientation is reported as a quaternion, from which users can obtain either the
// rotation matrix or Euler angles.
//
@ -35,11 +38,20 @@ namespace OVR {
// - By attaching SensorFusion to a SensorDevice, in which case it will
// automatically handle notifications from that device.
class SensorFusion : public NewOverrideBase
{
enum
{
MagMaxReferences = 1000
};
public:
SensorFusion(SensorDevice* sensor = 0);
~SensorFusion();
// *** Setup
// Attaches this SensorFusion to a sensor device, from which it will receive
// notification messages. If a sensor is attached, manual message notification
@ -47,53 +59,108 @@ public:
bool AttachToSensor(SensorDevice* sensor);
// Returns true if this Sensor fusion object is attached to a sensor.
bool IsAttachedToSensor() const { return Handler.IsHandlerInstalled(); }
bool IsAttachedToSensor() const { return Handler.IsHandlerInstalled(); }
void SetGravityEnabled(bool enableGravity) { EnableGravity = enableGravity; }
bool IsGravityEnabled() const { return EnableGravity;}
void SetYawCorrectionEnabled(bool enableYawCorrection) { EnableYawCorrection = enableYawCorrection; }
// Yaw correction is set up to work
bool IsYawCorrectionEnabled() const { return EnableYawCorrection;}
// Yaw correction is currently working (forcing a corrective yaw rotation)
bool IsYawCorrectionInProgress() const { return YawCorrectionInProgress;}
// *** State Query
// Obtain the current accumulated orientation. Many apps will want to use GetPredictedOrientation
// instead to reduce latency.
Quatf GetOrientation() const { return lockedGet(&Q); }
// Get predicted orientaion in the near future; predictDt is lookahead amount in seconds.
Quatf GetPredictedOrientation(float predictDt);
Quatf GetPredictedOrientation() { return GetPredictedOrientation(PredictionDT); }
// Obtain the last absolute acceleration reading, in m/s^2.
Vector3f GetAcceleration() const { return lockedGet(&A); }
// Obtain the last angular velocity reading, in rad/s.
Vector3f GetAngularVelocity() const { return lockedGet(&AngV); }
// Obtain the last raw magnetometer reading, in Gauss
Vector3f GetMagnetometer() const { return lockedGet(&RawMag); }
// Obtain the calibrated magnetometer reading (direction and field strength)
Vector3f GetCalibratedMagnetometer() const { OVR_ASSERT(MagCalibrated); return lockedGet(&CalMag); }
// Resets the current orientation.
void Reset();
// *** Configuration
void EnableMotionTracking(bool enable = true) { MotionTrackingEnabled = enable; }
bool IsMotionTrackingEnabled() const { return MotionTrackingEnabled; }
// *** Prediction Control
// Prediction functions.
// Prediction delta specifes how much prediction should be applied in seconds; it should in
// general be under the average rendering latency. Call GetPredictedOrientation() to get
// predicted orientation.
float GetPredictionDelta() const { return PredictionDT; }
void SetPrediction(float dt, bool enable = true) { PredictionDT = dt; EnablePrediction = enable; }
void SetPredictionEnabled(bool enable = true) { EnablePrediction = enable; }
bool IsPredictionEnabled() { return EnablePrediction; }
// *** Accelerometer/Gravity Correction Control
// Enables/disables gravity correction (on by default).
void SetGravityEnabled(bool enableGravity) { EnableGravity = enableGravity; }
bool IsGravityEnabled() const { return EnableGravity;}
// Gain used to correct gyro with accel. Default value is appropriate for typical use.
float GetAccelGain() const { return Gain; }
void SetAccelGain(float ag) { Gain = ag; }
// *** Magnetometer and Yaw Drift Correction Control
// Methods to load and save a mag calibration. Calibrations can optionally
// be specified by name to differentiate multiple calibrations under different conditions
// If LoadMagCalibration succeeds, it will override YawCorrectionEnabled based on
// saved calibration setting.
bool SaveMagCalibration(const char* calibrationName = NULL) const;
bool LoadMagCalibration(const char* calibrationName = NULL);
// Enables/disables magnetometer based yaw drift correction. Must also have mag calibration
// data for this correction to work.
void SetYawCorrectionEnabled(bool enable) { EnableYawCorrection = enable; }
// Determines if yaw correction is enabled.
bool IsYawCorrectionEnabled() const { return EnableYawCorrection;}
// Store the calibration matrix for the magnetometer
void SetMagCalibration(const Matrix4f& m)
{
MagCalibrationMatrix = m;
time(&MagCalibrationTime); // time stamp the calibration
MagCalibrated = true;
}
// Retrieves the magnetometer calibration matrix
Matrix4f GetMagCalibration() const { return MagCalibrationMatrix; }
// Retrieve the time of the calibration
time_t GetMagCalibrationTime() const { return MagCalibrationTime; }
// True only if the mag has calibration values stored
bool HasMagCalibration() const { return MagCalibrated;}
bool HasMagCalibration() const { return MagCalibrated;}
// Force the mag into the uncalibrated state
void ClearMagCalibration()
{
MagCalibrated = false;
MagReady = false;
}
void ClearMagCalibration() { MagCalibrated = false; }
// Set the magnetometer's reference orientation for use in yaw correction
void SetMagReference(const Quatf& q);
// Default to current HMD orientation
void SetMagReference() { SetMagReference(Q); }
// These refer to reference points that associate mag readings with orientations
void ClearMagReferences() { MagNumReferences = 0; }
bool HasMagReference() const { return MagReferenced; }
void ClearMagReference()
{
MagReferenced = false;
MagReady = false;
}
Vector3f GetCalibratedMagValue(const Vector3f& rawMag) const;
bool IsMagReady() const { return MagReady; }
void SetMagRefDistance(const float d) { MagRefDistance = d; }
// *** Message Handler Logic
// Notifies SensorFusion object about a new BodyFrame message from a sensor.
// Should be called by user if not attaching to a sensor.
@ -103,99 +170,33 @@ public:
handleMessage(msg);
}
// Obtain the current accumulated orientation.
Quatf GetOrientation() const
{
Lock::Locker lockScope(Handler.GetHandlerLock());
return Q;
}
Quatf GetPredictedOrientation() const
{
Lock::Locker lockScope(Handler.GetHandlerLock());
return QP;
}
// Obtain the last absolute acceleration reading, in m/s^2.
Vector3f GetAcceleration() const
{
Lock::Locker lockScope(Handler.GetHandlerLock());
return A;
}
// Obtain the last angular velocity reading, in rad/s.
Vector3f GetAngularVelocity() const
{
Lock::Locker lockScope(Handler.GetHandlerLock());
return AngV;
}
// Obtain the last magnetometer reading, in Gauss
Vector3f GetMagnetometer() const
{
Lock::Locker lockScope(Handler.GetHandlerLock());
return Mag;
}
// Obtain the raw magnetometer reading, in Gauss (uncalibrated!)
Vector3f GetRawMagnetometer() const
{
Lock::Locker lockScope(Handler.GetHandlerLock());
return RawMag;
}
float GetMagRefYaw() const
{
return MagRefYaw;
}
// For later
//Vector3f GetGravity() const;
// Resets the current orientation
void Reset()
{
MagReferenced = false;
Lock::Locker lockScope(Handler.GetHandlerLock());
Q = Quatf();
QP = Quatf();
Stage = 0;
}
// Configuration
// Gain used to correct gyro with accel. Default value is appropriate for typical use.
float GetAccelGain() const { return Gain; }
void SetAccelGain(float ag) { Gain = ag; }
// Multiplier for yaw rotation (turning); setting this higher than 1 (the default) can allow the game
// to be played without auxillary rotation controls, possibly making it more immersive. Whether this is more
// or less likely to cause motion sickness is unknown.
float GetYawMultiplier() const { return YawMult; }
void SetYawMultiplier(float y) { YawMult = y; }
void SetDelegateMessageHandler(MessageHandler* handler)
{ pDelegate = handler; }
// Prediction functions.
// Prediction delta specifes how much prediction should be applied in seconds; it should in
// general be under the average rendering latency. Call GetPredictedOrientation() to get
// predicted orientation.
float GetPredictionDelta() const { return PredictionDT; }
void SetPrediction(float dt, bool enable = true) { PredictionDT = dt; EnablePrediction = enable; }
void SetPredictionEnabled(bool enable = true) { EnablePrediction = enable; }
bool IsPredictionEnabled() { return EnablePrediction; }
// Methods for magnetometer calibration
float AngleDifference(float theta1, float theta2);
Vector3f CalculateSphereCenter(Vector3f p1, Vector3f p2,
Vector3f p3, Vector3f p4);
private:
SensorFusion* getThis() { return this; }
// Internal handler for messages; bypasses error checking.
void handleMessage(const MessageBodyFrame& msg);
// Helper used to read and return value within a Lock.
template<class C>
C lockedGet(const C* p) const
{
Lock::Locker lockScope(Handler.GetHandlerLock());
return *p;
}
class BodyFrameHandler : public MessageHandler
// Internal handler for messages; bypasses error checking.
void handleMessage(const MessageBodyFrame& msg);
// Set the magnetometer's reference orientation for use in yaw correction
// The supplied mag is an uncalibrated value
void setMagReference(const Quatf& q, const Vector3f& rawMag);
// Default to current HMD orientation
void setMagReference() { setMagReference(Q, RawMag); }
class BodyFrameHandler : public MessageHandler
{
SensorFusion* pFusion;
public:
@ -206,44 +207,44 @@ private:
virtual bool SupportsMessageType(MessageType type) const;
};
SensorInfo CachedSensorInfo;
Quatf Q;
Quatf QUncorrected;
Vector3f A;
Vector3f AngV;
Vector3f Mag;
Vector3f CalMag;
Vector3f RawMag;
unsigned int Stage;
float RunningTime;
float DeltaT;
BodyFrameHandler Handler;
MessageHandler* pDelegate;
float Gain;
float YawMult;
volatile bool EnableGravity;
bool EnablePrediction;
float PredictionDT;
Quatf QP;
float PredictionTimeIncrement;
SensorFilter FMag;
SensorFilter FAccW;
SensorFilter FRawMag;
SensorFilter FAngV;
int TiltCondCount;
float TiltErrorAngle;
Vector3f TiltErrorAxis;
Vector3f GyroOffset;
SensorFilterBase<float> TiltAngleFilter;
bool EnableYawCorrection;
Matrix4f MagCalibrationMatrix;
bool MagCalibrated;
int MagCondCount;
bool MagReferenced;
float MagRefDistance;
bool MagReady;
Quatf MagRefQ;
Vector3f MagRefM;
float MagRefYaw;
float YawErrorAngle;
int YawErrorCount;
bool YawCorrectionInProgress;
Matrix4f MagCalibrationMatrix;
time_t MagCalibrationTime;
int MagNumReferences;
Vector3f MagRefsInBodyFrame[MagMaxReferences];
Vector3f MagRefsInWorldFrame[MagMaxReferences];
int MagRefIdx;
int MagRefScore;
bool MotionTrackingEnabled;
};

76
interface/external/LibOVR/Src/OVR_SensorImpl.h vendored Executable file → Normal file
View file

@ -33,6 +33,8 @@ public:
// Enumerates devices, creating and destroying relevant objects in manager.
virtual void EnumerateDevices(EnumerateVisitor& visitor);
virtual bool MatchVendorProduct(UInt16 vendorId, UInt16 productId) const;
virtual bool DetectHIDDevice(DeviceManager* pdevMgr, const HIDDeviceDesc& desc);
protected:
DeviceManager* getManager() const { return (DeviceManager*) pManager; }
};
@ -58,16 +60,68 @@ public:
if ((other.Type == Device_Sensor) && (pFactory == other.pFactory))
{
const SensorDeviceCreateDesc& s2 = (const SensorDeviceCreateDesc&) other;
if ((HIDDesc.Path == s2.HIDDesc.Path) &&
(HIDDesc.SerialNumber == s2.HIDDesc.SerialNumber))
if (MatchHIDDevice(s2.HIDDesc))
return Match_Found;
}
return Match_None;
}
virtual bool MatchHIDDevice(const HIDDeviceDesc& hidDesc) const
{
// should paths comparison be case insensitive?
return ((HIDDesc.Path.CompareNoCase(hidDesc.Path) == 0) &&
(HIDDesc.SerialNumber == hidDesc.SerialNumber) &&
(HIDDesc.VersionNumber == hidDesc.VersionNumber));
}
virtual bool GetDeviceInfo(DeviceInfo* info) const;
};
// A simple stub for notification of a sensor in Boot Loader mode
// This descriptor does not support the creation of a device, only the detection
// of its existence to warn apps that the sensor device needs firmware.
// The Boot Loader descriptor reuses and is created by the Sensor device factory
// but in the future may use a dedicated factory
class BootLoaderDeviceCreateDesc : public HIDDeviceCreateDesc
{
public:
BootLoaderDeviceCreateDesc(DeviceFactory* factory, const HIDDeviceDesc& hidDesc)
: HIDDeviceCreateDesc(factory, Device_BootLoader, hidDesc) { }
virtual DeviceCreateDesc* Clone() const
{
return new BootLoaderDeviceCreateDesc(*this);
}
// Boot Loader device creation is not allowed
virtual DeviceBase* NewDeviceInstance() { return NULL; };
virtual MatchResult MatchDevice(const DeviceCreateDesc& other,
DeviceCreateDesc**) const
{
if ((other.Type == Device_BootLoader) && (pFactory == other.pFactory))
{
const BootLoaderDeviceCreateDesc& s2 = (const BootLoaderDeviceCreateDesc&) other;
if (MatchHIDDevice(s2.HIDDesc))
return Match_Found;
}
return Match_None;
}
virtual bool MatchHIDDevice(const HIDDeviceDesc& hidDesc) const
{
// should paths comparison be case insensitive?
return ((HIDDesc.Path.CompareNoCase(hidDesc.Path) == 0) &&
(HIDDesc.SerialNumber == hidDesc.SerialNumber));
}
virtual bool GetDeviceInfo(DeviceInfo* info) const
{
OVR_UNUSED(info);
return false;
}
};
//-------------------------------------------------------------------------------------
// ***** OVR::SensorDisplayInfoImpl
@ -135,9 +189,21 @@ public:
virtual bool SetRange(const SensorRange& range, bool waitFlag);
virtual void GetRange(SensorRange* range) const;
// Sets report rate (in Hz) of MessageBodyFrame messages (delivered through MessageHandler::OnMessage call).
// Currently supported maximum rate is 1000Hz. If the rate is set to 500 or 333 Hz then OnMessage will be
// called twice or thrice at the same 'tick'.
// If the rate is < 333 then the OnMessage / MessageBodyFrame will be called three
// times for each 'tick': the first call will contain averaged values, the second
// and third calls will provide with most recent two recorded samples.
virtual void SetReportRate(unsigned rateHz);
// Returns currently set report rate, in Hz. If 0 - error occurred.
// Note, this value may be different from the one provided for SetReportRate. The return
// value will contain the actual rate.
virtual unsigned GetReportRate() const;
// Hack to create HMD device from sensor display info.
static void EnumerateHMDFromSensorDisplayInfo( const SensorDisplayInfoImpl& displayInfo,
DeviceFactory::EnumerateVisitor& visitor);
static void EnumerateHMDFromSensorDisplayInfo(const SensorDisplayInfoImpl& displayInfo,
DeviceFactory::EnumerateVisitor& visitor);
protected:
void openDevice();
@ -146,6 +212,8 @@ protected:
Void setCoordinateFrame(CoordinateFrame coordframe);
bool setRange(const SensorRange& range);
Void setReportRate(unsigned rateHz);
// Called for decoded messages
void onTrackerMessage(TrackerMessage* message);

0
interface/external/LibOVR/Src/OVR_ThreadCommandQueue.h vendored Executable file → Normal file
View file

15
interface/external/LibOVR/Src/OVR_Win32_DeviceManager.h vendored Executable file → Normal file
View file

@ -40,11 +40,16 @@ public:
virtual void Shutdown();
virtual ThreadCommandQueue* GetThreadQueue();
virtual ThreadId GetThreadId() const;
virtual DeviceEnumerator<> EnumerateDevicesEx(const DeviceEnumerationArgs& args);
virtual bool GetDeviceInfo(DeviceInfo* info) const;
// Fills HIDDeviceDesc by using the path.
// Returns 'true' if successful, 'false' otherwise.
bool GetHIDDeviceDesc(const String& path, HIDDeviceDesc* pdevDesc) const;
Ptr<DeviceManagerThread> pThread;
};
@ -56,7 +61,7 @@ class DeviceManagerThread : public Thread, public ThreadCommandQueue, public Dev
friend class DeviceManager;
enum { ThreadStackSize = 32 * 1024 };
public:
DeviceManagerThread();
DeviceManagerThread(DeviceManager* pdevMgr);
~DeviceManagerThread();
virtual int Run();
@ -67,7 +72,7 @@ public:
// Notifier used for different updates (EVENT or regular timing or messages).
class Notifier
class Notifier
{
public:
// Called when overlapped I/O handle is signaled.
@ -108,6 +113,8 @@ public:
// DeviceStatus::Notifier interface.
bool OnMessage(MessageType type, const String& devicePath);
void DetachDeviceManager();
private:
bool threadInitialized() { return hCommandEvent != 0; }
@ -128,6 +135,10 @@ private:
// Object that manages notifications originating from Windows messages.
Ptr<DeviceStatus> pStatusObject;
Lock DevMgrLock;
// pDeviceMgr should be accessed under DevMgrLock
DeviceManager* pDeviceMgr; // back ptr, no addref.
};
}} // namespace Win32::OVR

2
interface/external/LibOVR/Src/OVR_Win32_DeviceStatus.h vendored Executable file → Normal file
View file

@ -92,6 +92,8 @@ private: // data
UINT_PTR LastTimerId;
Array<RecoveryTimerDesc> RecoveryTimers;
GUID HidGuid;
};
}} // namespace OVR::Win32

17
interface/external/LibOVR/Src/OVR_Win32_HIDDevice.h vendored Executable file → Normal file
View file

@ -140,9 +140,12 @@ public:
virtual bool Enumerate(HIDEnumerateVisitor* enumVisitor);
virtual OVR::HIDDevice* Open(const String& path);
GUID GetHIDGuid()
{ return HidGuid; }
// Fills HIDDeviceDesc by using the path.
// Returns 'true' if successful, 'false' otherwise.
bool GetHIDDeviceDesc(const String& path, HIDDeviceDesc* pdevDesc) const;
GUID GetHIDGuid() { return HidGuid; }
static HIDDeviceManager* CreateInternal(DeviceManager* manager);
private:
@ -171,7 +174,7 @@ func = (PFn_##func)::GetProcAddress(hHidLib, #func)
OVR_DECLARE_HIDFUNC(HidD_FreePreparsedData, BOOLEAN, (HIDP_PREPARSED_DATA *preparsedData));
OVR_DECLARE_HIDFUNC(HidP_GetCaps, NTSTATUS,(HIDP_PREPARSED_DATA *preparsedData, HIDP_CAPS* caps));
HANDLE CreateHIDFile(const char* path, bool exclusiveAccess = true)
HANDLE CreateHIDFile(const char* path, bool exclusiveAccess = true) const
{
return ::CreateFileA(path, GENERIC_WRITE|GENERIC_READ,
(!exclusiveAccess) ? (FILE_SHARE_READ|FILE_SHARE_WRITE) : 0x0,
@ -179,11 +182,11 @@ func = (PFn_##func)::GetProcAddress(hHidLib, #func)
}
// Helper functions to fill in HIDDeviceDesc from open device handle.
bool initVendorProductVersion(HANDLE hidDev, HIDDeviceDesc* desc);
bool initUsage(HANDLE hidDev, HIDDeviceDesc* desc);
void initStrings(HANDLE hidDev, HIDDeviceDesc* desc);
bool initVendorProductVersion(HANDLE hidDev, HIDDeviceDesc* desc) const;
bool initUsage(HANDLE hidDev, HIDDeviceDesc* desc) const;
void initStrings(HANDLE hidDev, HIDDeviceDesc* desc) const;
bool getFullDesc(HANDLE hidDev, HIDDeviceDesc* desc);
bool getFullDesc(HANDLE hidDev, HIDDeviceDesc* desc) const;
};
}} // namespace OVR::Win32

31
interface/external/LibOVR/Src/OVR_Win32_HMDDevice.h vendored Executable file → Normal file
View file

@ -17,6 +17,7 @@ otherwise accompanies this software in either electronic or hard copy form.
#define OVR_Win32_HMDDevice_h
#include "OVR_Win32_DeviceManager.h"
#include "OVR_Profile.h"
namespace OVR { namespace Win32 {
@ -76,10 +77,23 @@ public:
virtual MatchResult MatchDevice(const DeviceCreateDesc& other,
DeviceCreateDesc**) const;
virtual bool UpdateMatchedCandidate(const DeviceCreateDesc&);
// Matches device by path.
virtual bool MatchDevice(const String& path);
virtual bool UpdateMatchedCandidate(const DeviceCreateDesc&, bool* newDeviceFlag = NULL);
virtual bool GetDeviceInfo(DeviceInfo* info) const;
// Requests the currently used default profile. This profile affects the
// settings reported by HMDInfo.
Profile* GetProfileAddRef() const;
ProfileType GetProfileType() const
{
return (HResolution >= 1920) ? Profile_RiftDKHD : Profile_RiftDK1;
}
void SetScreenParameters(int x, int y, unsigned hres, unsigned vres, float hsize, float vsize)
{
DesktopX = x;
@ -114,13 +128,26 @@ class HMDDevice : public DeviceImpl<OVR::HMDDevice>
{
public:
HMDDevice(HMDDeviceCreateDesc* createDesc);
~HMDDevice();
~HMDDevice();
virtual bool Initialize(DeviceBase* parent);
virtual void Shutdown();
// Requests the currently used default profile. This profile affects the
// settings reported by HMDInfo.
virtual Profile* GetProfile() const;
virtual const char* GetProfileName() const;
virtual bool SetProfileName(const char* name);
// Query associated sensor.
virtual OVR::SensorDevice* GetSensor();
protected:
HMDDeviceCreateDesc* getDesc() const { return (HMDDeviceCreateDesc*)pCreateDesc.GetPtr(); }
// User name for the profile used with this device.
String ProfileName;
mutable Ptr<Profile> pCachedProfile;
};

0
interface/external/LibOVR/Src/OVR_Win32_SensorDevice.h vendored Executable file → Normal file
View file

3
interface/external/LibOVR/Src/Util/Util_LatencyTest.h vendored Executable file → Normal file
View file

@ -69,6 +69,9 @@ public:
bool DisplayScreenColor(Color& colorToDisplay);
const char* GetResultsString();
// Begin test. Equivalent to pressing the button on the latency tester.
void BeginTest();
private:
LatencyTest* getThis() { return this; }

View file

@ -1,109 +0,0 @@
/************************************************************************************
PublicHeader: OVR.h
Filename : Util_MagCalibration.h
Content : Procedures for calibrating the magnetometer
Created : April 16, 2013
Authors : Steve LaValle, Andrew Reisse
Copyright : Copyright 2013 Oculus VR, Inc. All Rights reserved.
Use of this software is subject to the terms of the Oculus license
agreement provided at the time of installation or download, or which
otherwise accompanies this software in either electronic or hard copy form.
*************************************************************************************/
#ifndef OVR_Util_MagCalibration_h
#define OVR_Util_MagCalibration_h
#include "../OVR_SensorFusion.h"
#include "../Kernel/OVR_String.h"
#include "../Kernel/OVR_Log.h"
namespace OVR { namespace Util {
class MagCalibration
{
public:
enum MagStatus
{
Mag_Uninitialized = 0,
Mag_AutoCalibrating = 1,
Mag_ManuallyCalibrating = 2,
Mag_Calibrated = 3,
};
MagCalibration() :
Status(Mag_Uninitialized),
MinMagDistance(0.3f), MinQuatDistance(0.5f),
SampleCount(0)
{
MinMagDistanceSq = MinMagDistance * MinMagDistance;
MinQuatDistanceSq = MinQuatDistance * MinQuatDistance;
}
// Methods that are useful for either auto or manual calibration
bool IsUnitialized() const { return Status == Mag_Uninitialized; }
bool IsCalibrated() const { return Status == Mag_Calibrated; }
int NumberOfSamples() const { return SampleCount; }
void ClearCalibration(SensorFusion& sf);
// Methods for automatic magnetometer calibration
void BeginAutoCalibration(SensorFusion& sf);
unsigned UpdateAutoCalibration(SensorFusion& sf);
bool IsAutoCalibrating() const { return Status == Mag_AutoCalibrating; }
// Methods for building a manual (user-guided) calibraton procedure
void BeginManualCalibration(SensorFusion& sf);
bool IsAcceptableSample(const Quatf& q, const Vector3f& m);
bool InsertIfAcceptable(const Quatf& q, const Vector3f& m);
// Returns true if successful, requiring that SampleCount = 4
bool SetCalibration(SensorFusion& sf);
bool IsManuallyCalibrating() const { return Status == Mag_ManuallyCalibrating; }
// This is the minimum acceptable distance (Euclidean) between raw
// magnetometer values to be acceptable for usage in calibration.
void SetMinMagDistance(float dist)
{
MinMagDistance = dist;
MinMagDistanceSq = MinMagDistance * MinMagDistance;
}
// The minimum acceptable distance (4D Euclidean) between orientations
// to be acceptable for calibration usage.
void SetMinQuatDistance(float dist)
{
MinQuatDistance = dist;
MinQuatDistanceSq = MinQuatDistance * MinQuatDistance;
}
// A result of the calibration, which is the center of a sphere that
// roughly approximates the magnetometer data.
Vector3f GetMagCenter() const { return MagCenter; }
private:
// Determine the unique sphere through 4 non-coplanar points
Vector3f CalculateSphereCenter(const Vector3f& p1, const Vector3f& p2,
const Vector3f& p3, const Vector3f& p4);
// Distance from p4 to the nearest point on a plane through p1, p2, p3
float PointToPlaneDistance(const Vector3f& p1, const Vector3f& p2,
const Vector3f& p3, const Vector3f& p4);
Vector3f MagCenter;
unsigned Status;
float MinMagDistance;
float MinQuatDistance;
float MinMagDistanceSq;
float MinQuatDistanceSq;
unsigned SampleCount;
Vector3f MagSamples[4];
Quatf QuatSamples[4];
};
}}
#endif

3
interface/external/LibOVR/Src/Util/Util_Render_Stereo.h vendored Executable file → Normal file
View file

@ -189,7 +189,7 @@ public:
void SetEyeToScreenDistance(float esd) { HMD.EyeToScreenDistance = esd; DirtyFlag = true; }
// Interpupillary distance used for stereo, in meters. Default is 0.064m (64 mm).
void SetIPD(float ipd) { InterpupillaryDistance = ipd; DirtyFlag = true; }
void SetIPD(float ipd) { InterpupillaryDistance = ipd; IPDOverride = DirtyFlag = true; }
float GetIPD() const { return InterpupillaryDistance; }
// Set full render target viewport; for HMD this includes both eyes.
@ -279,6 +279,7 @@ private:
// *** Computed State
bool DirtyFlag; // Set when any if the modifiable state changed.
bool IPDOverride; // True after SetIPD was called.
float YFov; // Vertical FOV.
float Aspect; // Aspect ratio: (w/h)*AspectMultiplier.
float ProjectionCenterOffset;

View file

@ -30,6 +30,12 @@ uniform vec2 leftBottom;
// the right and top edges of the view window
uniform vec2 rightTop;
// an offset value to apply to the texture coordinates
uniform vec2 texCoordOffset;
// a scale value to apply to the texture coordinates
uniform vec2 texCoordScale;
// the radius of the effect
uniform float radius;
@ -38,7 +44,7 @@ uniform vec2 noiseScale;
// given a texture coordinate, returns the 3D view space z coordinate
float texCoordToViewSpaceZ(vec2 texCoord) {
return (far * near) / (texture2D(depthTexture, texCoord).r * (far - near) - far);
return (far * near) / (texture2D(depthTexture, texCoord * texCoordScale + texCoordOffset).r * (far - near) - far);
}
// given a texture coordinate, returns the 3D view space coordinate
@ -54,11 +60,15 @@ void main(void) {
vec3 center = texCoordToViewSpace(gl_TexCoord[0].st);
vec2 rdenominator = 1.0 / (rightTop - leftBottom);
vec2 xyFactor = 2.0 * near * rdenominator;
vec2 zFactor = (rightTop + leftBottom) * rdenominator;
float occlusion = 4.0;
for (int i = 0; i < SAMPLE_KERNEL_SIZE; i++) {
vec3 offset = center + rotation * (radius * sampleKernel[i]);
vec4 projected = gl_ProjectionMatrix * vec4(offset, 1.0);
float depth = texCoordToViewSpaceZ(projected.xy * 0.5 / projected.w + vec2(0.5, 0.5));
vec2 projected = offset.xy * xyFactor + offset.z * zFactor;
float depth = texCoordToViewSpaceZ(projected * -0.5 / offset.z + vec2(0.5, 0.5));
occlusion += 1.0 - step(offset.z, depth);
}

View file

@ -0,0 +1,36 @@
#version 120
//
// oculus.frag
// fragment shader
//
// Created by Andrzej Kapolka on 11/26/13.
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
//
// this shader is an adaptation (HLSL -> GLSL, removed conditional) of the one in the Oculus sample
// code (Samples/OculusRoomTiny/RenderTiny_D3D1X_Device.cpp), which is under the Apache license
// (http://www.apache.org/licenses/LICENSE-2.0)
//
uniform sampler2D texture;
uniform vec2 lensCenter;
uniform vec2 screenCenter;
uniform vec2 scale;
uniform vec2 scaleIn;
uniform vec4 hmdWarpParam;
vec2 hmdWarp(vec2 in01) {
vec2 theta = (in01 - lensCenter) * scaleIn;
float rSq = theta.x * theta.x + theta.y * theta.y;
vec2 theta1 = theta * (hmdWarpParam.x + hmdWarpParam.y * rSq +
hmdWarpParam.z * rSq * rSq + hmdWarpParam.w * rSq * rSq * rSq);
return lensCenter + scale * theta1;
}
void main(void) {
vec2 tc = hmdWarp(gl_TexCoord[0].st);
vec2 below = step(screenCenter.st + vec2(-0.25, -0.5), tc.st);
vec2 above = vec2(1.0, 1.0) - step(screenCenter.st + vec2(0.25, 0.5), tc.st);
gl_FragColor = mix(vec4(0.0, 0.0, 0.0, 1.0), texture2D(texture, tc), above.s * above.t * below.s * below.t);
}

View file

@ -132,8 +132,6 @@ Application::Application(int& argc, char** argv, timeval &startup_time) :
_lookatIndicatorScale(1.0f),
_perfStatsOn(false),
_chatEntryOn(false),
_oculusProgram(0),
_oculusDistortionScale(1.25),
#ifndef _WIN32
_audio(&_audioScope, STARTUP_JITTER_SAMPLES),
#endif
@ -248,7 +246,6 @@ Application::~Application() {
VoxelNode::removeDeleteHook(&_voxels); // we don't need to do this processing on shutdown
delete Menu::getInstance();
delete _oculusProgram;
delete _settings;
delete _followMode;
delete _glWidget;
@ -425,7 +422,7 @@ void Application::paintGL() {
}
if (OculusManager::isConnected()) {
displayOculus(whichCamera);
OculusManager::display(whichCamera);
} else {
_glowEffect.prepare();
@ -516,14 +513,11 @@ void Application::paintGL() {
}
void Application::resetCamerasOnResizeGL(Camera& camera, int width, int height) {
float aspectRatio = ((float)width/(float)height); // based on screen resize
if (OculusManager::isConnected()) {
// more magic numbers; see Oculus SDK docs, p. 32
camera.setAspectRatio(aspectRatio *= 0.5);
camera.setFieldOfView(2 * atan((0.0468 * _oculusDistortionScale) / 0.041) * (180 / PIf));
OculusManager::configureCamera(camera, width, height);
} else {
camera.setAspectRatio(aspectRatio);
camera.setAspectRatio((float)width / height);
camera.setFieldOfView(Menu::getInstance()->getFieldOfView());
}
}
@ -682,9 +676,7 @@ void Application::keyPressEvent(QKeyEvent* event) {
break;
case Qt::Key_C:
if (isShifted) {
Menu::getInstance()->triggerOption(MenuOption::OcclusionCulling);
} else if (_nudgeStarted) {
if (_nudgeStarted) {
_nudgeGuidePosition.y -= _mouseVoxel.s;
} else {
_myAvatar.setDriveKeys(DOWN, 1);
@ -1472,7 +1464,6 @@ void Application::checkBandwidthMeterClick() {
void Application::setFullscreen(bool fullscreen) {
_window->setWindowState(fullscreen ? (_window->windowState() | Qt::WindowFullScreen) :
(_window->windowState() & ~Qt::WindowFullScreen));
updateCursor();
}
void Application::setRenderVoxels(bool voxelRender) {
@ -1486,6 +1477,54 @@ void Application::doKillLocalVoxels() {
_wantToKillLocalVoxels = true;
}
void Application::removeVoxel(glm::vec3 position,
float scale) {
VoxelDetail voxel;
voxel.x = position.x / TREE_SCALE;
voxel.y = position.y / TREE_SCALE;
voxel.z = position.z / TREE_SCALE;
voxel.s = scale / TREE_SCALE;
_voxelEditSender.sendVoxelEditMessage(PACKET_TYPE_ERASE_VOXEL, voxel);
// delete it locally to see the effect immediately (and in case no voxel server is present)
_voxels.deleteVoxelAt(voxel.x, voxel.y, voxel.z, voxel.s);
}
void Application::makeVoxel(glm::vec3 position,
float scale,
unsigned char red,
unsigned char green,
unsigned char blue,
bool isDestructive) {
VoxelDetail voxel;
voxel.x = position.x / TREE_SCALE;
voxel.y = position.y / TREE_SCALE;
voxel.z = position.z / TREE_SCALE;
voxel.s = scale / TREE_SCALE;
voxel.red = red;
voxel.green = green;
voxel.blue = blue;
PACKET_TYPE message = isDestructive ? PACKET_TYPE_SET_VOXEL_DESTRUCTIVE : PACKET_TYPE_SET_VOXEL;
_voxelEditSender.sendVoxelEditMessage(message, voxel);
// create the voxel locally so it appears immediately
_voxels.createVoxel(voxel.x, voxel.y, voxel.z, voxel.s,
voxel.red, voxel.green, voxel.blue,
isDestructive);
// Implement voxel fade effect
VoxelFade fade(VoxelFade::FADE_OUT, 1.0f, 1.0f, 1.0f);
const float VOXEL_BOUNDS_ADJUST = 0.01f;
float slightlyBigger = voxel.s * VOXEL_BOUNDS_ADJUST;
fade.voxelDetails.x = voxel.x - slightlyBigger;
fade.voxelDetails.y = voxel.y - slightlyBigger;
fade.voxelDetails.z = voxel.z - slightlyBigger;
fade.voxelDetails.s = voxel.s + slightlyBigger + slightlyBigger;
_voxelFades.push_back(fade);
}
const glm::vec3 Application::getMouseVoxelWorldCoordinates(const VoxelDetail _mouseVoxel) {
return glm::vec3((_mouseVoxel.x + _mouseVoxel.s / 2.f) * TREE_SCALE,
(_mouseVoxel.y + _mouseVoxel.s / 2.f) * TREE_SCALE,
@ -1523,7 +1562,6 @@ bool Application::sendVoxelsOperation(VoxelNode* node, void* extraData) {
SendVoxelsOperationArgs* args = (SendVoxelsOperationArgs*)extraData;
if (node->isColored()) {
const unsigned char* nodeOctalCode = node->getOctalCode();
unsigned char* codeColorBuffer = NULL;
int codeLength = 0;
int bytesInCode = 0;
@ -1544,10 +1582,9 @@ bool Application::sendVoxelsOperation(VoxelNode* node, void* extraData) {
}
// copy the colors over
codeColorBuffer[bytesInCode + RED_INDEX ] = node->getColor()[RED_INDEX ];
codeColorBuffer[bytesInCode + RED_INDEX] = node->getColor()[RED_INDEX];
codeColorBuffer[bytesInCode + GREEN_INDEX] = node->getColor()[GREEN_INDEX];
codeColorBuffer[bytesInCode + BLUE_INDEX ] = node->getColor()[BLUE_INDEX ];
codeColorBuffer[bytesInCode + BLUE_INDEX] = node->getColor()[BLUE_INDEX];
getInstance()->_voxelEditSender.queueVoxelEditMessage(PACKET_TYPE_SET_VOXEL_DESTRUCTIVE,
codeColorBuffer, codeAndColorLength);
@ -1611,7 +1648,6 @@ void Application::pasteVoxelsToOctalCode(const unsigned char* octalCodeDestinati
// the server as an set voxel message, this will also rebase the voxels to the new location
SendVoxelsOperationArgs args;
args.newBaseOctCode = octalCodeDestination;
_sharedVoxelSystem.getTree()->recurseTreeWithOperation(sendVoxelsOperation, &args);
if (_sharedVoxelSystem.getTree() != &_clipboard) {
@ -1770,7 +1806,7 @@ void Application::init() {
_voxels.setMaxVoxels(Menu::getInstance()->getMaxVoxels());
_voxels.setUseVoxelShader(Menu::getInstance()->isOptionChecked(MenuOption::UseVoxelShader));
_voxels.setVoxelsAsPoints(Menu::getInstance()->isOptionChecked(MenuOption::VoxelsAsPoints));
_voxels.setDisableFastVoxelPipeline(Menu::getInstance()->isOptionChecked(MenuOption::DisableFastVoxelPipeline));
_voxels.setDisableFastVoxelPipeline(false);
_voxels.init();
@ -2223,11 +2259,11 @@ void Application::updateLeap(float deltaTime) {
LeapManager::nextFrame();
}
void Application::updateSixense() {
void Application::updateSixense(float deltaTime) {
bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings);
PerformanceWarning warn(showWarnings, "Application::updateSixense()");
_sixenseManager.update();
_sixenseManager.update(deltaTime);
}
void Application::updateSerialDevices(float deltaTime) {
@ -2437,7 +2473,7 @@ void Application::update(float deltaTime) {
updateMouseVoxels(deltaTime, mouseRayOrigin, mouseRayDirection, distance, face); // UI/UX related to voxels
updateHandAndTouch(deltaTime); // Update state for touch sensors
updateLeap(deltaTime); // Leap finger-sensing device
updateSixense(); // Razer Hydra controllers
updateSixense(deltaTime); // Razer Hydra controllers
updateSerialDevices(deltaTime); // Read serial port interface devices
updateAvatar(deltaTime); // Sample hardware, update view frustum if needed, and send avatar data to mixer/nodes
updateThreads(deltaTime); // If running non-threaded, then give the threads some time to process...
@ -2577,10 +2613,11 @@ void Application::queryVoxels() {
bool wantExtraDebugging = Menu::getInstance()->isOptionChecked(MenuOption::ExtraDebugging);
// These will be the same for all servers, so we can set them up once and then reuse for each server we send to.
_voxelQuery.setWantLowResMoving(Menu::getInstance()->isOptionChecked(MenuOption::LowRes));
_voxelQuery.setWantColor(Menu::getInstance()->isOptionChecked(MenuOption::SendVoxelColors));
_voxelQuery.setWantDelta(Menu::getInstance()->isOptionChecked(MenuOption::DeltaSending));
_voxelQuery.setWantOcclusionCulling(Menu::getInstance()->isOptionChecked(MenuOption::OcclusionCulling));
_voxelQuery.setWantLowResMoving(!Menu::getInstance()->isOptionChecked(MenuOption::DisableLowRes));
_voxelQuery.setWantColor(!Menu::getInstance()->isOptionChecked(MenuOption::DisableColorVoxels));
_voxelQuery.setWantDelta(!Menu::getInstance()->isOptionChecked(MenuOption::DisableDeltaSending));
_voxelQuery.setWantOcclusionCulling(Menu::getInstance()->isOptionChecked(MenuOption::EnableOcclusionCulling));
_voxelQuery.setWantCompression(Menu::getInstance()->isOptionChecked(MenuOption::EnableVoxelPacketCompression));
_voxelQuery.setCameraPosition(_viewFrustum.getPosition());
_voxelQuery.setCameraOrientation(_viewFrustum.getOrientation());
@ -2642,15 +2679,16 @@ void Application::queryVoxels() {
int perServerPPS = 0;
const int SMALL_BUDGET = 10;
int perUnknownServer = SMALL_BUDGET;
int totalPPS = Menu::getInstance()->getMaxVoxelPacketsPerSecond();
// determine PPS based on number of servers
if (inViewServers >= 1) {
// set our preferred PPS to be exactly evenly divided among all of the voxel servers... and allocate 1 PPS
// for each unknown jurisdiction server
perServerPPS = (DEFAULT_MAX_VOXEL_PPS / inViewServers) - (unknownJurisdictionServers * perUnknownServer);
perServerPPS = (totalPPS / inViewServers) - (unknownJurisdictionServers * perUnknownServer);
} else {
if (unknownJurisdictionServers > 0) {
perUnknownServer = (DEFAULT_MAX_VOXEL_PPS / unknownJurisdictionServers);
perUnknownServer = (totalPPS / unknownJurisdictionServers);
}
}
@ -2857,146 +2895,7 @@ void Application::updateShadowMap() {
glViewport(0, 0, _glWidget->width(), _glWidget->height());
}
// this shader is an adaptation (HLSL -> GLSL, removed conditional) of the one in the Oculus sample
// code (Samples/OculusRoomTiny/RenderTiny_D3D1X_Device.cpp), which is under the Apache license
// (http://www.apache.org/licenses/LICENSE-2.0)
static const char* DISTORTION_FRAGMENT_SHADER =
"#version 120\n"
"uniform sampler2D texture;"
"uniform vec2 lensCenter;"
"uniform vec2 screenCenter;"
"uniform vec2 scale;"
"uniform vec2 scaleIn;"
"uniform vec4 hmdWarpParam;"
"vec2 hmdWarp(vec2 in01) {"
" vec2 theta = (in01 - lensCenter) * scaleIn;"
" float rSq = theta.x * theta.x + theta.y * theta.y;"
" vec2 theta1 = theta * (hmdWarpParam.x + hmdWarpParam.y * rSq + "
" hmdWarpParam.z * rSq * rSq + hmdWarpParam.w * rSq * rSq * rSq);"
" return lensCenter + scale * theta1;"
"}"
"void main(void) {"
" vec2 tc = hmdWarp(gl_TexCoord[0].st);"
" vec2 below = step(screenCenter.st + vec2(-0.25, -0.5), tc.st);"
" vec2 above = vec2(1.0, 1.0) - step(screenCenter.st + vec2(0.25, 0.5), tc.st);"
" gl_FragColor = mix(vec4(0.0, 0.0, 0.0, 1.0), texture2D(texture, tc), "
" above.s * above.t * below.s * below.t);"
"}";
void Application::displayOculus(Camera& whichCamera) {
_glowEffect.prepare();
// magic numbers ahoy! in order to avoid pulling in the Oculus utility library that calculates
// the rendering parameters from the hardware stats, i just folded their calculations into
// constants using the stats for the current-model hardware as contained in the SDK file
// LibOVR/Src/Util/Util_Render_Stereo.cpp
// eye
// render the left eye view to the left side of the screen
glMatrixMode(GL_PROJECTION);
glPushMatrix();
glLoadIdentity();
glTranslatef(0.151976, 0, 0); // +h, see Oculus SDK docs p. 26
gluPerspective(whichCamera.getFieldOfView(), whichCamera.getAspectRatio(),
whichCamera.getNearClip(), whichCamera.getFarClip());
glViewport(0, 0, _glWidget->width() / 2, _glWidget->height());
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glLoadIdentity();
glTranslatef(0.032, 0, 0); // dip/2, see p. 27
displaySide(whichCamera);
// and the right eye to the right side
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glTranslatef(-0.151976, 0, 0); // -h
gluPerspective(whichCamera.getFieldOfView(), whichCamera.getAspectRatio(),
whichCamera.getNearClip(), whichCamera.getFarClip());
glViewport(_glWidget->width() / 2, 0, _glWidget->width() / 2, _glWidget->height());
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glTranslatef(-0.032, 0, 0);
displaySide(whichCamera);
glPopMatrix();
// restore our normal viewport
glViewport(0, 0, _glWidget->width(), _glWidget->height());
QOpenGLFramebufferObject* fbo = _glowEffect.render(true);
glBindTexture(GL_TEXTURE_2D, fbo->texture());
if (_oculusProgram == 0) {
_oculusProgram = new ProgramObject();
_oculusProgram->addShaderFromSourceCode(QGLShader::Fragment, DISTORTION_FRAGMENT_SHADER);
_oculusProgram->link();
_textureLocation = _oculusProgram->uniformLocation("texture");
_lensCenterLocation = _oculusProgram->uniformLocation("lensCenter");
_screenCenterLocation = _oculusProgram->uniformLocation("screenCenter");
_scaleLocation = _oculusProgram->uniformLocation("scale");
_scaleInLocation = _oculusProgram->uniformLocation("scaleIn");
_hmdWarpParamLocation = _oculusProgram->uniformLocation("hmdWarpParam");
}
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluOrtho2D(0, _glWidget->width(), 0, _glWidget->height());
glDisable(GL_DEPTH_TEST);
// for reference on setting these values, see SDK file Samples/OculusRoomTiny/RenderTiny_Device.cpp
float scaleFactor = 1.0 / _oculusDistortionScale;
float aspectRatio = (_glWidget->width() * 0.5) / _glWidget->height();
glDisable(GL_BLEND);
_oculusProgram->bind();
_oculusProgram->setUniformValue(_textureLocation, 0);
_oculusProgram->setUniformValue(_lensCenterLocation, 0.287994, 0.5); // see SDK docs, p. 29
_oculusProgram->setUniformValue(_screenCenterLocation, 0.25, 0.5);
_oculusProgram->setUniformValue(_scaleLocation, 0.25 * scaleFactor, 0.5 * scaleFactor * aspectRatio);
_oculusProgram->setUniformValue(_scaleInLocation, 4, 2 / aspectRatio);
_oculusProgram->setUniformValue(_hmdWarpParamLocation, 1.0, 0.22, 0.24, 0);
glColor3f(1, 0, 1);
glBegin(GL_QUADS);
glTexCoord2f(0, 0);
glVertex2f(0, 0);
glTexCoord2f(0.5, 0);
glVertex2f(_glWidget->width()/2, 0);
glTexCoord2f(0.5, 1);
glVertex2f(_glWidget->width() / 2, _glWidget->height());
glTexCoord2f(0, 1);
glVertex2f(0, _glWidget->height());
glEnd();
_oculusProgram->setUniformValue(_lensCenterLocation, 0.787994, 0.5);
_oculusProgram->setUniformValue(_screenCenterLocation, 0.75, 0.5);
glBegin(GL_QUADS);
glTexCoord2f(0.5, 0);
glVertex2f(_glWidget->width() / 2, 0);
glTexCoord2f(1, 0);
glVertex2f(_glWidget->width(), 0);
glTexCoord2f(1, 1);
glVertex2f(_glWidget->width(), _glWidget->height());
glTexCoord2f(0.5, 1);
glVertex2f(_glWidget->width() / 2, _glWidget->height());
glEnd();
glEnable(GL_BLEND);
glBindTexture(GL_TEXTURE_2D, 0);
_oculusProgram->release();
glPopMatrix();
}
const GLfloat WHITE_SPECULAR_COLOR[] = { 1.0f, 1.0f, 1.0f, 1.0f };
const GLfloat NO_SPECULAR_COLOR[] = { 0.0f, 0.0f, 0.0f, 1.0f };
@ -3019,18 +2918,6 @@ void Application::setupWorldLight() {
glMateriali(GL_FRONT, GL_SHININESS, 96);
}
void Application::loadTranslatedViewMatrix(const glm::vec3& translation) {
glLoadMatrixf((const GLfloat*)&_untranslatedViewMatrix);
glTranslatef(translation.x + _viewMatrixTranslation.x, translation.y + _viewMatrixTranslation.y,
translation.z + _viewMatrixTranslation.z);
}
void Application::computeOffAxisFrustum(float& left, float& right, float& bottom, float& top, float& near,
float& far, glm::vec4& nearClipPlane, glm::vec4& farClipPlane) const {
_viewFrustum.computeOffAxisFrustum(left, right, bottom, top, near, far, nearClipPlane, farClipPlane);
}
void Application::displaySide(Camera& whichCamera, bool selfAvatarOnly) {
PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "Application::displaySide()");
// transform by eye offset
@ -3274,6 +3161,18 @@ void Application::displaySide(Camera& whichCamera, bool selfAvatarOnly) {
}
}
void Application::loadTranslatedViewMatrix(const glm::vec3& translation) {
glLoadMatrixf((const GLfloat*)&_untranslatedViewMatrix);
glTranslatef(translation.x + _viewMatrixTranslation.x, translation.y + _viewMatrixTranslation.y,
translation.z + _viewMatrixTranslation.z);
}
void Application::computeOffAxisFrustum(float& left, float& right, float& bottom, float& top, float& near,
float& far, glm::vec4& nearClipPlane, glm::vec4& farClipPlane) const {
_viewFrustum.computeOffAxisFrustum(left, right, bottom, top, near, far, nearClipPlane, farClipPlane);
}
void Application::displayOverlay() {
PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "Application::displayOverlay()");
@ -3608,7 +3507,7 @@ void Application::displayStats() {
// Voxel Rendering
voxelStats.str("");
voxelStats.precision(4);
voxelStats << "Voxel Rendering Slots" <<
voxelStats << "Voxel Rendering Slots " <<
"Max: " << _voxels.getMaxVoxels() / 1000.f << "K " <<
"Drawn: " << _voxels.getVoxelsWritten() / 1000.f << "K " <<
"Abandoned: " << _voxels.getAbandonedVoxels() / 1000.f << "K ";
@ -3847,7 +3746,7 @@ void Application::renderAvatars(bool forceRenderHead, bool selfAvatarOnly) {
if (!avatar->isInitialized()) {
avatar->init();
}
avatar->render(false, Menu::getInstance()->isOptionChecked(MenuOption::AvatarAsBalls));
avatar->render(false);
avatar->setDisplayingLookatVectors(Menu::getInstance()->isOptionChecked(MenuOption::LookAtVectors));
}
@ -3857,12 +3756,12 @@ void Application::renderAvatars(bool forceRenderHead, bool selfAvatarOnly) {
// render avatar fades
Glower glower;
for (vector<Avatar*>::iterator fade = _avatarFades.begin(); fade != _avatarFades.end(); fade++) {
(*fade)->render(false, Menu::getInstance()->isOptionChecked(MenuOption::AvatarAsBalls));
(*fade)->render(false);
}
}
// Render my own Avatar
_myAvatar.render(forceRenderHead, Menu::getInstance()->isOptionChecked(MenuOption::AvatarAsBalls));
_myAvatar.render(forceRenderHead);
_myAvatar.setDisplayingLookatVectors(Menu::getInstance()->isOptionChecked(MenuOption::LookAtVectors));
if (Menu::getInstance()->isOptionChecked(MenuOption::LookAtIndicator) && _lookatTargetAvatar) {
@ -4129,6 +4028,7 @@ void Application::resetSensors() {
_webcam.reset();
_faceshift.reset();
LeapManager::reset();
OculusManager::reset();
QCursor::setPos(_headMouseX, _headMouseY);
_myAvatar.reset();
_myTransmitter.resetLevels();
@ -4157,11 +4057,6 @@ void Application::setMenuShortcutsEnabled(bool enabled) {
setShortcutsEnabled(_window->menuBar(), enabled);
}
void Application::updateCursor() {
_glWidget->setCursor(OculusManager::isConnected() && _window->windowState().testFlag(Qt::WindowFullScreen) ?
Qt::BlankCursor : Qt::ArrowCursor);
}
void Application::attachNewHeadToNode(Node* newNode) {
if (newNode->getLinkedData() == NULL) {
newNode->setLinkedData(new Avatar(newNode));
@ -4326,13 +4221,26 @@ void* Application::networkReceive(void* args) {
app->_audio.addReceivedAudioToBuffer(app->_incomingPacket, bytesReceived);
break;
case PACKET_TYPE_VOXEL_DATA:
case PACKET_TYPE_VOXEL_DATA_MONOCHROME:
case PACKET_TYPE_Z_COMMAND:
case PACKET_TYPE_ERASE_VOXEL:
case PACKET_TYPE_VOXEL_STATS:
case PACKET_TYPE_ENVIRONMENT_DATA: {
PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings),
"Application::networkReceive()... _voxelProcessor.queueReceivedPacket()");
bool wantExtraDebugging = Menu::getInstance()->isOptionChecked(MenuOption::ExtraDebugging);
if (wantExtraDebugging && app->_incomingPacket[0] == PACKET_TYPE_VOXEL_DATA) {
int numBytesPacketHeader = numBytesForPacketHeader(app->_incomingPacket);
unsigned char* dataAt = app->_incomingPacket + numBytesPacketHeader;
dataAt += sizeof(VOXEL_PACKET_FLAGS);
VOXEL_PACKET_SEQUENCE sequence = (*(VOXEL_PACKET_SEQUENCE*)dataAt);
dataAt += sizeof(VOXEL_PACKET_SEQUENCE);
VOXEL_PACKET_SENT_TIME sentAt = (*(VOXEL_PACKET_SENT_TIME*)dataAt);
dataAt += sizeof(VOXEL_PACKET_SENT_TIME);
VOXEL_PACKET_SENT_TIME arrivedAt = usecTimestampNow();
int flightTime = arrivedAt - sentAt;
printf("got PACKET_TYPE_VOXEL_DATA, sequence:%d flightTime:%d\n", sequence, flightTime);
}
// add this packet to our list of voxel packets and process them on the voxel processing
app->_voxelProcessor.queueReceivedPacket(senderSockAddr, app->_incomingPacket, bytesReceived);

View file

@ -117,6 +117,15 @@ public:
void wheelEvent(QWheelEvent* event);
void makeVoxel(glm::vec3 position,
float scale,
unsigned char red,
unsigned char green,
unsigned char blue,
bool isDestructive);
void removeVoxel(glm::vec3 position, float scale);
const glm::vec3 getMouseVoxelWorldCoordinates(const VoxelDetail _mouseVoxel);
QGLWidget* getGLWidget() { return _glWidget; }
@ -155,6 +164,8 @@ public:
void setupWorldLight();
void displaySide(Camera& whichCamera, bool selfAvatarOnly = false);
/// Loads a view matrix that incorporates the specified model translation without the precision issues that can
/// result from matrix multiplication at high translation magnitudes.
void loadTranslatedViewMatrix(const glm::vec3& translation);
@ -247,7 +258,7 @@ private:
glm::vec3& eyePosition);
void updateHandAndTouch(float deltaTime);
void updateLeap(float deltaTime);
void updateSixense();
void updateSixense(float deltaTime);
void updateSerialDevices(float deltaTime);
void updateThreads(float deltaTime);
void updateMyAvatarSimulation(float deltaTime);
@ -272,8 +283,6 @@ private:
glm::vec3 getSunDirection();
void updateShadowMap();
void displayOculus(Camera& whichCamera);
void displaySide(Camera& whichCamera, bool selfAvatarOnly = false);
void displayOverlay();
void displayStats();
void renderAvatars(bool forceRenderHead, bool selfAvatarOnly = false);
@ -287,8 +296,6 @@ private:
void setMenuShortcutsEnabled(bool enabled);
void updateCursor();
static void attachNewHeadToNode(Node *newNode);
static void* networkReceive(void* args); // network receive thread
@ -415,15 +422,6 @@ private:
ChatEntry _chatEntry; // chat entry field
bool _chatEntryOn; // Whether to show the chat entry
ProgramObject* _oculusProgram; // The GLSL program containing the distortion shader
float _oculusDistortionScale; // Controls the Oculus field of view
int _textureLocation;
int _lensCenterLocation;
int _screenCenterLocation;
int _scaleLocation;
int _scaleInLocation;
int _hmdWarpParamLocation;
GeometryCache _geometryCache;
TextureCache _textureCache;

View file

@ -26,12 +26,8 @@
#include "Menu.h"
#include "Util.h"
// Uncomment the following definition to test audio device latency by copying output to input
//#define TEST_AUDIO_LOOPBACK
//#define SHOW_AUDIO_DEBUG
#define VISUALIZE_ECHO_CANCELLATION
static const int PHASE_DELAY_AT_90 = 20;
static const float AMPLITUDE_RATIO_AT_90 = 0.5;
static const int MIN_FLANGE_EFFECT_THRESHOLD = 600;
@ -83,11 +79,17 @@ inline void Audio::performIO(int16_t* inputLeft, int16_t* outputLeft, int16_t* o
memset(outputLeft, 0, PACKET_LENGTH_BYTES_PER_CHANNEL);
memset(outputRight, 0, PACKET_LENGTH_BYTES_PER_CHANNEL);
// If Mute button is pressed, clear the input buffer
// If Mute button is pressed, clear the input buffer
if (_muted) {
memset(inputLeft, 0, PACKET_LENGTH_BYTES_PER_CHANNEL);
}
// If local loopback enabled, copy input to output
if (Menu::getInstance()->isOptionChecked(MenuOption::EchoLocalAudio)) {
memcpy(outputLeft, inputLeft, PACKET_LENGTH_BYTES_PER_CHANNEL);
memcpy(outputRight, inputLeft, PACKET_LENGTH_BYTES_PER_CHANNEL);
}
// Add Procedural effects to input samples
addProceduralSounds(inputLeft, outputLeft, outputRight, BUFFER_LENGTH_SAMPLES_PER_CHANNEL);
@ -119,7 +121,7 @@ inline void Audio::performIO(int16_t* inputLeft, int16_t* outputLeft, int16_t* o
// + 12 for 3 floats for position + float for bearing + 1 attenuation byte
unsigned char dataPacket[MAX_PACKET_SIZE];
PACKET_TYPE packetType = Menu::getInstance()->isOptionChecked(MenuOption::EchoAudio)
PACKET_TYPE packetType = Menu::getInstance()->isOptionChecked(MenuOption::EchoServerAudio)
? PACKET_TYPE_MICROPHONE_AUDIO_WITH_ECHO
: PACKET_TYPE_MICROPHONE_AUDIO_NO_ECHO;
@ -359,7 +361,8 @@ Audio::Audio(Oscilloscope* scope, int16_t initialJitterBufferSamples) :
_collisionSoundDuration(0.0f),
_proceduralEffectSample(0),
_heartbeatMagnitude(0.0f),
_muted(false)
_muted(false),
_localEcho(false)
{
outputPortAudioError(Pa_Initialize());

View file

@ -70,6 +70,7 @@ public:
// in which case 'true' is returned - otherwise the return value is 'false'.
// The results of the analysis are written to the log.
bool eventuallyAnalyzePing();
private:
PaStream* _stream;
@ -109,6 +110,7 @@ private:
float _heartbeatMagnitude;
bool _muted;
bool _localEcho;
GLuint _micTextureId;
GLuint _muteTextureId;
QRect _iconBounds;

View file

@ -20,7 +20,7 @@
InfoView::InfoView(bool forced) :
_forced(forced) {
setWindowFlags(Qt::WindowStaysOnTopHint | Qt::CustomizeWindowHint);
setWindowFlags(Qt::WindowStaysOnTopHint | Qt::CustomizeWindowHint | Qt::WindowCloseButtonHint);
switchToResourcesParentIfRequired();
QString absPath = QFileInfo("resources/html/interface-welcome-allsvg.html").absoluteFilePath();

View file

@ -36,11 +36,18 @@
Menu* Menu::_instance = NULL;
Menu* Menu::getInstance() {
static QMutex menuInstanceMutex;
// lock the menu instance mutex to make sure we don't race and create two menus and crash
menuInstanceMutex.lock();
if (!_instance) {
qDebug("First call to Menu::getInstance() - initing menu.\n");
_instance = new Menu();
}
menuInstanceMutex.unlock();
return _instance;
}
@ -61,7 +68,8 @@ Menu::Menu() :
_lodToolsDialog(NULL),
_maxVoxels(DEFAULT_MAX_VOXELS_PER_SYSTEM),
_voxelSizeScale(DEFAULT_VOXEL_SIZE_SCALE),
_boundaryLevelAdjust(0)
_boundaryLevelAdjust(0),
_maxVoxelPacketsPerSecond(DEFAULT_MAX_VOXEL_PPS)
{
Application *appInstance = Application::getInstance();
@ -297,22 +305,14 @@ Menu::Menu() :
addCheckableActionToQMenuAndActionHash(voxelOptionsMenu, MenuOption::DontFadeOnVoxelServerChanges);
addActionToQMenuAndActionHash(voxelOptionsMenu, MenuOption::LodTools, Qt::SHIFT | Qt::Key_L, this, SLOT(lodTools()));
QMenu* cullingOptionsMenu = voxelOptionsMenu->addMenu("Culling Options");
addDisabledActionAndSeparator(cullingOptionsMenu, "Standard Settings");
addCheckableActionToQMenuAndActionHash(cullingOptionsMenu, MenuOption::OldVoxelCullingMode, 0,
false, this, SLOT(setOldVoxelCullingMode(bool)));
addCheckableActionToQMenuAndActionHash(cullingOptionsMenu, MenuOption::NewVoxelCullingMode, 0,
false, this, SLOT(setNewVoxelCullingMode(bool)));
addDisabledActionAndSeparator(cullingOptionsMenu, "Individual Option Settings");
addCheckableActionToQMenuAndActionHash(cullingOptionsMenu, MenuOption::DisableFastVoxelPipeline, 0,
false, appInstance->getVoxels(), SLOT(setDisableFastVoxelPipeline(bool)));
addCheckableActionToQMenuAndActionHash(cullingOptionsMenu, MenuOption::DisableHideOutOfView);
addCheckableActionToQMenuAndActionHash(cullingOptionsMenu, MenuOption::RemoveOutOfView);
addCheckableActionToQMenuAndActionHash(cullingOptionsMenu, MenuOption::UseFullFrustumInHide);
addCheckableActionToQMenuAndActionHash(cullingOptionsMenu, MenuOption::DisableConstantCulling);
QMenu* voxelProtoOptionsMenu = voxelOptionsMenu->addMenu("Voxel Server Protocol Options");
addCheckableActionToQMenuAndActionHash(voxelProtoOptionsMenu, MenuOption::DisableColorVoxels);
addCheckableActionToQMenuAndActionHash(voxelProtoOptionsMenu, MenuOption::DisableLowRes);
addCheckableActionToQMenuAndActionHash(voxelProtoOptionsMenu, MenuOption::DisableDeltaSending);
addCheckableActionToQMenuAndActionHash(voxelProtoOptionsMenu, MenuOption::EnableVoxelPacketCompression);
addCheckableActionToQMenuAndActionHash(voxelProtoOptionsMenu, MenuOption::EnableOcclusionCulling);
addCheckableActionToQMenuAndActionHash(voxelProtoOptionsMenu, MenuOption::DestructiveAddVoxel);
QMenu* avatarOptionsMenu = developerMenu->addMenu("Avatar Options");
@ -484,15 +484,9 @@ Menu::Menu() :
addCheckableActionToQMenuAndActionHash(renderDebugMenu, MenuOption::CoverageMapV2, Qt::SHIFT | Qt::CTRL | Qt::Key_P);
QMenu* audioDebugMenu = developerMenu->addMenu("Audio Debugging Tools");
addCheckableActionToQMenuAndActionHash(audioDebugMenu, MenuOption::EchoAudio);
QMenu* voxelProtoOptionsMenu = developerMenu->addMenu("Voxel Server Protocol Options");
addCheckableActionToQMenuAndActionHash(voxelProtoOptionsMenu, MenuOption::SendVoxelColors);
addCheckableActionToQMenuAndActionHash(voxelProtoOptionsMenu, MenuOption::LowRes);
addCheckableActionToQMenuAndActionHash(voxelProtoOptionsMenu, MenuOption::DeltaSending);
addCheckableActionToQMenuAndActionHash(voxelProtoOptionsMenu, MenuOption::OcclusionCulling);
addCheckableActionToQMenuAndActionHash(voxelProtoOptionsMenu, MenuOption::DestructiveAddVoxel);
addCheckableActionToQMenuAndActionHash(audioDebugMenu, MenuOption::EchoServerAudio);
addCheckableActionToQMenuAndActionHash(audioDebugMenu, MenuOption::EchoLocalAudio);
addCheckableActionToQMenuAndActionHash(developerMenu, MenuOption::ExtraDebugging);
addActionToQMenuAndActionHash(developerMenu, MenuOption::PasteToVoxel,
@ -523,6 +517,7 @@ void Menu::loadSettings(QSettings* settings) {
_fieldOfView = loadSetting(settings, "fieldOfView", DEFAULT_FIELD_OF_VIEW_DEGREES);
_faceshiftEyeDeflection = loadSetting(settings, "faceshiftEyeDeflection", DEFAULT_FACESHIFT_EYE_DEFLECTION);
_maxVoxels = loadSetting(settings, "maxVoxels", DEFAULT_MAX_VOXELS_PER_SYSTEM);
_maxVoxelPacketsPerSecond = loadSetting(settings, "maxVoxelsPPS", DEFAULT_MAX_VOXEL_PPS);
_voxelSizeScale = loadSetting(settings, "voxelSizeScale", DEFAULT_VOXEL_SIZE_SCALE);
_boundaryLevelAdjust = loadSetting(settings, "boundaryLevelAdjust", 0);
@ -552,6 +547,7 @@ void Menu::saveSettings(QSettings* settings) {
settings->setValue("fieldOfView", _fieldOfView);
settings->setValue("faceshiftEyeDeflection", _faceshiftEyeDeflection);
settings->setValue("maxVoxels", _maxVoxels);
settings->setValue("maxVoxelsPPS", _maxVoxelPacketsPerSecond);
settings->setValue("voxelSizeScale", _voxelSizeScale);
settings->setValue("boundaryLevelAdjust", _boundaryLevelAdjust);
settings->beginGroup("View Frustum Offset Camera");
@ -832,6 +828,16 @@ void Menu::editPreferences() {
maxVoxels->setSingleStep(STEP_MAX_VOXELS);
maxVoxels->setValue(_maxVoxels);
form->addRow("Maximum Voxels:", maxVoxels);
QSpinBox* maxVoxelsPPS = new QSpinBox();
const int MAX_MAX_VOXELS_PPS = 6000;
const int MIN_MAX_VOXELS_PPS = 60;
const int STEP_MAX_VOXELS_PPS = 10;
maxVoxelsPPS->setMaximum(MAX_MAX_VOXELS_PPS);
maxVoxelsPPS->setMinimum(MIN_MAX_VOXELS_PPS);
maxVoxelsPPS->setSingleStep(STEP_MAX_VOXELS_PPS);
maxVoxelsPPS->setValue(_maxVoxelPacketsPerSecond);
form->addRow("Maximum Voxels Packets Per Second:", maxVoxelsPPS);
QDialogButtonBox* buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
dialog.connect(buttons, SIGNAL(accepted()), SLOT(accept()));
@ -871,6 +877,8 @@ void Menu::editPreferences() {
_maxVoxels = maxVoxels->value();
applicationInstance->getVoxels()->setMaxVoxels(_maxVoxels);
_maxVoxelPacketsPerSecond = maxVoxelsPPS->value();
applicationInstance->getAvatar()->setLeanScale(leanScale->value());
@ -1138,30 +1146,3 @@ void Menu::updateFrustumRenderModeAction() {
}
}
void Menu::setOldVoxelCullingMode(bool oldMode) {
setVoxelCullingMode(oldMode);
}
void Menu::setNewVoxelCullingMode(bool newMode) {
setVoxelCullingMode(!newMode);
}
/// This will switch on or off several different individual settings options all at once based on choosing with Old or New
/// voxel culling mode.
void Menu::setVoxelCullingMode(bool oldMode) {
const QString menus[] = { MenuOption::DisableFastVoxelPipeline, MenuOption::RemoveOutOfView, MenuOption::DisableHideOutOfView,
MenuOption::UseFullFrustumInHide, MenuOption::DisableConstantCulling};
bool oldModeValue[] = { true, true, true, true, true };
bool newModeValue[] = { false, false, false, false, false };
for (int i = 0; i < sizeof(menus) / sizeof(menus[0]); i++) {
bool desiredValue = oldMode ? oldModeValue[i] : newModeValue[i];
if (isOptionChecked(menus[i]) != desiredValue) {
triggerOption(menus[i]);
}
}
// set the checkmarks accordingly...
_actionHash.value(MenuOption::OldVoxelCullingMode)->setChecked(oldMode);
_actionHash.value(MenuOption::NewVoxelCullingMode)->setChecked(!oldMode);
}

View file

@ -68,6 +68,9 @@ public:
void setBoundaryLevelAdjust(int boundaryLevelAdjust);
int getBoundaryLevelAdjust() const { return _boundaryLevelAdjust; }
// User Tweakable PPS from Voxel Server
int getMaxVoxelPacketsPerSecond() const { return _maxVoxelPacketsPerSecond; }
public slots:
void bandwidthDetails();
void voxelStatsDetails();
@ -93,8 +96,6 @@ private slots:
void chooseVoxelPaintColor();
void runTests();
void resetSwatchColors();
void setOldVoxelCullingMode(bool oldMode);
void setNewVoxelCullingMode(bool newMode);
private:
static Menu* _instance;
@ -124,7 +125,6 @@ private:
const char* member = NULL);
void updateFrustumRenderModeAction();
void setVoxelCullingMode(bool oldMode);
QHash<QString, QAction*> _actionHash;
int _audioJitterBufferSamples; /// number of extra samples to wait before starting audio playback
@ -140,6 +140,7 @@ private:
float _voxelSizeScale;
int _boundaryLevelAdjust;
QAction* _useVoxelShader;
int _maxVoxelPacketsPerSecond;
};
namespace MenuOption {
@ -161,14 +162,17 @@ namespace MenuOption {
const QString DecreaseVoxelSize = "Decrease Voxel Size";
const QString DeleteVoxels = "Delete";
const QString DestructiveAddVoxel = "Create Voxel is Destructive";
const QString DeltaSending = "Delta Sending";
const QString DisableConstantCulling = "Disable Constant Culling";
const QString DisableFastVoxelPipeline = "Disable Fast Voxel Pipeline";
const QString DisableColorVoxels = "Disable Colored Voxels";
const QString DisableDeltaSending = "Disable Delta Sending";
const QString DisableLowRes = "Disable Lower Resolution While Moving";
const QString DisplayFrustum = "Display Frustum";
const QString DisplayLeapHands = "Display Leap Hands";
const QString DontRenderVoxels = "Don't call _voxels.render()";
const QString DontCallOpenGLForVoxels = "Don't call glDrawRangeElementsEXT() for Voxels";
const QString EchoAudio = "Echo Audio";
const QString EnableOcclusionCulling = "Enable Occlusion Culling";
const QString EnableVoxelPacketCompression = "Enable Voxel Packet Compression";
const QString EchoServerAudio = "Echo Server Audio";
const QString EchoLocalAudio = "Echo Local Audio";
const QString ExportVoxels = "Export Voxels";
const QString ExtraDebugging = "Extra Debugging";
const QString DontFadeOnVoxelServerChanges = "Don't Fade In/Out on Voxel Server Changes";
@ -190,7 +194,6 @@ namespace MenuOption {
const QString GlowMode = "Cycle Glow Mode";
const QString GoToDomain = "Go To Domain...";
const QString GoToLocation = "Go To Location...";
const QString DisableHideOutOfView = "Disable Hide Out of View Voxels";
const QString GoToUser = "Go To User...";
const QString ImportVoxels = "Import Voxels";
const QString ImportVoxelsClipboard = "Import Voxels to Clipboard";
@ -206,12 +209,10 @@ namespace MenuOption {
const QString Login = "Login";
const QString LookAtIndicator = "Look-at Indicator";
const QString LookAtVectors = "Look-at Vectors";
const QString LowRes = "Lower Resolution While Moving";
const QString Mirror = "Mirror";
const QString MoveWithLean = "Move with Lean";
const QString NewVoxelCullingMode = "New Voxel Culling Mode";
const QString NudgeVoxels = "Nudge";
const QString OcclusionCulling = "Occlusion Culling";
const QString OffAxisProjection = "Off-Axis Projection";
const QString OldVoxelCullingMode = "Old Voxel Culling Mode";
const QString TurnWithHead = "Turn using Head";
@ -223,11 +224,9 @@ namespace MenuOption {
const QString PipelineWarnings = "Show Render Pipeline Warnings";
const QString Preferences = "Preferences...";
const QString RandomizeVoxelColors = "Randomize Voxel TRUE Colors";
const QString RemoveOutOfView = "Instead of Hide Remove Out of View Voxels";
const QString ResetAvatarSize = "Reset Avatar Size";
const QString ResetSwatchColors = "Reset Swatch Colors";
const QString RunTimingTests = "Run Timing Tests";
const QString SendVoxelColors = "Colored Voxels";
const QString SettingsImport = "Import Settings";
const QString Shadows = "Shadows";
const QString SettingsExport = "Export Settings";
@ -244,7 +243,6 @@ namespace MenuOption {
const QString TreeStats = "Calculate Tree Stats";
const QString TransmitterDrive = "Transmitter Drive";
const QString Quit = "Quit";
const QString UseFullFrustumInHide = "Use Full View Frustums when Culling";
const QString UseVoxelShader = "Use Voxel Shader";
const QString VoxelsAsPoints = "Draw Voxels as Points";
const QString Voxels = "Voxels";

View file

@ -25,6 +25,8 @@ void VoxelPacketProcessor::processPacket(const HifiSockAddr& senderAddress, unsi
ssize_t messageLength = packetLength;
Application* app = Application::getInstance();
bool wasStatsPacket = false;
// check to see if the UI thread asked us to kill the voxel tree. since we're the only thread allowed to do that
if (app->_wantToKillLocalVoxels) {
@ -32,12 +34,13 @@ void VoxelPacketProcessor::processPacket(const HifiSockAddr& senderAddress, unsi
app->_wantToKillLocalVoxels = false;
}
// note: PACKET_TYPE_VOXEL_STATS can have PACKET_TYPE_VOXEL_DATA or PACKET_TYPE_VOXEL_DATA_MONOCHROME
// note: PACKET_TYPE_VOXEL_STATS can have PACKET_TYPE_VOXEL_DATA
// immediately following them inside the same packet. So, we process the PACKET_TYPE_VOXEL_STATS first
// then process any remaining bytes as if it was another packet
if (packetData[0] == PACKET_TYPE_VOXEL_STATS) {
int statsMessageLength = app->parseVoxelStats(packetData, messageLength, senderAddress);
wasStatsPacket = true;
if (messageLength > statsMessageLength) {
packetData += statsMessageLength;
messageLength -= statsMessageLength;
@ -45,6 +48,7 @@ void VoxelPacketProcessor::processPacket(const HifiSockAddr& senderAddress, unsi
return; // bail since piggyback data doesn't match our versioning
}
} else {
// Note... stats packets don't have sequence numbers, so we don't want to send those to trackIncomingVoxelPacket()
return; // bail since no piggyback data
}
} // fall through to piggyback message

View file

@ -595,50 +595,66 @@ int VoxelSystem::parseData(unsigned char* sourceBuffer, int numBytes) {
unsigned char command = *sourceBuffer;
int numBytesPacketHeader = numBytesForPacketHeader(sourceBuffer);
unsigned char* voxelData = sourceBuffer + numBytesPacketHeader;
switch(command) {
case PACKET_TYPE_VOXEL_DATA: {
PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings),
"readBitstreamToTree()");
// ask the VoxelTree to read the bitstream into the tree
ReadBitstreamToTreeParams args(WANT_COLOR, WANT_EXISTS_BITS, NULL, getDataSourceUUID());
lockTree();
_tree->readBitstreamToTree(voxelData, numBytes - numBytesPacketHeader, args);
unlockTree();
}
break;
case PACKET_TYPE_VOXEL_DATA_MONOCHROME: {
PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings),
"readBitstreamToTree()");
// ask the VoxelTree to read the MONOCHROME bitstream into the tree
ReadBitstreamToTreeParams args(NO_COLOR, WANT_EXISTS_BITS, NULL, getDataSourceUUID());
lockTree();
_tree->readBitstreamToTree(voxelData, numBytes - numBytesPacketHeader, args);
unlockTree();
}
break;
case PACKET_TYPE_Z_COMMAND:
// the Z command is a special command that allows the sender to send high level semantic
// requests, like erase all, or add sphere scene, different receivers may handle these
// messages differently
char* packetData = (char *)sourceBuffer;
char* command = &packetData[numBytesPacketHeader]; // start of the command
int commandLength = strlen(command); // commands are null terminated strings
int totalLength = 1+commandLength+1;
unsigned char* dataAt = sourceBuffer + numBytesPacketHeader;
qDebug("got Z message len(%d)= %s\n", numBytes, command);
while (totalLength <= numBytes) {
if (0==strcmp(command,(char*)"erase all")) {
qDebug("got Z message == erase all - NOT SUPPORTED ON INTERFACE\n");
VOXEL_PACKET_FLAGS flags = (*(VOXEL_PACKET_FLAGS*)(dataAt));
dataAt += sizeof(VOXEL_PACKET_FLAGS);
VOXEL_PACKET_SEQUENCE sequence = (*(VOXEL_PACKET_SEQUENCE*)dataAt);
dataAt += sizeof(VOXEL_PACKET_SEQUENCE);
VOXEL_PACKET_SENT_TIME sentAt = (*(VOXEL_PACKET_SENT_TIME*)dataAt);
dataAt += sizeof(VOXEL_PACKET_SENT_TIME);
bool packetIsColored = oneAtBit(flags, PACKET_IS_COLOR_BIT);
bool packetIsCompressed = oneAtBit(flags, PACKET_IS_COMPRESSED_BIT);
VOXEL_PACKET_SENT_TIME arrivedAt = usecTimestampNow();
int flightTime = arrivedAt - sentAt;
VOXEL_PACKET_INTERNAL_SECTION_SIZE sectionLength = 0;
int dataBytes = numBytes - VOXEL_PACKET_HEADER_SIZE;
int subsection = 1;
while (dataBytes > 0) {
if (packetIsCompressed) {
if (dataBytes > sizeof(VOXEL_PACKET_INTERNAL_SECTION_SIZE)) {
sectionLength = (*(VOXEL_PACKET_INTERNAL_SECTION_SIZE*)dataAt);
dataAt += sizeof(VOXEL_PACKET_INTERNAL_SECTION_SIZE);
dataBytes -= sizeof(VOXEL_PACKET_INTERNAL_SECTION_SIZE);
} else {
sectionLength = 0;
dataBytes = 0; // stop looping something is wrong
}
} else {
sectionLength = dataBytes;
}
if (0==strcmp(command,(char*)"add scene")) {
qDebug("got Z message == add scene - NOT SUPPORTED ON INTERFACE\n");
if (sectionLength) {
// ask the VoxelTree to read the bitstream into the tree
ReadBitstreamToTreeParams args(packetIsColored ? WANT_COLOR : NO_COLOR, WANT_EXISTS_BITS, NULL, getDataSourceUUID());
lockTree();
VoxelPacketData packetData(packetIsCompressed);
packetData.loadFinalizedContent(dataAt, sectionLength);
if (Menu::getInstance()->isOptionChecked(MenuOption::ExtraDebugging)) {
qDebug("Got Packet color:%s compressed:%s sequence: %u flight:%d usec size:%d data:%d"
" subsection:%d sectionLength:%d uncompressed:%d\n",
debug::valueOf(packetIsColored), debug::valueOf(packetIsCompressed),
sequence, flightTime, numBytes, dataBytes, subsection, sectionLength, packetData.getUncompressedSize());
}
_tree->readBitstreamToTree(packetData.getUncompressedData(), packetData.getUncompressedSize(), args);
unlockTree();
dataBytes -= sectionLength;
dataAt += sectionLength;
}
totalLength += commandLength+1;
}
subsection++;
}
break;
}
@ -762,7 +778,10 @@ void VoxelSystem::checkForCulling() {
uint64_t start = usecTimestampNow();
uint64_t sinceLastViewCulling = (start - _lastViewCulling) / 1000;
bool constantCulling = !Menu::getInstance()->isOptionChecked(MenuOption::DisableConstantCulling);
// These items used to be menu options, we are not defaulting to and only supporting these modes.
bool constantCulling = true;
bool performHideOutOfViewLogic = true;
bool performRemoveOutOfViewLogic = false;
// If the view frustum is no longer changing, but has changed, since last time, then remove nodes that are out of view
if (constantCulling || (
@ -773,7 +792,7 @@ void VoxelSystem::checkForCulling() {
// When we call removeOutOfView() voxels, we don't actually remove the voxels from the VBOs, but we do remove
// them from tree, this makes our tree caclulations faster, but doesn't require us to fully rebuild the VBOs (which
// can be expensive).
if (!Menu::getInstance()->isOptionChecked(MenuOption::DisableHideOutOfView)) {
if (performHideOutOfViewLogic) {
// track how long its been since we were last moving. If we have recently moved then only use delta frustums, if
// it's been a long time since we last moved, then go ahead and do a full frustum cull.
@ -807,7 +826,7 @@ void VoxelSystem::checkForCulling() {
_lastViewCullingElapsed = (endViewCulling - start) / 1000;
}
} else if (Menu::getInstance()->isOptionChecked(MenuOption::RemoveOutOfView)) {
} else if (performRemoveOutOfViewLogic) {
_lastViewCulling = start;
removeOutOfView();
uint64_t endViewCulling = usecTimestampNow();
@ -1931,7 +1950,7 @@ void VoxelSystem::hideOutOfView(bool forceFullFrustum) {
// Both these problems are solved by intermittently calling this with forceFullFrustum set
// to true. This will essentially clean up the improperly hidden or shown voxels.
//
bool wantDeltaFrustums = !forceFullFrustum && !Menu::getInstance()->isOptionChecked(MenuOption::UseFullFrustumInHide);
bool wantDeltaFrustums = !forceFullFrustum;
hideOutOfViewArgs args(this, this->_tree, _culledOnce, widenFrustum, wantDeltaFrustums);
const bool wantViewFrustumDebugging = false; // change to true for additional debugging

View file

@ -21,6 +21,7 @@
#include "DataServerClient.h"
#include "Hand.h"
#include "Head.h"
#include "Menu.h"
#include "Physics.h"
#include "world.h"
#include "devices/OculusManager.h"
@ -394,7 +395,7 @@ void Avatar::simulate(float deltaTime, Transmitter* transmitter) {
_skeletonModel.simulate(deltaTime);
_head.setBodyRotation(glm::vec3(_bodyPitch, _bodyYaw, _bodyRoll));
glm::vec3 headPosition;
if (!_skeletonModel.getHeadPosition(headPosition)) {
if (Menu::getInstance()->isOptionChecked(MenuOption::AvatarAsBalls) || !_skeletonModel.getHeadPosition(headPosition)) {
headPosition = _bodyBall[BODY_BALL_HEAD_BASE].position;
}
_head.setPosition(headPosition);
@ -439,7 +440,7 @@ static TextRenderer* textRenderer() {
return renderer;
}
void Avatar::render(bool forceRenderHead, bool renderAvatarBalls) {
void Avatar::render(bool forceRenderHead) {
if (Application::getInstance()->getAvatar()->getHand().isRaveGloveActive()) {
_hand.setRaveLights(RAVE_LIGHTS_AVATAR);
@ -455,7 +456,7 @@ void Avatar::render(bool forceRenderHead, bool renderAvatarBalls) {
Glower glower(_moving && glm::length(toTarget) > GLOW_DISTANCE ? 1.0f : 0.0f);
// render body
renderBody(forceRenderHead, renderAvatarBalls);
renderBody(forceRenderHead);
// render sphere when far away
const float MAX_ANGLE = 10.f;
@ -666,7 +667,8 @@ void Avatar::updateArmIKAndConstraints(float deltaTime, AvatarJointID fingerTipJ
float distance = glm::length(armVector);
// don't let right hand get dragged beyond maximum arm length...
float armLength = _skeletonModel.isActive() ? _skeletonModel.getRightArmLength() : _skeleton.getArmLength();
float armLength = (_skeletonModel.isActive() && !Menu::getInstance()->isOptionChecked(MenuOption::AvatarAsBalls)) ?
_skeletonModel.getRightArmLength() : _skeleton.getArmLength();
const float ARM_RETRACTION = 0.75f;
float retractedArmLength = armLength * ARM_RETRACTION;
if (distance > retractedArmLength) {
@ -713,7 +715,7 @@ float Avatar::getBallRenderAlpha(int ball, bool forceRenderHead) const {
return 1.0f;
}
void Avatar::renderBody(bool forceRenderHead, bool renderAvatarBalls) {
void Avatar::renderBody(bool forceRenderHead) {
if (_head.getVideoFace().isFullFrame()) {
// Render the full-frame video
@ -721,7 +723,7 @@ void Avatar::renderBody(bool forceRenderHead, bool renderAvatarBalls) {
if (alpha > 0.0f) {
_head.getVideoFace().render(1.0f);
}
} else if (renderAvatarBalls || !(_voxels.getVoxelURL().isValid() || _skeletonModel.isActive())) {
} else if (Menu::getInstance()->isOptionChecked(MenuOption::AvatarAsBalls)) {
// Render the body as balls and cones
glm::vec3 skinColor, darkSkinColor;
getSkinColors(skinColor, darkSkinColor);
@ -738,7 +740,7 @@ void Avatar::renderBody(bool forceRenderHead, bool renderAvatarBalls) {
// Always render other people, and render myself when beyond threshold distance
if (b == BODY_BALL_HEAD_BASE) { // the head is rendered as a special
if (alpha > 0.0f) {
_head.render(alpha, false);
_head.render(alpha, true);
}
} else if (alpha > 0.0f) {
// Render the body ball sphere
@ -746,10 +748,6 @@ void Avatar::renderBody(bool forceRenderHead, bool renderAvatarBalls) {
skinColor.g - _bodyBall[b].touchForce * 0.2f,
skinColor.b - _bodyBall[b].touchForce * 0.1f);
if (b == BODY_BALL_NECK_BASE && _head.getFaceModel().isActive()) {
continue; // don't render the neck if we have a face model
}
if ((b != BODY_BALL_HEAD_TOP )
&& (b != BODY_BALL_HEAD_BASE )) {
glPushMatrix();
@ -793,7 +791,7 @@ void Avatar::renderBody(bool forceRenderHead, bool renderAvatarBalls) {
void Avatar::getSkinColors(glm::vec3& lighter, glm::vec3& darker) {
lighter = glm::vec3(SKIN_COLOR[0], SKIN_COLOR[1], SKIN_COLOR[2]);
darker = glm::vec3(DARK_SKIN_COLOR[0], DARK_SKIN_COLOR[1], DARK_SKIN_COLOR[2]);
if (_head.getFaceModel().isActive()) {
if (!Menu::getInstance()->isOptionChecked(MenuOption::AvatarAsBalls) && _head.getFaceModel().isActive()) {
lighter = glm::vec3(_head.getFaceModel().computeAverageColor());
const float SKIN_DARKENING = 0.9f;
darker = lighter * SKIN_DARKENING;

View file

@ -141,7 +141,7 @@ public:
void init();
void simulate(float deltaTime, Transmitter* transmitter);
void follow(Avatar* leadingAvatar);
void render(bool forceRenderHead, bool renderAvatarBalls);
void render(bool forceRenderHead);
//setters
void setDisplayingLookatVectors(bool displayingLookatVectors) { _head.setRenderLookatVectors(displayingLookatVectors); }
@ -256,7 +256,7 @@ private:
// private methods...
glm::vec3 calculateAverageEyePosition() { return _head.calculateAverageEyePosition(); } // get the position smack-dab between the eyes (for lookat)
float getBallRenderAlpha(int ball, bool forceRenderHead) const;
void renderBody(bool forceRenderHead, bool renderAvatarBalls);
void renderBody(bool forceRenderHead);
void initializeBodyBalls();
void resetBodyBalls();
void updateHandMovementAndTouching(float deltaTime, bool enableHandMovement);

View file

@ -61,6 +61,33 @@ void Hand::simulate(float deltaTime, bool isMine) {
updateRaveGloveParticles(deltaTime);
}
// Create a voxel at fingertip if controller button is pressed
const float FINGERTIP_VOXEL_SIZE = 0.0125;
for (size_t i = 0; i < getNumPalms(); ++i) {
PalmData& palm = getPalms()[i];
if (palm.isActive()) {
FingerData& finger = palm.getFingers()[0];
glm::vec3 newVoxelPosition = finger.getTipPosition();
if (palm.getControllerButtons() & BUTTON_1) {
if (glm::length(newVoxelPosition - _lastFingerAddVoxel) > (FINGERTIP_VOXEL_SIZE / 2.f)) {
QColor paintColor = Menu::getInstance()->getActionForOption(MenuOption::VoxelPaintColor)->data().value<QColor>();
Application::getInstance()->makeVoxel(newVoxelPosition,
FINGERTIP_VOXEL_SIZE,
paintColor.red(),
paintColor.green(),
paintColor.blue(),
true);
_lastFingerAddVoxel = newVoxelPosition;
}
} else if (palm.getControllerButtons() & BUTTON_2) {
if (glm::length(newVoxelPosition - _lastFingerDeleteVoxel) > (FINGERTIP_VOXEL_SIZE / 2.f)) {
Application::getInstance()->removeVoxel(newVoxelPosition, FINGERTIP_VOXEL_SIZE);
_lastFingerDeleteVoxel = newVoxelPosition;
}
}
}
}
}
void Hand::calculateGeometry() {
@ -157,6 +184,45 @@ void Hand::render() {
}
}
// If hand controller buttons pressed, render stuff as needed
if (getPalms().size() > 0) {
for (size_t i = 0; i < getPalms().size(); ++i) {
PalmData& palm = getPalms()[i];
// If trigger pulled, thrust in that direction and draw beam
const float MAX_THRUSTER_BEAM_LENGTH = 5.f;
const float THRUSTER_MARKER_SIZE = 0.0125f;
if (palm.getJoystickY() != 0.f) {
FingerData& finger = palm.getFingers()[0];
if (finger.isActive()) {
if (palm.getJoystickY() > 0.f) {
glColor3f(0, 1, 0);
} else {
glColor3f(1, 0, 0);
}
glm::vec3 palmPosition = palm.getPosition();
glm::vec3 pointerPosition = palmPosition +
glm::normalize(finger.getTipPosition() - palmPosition) *
MAX_THRUSTER_BEAM_LENGTH;
glPushMatrix();
glm::vec3 markerPosition = palmPosition +
glm::normalize(finger.getTipPosition() - palmPosition) *
MAX_THRUSTER_BEAM_LENGTH *
(0.5f + palm.getJoystickY() / 2.f);
glTranslatef(markerPosition.x, markerPosition.y, markerPosition.z);
glutSolidSphere(THRUSTER_MARKER_SIZE, 10, 10);
glPopMatrix();
glLineWidth(2.0);
glBegin(GL_LINES);
glVertex3f(palmPosition.x, palmPosition.y, palmPosition.z);
glVertex3f(pointerPosition.x, pointerPosition.y, pointerPosition.z);
glEnd();
}
}
}
}
glEnable(GL_DEPTH_TEST);
glEnable(GL_RESCALE_NORMAL);
@ -227,8 +293,8 @@ void Hand::renderLeapHands() {
//const glm::vec3 handColor = _ballColor;
const glm::vec3 handColor(1.0, 0.84, 0.66); // use the skin color
glDisable(GL_DEPTH_TEST);
glDepthMask(GL_FALSE);
glEnable(GL_DEPTH_TEST);
glDepthMask(GL_TRUE);
glPushMatrix();
// Draw the leap balls
for (size_t i = 0; i < _leapFingerTipBalls.size(); i++) {

View file

@ -30,6 +30,7 @@ enum RaveLightsSetting {
RAVE_LIGHTS_PARTICLES
};
class Avatar;
class ProgramObject;
@ -46,7 +47,7 @@ public:
bool isCollidable; // whether or not the ball responds to collisions
float touchForce; // a scalar determining the amount that the cursor (or hand) is penetrating the ball
};
void init();
void reset();
void simulate(float deltaTime, bool isMine);
@ -62,7 +63,7 @@ public:
// getters
const glm::vec3& getLeapFingerTipBallPosition (int ball) const { return _leapFingerTipBalls [ball].position;}
const glm::vec3& getLeapFingerRootBallPosition(int ball) const { return _leapFingerRootBalls[ball].position;}
private:
// disallow copies of the Hand, copy of owning Avatar is disallowed too
Hand(const Hand&);
@ -72,6 +73,8 @@ private:
float _raveGloveClock;
bool _raveGloveInitialized;
int _raveGloveEmitter[NUM_FINGERS];
int _controllerButtons; /// Button states read from hand-held controllers
Avatar* _owningAvatar;
float _renderAlpha;
@ -79,6 +82,8 @@ private:
std::vector<HandBall> _leapFingerTipBalls;
std::vector<HandBall> _leapFingerRootBalls;
glm::vec3 _lastFingerAddVoxel, _lastFingerDeleteVoxel;
// private methods
void setLeapHands(const std::vector<glm::vec3>& handPositions,
const std::vector<glm::vec3>& handNormals);

View file

@ -250,7 +250,9 @@ void Head::simulate(float deltaTime, bool isMine) {
calculateGeometry();
// the blend face may have custom eye meshes
_faceModel.getEyePositions(_leftEyePosition, _rightEyePosition);
if (!Menu::getInstance()->isOptionChecked(MenuOption::AvatarAsBalls)) {
_faceModel.getEyePositions(_leftEyePosition, _rightEyePosition);
}
}
void Head::calculateGeometry() {
@ -295,10 +297,11 @@ void Head::calculateGeometry() {
+ up * _scale * NOSE_UPTURN;
}
void Head::render(float alpha, bool isMine) {
void Head::render(float alpha, bool renderAvatarBalls) {
_renderAlpha = alpha;
if (!(_videoFace.render(alpha) || _faceModel.render(alpha))) {
bool lookatVectorsVisible = _renderLookatVectors;
if (renderAvatarBalls) {
glEnable(GL_DEPTH_TEST);
glEnable(GL_RESCALE_NORMAL);
@ -309,9 +312,12 @@ void Head::render(float alpha, bool isMine) {
renderMouth();
renderNose();
renderEyeBrows();
} else if (!_videoFace.render(alpha)) {
lookatVectorsVisible &= _faceModel.render(alpha);
}
if (_renderLookatVectors) {
if (lookatVectorsVisible) {
renderLookatVectors(_leftEyePosition, _rightEyePosition, _lookAtPosition);
}
}

View file

@ -44,7 +44,7 @@ public:
void init();
void reset();
void simulate(float deltaTime, bool isMine);
void render(float alpha, bool isMine);
void render(float alpha, bool renderAvatarBalls);
void renderMohawk();
void setScale(float scale);

View file

@ -325,7 +325,7 @@ void MyAvatar::simulate(float deltaTime, Transmitter* transmitter) {
_skeletonModel.simulate(deltaTime);
_head.setBodyRotation(glm::vec3(_bodyPitch, _bodyYaw, _bodyRoll));
glm::vec3 headPosition;
if (!_skeletonModel.getHeadPosition(headPosition)) {
if (Menu::getInstance()->isOptionChecked(MenuOption::AvatarAsBalls) || !_skeletonModel.getHeadPosition(headPosition)) {
headPosition = _bodyBall[BODY_BALL_HEAD_BASE].position;
}
_head.setPosition(headPosition);
@ -381,15 +381,18 @@ void MyAvatar::updateFromGyrosAndOrWebcam(bool turnWithHead) {
if (faceshift->isActive()) {
estimatedPosition = faceshift->getHeadTranslation();
estimatedRotation = safeEulerAngles(faceshift->getHeadRotation());
// Rotate the body if the head is turned quickly
// Rotate the body if the head is turned beyond the screen
if (turnWithHead) {
glm::vec3 headAngularVelocity = faceshift->getHeadAngularVelocity();
const float FACESHIFT_YAW_TURN_SENSITIVITY = 0.25f;
const float FACESHIFT_MIN_YAW_TURN = 10.f;
const float FACESHIFT_MAX_YAW_TURN = 30.f;
const float FACESHIFT_YAW_TURN_SENSITIVITY = 0.5f;
const float FACESHIFT_MIN_YAW_TURN = 15.f;
const float FACESHIFT_MAX_YAW_TURN = 50.f;
if ( (fabs(estimatedRotation.y) > FACESHIFT_MIN_YAW_TURN) &&
(fabs(estimatedRotation.y) < FACESHIFT_MAX_YAW_TURN) ) {
_bodyYawDelta += estimatedRotation.y * FACESHIFT_YAW_TURN_SENSITIVITY;
if (estimatedRotation.y > 0.f) {
_bodyYawDelta += (estimatedRotation.y - FACESHIFT_MIN_YAW_TURN) * FACESHIFT_YAW_TURN_SENSITIVITY;
} else {
_bodyYawDelta += (estimatedRotation.y + FACESHIFT_MIN_YAW_TURN) * FACESHIFT_YAW_TURN_SENSITIVITY;
}
}
}
} else if (gyros->isActive()) {
@ -459,22 +462,33 @@ void MyAvatar::updateFromGyrosAndOrWebcam(bool turnWithHead) {
if (!Menu::getInstance()->isOptionChecked(MenuOption::MoveWithLean)) {
return;
}
const float ANGULAR_DRIVE_SCALE = 0.1f;
const float ANGULAR_DEAD_ZONE = 0.3f;
setDriveKeys(FWD, glm::clamp(-_head.getLeanForward() * ANGULAR_DRIVE_SCALE - ANGULAR_DEAD_ZONE, 0.0f, 1.0f));
setDriveKeys(BACK, glm::clamp(_head.getLeanForward() * ANGULAR_DRIVE_SCALE - ANGULAR_DEAD_ZONE, 0.0f, 1.0f));
setDriveKeys(LEFT, glm::clamp(_head.getLeanSideways() * ANGULAR_DRIVE_SCALE - ANGULAR_DEAD_ZONE, 0.0f, 1.0f));
setDriveKeys(RIGHT, glm::clamp(-_head.getLeanSideways() * ANGULAR_DRIVE_SCALE - ANGULAR_DEAD_ZONE, 0.0f, 1.0f));
// only consider going up if we're not going in any of the four horizontal directions
if (_driveKeys[FWD] == 0.0f && _driveKeys[BACK] == 0.0f && _driveKeys[LEFT] == 0.0f && _driveKeys[RIGHT] == 0.0f) {
const float LINEAR_DRIVE_SCALE = 5.0f;
const float LINEAR_DEAD_ZONE = 0.95f;
float torsoDelta = glm::length(relativePosition) - TORSO_LENGTH;
setDriveKeys(UP, glm::clamp(torsoDelta * LINEAR_DRIVE_SCALE - LINEAR_DEAD_ZONE, 0.0f, 1.0f));
} else {
setDriveKeys(UP, 0.0f);
// Move with Lean by applying thrust proportional to leaning
glm::quat orientation = _head.getCameraOrientation();
glm::vec3 front = orientation * IDENTITY_FRONT;
glm::vec3 right = orientation * IDENTITY_RIGHT;
float leanForward = _head.getLeanForward();
float leanSideways = _head.getLeanSideways();
// Degrees of 'dead zone' when leaning, and amount of acceleration to apply to lean angle
const float LEAN_FWD_DEAD_ZONE = 15.f;
const float LEAN_SIDEWAYS_DEAD_ZONE = 10.f;
const float LEAN_FWD_THRUST_SCALE = 4.f;
const float LEAN_SIDEWAYS_THRUST_SCALE = 3.f;
if (fabs(leanForward) > LEAN_FWD_DEAD_ZONE) {
if (leanForward > 0.f) {
addThrust(front * -(leanForward - LEAN_FWD_DEAD_ZONE) * LEAN_FWD_THRUST_SCALE);
} else {
addThrust(front * -(leanForward + LEAN_FWD_DEAD_ZONE) * LEAN_FWD_THRUST_SCALE);
}
}
if (fabs(leanSideways) > LEAN_SIDEWAYS_DEAD_ZONE) {
if (leanSideways > 0.f) {
addThrust(right * -(leanSideways - LEAN_SIDEWAYS_DEAD_ZONE) * LEAN_SIDEWAYS_THRUST_SCALE);
} else {
addThrust(right * -(leanSideways + LEAN_SIDEWAYS_DEAD_ZONE) * LEAN_SIDEWAYS_THRUST_SCALE);
}
}
}
@ -483,7 +497,7 @@ static TextRenderer* textRenderer() {
return renderer;
}
void MyAvatar::render(bool forceRenderHead, bool renderAvatarBalls) {
void MyAvatar::render(bool forceRenderHead) {
if (Application::getInstance()->getAvatar()->getHand().isRaveGloveActive()) {
_hand.setRaveLights(RAVE_LIGHTS_AVATAR);
@ -493,7 +507,7 @@ void MyAvatar::render(bool forceRenderHead, bool renderAvatarBalls) {
renderDiskShadow(_position, glm::vec3(0.0f, 1.0f, 0.0f), _scale * 0.1f, 0.2f);
// render body
renderBody(forceRenderHead, renderAvatarBalls);
renderBody(forceRenderHead);
// if this is my avatar, then render my interactions with the other avatar
_avatarTouch.render(Application::getInstance()->getCamera()->getPosition());
@ -634,7 +648,7 @@ float MyAvatar::getBallRenderAlpha(int ball, bool forceRenderHead) const {
(distanceToCamera - DO_NOT_RENDER_INSIDE) / (RENDER_OPAQUE_OUTSIDE - DO_NOT_RENDER_INSIDE), 0.f, 1.f);
}
void MyAvatar::renderBody(bool forceRenderHead, bool renderAvatarBalls) {
void MyAvatar::renderBody(bool forceRenderHead) {
if (_head.getVideoFace().isFullFrame()) {
// Render the full-frame video
@ -642,7 +656,7 @@ void MyAvatar::renderBody(bool forceRenderHead, bool renderAvatarBalls) {
if (alpha > 0.0f) {
_head.getVideoFace().render(1.0f);
}
} else if (renderAvatarBalls || !(_voxels.getVoxelURL().isValid() || _skeletonModel.isActive())) {
} else if (Menu::getInstance()->isOptionChecked(MenuOption::AvatarAsBalls)) {
// Render the body as balls and cones
glm::vec3 skinColor, darkSkinColor;
getSkinColors(skinColor, darkSkinColor);
@ -676,10 +690,6 @@ void MyAvatar::renderBody(bool forceRenderHead, bool renderAvatarBalls) {
alpha);
}
if (b == BODY_BALL_NECK_BASE && _head.getFaceModel().isActive()) {
continue; // don't render the neck if we have a face model
}
if ((b != BODY_BALL_HEAD_TOP )
&& (b != BODY_BALL_HEAD_BASE )) {
glPushMatrix();
@ -715,7 +725,7 @@ void MyAvatar::renderBody(bool forceRenderHead, bool renderAvatarBalls) {
}
float alpha = getBallRenderAlpha(BODY_BALL_HEAD_BASE, forceRenderHead);
if (alpha > 0.0f) {
_head.render(alpha, true);
_head.render(alpha, false);
}
}
_hand.render();
@ -838,7 +848,25 @@ void MyAvatar::updateThrust(float deltaTime, Transmitter * transmitter) {
up;
}
}
// Add thrust and rotation from hand controllers
const float THRUST_MAG_HAND_JETS = THRUST_MAG_FWD;
const float JOYSTICK_YAW_MAG = YAW_MAG;
for (size_t i = 0; i < getHand().getPalms().size(); ++i) {
PalmData& palm = getHand().getPalms()[i];
if (palm.isActive()) {
if (palm.getJoystickY() != 0.f) {
FingerData& finger = palm.getFingers()[0];
if (finger.isActive()) {
}
glm::vec3 thrustDirection = glm::normalize(finger.getTipPosition() - palm.getPosition());
_thrust += thrustDirection * _scale * THRUST_MAG_HAND_JETS * palm.getJoystickY() * _thrustMultiplier * deltaTime;
}
if (palm.getJoystickX() != 0.f) {
_bodyYawDelta -= palm.getJoystickX() * JOYSTICK_YAW_MAG * deltaTime;
}
}
}
// Update speed brake status
const float MIN_SPEED_BRAKE_VELOCITY = _scale * 0.4f;
if ((glm::length(_thrust) == 0.0f) && _isThrustOn && (glm::length(_velocity) > MIN_SPEED_BRAKE_VELOCITY)) {
@ -1147,17 +1175,20 @@ bool operator<(const SortedAvatar& s1, const SortedAvatar& s2) {
}
void MyAvatar::updateChatCircle(float deltaTime) {
if (!Menu::getInstance()->isOptionChecked(MenuOption::ChatCircling)) {
if (!(_isChatCirclingEnabled = Menu::getInstance()->isOptionChecked(MenuOption::ChatCircling))) {
return;
}
// find all members and sort by distance
// find all circle-enabled members and sort by distance
QVector<SortedAvatar> sortedAvatars;
NodeList* nodeList = NodeList::getInstance();
for (NodeList::iterator node = nodeList->begin(); node != nodeList->end(); node++) {
if (node->getLinkedData() && node->getType() == NODE_TYPE_AGENT) {
SortedAvatar sortedAvatar;
sortedAvatar.avatar = (Avatar*)node->getLinkedData();
if (!sortedAvatar.avatar->isChatCirclingEnabled()) {
continue;
}
sortedAvatar.distance = glm::distance(_position, sortedAvatar.avatar->getPosition());
sortedAvatars.append(sortedAvatar);
}

View file

@ -20,7 +20,7 @@ public:
void reset();
void simulate(float deltaTime, Transmitter* transmitter);
void updateFromGyrosAndOrWebcam(bool turnWithHead);
void render(bool forceRenderHead, bool renderAvatarBalls);
void render(bool forceRenderHead);
void renderScreenTint(ScreenTintLayer layer);
// setters
@ -82,7 +82,7 @@ private:
// private methods
float getBallRenderAlpha(int ball, bool forceRenderHead) const;
void renderBody(bool forceRenderHead, bool renderAvatarBalls);
void renderBody(bool forceRenderHead);
void updateThrust(float deltaTime, Transmitter * transmitter);
void updateHandMovementAndTouching(float deltaTime, bool enableHandMovement);
void updateAvatarCollisions(float deltaTime);

View file

@ -23,6 +23,8 @@ Profile::Profile(const QString &username) :
if (!_username.isEmpty()) {
// we've been given a new username, ask the data-server for profile
DataServerClient::getClientValueForKey(DataServerKey::UUID);
DataServerClient::getClientValueForKey(DataServerKey::FaceMeshURL);
DataServerClient::getClientValueForKey(DataServerKey::SkeletonURL);
// send our current domain server to the data-server
updateDomain(NodeList::getInstance()->getDomainHostname());

View file

@ -6,21 +6,37 @@
// Copyright (c) 2012 High Fidelity, Inc. All rights reserved.
//
#include "OculusManager.h"
#include <QOpenGLFramebufferObject>
#include <glm/glm.hpp>
bool OculusManager::_isConnected = false;
#include "Application.h"
#include "InterfaceConfig.h"
#include "OculusManager.h"
ProgramObject OculusManager::_program;
int OculusManager::_textureLocation;
int OculusManager::_lensCenterLocation;
int OculusManager::_screenCenterLocation;
int OculusManager::_scaleLocation;
int OculusManager::_scaleInLocation;
int OculusManager::_hmdWarpParamLocation;
bool OculusManager::_isConnected = false;
float OculusManager::_yawOffset = 0;
#ifdef HAVE_LIBOVR
using namespace OVR;
using namespace OVR::Util::Render;
#ifdef __APPLE__
Ptr<DeviceManager> OculusManager::_deviceManager;
Ptr<HMDDevice> OculusManager::_hmdDevice;
Ptr<SensorDevice> OculusManager::_sensorDevice;
SensorFusion OculusManager::_sensorFusion;
float OculusManager::_yawOffset = 0;
SensorFusion* OculusManager::_sensorFusion;
StereoConfig OculusManager::_stereoConfig;
#endif
void OculusManager::connect() {
#ifdef __APPLE__
#ifdef HAVE_LIBOVR
System::Init();
_deviceManager = *DeviceManager::Create();
_hmdDevice = *_deviceManager->EnumerateDevices<HMDDevice>().CreateDevice();
@ -29,25 +45,152 @@ void OculusManager::connect() {
_isConnected = true;
_sensorDevice = *_hmdDevice->GetSensor();
_sensorFusion.AttachToSensor(_sensorDevice);
_sensorFusion = new SensorFusion;
_sensorFusion->AttachToSensor(_sensorDevice);
// default the yaw to the current orientation
_sensorFusion.SetMagReference();
HMDInfo info;
_hmdDevice->GetDeviceInfo(&info);
_stereoConfig.SetHMDInfo(info);
switchToResourcesParentIfRequired();
_program.addShaderFromSourceFile(QGLShader::Fragment, "resources/shaders/oculus.frag");
_program.link();
_textureLocation = _program.uniformLocation("texture");
_lensCenterLocation = _program.uniformLocation("lensCenter");
_screenCenterLocation = _program.uniformLocation("screenCenter");
_scaleLocation = _program.uniformLocation("scale");
_scaleInLocation = _program.uniformLocation("scaleIn");
_hmdWarpParamLocation = _program.uniformLocation("hmdWarpParam");
}
#endif
}
void OculusManager::configureCamera(Camera& camera, int screenWidth, int screenHeight) {
#ifdef HAVE_LIBOVR
_stereoConfig.SetFullViewport(Viewport(0, 0, screenWidth, screenHeight));
camera.setAspectRatio(_stereoConfig.GetAspect());
camera.setFieldOfView(_stereoConfig.GetYFOVDegrees());
#endif
}
void OculusManager::display(Camera& whichCamera) {
#ifdef HAVE_LIBOVR
Application::getInstance()->getGlowEffect()->prepare();
// render the left eye view to the left side of the screen
const StereoEyeParams& leftEyeParams = _stereoConfig.GetEyeRenderParams(StereoEye_Left);
glMatrixMode(GL_PROJECTION);
glPushMatrix();
glLoadIdentity();
glTranslatef(_stereoConfig.GetProjectionCenterOffset(), 0, 0);
gluPerspective(whichCamera.getFieldOfView(), whichCamera.getAspectRatio(),
whichCamera.getNearClip(), whichCamera.getFarClip());
glViewport(leftEyeParams.VP.x, leftEyeParams.VP.y, leftEyeParams.VP.w, leftEyeParams.VP.h);
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glLoadIdentity();
glTranslatef(_stereoConfig.GetIPD() * 0.5f, 0, 0);
Application::getInstance()->displaySide(whichCamera);
// and the right eye to the right side
const StereoEyeParams& rightEyeParams = _stereoConfig.GetEyeRenderParams(StereoEye_Right);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glTranslatef(-_stereoConfig.GetProjectionCenterOffset(), 0, 0);
gluPerspective(whichCamera.getFieldOfView(), whichCamera.getAspectRatio(),
whichCamera.getNearClip(), whichCamera.getFarClip());
glViewport(rightEyeParams.VP.x, rightEyeParams.VP.y, rightEyeParams.VP.w, rightEyeParams.VP.h);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glTranslatef(_stereoConfig.GetIPD() * -0.5f, 0, 0);
Application::getInstance()->displaySide(whichCamera);
glPopMatrix();
// restore our normal viewport
const Viewport& fullViewport = _stereoConfig.GetFullViewport();
glViewport(fullViewport.x, fullViewport.y, fullViewport.w, fullViewport.h);
QOpenGLFramebufferObject* fbo = Application::getInstance()->getGlowEffect()->render(true);
glBindTexture(GL_TEXTURE_2D, fbo->texture());
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluOrtho2D(fullViewport.x, fullViewport.x + fullViewport.w, fullViewport.y, fullViewport.y + fullViewport.h);
glDisable(GL_DEPTH_TEST);
// for reference on setting these values, see SDK file Samples/OculusRoomTiny/RenderTiny_Device.cpp
float scaleFactor = 1.0 / _stereoConfig.GetDistortionScale();
float aspectRatio = _stereoConfig.GetAspect();
glDisable(GL_BLEND);
_program.bind();
_program.setUniformValue(_textureLocation, 0);
const DistortionConfig& distortionConfig = _stereoConfig.GetDistortionConfig();
_program.setUniformValue(_lensCenterLocation, (0.5 + distortionConfig.XCenterOffset * 0.5) * 0.5, 0.5);
_program.setUniformValue(_screenCenterLocation, 0.25, 0.5);
_program.setUniformValue(_scaleLocation, 0.25 * scaleFactor, 0.5 * scaleFactor * aspectRatio);
_program.setUniformValue(_scaleInLocation, 4, 2 / aspectRatio);
_program.setUniformValue(_hmdWarpParamLocation, distortionConfig.K[0], distortionConfig.K[1],
distortionConfig.K[2], distortionConfig.K[3]);
glColor3f(1, 0, 1);
glBegin(GL_QUADS);
glTexCoord2f(0, 0);
glVertex2f(0, 0);
glTexCoord2f(0.5, 0);
glVertex2f(leftEyeParams.VP.w, 0);
glTexCoord2f(0.5, 1);
glVertex2f(leftEyeParams.VP.w, leftEyeParams.VP.h);
glTexCoord2f(0, 1);
glVertex2f(0, leftEyeParams.VP.h);
glEnd();
_program.setUniformValue(_lensCenterLocation, 0.5 + (0.5 - distortionConfig.XCenterOffset * 0.5) * 0.5, 0.5);
_program.setUniformValue(_screenCenterLocation, 0.75, 0.5);
glBegin(GL_QUADS);
glTexCoord2f(0.5, 0);
glVertex2f(leftEyeParams.VP.w, 0);
glTexCoord2f(1, 0);
glVertex2f(fullViewport.w, 0);
glTexCoord2f(1, 1);
glVertex2f(fullViewport.w, leftEyeParams.VP.h);
glTexCoord2f(0.5, 1);
glVertex2f(leftEyeParams.VP.w, leftEyeParams.VP.h);
glEnd();
glEnable(GL_BLEND);
glBindTexture(GL_TEXTURE_2D, 0);
_program.release();
glPopMatrix();
#endif
}
void OculusManager::reset() {
#ifdef HAVE_LIBOVR
_sensorFusion->Reset();
#endif
}
void OculusManager::updateYawOffset() {
#ifdef __APPLE__
#ifdef HAVE_LIBOVR
float yaw, pitch, roll;
_sensorFusion.GetOrientation().GetEulerAngles<Axis_Y, Axis_X, Axis_Z, Rotate_CCW, Handed_R>(&yaw, &pitch, &roll);
_sensorFusion->GetOrientation().GetEulerAngles<Axis_Y, Axis_X, Axis_Z, Rotate_CCW, Handed_R>(&yaw, &pitch, &roll);
_yawOffset = yaw;
#endif
}
void OculusManager::getEulerAngles(float& yaw, float& pitch, float& roll) {
#ifdef __APPLE__
_sensorFusion.GetOrientation().GetEulerAngles<Axis_Y, Axis_X, Axis_Z, Rotate_CCW, Handed_R>(&yaw, &pitch, &roll);
#ifdef HAVE_LIBOVR
_sensorFusion->GetOrientation().GetEulerAngles<Axis_Y, Axis_X, Axis_Z, Rotate_CCW, Handed_R>(&yaw, &pitch, &roll);
// convert each angle to degrees
// remove the yaw offset from the returned yaw

View file

@ -10,26 +10,50 @@
#define __hifi__OculusManager__
#include <iostream>
#ifdef HAVE_LIBOVR
#include <OVR.h>
#endif
using namespace OVR;
#include "renderer/ProgramObject.h"
class Camera;
/// Handles interaction with the Oculus Rift.
class OculusManager {
public:
static void connect();
static bool isConnected() { return _isConnected; }
static void configureCamera(Camera& camera, int screenWidth, int screenHeight);
static void display(Camera& whichCamera);
static void reset();
static void getEulerAngles(float& yaw, float& pitch, float& roll);
static void updateYawOffset();
private:
private:
static ProgramObject _program;
static int _textureLocation;
static int _lensCenterLocation;
static int _screenCenterLocation;
static int _scaleLocation;
static int _scaleInLocation;
static int _hmdWarpParamLocation;
static bool _isConnected;
static Ptr<DeviceManager> _deviceManager;
static Ptr<HMDDevice> _hmdDevice;
static Ptr<SensorDevice> _sensorDevice;
static SensorFusion _sensorFusion;
static float _yawOffset;
#ifdef HAVE_LIBOVR
static OVR::Ptr<OVR::DeviceManager> _deviceManager;
static OVR::Ptr<OVR::HMDDevice> _hmdDevice;
static OVR::Ptr<OVR::SensorDevice> _sensorDevice;
static OVR::SensorFusion* _sensorFusion;
static OVR::Util::Render::StereoConfig _stereoConfig;
#endif
};
#endif /* defined(__hifi__OculusManager__) */

View file

@ -25,7 +25,7 @@ SixenseManager::~SixenseManager() {
#endif
}
void SixenseManager::update() {
void SixenseManager::update(float deltaTime) {
#ifdef HAVE_SIXENSE
if (sixenseGetNumActiveControllers() == 0) {
return;
@ -42,28 +42,28 @@ void SixenseManager::update() {
sixenseControllerData data;
sixenseGetNewestData(i, &data);
// drive avatar with joystick and triggers
if (data.controller_index) {
avatar->setDriveKeys(ROT_LEFT, qMax(0.0f, -data.joystick_x));
avatar->setDriveKeys(ROT_RIGHT, qMax(0.0f, data.joystick_x));
avatar->setDriveKeys(ROT_UP, qMax(0.0f, data.joystick_y));
avatar->setDriveKeys(ROT_DOWN, qMax(0.0f, -data.joystick_y));
avatar->setDriveKeys(UP, data.trigger);
} else {
avatar->setDriveKeys(FWD, qMax(0.0f, data.joystick_y));
avatar->setDriveKeys(BACK, qMax(0.0f, -data.joystick_y));
avatar->setDriveKeys(LEFT, qMax(0.0f, -data.joystick_x));
avatar->setDriveKeys(RIGHT, qMax(0.0f, data.joystick_x));
avatar->setDriveKeys(DOWN, data.trigger);
}
// set palm position and normal based on Hydra position/orientation
// Set palm position and normal based on Hydra position/orientation
PalmData palm(&hand);
palm.setActive(true);
glm::vec3 position(-data.pos[0], data.pos[1], -data.pos[2]);
glm::vec3 position(data.pos[0], data.pos[1], data.pos[2]);
// Compute current velocity from position change
palm.setVelocity((position - palm.getPosition()) / deltaTime);
// Read controller buttons and joystick into the hand
palm.setControllerButtons(data.buttons);
palm.setTrigger(data.trigger);
palm.setJoystick(data.joystick_x, data.joystick_y);
// Adjust for distance between acquisition 'orb' and the user's torso
// (distance to the right of body center, distance below torso, distance behind torso)
const glm::vec3 SPHERE_TO_TORSO(-250.f, -300.f, -300.f);
position = SPHERE_TO_TORSO + position;
palm.setRawPosition(position);
glm::quat rotation(data.rot_quat[3], -data.rot_quat[0], data.rot_quat[1], -data.rot_quat[2]);
// Rotate about controller
rotation = glm::angleAxis(180.0f, 0.f, 1.f, 0.f) * rotation;
const glm::vec3 PALM_VECTOR(0.0f, -1.0f, 0.0f);
palm.setRawNormal(rotation * PALM_VECTOR);

View file

@ -16,7 +16,7 @@ public:
SixenseManager();
~SixenseManager();
void update();
void update(float deltaTime);
};
#endif /* defined(__interface__SixenseManager__) */

View file

@ -57,6 +57,8 @@ void AmbientOcclusionEffect::init() {
_leftBottomLocation = _occlusionProgram->uniformLocation("leftBottom");
_rightTopLocation = _occlusionProgram->uniformLocation("rightTop");
_noiseScaleLocation = _occlusionProgram->uniformLocation("noiseScale");
_texCoordOffsetLocation = _occlusionProgram->uniformLocation("texCoordOffset");
_texCoordScaleLocation = _occlusionProgram->uniformLocation("texCoordScale");
// generate the random rotation texture
glGenTextures(1, &_rotationTextureID);
@ -106,22 +108,25 @@ void AmbientOcclusionEffect::render() {
Application::getInstance()->computeOffAxisFrustum(
left, right, bottom, top, nearVal, farVal, nearClipPlane, farClipPlane);
int viewport[4];
glGetIntegerv(GL_VIEWPORT, viewport);
const int VIEWPORT_X_INDEX = 0;
const int VIEWPORT_WIDTH_INDEX = 2;
QSize widgetSize = Application::getInstance()->getGLWidget()->size();
float sMin = viewport[VIEWPORT_X_INDEX] / (float)widgetSize.width();
float sWidth = viewport[VIEWPORT_WIDTH_INDEX] / (float)widgetSize.width();
_occlusionProgram->bind();
_occlusionProgram->setUniformValue(_nearLocation, nearVal);
_occlusionProgram->setUniformValue(_farLocation, farVal);
_occlusionProgram->setUniformValue(_leftBottomLocation, left, bottom);
_occlusionProgram->setUniformValue(_rightTopLocation, right, top);
QSize widgetSize = Application::getInstance()->getGLWidget()->size();
_occlusionProgram->setUniformValue(_noiseScaleLocation, widgetSize.width() / (float)ROTATION_WIDTH,
_occlusionProgram->setUniformValue(_noiseScaleLocation, viewport[VIEWPORT_WIDTH_INDEX] / (float)ROTATION_WIDTH,
widgetSize.height() / (float)ROTATION_HEIGHT);
_occlusionProgram->setUniformValue(_texCoordOffsetLocation, sMin, 0.0f);
_occlusionProgram->setUniformValue(_texCoordScaleLocation, sWidth, 1.0f);
int viewport[4];
glGetIntegerv(GL_VIEWPORT, viewport);
const int VIEWPORT_X_INDEX = 0;
const int VIEWPORT_WIDTH_INDEX = 2;
float sMin = viewport[VIEWPORT_X_INDEX] / (float)widgetSize.width();
float sMax = (viewport[VIEWPORT_X_INDEX] + viewport[VIEWPORT_WIDTH_INDEX]) / (float)widgetSize.width();
renderFullscreenQuad(sMin, sMax);
renderFullscreenQuad();
_occlusionProgram->release();
@ -141,7 +146,7 @@ void AmbientOcclusionEffect::render() {
_blurProgram->bind();
_blurProgram->setUniformValue(_blurScaleLocation, 1.0f / widgetSize.width(), 1.0f / widgetSize.height());
renderFullscreenQuad(sMin, sMax);
renderFullscreenQuad(sMin, sMin + sWidth);
_blurProgram->release();

View file

@ -30,6 +30,8 @@ private:
int _leftBottomLocation;
int _rightTopLocation;
int _noiseScaleLocation;
int _texCoordOffsetLocation;
int _texCoordScaleLocation;
ProgramObject* _blurProgram;
int _blurScaleLocation;

View file

@ -27,6 +27,7 @@ VoxelStatsDialog::VoxelStatsDialog(QWidget* parent, NodeToVoxelSceneStats* model
for (int i = 0; i < MAX_VOXEL_SERVERS; i++) {
_voxelServerLables[i] = 0;
_extraServerDetails[i] = LESS;
}
for (int i = 0; i < MAX_STATS; i++) {
@ -40,20 +41,13 @@ VoxelStatsDialog::VoxelStatsDialog(QWidget* parent, NodeToVoxelSceneStats* model
this->QDialog::setLayout(_form);
// Setup stat items
_serverVoxels = AddStatItem("Voxels on Servers", GREENISH);
_localVoxels = AddStatItem("Local Voxels", YELLOWISH);
_localVoxelsMemory = AddStatItem("Voxels Memory", GREYISH);
_voxelsRendered = AddStatItem("Voxels Rendered", GREENISH);
_sendingMode = AddStatItem("Sending Mode", YELLOWISH);
/** NOT YET READY
VoxelSceneStats temp;
for (int i = 0; i < VoxelSceneStats::ITEM_COUNT; i++) {
VoxelSceneStats::Item item = (VoxelSceneStats::Item)(i);
VoxelSceneStats::ItemInfo& itemInfo = temp.getItemInfo(item);
AddStatItem(itemInfo.caption, itemInfo.colorRGBA);
}
**/
_serverVoxels = AddStatItem("Voxels on Servers");
_localVoxels = AddStatItem("Local Voxels");
_localVoxelsMemory = AddStatItem("Voxels Memory");
_voxelsRendered = AddStatItem("Voxels Rendered");
_sendingMode = AddStatItem("Sending Mode");
layout()->setSizeConstraint(QLayout::SetFixedSize);
}
void VoxelStatsDialog::RemoveStatItem(int item) {
@ -66,12 +60,34 @@ void VoxelStatsDialog::RemoveStatItem(int item) {
_labels[item] = NULL;
}
void VoxelStatsDialog::moreless(const QString& link) {
QStringList linkDetails = link.split("-");
const int COMMAND_ITEM = 0;
const int SERVER_NUMBER_ITEM = 1;
QString serverNumberString = linkDetails[SERVER_NUMBER_ITEM];
QString command = linkDetails[COMMAND_ITEM];
int serverNumber = serverNumberString.toInt();
if (command == "more") {
_extraServerDetails[serverNumber-1] = MORE;
} else if (command == "most") {
_extraServerDetails[serverNumber-1] = MOST;
} else {
_extraServerDetails[serverNumber-1] = LESS;
}
}
int VoxelStatsDialog::AddStatItem(const char* caption, unsigned colorRGBA) {
char strBuf[64];
const int STATS_LABEL_WIDTH = 900;
const int STATS_LABEL_WIDTH = 600;
_statCount++; // increment our current stat count
if (colorRGBA == 0) {
static unsigned rotatingColors[] = { GREENISH, YELLOWISH, GREYISH };
colorRGBA = rotatingColors[_statCount % (sizeof(rotatingColors)/sizeof(rotatingColors[0]))];
}
QLabel* label = _labels[_statCount] = new QLabel();
// Set foreground color to 62.5% brightness of the meter (otherwise will be hard to read on the bright background)
@ -84,11 +100,9 @@ int VoxelStatsDialog::AddStatItem(const char* caption, unsigned colorRGBA) {
rgb = ((rgb & colorpart1) >> 1) + ((rgb & colorpart2) >> 3);
palette.setColor(QPalette::WindowText, QColor::fromRgb(rgb));
label->setPalette(palette);
label->setFixedWidth(STATS_LABEL_WIDTH);
snprintf(strBuf, sizeof(strBuf), " %s:", caption);
_form->addRow(strBuf, label);
label->setFixedWidth(STATS_LABEL_WIDTH);
return _statCount;
}
@ -204,10 +218,11 @@ void VoxelStatsDialog::paintEvent(QPaintEvent* event) {
showAllVoxelServers();
this->QDialog::paintEvent(event);
//this->setFixedSize(this->width(), this->height());
}
void VoxelStatsDialog::showAllVoxelServers() {
QLocale locale(QLocale::English);
int serverNumber = 0;
int serverCount = 0;
NodeList* nodeList = NodeList::getInstance();
@ -220,11 +235,16 @@ void VoxelStatsDialog::showAllVoxelServers() {
if (serverCount > _voxelServerLabelsCount) {
char label[128] = { 0 };
sprintf(label, "Voxel Server %d",serverCount);
_voxelServerLables[serverCount-1] = AddStatItem(label, GREENISH);
int thisServerRow = _voxelServerLables[serverCount-1] = AddStatItem(label);
_labels[thisServerRow]->setTextFormat(Qt::RichText);
_labels[thisServerRow]->setTextInteractionFlags(Qt::TextBrowserInteraction);
connect(_labels[thisServerRow], SIGNAL(linkActivated(const QString&)), this, SLOT(moreless(const QString&)));
_voxelServerLabelsCount++;
}
std::stringstream serverDetails("");
std::stringstream extraDetails("");
std::stringstream linkDetails("");
if (nodeList->getNodeActiveSocketOrPing(&(*node))) {
serverDetails << "active ";
@ -233,7 +253,6 @@ void VoxelStatsDialog::showAllVoxelServers() {
}
QUuid nodeUUID = node->getUUID();
serverDetails << " " << nodeUUID.toString().toLocal8Bit().constData() << " ";
NodeToJurisdictionMap& voxelServerJurisdictions = Application::getInstance()->getVoxelServerJurisdictions();
@ -265,8 +284,88 @@ void VoxelStatsDialog::showAllVoxelServers() {
} // root code
} // jurisdiction
// now lookup stats details for this server...
if (_extraServerDetails[serverNumber-1] != LESS) {
Application::getInstance()->lockVoxelSceneStats();
NodeToVoxelSceneStats* sceneStats = Application::getInstance()->getVoxelSceneStats();
if (sceneStats->find(nodeUUID) != sceneStats->end()) {
VoxelSceneStats& stats = sceneStats->at(nodeUUID);
switch (_extraServerDetails[serverNumber-1]) {
case MOST: {
extraDetails << "<br/>" ;
const unsigned long USECS_PER_MSEC = 1000;
float lastFullEncode = stats.getLastFullTotalEncodeTime() / USECS_PER_MSEC;
float lastFullSend = stats.getLastFullElapsedTime() / USECS_PER_MSEC;
QString lastFullEncodeString = locale.toString(lastFullEncode);
QString lastFullSendString = locale.toString(lastFullSend);
extraDetails << "<br/>" << "Last Full Scene... " <<
"Encode Time: " << lastFullEncodeString.toLocal8Bit().constData() << " ms " <<
"Send Time: " << lastFullSendString.toLocal8Bit().constData() << " ms ";
for (int i = 0; i < VoxelSceneStats::ITEM_COUNT; i++) {
VoxelSceneStats::Item item = (VoxelSceneStats::Item)(i);
VoxelSceneStats::ItemInfo& itemInfo = stats.getItemInfo(item);
extraDetails << "<br/>" << itemInfo.caption << " " << stats.getItemValue(item);
}
} // fall through... since MOST has all of MORE
case MORE: {
QString totalString = locale.toString((uint)stats.getTotalVoxels());
QString internalString = locale.toString((uint)stats.getTotalInternal());
QString leavesString = locale.toString((uint)stats.getTotalLeaves());
serverDetails << "<br/>" << "Node UUID: " <<
nodeUUID.toString().toLocal8Bit().constData() << " ";
serverDetails << "<br/>" << "Voxels: " <<
totalString.toLocal8Bit().constData() << " total " <<
internalString.toLocal8Bit().constData() << " internal " <<
leavesString.toLocal8Bit().constData() << " leaves ";
QString incomingPacketsString = locale.toString((uint)stats.getIncomingPackets());
QString incomingBytesString = locale.toString((uint)stats.getIncomingBytes());
QString incomingWastedBytesString = locale.toString((uint)stats.getIncomingWastedBytes());
QString incomingOutOfOrderString = locale.toString((uint)stats.getIncomingOutOfOrder());
QString incomingLikelyLostString = locale.toString((uint)stats.getIncomingLikelyLost());
QString incomingFlightTimeString = locale.toString(stats.getIncomingFlightTimeAverage());
serverDetails << "<br/>" << "Incoming Packets: " <<
incomingPacketsString.toLocal8Bit().constData() <<
" Out of Order: " << incomingOutOfOrderString.toLocal8Bit().constData() <<
" Likely Lost: " << incomingLikelyLostString.toLocal8Bit().constData();
serverDetails << "<br/>" <<
" Average Flight Time: " << incomingFlightTimeString.toLocal8Bit().constData() << " msecs";
serverDetails << "<br/>" << "Incoming" <<
" Bytes: " << incomingBytesString.toLocal8Bit().constData() <<
" Wasted Bytes: " << incomingWastedBytesString.toLocal8Bit().constData();
serverDetails << extraDetails.str();
if (_extraServerDetails[serverNumber-1] == MORE) {
linkDetails << " " << " [<a href='most-" << serverNumber << "'>most...</a>]";
linkDetails << " " << " [<a href='less-" << serverNumber << "'>less...</a>]";
} else {
linkDetails << " " << " [<a href='more-" << serverNumber << "'>less...</a>]";
linkDetails << " " << " [<a href='less-" << serverNumber << "'>least...</a>]";
}
} break;
case LESS: {
// nothing
} break;
}
}
Application::getInstance()->unlockVoxelSceneStats();
} else {
linkDetails << " " << " [<a href='more-" << serverNumber << "'>more...</a>]";
linkDetails << " " << " [<a href='most-" << serverNumber << "'>most...</a>]";
}
serverDetails << linkDetails.str();
_labels[_voxelServerLables[serverCount - 1]]->setText(serverDetails.str().c_str());
} // is VOXEL_SERVER
} // Node Loop

View file

@ -17,6 +17,7 @@
#define MAX_STATS 100
#define MAX_VOXEL_SERVERS 50
#define DEFAULT_COLOR 0
class VoxelStatsDialog : public QDialog {
Q_OBJECT
@ -30,6 +31,7 @@ signals:
public slots:
void reject();
void moreless(const QString& link);
protected:
// State <- data model held by BandwidthMeter
@ -38,11 +40,14 @@ protected:
// Emits a 'closed' signal when this dialog is closed.
void closeEvent(QCloseEvent*);
int AddStatItem(const char* caption, unsigned colorRGBA);
int AddStatItem(const char* caption, unsigned colorRGBA = DEFAULT_COLOR);
void RemoveStatItem(int item);
void showAllVoxelServers();
private:
typedef enum { LESS, MORE, MOST } details;
QFormLayout* _form;
QLabel* _labels[MAX_STATS];
NodeToVoxelSceneStats* _model;
@ -55,6 +60,7 @@ private:
int _voxelsRendered;
int _voxelServerLables[MAX_VOXEL_SERVERS];
int _voxelServerLabelsCount;
details _extraServerDetails[MAX_VOXEL_SERVERS];
};
#endif /* defined(__interface__VoxelStatsDialog__) */

View file

@ -33,6 +33,7 @@ AvatarData::AvatarData(Node* owningNode) :
_leaderUUID(),
_handState(0),
_keyState(NO_KEY_DOWN),
_isChatCirclingEnabled(false),
_headData(NULL),
_handData(NULL)
{
@ -120,6 +121,9 @@ int AvatarData::getBroadcastData(unsigned char* destinationBuffer) {
setSemiNibbleAt(bitItems,HAND_STATE_START_BIT,_handState);
// faceshift state
if (_headData->_isFaceshiftConnected) { setAtBit(bitItems, IS_FACESHIFT_CONNECTED); }
if (_isChatCirclingEnabled) {
setAtBit(bitItems, IS_CHAT_CIRCLING_ENABLED);
}
*destinationBuffer++ = bitItems;
// If it is connected, pack up the data
@ -248,6 +252,8 @@ int AvatarData::parseData(unsigned char* sourceBuffer, int numBytes) {
_headData->_isFaceshiftConnected = oneAtBit(bitItems, IS_FACESHIFT_CONNECTED);
_isChatCirclingEnabled = oneAtBit(bitItems, IS_CHAT_CIRCLING_ENABLED);
// If it is connected, pack up the data
if (_headData->_isFaceshiftConnected) {
memcpy(&_headData->_leftEyeBlink, sourceBuffer, sizeof(float));

View file

@ -30,6 +30,7 @@
const int KEY_STATE_START_BIT = 0; // 1st and 2nd bits
const int HAND_STATE_START_BIT = 2; // 3rd and 4th bits
const int IS_FACESHIFT_CONNECTED = 4; // 5th bit
const int IS_CHAT_CIRCLING_ENABLED = 5;
const float MAX_AUDIO_LOUDNESS = 1000.0; // close enough for mouth animation
@ -93,6 +94,8 @@ public:
const std::string& setChatMessage() const { return _chatMessage; }
QString getQStringChatMessage() { return QString(_chatMessage.data()); }
bool isChatCirclingEnabled() const { return _isChatCirclingEnabled; }
const QUuid& getLeaderUUID() const { return _leaderUUID; }
void setHeadData(HeadData* headData) { _headData = headData; }
@ -124,6 +127,8 @@ protected:
// chat message
std::string _chatMessage;
bool _isChatCirclingEnabled;
std::vector<JointData> _joints;
HeadData* _headData;

View file

@ -37,6 +37,8 @@ PalmData& HandData::addNewPalm() {
PalmData::PalmData(HandData* owningHandData) :
_rawPosition(0, 0, 0),
_rawNormal(0, 1, 0),
_velocity(0, 0, 0),
_controllerButtons(0),
_isActive(false),
_leapID(LEAPID_INVALID),
_numFramesWithoutData(0),

View file

@ -41,6 +41,12 @@ enum RaveGloveEffectsMode
NUM_RAVE_GLOVE_EFFECTS_MODES
};
const int BUTTON_1 = 32;
const int BUTTON_2 = 64;
const int BUTTON_3 = 8;
const int BUTTON_4 = 16;
const int BUTTON_FWD = 128;
class HandData {
public:
HandData(AvatarData* owningAvatar);
@ -144,17 +150,34 @@ public:
void setLeapID(int id) { _leapID = id; }
void setRawPosition(const glm::vec3& pos) { _rawPosition = pos; }
void setRawNormal(const glm::vec3& normal) { _rawNormal = normal; }
void setVelocity(const glm::vec3& velocity) { _velocity = velocity; }
const glm::vec3& getVelocity() const { return _velocity; }
void incrementFramesWithoutData() { _numFramesWithoutData++; }
void resetFramesWithoutData() { _numFramesWithoutData = 0; }
int getFramesWithoutData() const { return _numFramesWithoutData; }
// Controller buttons
void setControllerButtons(int controllerButtons) { _controllerButtons = controllerButtons; }
int getControllerButtons() { return _controllerButtons; }
void setTrigger(float trigger) { _trigger = trigger; }
float getTrigger() { return _trigger; }
void setJoystick(float joystickX, float joystickY) { _joystickX = joystickX; _joystickY = joystickY; }
float getJoystickX() { return _joystickX; }
float getJoystickY() { return _joystickY; }
private:
std::vector<FingerData> _fingers;
glm::vec3 _rawPosition;
glm::vec3 _rawNormal;
bool _isActive; // This has current valid data
int _leapID; // the Leap's serial id for this tracked object
glm::vec3 _velocity;
int _controllerButtons;
float _trigger;
float _joystickX, _joystickY;
bool _isActive; // This has current valid data
int _leapID; // the Leap's serial id for this tracked object
int _numFramesWithoutData; // after too many frames without data, this tracked object assumed lost.
HandData* _owningHandData;
};

View file

@ -37,12 +37,15 @@ PACKET_VERSION versionForPacketType(PACKET_TYPE type) {
return 2;
case PACKET_TYPE_VOXEL_QUERY:
return 1;
return 2;
case PACKET_TYPE_SET_VOXEL:
case PACKET_TYPE_SET_VOXEL_DESTRUCTIVE:
case PACKET_TYPE_ERASE_VOXEL:
return 1;
case PACKET_TYPE_VOXEL_DATA:
return 1;
default:
return 0;

View file

@ -20,7 +20,6 @@ const PACKET_TYPE PACKET_TYPE_PING = 'P';
const PACKET_TYPE PACKET_TYPE_PING_REPLY = 'R';
const PACKET_TYPE PACKET_TYPE_KILL_NODE = 'K';
const PACKET_TYPE PACKET_TYPE_HEAD_DATA = 'H';
const PACKET_TYPE PACKET_TYPE_Z_COMMAND = 'Z';
const PACKET_TYPE PACKET_TYPE_INJECT_AUDIO = 'I';
const PACKET_TYPE PACKET_TYPE_MIXED_AUDIO = 'A';
const PACKET_TYPE PACKET_TYPE_MICROPHONE_AUDIO_NO_ECHO = 'M';
@ -41,7 +40,6 @@ const PACKET_TYPE PACKET_TYPE_DATA_SERVER_SEND = 'u';
const PACKET_TYPE PACKET_TYPE_DATA_SERVER_CONFIRM = 'c';
const PACKET_TYPE PACKET_TYPE_VOXEL_QUERY = 'q';
const PACKET_TYPE PACKET_TYPE_VOXEL_DATA = 'V';
const PACKET_TYPE PACKET_TYPE_VOXEL_DATA_MONOCHROME = 'v';
const PACKET_TYPE PACKET_TYPE_VOXEL_STATS = '#';
const PACKET_TYPE PACKET_TYPE_VOXEL_JURISDICTION = 'J';
const PACKET_TYPE PACKET_TYPE_VOXEL_JURISDICTION_REQUEST = 'j';

View file

@ -113,20 +113,20 @@ PerformanceWarning::~PerformanceWarning() {
if ((_alwaysDisplay || _renderWarningsOn) && elapsedmsec > 1) {
if (elapsedmsec > 1000) {
double elapsedsec = (end - _start) / 1000000.0;
qDebug("%s took %lf seconds %s\n", _message, elapsedsec, (_alwaysDisplay ? "" : "WARNING!") );
qDebug("%s took %.2lf seconds %s\n", _message, elapsedsec, (_alwaysDisplay ? "" : "WARNING!") );
} else {
if (_suppressShortTimings) {
if (elapsedmsec > 10) {
qDebug("%s took %lf milliseconds %s\n", _message, elapsedmsec,
qDebug("%s took %.1lf milliseconds %s\n", _message, elapsedmsec,
(_alwaysDisplay || (elapsedmsec < 10) ? "" : "WARNING!"));
}
} else {
qDebug("%s took %lf milliseconds %s\n", _message, elapsedmsec,
qDebug("%s took %.2lf milliseconds %s\n", _message, elapsedmsec,
(_alwaysDisplay || (elapsedmsec < 10) ? "" : "WARNING!"));
}
}
} else if (_alwaysDisplay) {
qDebug("%s took %lf milliseconds\n", _message, elapsedmsec);
qDebug("%s took %.2lf milliseconds\n", _message, elapsedmsec);
}
// if the caller gave us a pointer to store the running total, track it now.
if (_runningTotal) {

View file

@ -60,7 +60,7 @@ bool shouldDo(float desiredInterval, float deltaTime) {
return randFloat() < deltaTime / desiredInterval;
}
void outputBufferBits(unsigned char* buffer, int length, bool withNewLine) {
void outputBufferBits(const unsigned char* buffer, int length, bool withNewLine) {
for (int i = 0; i < length; i++) {
outputBits(buffer[i], false);
}
@ -69,20 +69,20 @@ void outputBufferBits(unsigned char* buffer, int length, bool withNewLine) {
}
}
void outputBits(unsigned char byte, bool withNewLine) {
void outputBits(unsigned char byte, bool withNewLine, bool usePrintf) {
if (isalnum(byte)) {
qDebug("[ %d (%c): ", byte, byte);
usePrintf ? (void)printf("[ %d (%c): ", byte, byte) : qDebug("[ %d (%c): ", byte, byte);
} else {
qDebug("[ %d (0x%x): ", byte, byte);
usePrintf ? (void)printf("[ %d (0x%x): ", byte, byte) : qDebug("[ %d (0x%x): ", byte, byte);
}
for (int i = 0; i < 8; i++) {
qDebug("%d", byte >> (7 - i) & 1);
usePrintf ? (void)printf("%d", byte >> (7 - i) & 1) : qDebug("%d", byte >> (7 - i) & 1);
}
qDebug(" ] ");
usePrintf ? (void)printf(" ] ") : qDebug(" ] ");
if (withNewLine) {
qDebug("\n");
usePrintf ? (void)printf("\n") : qDebug("\n");
}
}

View file

@ -52,8 +52,8 @@ bool randomBoolean();
bool shouldDo(float desiredInterval, float deltaTime);
void outputBufferBits(unsigned char* buffer, int length, bool withNewLine = true);
void outputBits(unsigned char byte, bool withNewLine = true);
void outputBufferBits(const unsigned char* buffer, int length, bool withNewLine = true);
void outputBits(unsigned char byte, bool withNewLine = true, bool usePrintf = false);
void printVoxelCode(unsigned char* voxelCode);
int numberOfOnes(unsigned char byte);
bool oneAtBit(unsigned char byte, int bitIndex);

View file

@ -16,25 +16,27 @@
VoxelNodeData::VoxelNodeData(Node* owningNode) :
VoxelQuery(owningNode),
_viewSent(false),
_voxelPacketAvailableBytes(MAX_VOXEL_PACKET_SIZE),
_voxelPacketAvailableBytes(MAX_PACKET_SIZE),
_maxSearchLevel(1),
_maxLevelReachedInLastSearch(1),
_lastTimeBagEmpty(0),
_viewFrustumChanging(false),
_viewFrustumJustStoppedChanging(true),
_currentPacketIsColor(true),
_currentPacketIsCompressed(false),
_voxelSendThread(NULL),
_lastClientBoundaryLevelAdjust(0),
_lastClientVoxelSizeScale(DEFAULT_VOXEL_SIZE_SCALE),
_lodChanged(false),
_lodInitialized(false)
{
_voxelPacket = new unsigned char[MAX_VOXEL_PACKET_SIZE];
_voxelPacket = new unsigned char[MAX_PACKET_SIZE];
_voxelPacketAt = _voxelPacket;
_lastVoxelPacket = new unsigned char[MAX_VOXEL_PACKET_SIZE];
_lastVoxelPacket = new unsigned char[MAX_PACKET_SIZE];
_lastVoxelPacketLength = 0;
_duplicatePacketCount = 0;
resetVoxelPacket();
_sequenceNumber = 0;
resetVoxelPacket(true); // don't bump sequence
}
void VoxelNodeData::initializeVoxelSendThread(VoxelServer* voxelServer) {
@ -45,8 +47,11 @@ void VoxelNodeData::initializeVoxelSendThread(VoxelServer* voxelServer) {
}
bool VoxelNodeData::packetIsDuplicate() const {
// since our packets now include header information, like sequence number, and createTime, we can't just do a memcmp
// of the entire packet, we need to compare only the packet content...
if (_lastVoxelPacketLength == getPacketLength()) {
return memcmp(_lastVoxelPacket, _voxelPacket, getPacketLength()) == 0;
return memcmp(_lastVoxelPacket + VOXEL_PACKET_HEADER_SIZE,
_voxelPacket+VOXEL_PACKET_HEADER_SIZE , getPacketLength() - VOXEL_PACKET_HEADER_SIZE) == 0;
}
return false;
}
@ -71,7 +76,7 @@ bool VoxelNodeData::shouldSuppressDuplicatePacket() {
if (sinceFirstSuppressedPacket < MAX_TIME_BETWEEN_DUPLICATE_PACKETS) {
// Finally, if we know we've sent at least one duplicate out, then suppress the rest...
if (_duplicatePacketCount > 1) {
if (_duplicatePacketCount >= 1) {
shouldSuppress = true;
}
} else {
@ -85,7 +90,7 @@ bool VoxelNodeData::shouldSuppressDuplicatePacket() {
return shouldSuppress;
}
void VoxelNodeData::resetVoxelPacket() {
void VoxelNodeData::resetVoxelPacket(bool lastWasSurpressed) {
// Whenever we call this, we will keep a copy of the last packet, so we can determine if the last packet has
// changed since we last reset it. Since we know that no two packets can ever be identical without being the same
// scene information, (e.g. the root node packet of a static scene), we can use this as a strategy for reducing
@ -95,20 +100,60 @@ void VoxelNodeData::resetVoxelPacket() {
// If we're moving, and the client asked for low res, then we force monochrome, otherwise, use
// the clients requested color state.
_currentPacketIsColor = (LOW_RES_MONO && getWantLowResMoving() && _viewFrustumChanging) ? false : getWantColor();
PACKET_TYPE voxelPacketType = _currentPacketIsColor ? PACKET_TYPE_VOXEL_DATA : PACKET_TYPE_VOXEL_DATA_MONOCHROME;
_currentPacketIsColor = getWantColor();
_currentPacketIsCompressed = getWantCompression();
VOXEL_PACKET_FLAGS flags = 0;
if (_currentPacketIsColor) {
setAtBit(flags,PACKET_IS_COLOR_BIT);
}
if (_currentPacketIsCompressed) {
setAtBit(flags,PACKET_IS_COMPRESSED_BIT);
}
int numBytesPacketHeader = populateTypeAndVersion(_voxelPacket, voxelPacketType);
_voxelPacketAvailableBytes = MAX_PACKET_SIZE;
int numBytesPacketHeader = populateTypeAndVersion(_voxelPacket, PACKET_TYPE_VOXEL_DATA);
_voxelPacketAt = _voxelPacket + numBytesPacketHeader;
_voxelPacketAvailableBytes = MAX_VOXEL_PACKET_SIZE - numBytesPacketHeader;
_voxelPacketAvailableBytes -= numBytesPacketHeader;
// pack in flags
VOXEL_PACKET_FLAGS* flagsAt = (VOXEL_PACKET_FLAGS*)_voxelPacketAt;
*flagsAt = flags;
_voxelPacketAt += sizeof(VOXEL_PACKET_FLAGS);
_voxelPacketAvailableBytes -= sizeof(VOXEL_PACKET_FLAGS);
// pack in sequence number
VOXEL_PACKET_SEQUENCE* sequenceAt = (VOXEL_PACKET_SEQUENCE*)_voxelPacketAt;
*sequenceAt = _sequenceNumber;
_voxelPacketAt += sizeof(VOXEL_PACKET_SEQUENCE);
_voxelPacketAvailableBytes -= sizeof(VOXEL_PACKET_SEQUENCE);
if (!(lastWasSurpressed || _lastVoxelPacketLength == VOXEL_PACKET_HEADER_SIZE)) {
_sequenceNumber++;
}
// pack in timestamp
VOXEL_PACKET_SENT_TIME now = usecTimestampNow();
VOXEL_PACKET_SENT_TIME* timeAt = (VOXEL_PACKET_SENT_TIME*)_voxelPacketAt;
*timeAt = now;
_voxelPacketAt += sizeof(VOXEL_PACKET_SENT_TIME);
_voxelPacketAvailableBytes -= sizeof(VOXEL_PACKET_SENT_TIME);
_voxelPacketWaiting = false;
}
void VoxelNodeData::writeToPacket(unsigned char* buffer, int bytes) {
memcpy(_voxelPacketAt, buffer, bytes);
_voxelPacketAvailableBytes -= bytes;
_voxelPacketAt += bytes;
_voxelPacketWaiting = true;
void VoxelNodeData::writeToPacket(const unsigned char* buffer, int bytes) {
// compressed packets include lead bytes which contain compressed size, this allows packing of
// multiple compressed portions together
if (_currentPacketIsCompressed) {
*(VOXEL_PACKET_INTERNAL_SECTION_SIZE*)_voxelPacketAt = bytes;
_voxelPacketAt += sizeof(VOXEL_PACKET_INTERNAL_SECTION_SIZE);
_voxelPacketAvailableBytes -= sizeof(VOXEL_PACKET_INTERNAL_SECTION_SIZE);
}
if (bytes <= _voxelPacketAvailableBytes) {
memcpy(_voxelPacketAt, buffer, bytes);
_voxelPacketAvailableBytes -= bytes;
_voxelPacketAt += bytes;
_voxelPacketWaiting = true;
}
}
VoxelNodeData::~VoxelNodeData() {
@ -180,7 +225,6 @@ void VoxelNodeData::setViewSent(bool viewSent) {
}
}
void VoxelNodeData::updateLastKnownViewFrustum() {
bool frustumChanges = !_lastKnownViewFrustum.isVerySimilar(_currentViewFrustum);

View file

@ -11,6 +11,7 @@
#include <iostream>
#include <NodeData.h>
#include <VoxelPacketData.h>
#include <VoxelQuery.h>
#include <CoverageMap.h>
@ -26,48 +27,52 @@ public:
VoxelNodeData(Node* owningNode);
virtual ~VoxelNodeData();
void resetVoxelPacket(); // resets voxel packet to after "V" header
void resetVoxelPacket(bool lastWasSurpressed = false); // resets voxel packet to after "V" header
void writeToPacket(unsigned char* buffer, int bytes); // writes to end of packet
void writeToPacket(const unsigned char* buffer, int bytes); // writes to end of packet
const unsigned char* getPacket() const { return _voxelPacket; }
int getPacketLength() const { return (MAX_VOXEL_PACKET_SIZE - _voxelPacketAvailableBytes); }
int getPacketLength() const { return (MAX_PACKET_SIZE - _voxelPacketAvailableBytes); }
bool isPacketWaiting() const { return _voxelPacketWaiting; }
bool packetIsDuplicate() const;
bool shouldSuppressDuplicatePacket();
int getAvailable() const { return _voxelPacketAvailableBytes; }
int getMaxSearchLevel() const { return _maxSearchLevel; };
void resetMaxSearchLevel() { _maxSearchLevel = 1; };
void incrementMaxSearchLevel() { _maxSearchLevel++; };
int getMaxSearchLevel() const { return _maxSearchLevel; }
void resetMaxSearchLevel() { _maxSearchLevel = 1; }
void incrementMaxSearchLevel() { _maxSearchLevel++; }
int getMaxLevelReached() const { return _maxLevelReachedInLastSearch; };
int getMaxLevelReached() const { return _maxLevelReachedInLastSearch; }
void setMaxLevelReached(int maxLevelReached) { _maxLevelReachedInLastSearch = maxLevelReached; }
VoxelNodeBag nodeBag;
CoverageMap map;
ViewFrustum& getCurrentViewFrustum() { return _currentViewFrustum; };
ViewFrustum& getLastKnownViewFrustum() { return _lastKnownViewFrustum; };
ViewFrustum& getCurrentViewFrustum() { return _currentViewFrustum; }
ViewFrustum& getLastKnownViewFrustum() { return _lastKnownViewFrustum; }
// These are not classic setters because they are calculating and maintaining state
// which is set asynchronously through the network receive
bool updateCurrentViewFrustum();
void updateLastKnownViewFrustum();
bool getViewSent() const { return _viewSent; };
bool getViewSent() const { return _viewSent; }
void setViewSent(bool viewSent);
bool getViewFrustumChanging() const { return _viewFrustumChanging; };
bool getViewFrustumJustStoppedChanging() const { return _viewFrustumJustStoppedChanging; };
bool getViewFrustumChanging() const { return _viewFrustumChanging; }
bool getViewFrustumJustStoppedChanging() const { return _viewFrustumJustStoppedChanging; }
bool moveShouldDump() const;
uint64_t getLastTimeBagEmpty() const { return _lastTimeBagEmpty; };
void setLastTimeBagEmpty(uint64_t lastTimeBagEmpty) { _lastTimeBagEmpty = lastTimeBagEmpty; };
uint64_t getLastTimeBagEmpty() const { return _lastTimeBagEmpty; }
void setLastTimeBagEmpty(uint64_t lastTimeBagEmpty) { _lastTimeBagEmpty = lastTimeBagEmpty; }
bool getCurrentPacketIsColor() const { return _currentPacketIsColor; };
bool getCurrentPacketIsColor() const { return _currentPacketIsColor; }
bool getCurrentPacketIsCompressed() const { return _currentPacketIsCompressed; }
bool getCurrentPacketFormatMatches() {
return (getCurrentPacketIsColor() == getWantColor() && getCurrentPacketIsCompressed() == getWantCompression());
}
bool hasLodChanged() const { return _lodChanged; };
@ -101,6 +106,7 @@ private:
bool _viewFrustumChanging;
bool _viewFrustumJustStoppedChanging;
bool _currentPacketIsColor;
bool _currentPacketIsCompressed;
VoxelSendThread* _voxelSendThread;
@ -109,6 +115,8 @@ private:
float _lastClientVoxelSizeScale;
bool _lodChanged;
bool _lodInitialized;
VOXEL_PACKET_SEQUENCE _sequenceNumber;
};
#endif /* defined(__hifi__VoxelNodeData__) */

View file

@ -8,19 +8,29 @@
// Threaded or non-threaded voxel packet sender
//
#include <NodeList.h>
#include <SharedUtil.h>
#include <PacketHeaders.h>
#include <EnvironmentData.h>
#include <NodeList.h>
#include <PacketHeaders.h>
#include <PerfStat.h>
#include <SharedUtil.h>
extern EnvironmentData environmentData[3];
#include "VoxelSendThread.h"
#include "VoxelServer.h"
#include "VoxelServerConsts.h"
uint64_t startSceneSleepTime = 0;
uint64_t endSceneSleepTime = 0;
VoxelSendThread::VoxelSendThread(const QUuid& nodeUUID, VoxelServer* myServer) :
_nodeUUID(nodeUUID),
_myServer(myServer) {
_myServer(myServer),
_packetData()
{
}
bool VoxelSendThread::process() {
@ -44,7 +54,7 @@ bool VoxelSendThread::process() {
// Sometimes the node data has not yet been linked, in which case we can't really do anything
if (nodeData) {
bool viewFrustumChanged = nodeData->updateCurrentViewFrustum();
if (_myServer->wantsDebugVoxelSending()) {
if (_myServer->wantsDebugVoxelSending() && _myServer->wantsVerboseDebug()) {
printf("nodeData->updateCurrentViewFrustum() changed=%s\n", debug::valueOf(viewFrustumChanged));
}
packetsSent = deepestLevelVoxelDistributor(node, nodeData, viewFrustumChanged);
@ -54,7 +64,7 @@ bool VoxelSendThread::process() {
}
}
} else {
if (_myServer->wantsDebugVoxelSending()) {
if (_myServer->wantsDebugVoxelSending() && _myServer->wantsVerboseDebug()) {
qDebug("VoxelSendThread::process() waiting for isInitialLoadComplete()\n");
}
}
@ -66,9 +76,10 @@ bool VoxelSendThread::process() {
int usecToSleep = VOXEL_SEND_INTERVAL_USECS - elapsed;
if (usecToSleep > 0) {
PerformanceWarning warn(false,"VoxelSendThread... usleep()",false,&_usleepTime,&_usleepCalls);
usleep(usecToSleep);
} else {
if (_myServer->wantsDebugVoxelSending()) {
if (_myServer->wantsDebugVoxelSending() && _myServer->wantsVerboseDebug()) {
std::cout << "Last send took too much time, not sleeping!\n";
}
}
@ -77,17 +88,34 @@ bool VoxelSendThread::process() {
return isStillRunning(); // keep running till they terminate us
}
uint64_t VoxelSendThread::_usleepTime = 0;
uint64_t VoxelSendThread::_usleepCalls = 0;
uint64_t VoxelSendThread::_totalBytes = 0;
uint64_t VoxelSendThread::_totalWastedBytes = 0;
uint64_t VoxelSendThread::_totalPackets = 0;
int VoxelSendThread::handlePacketSend(Node* node, VoxelNodeData* nodeData, int& trueBytesSent, int& truePacketsSent) {
bool debug = _myServer->wantsDebugVoxelSending();
uint64_t now = usecTimestampNow();
bool packetSent = false; // did we send a packet?
int packetsSent = 0;
// Here's where we check to see if this packet is a duplicate of the last packet. If it is, we will silently
// obscure the packet and not send it. This allows the callers and upper level logic to not need to know about
// this rate control savings.
if (nodeData->shouldSuppressDuplicatePacket()) {
nodeData->resetVoxelPacket(); // we still need to reset it though!
nodeData->resetVoxelPacket(true); // we still need to reset it though!
return packetsSent; // without sending...
}
const unsigned char* messageData = nodeData->getPacket();
int numBytesPacketHeader = numBytesForPacketHeader(messageData);
const unsigned char* dataAt = messageData + numBytesPacketHeader;
dataAt += sizeof(VOXEL_PACKET_FLAGS);
VOXEL_PACKET_SEQUENCE sequence = (*(VOXEL_PACKET_SEQUENCE*)dataAt);
dataAt += sizeof(VOXEL_PACKET_SEQUENCE);
// If we've got a stats message ready to send, then see if we can piggyback them together
if (nodeData->stats.isReadyToSend()) {
@ -102,6 +130,20 @@ int VoxelSendThread::handlePacketSend(Node* node, VoxelNodeData* nodeData, int&
memcpy(statsMessage + statsMessageLength, nodeData->getPacket(), nodeData->getPacketLength());
statsMessageLength += nodeData->getPacketLength();
// since a stats message is only included on end of scene, don't consider any of these bytes "wasted", since
// there was nothing else to send.
int thisWastedBytes = 0;
_totalWastedBytes += thisWastedBytes;
_totalBytes += nodeData->getPacketLength();
_totalPackets++;
if (debug) {
qDebug("Adding stats to packet at %llu [%llu]: sequence: %d size:%d [%llu] wasted bytes:%d [%llu]\n",
now,
_totalPackets,
sequence, nodeData->getPacketLength(), _totalBytes,
thisWastedBytes, _totalWastedBytes);
}
// actually send it
NodeList::getInstance()->getNodeSocket().writeDatagram((char*) statsMessage, statsMessageLength,
node->getActiveSocket()->getAddress(),
@ -127,11 +169,14 @@ int VoxelSendThread::handlePacketSend(Node* node, VoxelNodeData* nodeData, int&
node->getActiveSocket()->getPort());
}
// remember to track our stats
nodeData->stats.packetSent(nodeData->getPacketLength());
trueBytesSent += nodeData->getPacketLength();
truePacketsSent++;
packetsSent++;
nodeData->resetVoxelPacket();
if (packetSent) {
nodeData->stats.packetSent(nodeData->getPacketLength());
trueBytesSent += nodeData->getPacketLength();
truePacketsSent++;
packetsSent++;
nodeData->resetVoxelPacket();
}
return packetsSent;
}
@ -143,7 +188,6 @@ int VoxelSendThread::deepestLevelVoxelDistributor(Node* node, VoxelNodeData* nod
int packetsSentThisInterval = 0;
bool somethingToSend = true; // assume we have something
// FOR NOW... node tells us if it wants to receive only view frustum deltas
bool wantDelta = viewFrustumChanged && nodeData->getWantDelta();
@ -151,37 +195,51 @@ int VoxelSendThread::deepestLevelVoxelDistributor(Node* node, VoxelNodeData* nod
// If we're starting a fresh packet, then...
// If we're moving, and the client asked for low res, then we force monochrome, otherwise, use
// the clients requested color state.
bool wantColor = LOW_RES_MONO && nodeData->getWantLowResMoving() && viewFrustumChanged ? false : nodeData->getWantColor();
bool wantColor = nodeData->getWantColor();
bool wantCompression = nodeData->getWantCompression();
// If we have a packet waiting, and our desired want color, doesn't match the current waiting packets color
// then let's just send that waiting packet.
if (wantColor != nodeData->getCurrentPacketIsColor()) {
if (!nodeData->getCurrentPacketFormatMatches()) {
if (nodeData->isPacketWaiting()) {
if (_myServer->wantsDebugVoxelSending()) {
printf("wantColor=%s --- SENDING PARTIAL PACKET! nodeData->getCurrentPacketIsColor()=%s\n",
debug::valueOf(wantColor), debug::valueOf(nodeData->getCurrentPacketIsColor()));
if (_myServer->wantsDebugVoxelSending() && _myServer->wantsVerboseDebug()) {
printf("wantColor=%s wantCompression=%s SENDING PARTIAL PACKET! currentPacketIsColor=%s currentPacketIsCompressed=%s\n",
debug::valueOf(wantColor), debug::valueOf(wantCompression),
debug::valueOf(nodeData->getCurrentPacketIsColor()),
debug::valueOf(nodeData->getCurrentPacketIsCompressed()) );
}
packetsSentThisInterval += handlePacketSend(node, nodeData, trueBytesSent, truePacketsSent);
} else {
if (_myServer->wantsDebugVoxelSending()) {
printf("wantColor=%s --- FIXING HEADER! nodeData->getCurrentPacketIsColor()=%s\n",
debug::valueOf(wantColor), debug::valueOf(nodeData->getCurrentPacketIsColor()));
if (_myServer->wantsDebugVoxelSending() && _myServer->wantsVerboseDebug()) {
printf("wantColor=%s wantCompression=%s FIXING HEADER! currentPacketIsColor=%s currentPacketIsCompressed=%s\n",
debug::valueOf(wantColor), debug::valueOf(wantCompression),
debug::valueOf(nodeData->getCurrentPacketIsColor()),
debug::valueOf(nodeData->getCurrentPacketIsCompressed()) );
}
nodeData->resetVoxelPacket();
}
int targetSize = MAX_VOXEL_PACKET_DATA_SIZE;
if (wantCompression) {
targetSize = nodeData->getAvailable() - sizeof(VOXEL_PACKET_INTERNAL_SECTION_SIZE);
}
if (_myServer->wantsDebugVoxelSending() && _myServer->wantsVerboseDebug()) {
printf("line:%d _packetData.changeSettings() wantCompression=%s targetSize=%d\n",__LINE__,
debug::valueOf(wantCompression), targetSize);
}
_packetData.changeSettings(wantCompression, targetSize);
}
if (_myServer->wantsDebugVoxelSending()) {
printf("wantColor=%s getCurrentPacketIsColor()=%s, viewFrustumChanged=%s, getWantLowResMoving()=%s\n",
debug::valueOf(wantColor), debug::valueOf(nodeData->getCurrentPacketIsColor()),
debug::valueOf(viewFrustumChanged), debug::valueOf(nodeData->getWantLowResMoving()));
if (_myServer->wantsDebugVoxelSending() && _myServer->wantsVerboseDebug()) {
printf("wantColor/isColor=%s/%s wantCompression/isCompressed=%s/%s viewFrustumChanged=%s, getWantLowResMoving()=%s\n",
debug::valueOf(wantColor), debug::valueOf(nodeData->getCurrentPacketIsColor()),
debug::valueOf(wantCompression), debug::valueOf(nodeData->getCurrentPacketIsCompressed()),
debug::valueOf(viewFrustumChanged), debug::valueOf(nodeData->getWantLowResMoving()));
}
const ViewFrustum* lastViewFrustum = wantDelta ? &nodeData->getLastKnownViewFrustum() : NULL;
if (_myServer->wantsDebugVoxelSending()) {
if (_myServer->wantsDebugVoxelSending() && _myServer->wantsVerboseDebug()) {
printf("deepestLevelVoxelDistributor() viewFrustumChanged=%s, nodeBag.isEmpty=%s, viewSent=%s\n",
debug::valueOf(viewFrustumChanged), debug::valueOf(nodeData->nodeBag.isEmpty()),
debug::valueOf(nodeData->getViewSent())
@ -192,7 +250,7 @@ int VoxelSendThread::deepestLevelVoxelDistributor(Node* node, VoxelNodeData* nod
// the current view frustum for things to send.
if (viewFrustumChanged || nodeData->nodeBag.isEmpty()) {
uint64_t now = usecTimestampNow();
if (_myServer->wantsDebugVoxelSending()) {
if (_myServer->wantsDebugVoxelSending() && _myServer->wantsVerboseDebug()) {
printf("(viewFrustumChanged=%s || nodeData->nodeBag.isEmpty() =%s)...\n",
debug::valueOf(viewFrustumChanged), debug::valueOf(nodeData->nodeBag.isEmpty()));
if (nodeData->getLastTimeBagEmpty() > 0) {
@ -221,8 +279,20 @@ int VoxelSendThread::deepestLevelVoxelDistributor(Node* node, VoxelNodeData* nod
uint64_t now = usecTimestampNow();
nodeData->setLastTimeBagEmpty(now);
}
// track completed scenes and send out the stats packet accordingly
nodeData->stats.sceneCompleted();
::endSceneSleepTime = _usleepTime;
unsigned long sleepTime = ::endSceneSleepTime - ::startSceneSleepTime;
unsigned long encodeTime = nodeData->stats.getTotalEncodeTime();
unsigned long elapsedTime = nodeData->stats.getElapsedTime();
packetsSentThisInterval += handlePacketSend(node, nodeData, trueBytesSent, truePacketsSent);
if (_myServer->wantsDebugVoxelSending()) {
qDebug("Scene completed at %llu encodeTime:%lu sleepTime:%lu elapsed:%lu Packets:%llu Bytes:%llu Wasted:%llu\n",
usecTimestampNow(), encodeTime, sleepTime, elapsedTime, _totalPackets, _totalBytes, _totalWastedBytes);
}
if (_myServer->wantDisplayVoxelStats()) {
nodeData->stats.printDebugDetails();
@ -236,7 +306,15 @@ int VoxelSendThread::deepestLevelVoxelDistributor(Node* node, VoxelNodeData* nod
if (isFullScene) {
nodeData->nodeBag.deleteAll();
}
nodeData->stats.sceneStarted(isFullScene, viewFrustumChanged, _myServer->getServerTree().rootNode, _myServer->getJurisdiction());
if (_myServer->wantsDebugVoxelSending()) {
qDebug("Scene started at %llu Packets:%llu Bytes:%llu Wasted:%llu\n",
usecTimestampNow(),_totalPackets,_totalBytes,_totalWastedBytes);
}
::startSceneSleepTime = _usleepTime;
nodeData->stats.sceneStarted(isFullScene, viewFrustumChanged,
_myServer->getServerTree().rootNode, _myServer->getJurisdiction());
// This is the start of "resending" the scene.
bool dontRestartSceneOnMove = false; // this is experimental
@ -253,40 +331,29 @@ int VoxelSendThread::deepestLevelVoxelDistributor(Node* node, VoxelNodeData* nod
if (!nodeData->nodeBag.isEmpty()) {
int bytesWritten = 0;
uint64_t start = usecTimestampNow();
uint64_t startCompressTimeMsecs = VoxelPacketData::getCompressContentTime() / 1000;
uint64_t startCompressCalls = VoxelPacketData::getCompressContentCalls();
bool shouldSendEnvironments = _myServer->wantSendEnvironments() && shouldDo(ENVIRONMENT_SEND_INTERVAL_USECS, VOXEL_SEND_INTERVAL_USECS);
int clientMaxPacketsPerInterval = std::max(1,(nodeData->getMaxVoxelPacketsPerSecond() / INTERVALS_PER_SECOND));
int maxPacketsPerInterval = std::min(clientMaxPacketsPerInterval, _myServer->getPacketsPerClientPerInterval());
if (_myServer->wantsDebugVoxelSending()) {
if (_myServer->wantsDebugVoxelSending() && _myServer->wantsVerboseDebug()) {
printf("truePacketsSent=%d packetsSentThisInterval=%d maxPacketsPerInterval=%d server PPI=%d nodePPS=%d nodePPI=%d\n",
truePacketsSent, packetsSentThisInterval, maxPacketsPerInterval, _myServer->getPacketsPerClientPerInterval(),
nodeData->getMaxVoxelPacketsPerSecond(), clientMaxPacketsPerInterval);
}
int extraPackingAttempts = 0;
while (somethingToSend && packetsSentThisInterval < maxPacketsPerInterval - (shouldSendEnvironments ? 1 : 0)) {
if (_myServer->wantsDebugVoxelSending()) {
if (_myServer->wantsDebugVoxelSending() && _myServer->wantsVerboseDebug()) {
printf("truePacketsSent=%d packetsSentThisInterval=%d maxPacketsPerInterval=%d server PPI=%d nodePPS=%d nodePPI=%d\n",
truePacketsSent, packetsSentThisInterval, maxPacketsPerInterval, _myServer->getPacketsPerClientPerInterval(),
nodeData->getMaxVoxelPacketsPerSecond(), clientMaxPacketsPerInterval);
}
// Check to see if we're taking too long, and if so bail early...
uint64_t now = usecTimestampNow();
long elapsedUsec = (now - start);
long elapsedUsecPerPacket = (truePacketsSent == 0) ? 0 : (elapsedUsec / truePacketsSent);
long usecRemaining = (VOXEL_SEND_INTERVAL_USECS - elapsedUsec);
if (elapsedUsecPerPacket + SENDING_TIME_TO_SPARE > usecRemaining) {
if (_myServer->wantsDebugVoxelSending()) {
printf("packetLoop() usecRemaining=%ld bailing early took %ld usecs to generate %d bytes in %d packets (%ld usec avg), %d nodes still to send\n",
usecRemaining, elapsedUsec, trueBytesSent, truePacketsSent, elapsedUsecPerPacket,
nodeData->nodeBag.count());
}
break;
}
bool lastNodeDidntFit = false; // assume each node fits
if (!nodeData->nodeBag.isEmpty()) {
VoxelNode* subTree = nodeData->nodeBag.extract();
bool wantOcclusionCulling = nodeData->getWantOcclusionCulling();
@ -311,23 +378,94 @@ int VoxelSendThread::deepestLevelVoxelDistributor(Node* node, VoxelNodeData* nod
_myServer->getServerTree().lockForRead();
nodeData->stats.encodeStarted();
bytesWritten = _myServer->getServerTree().encodeTreeBitstream(subTree, _tempOutputBuffer, MAX_VOXEL_PACKET_SIZE - 1,
nodeData->nodeBag, params);
bytesWritten = _myServer->getServerTree().encodeTreeBitstream(subTree, &_packetData, nodeData->nodeBag, params);
// if we're trying to fill a full size packet, then we use this logic to determine if we have a DIDNT_FIT case.
if (_packetData.getTargetSize() == MAX_VOXEL_PACKET_DATA_SIZE) {
if (_packetData.hasContent() && bytesWritten == 0 &&
params.stopReason == EncodeBitstreamParams::DIDNT_FIT) {
lastNodeDidntFit = true;
}
} else {
// in compressed mode and we are trying to pack more... and we don't care if the _packetData has
// content or not... because in this case even if we were unable to pack any data, we want to drop
// below to our sendNow logic, but we do want to track that we attempted to pack extra
extraPackingAttempts++;
if (bytesWritten == 0 && params.stopReason == EncodeBitstreamParams::DIDNT_FIT) {
lastNodeDidntFit = true;
}
}
nodeData->stats.encodeStopped();
_myServer->getServerTree().unlock();
if (nodeData->getAvailable() >= bytesWritten) {
nodeData->writeToPacket(_tempOutputBuffer, bytesWritten);
} else {
packetsSentThisInterval += handlePacketSend(node, nodeData, trueBytesSent, truePacketsSent);
nodeData->writeToPacket(_tempOutputBuffer, bytesWritten);
}
} else {
if (nodeData->isPacketWaiting()) {
packetsSentThisInterval += handlePacketSend(node, nodeData, trueBytesSent, truePacketsSent);
// If the bag was empty then we didn't even attempt to encode, and so we know the bytesWritten were 0
bytesWritten = 0;
somethingToSend = false; // this will cause us to drop out of the loop...
}
// If the last node didn't fit, but we're in compressed mode, then we actually want to see if we can fit a
// little bit more in this packet. To do this we
// We only consider sending anything if there is something in the _packetData to send... But
// if bytesWritten == 0 it means either the subTree couldn't fit or we had an empty bag... Both cases
// mean we should send the previous packet contents and reset it.
if (lastNodeDidntFit) {
if (_packetData.hasContent()) {
// if for some reason the finalized size is greater than our available size, then probably the "compressed"
// form actually inflated beyond our padding, and in this case we will send the current packet, then
// write to out new packet...
int writtenSize = _packetData.getFinalizedSize()
+ (nodeData->getCurrentPacketIsCompressed() ? sizeof(VOXEL_PACKET_INTERNAL_SECTION_SIZE) : 0);
if (writtenSize > nodeData->getAvailable()) {
if (_myServer->wantsDebugVoxelSending() && _myServer->wantsVerboseDebug()) {
printf("writtenSize[%d] > available[%d] too big, sending packet as is.\n",
writtenSize, nodeData->getAvailable());
}
packetsSentThisInterval += handlePacketSend(node, nodeData, trueBytesSent, truePacketsSent);
}
if (_myServer->wantsDebugVoxelSending() && _myServer->wantsVerboseDebug()) {
printf("calling writeToPacket() available=%d compressedSize=%d uncompressedSize=%d target=%d\n",
nodeData->getAvailable(), _packetData.getFinalizedSize(),
_packetData.getUncompressedSize(), _packetData.getTargetSize());
}
nodeData->writeToPacket(_packetData.getFinalizedData(), _packetData.getFinalizedSize());
extraPackingAttempts = 0;
}
//packetsSentThisInterval = _myServer->getPacketsPerClientPerInterval(); // done for now, no nodes left
somethingToSend = false;
// If we're not running compressed, the we know we can just send now. Or if we're running compressed, but
// the packet doesn't have enough space to bother attempting to pack more...
bool sendNow = true;
if (nodeData->getCurrentPacketIsCompressed() &&
nodeData->getAvailable() >= MINIMUM_ATTEMPT_MORE_PACKING &&
extraPackingAttempts <= REASONABLE_NUMBER_OF_PACKING_ATTEMPTS) {
sendNow = false; // try to pack more
}
int targetSize = MAX_VOXEL_PACKET_DATA_SIZE;
if (sendNow) {
packetsSentThisInterval += handlePacketSend(node, nodeData, trueBytesSent, truePacketsSent);
if (wantCompression) {
targetSize = nodeData->getAvailable() - sizeof(VOXEL_PACKET_INTERNAL_SECTION_SIZE);
}
} else {
// If we're in compressed mode, then we want to see if we have room for more in this wire packet.
// but we've finalized the _packetData, so we want to start a new section, we will do that by
// resetting the packet settings with the max uncompressed size of our current available space
// in the wire packet. We also include room for our section header, and a little bit of padding
// to account for the fact that whenc compressing small amounts of data, we sometimes end up with
// a larger compressed size then uncompressed size
targetSize = nodeData->getAvailable() - sizeof(VOXEL_PACKET_INTERNAL_SECTION_SIZE) - COMPRESS_PADDING;
}
if (_myServer->wantsDebugVoxelSending() && _myServer->wantsVerboseDebug()) {
printf("line:%d _packetData.changeSettings() wantCompression=%s targetSize=%d\n",__LINE__,
debug::valueOf(nodeData->getWantCompression()), targetSize);
}
_packetData.changeSettings(nodeData->getWantCompression(), targetSize); // will do reset
}
}
@ -351,18 +489,26 @@ int VoxelSendThread::deepestLevelVoxelDistributor(Node* node, VoxelNodeData* nod
uint64_t end = usecTimestampNow();
int elapsedmsec = (end - start)/1000;
uint64_t endCompressCalls = VoxelPacketData::getCompressContentCalls();
int elapsedCompressCalls = endCompressCalls - startCompressCalls;
uint64_t endCompressTimeMsecs = VoxelPacketData::getCompressContentTime() / 1000;
int elapsedCompressTimeMsecs = endCompressTimeMsecs - startCompressTimeMsecs;
if (elapsedmsec > 100) {
if (elapsedmsec > 1000) {
int elapsedsec = (end - start)/1000000;
printf("WARNING! packetLoop() took %d seconds to generate %d bytes in %d packets %d nodes still to send\n",
elapsedsec, trueBytesSent, truePacketsSent, nodeData->nodeBag.count());
printf("WARNING! packetLoop() took %d seconds [%d milliseconds %d calls in compress] to generate %d bytes in %d packets %d nodes still to send\n",
elapsedsec, elapsedCompressTimeMsecs, elapsedCompressCalls, trueBytesSent, truePacketsSent, nodeData->nodeBag.count());
} else {
printf("WARNING! packetLoop() took %d milliseconds to generate %d bytes in %d packets, %d nodes still to send\n",
elapsedmsec, trueBytesSent, truePacketsSent, nodeData->nodeBag.count());
printf("WARNING! packetLoop() took %d milliseconds [%d milliseconds %d calls in compress] to generate %d bytes in %d packets, %d nodes still to send\n",
elapsedmsec, elapsedCompressTimeMsecs, elapsedCompressCalls, trueBytesSent, truePacketsSent, nodeData->nodeBag.count());
}
} else if (_myServer->wantsDebugVoxelSending()) {
printf("packetLoop() took %d milliseconds to generate %d bytes in %d packets, %d nodes still to send\n",
elapsedmsec, trueBytesSent, truePacketsSent, nodeData->nodeBag.count());
} else if (_myServer->wantsDebugVoxelSending() && _myServer->wantsVerboseDebug()) {
printf("packetLoop() took %d milliseconds [%d milliseconds %d calls in compress] to generate %d bytes in %d packets, %d nodes still to send\n",
elapsedmsec, elapsedCompressTimeMsecs, elapsedCompressCalls, trueBytesSent, truePacketsSent, nodeData->nodeBag.count());
}
// if after sending packets we've emptied our bag, then we want to remember that we've sent all
@ -370,13 +516,13 @@ int VoxelSendThread::deepestLevelVoxelDistributor(Node* node, VoxelNodeData* nod
if (nodeData->nodeBag.isEmpty()) {
nodeData->updateLastKnownViewFrustum();
nodeData->setViewSent(true);
if (_myServer->wantsDebugVoxelSending()) {
if (_myServer->wantsDebugVoxelSending() && _myServer->wantsVerboseDebug()) {
nodeData->map.printStats();
}
nodeData->map.erase(); // It would be nice if we could save this, and only reset it when the view frustum changes
}
if (_myServer->wantsDebugVoxelSending()) {
if (_myServer->wantsDebugVoxelSending() && _myServer->wantsVerboseDebug()) {
printf("truePacketsSent=%d packetsSentThisInterval=%d maxPacketsPerInterval=%d server PPI=%d nodePPS=%d nodePPI=%d\n",
truePacketsSent, packetsSentThisInterval, maxPacketsPerInterval, _myServer->getPacketsPerClientPerInterval(),
nodeData->getMaxVoxelPacketsPerSecond(), clientMaxPacketsPerInterval);

View file

@ -22,6 +22,14 @@
class VoxelSendThread : public virtual GenericThread {
public:
VoxelSendThread(const QUuid& nodeUUID, VoxelServer* myServer);
static uint64_t _totalBytes;
static uint64_t _totalWastedBytes;
static uint64_t _totalPackets;
static uint64_t _usleepTime;
static uint64_t _usleepCalls;
protected:
/// Implements generic processing behavior for this thread.
virtual bool process();
@ -33,7 +41,8 @@ private:
int handlePacketSend(Node* node, VoxelNodeData* nodeData, int& trueBytesSent, int& truePacketsSent);
int deepestLevelVoxelDistributor(Node* node, VoxelNodeData* nodeData, bool viewFrustumChanged);
unsigned char _tempOutputBuffer[MAX_VOXEL_PACKET_SIZE];
unsigned char _tempOutputBuffer[MAX_VOXEL_PACKET_SIZE]; // used by environment sending code
VoxelPacketData _packetData;
};
#endif // __voxel_server__VoxelSendThread__

Some files were not shown because too many files have changed in this diff Show more