mirror of
https://github.com/Armored-Dragon/overte.git
synced 2025-03-11 16:13:16 +01:00
Merge remote-tracking branch 'upstream/master' into cleanup
This commit is contained in:
commit
3277cb49a6
110 changed files with 3988 additions and 1232 deletions
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
1
interface/external/LibOVR/Include/OVR.h
vendored
1
interface/external/LibOVR/Include/OVR.h
vendored
|
@ -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"
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
BIN
interface/external/LibOVR/Lib/MacOS/libovr.a
vendored
BIN
interface/external/LibOVR/Lib/MacOS/libovr.a
vendored
Binary file not shown.
BIN
interface/external/LibOVR/Lib/UNIX/libovr.a
vendored
Normal file
BIN
interface/external/LibOVR/Lib/UNIX/libovr.a
vendored
Normal file
Binary file not shown.
BIN
interface/external/LibOVR/Lib/Win32/libovr.lib
vendored
BIN
interface/external/LibOVR/Lib/Win32/libovr.lib
vendored
Binary file not shown.
BIN
interface/external/LibOVR/Lib/x64/libovr64.lib
vendored
BIN
interface/external/LibOVR/Lib/x64/libovr64.lib
vendored
Binary file not shown.
0
interface/external/LibOVR/Src/Kernel/OVR_Alg.h
vendored
Executable file → Normal file
0
interface/external/LibOVR/Src/Kernel/OVR_Alg.h
vendored
Executable file → Normal file
0
interface/external/LibOVR/Src/Kernel/OVR_Allocator.h
vendored
Executable file → Normal file
0
interface/external/LibOVR/Src/Kernel/OVR_Allocator.h
vendored
Executable file → Normal file
0
interface/external/LibOVR/Src/Kernel/OVR_Array.h
vendored
Executable file → Normal file
0
interface/external/LibOVR/Src/Kernel/OVR_Array.h
vendored
Executable file → Normal file
1
interface/external/LibOVR/Src/Kernel/OVR_Atomic.h
vendored
Executable file → Normal file
1
interface/external/LibOVR/Src/Kernel/OVR_Atomic.h
vendored
Executable file → Normal 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
0
interface/external/LibOVR/Src/Kernel/OVR_Color.h
vendored
Executable file → Normal file
0
interface/external/LibOVR/Src/Kernel/OVR_ContainerAllocator.h
vendored
Executable file → Normal file
0
interface/external/LibOVR/Src/Kernel/OVR_ContainerAllocator.h
vendored
Executable file → Normal file
0
interface/external/LibOVR/Src/Kernel/OVR_File.h
vendored
Executable file → Normal file
0
interface/external/LibOVR/Src/Kernel/OVR_File.h
vendored
Executable file → Normal file
0
interface/external/LibOVR/Src/Kernel/OVR_Hash.h
vendored
Executable file → Normal file
0
interface/external/LibOVR/Src/Kernel/OVR_Hash.h
vendored
Executable file → Normal file
0
interface/external/LibOVR/Src/Kernel/OVR_KeyCodes.h
vendored
Executable file → Normal file
0
interface/external/LibOVR/Src/Kernel/OVR_KeyCodes.h
vendored
Executable file → Normal file
2
interface/external/LibOVR/Src/Kernel/OVR_List.h
vendored
Executable file → Normal file
2
interface/external/LibOVR/Src/Kernel/OVR_List.h
vendored
Executable file → Normal 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
0
interface/external/LibOVR/Src/Kernel/OVR_Log.h
vendored
Executable file → Normal file
596
interface/external/LibOVR/Src/Kernel/OVR_Math.h
vendored
Executable file → Normal file
596
interface/external/LibOVR/Src/Kernel/OVR_Math.h
vendored
Executable file → Normal 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
0
interface/external/LibOVR/Src/Kernel/OVR_RefCount.h
vendored
Executable file → Normal file
0
interface/external/LibOVR/Src/Kernel/OVR_Std.h
vendored
Executable file → Normal file
0
interface/external/LibOVR/Src/Kernel/OVR_Std.h
vendored
Executable file → Normal file
0
interface/external/LibOVR/Src/Kernel/OVR_String.h
vendored
Executable file → Normal file
0
interface/external/LibOVR/Src/Kernel/OVR_String.h
vendored
Executable file → Normal file
0
interface/external/LibOVR/Src/Kernel/OVR_StringHash.h
vendored
Executable file → Normal file
0
interface/external/LibOVR/Src/Kernel/OVR_StringHash.h
vendored
Executable file → Normal file
2
interface/external/LibOVR/Src/Kernel/OVR_SysFile.h
vendored
Executable file → Normal file
2
interface/external/LibOVR/Src/Kernel/OVR_SysFile.h
vendored
Executable file → Normal 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
0
interface/external/LibOVR/Src/Kernel/OVR_System.h
vendored
Executable file → Normal file
0
interface/external/LibOVR/Src/Kernel/OVR_Threads.h
vendored
Executable file → Normal file
0
interface/external/LibOVR/Src/Kernel/OVR_Threads.h
vendored
Executable file → Normal file
2
interface/external/LibOVR/Src/Kernel/OVR_Timer.h
vendored
Executable file → Normal file
2
interface/external/LibOVR/Src/Kernel/OVR_Timer.h
vendored
Executable file → Normal 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
2
interface/external/LibOVR/Src/Kernel/OVR_Types.h
vendored
Executable file → Normal 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
0
interface/external/LibOVR/Src/Kernel/OVR_UTF8Util.h
vendored
Executable file → Normal file
101
interface/external/LibOVR/Src/OVR_Device.h
vendored
Executable file → Normal file
101
interface/external/LibOVR/Src/OVR_Device.h
vendored
Executable file → Normal 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
1
interface/external/LibOVR/Src/OVR_DeviceConstants.h
vendored
Executable file → Normal 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
15
interface/external/LibOVR/Src/OVR_DeviceHandle.h
vendored
Executable file → Normal 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
57
interface/external/LibOVR/Src/OVR_DeviceImpl.h
vendored
Executable file → Normal 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
3
interface/external/LibOVR/Src/OVR_DeviceMessages.h
vendored
Executable file → Normal 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
4
interface/external/LibOVR/Src/OVR_HIDDevice.h
vendored
Executable file → Normal 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
0
interface/external/LibOVR/Src/OVR_HIDDeviceBase.h
vendored
Executable file → Normal file
50
interface/external/LibOVR/Src/OVR_HIDDeviceImpl.h
vendored
Executable file → Normal file
50
interface/external/LibOVR/Src/OVR_HIDDeviceImpl.h
vendored
Executable file → Normal 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
143
interface/external/LibOVR/Src/OVR_JSON.h
vendored
Normal 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
22
interface/external/LibOVR/Src/OVR_LatencyTestImpl.h
vendored
Executable file → Normal 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
|
||||
|
|
111
interface/external/LibOVR/Src/OVR_Linux_DeviceManager.h
vendored
Normal file
111
interface/external/LibOVR/Src/OVR_Linux_DeviceManager.h
vendored
Normal 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
|
124
interface/external/LibOVR/Src/OVR_Linux_HIDDevice.h
vendored
Normal file
124
interface/external/LibOVR/Src/OVR_Linux_HIDDevice.h
vendored
Normal 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
|
156
interface/external/LibOVR/Src/OVR_Linux_HMDDevice.h
vendored
Normal file
156
interface/external/LibOVR/Src/OVR_Linux_HMDDevice.h
vendored
Normal 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
12
interface/external/LibOVR/Src/OVR_OSX_DeviceManager.h
vendored
Executable file → Normal 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
9
interface/external/LibOVR/Src/OVR_OSX_HIDDevice.h
vendored
Executable file → Normal 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
26
interface/external/LibOVR/Src/OVR_OSX_HMDDevice.h
vendored
Executable file → Normal 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;
|
||||
};
|
||||
|
||||
|
||||
|
|
243
interface/external/LibOVR/Src/OVR_Profile.h
vendored
Normal file
243
interface/external/LibOVR/Src/OVR_Profile.h
vendored
Normal 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
203
interface/external/LibOVR/Src/OVR_SensorFilter.h
vendored
Executable file → Normal 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
267
interface/external/LibOVR/Src/OVR_SensorFusion.h
vendored
Executable file → Normal 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
76
interface/external/LibOVR/Src/OVR_SensorImpl.h
vendored
Executable file → Normal 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
0
interface/external/LibOVR/Src/OVR_ThreadCommandQueue.h
vendored
Executable file → Normal file
15
interface/external/LibOVR/Src/OVR_Win32_DeviceManager.h
vendored
Executable file → Normal file
15
interface/external/LibOVR/Src/OVR_Win32_DeviceManager.h
vendored
Executable file → Normal 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
2
interface/external/LibOVR/Src/OVR_Win32_DeviceStatus.h
vendored
Executable file → Normal 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
17
interface/external/LibOVR/Src/OVR_Win32_HIDDevice.h
vendored
Executable file → Normal 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
31
interface/external/LibOVR/Src/OVR_Win32_HMDDevice.h
vendored
Executable file → Normal 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
0
interface/external/LibOVR/Src/OVR_Win32_SensorDevice.h
vendored
Executable file → Normal file
3
interface/external/LibOVR/Src/Util/Util_LatencyTest.h
vendored
Executable file → Normal file
3
interface/external/LibOVR/Src/Util/Util_LatencyTest.h
vendored
Executable file → Normal 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; }
|
||||
|
||||
|
|
|
@ -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
3
interface/external/LibOVR/Src/Util/Util_Render_Stereo.h
vendored
Executable file → Normal 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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
36
interface/resources/shaders/oculus.frag
Normal file
36
interface/resources/shaders/oculus.frag
Normal 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);
|
||||
}
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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());
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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++) {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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__) */
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -16,7 +16,7 @@ public:
|
|||
SixenseManager();
|
||||
~SixenseManager();
|
||||
|
||||
void update();
|
||||
void update(float deltaTime);
|
||||
};
|
||||
|
||||
#endif /* defined(__interface__SixenseManager__) */
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -30,6 +30,8 @@ private:
|
|||
int _leftBottomLocation;
|
||||
int _rightTopLocation;
|
||||
int _noiseScaleLocation;
|
||||
int _texCoordOffsetLocation;
|
||||
int _texCoordScaleLocation;
|
||||
|
||||
ProgramObject* _blurProgram;
|
||||
int _blurScaleLocation;
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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__) */
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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';
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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__) */
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
Loading…
Reference in a new issue