//// Avatar.cpp// interface//// Created by Philip Rosedale on 9/11/12.// adapted by Jeffrey Ventrella// Copyright (c) 2013 Physical, Inc.. All rights reserved. #include <glm/glm.hpp>#include <vector>#include <lodepng.h>#include <SharedUtil.h>#include "Avatar.h"#include "Head.h"#include "Log.h"#include "ui/TextRenderer.h"#include <AgentList.h>#include <AgentTypes.h>#include <PacketHeaders.h> using namespace std; const bool BALLS_ON = false;const bool USING_AVATAR_GRAVITY = true;const float GRAVITY_SCALE = 10.0f;const float BOUNCE = 0.3f;const float THRUST_MAG = 1200.0;const float YAW_MAG = 500.0;const float BODY_SPIN_FRICTION = 5.0;const float BODY_UPRIGHT_FORCE = 10.0;const float BODY_PITCH_WHILE_WALKING = 40.0;const float BODY_ROLL_WHILE_TURNING = 0.1;const float VELOCITY_DECAY = 5.0;const float MY_HAND_HOLDING_PULL = 0.2;const float YOUR_HAND_HOLDING_PULL = 1.0;const float BODY_SPRING_DEFAULT_TIGHTNESS = 1500.0f;const float BODY_SPRING_FORCE = 300.0f;const float BODY_SPRING_DECAY = 16.0f;const float COLLISION_RADIUS_SCALAR = 1.8;const float COLLISION_BALL_FORCE = 1.0;const float COLLISION_BODY_FORCE = 6.0;const float COLLISION_BALL_FRICTION = 60.0;const float COLLISION_BODY_FRICTION = 0.5;const float HEAD_ROTATION_SCALE = 0.70;const float HEAD_ROLL_SCALE = 0.40;const float HEAD_MAX_PITCH = 45;const float HEAD_MIN_PITCH = -45;const float HEAD_MAX_YAW = 85;const float HEAD_MIN_YAW = -85;const float AVATAR_BRAKING_RANGE = 1.6f;const float AVATAR_BRAKING_STRENGTH = 30.0f; float skinColor [] = {1.0, 0.84, 0.66};float lightBlue [] = {0.7, 0.8, 1.0}; /*float browColor [] = {210.0/255.0, 105.0/255.0, 30.0/255.0};float mouthColor[] = {1, 0, 0}; float BrowRollAngle [5] = {0, 15, 30, -30, -15};float BrowPitchAngle[3] = {-70, -60, -50};float eyeColor [3] = {1,1,1}; float MouthWidthChoices[3] = {0.5, 0.77, 0.3}; float browWidth = 0.8;float browThickness = 0.16; //char iris_texture_file[] = "resources/images/green_eye.png";*/bool usingBigSphereCollisionTest = true; float chatMessageScale = 0.0015;float chatMessageHeight = 0.45; /*vector<unsigned char> iris_texture;unsigned int iris_texture_width = 512;unsigned int iris_texture_height = 256;*/ Avatar::Avatar(bool isMine) { _orientation.setToIdentity(); _velocity = glm::vec3(0.0f, 0.0f, 0.0f); _thrust = glm::vec3(0.0f, 0.0f, 0.0f); _rotation = glm::quat(0.0f, 0.0f, 0.0f, 0.0f); _bodyYaw = -90.0; _bodyPitch = 0.0; _bodyRoll = 0.0; _bodyPitchDelta = 0.0; _bodyYawDelta = 0.0; _bodyRollDelta = 0.0; _mousePressed = false; _mode = AVATAR_MODE_STANDING; _isMine = isMine; _maxArmLength = 0.0; _transmitterHz = 0.0; _transmitterPackets = 0; _transmitterIsFirstData = true; _transmitterInitialReading = glm::vec3(0.f, 0.f, 0.f); _speed = 0.0; _pelvisStandingHeight = 0.0f; _displayingHead = true; _TEST_bigSphereRadius = 0.4f; _TEST_bigSpherePosition = glm::vec3(5.0f, _TEST_bigSphereRadius, 5.0f); for (int i = 0; i < MAX_DRIVE_KEYS; i++) _driveKeys[i] = false; _head.initialize(); _movedHandOffset = glm::vec3(0.0f, 0.0f, 0.0f); _renderYaw = 0.0; _renderPitch = 0.0; _sphere = NULL; _handHoldingPosition = glm::vec3(0.0f, 0.0f, 0.0f); _distanceToNearestAvatar = std::numeric_limits<float>::max(); _gravity = glm::vec3(0.0f, -1.0f, 0.0f); // default initializeSkeleton(); _avatarTouch.setReachableRadius(0.6); /* if (iris_texture.size() == 0) { switchToResourcesParentIfRequired(); unsigned error = lodepng::decode(iris_texture, iris_texture_width, iris_texture_height, iris_texture_file); if (error != 0) { printLog("error %u: %s\n", error, lodepng_error_text(error)); } }*/ if (BALLS_ON) { _balls = new Balls(100); } else { _balls = NULL; }} Avatar::Avatar(const Avatar &otherAvatar) { _velocity = otherAvatar._velocity; _thrust = otherAvatar._thrust; _rotation = otherAvatar._rotation; _bodyYaw = otherAvatar._bodyYaw; _bodyPitch = otherAvatar._bodyPitch; _bodyRoll = otherAvatar._bodyRoll; _bodyPitchDelta = otherAvatar._bodyPitchDelta; _bodyYawDelta = otherAvatar._bodyYawDelta; _bodyRollDelta = otherAvatar._bodyRollDelta; _mousePressed = otherAvatar._mousePressed; _mode = otherAvatar._mode; _isMine = otherAvatar._isMine; _renderYaw = otherAvatar._renderYaw; _renderPitch = otherAvatar._renderPitch; _maxArmLength = otherAvatar._maxArmLength; _transmitterTimer = otherAvatar._transmitterTimer; _transmitterIsFirstData = otherAvatar._transmitterIsFirstData; _transmitterTimeLastReceived = otherAvatar._transmitterTimeLastReceived; _transmitterHz = otherAvatar._transmitterHz; _transmitterInitialReading = otherAvatar._transmitterInitialReading; _transmitterPackets = otherAvatar._transmitterPackets; _TEST_bigSphereRadius = otherAvatar._TEST_bigSphereRadius; _TEST_bigSpherePosition = otherAvatar._TEST_bigSpherePosition; _movedHandOffset = otherAvatar._movedHandOffset; _orientation.set(otherAvatar._orientation); _sphere = NULL; initializeSkeleton(); for (int i = 0; i < MAX_DRIVE_KEYS; i++) _driveKeys[i] = otherAvatar._driveKeys[i]; _head.pupilSize = otherAvatar._head.pupilSize; _head.interPupilDistance = otherAvatar._head.interPupilDistance; _head.interBrowDistance = otherAvatar._head.interBrowDistance; _head.nominalPupilSize = otherAvatar._head.nominalPupilSize; _head.yawRate = otherAvatar._head.yawRate; _head.pitchRate = otherAvatar._head.pitchRate; _head.rollRate = otherAvatar._head.rollRate; _head.eyebrowPitch[0] = otherAvatar._head.eyebrowPitch[0]; _head.eyebrowPitch[1] = otherAvatar._head.eyebrowPitch[1]; _head.eyebrowRoll [0] = otherAvatar._head.eyebrowRoll [0]; _head.eyebrowRoll [1] = otherAvatar._head.eyebrowRoll [1]; _head.mouthPitch = otherAvatar._head.mouthPitch; _head.mouthYaw = otherAvatar._head.mouthYaw; _head.mouthWidth = otherAvatar._head.mouthWidth; _head.mouthHeight = otherAvatar._head.mouthHeight; _head.eyeballPitch[0] = otherAvatar._head.eyeballPitch[0]; _head.eyeballPitch[1] = otherAvatar._head.eyeballPitch[1]; _head.eyeballScaleX = otherAvatar._head.eyeballScaleX; _head.eyeballScaleY = otherAvatar._head.eyeballScaleY; _head.eyeballScaleZ = otherAvatar._head.eyeballScaleZ; _head.eyeballYaw[0] = otherAvatar._head.eyeballYaw[0]; _head.eyeballYaw[1] = otherAvatar._head.eyeballYaw[1]; _head.pitchTarget = otherAvatar._head.pitchTarget; _head.yawTarget = otherAvatar._head.yawTarget; _head.noiseEnvelope = otherAvatar._head.noiseEnvelope; _head.pupilConverge = otherAvatar._head.pupilConverge; _head.leanForward = otherAvatar._head.leanForward; _head.leanSideways = otherAvatar._head.leanSideways; _head.eyeContact = otherAvatar._head.eyeContact; _head.eyeContactTarget = otherAvatar._head.eyeContactTarget; _head.scale = otherAvatar._head.scale; _head.audioAttack = otherAvatar._head.audioAttack; _head.averageLoudness = otherAvatar._head.averageLoudness; _head.lastLoudness = otherAvatar._head.lastLoudness; _head.browAudioLift = otherAvatar._head.browAudioLift; _head.noise = otherAvatar._head.noise; _distanceToNearestAvatar = otherAvatar._distanceToNearestAvatar; initializeSkeleton(); /* if (iris_texture.size() == 0) { switchToResourcesParentIfRequired(); unsigned error = lodepng::decode(iris_texture, iris_texture_width, iris_texture_height, iris_texture_file); if (error != 0) { printLog("error %u: %s\n", error, lodepng_error_text(error)); } }*/} Avatar::~Avatar() { if (_sphere != NULL) { gluDeleteQuadric(_sphere); }} Avatar* Avatar::clone() const { return new Avatar(*this);} void Avatar::reset() { _headPitch = _headYaw = _headRoll = 0; _head.leanForward = _head.leanSideways = 0;} // Update avatar head rotation with sensor datavoid Avatar::updateHeadFromGyros(float deltaTime, SerialInterface* serialInterface, glm::vec3* gravity) { float measuredPitchRate = 0.0f; float measuredRollRate = 0.0f; float measuredYawRate = 0.0f; measuredPitchRate = serialInterface->getLastPitchRate(); measuredYawRate = serialInterface->getLastYawRate(); measuredRollRate = serialInterface->getLastRollRate(); // Update avatar head position based on measured gyro rates const float MAX_PITCH = 45; const float MIN_PITCH = -45; const float MAX_YAW = 85; const float MIN_YAW = -85; const float MAX_ROLL = 50; const float MIN_ROLL = -50; addHeadPitch(measuredPitchRate * deltaTime); addHeadYaw(measuredYawRate * deltaTime); addHeadRoll(measuredRollRate * deltaTime); setHeadPitch(glm::clamp(getHeadPitch(), MIN_PITCH, MAX_PITCH)); setHeadYaw(glm::clamp(getHeadYaw(), MIN_YAW, MAX_YAW)); setHeadRoll(glm::clamp(getHeadRoll(), MIN_ROLL, MAX_ROLL)); // Update head lean distance based on accelerometer data const float LEAN_SENSITIVITY = 0.15; const float LEAN_MAX = 0.45; const float LEAN_AVERAGING = 10.0; glm::vec3 headRotationRates(getHeadPitch(), getHeadYaw(), getHeadRoll()); float headRateMax = 50.f; glm::vec3 leaning = (serialInterface->getLastAcceleration() - serialInterface->getGravity()) * LEAN_SENSITIVITY * (1.f - fminf(glm::length(headRotationRates), headRateMax) / headRateMax); leaning.y = 0.f; if (glm::length(leaning) < LEAN_MAX) { _head.leanForward = _head.leanForward * (1.f - LEAN_AVERAGING * deltaTime) + (LEAN_AVERAGING * deltaTime) * leaning.z * LEAN_SENSITIVITY; _head.leanSideways = _head.leanSideways * (1.f - LEAN_AVERAGING * deltaTime) + (LEAN_AVERAGING * deltaTime) * leaning.x * LEAN_SENSITIVITY; } setHeadLeanSideways(_head.leanSideways); setHeadLeanForward(_head.leanForward); } float Avatar::getAbsoluteHeadYaw() const { return _bodyYaw + _headYaw;} float Avatar::getAbsoluteHeadPitch() const { return _bodyPitch + _headPitch;} void Avatar::addLean(float x, float z) { //Add lean as impulse _head.leanSideways += x; _head.leanForward += z;} void Avatar::setLeanForward(float dist){ _head.leanForward = dist;} void Avatar::setLeanSideways(float dist){ _head.leanSideways = dist;} void Avatar::setMousePressed(bool mousePressed) { _mousePressed = mousePressed;} bool Avatar::getIsNearInteractingOther() { return _avatarTouch.getAbleToReachOtherAvatar(); } void Avatar::simulate(float deltaTime) { // update balls if (_balls) { _balls->simulate(deltaTime); } // if other avatar, update head position from network data // update avatar skeleton updateSkeleton(); //detect and respond to collisions with other avatars... if (_isMine) { updateAvatarCollisions(deltaTime); } //update the movement of the hand and process handshaking with other avatars... updateHandMovementAndTouching(deltaTime); _avatarTouch.simulate(deltaTime); // apply gravity and collision with the ground/floor if (USING_AVATAR_GRAVITY) { if (_position.y > _pelvisStandingHeight + 0.01f) { _velocity += _gravity * (GRAVITY_SCALE * deltaTime); } else if (_position.y < _pelvisStandingHeight) { _position.y = _pelvisStandingHeight; _velocity.y = -_velocity.y * BOUNCE; } } // update body springs updateBodySprings(deltaTime); // test for avatar collision response with the big sphere if (usingBigSphereCollisionTest) { updateCollisionWithSphere(_TEST_bigSpherePosition, _TEST_bigSphereRadius, deltaTime); } // driving the avatar around should only apply if this is my avatar (as opposed to an avatar being driven remotely) if (_isMine) { _thrust = glm::vec3(0.0f, 0.0f, 0.0f); if (_driveKeys[FWD ]) {_thrust += THRUST_MAG * deltaTime * _orientation.getFront();} if (_driveKeys[BACK ]) {_thrust -= THRUST_MAG * deltaTime * _orientation.getFront();} if (_driveKeys[RIGHT ]) {_thrust += THRUST_MAG * deltaTime * _orientation.getRight();} if (_driveKeys[LEFT ]) {_thrust -= THRUST_MAG * deltaTime * _orientation.getRight();} if (_driveKeys[UP ]) {_thrust += THRUST_MAG * deltaTime * _orientation.getUp();} if (_driveKeys[DOWN ]) {_thrust -= THRUST_MAG * deltaTime * _orientation.getUp();} if (_driveKeys[ROT_RIGHT]) {_bodyYawDelta -= YAW_MAG * deltaTime;} if (_driveKeys[ROT_LEFT ]) {_bodyYawDelta += YAW_MAG * deltaTime;} } // update body yaw by body yaw delta if (_isMine) { _bodyPitch += _bodyPitchDelta * deltaTime; _bodyYaw += _bodyYawDelta * deltaTime; _bodyRoll += _bodyRollDelta * deltaTime; } // decay body rotation momentum float bodySpinMomentum = 1.0 - BODY_SPIN_FRICTION * deltaTime; if (bodySpinMomentum < 0.0f) { bodySpinMomentum = 0.0f; } _bodyPitchDelta *= bodySpinMomentum; _bodyYawDelta *= bodySpinMomentum; _bodyRollDelta *= bodySpinMomentum; // add thrust to velocity _velocity += _thrust * deltaTime; // calculate speed _speed = glm::length(_velocity); //pitch and roll the body as a function of forward speed and turning delta float forwardComponentOfVelocity = glm::dot(_orientation.getFront(), _velocity); _bodyPitch += BODY_PITCH_WHILE_WALKING * deltaTime * forwardComponentOfVelocity; _bodyRoll += BODY_ROLL_WHILE_TURNING * deltaTime * _speed * _bodyYawDelta; // these forces keep the body upright... float tiltDecay = 1.0 - BODY_UPRIGHT_FORCE * deltaTime; if (tiltDecay < 0.0f) {tiltDecay = 0.0f;} _bodyPitch *= tiltDecay; _bodyRoll *= tiltDecay; //the following will be used to make the avatar upright no matter what gravity is //float f = angleBetween(_orientation.getUp(), _gravity); // update position by velocity _position += _velocity * deltaTime; // decay velocity float decay = 1.0 - VELOCITY_DECAY * deltaTime; if ( decay < 0.0 ) { _velocity = glm::vec3( 0.0f, 0.0f, 0.0f ); } else { _velocity *= decay; } // If another avatar is near, dampen velocity as a function of closeness if (_isMine && (_distanceToNearestAvatar < AVATAR_BRAKING_RANGE)) { float closeness = 1.0f - (_distanceToNearestAvatar / AVATAR_BRAKING_RANGE); float drag = 1.0f - closeness * AVATAR_BRAKING_STRENGTH * deltaTime; if ( drag > 0.0f ) { _velocity *= drag; } else { _velocity = glm::vec3( 0.0f, 0.0f, 0.0f ); } } // Get head position data from network for other people if (!_isMine) { _head.leanSideways = getHeadLeanSideways(); _head.leanForward = getHeadLeanForward(); } //apply the head lean values to the springy position... if (fabs(_head.leanSideways + _head.leanForward) > 0.0f) { glm::vec3 headLean = _orientation.getRight() * _head.leanSideways + _orientation.getFront() * _head.leanForward; // this is not a long-term solution, but it works ok for initial purposes of making the avatar lean _joint[ AVATAR_JOINT_TORSO ].springyPosition += headLean * 0.1f; _joint[ AVATAR_JOINT_CHEST ].springyPosition += headLean * 0.4f; _joint[ AVATAR_JOINT_NECK_BASE ].springyPosition += headLean * 0.7f; _joint[ AVATAR_JOINT_HEAD_BASE ].springyPosition += headLean * 1.0f; _joint[ AVATAR_JOINT_LEFT_COLLAR ].springyPosition += headLean * 0.6f; _joint[ AVATAR_JOINT_LEFT_SHOULDER ].springyPosition += headLean * 0.6f; _joint[ AVATAR_JOINT_LEFT_ELBOW ].springyPosition += headLean * 0.2f; _joint[ AVATAR_JOINT_LEFT_WRIST ].springyPosition += headLean * 0.1f; _joint[ AVATAR_JOINT_LEFT_FINGERTIPS ].springyPosition += headLean * 0.0f; _joint[ AVATAR_JOINT_RIGHT_COLLAR ].springyPosition += headLean * 0.6f; _joint[ AVATAR_JOINT_RIGHT_SHOULDER ].springyPosition += headLean * 0.6f; _joint[ AVATAR_JOINT_RIGHT_ELBOW ].springyPosition += headLean * 0.2f; _joint[ AVATAR_JOINT_RIGHT_WRIST ].springyPosition += headLean * 0.1f; _joint[ AVATAR_JOINT_RIGHT_FINGERTIPS ].springyPosition += headLean * 0.0f; } // update head state _head.setPositionRotationAndScale( _joint[ AVATAR_JOINT_HEAD_BASE ].springyPosition, glm::vec3(_headYaw, _headPitch, _headRoll), _joint[ AVATAR_JOINT_HEAD_BASE ].radius ); _head.setAudioLoudness(_audioLoudness); _head.setSkinColor(glm::vec3(skinColor[0], skinColor[1], skinColor[2])); _head.simulate(deltaTime, _isMine); // use speed and angular velocity to determine walking vs. standing if (_speed + fabs(_bodyYawDelta) > 0.2) { _mode = AVATAR_MODE_WALKING; } else { _mode = AVATAR_MODE_INTERACTING; }} void Avatar::updateHandMovementAndTouching(float deltaTime) { // reset hand and arm positions according to hand movement glm::vec3 transformedHandMovement = _orientation.getRight() * _movedHandOffset.x * 2.0f + _orientation.getUp() * -_movedHandOffset.y * 1.0f + _orientation.getFront() * -_movedHandOffset.y * 1.0f; _joint[ AVATAR_JOINT_RIGHT_FINGERTIPS ].position += transformedHandMovement; if (_isMine) { _avatarTouch.setMyBodyPosition(_position); Avatar * _interactingOther = NULL; float closestDistance = std::numeric_limits<float>::max(); Lines 1–500 of 1629. Load more
// // Avatar.cpp // interface // // Created by Philip Rosedale on 9/11/12. // adapted by Jeffrey Ventrella // Copyright (c) 2013 Physical, Inc.. All rights reserved. #include <glm/glm.hpp> #include <vector> #include <lodepng.h> #include <SharedUtil.h> #include "Avatar.h" #include "Head.h" #include "Log.h" #include "ui/TextRenderer.h" #include <AgentList.h> #include <AgentTypes.h> #include <PacketHeaders.h> using namespace std; const bool BALLS_ON = false; const bool USING_AVATAR_GRAVITY = true; const float GRAVITY_SCALE = 10.0f; const float BOUNCE = 0.3f; const float THRUST_MAG = 1200.0; const float YAW_MAG = 500.0; const float BODY_SPIN_FRICTION = 5.0; const float BODY_UPRIGHT_FORCE = 10.0; const float BODY_PITCH_WHILE_WALKING = 40.0; const float BODY_ROLL_WHILE_TURNING = 0.1; const float VELOCITY_DECAY = 5.0; const float MY_HAND_HOLDING_PULL = 0.2; const float YOUR_HAND_HOLDING_PULL = 1.0; const float BODY_SPRING_DEFAULT_TIGHTNESS = 1500.0f; const float BODY_SPRING_FORCE = 300.0f; const float BODY_SPRING_DECAY = 16.0f; const float COLLISION_RADIUS_SCALAR = 1.8; const float COLLISION_BALL_FORCE = 1.0; const float COLLISION_BODY_FORCE = 6.0; const float COLLISION_BALL_FRICTION = 60.0; const float COLLISION_BODY_FRICTION = 0.5; const float HEAD_ROTATION_SCALE = 0.70; const float HEAD_ROLL_SCALE = 0.40; const float HEAD_MAX_PITCH = 45; const float HEAD_MIN_PITCH = -45; const float HEAD_MAX_YAW = 85; const float HEAD_MIN_YAW = -85; const float AVATAR_BRAKING_RANGE = 1.6f; const float AVATAR_BRAKING_STRENGTH = 30.0f; float skinColor [] = {1.0, 0.84, 0.66}; float lightBlue [] = {0.7, 0.8, 1.0}; /* float browColor [] = {210.0/255.0, 105.0/255.0, 30.0/255.0}; float mouthColor[] = {1, 0, 0}; float BrowRollAngle [5] = {0, 15, 30, -30, -15}; float BrowPitchAngle[3] = {-70, -60, -50}; float eyeColor [3] = {1,1,1}; float MouthWidthChoices[3] = {0.5, 0.77, 0.3}; float browWidth = 0.8; float browThickness = 0.16; //char iris_texture_file[] = "resources/images/green_eye.png"; */ bool usingBigSphereCollisionTest = true; float chatMessageScale = 0.0015; float chatMessageHeight = 0.45; /* vector<unsigned char> iris_texture; unsigned int iris_texture_width = 512; unsigned int iris_texture_height = 256; */ Avatar::Avatar(bool isMine) { _orientation.setToIdentity(); _velocity = glm::vec3(0.0f, 0.0f, 0.0f); _thrust = glm::vec3(0.0f, 0.0f, 0.0f); _rotation = glm::quat(0.0f, 0.0f, 0.0f, 0.0f); _bodyYaw = -90.0; _bodyPitch = 0.0; _bodyRoll = 0.0; _bodyPitchDelta = 0.0; _bodyYawDelta = 0.0; _bodyRollDelta = 0.0; _mousePressed = false; _mode = AVATAR_MODE_STANDING; _isMine = isMine; _maxArmLength = 0.0; _transmitterHz = 0.0; _transmitterPackets = 0; _transmitterIsFirstData = true; _transmitterInitialReading = glm::vec3(0.f, 0.f, 0.f); _speed = 0.0; _pelvisStandingHeight = 0.0f; _displayingHead = true; _TEST_bigSphereRadius = 0.4f; _TEST_bigSpherePosition = glm::vec3(5.0f, _TEST_bigSphereRadius, 5.0f); for (int i = 0; i < MAX_DRIVE_KEYS; i++) _driveKeys[i] = false; _head.initialize(); _movedHandOffset = glm::vec3(0.0f, 0.0f, 0.0f); _renderYaw = 0.0; _renderPitch = 0.0; _sphere = NULL; _handHoldingPosition = glm::vec3(0.0f, 0.0f, 0.0f); _distanceToNearestAvatar = std::numeric_limits<float>::max(); _gravity = glm::vec3(0.0f, -1.0f, 0.0f); // default initializeSkeleton(); _avatarTouch.setReachableRadius(0.6); /* if (iris_texture.size() == 0) { switchToResourcesParentIfRequired(); unsigned error = lodepng::decode(iris_texture, iris_texture_width, iris_texture_height, iris_texture_file); if (error != 0) { printLog("error %u: %s\n", error, lodepng_error_text(error)); } } */ if (BALLS_ON) { _balls = new Balls(100); } else { _balls = NULL; } } Avatar::Avatar(const Avatar &otherAvatar) { _velocity = otherAvatar._velocity; _thrust = otherAvatar._thrust; _rotation = otherAvatar._rotation; _bodyYaw = otherAvatar._bodyYaw; _bodyPitch = otherAvatar._bodyPitch; _bodyRoll = otherAvatar._bodyRoll; _bodyPitchDelta = otherAvatar._bodyPitchDelta; _bodyYawDelta = otherAvatar._bodyYawDelta; _bodyRollDelta = otherAvatar._bodyRollDelta; _mousePressed = otherAvatar._mousePressed; _mode = otherAvatar._mode; _isMine = otherAvatar._isMine; _renderYaw = otherAvatar._renderYaw; _renderPitch = otherAvatar._renderPitch; _maxArmLength = otherAvatar._maxArmLength; _transmitterTimer = otherAvatar._transmitterTimer; _transmitterIsFirstData = otherAvatar._transmitterIsFirstData; _transmitterTimeLastReceived = otherAvatar._transmitterTimeLastReceived; _transmitterHz = otherAvatar._transmitterHz; _transmitterInitialReading = otherAvatar._transmitterInitialReading; _transmitterPackets = otherAvatar._transmitterPackets; _TEST_bigSphereRadius = otherAvatar._TEST_bigSphereRadius; _TEST_bigSpherePosition = otherAvatar._TEST_bigSpherePosition; _movedHandOffset = otherAvatar._movedHandOffset; _orientation.set(otherAvatar._orientation); _sphere = NULL; initializeSkeleton(); for (int i = 0; i < MAX_DRIVE_KEYS; i++) _driveKeys[i] = otherAvatar._driveKeys[i]; _head.pupilSize = otherAvatar._head.pupilSize; _head.interPupilDistance = otherAvatar._head.interPupilDistance; _head.interBrowDistance = otherAvatar._head.interBrowDistance; _head.nominalPupilSize = otherAvatar._head.nominalPupilSize; _head.yawRate = otherAvatar._head.yawRate; _head.pitchRate = otherAvatar._head.pitchRate; _head.rollRate = otherAvatar._head.rollRate; _head.eyebrowPitch[0] = otherAvatar._head.eyebrowPitch[0]; _head.eyebrowPitch[1] = otherAvatar._head.eyebrowPitch[1]; _head.eyebrowRoll [0] = otherAvatar._head.eyebrowRoll [0]; _head.eyebrowRoll [1] = otherAvatar._head.eyebrowRoll [1]; _head.mouthPitch = otherAvatar._head.mouthPitch; _head.mouthYaw = otherAvatar._head.mouthYaw; _head.mouthWidth = otherAvatar._head.mouthWidth; _head.mouthHeight = otherAvatar._head.mouthHeight; _head.eyeballPitch[0] = otherAvatar._head.eyeballPitch[0]; _head.eyeballPitch[1] = otherAvatar._head.eyeballPitch[1]; _head.eyeballScaleX = otherAvatar._head.eyeballScaleX; _head.eyeballScaleY = otherAvatar._head.eyeballScaleY; _head.eyeballScaleZ = otherAvatar._head.eyeballScaleZ; _head.eyeballYaw[0] = otherAvatar._head.eyeballYaw[0]; _head.eyeballYaw[1] = otherAvatar._head.eyeballYaw[1]; _head.pitchTarget = otherAvatar._head.pitchTarget; _head.yawTarget = otherAvatar._head.yawTarget; _head.noiseEnvelope = otherAvatar._head.noiseEnvelope; _head.pupilConverge = otherAvatar._head.pupilConverge; _head.leanForward = otherAvatar._head.leanForward; _head.leanSideways = otherAvatar._head.leanSideways; _head.eyeContact = otherAvatar._head.eyeContact; _head.eyeContactTarget = otherAvatar._head.eyeContactTarget; _head.scale = otherAvatar._head.scale; _head.audioAttack = otherAvatar._head.audioAttack; _head.averageLoudness = otherAvatar._head.averageLoudness; _head.lastLoudness = otherAvatar._head.lastLoudness; _head.browAudioLift = otherAvatar._head.browAudioLift; _head.noise = otherAvatar._head.noise; _distanceToNearestAvatar = otherAvatar._distanceToNearestAvatar; initializeSkeleton(); /* if (iris_texture.size() == 0) { switchToResourcesParentIfRequired(); unsigned error = lodepng::decode(iris_texture, iris_texture_width, iris_texture_height, iris_texture_file); if (error != 0) { printLog("error %u: %s\n", error, lodepng_error_text(error)); } } */ } Avatar::~Avatar() { if (_sphere != NULL) { gluDeleteQuadric(_sphere); } } Avatar* Avatar::clone() const { return new Avatar(*this); } void Avatar::reset() { _headPitch = _headYaw = _headRoll = 0; _head.leanForward = _head.leanSideways = 0; } // Update avatar head rotation with sensor data void Avatar::updateHeadFromGyros(float deltaTime, SerialInterface* serialInterface, glm::vec3* gravity) { float measuredPitchRate = 0.0f; float measuredRollRate = 0.0f; float measuredYawRate = 0.0f; measuredPitchRate = serialInterface->getLastPitchRate(); measuredYawRate = serialInterface->getLastYawRate(); measuredRollRate = serialInterface->getLastRollRate(); // Update avatar head position based on measured gyro rates const float MAX_PITCH = 45; const float MIN_PITCH = -45; const float MAX_YAW = 85; const float MIN_YAW = -85; const float MAX_ROLL = 50; const float MIN_ROLL = -50; addHeadPitch(measuredPitchRate * deltaTime); addHeadYaw(measuredYawRate * deltaTime); addHeadRoll(measuredRollRate * deltaTime); setHeadPitch(glm::clamp(getHeadPitch(), MIN_PITCH, MAX_PITCH)); setHeadYaw(glm::clamp(getHeadYaw(), MIN_YAW, MAX_YAW)); setHeadRoll(glm::clamp(getHeadRoll(), MIN_ROLL, MAX_ROLL)); // Update head lean distance based on accelerometer data const float LEAN_SENSITIVITY = 0.15; const float LEAN_MAX = 0.45; const float LEAN_AVERAGING = 10.0; glm::vec3 headRotationRates(getHeadPitch(), getHeadYaw(), getHeadRoll()); float headRateMax = 50.f; glm::vec3 leaning = (serialInterface->getLastAcceleration() - serialInterface->getGravity()) * LEAN_SENSITIVITY * (1.f - fminf(glm::length(headRotationRates), headRateMax) / headRateMax); leaning.y = 0.f; if (glm::length(leaning) < LEAN_MAX) { _head.leanForward = _head.leanForward * (1.f - LEAN_AVERAGING * deltaTime) + (LEAN_AVERAGING * deltaTime) * leaning.z * LEAN_SENSITIVITY; _head.leanSideways = _head.leanSideways * (1.f - LEAN_AVERAGING * deltaTime) + (LEAN_AVERAGING * deltaTime) * leaning.x * LEAN_SENSITIVITY; } setHeadLeanSideways(_head.leanSideways); setHeadLeanForward(_head.leanForward); } float Avatar::getAbsoluteHeadYaw() const { return _bodyYaw + _headYaw; } float Avatar::getAbsoluteHeadPitch() const { return _bodyPitch + _headPitch; } void Avatar::addLean(float x, float z) { //Add lean as impulse _head.leanSideways += x; _head.leanForward += z; } void Avatar::setLeanForward(float dist){ _head.leanForward = dist; } void Avatar::setLeanSideways(float dist){ _head.leanSideways = dist; } void Avatar::setMousePressed(bool mousePressed) { _mousePressed = mousePressed; } bool Avatar::getIsNearInteractingOther() { return _avatarTouch.getAbleToReachOtherAvatar(); } void Avatar::simulate(float deltaTime) { // update balls if (_balls) { _balls->simulate(deltaTime); } // if other avatar, update head position from network data // update avatar skeleton updateSkeleton(); //detect and respond to collisions with other avatars... if (_isMine) { updateAvatarCollisions(deltaTime); } //update the movement of the hand and process handshaking with other avatars... updateHandMovementAndTouching(deltaTime); _avatarTouch.simulate(deltaTime); // apply gravity and collision with the ground/floor if (USING_AVATAR_GRAVITY) { if (_position.y > _pelvisStandingHeight + 0.01f) { _velocity += _gravity * (GRAVITY_SCALE * deltaTime); } else if (_position.y < _pelvisStandingHeight) { _position.y = _pelvisStandingHeight; _velocity.y = -_velocity.y * BOUNCE; } } // update body springs updateBodySprings(deltaTime); // test for avatar collision response with the big sphere if (usingBigSphereCollisionTest) { updateCollisionWithSphere(_TEST_bigSpherePosition, _TEST_bigSphereRadius, deltaTime); } // driving the avatar around should only apply if this is my avatar (as opposed to an avatar being driven remotely) if (_isMine) { _thrust = glm::vec3(0.0f, 0.0f, 0.0f); if (_driveKeys[FWD ]) {_thrust += THRUST_MAG * deltaTime * _orientation.getFront();} if (_driveKeys[BACK ]) {_thrust -= THRUST_MAG * deltaTime * _orientation.getFront();} if (_driveKeys[RIGHT ]) {_thrust += THRUST_MAG * deltaTime * _orientation.getRight();} if (_driveKeys[LEFT ]) {_thrust -= THRUST_MAG * deltaTime * _orientation.getRight();} if (_driveKeys[UP ]) {_thrust += THRUST_MAG * deltaTime * _orientation.getUp();} if (_driveKeys[DOWN ]) {_thrust -= THRUST_MAG * deltaTime * _orientation.getUp();} if (_driveKeys[ROT_RIGHT]) {_bodyYawDelta -= YAW_MAG * deltaTime;} if (_driveKeys[ROT_LEFT ]) {_bodyYawDelta += YAW_MAG * deltaTime;} } // update body yaw by body yaw delta if (_isMine) { _bodyPitch += _bodyPitchDelta * deltaTime; _bodyYaw += _bodyYawDelta * deltaTime; _bodyRoll += _bodyRollDelta * deltaTime; } // decay body rotation momentum float bodySpinMomentum = 1.0 - BODY_SPIN_FRICTION * deltaTime; if (bodySpinMomentum < 0.0f) { bodySpinMomentum = 0.0f; } _bodyPitchDelta *= bodySpinMomentum; _bodyYawDelta *= bodySpinMomentum; _bodyRollDelta *= bodySpinMomentum; // add thrust to velocity _velocity += _thrust * deltaTime; // calculate speed _speed = glm::length(_velocity); //pitch and roll the body as a function of forward speed and turning delta float forwardComponentOfVelocity = glm::dot(_orientation.getFront(), _velocity); _bodyPitch += BODY_PITCH_WHILE_WALKING * deltaTime * forwardComponentOfVelocity; _bodyRoll += BODY_ROLL_WHILE_TURNING * deltaTime * _speed * _bodyYawDelta; // these forces keep the body upright... float tiltDecay = 1.0 - BODY_UPRIGHT_FORCE * deltaTime; if (tiltDecay < 0.0f) {tiltDecay = 0.0f;} _bodyPitch *= tiltDecay; _bodyRoll *= tiltDecay; //the following will be used to make the avatar upright no matter what gravity is //float f = angleBetween(_orientation.getUp(), _gravity); // update position by velocity _position += _velocity * deltaTime; // decay velocity float decay = 1.0 - VELOCITY_DECAY * deltaTime; if ( decay < 0.0 ) { _velocity = glm::vec3( 0.0f, 0.0f, 0.0f ); } else { _velocity *= decay; } // If another avatar is near, dampen velocity as a function of closeness if (_isMine && (_distanceToNearestAvatar < AVATAR_BRAKING_RANGE)) { float closeness = 1.0f - (_distanceToNearestAvatar / AVATAR_BRAKING_RANGE); float drag = 1.0f - closeness * AVATAR_BRAKING_STRENGTH * deltaTime; if ( drag > 0.0f ) { _velocity *= drag; } else { _velocity = glm::vec3( 0.0f, 0.0f, 0.0f ); } } // Get head position data from network for other people if (!_isMine) { _head.leanSideways = getHeadLeanSideways(); _head.leanForward = getHeadLeanForward(); } //apply the head lean values to the springy position... if (fabs(_head.leanSideways + _head.leanForward) > 0.0f) { glm::vec3 headLean = _orientation.getRight() * _head.leanSideways + _orientation.getFront() * _head.leanForward; // this is not a long-term solution, but it works ok for initial purposes of making the avatar lean _joint[ AVATAR_JOINT_TORSO ].springyPosition += headLean * 0.1f; _joint[ AVATAR_JOINT_CHEST ].springyPosition += headLean * 0.4f; _joint[ AVATAR_JOINT_NECK_BASE ].springyPosition += headLean * 0.7f; _joint[ AVATAR_JOINT_HEAD_BASE ].springyPosition += headLean * 1.0f; _joint[ AVATAR_JOINT_LEFT_COLLAR ].springyPosition += headLean * 0.6f; _joint[ AVATAR_JOINT_LEFT_SHOULDER ].springyPosition += headLean * 0.6f; _joint[ AVATAR_JOINT_LEFT_ELBOW ].springyPosition += headLean * 0.2f; _joint[ AVATAR_JOINT_LEFT_WRIST ].springyPosition += headLean * 0.1f; _joint[ AVATAR_JOINT_LEFT_FINGERTIPS ].springyPosition += headLean * 0.0f; _joint[ AVATAR_JOINT_RIGHT_COLLAR ].springyPosition += headLean * 0.6f; _joint[ AVATAR_JOINT_RIGHT_SHOULDER ].springyPosition += headLean * 0.6f; _joint[ AVATAR_JOINT_RIGHT_ELBOW ].springyPosition += headLean * 0.2f; _joint[ AVATAR_JOINT_RIGHT_WRIST ].springyPosition += headLean * 0.1f; _joint[ AVATAR_JOINT_RIGHT_FINGERTIPS ].springyPosition += headLean * 0.0f; } // update head state _head.setPositionRotationAndScale( _joint[ AVATAR_JOINT_HEAD_BASE ].springyPosition, glm::vec3(_headYaw, _headPitch, _headRoll), _joint[ AVATAR_JOINT_HEAD_BASE ].radius ); _head.setAudioLoudness(_audioLoudness); _head.setSkinColor(glm::vec3(skinColor[0], skinColor[1], skinColor[2])); _head.simulate(deltaTime, _isMine); // use speed and angular velocity to determine walking vs. standing if (_speed + fabs(_bodyYawDelta) > 0.2) { _mode = AVATAR_MODE_WALKING; } else { _mode = AVATAR_MODE_INTERACTING; } } void Avatar::updateHandMovementAndTouching(float deltaTime) { // reset hand and arm positions according to hand movement glm::vec3 transformedHandMovement = _orientation.getRight() * _movedHandOffset.x * 2.0f + _orientation.getUp() * -_movedHandOffset.y * 1.0f + _orientation.getFront() * -_movedHandOffset.y * 1.0f; _joint[ AVATAR_JOINT_RIGHT_FINGERTIPS ].position += transformedHandMovement; if (_isMine) { _avatarTouch.setMyBodyPosition(_position); Avatar * _interactingOther = NULL; float closestDistance = std::numeric_limits<float>::max();