Merge pull request #479 from Ventrella/master

preparing avatar balls for extended touch capabilities
This commit is contained in:
birarda 2013-06-03 17:57:48 -07:00
commit c7921c4be9
7 changed files with 453 additions and 294 deletions

View file

@ -1,106 +1,106 @@
#version 120 #version 120
// //
// For licensing information, see http://http.developer.nvidia.com/GPUGems/gpugems_app01.html: // For licensing information, see http://http.developer.nvidia.com/GPUGems/gpugems_app01.html:
// //
// NVIDIA Statement on the Software // NVIDIA Statement on the Software
// //
// The source code provided is freely distributable, so long as the NVIDIA header remains unaltered and user modifications are // The source code provided is freely distributable, so long as the NVIDIA header remains unaltered and user modifications are
// detailed. // detailed.
// //
// No Warranty // No Warranty
// //
// THE SOFTWARE AND ANY OTHER MATERIALS PROVIDED BY NVIDIA ON THE ENCLOSED CD-ROM ARE PROVIDED "AS IS." NVIDIA DISCLAIMS ALL // THE SOFTWARE AND ANY OTHER MATERIALS PROVIDED BY NVIDIA ON THE ENCLOSED CD-ROM ARE PROVIDED "AS IS." NVIDIA DISCLAIMS ALL
// WARRANTIES, EXPRESS, IMPLIED OR STATUTORY, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF TITLE, MERCHANTABILITY, // WARRANTIES, EXPRESS, IMPLIED OR STATUTORY, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF TITLE, MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
// //
// Limitation of Liability // Limitation of Liability
// //
// NVIDIA SHALL NOT BE LIABLE TO ANY USER, DEVELOPER, DEVELOPER'S CUSTOMERS, OR ANY OTHER PERSON OR ENTITY CLAIMING THROUGH OR // NVIDIA SHALL NOT BE LIABLE TO ANY USER, DEVELOPER, DEVELOPER'S CUSTOMERS, OR ANY OTHER PERSON OR ENTITY CLAIMING THROUGH OR
// UNDER DEVELOPER FOR ANY LOSS OF PROFITS, INCOME, SAVINGS, OR ANY OTHER CONSEQUENTIAL, INCIDENTAL, SPECIAL, PUNITIVE, DIRECT // UNDER DEVELOPER FOR ANY LOSS OF PROFITS, INCOME, SAVINGS, OR ANY OTHER CONSEQUENTIAL, INCIDENTAL, SPECIAL, PUNITIVE, DIRECT
// OR INDIRECT DAMAGES (WHETHER IN AN ACTION IN CONTRACT, TORT OR BASED ON A WARRANTY), EVEN IF NVIDIA HAS BEEN ADVISED OF THE // OR INDIRECT DAMAGES (WHETHER IN AN ACTION IN CONTRACT, TORT OR BASED ON A WARRANTY), EVEN IF NVIDIA HAS BEEN ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGES. THESE LIMITATIONS SHALL APPLY NOTWITHSTANDING ANY FAILURE OF THE ESSENTIAL PURPOSE OF ANY // POSSIBILITY OF SUCH DAMAGES. THESE LIMITATIONS SHALL APPLY NOTWITHSTANDING ANY FAILURE OF THE ESSENTIAL PURPOSE OF ANY
// LIMITED REMEDY. IN NO EVENT SHALL NVIDIA'S AGGREGATE LIABILITY TO DEVELOPER OR ANY OTHER PERSON OR ENTITY CLAIMING THROUGH // LIMITED REMEDY. IN NO EVENT SHALL NVIDIA'S AGGREGATE LIABILITY TO DEVELOPER OR ANY OTHER PERSON OR ENTITY CLAIMING THROUGH
// OR UNDER DEVELOPER EXCEED THE AMOUNT OF MONEY ACTUALLY PAID BY DEVELOPER TO NVIDIA FOR THE SOFTWARE OR ANY OTHER MATERIALS. // OR UNDER DEVELOPER EXCEED THE AMOUNT OF MONEY ACTUALLY PAID BY DEVELOPER TO NVIDIA FOR THE SOFTWARE OR ANY OTHER MATERIALS.
// //
// //
// Atmospheric scattering fragment shader // Atmospheric scattering fragment shader
// //
// Author: Sean O'Neil // Author: Sean O'Neil
// //
// Copyright (c) 2004 Sean O'Neil // Copyright (c) 2004 Sean O'Neil
// //
uniform vec3 v3CameraPos; // The camera's current position uniform vec3 v3CameraPos; // The camera's current position
uniform vec3 v3InvWavelength; // 1 / pow(wavelength, 4) for the red, green, and blue channels uniform vec3 v3InvWavelength; // 1 / pow(wavelength, 4) for the red, green, and blue channels
uniform float fInnerRadius; // The inner (planetary) radius uniform float fInnerRadius; // The inner (planetary) radius
uniform float fKrESun; // Kr * ESun uniform float fKrESun; // Kr * ESun
uniform float fKmESun; // Km * ESun uniform float fKmESun; // Km * ESun
uniform float fKr4PI; // Kr * 4 * PI uniform float fKr4PI; // Kr * 4 * PI
uniform float fKm4PI; // Km * 4 * PI uniform float fKm4PI; // Km * 4 * PI
uniform float fScale; // 1 / (fOuterRadius - fInnerRadius) uniform float fScale; // 1 / (fOuterRadius - fInnerRadius)
uniform float fScaleDepth; // The scale depth (i.e. the altitude at which the atmosphere's average density is found) uniform float fScaleDepth; // The scale depth (i.e. the altitude at which the atmosphere's average density is found)
uniform float fScaleOverScaleDepth; // fScale / fScaleDepth uniform float fScaleOverScaleDepth; // fScale / fScaleDepth
const int nSamples = 2; const int nSamples = 2;
const float fSamples = 2.0; const float fSamples = 2.0;
uniform vec3 v3LightPos; uniform vec3 v3LightPos;
uniform float g; uniform float g;
uniform float g2; uniform float g2;
varying vec3 position; varying vec3 position;
float scale(float fCos) float scale(float fCos)
{ {
float x = 1.0 - fCos; float x = 1.0 - fCos;
return fScaleDepth * exp(-0.00287 + x*(0.459 + x*(3.83 + x*(-6.80 + x*5.25)))); return fScaleDepth * exp(-0.00287 + x*(0.459 + x*(3.83 + x*(-6.80 + x*5.25))));
} }
void main (void) void main (void)
{ {
// Get the ray from the camera to the vertex, and its length (which is the far point of the ray passing through the atmosphere) // Get the ray from the camera to the vertex, and its length (which is the far point of the ray passing through the atmosphere)
vec3 v3Pos = position; vec3 v3Pos = position;
vec3 v3Ray = v3Pos - v3CameraPos; vec3 v3Ray = v3Pos - v3CameraPos;
float fFar = length(v3Ray); float fFar = length(v3Ray);
v3Ray /= fFar; v3Ray /= fFar;
// Calculate the ray's starting position, then calculate its scattering offset // Calculate the ray's starting position, then calculate its scattering offset
vec3 v3Start = v3CameraPos; vec3 v3Start = v3CameraPos;
float fHeight = length(v3Start); float fHeight = length(v3Start);
float fDepth = exp(fScaleOverScaleDepth * (fInnerRadius - fHeight)); float fDepth = exp(fScaleOverScaleDepth * (fInnerRadius - fHeight));
float fStartAngle = dot(v3Ray, v3Start) / fHeight; float fStartAngle = dot(v3Ray, v3Start) / fHeight;
float fStartOffset = fDepth * scale(fStartAngle); float fStartOffset = fDepth * scale(fStartAngle);
// Initialize the scattering loop variables // Initialize the scattering loop variables
//gl_FrontColor = vec4(0.0, 0.0, 0.0, 0.0); //gl_FrontColor = vec4(0.0, 0.0, 0.0, 0.0);
float fSampleLength = fFar / fSamples; float fSampleLength = fFar / fSamples;
float fScaledLength = fSampleLength * fScale; float fScaledLength = fSampleLength * fScale;
vec3 v3SampleRay = v3Ray * fSampleLength; vec3 v3SampleRay = v3Ray * fSampleLength;
vec3 v3SamplePoint = v3Start + v3SampleRay * 0.5; vec3 v3SamplePoint = v3Start + v3SampleRay * 0.5;
// Now loop through the sample rays // Now loop through the sample rays
vec3 v3FrontColor = vec3(0.0, 0.0, 0.0); vec3 v3FrontColor = vec3(0.0, 0.0, 0.0);
for(int i=0; i<nSamples; i++) for(int i=0; i<nSamples; i++)
{ {
float fHeight = length(v3SamplePoint); float fHeight = length(v3SamplePoint);
float fDepth = exp(fScaleOverScaleDepth * (fInnerRadius - fHeight)); float fDepth = exp(fScaleOverScaleDepth * (fInnerRadius - fHeight));
float fLightAngle = dot(v3LightPos, v3SamplePoint) / fHeight; float fLightAngle = dot(v3LightPos, v3SamplePoint) / fHeight;
float fCameraAngle = dot((v3Ray), v3SamplePoint) / fHeight * 0.99; float fCameraAngle = dot((v3Ray), v3SamplePoint) / fHeight * 0.99;
float fScatter = (fStartOffset + fDepth * (scale(fLightAngle) - scale(fCameraAngle))); float fScatter = (fStartOffset + fDepth * (scale(fLightAngle) - scale(fCameraAngle)));
vec3 v3Attenuate = exp(-fScatter * (v3InvWavelength * fKr4PI + fKm4PI)); vec3 v3Attenuate = exp(-fScatter * (v3InvWavelength * fKr4PI + fKm4PI));
v3FrontColor += v3Attenuate * (fDepth * fScaledLength); v3FrontColor += v3Attenuate * (fDepth * fScaledLength);
v3SamplePoint += v3SampleRay; v3SamplePoint += v3SampleRay;
} }
// Finally, scale the Mie and Rayleigh colors and set up the varying variables for the pixel shader // Finally, scale the Mie and Rayleigh colors and set up the varying variables for the pixel shader
vec3 secondaryFrontColor = v3FrontColor * fKmESun; vec3 secondaryFrontColor = v3FrontColor * fKmESun;
vec3 frontColor = v3FrontColor * (v3InvWavelength * fKrESun); vec3 frontColor = v3FrontColor * (v3InvWavelength * fKrESun);
vec3 v3Direction = v3CameraPos - v3Pos; vec3 v3Direction = v3CameraPos - v3Pos;
float fCos = dot(v3LightPos, v3Direction) / length(v3Direction); float fCos = dot(v3LightPos, v3Direction) / length(v3Direction);
float fMiePhase = 1.5 * ((1.0 - g2) / (2.0 + g2)) * (1.0 + fCos*fCos) / pow(1.0 + g2 - 2.0*g*fCos, 1.5); float fMiePhase = 1.5 * ((1.0 - g2) / (2.0 + g2)) * (1.0 + fCos*fCos) / pow(1.0 + g2 - 2.0*g*fCos, 1.5);
gl_FragColor.rgb = frontColor.rgb + fMiePhase * secondaryFrontColor.rgb; gl_FragColor.rgb = frontColor.rgb + fMiePhase * secondaryFrontColor.rgb;
gl_FragColor.a = gl_FragColor.b; gl_FragColor.a = gl_FragColor.b;
} }

View file

@ -50,6 +50,8 @@
using namespace std; using namespace std;
const bool TESTING_AVATAR_TOUCH = false;
// Starfield information // Starfield information
static char STAR_FILE[] = "https://s3-us-west-1.amazonaws.com/highfidelity/stars.txt"; static char STAR_FILE[] = "https://s3-us-west-1.amazonaws.com/highfidelity/stars.txt";
static char STAR_CACHE_FILE[] = "cachedStars.txt"; static char STAR_CACHE_FILE[] = "cachedStars.txt";
@ -287,22 +289,22 @@ void Application::paintGL() {
if (_myCamera.getMode() == CAMERA_MODE_MIRROR) { if (_myCamera.getMode() == CAMERA_MODE_MIRROR) {
_myCamera.setTightness (100.0f); _myCamera.setTightness (100.0f);
_myCamera.setTargetPosition(_myAvatar.getSpringyHeadPosition()); _myCamera.setTargetPosition(_myAvatar.getBallPosition(AVATAR_JOINT_HEAD_BASE));
_myCamera.setTargetRotation(_myAvatar.getWorldAlignedOrientation() * glm::quat(glm::vec3(0.0f, PI, 0.0f))); _myCamera.setTargetRotation(_myAvatar.getWorldAlignedOrientation() * glm::quat(glm::vec3(0.0f, PI, 0.0f)));
} else if (OculusManager::isConnected()) { } else if (OculusManager::isConnected()) {
_myCamera.setUpShift (0.0f); _myCamera.setUpShift (0.0f);
_myCamera.setDistance (0.0f); _myCamera.setDistance (0.0f);
_myCamera.setTightness (100.0f); _myCamera.setTightness (100.0f);
_myCamera.setTargetPosition(_myAvatar.getHeadPosition()); _myCamera.setTargetPosition(_myAvatar.getHeadJointPosition());
_myCamera.setTargetRotation(_myAvatar.getHead().getOrientation()); _myCamera.setTargetRotation(_myAvatar.getHead().getOrientation());
} else if (_myCamera.getMode() == CAMERA_MODE_FIRST_PERSON) { } else if (_myCamera.getMode() == CAMERA_MODE_FIRST_PERSON) {
_myCamera.setTargetPosition(_myAvatar.getSpringyHeadPosition()); _myCamera.setTargetPosition(_myAvatar.getBallPosition(AVATAR_JOINT_HEAD_BASE));
_myCamera.setTargetRotation(_myAvatar.getHead().getWorldAlignedOrientation()); _myCamera.setTargetRotation(_myAvatar.getHead().getWorldAlignedOrientation());
} else if (_myCamera.getMode() == CAMERA_MODE_THIRD_PERSON) { } else if (_myCamera.getMode() == CAMERA_MODE_THIRD_PERSON) {
_myCamera.setTargetPosition(_myAvatar.getHeadPosition()); _myCamera.setTargetPosition(_myAvatar.getHeadJointPosition());
_myCamera.setTargetRotation(_myAvatar.getHead().getWorldAlignedOrientation()); _myCamera.setTargetRotation(_myAvatar.getHead().getWorldAlignedOrientation());
} }
@ -1078,24 +1080,29 @@ void Application::idle() {
_myAvatar.simulate(deltaTime, NULL); _myAvatar.simulate(deltaTime, NULL);
} }
if (_myCamera.getMode() != CAMERA_MODE_MIRROR && !OculusManager::isConnected()) { if (TESTING_AVATAR_TOUCH) {
if (_manualFirstPerson) { if (_myCamera.getMode() != CAMERA_MODE_THIRD_PERSON) {
if (_myCamera.getMode() != CAMERA_MODE_FIRST_PERSON ) { _myCamera.setMode(CAMERA_MODE_THIRD_PERSON);
_myCamera.setMode(CAMERA_MODE_FIRST_PERSON); _myCamera.setModeShiftRate(1.0f);
_myCamera.setModeShiftRate(1.0f); }
} } else {
} else { if (_myCamera.getMode() != CAMERA_MODE_MIRROR && !OculusManager::isConnected()) {
if (_manualFirstPerson) {
if (_myAvatar.getIsNearInteractingOther()) { if (_myCamera.getMode() != CAMERA_MODE_FIRST_PERSON ) {
if (_myCamera.getMode() != CAMERA_MODE_FIRST_PERSON) {
_myCamera.setMode(CAMERA_MODE_FIRST_PERSON); _myCamera.setMode(CAMERA_MODE_FIRST_PERSON);
_myCamera.setModeShiftRate(1.0f); _myCamera.setModeShiftRate(1.0f);
} }
} } else {
else { if (_myAvatar.getIsNearInteractingOther()) {
if (_myCamera.getMode() != CAMERA_MODE_THIRD_PERSON) { if (_myCamera.getMode() != CAMERA_MODE_FIRST_PERSON) {
_myCamera.setMode(CAMERA_MODE_THIRD_PERSON); _myCamera.setMode(CAMERA_MODE_FIRST_PERSON);
_myCamera.setModeShiftRate(1.0f); _myCamera.setModeShiftRate(1.0f);
}
} else {
if (_myCamera.getMode() != CAMERA_MODE_THIRD_PERSON) {
_myCamera.setMode(CAMERA_MODE_THIRD_PERSON);
_myCamera.setModeShiftRate(1.0f);
}
} }
} }
} }
@ -1547,7 +1554,7 @@ void Application::loadViewFrustum(Camera& camera, ViewFrustum& viewFrustum) {
if (_cameraFrustum->isChecked()) { if (_cameraFrustum->isChecked()) {
position = camera.getPosition(); position = camera.getPosition();
} else { } else {
position = _myAvatar.getHeadPosition(); position = _myAvatar.getHeadJointPosition();
} }
float fov = camera.getFieldOfView(); float fov = camera.getFieldOfView();

View file

@ -143,7 +143,7 @@ int audioCallback (const void* inputBuffer,
unsigned char *currentPacketPtr = dataPacket + 1; unsigned char *currentPacketPtr = dataPacket + 1;
// memcpy the three float positions // memcpy the three float positions
memcpy(currentPacketPtr, &interfaceAvatar->getHeadPosition(), sizeof(float) * 3); memcpy(currentPacketPtr, &interfaceAvatar->getHeadJointPosition(), sizeof(float) * 3);
currentPacketPtr += (sizeof(float) * 3); currentPacketPtr += (sizeof(float) * 3);
// tell the mixer not to add additional attenuation to our source // tell the mixer not to add additional attenuation to our source

View file

@ -44,7 +44,7 @@ const float HEAD_MAX_YAW = 85;
const float HEAD_MIN_YAW = -85; const float HEAD_MIN_YAW = -85;
const float PERIPERSONAL_RADIUS = 1.0f; const float PERIPERSONAL_RADIUS = 1.0f;
const float AVATAR_BRAKING_STRENGTH = 40.0f; const float AVATAR_BRAKING_STRENGTH = 40.0f;
const float JOINT_TOUCH_RANGE = 0.01f; const float MOUSE_RAY_TOUCH_RANGE = 0.01f;
const float FLOATING_HEIGHT = 0.13f; const float FLOATING_HEIGHT = 0.13f;
const bool USING_HEAD_LEAN = false; const bool USING_HEAD_LEAN = false;
const float LEAN_SENSITIVITY = 0.15; const float LEAN_SENSITIVITY = 0.15;
@ -64,7 +64,8 @@ float chatMessageHeight = 0.20;
Avatar::Avatar(Agent* owningAgent) : Avatar::Avatar(Agent* owningAgent) :
AvatarData(owningAgent), AvatarData(owningAgent),
_head(this), _head(this),
_TEST_bigSphereRadius(0.4f), _ballSpringsInitialized(false),
_TEST_bigSphereRadius(0.5f),
_TEST_bigSpherePosition(5.0f, _TEST_bigSphereRadius, 5.0f), _TEST_bigSpherePosition(5.0f, _TEST_bigSphereRadius, 5.0f),
_mousePressed(false), _mousePressed(false),
_bodyPitchDelta(0.0f), _bodyPitchDelta(0.0f),
@ -101,10 +102,10 @@ Avatar::Avatar(Agent* owningAgent) :
initializeBodyBalls(); initializeBodyBalls();
_height = _skeleton.getHeight() + _bodyBall[ AVATAR_JOINT_LEFT_HEEL ].radius + _bodyBall[ AVATAR_JOINT_HEAD_BASE ].radius; _height = _skeleton.getHeight() + _bodyBall[ BODY_BALL_LEFT_HEEL ].radius + _bodyBall[ BODY_BALL_HEAD_BASE ].radius;
_maxArmLength = _skeleton.getArmLength(); _maxArmLength = _skeleton.getArmLength();
_pelvisStandingHeight = _skeleton.getPelvisStandingHeight() + _bodyBall[ AVATAR_JOINT_LEFT_HEEL ].radius; _pelvisStandingHeight = _skeleton.getPelvisStandingHeight() + _bodyBall[ BODY_BALL_LEFT_HEEL ].radius;
_pelvisFloatingHeight = _skeleton.getPelvisFloatingHeight() + _bodyBall[ AVATAR_JOINT_LEFT_HEEL ].radius; _pelvisFloatingHeight = _skeleton.getPelvisFloatingHeight() + _bodyBall[ BODY_BALL_LEFT_HEEL ].radius;
_avatarTouch.setReachableRadius(PERIPERSONAL_RADIUS); _avatarTouch.setReachableRadius(PERIPERSONAL_RADIUS);
@ -118,49 +119,142 @@ Avatar::Avatar(Agent* owningAgent) :
void Avatar::initializeBodyBalls() { void Avatar::initializeBodyBalls() {
for (int b=0; b<NUM_AVATAR_JOINTS; b++) { _ballSpringsInitialized = false; //this gets set to true on the first update pass...
_bodyBall[b].isCollidable = true;
for (int b = 0; b < NUM_AVATAR_BODY_BALLS; b++) {
_bodyBall[b].parentJoint = AVATAR_JOINT_NULL;
_bodyBall[b].parentOffset = glm::vec3(0.0, 0.0, 0.0);
_bodyBall[b].position = glm::vec3(0.0, 0.0, 0.0); _bodyBall[b].position = glm::vec3(0.0, 0.0, 0.0);
_bodyBall[b].velocity = glm::vec3(0.0, 0.0, 0.0); _bodyBall[b].velocity = glm::vec3(0.0, 0.0, 0.0);
_bodyBall[b].radius = 0.0; _bodyBall[b].radius = 0.0;
_bodyBall[b].touchForce = 0.0; _bodyBall[b].touchForce = 0.0;
_bodyBall[b].isCollidable = true;
_bodyBall[b].jointTightness = BODY_SPRING_DEFAULT_TIGHTNESS; _bodyBall[b].jointTightness = BODY_SPRING_DEFAULT_TIGHTNESS;
} }
// specify the radius of each ball
_bodyBall[ BODY_BALL_PELVIS ].radius = 0.07;
_bodyBall[ BODY_BALL_TORSO ].radius = 0.065;
_bodyBall[ BODY_BALL_CHEST ].radius = 0.08;
_bodyBall[ BODY_BALL_NECK_BASE ].radius = 0.03;
_bodyBall[ BODY_BALL_HEAD_BASE ].radius = 0.07;
_bodyBall[ BODY_BALL_LEFT_COLLAR ].radius = 0.04;
_bodyBall[ BODY_BALL_LEFT_SHOULDER ].radius = 0.03;
_bodyBall[ BODY_BALL_LEFT_ELBOW ].radius = 0.02;
_bodyBall[ BODY_BALL_LEFT_WRIST ].radius = 0.02;
_bodyBall[ BODY_BALL_LEFT_FINGERTIPS ].radius = 0.01;
_bodyBall[ BODY_BALL_RIGHT_COLLAR ].radius = 0.04;
_bodyBall[ BODY_BALL_RIGHT_SHOULDER ].radius = 0.03;
_bodyBall[ BODY_BALL_RIGHT_ELBOW ].radius = 0.02;
_bodyBall[ BODY_BALL_RIGHT_WRIST ].radius = 0.02;
_bodyBall[ BODY_BALL_RIGHT_FINGERTIPS ].radius = 0.01;
_bodyBall[ BODY_BALL_LEFT_HIP ].radius = 0.04;
//_bodyBall[ BODY_BALL_LEFT_MID_THIGH ].radius = 0.03;
_bodyBall[ BODY_BALL_LEFT_KNEE ].radius = 0.025;
_bodyBall[ BODY_BALL_LEFT_HEEL ].radius = 0.025;
_bodyBall[ BODY_BALL_LEFT_TOES ].radius = 0.025;
_bodyBall[ BODY_BALL_RIGHT_HIP ].radius = 0.04;
_bodyBall[ BODY_BALL_RIGHT_KNEE ].radius = 0.025;
_bodyBall[ BODY_BALL_RIGHT_HEEL ].radius = 0.025;
_bodyBall[ BODY_BALL_RIGHT_TOES ].radius = 0.025;
// specify the radii of the joints
_bodyBall[ AVATAR_JOINT_PELVIS ].radius = 0.07;
_bodyBall[ AVATAR_JOINT_TORSO ].radius = 0.065;
_bodyBall[ AVATAR_JOINT_CHEST ].radius = 0.08;
_bodyBall[ AVATAR_JOINT_NECK_BASE ].radius = 0.03;
_bodyBall[ AVATAR_JOINT_HEAD_BASE ].radius = 0.07;
_bodyBall[ AVATAR_JOINT_LEFT_COLLAR ].radius = 0.04; // specify the parent joint for each ball
_bodyBall[ AVATAR_JOINT_LEFT_SHOULDER ].radius = 0.03; _bodyBall[ BODY_BALL_PELVIS ].parentJoint = AVATAR_JOINT_PELVIS;
_bodyBall[ AVATAR_JOINT_LEFT_ELBOW ].radius = 0.02; _bodyBall[ BODY_BALL_TORSO ].parentJoint = AVATAR_JOINT_TORSO;
_bodyBall[ AVATAR_JOINT_LEFT_WRIST ].radius = 0.02; _bodyBall[ BODY_BALL_CHEST ].parentJoint = AVATAR_JOINT_CHEST;
_bodyBall[ AVATAR_JOINT_LEFT_FINGERTIPS ].radius = 0.01; _bodyBall[ BODY_BALL_NECK_BASE ].parentJoint = AVATAR_JOINT_NECK_BASE;
_bodyBall[ BODY_BALL_HEAD_BASE ].parentJoint = AVATAR_JOINT_HEAD_BASE;
_bodyBall[ BODY_BALL_HEAD_TOP ].parentJoint = AVATAR_JOINT_HEAD_TOP;
_bodyBall[ BODY_BALL_LEFT_COLLAR ].parentJoint = AVATAR_JOINT_LEFT_COLLAR;
_bodyBall[ BODY_BALL_LEFT_SHOULDER ].parentJoint = AVATAR_JOINT_LEFT_SHOULDER;
_bodyBall[ BODY_BALL_LEFT_ELBOW ].parentJoint = AVATAR_JOINT_LEFT_ELBOW;
_bodyBall[ BODY_BALL_LEFT_WRIST ].parentJoint = AVATAR_JOINT_LEFT_WRIST;
_bodyBall[ BODY_BALL_LEFT_FINGERTIPS ].parentJoint = AVATAR_JOINT_LEFT_FINGERTIPS;
_bodyBall[ BODY_BALL_RIGHT_COLLAR ].parentJoint = AVATAR_JOINT_RIGHT_COLLAR;
_bodyBall[ BODY_BALL_RIGHT_SHOULDER ].parentJoint = AVATAR_JOINT_RIGHT_SHOULDER;
_bodyBall[ BODY_BALL_RIGHT_ELBOW ].parentJoint = AVATAR_JOINT_RIGHT_ELBOW;
_bodyBall[ BODY_BALL_RIGHT_WRIST ].parentJoint = AVATAR_JOINT_RIGHT_WRIST;
_bodyBall[ BODY_BALL_RIGHT_FINGERTIPS ].parentJoint = AVATAR_JOINT_RIGHT_FINGERTIPS;
_bodyBall[ BODY_BALL_LEFT_HIP ].parentJoint = AVATAR_JOINT_LEFT_HIP;
_bodyBall[ BODY_BALL_LEFT_KNEE ].parentJoint = AVATAR_JOINT_LEFT_KNEE;
_bodyBall[ BODY_BALL_LEFT_HEEL ].parentJoint = AVATAR_JOINT_LEFT_HEEL;
_bodyBall[ BODY_BALL_LEFT_TOES ].parentJoint = AVATAR_JOINT_LEFT_TOES;
_bodyBall[ BODY_BALL_RIGHT_HIP ].parentJoint = AVATAR_JOINT_RIGHT_HIP;
_bodyBall[ BODY_BALL_RIGHT_KNEE ].parentJoint = AVATAR_JOINT_RIGHT_KNEE;
_bodyBall[ BODY_BALL_RIGHT_HEEL ].parentJoint = AVATAR_JOINT_RIGHT_HEEL;
_bodyBall[ BODY_BALL_RIGHT_TOES ].parentJoint = AVATAR_JOINT_RIGHT_TOES;
_bodyBall[ AVATAR_JOINT_RIGHT_COLLAR ].radius = 0.04; //_bodyBall[ BODY_BALL_LEFT_MID_THIGH].parentJoint = AVATAR_JOINT_LEFT_HIP;
_bodyBall[ AVATAR_JOINT_RIGHT_SHOULDER ].radius = 0.03;
_bodyBall[ AVATAR_JOINT_RIGHT_ELBOW ].radius = 0.02;
_bodyBall[ AVATAR_JOINT_RIGHT_WRIST ].radius = 0.02;
_bodyBall[ AVATAR_JOINT_RIGHT_FINGERTIPS ].radius = 0.01;
_bodyBall[ AVATAR_JOINT_LEFT_HIP ].radius = 0.04; // specify the parent offset for each ball
_bodyBall[ AVATAR_JOINT_LEFT_KNEE ].radius = 0.025; _bodyBall[ BODY_BALL_PELVIS ].parentOffset = glm::vec3(0.0, 0.0, 0.0);
_bodyBall[ AVATAR_JOINT_LEFT_HEEL ].radius = 0.025; _bodyBall[ BODY_BALL_TORSO ].parentOffset = glm::vec3(0.0, 0.0, 0.0);
_bodyBall[ AVATAR_JOINT_LEFT_TOES ].radius = 0.025; _bodyBall[ BODY_BALL_CHEST ].parentOffset = glm::vec3(0.0, 0.0, 0.0);
_bodyBall[ BODY_BALL_NECK_BASE ].parentOffset = glm::vec3(0.0, 0.0, 0.0);
_bodyBall[ BODY_BALL_HEAD_BASE ].parentOffset = glm::vec3(0.0, 0.0, 0.0);
_bodyBall[ BODY_BALL_HEAD_TOP ].parentOffset = glm::vec3(0.0, 0.0, 0.0);
_bodyBall[ BODY_BALL_LEFT_COLLAR ].parentOffset = glm::vec3(0.0, 0.0, 0.0);
_bodyBall[ BODY_BALL_LEFT_SHOULDER ].parentOffset = glm::vec3(0.0, 0.0, 0.0);
_bodyBall[ BODY_BALL_LEFT_ELBOW ].parentOffset = glm::vec3(0.0, 0.0, 0.0);
_bodyBall[ BODY_BALL_LEFT_WRIST ].parentOffset = glm::vec3(0.0, 0.0, 0.0);
_bodyBall[ BODY_BALL_LEFT_FINGERTIPS ].parentOffset = glm::vec3(0.0, 0.0, 0.0);
_bodyBall[ BODY_BALL_RIGHT_COLLAR ].parentOffset = glm::vec3(0.0, 0.0, 0.0);
_bodyBall[ BODY_BALL_RIGHT_SHOULDER ].parentOffset = glm::vec3(0.0, 0.0, 0.0);
_bodyBall[ BODY_BALL_RIGHT_ELBOW ].parentOffset = glm::vec3(0.0, 0.0, 0.0);
_bodyBall[ BODY_BALL_RIGHT_WRIST ].parentOffset = glm::vec3(0.0, 0.0, 0.0);
_bodyBall[ BODY_BALL_RIGHT_FINGERTIPS ].parentOffset = glm::vec3(0.0, 0.0, 0.0);
_bodyBall[ BODY_BALL_LEFT_HIP ].parentOffset = glm::vec3(0.0, 0.0, 0.0);
_bodyBall[ BODY_BALL_LEFT_KNEE ].parentOffset = glm::vec3(0.0, 0.0, 0.0);
_bodyBall[ BODY_BALL_LEFT_HEEL ].parentOffset = glm::vec3(0.0, 0.0, 0.0);
_bodyBall[ BODY_BALL_LEFT_TOES ].parentOffset = glm::vec3(0.0, 0.0, 0.0);
_bodyBall[ BODY_BALL_RIGHT_HIP ].parentOffset = glm::vec3(0.0, 0.0, 0.0);
_bodyBall[ BODY_BALL_RIGHT_KNEE ].parentOffset = glm::vec3(0.0, 0.0, 0.0);
_bodyBall[ BODY_BALL_RIGHT_HEEL ].parentOffset = glm::vec3(0.0, 0.0, 0.0);
_bodyBall[ BODY_BALL_RIGHT_TOES ].parentOffset = glm::vec3(0.0, 0.0, 0.0);
_bodyBall[ AVATAR_JOINT_RIGHT_HIP ].radius = 0.04; //_bodyBall[ BODY_BALL_LEFT_MID_THIGH].parentOffset = glm::vec3(-0.1, -0.1, 0.0);
_bodyBall[ AVATAR_JOINT_RIGHT_KNEE ].radius = 0.025;
_bodyBall[ AVATAR_JOINT_RIGHT_HEEL ].radius = 0.025;
_bodyBall[ AVATAR_JOINT_RIGHT_TOES ].radius = 0.025; // specify the parent BALL for each ball
_bodyBall[ BODY_BALL_PELVIS ].parentBall = BODY_BALL_NULL;
_bodyBall[ BODY_BALL_TORSO ].parentBall = BODY_BALL_PELVIS;
_bodyBall[ BODY_BALL_CHEST ].parentBall = BODY_BALL_TORSO;
_bodyBall[ BODY_BALL_NECK_BASE ].parentBall = BODY_BALL_CHEST;
_bodyBall[ BODY_BALL_HEAD_BASE ].parentBall = BODY_BALL_NECK_BASE;
_bodyBall[ BODY_BALL_HEAD_TOP ].parentBall = BODY_BALL_HEAD_BASE;
_bodyBall[ BODY_BALL_LEFT_COLLAR ].parentBall = BODY_BALL_CHEST;
_bodyBall[ BODY_BALL_LEFT_SHOULDER ].parentBall = BODY_BALL_LEFT_COLLAR;
_bodyBall[ BODY_BALL_LEFT_ELBOW ].parentBall = BODY_BALL_LEFT_SHOULDER;
_bodyBall[ BODY_BALL_LEFT_WRIST ].parentBall = BODY_BALL_LEFT_ELBOW;
_bodyBall[ BODY_BALL_LEFT_FINGERTIPS ].parentBall = BODY_BALL_LEFT_WRIST;
_bodyBall[ BODY_BALL_RIGHT_COLLAR ].parentBall = BODY_BALL_CHEST;
_bodyBall[ BODY_BALL_RIGHT_SHOULDER ].parentBall = BODY_BALL_RIGHT_COLLAR;
_bodyBall[ BODY_BALL_RIGHT_ELBOW ].parentBall = BODY_BALL_RIGHT_SHOULDER;
_bodyBall[ BODY_BALL_RIGHT_WRIST ].parentBall = BODY_BALL_RIGHT_ELBOW;
_bodyBall[ BODY_BALL_RIGHT_FINGERTIPS ].parentBall = BODY_BALL_RIGHT_WRIST;
_bodyBall[ BODY_BALL_LEFT_HIP ].parentBall = BODY_BALL_PELVIS;
//_bodyBall[ BODY_BALL_LEFT_MID_THIGH ].parentBall = BODY_BALL_LEFT_HIP;
// _bodyBall[ BODY_BALL_LEFT_KNEE ].parentBall = BODY_BALL_LEFT_MID_THIGH;
_bodyBall[ BODY_BALL_LEFT_KNEE ].parentBall = BODY_BALL_LEFT_HIP;
_bodyBall[ BODY_BALL_LEFT_HEEL ].parentBall = BODY_BALL_LEFT_KNEE;
_bodyBall[ BODY_BALL_LEFT_TOES ].parentBall = BODY_BALL_LEFT_HEEL;
_bodyBall[ BODY_BALL_RIGHT_HIP ].parentBall = BODY_BALL_PELVIS;
_bodyBall[ BODY_BALL_RIGHT_KNEE ].parentBall = BODY_BALL_RIGHT_HIP;
_bodyBall[ BODY_BALL_RIGHT_HEEL ].parentBall = BODY_BALL_RIGHT_KNEE;
_bodyBall[ BODY_BALL_RIGHT_TOES ].parentBall = BODY_BALL_RIGHT_HEEL;
/* /*
// to aid in hand-shaking and hand-holding, the right hand is not collidable // to aid in hand-shaking and hand-holding, the right hand is not collidable
_bodyBall[ AVATAR_JOINT_RIGHT_ELBOW ].isCollidable = false; _bodyBall[ BODY_BALL_RIGHT_ELBOW ].isCollidable = false;
_bodyBall[ AVATAR_JOINT_RIGHT_WRIST ].isCollidable = false; _bodyBall[ BODY_BALL_RIGHT_WRIST ].isCollidable = false;
_bodyBall[ AVATAR_JOINT_RIGHT_FINGERTIPS].isCollidable = false; _bodyBall[ BODY_BALL_RIGHT_FINGERTIPS].isCollidable = false;
*/ */
} }
@ -275,6 +369,25 @@ void Avatar::simulate(float deltaTime, Transmitter* transmitter) {
// update avatar skeleton // update avatar skeleton
_skeleton.update(deltaTime, getOrientation(), _position); _skeleton.update(deltaTime, getOrientation(), _position);
//determine the lengths of the body springs now that we have updated the skeleton at least once
if (!_ballSpringsInitialized) {
for (int b = 0; b < NUM_AVATAR_BODY_BALLS; b++) {
glm::vec3 targetPosition
= _skeleton.joint[_bodyBall[b].parentJoint].position
+ _skeleton.joint[_bodyBall[b].parentJoint].rotation * _bodyBall[b].parentOffset;
glm::vec3 parentTargetPosition
= _skeleton.joint[_bodyBall[b].parentJoint].position
+ _skeleton.joint[_bodyBall[b].parentJoint].rotation * _bodyBall[b].parentOffset;
_bodyBall[b].springLength = glm::length(targetPosition - parentTargetPosition);
}
_ballSpringsInitialized = true;
}
// if this is not my avatar, then hand position comes from transmitted data // if this is not my avatar, then hand position comes from transmitted data
if (_owningAgent) { if (_owningAgent) {
_skeleton.joint[ AVATAR_JOINT_RIGHT_FINGERTIPS ].position = _handPosition; _skeleton.joint[ AVATAR_JOINT_RIGHT_FINGERTIPS ].position = _handPosition;
@ -456,22 +569,22 @@ void Avatar::simulate(float deltaTime, Transmitter* transmitter) {
right * _head.getLeanSideways() + right * _head.getLeanSideways() +
front * _head.getLeanForward(); front * _head.getLeanForward();
_bodyBall[ AVATAR_JOINT_TORSO ].position += headLean * 0.1f; _bodyBall[ BODY_BALL_TORSO ].position += headLean * 0.1f;
_bodyBall[ AVATAR_JOINT_CHEST ].position += headLean * 0.4f; _bodyBall[ BODY_BALL_CHEST ].position += headLean * 0.4f;
_bodyBall[ AVATAR_JOINT_NECK_BASE ].position += headLean * 0.7f; _bodyBall[ BODY_BALL_NECK_BASE ].position += headLean * 0.7f;
_bodyBall[ AVATAR_JOINT_HEAD_BASE ].position += headLean * 1.0f; _bodyBall[ BODY_BALL_HEAD_BASE ].position += headLean * 1.0f;
_bodyBall[ AVATAR_JOINT_LEFT_COLLAR ].position += headLean * 0.6f; _bodyBall[ BODY_BALL_LEFT_COLLAR ].position += headLean * 0.6f;
_bodyBall[ AVATAR_JOINT_LEFT_SHOULDER ].position += headLean * 0.6f; _bodyBall[ BODY_BALL_LEFT_SHOULDER ].position += headLean * 0.6f;
_bodyBall[ AVATAR_JOINT_LEFT_ELBOW ].position += headLean * 0.2f; _bodyBall[ BODY_BALL_LEFT_ELBOW ].position += headLean * 0.2f;
_bodyBall[ AVATAR_JOINT_LEFT_WRIST ].position += headLean * 0.1f; _bodyBall[ BODY_BALL_LEFT_WRIST ].position += headLean * 0.1f;
_bodyBall[ AVATAR_JOINT_LEFT_FINGERTIPS ].position += headLean * 0.0f; _bodyBall[ BODY_BALL_LEFT_FINGERTIPS ].position += headLean * 0.0f;
_bodyBall[ AVATAR_JOINT_RIGHT_COLLAR ].position += headLean * 0.6f; _bodyBall[ BODY_BALL_RIGHT_COLLAR ].position += headLean * 0.6f;
_bodyBall[ AVATAR_JOINT_RIGHT_SHOULDER ].position += headLean * 0.6f; _bodyBall[ BODY_BALL_RIGHT_SHOULDER ].position += headLean * 0.6f;
_bodyBall[ AVATAR_JOINT_RIGHT_ELBOW ].position += headLean * 0.2f; _bodyBall[ BODY_BALL_RIGHT_ELBOW ].position += headLean * 0.2f;
_bodyBall[ AVATAR_JOINT_RIGHT_WRIST ].position += headLean * 0.1f; _bodyBall[ BODY_BALL_RIGHT_WRIST ].position += headLean * 0.1f;
_bodyBall[ AVATAR_JOINT_RIGHT_FINGERTIPS ].position += headLean * 0.0f; _bodyBall[ BODY_BALL_RIGHT_FINGERTIPS ].position += headLean * 0.0f;
} }
} }
@ -485,8 +598,8 @@ void Avatar::simulate(float deltaTime, Transmitter* transmitter) {
} }
_head.setBodyRotation (glm::vec3(_bodyPitch, _bodyYaw, _bodyRoll)); _head.setBodyRotation (glm::vec3(_bodyPitch, _bodyYaw, _bodyRoll));
_head.setPosition(_bodyBall[ AVATAR_JOINT_HEAD_BASE ].position); _head.setPosition(_bodyBall[ BODY_BALL_HEAD_BASE ].position);
_head.setScale (_bodyBall[ AVATAR_JOINT_HEAD_BASE ].radius); _head.setScale (_bodyBall[ BODY_BALL_HEAD_BASE ].radius);
_head.setSkinColor(glm::vec3(SKIN_COLOR[0], SKIN_COLOR[1], SKIN_COLOR[2])); _head.setSkinColor(glm::vec3(SKIN_COLOR[0], SKIN_COLOR[1], SKIN_COLOR[2]));
_head.simulate(deltaTime, !_owningAgent); _head.simulate(deltaTime, !_owningAgent);
@ -500,12 +613,12 @@ void Avatar::simulate(float deltaTime, Transmitter* transmitter) {
void Avatar::checkForMouseRayTouching() { void Avatar::checkForMouseRayTouching() {
for (int b = 0; b < NUM_AVATAR_JOINTS; b++) { for (int b = 0; b < NUM_AVATAR_BODY_BALLS; b++) {
glm::vec3 directionToBodySphere = glm::normalize(_bodyBall[b].position - _mouseRayOrigin); glm::vec3 directionToBodySphere = glm::normalize(_bodyBall[b].position - _mouseRayOrigin);
float dot = glm::dot(directionToBodySphere, _mouseRayDirection); float dot = glm::dot(directionToBodySphere, _mouseRayDirection);
float range = _bodyBall[b].radius * JOINT_TOUCH_RANGE; float range = _bodyBall[b].radius * MOUSE_RAY_TOUCH_RANGE;
if (dot > (1.0f - range)) { if (dot > (1.0f - range)) {
_bodyBall[b].touchForce = (dot - (1.0f - range)) / range; _bodyBall[b].touchForce = (dot - (1.0f - range)) / range;
@ -557,11 +670,6 @@ void Avatar::updateHandMovementAndTouching(float deltaTime) {
if (agent->getLinkedData() != NULL && agent->getType() == AGENT_TYPE_AVATAR) { if (agent->getLinkedData() != NULL && agent->getType() == AGENT_TYPE_AVATAR) {
Avatar *otherAvatar = (Avatar *)agent->getLinkedData(); Avatar *otherAvatar = (Avatar *)agent->getLinkedData();
//Test: Show angle between your fwd vector and nearest avatar
//glm::vec3 vectorBetweenUs = otherAvatar->getJointPosition(AVATAR_JOINT_PELVIS) -
// getJointPosition(AVATAR_JOINT_PELVIS);
//printLog("Angle between: %f\n", angleBetween(vectorBetweenUs, getBodyFrontDirection()));
// test whether shoulders are close enough to allow for reaching to touch hands // test whether shoulders are close enough to allow for reaching to touch hands
glm::vec3 v(_position - otherAvatar->_position); glm::vec3 v(_position - otherAvatar->_position);
float distance = glm::length(v); float distance = glm::length(v);
@ -579,7 +687,7 @@ void Avatar::updateHandMovementAndTouching(float deltaTime) {
_avatarTouch.setHasInteractingOther(true); _avatarTouch.setHasInteractingOther(true);
_avatarTouch.setYourBodyPosition(_interactingOther->_position); _avatarTouch.setYourBodyPosition(_interactingOther->_position);
_avatarTouch.setYourHandPosition(_interactingOther->_bodyBall[ AVATAR_JOINT_RIGHT_FINGERTIPS ].position); _avatarTouch.setYourHandPosition(_interactingOther->_bodyBall[ BODY_BALL_RIGHT_FINGERTIPS ].position);
_avatarTouch.setYourOrientation (_interactingOther->getOrientation()); _avatarTouch.setYourOrientation (_interactingOther->getOrientation());
_avatarTouch.setYourHandState (_interactingOther->_handState); _avatarTouch.setYourHandState (_interactingOther->_handState);
@ -650,32 +758,29 @@ void Avatar::updateHandMovementAndTouching(float deltaTime) {
} }
_avatarTouch.setMyHandState(_handState); _avatarTouch.setMyHandState(_handState);
_avatarTouch.setMyHandPosition(_bodyBall[ AVATAR_JOINT_RIGHT_FINGERTIPS ].position); _avatarTouch.setMyHandPosition(_bodyBall[ BODY_BALL_RIGHT_FINGERTIPS ].position);
} }
} }
void Avatar::updateCollisionWithSphere(glm::vec3 position, float radius, float deltaTime) { void Avatar::updateCollisionWithSphere(glm::vec3 position, float radius, float deltaTime) {
float myBodyApproximateBoundingRadius = 1.0f; float myBodyApproximateBoundingRadius = 1.0f;
glm::vec3 vectorFromMyBodyToBigSphere(_position - position); glm::vec3 vectorFromMyBodyToBigSphere(_position - position);
bool jointCollision = false;
float distanceToBigSphere = glm::length(vectorFromMyBodyToBigSphere); float distanceToBigSphere = glm::length(vectorFromMyBodyToBigSphere);
if (distanceToBigSphere < myBodyApproximateBoundingRadius + radius) { if (distanceToBigSphere < myBodyApproximateBoundingRadius + radius) {
for (int b = 0; b < NUM_AVATAR_JOINTS; b++) { for (int b = 0; b < NUM_AVATAR_BODY_BALLS; b++) {
glm::vec3 vectorFromJointToBigSphereCenter(_bodyBall[b].position - position); glm::vec3 vectorFromBallToBigSphereCenter(_bodyBall[b].position - position);
float distanceToBigSphereCenter = glm::length(vectorFromJointToBigSphereCenter); float distanceToBigSphereCenter = glm::length(vectorFromBallToBigSphereCenter);
float combinedRadius = _bodyBall[b].radius + radius; float combinedRadius = _bodyBall[b].radius + radius;
if (distanceToBigSphereCenter < combinedRadius) { if (distanceToBigSphereCenter < combinedRadius) {
jointCollision = true;
if (distanceToBigSphereCenter > 0.0) { if (distanceToBigSphereCenter > 0.0) {
glm::vec3 directionVector = vectorFromJointToBigSphereCenter / distanceToBigSphereCenter; glm::vec3 directionVector = vectorFromBallToBigSphereCenter / distanceToBigSphereCenter;
float penetration = 1.0 - (distanceToBigSphereCenter / combinedRadius); float penetration = 1.0 - (distanceToBigSphereCenter / combinedRadius);
glm::vec3 collisionForce = vectorFromJointToBigSphereCenter * penetration; glm::vec3 collisionForce = vectorFromBallToBigSphereCenter * penetration;
_bodyBall[b].velocity += collisionForce * 0.0f * deltaTime; _velocity += collisionForce * 40.0f * deltaTime;
_velocity += collisionForce * 40.0f * deltaTime;
_bodyBall[b].position = position + directionVector * combinedRadius; _bodyBall[b].position = position + directionVector * combinedRadius;
} }
} }
@ -729,7 +834,7 @@ void Avatar::updateAvatarCollisions(float deltaTime) {
// Reset detector for nearest avatar // Reset detector for nearest avatar
_distanceToNearestAvatar = std::numeric_limits<float>::max(); _distanceToNearestAvatar = std::numeric_limits<float>::max();
//loop through all the other avatars for potential interactions... // loop through all the other avatars for potential interactions...
AgentList* agentList = AgentList::getInstance(); AgentList* agentList = AgentList::getInstance();
for (AgentList::iterator agent = agentList->begin(); agent != agentList->end(); agent++) { for (AgentList::iterator agent = agentList->begin(); agent != agentList->end(); agent++) {
if (agent->getLinkedData() != NULL && agent->getType() == AGENT_TYPE_AVATAR) { if (agent->getLinkedData() != NULL && agent->getType() == AGENT_TYPE_AVATAR) {
@ -739,7 +844,7 @@ void Avatar::updateAvatarCollisions(float deltaTime) {
glm::vec3 vectorBetweenBoundingSpheres(_position - otherAvatar->_position); glm::vec3 vectorBetweenBoundingSpheres(_position - otherAvatar->_position);
if (glm::length(vectorBetweenBoundingSpheres) < _height * ONE_HALF + otherAvatar->_height * ONE_HALF) { if (glm::length(vectorBetweenBoundingSpheres) < _height * ONE_HALF + otherAvatar->_height * ONE_HALF) {
//apply forces from collision // apply forces from collision
applyCollisionWithOtherAvatar(otherAvatar, deltaTime); applyCollisionWithOtherAvatar(otherAvatar, deltaTime);
} }
@ -755,30 +860,30 @@ void Avatar::updateAvatarCollisions(float deltaTime) {
} }
} }
//detect collisions with other avatars and respond // detect collisions with other avatars and respond
void Avatar::applyCollisionWithOtherAvatar(Avatar * otherAvatar, float deltaTime) { void Avatar::applyCollisionWithOtherAvatar(Avatar * otherAvatar, float deltaTime) {
glm::vec3 bodyPushForce = glm::vec3(0.0f, 0.0f, 0.0f); glm::vec3 bodyPushForce = glm::vec3(0.0f, 0.0f, 0.0f);
// loop through the joints of each avatar to check for every possible collision // loop through the body balls of each avatar to check for every possible collision
for (int b=1; b<NUM_AVATAR_JOINTS; b++) { for (int b = 1; b < NUM_AVATAR_BODY_BALLS; b++) {
if (_bodyBall[b].isCollidable) { if (_bodyBall[b].isCollidable) {
for (int o=b+1; o<NUM_AVATAR_JOINTS; o++) { for (int o = b+1; o < NUM_AVATAR_BODY_BALLS; o++) {
if (otherAvatar->_bodyBall[o].isCollidable) { if (otherAvatar->_bodyBall[o].isCollidable) {
glm::vec3 vectorBetweenJoints(_bodyBall[b].position - otherAvatar->_bodyBall[o].position); glm::vec3 vectorBetweenBalls(_bodyBall[b].position - otherAvatar->_bodyBall[o].position);
float distanceBetweenJoints = glm::length(vectorBetweenJoints); float distanceBetweenBalls = glm::length(vectorBetweenBalls);
if (distanceBetweenJoints > 0.0) { // to avoid divide by zero if (distanceBetweenBalls > 0.0) { // to avoid divide by zero
float combinedRadius = _bodyBall[b].radius + otherAvatar->_bodyBall[o].radius; float combinedRadius = _bodyBall[b].radius + otherAvatar->_bodyBall[o].radius;
// check for collision // check for collision
if (distanceBetweenJoints < combinedRadius * COLLISION_RADIUS_SCALAR) { if (distanceBetweenBalls < combinedRadius * COLLISION_RADIUS_SCALAR) {
glm::vec3 directionVector = vectorBetweenJoints / distanceBetweenJoints; glm::vec3 directionVector = vectorBetweenBalls / distanceBetweenBalls;
// push balls away from each other and apply friction // push balls away from each other and apply friction
float penetration = 1.0f - (distanceBetweenJoints / (combinedRadius * COLLISION_RADIUS_SCALAR)); float penetration = 1.0f - (distanceBetweenBalls / (combinedRadius * COLLISION_RADIUS_SCALAR));
glm::vec3 ballPushForce = directionVector * COLLISION_BALL_FORCE * penetration * deltaTime; glm::vec3 ballPushForce = directionVector * COLLISION_BALL_FORCE * penetration * deltaTime;
bodyPushForce += directionVector * COLLISION_BODY_FORCE * penetration * deltaTime; bodyPushForce += directionVector * COLLISION_BODY_FORCE * penetration * deltaTime;
@ -793,7 +898,7 @@ void Avatar::applyCollisionWithOtherAvatar(Avatar * otherAvatar, float deltaTime
} // b loop } // b loop
} // collidable } // collidable
//apply force on the whole body // apply force on the whole body
_velocity += bodyPushForce; _velocity += bodyPushForce;
} }
@ -833,7 +938,7 @@ void Avatar::render(bool lookingInMirror) {
// render a simple round on the ground projected down from the avatar's position // render a simple round on the ground projected down from the avatar's position
renderDiskShadow(_position, glm::vec3(0.0f, 1.0f, 0.0f), 0.1f, 0.2f); renderDiskShadow(_position, glm::vec3(0.0f, 1.0f, 0.0f), 0.1f, 0.2f);
//render body // render body
renderBody(lookingInMirror); renderBody(lookingInMirror);
// if this is my avatar, then render my interactions with the other avatar // if this is my avatar, then render my interactions with the other avatar
@ -857,7 +962,7 @@ void Avatar::render(bool lookingInMirror) {
} }
glPushMatrix(); glPushMatrix();
glm::vec3 chatPosition = _bodyBall[AVATAR_JOINT_HEAD_BASE].position + getBodyUpDirection() * chatMessageHeight; glm::vec3 chatPosition = _bodyBall[BODY_BALL_HEAD_BASE].position + getBodyUpDirection() * chatMessageHeight;
glTranslatef(chatPosition.x, chatPosition.y, chatPosition.z); glTranslatef(chatPosition.x, chatPosition.y, chatPosition.z);
glm::quat chatRotation = Application::getInstance()->getCamera()->getRotation(); glm::quat chatRotation = Application::getInstance()->getCamera()->getRotation();
glm::vec3 chatAxis = glm::axis(chatRotation); glm::vec3 chatAxis = glm::axis(chatRotation);
@ -893,61 +998,73 @@ void Avatar::render(bool lookingInMirror) {
} }
void Avatar::resetBodyBalls() { void Avatar::resetBodyBalls() {
for (int b = 0; b < NUM_AVATAR_JOINTS; b++) { for (int b = 0; b < NUM_AVATAR_BODY_BALLS; b++) {
_bodyBall[b].position = _skeleton.joint[b].position;
glm::vec3 targetPosition
= _skeleton.joint[_bodyBall[b].parentJoint].position
+ _skeleton.joint[_bodyBall[b].parentJoint].rotation * _bodyBall[b].parentOffset;
_bodyBall[b].position = targetPosition; // put ball on target position
_bodyBall[b].velocity = glm::vec3(0.0f, 0.0f, 0.0f); _bodyBall[b].velocity = glm::vec3(0.0f, 0.0f, 0.0f);
} }
} }
void Avatar::updateBodyBalls(float deltaTime) { void Avatar::updateBodyBalls(float deltaTime) {
// Check for a large repositioning, and re-initialize balls if this has happened // Check for a large repositioning, and re-initialize balls if this has happened
const float BEYOND_BODY_SPRING_RANGE = 2.f; const float BEYOND_BODY_SPRING_RANGE = 2.f;
if (glm::length(_position - _bodyBall[AVATAR_JOINT_PELVIS].position) > BEYOND_BODY_SPRING_RANGE) { if (glm::length(_position - _bodyBall[BODY_BALL_PELVIS].position) > BEYOND_BODY_SPRING_RANGE) {
resetBodyBalls(); resetBodyBalls();
} }
for (int b = 0; b < NUM_AVATAR_JOINTS; b++) { for (int b = 0; b < NUM_AVATAR_BODY_BALLS; b++) {
glm::vec3 springVector(_bodyBall[b].position);
if (_ballSpringsInitialized) {
if (_skeleton.joint[b].parent == AVATAR_JOINT_NULL) {
springVector -= _position; // apply spring forces
} glm::vec3 springVector(_bodyBall[b].position);
else {
springVector -= _bodyBall[ _skeleton.joint[b].parent ].position;
}
float length = glm::length(springVector);
if (length > 0.0f) { // to avoid divide by zero
glm::vec3 springDirection = springVector / length;
float force = (length - _skeleton.joint[b].length) * BODY_SPRING_FORCE * deltaTime;
_bodyBall[b].velocity -= springDirection * force;
if (_skeleton.joint[b].parent != AVATAR_JOINT_NULL) { if (b == BODY_BALL_PELVIS) {
_bodyBall[_skeleton.joint[b].parent].velocity += springDirection * force; springVector -= _position;
} else {
springVector -= _bodyBall[_bodyBall[b].parentBall].position;
} }
float length = glm::length(springVector);
if (length > 0.0f) { // to avoid divide by zero
glm::vec3 springDirection = springVector / length;
float force = (length - _skeleton.joint[b].length) * BODY_SPRING_FORCE * deltaTime;
_bodyBall[b].velocity -= springDirection * force;
if (_bodyBall[b].parentBall != BODY_BALL_NULL) {
_bodyBall[_bodyBall[b].parentBall].velocity += springDirection * force;
}
}
} }
// apply tightness force - (causing ball position to be close to skeleton joint position) // apply tightness force - (causing ball position to be close to skeleton joint position)
_bodyBall[b].velocity += (_skeleton.joint[b].position - _bodyBall[b].position) * _bodyBall[b].jointTightness * deltaTime; glm::vec3 targetPosition
= _skeleton.joint[_bodyBall[b].parentJoint].position
+ _skeleton.joint[_bodyBall[b].parentJoint].rotation * _bodyBall[b].parentOffset;
_bodyBall[b].velocity += (targetPosition - _bodyBall[b].position) * _bodyBall[b].jointTightness * deltaTime;
// apply decay // apply decay
float decay = 1.0 - BODY_SPRING_DECAY * deltaTime; float decay = 1.0 - BODY_SPRING_DECAY * deltaTime;
if (decay > 0.0) { if (decay > 0.0) {
_bodyBall[b].velocity *= decay; _bodyBall[b].velocity *= decay;
} } else {
else {
_bodyBall[b].velocity = glm::vec3(0.0f, 0.0f, 0.0f); _bodyBall[b].velocity = glm::vec3(0.0f, 0.0f, 0.0f);
} }
/* /*
//apply forces from touch... // apply forces from touch...
if (_skeleton.joint[b].touchForce > 0.0) { if (_bodyBall[b].touchForce > 0.0) {
_skeleton.joint[b].springyVelocity += _mouseRayDirection * _skeleton.joint[b].touchForce * 0.7f; _bodyBall[b].velocity += _mouseRayDirection * _bodyBall[b].touchForce * 0.7f;
} }
*/ */
//update position by velocity... // update position by velocity...
_bodyBall[b].position += _bodyBall[b].velocity * deltaTime; _bodyBall[b].position += _bodyBall[b].velocity * deltaTime;
} }
} }
@ -1010,8 +1127,8 @@ void Avatar::renderBody(bool lookingInMirror) {
const float RENDER_TRANSLUCENT_BEYOND = 0.5f; const float RENDER_TRANSLUCENT_BEYOND = 0.5f;
// Render the body as balls and cones // Render the body as balls and cones
for (int b = 0; b < NUM_AVATAR_JOINTS; b++) { for (int b = 0; b < NUM_AVATAR_BODY_BALLS; b++) {
float distanceToCamera = glm::length(_cameraPosition - _skeleton.joint[b].position); float distanceToCamera = glm::length(_cameraPosition - _bodyBall[b].position);
float alpha = lookingInMirror ? 1.0f : glm::clamp((distanceToCamera - RENDER_TRANSLUCENT_BEYOND) / float alpha = lookingInMirror ? 1.0f : glm::clamp((distanceToCamera - RENDER_TRANSLUCENT_BEYOND) /
(RENDER_OPAQUE_BEYOND - RENDER_TRANSLUCENT_BEYOND), 0.f, 1.f); (RENDER_OPAQUE_BEYOND - RENDER_TRANSLUCENT_BEYOND), 0.f, 1.f);
@ -1021,18 +1138,18 @@ void Avatar::renderBody(bool lookingInMirror) {
} }
// Always render other people, and render myself when beyond threshold distance // Always render other people, and render myself when beyond threshold distance
if (b == AVATAR_JOINT_HEAD_BASE) { // the head is rendered as a special if (b == BODY_BALL_HEAD_BASE) { // the head is rendered as a special
if (lookingInMirror || _owningAgent || distanceToCamera > RENDER_OPAQUE_BEYOND * 0.5) { if (lookingInMirror || _owningAgent || distanceToCamera > RENDER_OPAQUE_BEYOND * 0.5) {
_head.render(lookingInMirror, _cameraPosition, alpha); _head.render(lookingInMirror, _cameraPosition, alpha);
} }
} else if (_owningAgent || distanceToCamera > RENDER_TRANSLUCENT_BEYOND } else if (_owningAgent || distanceToCamera > RENDER_TRANSLUCENT_BEYOND
|| b == AVATAR_JOINT_RIGHT_ELBOW || b == BODY_BALL_RIGHT_ELBOW
|| b == AVATAR_JOINT_RIGHT_WRIST || b == BODY_BALL_RIGHT_WRIST
|| b == AVATAR_JOINT_RIGHT_FINGERTIPS ) { || b == BODY_BALL_RIGHT_FINGERTIPS ) {
// Render the sphere at the joint // Render the body ball sphere
if (_owningAgent || b == AVATAR_JOINT_RIGHT_ELBOW if (_owningAgent || b == BODY_BALL_RIGHT_ELBOW
|| b == AVATAR_JOINT_RIGHT_WRIST || b == BODY_BALL_RIGHT_WRIST
|| b == AVATAR_JOINT_RIGHT_FINGERTIPS ) { || b == BODY_BALL_RIGHT_FINGERTIPS ) {
glColor3f(SKIN_COLOR[0] + _bodyBall[b].touchForce * 0.3f, glColor3f(SKIN_COLOR[0] + _bodyBall[b].touchForce * 0.3f,
SKIN_COLOR[1] - _bodyBall[b].touchForce * 0.2f, SKIN_COLOR[1] - _bodyBall[b].touchForce * 0.2f,
SKIN_COLOR[2] - _bodyBall[b].touchForce * 0.1f); SKIN_COLOR[2] - _bodyBall[b].touchForce * 0.1f);
@ -1043,36 +1160,36 @@ void Avatar::renderBody(bool lookingInMirror) {
alpha); alpha);
} }
if ((b != AVATAR_JOINT_HEAD_TOP ) if ((b != BODY_BALL_HEAD_TOP )
&& (b != AVATAR_JOINT_HEAD_BASE )) { && (b != BODY_BALL_HEAD_BASE )) {
glPushMatrix(); glPushMatrix();
glTranslatef(_bodyBall[b].position.x, _bodyBall[b].position.y, _bodyBall[b].position.z); glTranslatef(_bodyBall[b].position.x, _bodyBall[b].position.y, _bodyBall[b].position.z);
glutSolidSphere(_bodyBall[b].radius, 20.0f, 20.0f); glutSolidSphere(_bodyBall[b].radius, 20.0f, 20.0f);
glPopMatrix(); glPopMatrix();
} }
// Render the cone connecting this joint to its parent // Render the cone connecting this ball to its parent
if (_skeleton.joint[b].parent != AVATAR_JOINT_NULL) { if (_bodyBall[b].parentBall != BODY_BALL_NULL) {
if ((b != AVATAR_JOINT_HEAD_TOP ) if ((b != BODY_BALL_HEAD_TOP )
&& (b != AVATAR_JOINT_HEAD_BASE ) && (b != BODY_BALL_HEAD_BASE )
&& (b != AVATAR_JOINT_PELVIS ) && (b != BODY_BALL_PELVIS )
&& (b != AVATAR_JOINT_TORSO ) && (b != BODY_BALL_TORSO )
&& (b != AVATAR_JOINT_CHEST ) && (b != BODY_BALL_CHEST )
&& (b != AVATAR_JOINT_LEFT_COLLAR ) && (b != BODY_BALL_LEFT_COLLAR )
&& (b != AVATAR_JOINT_LEFT_SHOULDER ) && (b != BODY_BALL_LEFT_SHOULDER )
&& (b != AVATAR_JOINT_RIGHT_COLLAR ) && (b != BODY_BALL_RIGHT_COLLAR )
&& (b != AVATAR_JOINT_RIGHT_SHOULDER)) { && (b != BODY_BALL_RIGHT_SHOULDER)) {
glColor3fv(DARK_SKIN_COLOR); glColor3fv(DARK_SKIN_COLOR);
float r1 = _bodyBall[_skeleton.joint[b].parent ].radius * 0.8; float r1 = _bodyBall[_bodyBall[b].parentBall ].radius * 0.8;
float r2 = _bodyBall[b ].radius * 0.8; float r2 = _bodyBall[b].radius * 0.8;
if (b == AVATAR_JOINT_HEAD_BASE) { if (b == BODY_BALL_HEAD_BASE) {
r1 *= 0.5f; r1 *= 0.5f;
} }
renderJointConnectingCone renderJointConnectingCone
( (
_bodyBall[_skeleton.joint[b].parent ].position, _bodyBall[_bodyBall[b].parentBall].position,
_bodyBall[b ].position, r2, r2 _bodyBall[b].position, r2, r2
); );
} }
} }
@ -1110,7 +1227,7 @@ void Avatar::setHeadFromGyros(glm::vec3* eulerAngles, glm::vec3* angularVelocity
_head.setYaw (angles.x); _head.setYaw (angles.x);
_head.setPitch(angles.y); _head.setPitch(angles.y);
_head.setRoll (angles.z); _head.setRoll (angles.z);
//printLog("Y/P/R: %3.1f, %3.1f, %3.1f\n", angles.x, angles.y, angles.z); // printLog("Y/P/R: %3.1f, %3.1f, %3.1f\n", angles.x, angles.y, angles.z);
} }
} }

View file

@ -20,6 +20,40 @@
#include "Skeleton.h" #include "Skeleton.h"
#include "Transmitter.h" #include "Transmitter.h"
enum AvatarBodyBallID
{
BODY_BALL_NULL = -1,
BODY_BALL_PELVIS,
BODY_BALL_TORSO,
BODY_BALL_CHEST,
BODY_BALL_NECK_BASE,
BODY_BALL_HEAD_BASE,
BODY_BALL_HEAD_TOP,
BODY_BALL_LEFT_COLLAR,
BODY_BALL_LEFT_SHOULDER,
BODY_BALL_LEFT_ELBOW,
BODY_BALL_LEFT_WRIST,
BODY_BALL_LEFT_FINGERTIPS,
BODY_BALL_RIGHT_COLLAR,
BODY_BALL_RIGHT_SHOULDER,
BODY_BALL_RIGHT_ELBOW,
BODY_BALL_RIGHT_WRIST,
BODY_BALL_RIGHT_FINGERTIPS,
BODY_BALL_LEFT_HIP,
BODY_BALL_LEFT_KNEE,
BODY_BALL_LEFT_HEEL,
BODY_BALL_LEFT_TOES,
BODY_BALL_RIGHT_HIP,
BODY_BALL_RIGHT_KNEE,
BODY_BALL_RIGHT_HEEL,
BODY_BALL_RIGHT_TOES,
//TEST!
//BODY_BALL_LEFT_MID_THIGH,
NUM_AVATAR_BODY_BALLS
};
enum DriveKeys enum DriveKeys
{ {
FWD = 0, FWD = 0,
@ -64,19 +98,14 @@ public:
void setOrientation (const glm::quat& orientation); void setOrientation (const glm::quat& orientation);
//getters //getters
float getHeadYawRate () const { return _head.yawRate;}
float getHeadYawRate () const { return _head.yawRate;} float getBodyYaw () const { return _bodyYaw;}
float getBodyYaw () const { return _bodyYaw;} bool getIsNearInteractingOther () const { return _avatarTouch.getAbleToReachOtherAvatar();}
bool getIsNearInteractingOther() const { return _avatarTouch.getAbleToReachOtherAvatar();} const glm::vec3& getHeadJointPosition () const { return _skeleton.joint[ AVATAR_JOINT_HEAD_BASE ].position;}
const glm::vec3& getHeadPosition () const { return _skeleton.joint[ AVATAR_JOINT_HEAD_BASE ].position;} const glm::vec3& getBallPosition (AvatarJointID j) const { return _bodyBall[j].position;}
const glm::vec3& getSpringyHeadPosition () const { return _bodyBall[ AVATAR_JOINT_HEAD_BASE ].position;} glm::vec3 getBodyRightDirection () const { return getOrientation() * AVATAR_RIGHT; }
const glm::vec3& getJointPosition (AvatarJointID j) const { return _bodyBall[j].position;} glm::vec3 getBodyUpDirection () const { return getOrientation() * AVATAR_UP; }
glm::vec3 getBodyFrontDirection () const { return getOrientation() * AVATAR_FRONT; }
glm::vec3 getBodyRightDirection () const { return getOrientation() * AVATAR_RIGHT; }
glm::vec3 getBodyUpDirection () const { return getOrientation() * AVATAR_UP; }
glm::vec3 getBodyFrontDirection () const { return getOrientation() * AVATAR_FRONT; }
const glm::vec3& getVelocity () const { return _velocity;} const glm::vec3& getVelocity () const { return _velocity;}
float getSpeed () const { return _speed;} float getSpeed () const { return _speed;}
float getHeight () const { return _height;} float getHeight () const { return _height;}
@ -106,16 +135,21 @@ private:
struct AvatarBall struct AvatarBall
{ {
glm::vec3 position; AvatarJointID parentJoint; // the skeletal joint that serves as a reference for determining the position
glm::vec3 velocity; glm::vec3 parentOffset; // a 3D vector in the frame of reference of the parent skeletal joint
float jointTightness; AvatarBodyBallID parentBall; // the ball to which this ball is constrained for spring forces
float radius; glm::vec3 position; // the actual dynamic position of the ball at any given time
bool isCollidable; glm::vec3 velocity; // the velocity of the ball
float touchForce; float springLength; // the ideal length of the spring between this ball and its parentBall
float jointTightness; // how tightly the ball position attempts to stay at its ideal position (determined by parentOffset)
float radius; // the radius of the ball
bool isCollidable; // whether or not the ball responds to collisions
float touchForce; // a scalar determining the amount that the cursor (or hand) is penetrating the ball
}; };
Head _head; Head _head;
Skeleton _skeleton; Skeleton _skeleton;
bool _ballSpringsInitialized;
float _TEST_bigSphereRadius; float _TEST_bigSphereRadius;
glm::vec3 _TEST_bigSpherePosition; glm::vec3 _TEST_bigSpherePosition;
bool _mousePressed; bool _mousePressed;
@ -123,8 +157,7 @@ private:
float _bodyYawDelta; float _bodyYawDelta;
float _bodyRollDelta; float _bodyRollDelta;
glm::vec3 _movedHandOffset; glm::vec3 _movedHandOffset;
glm::quat _rotation; // the rotation of the avatar body as a whole expressed as a quaternion AvatarBall _bodyBall[ NUM_AVATAR_BODY_BALLS ];
AvatarBall _bodyBall[ NUM_AVATAR_JOINTS ];
AvatarMode _mode; AvatarMode _mode;
glm::vec3 _cameraPosition; glm::vec3 _cameraPosition;
glm::vec3 _handHoldingPosition; glm::vec3 _handHoldingPosition;

View file

@ -14,7 +14,7 @@ Skeleton::Skeleton() {
void Skeleton::initialize() { void Skeleton::initialize() {
for (int b=0; b<NUM_AVATAR_JOINTS; b++) { for (int b = 0; b < NUM_AVATAR_JOINTS; b++) {
joint[b].parent = AVATAR_JOINT_NULL; joint[b].parent = AVATAR_JOINT_NULL;
joint[b].position = glm::vec3(0.0, 0.0, 0.0); joint[b].position = glm::vec3(0.0, 0.0, 0.0);
joint[b].defaultPosePosition = glm::vec3(0.0, 0.0, 0.0); joint[b].defaultPosePosition = glm::vec3(0.0, 0.0, 0.0);
@ -101,6 +101,7 @@ void Skeleton::update(float deltaTime, const glm::quat& orientation, glm::vec3 p
} }
} }
float Skeleton::getArmLength() { float Skeleton::getArmLength() {
return joint[ AVATAR_JOINT_RIGHT_ELBOW ].length return joint[ AVATAR_JOINT_RIGHT_ELBOW ].length
+ joint[ AVATAR_JOINT_RIGHT_WRIST ].length + joint[ AVATAR_JOINT_RIGHT_WRIST ].length

View file

@ -55,14 +55,15 @@ public:
float getHeight(); float getHeight();
float getPelvisStandingHeight(); float getPelvisStandingHeight();
float getPelvisFloatingHeight(); float getPelvisFloatingHeight();
//glm::vec3 getJointVectorFromParent(AvatarJointID jointID) {return joint[jointID].position - joint[joint[jointID].parent].position; }
struct AvatarJoint struct AvatarJoint
{ {
AvatarJointID parent; // which joint is this joint connected to? AvatarJointID parent; // which joint is this joint connected to?
glm::vec3 position; // the position at the "end" of the joint - in global space glm::vec3 position; // the position at the "end" of the joint - in global space
glm::vec3 defaultPosePosition; // the parent relative position when the avatar is in the "T-pose" glm::vec3 defaultPosePosition; // the parent relative position when the avatar is in the default pose
glm::quat rotation; // the parent-relative rotation (orientation) of the joint as a quaternion glm::quat rotation; // the parent-relative rotation (orientation) of the joint as a quaternion
float length; // the length of vector connecting the joint and its parent float length; // the length of vector between the joint and its parent
}; };
AvatarJoint joint[ NUM_AVATAR_JOINTS ]; AvatarJoint joint[ NUM_AVATAR_JOINTS ];