mirror of
https://github.com/overte-org/overte.git
synced 2025-04-20 11:45:36 +02:00
Merge branch 'master' of https://github.com/highfidelity/hifi into replace_qnetworkaccessmanager
This commit is contained in:
commit
6b88724578
20 changed files with 1120 additions and 76 deletions
|
@ -10,6 +10,7 @@ add_definitions(-DGLM_FORCE_RADIANS)
|
|||
if (WIN32)
|
||||
add_definitions(-DNOMINMAX -D_CRT_SECURE_NO_WARNINGS)
|
||||
set(CMAKE_PREFIX_PATH ${CMAKE_PREFIX_PATH} "C:\\Program Files\\Microsoft SDKs\\Windows\\v7.1 ")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /MP")
|
||||
elseif (CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX)
|
||||
#SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wno-long-long -pedantic")
|
||||
#SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wno-unknown-pragmas")
|
||||
|
|
5
interface/external/oculus/readme.txt
vendored
5
interface/external/oculus/readme.txt
vendored
|
@ -10,4 +10,7 @@ You can download the Oculus SDK from https://developer.oculusvr.com/ (account cr
|
|||
You may optionally choose to copy the SDK folders to a location outside the repository (so you can re-use with different checkouts and different projects).
|
||||
If so our CMake find module expects you to set the ENV variable 'HIFI_LIB_DIR' to a directory containing a subfolder 'oculus' that contains the three folders mentioned above.
|
||||
|
||||
2. Clear your build directory, run cmake and build, and you should be all set.
|
||||
NOTE: For Windows users, you should copy libovr.lib and libovrd.lib from the \oculus\Lib\Win32\VS2010 directory to the \oculus\Lib\Win32\ directory.
|
||||
|
||||
2. Clear your build directory, run cmake and build, and you should be all set.
|
||||
|
||||
|
|
|
@ -11,18 +11,114 @@
|
|||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
// the texture containing our permutations and normals
|
||||
uniform sampler2D permutationNormalTexture;
|
||||
// implementation based on Ken Perlin's Improved Noise reference implementation (orig. in Java) at
|
||||
// http://mrl.nyu.edu/~perlin/noise/
|
||||
|
||||
uniform sampler2D permutationTexture;
|
||||
|
||||
// the noise frequency
|
||||
const float frequency = 65536.0; // looks better with current TREE_SCALE, was 1024 when TREE_SCALE was either 512 or 128
|
||||
const float frequency = 256.0;
|
||||
//const float frequency = 65536.0; // looks better with current TREE_SCALE, was 1024 when TREE_SCALE was either 512 or 128
|
||||
|
||||
// the noise amplitude
|
||||
const float amplitude = 0.1;
|
||||
const float amplitude = 0.5;
|
||||
|
||||
// the position in model space
|
||||
varying vec3 position;
|
||||
|
||||
// gradient based on gradients from cube edge centers rather than random from texture lookup
|
||||
float randomEdgeGrad(int hash, vec3 position){
|
||||
int h = int(mod(hash, 16));
|
||||
float u = h < 8 ? position.x : position.y;
|
||||
float v = h < 4 ? position.y : h == 12 || h == 14 ? position.x : position.z;
|
||||
bool even = mod(hash, 2) == 0;
|
||||
bool four = mod(hash, 4) == 0;
|
||||
return (even ? u : -u) + (four ? v : -v);
|
||||
}
|
||||
|
||||
// still have the option to lookup based on texture
|
||||
float randomTextureGrad(int hash, vec3 position){
|
||||
float u = float(hash) / 256.0;
|
||||
vec3 g = -1 + 2 * texture2D(permutationTexture, vec2(u, 0.75)).rgb;
|
||||
return dot(position, g);
|
||||
}
|
||||
|
||||
float improvedGrad(int hash, vec3 position){
|
||||
// Untested whether texture lookup is faster than math, uncomment one line or the other to try out
|
||||
// cube edge gradients versus random spherical gradients sent in texture.
|
||||
|
||||
// return randomTextureGrad(hash, position);
|
||||
return randomEdgeGrad(hash, position);
|
||||
}
|
||||
|
||||
// 5th order fade function to remove 2nd order discontinuties
|
||||
vec3 fade3(vec3 t){
|
||||
return t * t * t * (t * (t * 6 - 15) + 10);
|
||||
}
|
||||
|
||||
int permutation(int index){
|
||||
float u = float(index) / 256.0;
|
||||
float t = texture2D(permutationTexture, vec2(u, 0.25)).r;
|
||||
return int(t * 256);
|
||||
}
|
||||
|
||||
float improvedNoise(vec3 position){
|
||||
int X = int(mod(floor(position.x), 256));
|
||||
int Y = int(mod(floor(position.y), 256));
|
||||
int Z = int(mod(floor(position.z), 256));
|
||||
|
||||
vec3 fracs = fract(position);
|
||||
|
||||
vec3 fades = fade3(fracs);
|
||||
|
||||
int A = permutation(X + 0) + Y;
|
||||
int AA = permutation(A + 0) + Z;
|
||||
int AB = permutation(A + 1) + Z;
|
||||
int B = permutation(X + 1) + Y;
|
||||
int BA = permutation(B + 0) + Z;
|
||||
int BB = permutation(B + 1) + Z;
|
||||
|
||||
float gradAA0 = improvedGrad(permutation(AA + 0), vec3(fracs.x , fracs.y , fracs.z ));
|
||||
float gradBA0 = improvedGrad(permutation(BA + 0), vec3(fracs.x - 1, fracs.y , fracs.z ));
|
||||
float gradAB0 = improvedGrad(permutation(AB + 0), vec3(fracs.x , fracs.y - 1, fracs.z ));
|
||||
float gradBB0 = improvedGrad(permutation(BB + 0), vec3(fracs.x - 1, fracs.y - 1, fracs.z ));
|
||||
float gradAA1 = improvedGrad(permutation(AA + 1), vec3(fracs.x , fracs.y , fracs.z - 1));
|
||||
float gradBA1 = improvedGrad(permutation(BA + 1), vec3(fracs.x - 1, fracs.y , fracs.z - 1));
|
||||
float gradAB1 = improvedGrad(permutation(AB + 1), vec3(fracs.x , fracs.y - 1, fracs.z - 1));
|
||||
float gradBB1 = improvedGrad(permutation(BB + 1), vec3(fracs.x - 1, fracs.y - 1, fracs.z - 1));
|
||||
|
||||
return mix(mix(mix(gradAA0, gradBA0, fades.x), mix(gradAB0, gradBB0, fades.x), fades.y), mix(mix(gradAA1, gradBA1, fades.x), mix(gradAB1, gradBB1, fades.x), fades.y), fades.z);
|
||||
}
|
||||
|
||||
float turbulence(vec3 position, float power){
|
||||
return (1.0f / power) * improvedNoise(power * position);
|
||||
}
|
||||
|
||||
float turb(vec3 position){
|
||||
return turbulence(position, 1)
|
||||
+ turbulence(position, 2),
|
||||
+ turbulence(position, 4)
|
||||
+ turbulence(position, 8)
|
||||
+ turbulence(position, 16)
|
||||
+ turbulence(position, 32)
|
||||
+ turbulence(position, 64)
|
||||
+ turbulence(position, 128)
|
||||
;
|
||||
}
|
||||
|
||||
|
||||
void main(void) {
|
||||
|
||||
// get noise in range 0 .. 1
|
||||
float noise = clamp(0.5f + amplitude * turb(position * frequency), 0, 1);
|
||||
|
||||
// apply vertex lighting
|
||||
vec3 color = gl_Color.rgb * vec3(noise, noise, noise);
|
||||
gl_FragColor = vec4(color, 1);
|
||||
}
|
||||
|
||||
|
||||
/* old implementation
|
||||
// returns the gradient at a single corner of our sampling cube
|
||||
vec3 grad(vec3 location) {
|
||||
float p1 = texture2D(permutationNormalTexture, vec2(location.x / 256.0, 0.25)).r;
|
||||
|
@ -60,7 +156,4 @@ float perlin(vec3 location) {
|
|||
mix(mix(ffcv, cfcv, params.x), mix(fccv, cccv, params.x), params.y),
|
||||
params.z);
|
||||
}
|
||||
|
||||
void main(void) {
|
||||
gl_FragColor = vec4(gl_Color.rgb * (1.0 + amplitude*(perlin(position * frequency) - 1.0)), 1.0);
|
||||
}
|
||||
*/
|
|
@ -399,18 +399,19 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) :
|
|||
}
|
||||
|
||||
Application::~Application() {
|
||||
qInstallMessageHandler(NULL);
|
||||
|
||||
saveSettings();
|
||||
storeSizeAndPosition();
|
||||
saveScripts();
|
||||
|
||||
int DELAY_TIME = 1000;
|
||||
UserActivityLogger::getInstance().close(DELAY_TIME);
|
||||
|
||||
qInstallMessageHandler(NULL);
|
||||
|
||||
// make sure we don't call the idle timer any more
|
||||
delete idleTimer;
|
||||
|
||||
|
||||
_sharedVoxelSystem.changeTree(new VoxelTree);
|
||||
|
||||
saveSettings();
|
||||
|
||||
delete _voxelImporter;
|
||||
|
||||
// let the avatar mixer know we're out
|
||||
|
@ -433,8 +434,6 @@ Application::~Application() {
|
|||
_particleEditSender.terminate();
|
||||
_modelEditSender.terminate();
|
||||
|
||||
storeSizeAndPosition();
|
||||
saveScripts();
|
||||
|
||||
VoxelTreeElement::removeDeleteHook(&_voxels); // we don't need to do this processing on shutdown
|
||||
Menu::getInstance()->deleteLater();
|
||||
|
@ -589,13 +588,17 @@ void Application::paintGL() {
|
|||
//Note, the camera distance is set in Camera::setMode() so we dont have to do it here.
|
||||
_myCamera.setTightness(0.0f); // Camera is directly connected to head without smoothing
|
||||
_myCamera.setTargetPosition(_myAvatar->getUprightHeadPosition());
|
||||
_myCamera.setTargetRotation(_myAvatar->getWorldAlignedOrientation());
|
||||
if (OculusManager::isConnected()) {
|
||||
_myCamera.setTargetRotation(_myAvatar->getWorldAlignedOrientation());
|
||||
} else {
|
||||
_myCamera.setTargetRotation(_myAvatar->getHead()->getOrientation());
|
||||
}
|
||||
|
||||
} else if (_myCamera.getMode() == CAMERA_MODE_MIRROR) {
|
||||
_myCamera.setTightness(0.0f);
|
||||
_myCamera.setDistance(MIRROR_FULLSCREEN_DISTANCE * _scaleMirror);
|
||||
_myCamera.setTargetRotation(_myAvatar->getWorldAlignedOrientation() * glm::quat(glm::vec3(0.0f, PI + _rotateMirror, 0.0f)));
|
||||
_myCamera.setTargetPosition(_myAvatar->getHead()->calculateAverageEyePosition());
|
||||
_myCamera.setTargetPosition(_myAvatar->getHead()->calculateAverageEyePosition() + glm::vec3(0, _raiseMirror * _myAvatar->getScale(), 0));
|
||||
}
|
||||
|
||||
// Update camera position
|
||||
|
@ -633,6 +636,10 @@ void Application::paintGL() {
|
|||
//If we aren't using the glow shader, we have to clear the color and depth buffer
|
||||
if (!glowEnabled) {
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
} else if (OculusManager::isConnected()) {
|
||||
//Clear the color buffer to ensure that there isnt any residual color
|
||||
//Left over from when OR was not connected.
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
}
|
||||
|
||||
if (OculusManager::isConnected()) {
|
||||
|
@ -644,13 +651,8 @@ void Application::paintGL() {
|
|||
}
|
||||
|
||||
} else if (TV3DManager::isConnected()) {
|
||||
if (glowEnabled) {
|
||||
_glowEffect.prepare();
|
||||
}
|
||||
|
||||
TV3DManager::display(whichCamera);
|
||||
if (glowEnabled) {
|
||||
_glowEffect.render();
|
||||
}
|
||||
|
||||
} else {
|
||||
if (glowEnabled) {
|
||||
|
@ -1138,7 +1140,7 @@ void Application::mouseMoveEvent(QMouseEvent* event) {
|
|||
|
||||
_lastMouseMove = usecTimestampNow();
|
||||
|
||||
if (_mouseHidden && showMouse && !OculusManager::isConnected()) {
|
||||
if (_mouseHidden && showMouse && !OculusManager::isConnected() && !TV3DManager::isConnected()) {
|
||||
getGLWidget()->setCursor(Qt::ArrowCursor);
|
||||
_mouseHidden = false;
|
||||
_seenMouseMove = true;
|
||||
|
|
|
@ -31,8 +31,6 @@ static const QString IS_TRUNCATED_NAME = "IsTruncated";
|
|||
static const QString CONTAINER_NAME = "Contents";
|
||||
static const QString KEY_NAME = "Key";
|
||||
|
||||
static const int SCRIPT_PATH = Qt::UserRole;
|
||||
|
||||
ScriptItem::ScriptItem(const QString& filename, const QString& fullPath) :
|
||||
_filename(filename),
|
||||
_fullPath(fullPath) {
|
||||
|
|
|
@ -266,7 +266,7 @@ void OculusManager::display(const glm::quat &bodyOrientation, const glm::vec3 &p
|
|||
|
||||
ApplicationOverlay& applicationOverlay = Application::getInstance()->getApplicationOverlay();
|
||||
|
||||
// We only need to render the overlays to a texture once, then we just render the texture as a quad
|
||||
// We only need to render the overlays to a texture once, then we just render the texture on the hemisphere
|
||||
// PrioVR will only work if renderOverlay is called, calibration is connected to Application::renderingOverlay()
|
||||
applicationOverlay.renderOverlay(true);
|
||||
const bool displayOverlays = Menu::getInstance()->isOptionChecked(MenuOption::DisplayOculusOverlays);
|
||||
|
|
|
@ -93,6 +93,18 @@ void TV3DManager::display(Camera& whichCamera) {
|
|||
int portalW = Application::getInstance()->getGLWidget()->width() / 2;
|
||||
int portalH = Application::getInstance()->getGLWidget()->height();
|
||||
|
||||
const bool glowEnabled = Menu::getInstance()->isOptionChecked(MenuOption::EnableGlowEffect);
|
||||
|
||||
ApplicationOverlay& applicationOverlay = Application::getInstance()->getApplicationOverlay();
|
||||
|
||||
// We only need to render the overlays to a texture once, then we just render the texture as a quad
|
||||
// PrioVR will only work if renderOverlay is called, calibration is connected to Application::renderingOverlay()
|
||||
applicationOverlay.renderOverlay(true);
|
||||
|
||||
if (glowEnabled) {
|
||||
Application::getInstance()->getGlowEffect()->prepare();
|
||||
}
|
||||
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
|
||||
glEnable(GL_SCISSOR_TEST);
|
||||
|
@ -102,13 +114,21 @@ void TV3DManager::display(Camera& whichCamera) {
|
|||
|
||||
glPushMatrix();
|
||||
{
|
||||
|
||||
glMatrixMode(GL_PROJECTION);
|
||||
glLoadIdentity(); // reset projection matrix
|
||||
glFrustum(_leftEye.left, _leftEye.right, _leftEye.bottom, _leftEye.top, nearZ, farZ); // set left view frustum
|
||||
GLfloat p[4][4];
|
||||
glGetFloatv(GL_PROJECTION_MATRIX, &(p[0][0]));
|
||||
GLfloat cotangent = p[1][1];
|
||||
GLfloat fov = atan(1.0f / cotangent);
|
||||
glTranslatef(_leftEye.modelTranslation, 0.0, 0.0); // translate to cancel parallax
|
||||
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
glLoadIdentity();
|
||||
Application::getInstance()->displaySide(whichCamera);
|
||||
|
||||
applicationOverlay.displayOverlayTexture3DTV(whichCamera, _aspect, fov);
|
||||
}
|
||||
glPopMatrix();
|
||||
glDisable(GL_SCISSOR_TEST);
|
||||
|
@ -124,14 +144,25 @@ void TV3DManager::display(Camera& whichCamera) {
|
|||
glMatrixMode(GL_PROJECTION);
|
||||
glLoadIdentity(); // reset projection matrix
|
||||
glFrustum(_rightEye.left, _rightEye.right, _rightEye.bottom, _rightEye.top, nearZ, farZ); // set left view frustum
|
||||
GLfloat p[4][4];
|
||||
glGetFloatv(GL_PROJECTION_MATRIX, &(p[0][0]));
|
||||
GLfloat cotangent = p[1][1];
|
||||
GLfloat fov = atan(1.0f / cotangent);
|
||||
glTranslatef(_rightEye.modelTranslation, 0.0, 0.0); // translate to cancel parallax
|
||||
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
glLoadIdentity();
|
||||
Application::getInstance()->displaySide(whichCamera);
|
||||
|
||||
applicationOverlay.displayOverlayTexture3DTV(whichCamera, _aspect, fov);
|
||||
}
|
||||
glPopMatrix();
|
||||
glDisable(GL_SCISSOR_TEST);
|
||||
|
||||
// reset the viewport to how we started
|
||||
glViewport(0, 0, Application::getInstance()->getGLWidget()->width(), Application::getInstance()->getGLWidget()->height());
|
||||
|
||||
if (glowEnabled) {
|
||||
Application::getInstance()->getGlowEffect()->render();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,6 +11,8 @@
|
|||
|
||||
#include <glm/gtx/norm.hpp>
|
||||
|
||||
#include <AngularConstraint.h>
|
||||
//#include <GeometryUtil.h>
|
||||
#include <SharedUtil.h>
|
||||
|
||||
#include "JointState.h"
|
||||
|
@ -18,7 +20,48 @@
|
|||
JointState::JointState() :
|
||||
_animationPriority(0.0f),
|
||||
_fbxJoint(NULL),
|
||||
_isConstrained(false) {
|
||||
_constraint(NULL) {
|
||||
}
|
||||
|
||||
JointState::JointState(const JointState& other) : _constraint(NULL) {
|
||||
_transform = other._transform;
|
||||
_rotation = other._rotation;
|
||||
_rotationInParentFrame = other._rotationInParentFrame;
|
||||
_animationPriority = other._animationPriority;
|
||||
_fbxJoint = other._fbxJoint;
|
||||
// DO NOT copy _constraint
|
||||
}
|
||||
|
||||
JointState::~JointState() {
|
||||
delete _constraint;
|
||||
_constraint = NULL;
|
||||
if (_constraint) {
|
||||
delete _constraint;
|
||||
_constraint = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void JointState::setFBXJoint(const FBXJoint* joint) {
|
||||
assert(joint != NULL);
|
||||
_rotationInParentFrame = joint->rotation;
|
||||
// NOTE: JointState does not own the FBXJoint to which it points.
|
||||
_fbxJoint = joint;
|
||||
if (_constraint) {
|
||||
delete _constraint;
|
||||
_constraint = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void JointState::updateConstraint() {
|
||||
if (_constraint) {
|
||||
delete _constraint;
|
||||
_constraint = NULL;
|
||||
}
|
||||
if (glm::distance2(glm::vec3(-PI), _fbxJoint->rotationMin) > EPSILON ||
|
||||
glm::distance2(glm::vec3(PI), _fbxJoint->rotationMax) > EPSILON ) {
|
||||
// this joint has rotation constraints
|
||||
_constraint = AngularConstraint::newAngularConstraint(_fbxJoint->rotationMin, _fbxJoint->rotationMax);
|
||||
}
|
||||
}
|
||||
|
||||
void JointState::copyState(const JointState& state) {
|
||||
|
@ -30,18 +73,7 @@ void JointState::copyState(const JointState& state) {
|
|||
_visibleTransform = state._visibleTransform;
|
||||
_visibleRotation = extractRotation(_visibleTransform);
|
||||
_visibleRotationInParentFrame = state._visibleRotationInParentFrame;
|
||||
// DO NOT copy _fbxJoint
|
||||
}
|
||||
|
||||
void JointState::setFBXJoint(const FBXJoint* joint) {
|
||||
assert(joint != NULL);
|
||||
_rotationInParentFrame = joint->rotation;
|
||||
// NOTE: JointState does not own the FBXJoint to which it points.
|
||||
_fbxJoint = joint;
|
||||
// precompute whether there are any constraints or not
|
||||
float distanceMin = glm::distance(_fbxJoint->rotationMin, glm::vec3(-PI));
|
||||
float distanceMax = glm::distance(_fbxJoint->rotationMax, glm::vec3(PI));
|
||||
_isConstrained = distanceMin > EPSILON || distanceMax > EPSILON;
|
||||
// DO NOT copy _fbxJoint or _constraint
|
||||
}
|
||||
|
||||
void JointState::computeTransform(const glm::mat4& parentTransform) {
|
||||
|
@ -70,11 +102,15 @@ void JointState::restoreRotation(float fraction, float priority) {
|
|||
}
|
||||
}
|
||||
|
||||
void JointState::setRotationFromBindFrame(const glm::quat& rotation, float priority) {
|
||||
void JointState::setRotationFromBindFrame(const glm::quat& rotation, float priority, bool constrain) {
|
||||
// rotation is from bind- to model-frame
|
||||
assert(_fbxJoint != NULL);
|
||||
if (priority >= _animationPriority) {
|
||||
setRotationInParentFrame(_rotationInParentFrame * glm::inverse(_rotation) * rotation * glm::inverse(_fbxJoint->inverseBindRotation));
|
||||
glm::quat targetRotation = _rotationInParentFrame * glm::inverse(_rotation) * rotation * glm::inverse(_fbxJoint->inverseBindRotation);
|
||||
if (constrain && _constraint) {
|
||||
_constraint->softClamp(targetRotation, _rotationInParentFrame, 0.5f);
|
||||
}
|
||||
setRotationInParentFrame(targetRotation);
|
||||
_animationPriority = priority;
|
||||
}
|
||||
}
|
||||
|
@ -99,7 +135,7 @@ void JointState::applyRotationDelta(const glm::quat& delta, bool constrain, floa
|
|||
return;
|
||||
}
|
||||
_animationPriority = priority;
|
||||
if (!constrain || !_isConstrained) {
|
||||
if (!constrain || _constraint == NULL) {
|
||||
// no constraints
|
||||
_rotationInParentFrame = _rotationInParentFrame * glm::inverse(_rotation) * delta * _rotation;
|
||||
_rotation = delta * _rotation;
|
||||
|
@ -122,10 +158,12 @@ void JointState::mixRotationDelta(const glm::quat& delta, float mixFactor, float
|
|||
if (mixFactor > 0.0f && mixFactor <= 1.0f) {
|
||||
targetRotation = safeMix(targetRotation, _fbxJoint->rotation, mixFactor);
|
||||
}
|
||||
if (_constraint) {
|
||||
_constraint->softClamp(targetRotation, _rotationInParentFrame, 0.5f);
|
||||
}
|
||||
setRotationInParentFrame(targetRotation);
|
||||
}
|
||||
|
||||
|
||||
glm::quat JointState::computeParentRotation() const {
|
||||
// R = Rp * Rpre * r * Rpost
|
||||
// Rp = R * (Rpre * r * Rpost)^
|
||||
|
|
|
@ -18,15 +18,19 @@
|
|||
|
||||
#include <FBXReader.h>
|
||||
|
||||
class AngularConstraint;
|
||||
|
||||
class JointState {
|
||||
public:
|
||||
JointState();
|
||||
|
||||
void copyState(const JointState& state);
|
||||
JointState(const JointState& other);
|
||||
~JointState();
|
||||
|
||||
void setFBXJoint(const FBXJoint* joint);
|
||||
const FBXJoint& getFBXJoint() const { return *_fbxJoint; }
|
||||
|
||||
void updateConstraint();
|
||||
void copyState(const JointState& state);
|
||||
|
||||
void computeTransform(const glm::mat4& parentTransform);
|
||||
|
||||
|
@ -64,7 +68,7 @@ public:
|
|||
/// \param rotation is from bind- to model-frame
|
||||
/// computes and sets new _rotationInParentFrame
|
||||
/// NOTE: the JointState's model-frame transform/rotation are NOT updated!
|
||||
void setRotationFromBindFrame(const glm::quat& rotation, float priority);
|
||||
void setRotationFromBindFrame(const glm::quat& rotation, float priority, bool constrain = false);
|
||||
|
||||
void setRotationInParentFrame(const glm::quat& targetRotation);
|
||||
const glm::quat& getRotationInParentFrame() const { return _rotationInParentFrame; }
|
||||
|
@ -95,7 +99,7 @@ private:
|
|||
glm::quat _visibleRotationInParentFrame;
|
||||
|
||||
const FBXJoint* _fbxJoint; // JointState does NOT own its FBXJoint
|
||||
bool _isConstrained;
|
||||
AngularConstraint* _constraint; // JointState owns its AngularConstraint
|
||||
};
|
||||
|
||||
#endif // hifi_JointState_h
|
||||
|
|
|
@ -561,8 +561,6 @@ bool Model::updateGeometry() {
|
|||
void Model::setJointStates(QVector<JointState> states) {
|
||||
_jointStates = states;
|
||||
|
||||
// compute an approximate bounding radius for broadphase collision queries
|
||||
// against PhysicsSimulation boundaries
|
||||
int numJoints = _jointStates.size();
|
||||
float radius = 0.0f;
|
||||
for (int i = 0; i < numJoints; ++i) {
|
||||
|
@ -570,6 +568,7 @@ void Model::setJointStates(QVector<JointState> states) {
|
|||
if (distance > radius) {
|
||||
radius = distance;
|
||||
}
|
||||
_jointStates[i].updateConstraint();
|
||||
}
|
||||
for (int i = 0; i < _jointStates.size(); i++) {
|
||||
_jointStates[i].slaveVisibleTransform();
|
||||
|
@ -1159,14 +1158,9 @@ void Model::inverseKinematics(int endIndex, glm::vec3 targetPosition, const glm:
|
|||
}
|
||||
glm::quat deltaRotation = rotationBetween(leverArm, targetPosition - pivot);
|
||||
|
||||
/* DON'T REMOVE! This code provides the gravitational effect on the IK solution.
|
||||
* It is commented out for the moment because we're blending the IK solution with
|
||||
* the default pose which provides similar stability, but we might want to use
|
||||
* gravity again later.
|
||||
|
||||
// We want to mix the shortest rotation with one that will pull the system down with gravity.
|
||||
// So we compute a simplified center of mass, where each joint has a mass of 1.0 and we don't
|
||||
// bother averaging it because we only need direction.
|
||||
// We want to mix the shortest rotation with one that will pull the system down with gravity
|
||||
// so that limbs don't float unrealistically. To do this we compute a simplified center of mass
|
||||
// where each joint has unit mass and we don't bother averaging it because we only need direction.
|
||||
if (j > 1) {
|
||||
|
||||
glm::vec3 centerOfMass(0.0f);
|
||||
|
@ -1188,11 +1182,9 @@ void Model::inverseKinematics(int endIndex, glm::vec3 targetPosition, const glm:
|
|||
}
|
||||
deltaRotation = safeMix(deltaRotation, gravityDelta, mixFactor);
|
||||
}
|
||||
*/
|
||||
|
||||
// Apply the rotation, but use mixRotationDelta() which blends a bit of the default pose
|
||||
// at in the process. This provides stability to the IK solution and removes the necessity
|
||||
// for the gravity effect.
|
||||
// at in the process. This provides stability to the IK solution for most models.
|
||||
glm::quat oldNextRotation = nextState.getRotation();
|
||||
float mixFactor = 0.03f;
|
||||
nextState.mixRotationDelta(deltaRotation, mixFactor, priority);
|
||||
|
@ -1217,7 +1209,7 @@ void Model::inverseKinematics(int endIndex, glm::vec3 targetPosition, const glm:
|
|||
} while (numIterations < MAX_ITERATION_COUNT && distanceToGo < ACCEPTABLE_IK_ERROR);
|
||||
|
||||
// set final rotation of the end joint
|
||||
endState.setRotationFromBindFrame(targetRotation, priority);
|
||||
endState.setRotationFromBindFrame(targetRotation, priority, true);
|
||||
|
||||
_shapesAreDirty = !_shapes.isEmpty();
|
||||
}
|
||||
|
|
|
@ -85,6 +85,33 @@ void TextureCache::setFrameBufferSize(QSize frameBufferSize) {
|
|||
}
|
||||
}
|
||||
|
||||
// use fixed table of permutations. Could also make ordered list programmatically
|
||||
// and then shuffle algorithm. For testing, this ensures consistent behavior in each run.
|
||||
// this list taken from Ken Perlin's Improved Noise reference implementation (orig. in Java) at
|
||||
// http://mrl.nyu.edu/~perlin/noise/
|
||||
|
||||
const int permutation[256] =
|
||||
{
|
||||
151, 160, 137, 91, 90, 15, 131, 13, 201, 95, 96, 53, 194, 233, 7, 225,
|
||||
140, 36, 103, 30, 69, 142, 8, 99, 37, 240, 21, 10, 23, 190, 6, 148,
|
||||
247, 120, 234, 75, 0, 26, 197, 62, 94, 252, 219, 203, 117, 35, 11, 32,
|
||||
57, 177, 33, 88, 237, 149, 56, 87, 174, 20, 125, 136, 171, 168, 68, 175,
|
||||
74, 165, 71, 134, 139, 48, 27, 166, 77, 146, 158, 231, 83, 111, 229, 122,
|
||||
60, 211, 133, 230, 220, 105, 92, 41, 55, 46, 245, 40, 244, 102, 143, 54,
|
||||
65, 25, 63, 161, 1, 216, 80, 73, 209, 76, 132, 187, 208, 89, 18, 169,
|
||||
200, 196, 135, 130, 116, 188, 159, 86, 164, 100, 109, 198, 173, 186, 3, 64,
|
||||
52, 217, 226, 250, 124, 123, 5, 202, 38, 147, 118, 126, 255, 82, 85, 212,
|
||||
207, 206, 59, 227, 47, 16, 58, 17, 182, 189, 28, 42, 223, 183, 170, 213,
|
||||
119, 248, 152, 2, 44, 154, 163, 70, 221, 153, 101, 155, 167, 43, 172, 9,
|
||||
129, 22, 39, 253, 19, 98, 108, 110, 79, 113, 224, 232, 178, 185, 112, 104,
|
||||
218, 246, 97, 228, 251, 34, 242, 193, 238, 210, 144, 12, 191, 179, 162, 241,
|
||||
81, 51, 145, 235, 249, 14, 239, 107, 49, 192, 214, 31, 181, 199, 106, 157,
|
||||
184, 84, 204, 176, 115, 121, 50, 45, 127, 4, 150, 254, 138, 236, 205, 93,
|
||||
222, 114, 67, 29, 24, 72, 243, 141, 128, 195, 78, 66, 215, 61, 156, 180
|
||||
};
|
||||
|
||||
#define USE_CHRIS_NOISE 1
|
||||
|
||||
GLuint TextureCache::getPermutationNormalTextureID() {
|
||||
if (_permutationNormalTextureID == 0) {
|
||||
glGenTextures(1, &_permutationNormalTextureID);
|
||||
|
@ -92,10 +119,17 @@ GLuint TextureCache::getPermutationNormalTextureID() {
|
|||
|
||||
// the first line consists of random permutation offsets
|
||||
unsigned char data[256 * 2 * 3];
|
||||
#if (USE_CHRIS_NOISE==1)
|
||||
for (int i = 0; i < 256; i++) {
|
||||
data[3*i+0] = permutation[i];
|
||||
data[3*i+1] = permutation[i];
|
||||
data[3*i+2] = permutation[i];
|
||||
#else
|
||||
for (int i = 0; i < 256 * 3; i++) {
|
||||
data[i] = rand() % 256;
|
||||
#endif
|
||||
}
|
||||
// the next, random unit normals
|
||||
|
||||
for (int i = 256 * 3; i < 256 * 3 * 2; i += 3) {
|
||||
glm::vec3 randvec = glm::sphericalRand(1.0f);
|
||||
data[i] = ((randvec.x + 1.0f) / 2.0f) * 255.0f;
|
||||
|
@ -105,7 +139,6 @@ GLuint TextureCache::getPermutationNormalTextureID() {
|
|||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 256, 2, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
}
|
||||
return _permutationNormalTextureID;
|
||||
|
|
|
@ -205,7 +205,7 @@ void ApplicationOverlay::getClickLocation(int &x, int &y) const {
|
|||
}
|
||||
}
|
||||
|
||||
// Draws the FBO texture for Oculus rift. TODO: Draw a curved texture instead of plane.
|
||||
// Draws the FBO texture for Oculus rift.
|
||||
void ApplicationOverlay::displayOverlayTextureOculus(Camera& whichCamera) {
|
||||
|
||||
if (_alpha == 0.0f) {
|
||||
|
@ -292,6 +292,107 @@ void ApplicationOverlay::displayOverlayTextureOculus(Camera& whichCamera) {
|
|||
|
||||
}
|
||||
|
||||
// Draws the FBO texture for 3DTV.
|
||||
void ApplicationOverlay::displayOverlayTexture3DTV(Camera& whichCamera, float aspectRatio, float fov) {
|
||||
|
||||
if (_alpha == 0.0f) {
|
||||
return;
|
||||
}
|
||||
|
||||
Application* application = Application::getInstance();
|
||||
|
||||
MyAvatar* myAvatar = application->getAvatar();
|
||||
const glm::vec3& viewMatrixTranslation = application->getViewMatrixTranslation();
|
||||
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_CONSTANT_ALPHA, GL_ONE);
|
||||
glBindTexture(GL_TEXTURE_2D, getFramebufferObject()->texture());
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
glDisable(GL_LIGHTING);
|
||||
glEnable(GL_TEXTURE_2D);
|
||||
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
|
||||
glPushMatrix();
|
||||
glLoadIdentity();
|
||||
// Transform to world space
|
||||
glm::quat rotation = whichCamera.getRotation();
|
||||
glm::vec3 axis2 = glm::axis(rotation);
|
||||
glRotatef(-glm::degrees(glm::angle(rotation)), axis2.x, axis2.y, axis2.z);
|
||||
glTranslatef(viewMatrixTranslation.x, viewMatrixTranslation.y, viewMatrixTranslation.z);
|
||||
|
||||
// Translate to the front of the camera
|
||||
glm::vec3 pos = whichCamera.getPosition();
|
||||
glm::quat rot = myAvatar->getOrientation();
|
||||
glm::vec3 axis = glm::axis(rot);
|
||||
|
||||
glTranslatef(pos.x, pos.y, pos.z);
|
||||
glRotatef(glm::degrees(glm::angle(rot)), axis.x, axis.y, axis.z);
|
||||
|
||||
glColor4f(1.0f, 1.0f, 1.0f, _alpha);
|
||||
|
||||
//Render
|
||||
// fov -= RADIANS_PER_DEGREE * 2.5f; //reduce by 5 degrees so it fits in the view
|
||||
const GLfloat distance = 1.0f;
|
||||
|
||||
const GLfloat halfQuadHeight = distance * tan(fov);
|
||||
const GLfloat halfQuadWidth = halfQuadHeight * aspectRatio;
|
||||
const GLfloat quadWidth = halfQuadWidth * 2.0f;
|
||||
const GLfloat quadHeight = halfQuadHeight * 2.0f;
|
||||
|
||||
GLfloat x = -halfQuadWidth;
|
||||
GLfloat y = -halfQuadHeight;
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
|
||||
glBegin(GL_QUADS);
|
||||
|
||||
glTexCoord2f(0.0f, 1.0f); glVertex3f(x, y + quadHeight, -distance);
|
||||
glTexCoord2f(1.0f, 1.0f); glVertex3f(x + quadWidth, y + quadHeight, -distance);
|
||||
glTexCoord2f(1.0f, 0.0f); glVertex3f(x + quadWidth, y, -distance);
|
||||
glTexCoord2f(0.0f, 0.0f); glVertex3f(x, y, -distance);
|
||||
|
||||
glEnd();
|
||||
|
||||
if (_crosshairTexture == 0) {
|
||||
_crosshairTexture = Application::getInstance()->getGLWidget()->bindTexture(QImage(Application::resourcesPath() + "images/sixense-reticle.png"));
|
||||
}
|
||||
|
||||
//draw the mouse pointer
|
||||
glBindTexture(GL_TEXTURE_2D, _crosshairTexture);
|
||||
|
||||
const float reticleSize = 40.0f / application->getGLWidget()->width() * quadWidth;
|
||||
x -= reticleSize / 2.0f;
|
||||
y += reticleSize / 2.0f;
|
||||
const float mouseX = (application->getMouseX() / (float)application->getGLWidget()->width()) * quadWidth;
|
||||
const float mouseY = (1.0 - (application->getMouseY() / (float)application->getGLWidget()->height())) * quadHeight;
|
||||
|
||||
glBegin(GL_QUADS);
|
||||
|
||||
glColor3f(RETICLE_COLOR[0], RETICLE_COLOR[1], RETICLE_COLOR[2]);
|
||||
|
||||
glTexCoord2d(0.0f, 0.0f); glVertex3f(x + mouseX, y + mouseY, -distance);
|
||||
glTexCoord2d(1.0f, 0.0f); glVertex3f(x + mouseX + reticleSize, y + mouseY, -distance);
|
||||
glTexCoord2d(1.0f, 1.0f); glVertex3f(x + mouseX + reticleSize, y + mouseY - reticleSize, -distance);
|
||||
glTexCoord2d(0.0f, 1.0f); glVertex3f(x + mouseX, y + mouseY - reticleSize, -distance);
|
||||
|
||||
glEnd();
|
||||
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
|
||||
glPopMatrix();
|
||||
|
||||
glDepthMask(GL_TRUE);
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
glDisable(GL_TEXTURE_2D);
|
||||
|
||||
glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_CONSTANT_ALPHA, GL_ONE);
|
||||
glEnable(GL_LIGHTING);
|
||||
|
||||
glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
|
||||
}
|
||||
|
||||
//Renders optional pointers
|
||||
void ApplicationOverlay::renderPointers() {
|
||||
Application* application = Application::getInstance();
|
||||
|
|
|
@ -29,6 +29,7 @@ public:
|
|||
void renderOverlay(bool renderToTexture = false);
|
||||
void displayOverlayTexture();
|
||||
void displayOverlayTextureOculus(Camera& whichCamera);
|
||||
void displayOverlayTexture3DTV(Camera& whichCamera, float aspectRatio, float fov);
|
||||
void computeOculusPickRay(float x, float y, glm::vec3& direction) const;
|
||||
void getClickLocation(int &x, int &y) const;
|
||||
|
||||
|
|
|
@ -83,17 +83,10 @@ void UserActivityLogger::close(int delayTime) {
|
|||
|
||||
// In order to get the end of the session, we need to give the account manager enough time to send the packet.
|
||||
QEventLoop loop;
|
||||
// Here we connect the callbacks to stop the event loop
|
||||
JSONCallbackParameters params;
|
||||
params.jsonCallbackReceiver = &loop;
|
||||
params.errorCallbackReceiver = &loop;
|
||||
params.jsonCallbackMethod = "quit";
|
||||
params.errorCallbackMethod = "quit";
|
||||
// In case something goes wrong, we also setup a timer so that the delai is not greater than delayTime
|
||||
QTimer timer;
|
||||
connect(&timer, &QTimer::timeout, &loop, &QEventLoop::quit);
|
||||
// Now we can log it
|
||||
logAction(ACTION_NAME, QJsonObject(), params);
|
||||
logAction(ACTION_NAME, QJsonObject());
|
||||
timer.start(delayTime);
|
||||
loop.exec();
|
||||
}
|
||||
|
|
|
@ -183,7 +183,6 @@ void ParticleCollisionSystem::updateCollisionWithParticles(Particle* particleA)
|
|||
|
||||
// MIN_VALID_SPEED is obtained by computing speed gained at one gravity after the shortest expected frame
|
||||
const float MIN_EXPECTED_FRAME_PERIOD = 0.0167f; // 1/60th of a second
|
||||
const float HALTING_SPEED = 9.8 * MIN_EXPECTED_FRAME_PERIOD / (float)(TREE_SCALE);
|
||||
|
||||
void ParticleCollisionSystem::updateCollisionWithAvatars(Particle* particle) {
|
||||
// particles that are in hand, don't collide with avatars
|
||||
|
|
201
libraries/shared/src/AngularConstraint.cpp
Normal file
201
libraries/shared/src/AngularConstraint.cpp
Normal file
|
@ -0,0 +1,201 @@
|
|||
//
|
||||
// AngularConstraint.cpp
|
||||
// interface/src/renderer
|
||||
//
|
||||
// Created by Andrew Meadows on 2014.05.30
|
||||
// Copyright 2014 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include <glm/gtx/norm.hpp>
|
||||
|
||||
#include "AngularConstraint.h"
|
||||
#include "SharedUtil.h"
|
||||
|
||||
// helper function
|
||||
/// \param angle radian angle to be clamped within angleMin and angleMax
|
||||
/// \param angleMin minimum value
|
||||
/// \param angleMax maximum value
|
||||
/// \return value between minAngle and maxAngle closest to angle
|
||||
float clampAngle(float angle, float angleMin, float angleMax) {
|
||||
float minDistance = angle - angleMin;
|
||||
float maxDistance = angle - angleMax;
|
||||
if (maxDistance > 0.0f) {
|
||||
minDistance = glm::min(minDistance, angleMin + TWO_PI - angle);
|
||||
angle = (minDistance < maxDistance) ? angleMin : angleMax;
|
||||
} else if (minDistance < 0.0f) {
|
||||
maxDistance = glm::max(maxDistance, angleMax - TWO_PI - angle);
|
||||
angle = (minDistance > maxDistance) ? angleMin : angleMax;
|
||||
}
|
||||
return angle;
|
||||
}
|
||||
|
||||
// static
|
||||
AngularConstraint* AngularConstraint::newAngularConstraint(const glm::vec3& minAngles, const glm::vec3& maxAngles) {
|
||||
float minDistance2 = glm::distance2(minAngles, glm::vec3(-PI, -PI, -PI));
|
||||
float maxDistance2 = glm::distance2(maxAngles, glm::vec3(PI, PI, PI));
|
||||
if (minDistance2 < EPSILON && maxDistance2 < EPSILON) {
|
||||
// no constraint
|
||||
return NULL;
|
||||
}
|
||||
// count the zero length elements
|
||||
glm::vec3 rangeAngles = maxAngles - minAngles;
|
||||
int pivotIndex = -1;
|
||||
int numZeroes = 0;
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
if (rangeAngles[i] < EPSILON) {
|
||||
++numZeroes;
|
||||
} else {
|
||||
pivotIndex = i;
|
||||
}
|
||||
}
|
||||
if (numZeroes == 2) {
|
||||
// this is a hinge
|
||||
int forwardIndex = (pivotIndex + 1) % 3;
|
||||
glm::vec3 forwardAxis(0.0f);
|
||||
forwardAxis[forwardIndex] = 1.0f;
|
||||
glm::vec3 rotationAxis(0.0f);
|
||||
rotationAxis[pivotIndex] = 1.0f;
|
||||
return new HingeConstraint(forwardAxis, rotationAxis, minAngles[pivotIndex], maxAngles[pivotIndex]);
|
||||
} else if (numZeroes == 0) {
|
||||
// approximate the angular limits with a cone roller
|
||||
// we assume the roll is about z
|
||||
glm::vec3 middleAngles = 0.5f * (maxAngles + minAngles);
|
||||
glm::quat yaw = glm::angleAxis(middleAngles[1], glm::vec3(0.0f, 1.0f, 0.0f));
|
||||
glm::quat pitch = glm::angleAxis(middleAngles[0], glm::vec3(1.0f, 0.0f, 0.0f));
|
||||
glm::vec3 coneAxis = pitch * yaw * glm::vec3(0.0f, 0.0f, 1.0f);
|
||||
// the coneAngle is half the average range of the two non-roll rotations
|
||||
glm::vec3 range = maxAngles - minAngles;
|
||||
float coneAngle = 0.25f * (range[0] + range[1]);
|
||||
return new ConeRollerConstraint(coneAngle, coneAxis, minAngles.z, maxAngles.z);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool AngularConstraint::softClamp(glm::quat& targetRotation, const glm::quat& oldRotation, float mixFraction) {
|
||||
glm::quat clampedTarget = targetRotation;
|
||||
bool clamped = clamp(clampedTarget);
|
||||
if (clamped) {
|
||||
// check if oldRotation is also clamped
|
||||
glm::quat clampedOld = oldRotation;
|
||||
bool clamped2 = clamp(clampedOld);
|
||||
if (clamped2) {
|
||||
// oldRotation is already beyond the constraint
|
||||
// we clamp again midway between targetRotation and clamped oldPosition
|
||||
clampedTarget = glm::shortMix(clampedOld, targetRotation, mixFraction);
|
||||
// and then clamp that
|
||||
clamp(clampedTarget);
|
||||
}
|
||||
// finally we mix targetRotation with the clampedTarget
|
||||
targetRotation = glm::shortMix(clampedTarget, targetRotation, mixFraction);
|
||||
}
|
||||
return clamped;
|
||||
}
|
||||
|
||||
HingeConstraint::HingeConstraint(const glm::vec3& forwardAxis, const glm::vec3& rotationAxis, float minAngle, float maxAngle)
|
||||
: _minAngle(minAngle), _maxAngle(maxAngle) {
|
||||
assert(_minAngle < _maxAngle);
|
||||
// we accept the rotationAxis direction
|
||||
assert(glm::length(rotationAxis) > EPSILON);
|
||||
_rotationAxis = glm::normalize(rotationAxis);
|
||||
// but we compute the final _forwardAxis
|
||||
glm::vec3 otherAxis = glm::cross(_rotationAxis, forwardAxis);
|
||||
assert(glm::length(otherAxis) > EPSILON);
|
||||
_forwardAxis = glm::normalize(glm::cross(otherAxis, _rotationAxis));
|
||||
}
|
||||
|
||||
// virtual
|
||||
bool HingeConstraint::clamp(glm::quat& rotation) const {
|
||||
glm::vec3 forward = rotation * _forwardAxis;
|
||||
forward -= glm::dot(forward, _rotationAxis) * _rotationAxis;
|
||||
float length = glm::length(forward);
|
||||
if (length < EPSILON) {
|
||||
// infinite number of solutions ==> choose the middle of the contrained range
|
||||
rotation = glm::angleAxis(0.5f * (_minAngle + _maxAngle), _rotationAxis);
|
||||
return true;
|
||||
}
|
||||
forward /= length;
|
||||
float sign = (glm::dot(glm::cross(_forwardAxis, forward), _rotationAxis) > 0.0f ? 1.0f : -1.0f);
|
||||
//float angle = sign * acos(glm::dot(forward, _forwardAxis) / length);
|
||||
float angle = sign * acos(glm::dot(forward, _forwardAxis));
|
||||
glm::quat newRotation = glm::angleAxis(clampAngle(angle, _minAngle, _maxAngle), _rotationAxis);
|
||||
if (fabsf(1.0f - glm::dot(newRotation, rotation)) > EPSILON * EPSILON) {
|
||||
rotation = newRotation;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool HingeConstraint::softClamp(glm::quat& targetRotation, const glm::quat& oldRotation, float mixFraction) {
|
||||
// the hinge works best without a soft clamp
|
||||
return clamp(targetRotation);
|
||||
}
|
||||
|
||||
ConeRollerConstraint::ConeRollerConstraint(float coneAngle, const glm::vec3& coneAxis, float minRoll, float maxRoll)
|
||||
: _coneAngle(coneAngle), _minRoll(minRoll), _maxRoll(maxRoll) {
|
||||
assert(_maxRoll >= _minRoll);
|
||||
float axisLength = glm::length(coneAxis);
|
||||
assert(axisLength > EPSILON);
|
||||
_coneAxis = coneAxis / axisLength;
|
||||
}
|
||||
|
||||
// virtual
|
||||
bool ConeRollerConstraint::clamp(glm::quat& rotation) const {
|
||||
bool applied = false;
|
||||
glm::vec3 rotatedAxis = rotation * _coneAxis;
|
||||
glm::vec3 perpAxis = glm::cross(rotatedAxis, _coneAxis);
|
||||
float perpAxisLength = glm::length(perpAxis);
|
||||
if (perpAxisLength > EPSILON) {
|
||||
perpAxis /= perpAxisLength;
|
||||
// enforce the cone
|
||||
float angle = acosf(glm::dot(rotatedAxis, _coneAxis));
|
||||
if (angle > _coneAngle) {
|
||||
rotation = glm::angleAxis(angle - _coneAngle, perpAxis) * rotation;
|
||||
rotatedAxis = rotation * _coneAxis;
|
||||
applied = true;
|
||||
}
|
||||
} else {
|
||||
// the rotation is 100% roll
|
||||
// there is no obvious perp axis so we must pick one
|
||||
perpAxis = rotatedAxis;
|
||||
// find the first non-zero element:
|
||||
float iValue = 0.0f;
|
||||
int i = 0;
|
||||
for (i = 0; i < 3; ++i) {
|
||||
if (fabsf(perpAxis[i]) > EPSILON) {
|
||||
iValue = perpAxis[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
assert(i != 3);
|
||||
// swap or negate the next element
|
||||
int j = (i + 1) % 3;
|
||||
float jValue = perpAxis[j];
|
||||
if (fabsf(jValue - iValue) > EPSILON) {
|
||||
perpAxis[i] = jValue;
|
||||
perpAxis[j] = iValue;
|
||||
} else {
|
||||
perpAxis[i] = -iValue;
|
||||
}
|
||||
perpAxis = glm::cross(perpAxis, rotatedAxis);
|
||||
perpAxisLength = glm::length(perpAxis);
|
||||
assert(perpAxisLength > EPSILON);
|
||||
perpAxis /= perpAxisLength;
|
||||
}
|
||||
// measure the roll
|
||||
// NOTE: perpAxis is perpendicular to both _coneAxis and rotatedConeAxis, so we can
|
||||
// rotate it again and we'll end up with an something that has only been rolled.
|
||||
glm::vec3 rolledPerpAxis = rotation * perpAxis;
|
||||
float sign = glm::dot(rotatedAxis, glm::cross(perpAxis, rolledPerpAxis)) > 0.0f ? 1.0f : -1.0f;
|
||||
float roll = sign * angleBetween(rolledPerpAxis, perpAxis);
|
||||
if (roll < _minRoll || roll > _maxRoll) {
|
||||
float clampedRoll = clampAngle(roll, _minRoll, _maxRoll);
|
||||
rotation = glm::normalize(glm::angleAxis(clampedRoll - roll, rotatedAxis) * rotation);
|
||||
applied = true;
|
||||
}
|
||||
return applied;
|
||||
}
|
||||
|
||||
|
55
libraries/shared/src/AngularConstraint.h
Normal file
55
libraries/shared/src/AngularConstraint.h
Normal file
|
@ -0,0 +1,55 @@
|
|||
//
|
||||
// AngularConstraint.h
|
||||
// interface/src/renderer
|
||||
//
|
||||
// Created by Andrew Meadows on 2014.05.30
|
||||
// Copyright 2013 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#ifndef hifi_AngularConstraint_h
|
||||
#define hifi_AngularConstraint_h
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
|
||||
class AngularConstraint {
|
||||
public:
|
||||
/// \param minAngles minumum euler angles for the constraint
|
||||
/// \param maxAngles minumum euler angles for the constraint
|
||||
/// \return pointer to new AngularConstraint of the right type or NULL if none could be made
|
||||
static AngularConstraint* newAngularConstraint(const glm::vec3& minAngles, const glm::vec3& maxAngles);
|
||||
|
||||
AngularConstraint() {}
|
||||
virtual ~AngularConstraint() {}
|
||||
virtual bool clamp(glm::quat& rotation) const = 0;
|
||||
virtual bool softClamp(glm::quat& targetRotation, const glm::quat& oldRotation, float mixFraction);
|
||||
protected:
|
||||
};
|
||||
|
||||
class HingeConstraint : public AngularConstraint {
|
||||
public:
|
||||
HingeConstraint(const glm::vec3& forwardAxis, const glm::vec3& rotationAxis, float minAngle, float maxAngle);
|
||||
virtual bool clamp(glm::quat& rotation) const;
|
||||
virtual bool softClamp(glm::quat& targetRotation, const glm::quat& oldRotation, float mixFraction);
|
||||
protected:
|
||||
glm::vec3 _forwardAxis;
|
||||
glm::vec3 _rotationAxis;
|
||||
float _minAngle;
|
||||
float _maxAngle;
|
||||
};
|
||||
|
||||
class ConeRollerConstraint : public AngularConstraint {
|
||||
public:
|
||||
ConeRollerConstraint(float coneAngle, const glm::vec3& coneAxis, float minRoll, float maxRoll);
|
||||
virtual bool clamp(glm::quat& rotation) const;
|
||||
private:
|
||||
float _coneAngle;
|
||||
glm::vec3 _coneAxis;
|
||||
float _minRoll;
|
||||
float _maxRoll;
|
||||
};
|
||||
|
||||
#endif // hifi_AngularConstraint_h
|
476
tests/shared/src/AngularConstraintTests.cpp
Normal file
476
tests/shared/src/AngularConstraintTests.cpp
Normal file
|
@ -0,0 +1,476 @@
|
|||
//
|
||||
// AngularConstraintTests.cpp
|
||||
// tests/physics/src
|
||||
//
|
||||
// Created by Andrew Meadows on 2014.05.30
|
||||
// Copyright 2014 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include <AngularConstraint.h>
|
||||
#include <SharedUtil.h>
|
||||
#include <StreamUtils.h>
|
||||
|
||||
#include "AngularConstraintTests.h"
|
||||
|
||||
|
||||
void AngularConstraintTests::testHingeConstraint() {
|
||||
float minAngle = -PI;
|
||||
float maxAngle = 0.0f;
|
||||
glm::vec3 yAxis(0.0f, 1.0f, 0.0f);
|
||||
glm::vec3 minAngles(0.0f, -PI, 0.0f);
|
||||
glm::vec3 maxAngles(0.0f, 0.0f, 0.0f);
|
||||
|
||||
AngularConstraint* c = AngularConstraint::newAngularConstraint(minAngles, maxAngles);
|
||||
if (!c) {
|
||||
std::cout << __FILE__ << ":" << __LINE__
|
||||
<< " ERROR: newAngularConstraint() should make a constraint" << std::endl;
|
||||
}
|
||||
|
||||
{ // test in middle of constraint
|
||||
float angle = 0.5f * (minAngle + maxAngle);
|
||||
glm::quat rotation = glm::angleAxis(angle, yAxis);
|
||||
|
||||
glm::quat newRotation = rotation;
|
||||
bool constrained = c->clamp(newRotation);
|
||||
if (constrained) {
|
||||
std::cout << __FILE__ << ":" << __LINE__
|
||||
<< " ERROR: HingeConstraint should not clamp()" << std::endl;
|
||||
}
|
||||
if (rotation != newRotation) {
|
||||
std::cout << __FILE__ << ":" << __LINE__
|
||||
<< " ERROR: HingeConstraint should not change rotation" << std::endl;
|
||||
}
|
||||
}
|
||||
{ // test just inside min edge of constraint
|
||||
float angle = minAngle + 10.f * EPSILON;
|
||||
glm::quat rotation = glm::angleAxis(angle, yAxis);
|
||||
|
||||
glm::quat newRotation = rotation;
|
||||
bool constrained = c->clamp(newRotation);
|
||||
if (constrained) {
|
||||
std::cout << __FILE__ << ":" << __LINE__
|
||||
<< " ERROR: HingeConstraint should not clamp()" << std::endl;
|
||||
}
|
||||
if (rotation != newRotation) {
|
||||
std::cout << __FILE__ << ":" << __LINE__
|
||||
<< " ERROR: HingeConstraint should not change rotation" << std::endl;
|
||||
}
|
||||
}
|
||||
{ // test just inside max edge of constraint
|
||||
float angle = maxAngle - 10.f * EPSILON;
|
||||
glm::quat rotation = glm::angleAxis(angle, yAxis);
|
||||
|
||||
glm::quat newRotation = rotation;
|
||||
bool constrained = c->clamp(newRotation);
|
||||
if (constrained) {
|
||||
std::cout << __FILE__ << ":" << __LINE__
|
||||
<< " ERROR: HingeConstraint should not clamp()" << std::endl;
|
||||
}
|
||||
if (rotation != newRotation) {
|
||||
std::cout << __FILE__ << ":" << __LINE__
|
||||
<< " ERROR: HingeConstraint should not change rotation" << std::endl;
|
||||
}
|
||||
}
|
||||
{ // test just outside min edge of constraint
|
||||
float angle = minAngle - 0.001f;
|
||||
glm::quat rotation = glm::angleAxis(angle, yAxis);
|
||||
|
||||
glm::quat newRotation = rotation;
|
||||
bool constrained = c->clamp(newRotation);
|
||||
if (!constrained) {
|
||||
std::cout << __FILE__ << ":" << __LINE__
|
||||
<< " ERROR: HingeConstraint should clamp()" << std::endl;
|
||||
}
|
||||
if (rotation == newRotation) {
|
||||
std::cout << __FILE__ << ":" << __LINE__
|
||||
<< " ERROR: HingeConstraint should change rotation" << std::endl;
|
||||
}
|
||||
glm::quat expectedRotation = glm::angleAxis(minAngle, yAxis);
|
||||
float qDot = glm::dot(expectedRotation, newRotation);
|
||||
if (fabsf(qDot - 1.0f) > EPSILON) {
|
||||
std::cout << __FILE__ << ":" << __LINE__
|
||||
<< " ERROR: HingeConstraint rotation = " << newRotation << " but expected " << expectedRotation << std::endl;
|
||||
}
|
||||
}
|
||||
{ // test just outside max edge of constraint
|
||||
float angle = maxAngle + 0.001f;
|
||||
glm::quat rotation = glm::angleAxis(angle, yAxis);
|
||||
|
||||
glm::quat newRotation = rotation;
|
||||
bool constrained = c->clamp(newRotation);
|
||||
if (!constrained) {
|
||||
std::cout << __FILE__ << ":" << __LINE__
|
||||
<< " ERROR: HingeConstraint should clamp()" << std::endl;
|
||||
}
|
||||
if (rotation == newRotation) {
|
||||
std::cout << __FILE__ << ":" << __LINE__
|
||||
<< " ERROR: HingeConstraint should change rotation" << std::endl;
|
||||
}
|
||||
glm::quat expectedRotation = glm::angleAxis(maxAngle, yAxis);
|
||||
float qDot = glm::dot(expectedRotation, newRotation);
|
||||
if (fabsf(qDot - 1.0f) > EPSILON) {
|
||||
std::cout << __FILE__ << ":" << __LINE__
|
||||
<< " ERROR: HingeConstraint rotation = " << newRotation << " but expected " << expectedRotation << std::endl;
|
||||
}
|
||||
}
|
||||
{ // test far outside min edge of constraint (wraps around to max)
|
||||
float angle = minAngle - 0.75f * (TWO_PI - (maxAngle - minAngle));
|
||||
glm::quat rotation = glm::angleAxis(angle, yAxis);
|
||||
|
||||
glm::quat newRotation = rotation;
|
||||
bool constrained = c->clamp(newRotation);
|
||||
if (!constrained) {
|
||||
std::cout << __FILE__ << ":" << __LINE__
|
||||
<< " ERROR: HingeConstraint should clamp()" << std::endl;
|
||||
}
|
||||
if (rotation == newRotation) {
|
||||
std::cout << __FILE__ << ":" << __LINE__
|
||||
<< " ERROR: HingeConstraint should change rotation" << std::endl;
|
||||
}
|
||||
glm::quat expectedRotation = glm::angleAxis(maxAngle, yAxis);
|
||||
float qDot = glm::dot(expectedRotation, newRotation);
|
||||
if (fabsf(qDot - 1.0f) > EPSILON) {
|
||||
std::cout << __FILE__ << ":" << __LINE__
|
||||
<< " ERROR: HingeConstraint rotation = " << newRotation << " but expected " << expectedRotation << std::endl;
|
||||
}
|
||||
}
|
||||
{ // test far outside max edge of constraint (wraps around to min)
|
||||
float angle = maxAngle + 0.75f * (TWO_PI - (maxAngle - minAngle));
|
||||
glm::quat rotation = glm::angleAxis(angle, yAxis);
|
||||
|
||||
glm::quat newRotation = rotation;
|
||||
bool constrained = c->clamp(newRotation);
|
||||
if (!constrained) {
|
||||
std::cout << __FILE__ << ":" << __LINE__
|
||||
<< " ERROR: HingeConstraint should clamp()" << std::endl;
|
||||
}
|
||||
if (rotation == newRotation) {
|
||||
std::cout << __FILE__ << ":" << __LINE__
|
||||
<< " ERROR: HingeConstraint should change rotation" << std::endl;
|
||||
}
|
||||
glm::quat expectedRotation = glm::angleAxis(minAngle, yAxis);
|
||||
float qDot = glm::dot(expectedRotation, newRotation);
|
||||
if (fabsf(qDot - 1.0f) > EPSILON) {
|
||||
std::cout << __FILE__ << ":" << __LINE__
|
||||
<< " ERROR: HingeConstraint rotation = " << newRotation << " but expected " << expectedRotation << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
float ACCEPTABLE_ERROR = 1.0e-4f;
|
||||
{ // test nearby but off-axis rotation
|
||||
float offAngle = 0.1f;
|
||||
glm::quat offRotation(offAngle, glm::vec3(1.0f, 0.0f, 0.0f));
|
||||
float angle = 0.5f * (maxAngle + minAngle);
|
||||
glm::quat rotation = offRotation * glm::angleAxis(angle, yAxis);
|
||||
|
||||
glm::quat newRotation = rotation;
|
||||
bool constrained = c->clamp(newRotation);
|
||||
if (!constrained) {
|
||||
std::cout << __FILE__ << ":" << __LINE__
|
||||
<< " ERROR: HingeConstraint should clamp()" << std::endl;
|
||||
}
|
||||
if (rotation == newRotation) {
|
||||
std::cout << __FILE__ << ":" << __LINE__
|
||||
<< " ERROR: HingeConstraint should change rotation" << std::endl;
|
||||
}
|
||||
glm::quat expectedRotation = glm::angleAxis(angle, yAxis);
|
||||
float qDot = glm::dot(expectedRotation, newRotation);
|
||||
if (fabsf(qDot - 1.0f) > ACCEPTABLE_ERROR) {
|
||||
std::cout << __FILE__ << ":" << __LINE__
|
||||
<< " ERROR: HingeConstraint rotation = " << newRotation << " but expected " << expectedRotation << std::endl;
|
||||
}
|
||||
}
|
||||
{ // test way off rotation > maxAngle
|
||||
float offAngle = 0.5f;
|
||||
glm::quat offRotation = glm::angleAxis(offAngle, glm::vec3(1.0f, 0.0f, 0.0f));
|
||||
float angle = maxAngle + 0.2f * (TWO_PI - (maxAngle - minAngle));
|
||||
glm::quat rotation = glm::angleAxis(angle, yAxis);
|
||||
rotation = offRotation * glm::angleAxis(angle, yAxis);
|
||||
|
||||
glm::quat newRotation = rotation;
|
||||
bool constrained = c->clamp(newRotation);
|
||||
if (!constrained) {
|
||||
std::cout << __FILE__ << ":" << __LINE__
|
||||
<< " ERROR: HingeConstraint should clamp()" << std::endl;
|
||||
}
|
||||
if (rotation == newRotation) {
|
||||
std::cout << __FILE__ << ":" << __LINE__
|
||||
<< " ERROR: HingeConstraint should change rotation" << std::endl;
|
||||
}
|
||||
glm::quat expectedRotation = glm::angleAxis(maxAngle, yAxis);
|
||||
float qDot = glm::dot(expectedRotation, newRotation);
|
||||
if (fabsf(qDot - 1.0f) > ACCEPTABLE_ERROR) {
|
||||
std::cout << __FILE__ << ":" << __LINE__
|
||||
<< " ERROR: HingeConstraint rotation = " << newRotation << " but expected " << expectedRotation << std::endl;
|
||||
}
|
||||
}
|
||||
{ // test way off rotation < minAngle
|
||||
float offAngle = 0.5f;
|
||||
glm::quat offRotation = glm::angleAxis(offAngle, glm::vec3(1.0f, 0.0f, 0.0f));
|
||||
float angle = minAngle - 0.2f * (TWO_PI - (maxAngle - minAngle));
|
||||
glm::quat rotation = glm::angleAxis(angle, yAxis);
|
||||
rotation = offRotation * glm::angleAxis(angle, yAxis);
|
||||
|
||||
glm::quat newRotation = rotation;
|
||||
bool constrained = c->clamp(newRotation);
|
||||
if (!constrained) {
|
||||
std::cout << __FILE__ << ":" << __LINE__
|
||||
<< " ERROR: HingeConstraint should clamp()" << std::endl;
|
||||
}
|
||||
if (rotation == newRotation) {
|
||||
std::cout << __FILE__ << ":" << __LINE__
|
||||
<< " ERROR: HingeConstraint should change rotation" << std::endl;
|
||||
}
|
||||
glm::quat expectedRotation = glm::angleAxis(minAngle, yAxis);
|
||||
float qDot = glm::dot(expectedRotation, newRotation);
|
||||
if (fabsf(qDot - 1.0f) > ACCEPTABLE_ERROR) {
|
||||
std::cout << __FILE__ << ":" << __LINE__
|
||||
<< " ERROR: HingeConstraint rotation = " << newRotation << " but expected " << expectedRotation << std::endl;
|
||||
}
|
||||
}
|
||||
{ // test way off rotation > maxAngle with wrap over to minAngle
|
||||
float offAngle = -0.5f;
|
||||
glm::quat offRotation = glm::angleAxis(offAngle, glm::vec3(1.0f, 0.0f, 0.0f));
|
||||
float angle = maxAngle + 0.6f * (TWO_PI - (maxAngle - minAngle));
|
||||
glm::quat rotation = glm::angleAxis(angle, yAxis);
|
||||
rotation = offRotation * glm::angleAxis(angle, yAxis);
|
||||
|
||||
glm::quat newRotation = rotation;
|
||||
bool constrained = c->clamp(newRotation);
|
||||
if (!constrained) {
|
||||
std::cout << __FILE__ << ":" << __LINE__
|
||||
<< " ERROR: HingeConstraint should clamp()" << std::endl;
|
||||
}
|
||||
if (rotation == newRotation) {
|
||||
std::cout << __FILE__ << ":" << __LINE__
|
||||
<< " ERROR: HingeConstraint should change rotation" << std::endl;
|
||||
}
|
||||
glm::quat expectedRotation = glm::angleAxis(minAngle, yAxis);
|
||||
float qDot = glm::dot(expectedRotation, newRotation);
|
||||
if (fabsf(qDot - 1.0f) > ACCEPTABLE_ERROR) {
|
||||
std::cout << __FILE__ << ":" << __LINE__
|
||||
<< " ERROR: HingeConstraint rotation = " << newRotation << " but expected " << expectedRotation << std::endl;
|
||||
}
|
||||
}
|
||||
{ // test way off rotation < minAngle with wrap over to maxAngle
|
||||
float offAngle = -0.6f;
|
||||
glm::quat offRotation = glm::angleAxis(offAngle, glm::vec3(1.0f, 0.0f, 0.0f));
|
||||
float angle = minAngle - 0.7f * (TWO_PI - (maxAngle - minAngle));
|
||||
glm::quat rotation = glm::angleAxis(angle, yAxis);
|
||||
rotation = offRotation * glm::angleAxis(angle, yAxis);
|
||||
|
||||
glm::quat newRotation = rotation;
|
||||
bool constrained = c->clamp(newRotation);
|
||||
if (!constrained) {
|
||||
std::cout << __FILE__ << ":" << __LINE__
|
||||
<< " ERROR: HingeConstraint should clamp()" << std::endl;
|
||||
}
|
||||
if (rotation == newRotation) {
|
||||
std::cout << __FILE__ << ":" << __LINE__
|
||||
<< " ERROR: HingeConstraint should change rotation" << std::endl;
|
||||
}
|
||||
glm::quat expectedRotation = glm::angleAxis(maxAngle, yAxis);
|
||||
float qDot = glm::dot(expectedRotation, newRotation);
|
||||
if (fabsf(qDot - 1.0f) > ACCEPTABLE_ERROR) {
|
||||
std::cout << __FILE__ << ":" << __LINE__
|
||||
<< " ERROR: HingeConstraint rotation = " << newRotation << " but expected " << expectedRotation << std::endl;
|
||||
}
|
||||
}
|
||||
delete c;
|
||||
}
|
||||
|
||||
void AngularConstraintTests::testConeRollerConstraint() {
|
||||
float minAngleX = -PI / 5.0f;
|
||||
float minAngleY = -PI / 5.0f;
|
||||
float minAngleZ = -PI / 8.0f;
|
||||
|
||||
float maxAngleX = PI / 4.0f;
|
||||
float maxAngleY = PI / 3.0f;
|
||||
float maxAngleZ = PI / 4.0f;
|
||||
|
||||
glm::vec3 minAngles(minAngleX, minAngleY, minAngleZ);
|
||||
glm::vec3 maxAngles(maxAngleX, maxAngleY, maxAngleZ);
|
||||
AngularConstraint* c = AngularConstraint::newAngularConstraint(minAngles, maxAngles);
|
||||
|
||||
float expectedConeAngle = 0.25 * (maxAngleX - minAngleX + maxAngleY - minAngleY);
|
||||
glm::vec3 middleAngles = 0.5f * (maxAngles + minAngles);
|
||||
glm::quat yaw = glm::angleAxis(middleAngles[1], glm::vec3(0.0f, 1.0f, 0.0f));
|
||||
glm::quat pitch = glm::angleAxis(middleAngles[0], glm::vec3(1.0f, 0.0f, 0.0f));
|
||||
glm::vec3 expectedConeAxis = pitch * yaw * glm::vec3(0.0f, 0.0f, 1.0f);
|
||||
|
||||
glm::vec3 xAxis(1.0f, 0.0f, 0.0f);
|
||||
glm::vec3 perpAxis = glm::normalize(xAxis - glm::dot(xAxis, expectedConeAxis) * expectedConeAxis);
|
||||
|
||||
if (!c) {
|
||||
std::cout << __FILE__ << ":" << __LINE__
|
||||
<< " ERROR: newAngularConstraint() should make a constraint" << std::endl;
|
||||
}
|
||||
{ // test in middle of constraint
|
||||
glm::vec3 angles(PI/20.0f, 0.0f, PI/10.0f);
|
||||
glm::quat rotation(angles);
|
||||
|
||||
glm::quat newRotation = rotation;
|
||||
bool constrained = c->clamp(newRotation);
|
||||
if (constrained) {
|
||||
std::cout << __FILE__ << ":" << __LINE__
|
||||
<< " ERROR: ConeRollerConstraint should not clamp()" << std::endl;
|
||||
}
|
||||
if (rotation != newRotation) {
|
||||
std::cout << __FILE__ << ":" << __LINE__
|
||||
<< " ERROR: ConeRollerConstraint should not change rotation" << std::endl;
|
||||
}
|
||||
}
|
||||
float deltaAngle = 0.001f;
|
||||
{ // test just inside edge of cone
|
||||
glm::quat rotation = glm::angleAxis(expectedConeAngle - deltaAngle, perpAxis);
|
||||
|
||||
glm::quat newRotation = rotation;
|
||||
bool constrained = c->clamp(newRotation);
|
||||
if (constrained) {
|
||||
std::cout << __FILE__ << ":" << __LINE__
|
||||
<< " ERROR: ConeRollerConstraint should not clamp()" << std::endl;
|
||||
}
|
||||
if (rotation != newRotation) {
|
||||
std::cout << __FILE__ << ":" << __LINE__
|
||||
<< " ERROR: ConeRollerConstraint should not change rotation" << std::endl;
|
||||
}
|
||||
}
|
||||
{ // test just outside edge of cone
|
||||
glm::quat rotation = glm::angleAxis(expectedConeAngle + deltaAngle, perpAxis);
|
||||
|
||||
glm::quat newRotation = rotation;
|
||||
bool constrained = c->clamp(newRotation);
|
||||
if (!constrained) {
|
||||
std::cout << __FILE__ << ":" << __LINE__
|
||||
<< " ERROR: ConeRollerConstraint should clamp()" << std::endl;
|
||||
}
|
||||
if (rotation == newRotation) {
|
||||
std::cout << __FILE__ << ":" << __LINE__
|
||||
<< " ERROR: ConeRollerConstraint should change rotation" << std::endl;
|
||||
}
|
||||
}
|
||||
{ // test just inside min edge of roll
|
||||
glm::quat rotation = glm::angleAxis(minAngleZ + deltaAngle, expectedConeAxis);
|
||||
|
||||
glm::quat newRotation = rotation;
|
||||
bool constrained = c->clamp(newRotation);
|
||||
if (constrained) {
|
||||
std::cout << __FILE__ << ":" << __LINE__
|
||||
<< " ERROR: ConeRollerConstraint should not clamp()" << std::endl;
|
||||
}
|
||||
if (rotation != newRotation) {
|
||||
std::cout << __FILE__ << ":" << __LINE__
|
||||
<< " ERROR: ConeRollerConstraint should not change rotation" << std::endl;
|
||||
}
|
||||
}
|
||||
{ // test just inside max edge of roll
|
||||
glm::quat rotation = glm::angleAxis(maxAngleZ - deltaAngle, expectedConeAxis);
|
||||
|
||||
glm::quat newRotation = rotation;
|
||||
bool constrained = c->clamp(newRotation);
|
||||
if (constrained) {
|
||||
std::cout << __FILE__ << ":" << __LINE__
|
||||
<< " ERROR: ConeRollerConstraint should not clamp()" << std::endl;
|
||||
}
|
||||
if (rotation != newRotation) {
|
||||
std::cout << __FILE__ << ":" << __LINE__
|
||||
<< " ERROR: ConeRollerConstraint should not change rotation" << std::endl;
|
||||
}
|
||||
}
|
||||
{ // test just outside min edge of roll
|
||||
glm::quat rotation = glm::angleAxis(minAngleZ - deltaAngle, expectedConeAxis);
|
||||
|
||||
glm::quat newRotation = rotation;
|
||||
bool constrained = c->clamp(newRotation);
|
||||
if (!constrained) {
|
||||
std::cout << __FILE__ << ":" << __LINE__
|
||||
<< " ERROR: ConeRollerConstraint should clamp()" << std::endl;
|
||||
}
|
||||
if (rotation == newRotation) {
|
||||
std::cout << __FILE__ << ":" << __LINE__
|
||||
<< " ERROR: ConeRollerConstraint should change rotation" << std::endl;
|
||||
}
|
||||
glm::quat expectedRotation = glm::angleAxis(minAngleZ, expectedConeAxis);
|
||||
if (fabsf(1.0f - glm::dot(newRotation, expectedRotation)) > EPSILON) {
|
||||
std::cout << __FILE__ << ":" << __LINE__
|
||||
<< " ERROR: rotation = " << newRotation << " but expected " << expectedRotation << std::endl;
|
||||
}
|
||||
}
|
||||
{ // test just outside max edge of roll
|
||||
glm::quat rotation = glm::angleAxis(maxAngleZ + deltaAngle, expectedConeAxis);
|
||||
|
||||
glm::quat newRotation = rotation;
|
||||
bool constrained = c->clamp(newRotation);
|
||||
if (!constrained) {
|
||||
std::cout << __FILE__ << ":" << __LINE__
|
||||
<< " ERROR: ConeRollerConstraint should clamp()" << std::endl;
|
||||
}
|
||||
if (rotation == newRotation) {
|
||||
std::cout << __FILE__ << ":" << __LINE__
|
||||
<< " ERROR: ConeRollerConstraint should change rotation" << std::endl;
|
||||
}
|
||||
glm::quat expectedRotation = glm::angleAxis(maxAngleZ, expectedConeAxis);
|
||||
if (fabsf(1.0f - glm::dot(newRotation, expectedRotation)) > EPSILON) {
|
||||
std::cout << __FILE__ << ":" << __LINE__
|
||||
<< " ERROR: rotation = " << newRotation << " but expected " << expectedRotation << std::endl;
|
||||
}
|
||||
}
|
||||
deltaAngle = 0.25f * expectedConeAngle;
|
||||
{ // test far outside cone and min roll
|
||||
glm::quat roll = glm::angleAxis(minAngleZ - deltaAngle, expectedConeAxis);
|
||||
glm::quat pitchYaw = glm::angleAxis(expectedConeAngle + deltaAngle, perpAxis);
|
||||
glm::quat rotation = pitchYaw * roll;
|
||||
|
||||
glm::quat newRotation = rotation;
|
||||
bool constrained = c->clamp(newRotation);
|
||||
if (!constrained) {
|
||||
std::cout << __FILE__ << ":" << __LINE__
|
||||
<< " ERROR: ConeRollerConstraint should clamp()" << std::endl;
|
||||
}
|
||||
if (rotation == newRotation) {
|
||||
std::cout << __FILE__ << ":" << __LINE__
|
||||
<< " ERROR: ConeRollerConstraint should change rotation" << std::endl;
|
||||
}
|
||||
glm::quat expectedRoll = glm::angleAxis(minAngleZ, expectedConeAxis);
|
||||
glm::quat expectedPitchYaw = glm::angleAxis(expectedConeAngle, perpAxis);
|
||||
glm::quat expectedRotation = expectedPitchYaw * expectedRoll;
|
||||
if (fabsf(1.0f - glm::dot(newRotation, expectedRotation)) > EPSILON) {
|
||||
std::cout << __FILE__ << ":" << __LINE__
|
||||
<< " ERROR: rotation = " << newRotation << " but expected " << expectedRotation << std::endl;
|
||||
}
|
||||
}
|
||||
{ // test far outside cone and max roll
|
||||
glm::quat roll = glm::angleAxis(maxAngleZ + deltaAngle, expectedConeAxis);
|
||||
glm::quat pitchYaw = glm::angleAxis(- expectedConeAngle - deltaAngle, perpAxis);
|
||||
glm::quat rotation = pitchYaw * roll;
|
||||
|
||||
glm::quat newRotation = rotation;
|
||||
bool constrained = c->clamp(newRotation);
|
||||
if (!constrained) {
|
||||
std::cout << __FILE__ << ":" << __LINE__
|
||||
<< " ERROR: ConeRollerConstraint should clamp()" << std::endl;
|
||||
}
|
||||
if (rotation == newRotation) {
|
||||
std::cout << __FILE__ << ":" << __LINE__
|
||||
<< " ERROR: ConeRollerConstraint should change rotation" << std::endl;
|
||||
}
|
||||
glm::quat expectedRoll = glm::angleAxis(maxAngleZ, expectedConeAxis);
|
||||
glm::quat expectedPitchYaw = glm::angleAxis(- expectedConeAngle, perpAxis);
|
||||
glm::quat expectedRotation = expectedPitchYaw * expectedRoll;
|
||||
if (fabsf(1.0f - glm::dot(newRotation, expectedRotation)) > EPSILON) {
|
||||
std::cout << __FILE__ << ":" << __LINE__
|
||||
<< " ERROR: rotation = " << newRotation << " but expected " << expectedRotation << std::endl;
|
||||
}
|
||||
}
|
||||
delete c;
|
||||
}
|
||||
|
||||
void AngularConstraintTests::runAllTests() {
|
||||
testHingeConstraint();
|
||||
testConeRollerConstraint();
|
||||
}
|
21
tests/shared/src/AngularConstraintTests.h
Normal file
21
tests/shared/src/AngularConstraintTests.h
Normal file
|
@ -0,0 +1,21 @@
|
|||
//
|
||||
// AngularConstraintTests.h
|
||||
// tests/physics/src
|
||||
//
|
||||
// Created by Andrew Meadows on 2014.05.30
|
||||
// Copyright 2014 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#ifndef hifi_AngularConstraintTests_h
|
||||
#define hifi_AngularConstraintTests_h
|
||||
|
||||
namespace AngularConstraintTests {
|
||||
void testHingeConstraint();
|
||||
void testConeRollerConstraint();
|
||||
void runAllTests();
|
||||
}
|
||||
|
||||
#endif // hifi_AngularConstraintTests_h
|
|
@ -8,9 +8,11 @@
|
|||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include "AngularConstraintTests.h"
|
||||
#include "MovingPercentileTests.h"
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
MovingPercentileTests::runAllTests();
|
||||
AngularConstraintTests::runAllTests();
|
||||
return 0;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue