// // Head.cpp // interface // // Created by Philip Rosedale on 9/11/12. // adapted by Jeffrey Ventrella, starting on April 2, 2013 // Copyright (c) 2012 Physical, Inc.. All rights reserved. // #include #include #include #include #include #include #include #include "Head.h" #include #include using namespace std; 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; const float DECAY = 0.1; char iris_texture_file[] = "resources/images/green_eye.png"; vector iris_texture; unsigned int iris_texture_width = 512; unsigned int iris_texture_height = 256; Head::Head() { initializeAvatar(); //position = glm::vec3(0,0,0); //velocity = glm::vec3(0,0,0); //thrust = glm::vec3(0,0,0); for (int i = 0; i < MAX_DRIVE_KEYS; i++) driveKeys[i] = false; PupilSize = 0.10; interPupilDistance = 0.6; interBrowDistance = 0.75; NominalPupilSize = 0.10; Yaw = 0.0; EyebrowPitch[0] = EyebrowPitch[1] = -30; EyebrowRoll[0] = 20; EyebrowRoll[1] = -20; MouthPitch = 0; MouthYaw = 0; MouthWidth = 1.0; MouthHeight = 0.2; EyeballPitch[0] = EyeballPitch[1] = 0; EyeballScaleX = 1.2; EyeballScaleY = 1.5; EyeballScaleZ = 1.0; EyeballYaw[0] = EyeballYaw[1] = 0; PitchTarget = YawTarget = 0; NoiseEnvelope = 1.0; PupilConverge = 10.0; leanForward = 0.0; leanSideways = 0.0; eyeContact = 1; eyeContactTarget = LEFT_EYE; scale = 1.0; renderYaw = 0.0; renderPitch = 0.0; audioAttack = 0.0; loudness = 0.0; averageLoudness = 0.0; lastLoudness = 0.0; browAudioLift = 0.0; noise = 0; handBeingMoved = false; previousHandBeingMoved = false; movedHandOffset = glm::vec3( 0.0, 0.0, 0.0 ); sphere = NULL; usingSprings = false; springForce = 6.0f; springToBodyTightness = 4.0f; springVelocityDecay = 16.0f; hand = new Hand(glm::vec3(skinColor[0], skinColor[1], skinColor[2])); if (iris_texture.size() == 0) { switchToResourcesIfRequired(); unsigned error = lodepng::decode(iris_texture, iris_texture_width, iris_texture_height, iris_texture_file); if (error != 0) { std::cout << "error " << error << ": " << lodepng_error_text(error) << std::endl; } } for (int o=0; ogetRelativeValue(PITCH_RATE); YawRate = serialInterface->getRelativeValue(YAW_RATE); float measured_lateral_accel = serialInterface->getRelativeValue(ACCEL_X) - ROLL_ACCEL_COUPLING*serialInterface->getRelativeValue(ROLL_RATE); float measured_fwd_accel = serialInterface->getRelativeValue(ACCEL_Z) - PITCH_ACCEL_COUPLING*serialInterface->getRelativeValue(PITCH_RATE); float measured_roll_rate = serialInterface->getRelativeValue(ROLL_RATE); //std::cout << "Pitch Rate: " << serialInterface->getRelativeValue(PITCH_RATE) << // " fwd_accel: " << serialInterface->getRelativeValue(ACCEL_Z) << "\n"; //std::cout << "Roll Rate: " << serialInterface->getRelativeValue(ROLL_RATE) << //" ACCEL_X: " << serialInterface->getRelativeValue(ACCEL_X) << "\n"; //std::cout << "Pitch: " << Pitch << "\n"; // Update avatar head position based on measured gyro rates const float HEAD_ROTATION_SCALE = 0.70; const float HEAD_ROLL_SCALE = 0.40; const float HEAD_LEAN_SCALE = 0.01; const float MAX_PITCH = 45; const float MIN_PITCH = -45; const float MAX_YAW = 85; const float MIN_YAW = -85; if ((Pitch < MAX_PITCH) && (Pitch > MIN_PITCH)) addPitch(measured_pitch_rate * -HEAD_ROTATION_SCALE * frametime); addRoll(-measured_roll_rate * HEAD_ROLL_SCALE * frametime); if (head_mirror) { if ((Yaw < MAX_YAW) && (Yaw > MIN_YAW)) addYaw(-YawRate * HEAD_ROTATION_SCALE * frametime); addLean(-measured_lateral_accel * frametime * HEAD_LEAN_SCALE, -measured_fwd_accel*frametime * HEAD_LEAN_SCALE); } else { if ((Yaw < MAX_YAW) && (Yaw > MIN_YAW)) addYaw(YawRate * -HEAD_ROTATION_SCALE * frametime); addLean(measured_lateral_accel * frametime * -HEAD_LEAN_SCALE, measured_fwd_accel*frametime * HEAD_LEAN_SCALE); } } void Head::addLean(float x, float z) { // Add Body lean as impulse leanSideways += x; leanForward += z; } void Head::setLeanForward(float dist){ leanForward = dist; } void Head::setLeanSideways(float dist){ leanSideways = dist; } void Head::simulate(float deltaTime) { //------------------------------------- // DEBUG - other avatars... //------------------------------------- closeEnoughToInteract = 0.5f; closestOtherAvatar = -1; float closestDistance = 10000.0f; /* AgentList * agentList = AgentList::getInstance(); for(std::vector::iterator agent = agentList->getAgents().begin(); agent != agentList->getAgents().end(); agent++) { if (( agent->getLinkedData() != NULL && ( agent->getType() == AGENT_TYPE_INTERFACE ) )) { Head *agentHead = (Head *)agent->getLinkedData(); // when this is working, I will grab the position here... //glm::vec3 pos = agentHead->getPos(); } } */ for (int o=0; osimulate(deltaTime); } void Head::render(int faceToFace, int isMine) { //--------------------------------------------------- // show avatar position //--------------------------------------------------- glPushMatrix(); glTranslatef( position.x, position.y, position.z ); glScalef( 0.03, 0.03, 0.03 ); glutSolidSphere( 1, 10, 10 ); glPopMatrix(); //--------------------------------------------------- // show avatar orientation //--------------------------------------------------- renderOrientationDirections(avatar.bone[ AVATAR_BONE_HEAD ].position, avatar.bone[ AVATAR_BONE_HEAD ].orientation, 0.2f ); //--------------------------------------------------- // render body //--------------------------------------------------- renderBody(); //--------------------------------------------------- // render head //--------------------------------------------------- renderHead( faceToFace, isMine ); //--------------------------------------------------- // render other avatars (DEBUG TEST) //--------------------------------------------------- for (int o=0; orender(1); // Don't render a head if it is really close to your location, because that is your own head! //if (!isMine || faceToFace) { glRotatef(Pitch, 1, 0, 0); glRotatef(Roll, 0, 0, 1); // Overall scale of head if (faceToFace) glScalef(2.0, 2.0, 2.0); else glScalef(0.75, 1.0, 1.0); glColor3fv(skinColor); // Head glutSolidSphere(1, 30, 30); // Ears glPushMatrix(); glTranslatef(1.0, 0, 0); for(side = 0; side < 2; side++) { glPushMatrix(); glScalef(0.3, 0.65, .65); glutSolidSphere(0.5, 30, 30); glPopMatrix(); glTranslatef(-2.0, 0, 0); } glPopMatrix(); // Eyebrows audioAttack = 0.9*audioAttack + 0.1*fabs(loudness - lastLoudness); lastLoudness = loudness; const float BROW_LIFT_THRESHOLD = 100; if (audioAttack > BROW_LIFT_THRESHOLD) browAudioLift += sqrt(audioAttack)/1000.0; browAudioLift *= .90; glPushMatrix(); glTranslatef(-interBrowDistance/2.0,0.4,0.45); for(side = 0; side < 2; side++) { glColor3fv(browColor); glPushMatrix(); glTranslatef(0, 0.35 + browAudioLift, 0); glRotatef(EyebrowPitch[side]/2.0, 1, 0, 0); glRotatef(EyebrowRoll[side]/2.0, 0, 0, 1); glScalef(browWidth, browThickness, 1); glutSolidCube(0.5); glPopMatrix(); glTranslatef(interBrowDistance, 0, 0); } glPopMatrix(); // Mouth glPushMatrix(); glTranslatef(0,-0.35,0.75); glColor3f(0,0,0); glRotatef(MouthPitch, 1, 0, 0); glRotatef(MouthYaw, 0, 0, 1); glScalef(MouthWidth*(.7 + sqrt(averageLoudness)/60.0), MouthHeight*(1.0 + sqrt(averageLoudness)/30.0), 1); glutSolidCube(0.5); glPopMatrix(); glTranslatef(0, 1.0, 0); glTranslatef(-interPupilDistance/2.0,-0.68,0.7); // Right Eye glRotatef(-10, 1, 0, 0); glColor3fv(eyeColor); glPushMatrix(); { glTranslatef(interPupilDistance/10.0, 0, 0.05); glRotatef(20, 0, 0, 1); glScalef(EyeballScaleX, EyeballScaleY, EyeballScaleZ); glutSolidSphere(0.25, 30, 30); } glPopMatrix(); // Right Pupil if (sphere == NULL) { sphere = gluNewQuadric(); gluQuadricTexture(sphere, GL_TRUE); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); gluQuadricOrientation(sphere, GLU_OUTSIDE); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, iris_texture_width, iris_texture_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, &iris_texture[0]); } glPushMatrix(); { glRotatef(EyeballPitch[1], 1, 0, 0); glRotatef(EyeballYaw[1] + PupilConverge, 0, 1, 0); glTranslatef(0,0,.35); glRotatef(-75,1,0,0); glScalef(1.0, 0.4, 1.0); glEnable(GL_TEXTURE_2D); gluSphere(sphere, PupilSize, 15, 15); glDisable(GL_TEXTURE_2D); } glPopMatrix(); // Left Eye glColor3fv(eyeColor); glTranslatef(interPupilDistance, 0, 0); glPushMatrix(); { glTranslatef(-interPupilDistance/10.0, 0, .05); glRotatef(-20, 0, 0, 1); glScalef(EyeballScaleX, EyeballScaleY, EyeballScaleZ); glutSolidSphere(0.25, 30, 30); } glPopMatrix(); // Left Pupil glPushMatrix(); { glRotatef(EyeballPitch[0], 1, 0, 0); glRotatef(EyeballYaw[0] - PupilConverge, 0, 1, 0); glTranslatef(0, 0, .35); glRotatef(-75, 1, 0, 0); glScalef(1.0, 0.4, 1.0); glEnable(GL_TEXTURE_2D); gluSphere(sphere, PupilSize, 15, 15); glDisable(GL_TEXTURE_2D); } glPopMatrix(); } glPopMatrix(); } void Head::setHandMovement( glm::vec3 movement ) { handBeingMoved = true; movedHandOffset = movement; } void Head::initializeAvatar() { //avatar.position = glm::vec3( 0.0, 0.0, 0.0 ); avatar.velocity = glm::vec3( 0.0, 0.0, 0.0 ); avatar.thrust = glm::vec3( 0.0, 0.0, 0.0 ); avatar.orientation.setToIdentity(); closestOtherAvatar = 0; bodyYaw = -90.0; bodyPitch = 0.0; bodyRoll = 0.0; bodyYawDelta = 0.0; for (int b=0; b 0.0f ) { glm::vec3 springDirection = springVector / length; float force = ( length - avatar.bone[b].length ) * springForce * deltaTime; avatar.bone[ b ].springyVelocity -= springDirection * force; avatar.bone[ avatar.bone[b].parent ].springyVelocity += springDirection * force; } avatar.bone[b].springyVelocity += ( avatar.bone[b].position - avatar.bone[b].springyPosition ) * springToBodyTightness * deltaTime; float decay = 1.0 - springVelocityDecay * deltaTime; if ( decay > 0.0 ) { avatar.bone[b].springyVelocity *= decay; } else { avatar.bone[b].springyVelocity = glm::vec3( 0.0f, 0.0f, 0.0f ); } avatar.bone[b].springyPosition += avatar.bone[b].springyVelocity; } } float Head::getBodyYaw() { return bodyYaw; } glm::vec3 Head::getHeadLookatDirection() { return glm::vec3 ( avatar.orientation.getFront().x, avatar.orientation.getFront().y, avatar.orientation.getFront().z ); } glm::vec3 Head::getHeadLookatDirectionUp() { return glm::vec3 ( avatar.orientation.getUp().x, avatar.orientation.getUp().y, avatar.orientation.getUp().z ); } glm::vec3 Head::getHeadLookatDirectionRight() { return glm::vec3 ( avatar.orientation.getRight().x, avatar.orientation.getRight().y, avatar.orientation.getRight().z ); } glm::vec3 Head::getHeadPosition() { return glm::vec3 ( avatar.bone[ AVATAR_BONE_HEAD ].position.x, avatar.bone[ AVATAR_BONE_HEAD ].position.y, avatar.bone[ AVATAR_BONE_HEAD ].position.z ); } glm::vec3 Head::getBodyPosition() { return glm::vec3 ( avatar.bone[ AVATAR_BONE_PELVIS_SPINE ].position.x, avatar.bone[ AVATAR_BONE_PELVIS_SPINE ].position.y, avatar.bone[ AVATAR_BONE_PELVIS_SPINE ].position.z ); } void Head::updateHandMovement() { glm::vec3 transformedHandMovement; transformedHandMovement = avatar.orientation.getRight() * -movedHandOffset.x + avatar.orientation.getUp() * -movedHandOffset.y + avatar.orientation.getFront() * -movedHandOffset.y * 0.4f; //if holding hands, add a pull to the hand... if ( usingSprings ) { if ( closestOtherAvatar != -1 ) { glm::vec3 handShakePull( DEBUG_otherAvatarListPosition[ closestOtherAvatar ]); handShakePull -= avatar.bone[ AVATAR_BONE_RIGHT_HAND ].position; handShakePull *= 0.3; transformedHandMovement += handShakePull; } } avatar.bone[ AVATAR_BONE_RIGHT_HAND ].position += transformedHandMovement; glm::vec3 armVector = avatar.bone[ AVATAR_BONE_RIGHT_HAND ].position; armVector -= avatar.bone[ AVATAR_BONE_RIGHT_SHOULDER ].position; //------------------------------------------------------------------------------- // test to see if right hand is being dragged beyond maximum arm length //------------------------------------------------------------------------------- float distance = glm::length( armVector ); //------------------------------------------------------------------------------- // if right hand is being dragged beyond maximum arm length... //------------------------------------------------------------------------------- if ( distance > avatar.maxArmLength ) { //------------------------------------------------------------------------------- // reset right hand to be constrained to maximum arm length //------------------------------------------------------------------------------- avatar.bone[ AVATAR_BONE_RIGHT_HAND ].position = avatar.bone[ AVATAR_BONE_RIGHT_SHOULDER ].position; glm::vec3 armNormal = armVector / distance; armVector = armNormal * avatar.maxArmLength; distance = avatar.maxArmLength; glm::vec3 constrainedPosition = avatar.bone[ AVATAR_BONE_RIGHT_SHOULDER ].position; constrainedPosition += armVector; avatar.bone[ AVATAR_BONE_RIGHT_HAND ].position = constrainedPosition; } //----------------------------------------------------------------------------- // set elbow position //----------------------------------------------------------------------------- glm::vec3 newElbowPosition = avatar.bone[ AVATAR_BONE_RIGHT_SHOULDER ].position; newElbowPosition += armVector * (float)ONE_HALF; glm::vec3 perpendicular = glm::cross( avatar.orientation.getFront(), armVector ); // XXXBHG - Jeffery, you should clean this up. You can't multiple glm::vec3's by doubles, only floats newElbowPosition += perpendicular * (float)(( 1.0f - ( avatar.maxArmLength / distance ) ) * ONE_HALF); avatar.bone[ AVATAR_BONE_RIGHT_UPPER_ARM ].position = newElbowPosition; //----------------------------------------------------------------------------- // set wrist position //----------------------------------------------------------------------------- glm::vec3 vv( avatar.bone[ AVATAR_BONE_RIGHT_HAND ].position ); vv -= avatar.bone[ AVATAR_BONE_RIGHT_UPPER_ARM ].position; glm::vec3 newWristPosition = avatar.bone[ AVATAR_BONE_RIGHT_UPPER_ARM ].position; newWristPosition += vv * 0.7f; avatar.bone[ AVATAR_BONE_RIGHT_FOREARM ].position = newWristPosition; } void Head::renderBody() { //----------------------------------------- // Render bone positions as spheres //----------------------------------------- for (int b=0; bgetPos().x, hand->getPos().y, hand->getPos().z); //previous to Ventrella change avatar.bone[ AVATAR_BONE_RIGHT_HAND ].position.x, avatar.bone[ AVATAR_BONE_RIGHT_HAND ].position.y, avatar.bone[ AVATAR_BONE_RIGHT_HAND ].position.z ); return strlen(data); } //called on the other agents - assigns it to my views of the others void Head::parseData(void *data, int size) { sscanf ( (char *)data, "H%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f", &Pitch, &Yaw, &Roll, //&avatar.yaw, &avatar.pitch, &avatar.roll, &position.x, &position.y, &position.z, &loudness, &averageLoudness, &avatar.bone[ AVATAR_BONE_RIGHT_HAND ].position.x, &avatar.bone[ AVATAR_BONE_RIGHT_HAND ].position.y, &avatar.bone[ AVATAR_BONE_RIGHT_HAND ].position.z ); handBeingMoved = true; } void Head::SetNewHeadTarget(float pitch, float yaw) { PitchTarget = pitch; YawTarget = yaw; }