Skip to content

Instantly share code, notes, and snippets.

@JoeJGit
Last active February 12, 2018 09:42
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save JoeJGit/7e269bdee6994c34b54011918f69b8cd to your computer and use it in GitHub Desktop.
Save JoeJGit/7e269bdee6994c34b54011918f69b8cd to your computer and use it in GitHub Desktop.
/* Copyright (c) <2003-2016> <Newton Game Dynamics>
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely
*/
#include <toolbox_stdafx.h>
#include "SkyBox.h"
#include "DemoEntityManager.h"
#include "DemoCamera.h"
#include "PhysicsUtils.h"
#include "DemoMesh.h"
#include "OpenGlUtil.h"
#include <dCustomBallAndSocket.h>
#define N_PI dFloat(3.1415926535897932384626433832795)
/////////////////////////////////////////
#define TEST_CASE 0
//#define AVERAGE_CONSTRAINT_SPACE // seems promising (enable limit visualization to see how it rotates constraint space to the average of both bodies), but requiers more work: twist should not rotate the arc, TEST_CASE 3 still jitters - see why
#if (TEST_CASE==0) // default
//#define DBG_FORCE_LIMIT_MOTOR // to test motor in limits constraint space, which is not used otherwiese in any given examples
//#define DBG_ZERO_MOTOR_TORQUE
//#define DBG_LOW_MOTOR_TORQUE
#define ENABLE_LIMIT_OVER_MOTOR // if limit violated, constrain limits and ignore motor (causes ragdoll stacks to go crazy with DBG_FORCE_LIMIT_MOTOR, but seems to work if joint set up in proper orientation)
//#define ENABLE_ERROR_ALIGNED_P2P // align point to point constraint space to displacement error, but seems no difference
#define ZERO_MASS_BASES // static bodies at the base so stuff is fixed
#endif
#if (TEST_CASE==1) // see twist swing motor in action for all joints
#define DBG_FORCE_LIMIT_MOTOR // to test motor in limits constraint space, which is not used otherwiese in any given examples
//#define DBG_ZERO_MOTOR_TORQUE
//#define DBG_LOW_MOTOR_TORQUE
//#define ENABLE_LIMIT_OVER_MOTOR // if limit violated, constrain limits and ignore motor (causes ragdoll stacks to go crazy with DBG_FORCE_LIMIT_MOTOR, but seems to work if joint set up in proper orientation)
//#define ENABLE_ERROR_ALIGNED_P2P // align point to point constraint space to displacement error, not sure about this
//#define ZERO_MASS_BASES // static bodies at the base so stuff is fixed
#endif
#if (TEST_CASE==2) // test if limits still work with motor
#define DBG_FORCE_LIMIT_MOTOR // to test motor in limits constraint space, which is not used otherwiese in any given examples
#define DBG_ZERO_MOTOR_TORQUE
//#define DBG_LOW_MOTOR_TORQUE
#define ENABLE_LIMIT_OVER_MOTOR // if limit violated, constrain limits and ignore motor (causes ragdoll stacks to go crazy with DBG_FORCE_LIMIT_MOTOR, but seems to work if joint set up in proper orientation)
//#define ENABLE_ERROR_ALIGNED_P2P // align point to point constraint space to displacement error, not sure about this
#define ZERO_MASS_BASES // static bodies at the base so stuff is fixed
#endif
#if (TEST_CASE==3) // animated ragdoll stack go crazy, allthough limits should not be violated. Eventually twist / swing decomposition breaks here and we see some gimbal lock issue. Joints are setup 90 degrees wrong, but this might become a problem
#define DBG_FORCE_LIMIT_MOTOR // to test motor in limits constraint space, which is not used otherwiese in any given examples
//#define DBG_ZERO_MOTOR_TORQUE
//#define DBG_LOW_MOTOR_TORQUE
#define ENABLE_LIMIT_OVER_MOTOR // if limit violated, constrain limits and ignore motor (causes ragdoll stacks to go crazy with DBG_FORCE_LIMIT_MOTOR, but seems to work if joint set up in proper orientation)
//#define ENABLE_ERROR_ALIGNED_P2P // align point to point constraint space to displacement error, not sure about this
#define ZERO_MASS_BASES // static bodies at the base so stuff is fixed
#endif
//#define JULY // to compare with older version of Newton
/////////////////////////////////////////
static NewtonBody* CreateBox (DemoEntityManager* const scene, const dVector& location, const dVector& size, const dFloat mass = 1)
{
NewtonWorld* const world = scene->GetNewton();
int materialID = NewtonMaterialGetDefaultGroupID (world);
NewtonCollision* const collision = CreateConvexCollision (world, dGetIdentityMatrix(), size, _BOX_PRIMITIVE, 0);
DemoMesh* const geometry = new DemoMesh("primitive", collision, "smilli.tga", "smilli.tga", "smilli.tga");
dMatrix matrix (dGetIdentityMatrix());
matrix.m_posit = location;
matrix.m_posit.m_w = 1;
NewtonBody* const body = CreateSimpleSolid (scene, geometry, mass, matrix, collision, materialID);
geometry->Release();
NewtonDestroyCollision(collision);
return body;
}
class JoesRagdollJoint: public dCustomBallAndSocket
{
public:
dQuaternion m_target; // relative target rotation to reach at next timestep
// motor:
dFloat m_maxMotorTorque;
dFloat m_reduceError;
dFloat m_pin_length;
dFloat m_stiffness;
dFloat m_anim_speed;
dFloat m_anim_offset;
dFloat m_anim_time;
// limits:
bool m_isLimitJoint;
bool m_limitWithMotor;
dFloat m_maxLimitTorque;
dFloat m_minTwistAngle;
dFloat m_maxTwistAngle;
dFloat m_coneAngle;
dFloat m_arcAngle;
dFloat m_arcAngleCos;
dFloat m_arcAngleSin;
dFloat const E = dFloat(1.0E-16);
dFloat const E_angle = dFloat(1.0e-10);
JoesRagdollJoint(NewtonBody* child, NewtonBody* parent, const dMatrix &localMatrix0, const dMatrix &localMatrix1, NewtonWorld *world, bool isLimitJoint = false, bool isMotorizedLimitJoint = false)
:dCustomBallAndSocket(localMatrix0, localMatrix1, child, parent)
{
m_localMatrix0 = localMatrix0;
m_localMatrix1 = localMatrix1;
m_target = dQuaternion(dVector(1, 0, 0), 0);
m_reduceError = dFloat(0.95); // amount of error to reduce per timestep (more -> oszillation)
m_stiffness = dFloat(0.9f);
m_maxLimitTorque = dFloat(1000);
m_maxMotorTorque = dFloat(300);
#ifdef DBG_ZERO_MOTOR_TORQUE
m_maxMotorTorque = 0;
#endif
#ifdef DBG_LOW_MOTOR_TORQUE
m_maxMotorTorque = 30;
#endif
m_anim_speed = 0;
m_anim_offset = 0;
m_anim_time = 0;
m_isLimitJoint = isLimitJoint;
m_limitWithMotor = isLimitJoint && isMotorizedLimitJoint;
#ifdef DBG_FORCE_LIMIT_MOTOR
m_isLimitJoint = 1;
m_limitWithMotor = 1;
#endif
m_coneAngle = dFloat(2.8);
#ifdef AVERAGE_CONSTRAINT_SPACE
m_coneAngle /= 2;
m_arcAngle /= 2;
#endif
m_arcAngle = -1; // negative number: simple cone and no arc
m_arcAngleCos = dCos (0);
m_arcAngleSin = dSin (0);
m_minTwistAngle = 1; m_maxTwistAngle = -1; // unrestricted
}
void SetTwistSwingLimits (const dFloat coneAngle, const dFloat arcAngle, const dFloat minTwistAngle, const dFloat maxTwistAngle)
{
dFloat const maxAng = dFloat(2.8); // to prevent flipping on the pole on the backside
m_coneAngle = dMin (maxAng, coneAngle);
m_arcAngle = dMax (dFloat(0), dMin (maxAng, arcAngle + m_coneAngle) - m_coneAngle);
#ifdef AVERAGE_CONSTRAINT_SPACE
m_coneAngle /= 2;
m_arcAngle /= 2;
#endif
m_arcAngleCos = dCos (m_arcAngle);
m_arcAngleSin = dSin (m_arcAngle);
m_minTwistAngle = minTwistAngle;
m_maxTwistAngle = maxTwistAngle;
}
dVector BodyGetPointVelocity(const NewtonBody* const body, const dVector &point)
{
dMatrix matrix;
dVector v(0,0,0,0);
dVector w(0,0,0,0);
dVector c(0,0,0,0);
NewtonBodyGetVelocity(body, &v[0]);
NewtonBodyGetOmega(body, &w[0]);
NewtonBodyGetMatrix(body, &matrix[0][0]);
c = matrix.m_posit; // TODO: Does not handle COM offset !!!!!!!!!!!!!!!!!!!!!!
return v + w.CrossProduct(point - c);
}
void SubmitConstraints(dFloat timestep, int threadIndex)
{
dFloat invTimestep = 1 / timestep;
dMatrix matrix0;
dMatrix matrix1;
CalculateGlobalMatrix(matrix0, matrix1);
dQuaternion q0(matrix0);
dQuaternion q1(matrix1);
#ifdef AVERAGE_CONSTRAINT_SPACE
dQuaternion qAV;// = q0.Slerp(q1, dFloat(0.5f) ); // lerp instead, requires no epsilon:
{
dFloat t (0.5f);
dFloat s (1.0f - 0.5f);
if (q0.DotProduct(q1) < 0) s *= -1;
qAV.m_q0 = t*q1.m_q0 + s*q0.m_q0;
qAV.m_q1 = t*q1.m_q1 + s*q0.m_q1;
qAV.m_q2 = t*q1.m_q2 + s*q0.m_q2;
qAV.m_q3 = t*q1.m_q3 + s*q0.m_q3;
qAV.Normalize();
}
dMatrix matrixCS (qAV, dVector(0,0,0,1));
#else
dMatrix &matrixCS = matrix0;
#endif
// constrain position
{
#ifdef ENABLE_ERROR_ALIGNED_P2P
const dVector& p0 = matrix0.m_posit;
const dVector& p1 = matrix1.m_posit;
dMatrix matrixCS_P2P;
dVector alignTo = p0-p1;
if ((alignTo.DotProduct3(alignTo)) < E) matrixCS_P2P = matrix0;
else matrixCS_P2P = dGrammSchmidt(alignTo);
NewtonUserJointAddLinearRow (m_joint, &p0[0], &p1[0], &matrixCS_P2P.m_front[0]);
NewtonUserJointAddLinearRow (m_joint, &p0[0], &p1[0], &matrixCS_P2P.m_up[0]);
NewtonUserJointAddLinearRow (m_joint, &p0[0], &p1[0], &matrixCS_P2P.m_right[0]);
#else
//dCustomBallAndSocket::SubmitConstraints(timestep, threadIndex);
const dVector& p0 = matrix0.m_posit;
const dVector& p1 = matrix1.m_posit;
for (int i = 0; i < 3; i++) {
NewtonUserJointAddLinearRow(m_joint, &p0[0], &p1[0], &matrix1[i][0]);
NewtonUserJointSetRowStiffness(m_joint, m_stiffness);
}
#endif
}
// calculate motor
dVector angAcc (0,0,0,0);
dVector errorAxis = matrix0.m_front;
{
if (m_anim_speed != 0) // some animation to illustrate purpose
{
m_anim_time += timestep * m_anim_speed;
dFloat a0 = dSin(m_anim_time);
dFloat a1 = m_anim_offset * N_PI;
dVector axis(dSin(a1), 0, dCos(a1));
m_target = dQuaternion(axis, a0/2);
}
// measure error
dQuaternion qt0 = m_target * q1;
dQuaternion qErr = ((q0.DotProduct(qt0) < 0)
? dQuaternion(-q0.m_q0, q0.m_q1, q0.m_q2, q0.m_q3)
: dQuaternion(q0.m_q0, -q0.m_q1, -q0.m_q2, -q0.m_q3)) * qt0;
qErr.Normalize();
dFloat errorAngle = 2 * dAcos(dClamp (qErr.m_q0, dFloat(-1), dFloat(1)));
if (errorAngle > E_angle)
{
errorAxis = dVector (qErr.m_q1, qErr.m_q2, qErr.m_q3, 0);
errorAxis = errorAxis.Scale(1 / dSqrt(errorAxis.DotProduct3(errorAxis)));
}
dVector angVel0(0,0,0,0);
dVector angVel1(0,0,0,0);
NewtonBodyGetOmega(m_body0, (dFloat*)&angVel0);
NewtonBodyGetOmega(m_body1, (dFloat*)&angVel1);
angAcc = (errorAxis.Scale(m_reduceError * errorAngle * invTimestep) - (angVel0 - angVel1)).Scale(invTimestep);
}
if (m_isLimitJoint)
{
const dVector& coneDir0 = matrixCS.m_front;
const dVector& coneDir1 = matrix1.m_front;
dFloat dot = coneDir0.DotProduct3(coneDir1);
if (dot < dFloat(-0.999)) return; // todo: Assuming it's ok to not submit any rows in this case
// claculate swing
dVector swingAxis;
dFloat swingErrorAngle = 0;
{
if (m_arcAngle <= 0) // simple cone limit
{
swingAxis = (coneDir0.CrossProduct(coneDir1));
swingErrorAngle = dAcos (dClamp(dot, dFloat(-1), dFloat(1))) - m_coneAngle;
}
else // cone on arc limit - think of an piece of pizza (arc) and an allowed max distance from it (cone):
{
// project current axis to the arc plane (y)
dVector d = matrix1.UnrotateVector (matrixCS.m_front);
dVector cone = d; cone.m_y = 0;
dFloat sql = cone.DotProduct3(cone);
cone = (sql > E) ? cone.Scale (1 / dSqrt (sql)) : dVector(1, 0, 0, 0);
// clamp the result to be within the arc angle
if (cone.m_x < m_arcAngleCos)
cone = dVector ( m_arcAngleCos, 0, ( (cone.m_z < 0) ? -m_arcAngleSin : m_arcAngleSin));
// do a regular cone constraint from that
swingErrorAngle = dAcos (dClamp(d.DotProduct3(cone), dFloat(-1), dFloat(1))) - m_coneAngle;
swingAxis = matrix1.RotateVector(d.CrossProduct(cone));
}
dFloat sql = swingAxis.DotProduct3(swingAxis);
if (sql > E) swingAxis = swingAxis.Scale (1 / dSqrt (sql));
else swingAxis = matrixCS.m_up;
}
// twist
dVector twistAxis = coneDir0; // this is the only real change from the last version, which used (coneDir0+coneDir1).Unit() resulting in non orthogonal twist/swing axis
// but still, sometimes it fails (not sure why) - force orthogonal:
{
twistAxis -= swingAxis.Scale(swingAxis.DotProduct3(twistAxis));
dFloat sql = twistAxis.DotProduct3(twistAxis);
if (sql > E) twistAxis = twistAxis.Scale (1 / dSqrt (sql));
else twistAxis = coneDir0; // should never happen
}
{
dFloat twistAngle = 0;
{
#if 1
// this does not account for a orthogonal fix, but still works best for now:
dFloat *q0p = (dFloat*)&q0;
dFloat *q1p = (dFloat*)&q1;
// factor rotation about x axis between quat0 and quat1. Code is an optimization of this: qt = q0.Inversed() * q1; halfTwistAngle = atan (qt.x / qt.w);
twistAngle = 2 * atan (
( ( ( (q0p[0] * q1p[1]) + (-q0p[1] * q1p[0]) ) + (-q0p[2] * q1p[3]) ) - (-q0p[3] * q1p[2]) ) /
( ( ( (q0p[0] * q1p[0]) - (-q0p[1] * q1p[1]) ) - (-q0p[2] * q1p[2]) ) - (-q0p[3] * q1p[3]) ) );
#else
// try to account for orthogonal fix (rotationAxis may not be exactly (1,0,0)), but does not work. i may think of this wrong, but this requires more work.
dQuaternion qt = q1 * q0.Inverse();
dVector rotationAxis (qt.m_q1, qt.m_q2, qt.m_q3);
dFloat rotationAngle = 2 * dAcos(dClamp(qt.m_q0, dFloat(-1), dFloat(1)));
dFloat sql = rotationAxis.DotProduct3(rotationAxis);
if (sql > E)
{
rotationAxis = rotationAxis.Scale (1 / dSqrt (sql));
twistAngle = rotationAngle * twistAxis.DotProduct3 (rotationAxis);
}
#endif
}
dFloat minTourque = 0;
dFloat maxTourque = 0;
dFloat errorAngle = 0;
if (1 && m_maxTwistAngle >= m_minTwistAngle) // twist restricted?
{
if (m_maxTwistAngle == m_minTwistAngle) // no freedom for any twist
{
errorAngle = twistAngle - m_maxTwistAngle;
minTourque = -m_maxLimitTorque;
maxTourque = m_maxLimitTorque;
}
else if (twistAngle > m_maxTwistAngle)
{
errorAngle = twistAngle - m_maxTwistAngle;
maxTourque = m_maxLimitTorque;
}
else if (twistAngle < m_minTwistAngle)
{
errorAngle = twistAngle - m_minTwistAngle;
minTourque = -m_maxLimitTorque;
}
}
if (m_limitWithMotor
#ifdef ENABLE_LIMIT_OVER_MOTOR
&& errorAngle == 0
#endif
)
{
dVector &axis = twistAxis;
dFloat relAccel = angAcc.DotProduct3(axis);
NewtonUserJointAddAngularRow(m_joint, 0, &axis[0]);
NewtonUserJointSetRowAcceleration(m_joint, relAccel);
NewtonUserJointSetRowMinimumFriction(m_joint, -m_maxMotorTorque);
NewtonUserJointSetRowMaximumFriction(m_joint, m_maxMotorTorque);
}
else
{
NewtonUserJointAddAngularRow (m_joint, errorAngle, (dFloat*)&twistAxis);
NewtonUserJointSetRowMinimumFriction (m_joint, minTourque);
NewtonUserJointSetRowMaximumFriction (m_joint, maxTourque);
}
NewtonUserJointSetRowStiffness(m_joint, m_stiffness);
}
// apply swing
{
dFloat maxTourque = 0;
if (swingErrorAngle > 0) maxTourque = m_maxLimitTorque;
if (m_limitWithMotor
#ifdef ENABLE_LIMIT_OVER_MOTOR
&& swingErrorAngle == 0
#endif
)
{
dVector &axis = swingAxis;
dFloat relAccel = angAcc.DotProduct3(axis);
NewtonUserJointAddAngularRow(m_joint, 0, &axis[0]);
NewtonUserJointSetRowAcceleration(m_joint, relAccel);
NewtonUserJointSetRowMinimumFriction(m_joint, -m_maxMotorTorque);
NewtonUserJointSetRowMaximumFriction(m_joint, m_maxMotorTorque);
}
else
{
NewtonUserJointAddAngularRow (m_joint, swingErrorAngle, (dFloat*)&swingAxis);
NewtonUserJointSetRowMinimumFriction (m_joint, 0);
NewtonUserJointSetRowMaximumFriction (m_joint, maxTourque);
}
NewtonUserJointSetRowStiffness(m_joint, m_stiffness);
}
dAssert (dAbs(twistAxis.DotProduct3(swingAxis)) < dFloat(0.001)); // todo: in theory it could happen swingAxis == twistAxis
// final row if motor
if (m_limitWithMotor) // todo: guess it's ok to submit this row only if motor turned on
{
dVector axis = swingAxis.CrossProduct(twistAxis);
dFloat relAccel = angAcc.DotProduct3(axis);
NewtonUserJointAddAngularRow(m_joint, 0, &axis[0]);
NewtonUserJointSetRowAcceleration(m_joint, relAccel);
NewtonUserJointSetRowMinimumFriction(m_joint, -m_maxMotorTorque);
NewtonUserJointSetRowMaximumFriction(m_joint, m_maxMotorTorque);
NewtonUserJointSetRowStiffness(m_joint, m_stiffness);
}
}
else // no limits but motor
{
dMatrix basis = dGrammSchmidt(errorAxis);
for (int n = 0; n < 3; n++) {
// calculate the desired acceleration
dVector &axis = basis[n];
dFloat relAccel = angAcc.DotProduct3(axis);
NewtonUserJointAddAngularRow(m_joint, 0, &axis[0]);
NewtonUserJointSetRowAcceleration(m_joint, relAccel);
NewtonUserJointSetRowMinimumFriction(m_joint, -m_maxMotorTorque);
NewtonUserJointSetRowMaximumFriction(m_joint, m_maxMotorTorque);
NewtonUserJointSetRowStiffness(m_joint, m_stiffness);
}
}
}
void Debug(dDebugDisplay* const debugDisplay) const
{
//dCustomJoint::Debug(debugDisplay);
struct Vis
{
static void Line (dDebugDisplay* const debugDisplay, dVector p0, dVector p1, dFloat r, dFloat g, dFloat b, dMatrix *frame = 0)
{
if (frame)
{
p0 = frame->TransformVector (p0);
p1 = frame->TransformVector (p1);
}
dVector color (r,g,b,1);
#ifdef JULY
debugDisplay->SetColor(0, color);
debugDisplay->DrawLine (0, p0, p1);
#else
debugDisplay->SetColor(color);
debugDisplay->DrawLine (p0, p1);
#endif
}
static void Vector (dDebugDisplay* const debugDisplay, dVector o, dVector v, dFloat r, dFloat g, dFloat b, dMatrix *frame = 0)
{
dVector p0 = o;
dVector p1 = o + v;
Line (debugDisplay, p0, p1, r,g,b, frame);
}
static dVector ToCartesian (dVector s)
{
dFloat r = s.m_x;
dFloat p = s.m_y;
dFloat t = s.m_z;
return dVector (
r*dCos(p) * dSin(t),
r*dSin(p) * dSin(t),
r*dCos(t),
0);
}
};
dMatrix matrix0;
dMatrix matrix1;
CalculateGlobalMatrix(matrix0, matrix1);
#ifdef AVERAGE_CONSTRAINT_SPACE
dQuaternion q0(matrix0);
dQuaternion q1(matrix1);
dQuaternion qAV;// = q0.Slerp(q1, dFloat(0.5f) ); // lerp instead, requires no epsilon:
{
dFloat t (0.5f);
dFloat s (1.0f - 0.5f);
if (q0.DotProduct(q1) < 0) s *= -1;
qAV.m_q0 = t*q1.m_q0 + s*q0.m_q0;
qAV.m_q1 = t*q1.m_q1 + s*q0.m_q1;
qAV.m_q2 = t*q1.m_q2 + s*q0.m_q2;
qAV.m_q3 = t*q1.m_q3 + s*q0.m_q3;
qAV.Normalize();
}
dMatrix matrixCS (qAV, matrix0.m_posit);
dMatrix &matrixVIS = matrixCS;
#else
dMatrix &matrixCS = matrix0;
dMatrix &matrixVIS = matrix1;
#endif
#if 0
if (1 && m_isLimitJoint) // vis constraint space
{
const dVector& p0 = matrix0.m_posit;
//const dVector& p1 = matrix1.m_posit;
const dVector& coneDir0 = matrixCS.m_front;
const dVector& coneDir1 = matrix1.m_front;
dFloat dot = coneDir0.DotProduct3(coneDir1);
if (dot < -0.999) return; // should never happen
dVector twistAxis = coneDir0;
dVector swingAxis;
{
dFloat swingErrorAngle = 0;
if (m_arcAngle <= 0) // simple cone limit
{
swingAxis = (coneDir0.CrossProduct(coneDir1));
swingErrorAngle = dAcos (dClamp(dot, dFloat(-1), dFloat(1))) - m_coneAngle;
}
else // cone on arc limit - think of an piece of pizza (arc) and an allowed max distance from it (cone):
{
// project current axis to the arc plane (y)
dVector d = matrix1.UnrotateVector (matrixCS.m_front);
dVector cone = d; cone.m_y = 0;
dFloat sql = cone.DotProduct3(cone);
cone = (sql > E) ? cone.Scale (1 / dSqrt (sql)) : dVector(1, 0, 0, 0);
// clamp the result to be within the arc angle
if (cone.m_x < m_arcAngleCos)
cone = dVector ( m_arcAngleCos, 0, ( (cone.m_z < 0) ? -m_arcAngleSin : m_arcAngleSin));
// do a regular cone constraint from that
dFloat maxTourque = 0;
swingErrorAngle = dAcos (dClamp(d.DotProduct3(cone), dFloat(-1), dFloat(1))) - m_coneAngle;
if (swingErrorAngle > 0) maxTourque = m_maxLimitTorque;
swingAxis = matrix1.RotateVector(d.CrossProduct(cone));
}
dFloat sql = swingAxis.DotProduct3(swingAxis);
if (sql > E) swingAxis = swingAxis.Scale (1 / dSqrt (sql));
else swingAxis = matrixCS.m_up;
}
dFloat size = 1.5f;
Vis::Vector (debugDisplay, p0, twistAxis.Scale(size), 1,0,0, 0);
Vis::Vector (debugDisplay, p0, swingAxis.Scale(size), 0,1,0, 0);
}
#endif
// vis limits
if (1)
{
int const subdiv = 36;
dFloat const radius = 0.4f;
dFloat yAngle = m_coneAngle;
dFloat xAngle = dMax(m_arcAngle,dFloat(0));
dFloat minTAngle = m_minTwistAngle;
dFloat maxTAngle = m_maxTwistAngle;
dFloat minXAngle = -xAngle;
dFloat maxXAngle = xAngle;
int i;
// vis swing limit
dVector ot = matrix1.m_posit;
dVector ob = matrix1.m_posit;
dMatrix ortho = dYawMatrix(N_PI * 0.5f - minXAngle) * matrixVIS;
for (i=0; i<=subdiv/4; i++)
{
dFloat t = dFloat(i) / dFloat(subdiv);
dVector c = Vis::ToCartesian (dVector (radius, N_PI * 2.0f * t, yAngle));
dVector nt = ortho.TransformVector (c); Vis::Line (debugDisplay, nt, ot, 1,1,1); ot = nt;
c[1] *= -1;
dVector nb = ortho.TransformVector (c); Vis::Line (debugDisplay, nb, ob, 1,1,1); ob = nb;
}
ot = matrix1[3];
ob = matrix1[3];
ortho = dYawMatrix(N_PI * 0.5f - maxXAngle) * matrixVIS;
for (i=subdiv/2; i>=subdiv/4; i--)
{
dFloat t = dFloat(i) / dFloat(subdiv);
dVector c = Vis::ToCartesian (dVector (radius, N_PI * 2.0f * t, yAngle));
dVector nt = ortho.TransformVector (c); Vis::Line (debugDisplay, nt, ot, 1,1,1); ot = nt;
c[1] *= -1;
dVector nb = ortho.TransformVector (c); Vis::Line (debugDisplay, nb, ob, 1,1,1); ob = nb;
}
ot = matrix1[3];
ob = matrix1[3];
ortho = dPitchMatrix(N_PI * 0.5f) * dYawMatrix(-minXAngle) * matrixVIS;//matrix1 * sMat4::rotationY(-minXAngle) * sMat4::rotationX(N_PI * 0.5);
dFloat angDiff = maxXAngle - minXAngle;
int subdiv2 = 1 + (angDiff / N_PI * 180.0f / (360.0f / dFloat(subdiv)));
for (i=0; i<=subdiv2; i++)
{
dFloat t = dFloat(i) / dFloat(subdiv2);
dVector c = Vis::ToCartesian (dVector (radius, angDiff * t, N_PI * 0.5f - yAngle));
dVector nt = ortho.TransformVector (c); Vis::Line (debugDisplay, nt, ot, 1,1,1); ot = nt;
c[2] *= -1;
dVector nb = ortho.TransformVector (c); Vis::Line (debugDisplay, nb, ob, 1,1,1); ob = nb;
}
Vis::Line (debugDisplay, matrix1.m_posit, ot, 1,1,1);
Vis::Line (debugDisplay, matrix1.m_posit, ob, 1,1,1);
// vis twist limit
ot = matrix1.m_posit;
ortho = dYawMatrix(N_PI * -0.5f) * dPitchMatrix(N_PI * -0.5f) * matrix1;//matrix1 * sMat4::rotationX(N_PI * -0.5) * sMat4::rotationY(N_PI * -0.5);
angDiff = maxTAngle - minTAngle;
subdiv2 = 1 + (angDiff / N_PI * 180.0f / (360.f / dFloat(subdiv)));
for (i=0; i<=subdiv2; i++)
{
dFloat t = dFloat(i) / dFloat(subdiv2);
dVector c = Vis::ToCartesian (dVector (radius, angDiff * t + minTAngle, N_PI * 0.5f));
dVector nt = ortho.TransformVector (c); Vis::Line (debugDisplay, nt, ot, 1,1,0); ot = nt;
}
Vis::Line (debugDisplay, matrix1[3], ot, 1,1,0);
dQuaternion quat0(matrix0), quat1(matrix1);
dFloat *q0 = (dFloat*)&quat0;
dFloat *q1 = (dFloat*)&quat1;
dFloat twistAngle = 2.0f * atan (
( ( ( (q0[0] * q1[1]) + (-q0[1] * q1[0]) ) + (-q0[2] * q1[3]) ) - (-q0[3] * q1[2]) ) /
( ( ( (q0[0] * q1[0]) - (-q0[1] * q1[1]) ) - (-q0[2] * q1[2]) ) - (-q0[3] * q1[3]) ) );
Vis::Vector (debugDisplay, matrix1[3], matrix1.RotateVector(dVector(0,1,0).Scale(radius)), 0,1,0); // zero twist
Vis::Vector (debugDisplay, matrix1[3], matrix1.RotateVector(dVector(0, dCos(twistAngle), dSin(-twistAngle)).Scale(radius)), 1,0,0); // current twist
Vis::Vector (debugDisplay, matrix0[3], matrix0[0].Scale(radius), 0.5f,0.5f,1); // current pin
}
}
};
inline dFloat randF (unsigned int time)
{
time *= 1664525;
time ^= (time << 16);
time *= 16807;
return dFloat(time & 0xFFFFFFFFu) / dFloat(0xFFFFFFFFu);
}
void AddJoesPoweredRagDoll (DemoEntityManager* const scene, const dVector& origin, const dFloat animSpeed, const int numSegments,
const int numArms = 1,
const dFloat torsoHeight = 1,
const dFloat torsoWidth = 4.0f,
const dFloat randomness = 0,
const dFloat armHeight = 1,
const dFloat armWidth = 0.5f,
const int pickMe = -1)
{
dFloat height = torsoHeight;
dFloat width = torsoWidth;
dVector size (width, height, width);
NewtonBody* torso = CreateBox (scene, origin + dVector (0, 0.5f, 0, 0), size
#ifdef ZERO_MASS_BASES
,0
#endif
);
dMatrix torsoMatrix;
NewtonBodyGetMatrix (torso, (dFloat*) &torsoMatrix);
int bodyIndex = 0;
NewtonBody* pickBody = 0;
for (int j=0; j < numArms; j++)
{
dFloat angle = dFloat(j) / dFloat(numArms) * N_PI*2.0f;
dMatrix armRotation = dPitchMatrix(angle);
dMatrix armTransform = armRotation * torsoMatrix;
NewtonBody* parent = torso;
int numBodies = numSegments;
if (randomness > 0) numBodies += int (randF(j) * dFloat(numSegments) + 0.5f);
for (int i=0; i < numBodies; i++)
{
dFloat height = armHeight;
dFloat width = armWidth;
dVector size (width, height, width);
dVector pos (0, height * dFloat(i + (numArms>1 ? 2 : 1)), 0, 0);
NewtonBody* child = CreateBox (scene, pos, size);
dMatrix bodyMatrix;
NewtonBodyGetMatrix (child, (dFloat*) &bodyMatrix);
bodyMatrix = bodyMatrix * armTransform;
NewtonBodySetMatrix (child, (dFloat*) &bodyMatrix);
dMatrix matrix0 = dGetIdentityMatrix(); matrix0.m_posit = dVector (0, height*-0.5f, 0, 1);
dMatrix matrix1 = dGetIdentityMatrix(); matrix1.m_posit = dVector (0, height*0.5f, 0, 1);
if (parent == torso)
{
matrix1.m_posit.m_y += height;
matrix1 = matrix1 * armRotation;
}
if (randomness > 0)
{
dMatrix rotation = dPitchMatrix(randF(bodyIndex*3+0) * N_PI * 0.25f * randomness);
rotation = rotation * dYawMatrix(randF(bodyIndex*3+1) * N_PI * 0.25f * randomness);
rotation = rotation * dYawMatrix(randF(bodyIndex*3+2) * N_PI * 0.25f * randomness);
matrix0 = matrix0 * rotation;
}
JoesRagdollJoint* joint = new JoesRagdollJoint (child, parent, matrix0, matrix1, scene->GetNewton());
if (animSpeed != 0) {
joint->m_anim_speed = animSpeed, joint->m_anim_offset = dFloat(i) / dFloat(numBodies); // animated
}
parent = child;
if (bodyIndex == pickMe) {
pickBody = child;
}
bodyIndex++;
}
}
if (pickBody)
{
dMatrix matrix;
NewtonBodyGetMatrix(pickBody, &matrix[0][0]);
new dCustomBallAndSocket(matrix, pickBody);
}
}
void AddJoesLimitJoint (DemoEntityManager* const scene, const dVector& origin, const int testCase)
{
NewtonBody* shoulder = CreateBox (scene, origin + dVector (-0.5f, 2.0f, 0, 0), dVector (0.5f, 0.5f, 0.5f, 0)
#ifdef ZERO_MASS_BASES
,0
#endif
);
NewtonBody* bicep = CreateBox (scene, origin + dVector ( 0, 2.0f, 0, 0), dVector (1, 0.25f, 0.25f, 0));
dMatrix localMatShoulder0 = dGetIdentityMatrix();
if (testCase == -1) localMatShoulder0 = dPitchMatrix(N_PI*-0.5f) * dYawMatrix(N_PI*-0.25f) * dRollMatrix(N_PI*-0.15f);
localMatShoulder0.m_posit = dVector (0.25f, 0, 0, 1);
dMatrix localMatShoulder1 = dGetIdentityMatrix();
localMatShoulder1.m_posit = dVector (-0.5f, 0, 0, 1);
dMatrix localMatBicep0 = dYawMatrix(N_PI*-0.5f);
localMatBicep0.m_posit = dVector (0.5f, 0, 0, 1);
dMatrix localMatBicep1 = dGetIdentityMatrix();
localMatBicep1.m_posit = dVector (-0.5f, 0, 0, 1);
JoesRagdollJoint* shoulderJoint = new JoesRagdollJoint (bicep, shoulder, localMatShoulder1, localMatShoulder0, scene->GetNewton(), true);
switch (testCase)
{
case 1:
shoulderJoint->SetTwistSwingLimits (0, N_PI/2, 0, 0); // just arc (hinge; robust for knees and elbows, unsure if traditional approach is better)
break;
case 2:
shoulderJoint->SetTwistSwingLimits (N_PI/4*3, 0, 0, 0); // large swing cone to show robust twist (the larger the swing angle becomes, the more rotation happens around its axis: Drag bicep back and then around to see)
break;
case 3:
shoulderJoint->SetTwistSwingLimits (N_PI/4, 0, 0, 0); // small swing cone
break;
case 4:
shoulderJoint->SetTwistSwingLimits (0, 0, -N_PI/2, N_PI/2); // no swing (hinge, but neither practical nor robust)
break;
default:
shoulderJoint->SetTwistSwingLimits (N_PI*dFloat(0.2), N_PI*dFloat(0.1), -N_PI*dFloat(0.5), N_PI*dFloat(0.2)); // natural limit
}
if (testCase == -1)
{
NewtonBody* arm = CreateBox (scene, origin + dVector (1,2,0,0), dVector (1, dFloat(0.2f), dFloat(0.2f), 0));
JoesRagdollJoint* ellbowJoint = new JoesRagdollJoint (arm, bicep, localMatBicep1, localMatBicep0, scene->GetNewton(), true);
ellbowJoint->SetTwistSwingLimits (0, N_PI*dFloat(0.4), 0, 0);
}
}
void StandardJoints (DemoEntityManager* const scene)
//void JoesJointTest (DemoEntityManager* const scene)
{
scene->CreateSkyBox();
// customize the scene after loading
// set a user friction variable in the body for variable friction demos
// later this will be done using LUA script
dMatrix offsetMatrix (dGetIdentityMatrix());
CreateLevelMesh (scene, "flatPlane.ngd", 1);
//CreateLevelMesh (scene, "flatPlane1.ngd", 1);
dVector location (0,0,0,0);
dVector size (1.5f, 2.0f, 2.0f, 0);
#if 1
AddJoesPoweredRagDoll(scene, dVector(40.0f, 10.0f, -20.0f), 1.5f, 4);
AddJoesPoweredRagDoll(scene, dVector(40.0f, 10.0f, -10.0f), 0, 4);
#endif
#if 0
AddJoesPoweredRagDoll(scene, dVector(40.0f, 10.0f, -30.0f), 0, 20);
AddJoesPoweredRagDoll(scene, dVector(40.0f, 10.0f, 0), 0, 4, 4, 1, 1);
AddJoesPoweredRagDoll(scene, dVector(40.0f, 10.0f, 10.0f), 0, 7, 2, 0.4f, 0.4f, 1.3f);
AddJoesPoweredRagDoll(scene, dVector(40.0f, 10.0f, 20.0f), 0, 5, 3, 0.4f, 0.4f, 1, 0.5f, 0.5f);
AddJoesPoweredRagDoll(scene, dVector(40.0f, 10.0f, 30.0f), 0, 3, 5, 1, 1, 1.3f, 0.5f, 0.5f, 4);
#endif
#if 1
AddJoesLimitJoint (scene, dVector(-40.0f, 0, 2.5f), -1);
AddJoesLimitJoint (scene, dVector(-40.0f, 0, -0), 0);
AddJoesLimitJoint (scene, dVector(-40.0f, 0, -2.5f), 1);
AddJoesLimitJoint (scene, dVector(-40.0f, 0, -5.0f), 2);
AddJoesLimitJoint (scene, dVector(-40.0f, 0, -7.5f), 3);
AddJoesLimitJoint (scene, dVector(-40.0f, 0, -10.f), 4);
#endif
// place camera into position
dMatrix camMatrix (dGetIdentityMatrix());
dQuaternion rot (camMatrix);
dVector origin (-50.0f, 5.0f, 0, 0);
scene->SetCameraMatrix(rot, origin);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment