History

Merge remote-tracking branch 'upstream/master'

Philip Rosedale committed 13 years ago

Commit dec7e592 parents a89b011 d7d435113 files · +642 −208Browse files

Showing the changes to interface/src (8 of 13 files). Show all

Unified · Split

8 files
  1. interface/src/Audio.cpp +1 −26
  2. interface/src/Audio.h +0 −2
  3. interface/src/AudioData.h +0 −1
  4. interface/src/Avatar.cpp +76 −88
  5. interface/src/Avatar.h +2 −57
  6. interface/src/Head.cpp +378 −0
  7. interface/src/Head.h +93 −0
  8. interface/src/main.cpp +38 −9
interface/src/Audio.cpp+1−26
@@ -71,11 +71,6 @@ float flangeIntensity = 0; · 70 unmodified lines
7171 float flangeRate = 0;
7272 float flangeWeight = 0;
7373
74−int16_t *walkingSoundArray;
75−int walkingSoundSamples;
76−int samplesLeftForWalk = 0;
77−int16_t *sampleWalkPointer;
78−
7974 timeval firstPlaybackTimer;
8075 int packetsReceivedThisPlayback = 0;
8176 float usecsAtStartup = 0;
@@ -357,10 +352,6 @@ bool Audio::getMixerLoopbackFlag() { · 275 unmodified lines
357352 return audioData->mixerLoopbackFlag;
358353 }
359354
360−void Audio::setWalkingState(bool newWalkState) {
361− audioData->playWalkSound = newWalkState;
362−}
363−
364355 /**
365356 * Initialize portaudio and start an audio stream.
366357 * Should be called at the beginning of program exection.
@@ -368,23 +359,7 @@ void Audio::setWalkingState(bool newWalkState) { · 1 unmodified line
368359 * @return Returns true if successful or false if an error occurred.
369360 Use Audio::getError() to retrieve the error code.
370361 */
371−Audio::Audio(Oscilloscope *s, Avatar *linkedAvatar)
372−{
373− // read the walking sound from the raw file and store it
374− // in the in memory array
375−
376− switchToResourcesParentIfRequired();
377− FILE *soundFile = fopen("resources/audio/walking.raw", "r");
378−
379− // get length of file:
380− std::fseek(soundFile, 0, SEEK_END);
381− walkingSoundSamples = std::ftell(soundFile) / sizeof(int16_t);
382− walkingSoundArray = new int16_t[walkingSoundSamples];
383− std::rewind(soundFile);
384−
385− std::fread(walkingSoundArray, sizeof(int16_t), walkingSoundSamples, soundFile);
386− std::fclose(soundFile);
387−
362+Audio::Audio(Oscilloscope* s, Avatar* linkedAvatar) {
388363 paError = Pa_Initialize();
389364 if (paError != paNoError) goto error;
390365
interface/src/Audio.h+0−2
@@ -28,8 +28,6 @@ public: · 27 unmodified lines
2828 float getInputLoudness() const;
2929 void updateMixerParams(in_addr_t mixerAddress, in_port_t mixerPort);
3030
31− void setWalkingState(bool newWalkState);
32−
3331 void setLastAcceleration(glm::vec3 a) { audioData->setLastAcceleration(a); };
3432 void setLastVelocity(glm::vec3 v) { audioData->setLastVelocity(v); };
3533
interface/src/AudioData.h+0−1
@@ -38,7 +38,6 @@ class AudioData { · 37 unmodified lines
3838 float lastInputLoudness;
3939
4040 bool mixerLoopbackFlag;
41− bool playWalkSound;
4241
4342 // Added avatar acceleration and velocity for procedural effects sounds from client
4443 void setLastVelocity(glm::vec3 v) { _lastVelocity = v; };
interface/src/Avatar.cpp+76−88
@@ -11,6 +11,7 @@ · 10 unmodified lines
1111 #include <lodepng.h>
1212 #include <SharedUtil.h>
1313 #include "Avatar.h"
14+#include "Head.h"
1415 #include "Log.h"
1516 #include "ui/TextRenderer.h"
1617 #include <AgentList.h>
@@ -23,12 +24,11 @@ const bool BALLS_ON = false; · 6 unmodified lines
2324 const bool USING_AVATAR_GRAVITY = true;
2425 const float GRAVITY_SCALE = 10.0f;
2526 const float BOUNCE = 0.3f;
26−const float DECAY = 0.1;
2727 const float THRUST_MAG = 1200.0;
2828 const float YAW_MAG = 500.0;
2929 const float BODY_SPIN_FRICTION = 5.0;
3030 const float BODY_UPRIGHT_FORCE = 10.0;
31−const float BODY_PITCH_WHILE_WALKING = 30.0;
31+const float BODY_PITCH_WHILE_WALKING = 40.0;
3232 const float BODY_ROLL_WHILE_TURNING = 0.1;
3333 const float VELOCITY_DECAY = 5.0;
3434 const float MY_HAND_HOLDING_PULL = 0.2;
@@ -52,6 +52,8 @@ const float AVATAR_BRAKING_STRENGTH = 30.0f; · 17 unmodified lines
5252
5353 float skinColor [] = {1.0, 0.84, 0.66};
5454 float lightBlue [] = {0.7, 0.8, 1.0};
55+
56+/*
5557 float browColor [] = {210.0/255.0, 105.0/255.0, 30.0/255.0};
5658 float mouthColor[] = {1, 0, 0};
5759
@@ -64,16 +66,19 @@ float MouthWidthChoices[3] = {0.5, 0.77, 0.3}; · 6 unmodified lines
6466 float browWidth = 0.8;
6567 float browThickness = 0.16;
6668
67−bool usingBigSphereCollisionTest = true;
6869
69−char iris_texture_file[] = "resources/images/green_eye.png";
70+//char iris_texture_file[] = "resources/images/green_eye.png";
71+*/
72+bool usingBigSphereCollisionTest = true;
7073
7174 float chatMessageScale = 0.0015;
7275 float chatMessageHeight = 0.45;
7376
77+/*
7478 vector<unsigned char> iris_texture;
7579 unsigned int iris_texture_width = 512;
7680 unsigned int iris_texture_height = 256;
81+*/
7782
7883 Avatar::Avatar(bool isMine) {
7984
@@ -104,43 +109,8 @@ Avatar::Avatar(bool isMine) { · 24 unmodified lines
104109
105110 for (int i = 0; i < MAX_DRIVE_KEYS; i++) _driveKeys[i] = false;
106111
107− _head.pupilSize = 0.10;
108− _head.interPupilDistance = 0.6;
109− _head.interBrowDistance = 0.75;
110− _head.nominalPupilSize = 0.10;
111− _head.pitchRate = 0.0;
112− _head.yawRate = 0.0;
113− _head.rollRate = 0.0;
114− _head.eyebrowPitch[0] = -30;
115− _head.eyebrowPitch[1] = -30;
116− _head.eyebrowRoll [0] = 20;
117− _head.eyebrowRoll [1] = -20;
118− _head.mouthPitch = 0;
119− _head.mouthYaw = 0;
120− _head.mouthWidth = 1.0;
121− _head.mouthHeight = 0.2;
122− _head.eyeballPitch[0] = 0;
123− _head.eyeballPitch[1] = 0;
124− _head.eyeballScaleX = 1.2;
125− _head.eyeballScaleY = 1.5;
126− _head.eyeballScaleZ = 1.0;
127− _head.eyeballYaw[0] = 0;
128− _head.eyeballYaw[1] = 0;
129− _head.pitchTarget = 0;
130− _head.yawTarget = 0;
131− _head.noiseEnvelope = 1.0;
132− _head.pupilConverge = 10.0;
133− _head.leanForward = 0.0;
134− _head.leanSideways = 0.0;
135− _head.eyeContact = 1;
136− _head.eyeContactTarget = LEFT_EYE;
137− _head.scale = 1.0;
138− _head.audioAttack = 0.0;
139− _head.averageLoudness = 0.0;
140− _head.lastLoudness = 0.0;
141− _head.browAudioLift = 0.0;
142− _head.noise = 0;
143− _head.returnSpringScale = 1.0;
112+ _head.initialize();
113+
144114 _movedHandOffset = glm::vec3(0.0f, 0.0f, 0.0f);
145115 _renderYaw = 0.0;
146116 _renderPitch = 0.0;
@@ -153,6 +123,7 @@ Avatar::Avatar(bool isMine) { · 6 unmodified lines
153123
154124 _avatarTouch.setReachableRadius(0.6);
155125
126+ /*
156127 if (iris_texture.size() == 0) {
157128 switchToResourcesParentIfRequired();
158129 unsigned error = lodepng::decode(iris_texture, iris_texture_width, iris_texture_height, iris_texture_file);
@@ -160,6 +131,7 @@ Avatar::Avatar(bool isMine) { · 1 unmodified line
160131 printLog("error %u: %s\n", error, lodepng_error_text(error));
161132 }
162133 }
134+*/
163135
164136 if (BALLS_ON) { _balls = new Balls(100); }
165137 else { _balls = NULL; }
@@ -239,7 +211,8 @@ Avatar::Avatar(const Avatar &otherAvatar) { · 73 unmodified lines
239211 _distanceToNearestAvatar = otherAvatar._distanceToNearestAvatar;
240212
241213 initializeSkeleton();
242−
214+
215+/*
243216 if (iris_texture.size() == 0) {
244217 switchToResourcesParentIfRequired();
245218 unsigned error = lodepng::decode(iris_texture, iris_texture_width, iris_texture_height, iris_texture_file);
@@ -247,6 +220,7 @@ Avatar::Avatar(const Avatar &otherAvatar) { · 1 unmodified line
247220 printLog("error %u: %s\n", error, lodepng_error_text(error));
248221 }
249222 }
223+*/
250224 }
251225
252226 Avatar::~Avatar() {
@@ -452,8 +426,52 @@ void Avatar::simulate(float deltaTime) { · 199 unmodified lines
452426 }
453427 }
454428
429+
430+
431+
432+
433+ // Get head position data from network for other people
434+ if (!_isMine) {
435+ _head.leanSideways = getHeadLeanSideways();
436+ _head.leanForward = getHeadLeanForward();
437+ }
438+
439+ //apply the head lean values to the springy position...
440+ if (fabs(_head.leanSideways + _head.leanForward) > 0.0f) {
441+ glm::vec3 headLean =
442+ _orientation.getRight() * _head.leanSideways +
443+ _orientation.getFront() * _head.leanForward;
444+
445+ // this is not a long-term solution, but it works ok for initial purposes of making the avatar lean
446+ _joint[ AVATAR_JOINT_TORSO ].springyPosition += headLean * 0.1f;
447+ _joint[ AVATAR_JOINT_CHEST ].springyPosition += headLean * 0.4f;
448+ _joint[ AVATAR_JOINT_NECK_BASE ].springyPosition += headLean * 0.7f;
449+ _joint[ AVATAR_JOINT_HEAD_BASE ].springyPosition += headLean * 1.0f;
450+
451+ _joint[ AVATAR_JOINT_LEFT_COLLAR ].springyPosition += headLean * 0.6f;
452+ _joint[ AVATAR_JOINT_LEFT_SHOULDER ].springyPosition += headLean * 0.6f;
453+ _joint[ AVATAR_JOINT_LEFT_ELBOW ].springyPosition += headLean * 0.2f;
454+ _joint[ AVATAR_JOINT_LEFT_WRIST ].springyPosition += headLean * 0.1f;
455+ _joint[ AVATAR_JOINT_LEFT_FINGERTIPS ].springyPosition += headLean * 0.0f;
456+
457+ _joint[ AVATAR_JOINT_RIGHT_COLLAR ].springyPosition += headLean * 0.6f;
458+ _joint[ AVATAR_JOINT_RIGHT_SHOULDER ].springyPosition += headLean * 0.6f;
459+ _joint[ AVATAR_JOINT_RIGHT_ELBOW ].springyPosition += headLean * 0.2f;
460+ _joint[ AVATAR_JOINT_RIGHT_WRIST ].springyPosition += headLean * 0.1f;
461+ _joint[ AVATAR_JOINT_RIGHT_FINGERTIPS ].springyPosition += headLean * 0.0f;
462+ }
463+
464+
455465 // update head state
456− updateHead(deltaTime);
466+ _head.setPositionRotationAndScale(
467+ _joint[ AVATAR_JOINT_HEAD_BASE ].springyPosition,
468+ glm::vec3(_headYaw, _headPitch, _headRoll),
469+ _joint[ AVATAR_JOINT_HEAD_BASE ].radius
470+ );
471+
472+ _head.setAudioLoudness(_audioLoudness);
473+ _head.setSkinColor(glm::vec3(skinColor[0], skinColor[1], skinColor[2]));
474+ _head.simulate(deltaTime, _isMine);
457475
458476 // use speed and angular velocity to determine walking vs. standing
459477 if (_speed + fabs(_bodyYawDelta) > 0.2) {
@@ -463,7 +481,6 @@ void Avatar::simulate(float deltaTime) { · 3 unmodified lines
463481 }
464482 }
465483
466−
467484 void Avatar::updateHandMovementAndTouching(float deltaTime) {
468485
469486 // reset hand and arm positions according to hand movement
@@ -540,7 +557,6 @@ void Avatar::updateHandMovementAndTouching(float deltaTime) { · 70 unmodified lines
540557 _avatarTouch.setHoldingHands(false);
541558 }
542559
543−
544560 //if holding hands, apply the appropriate forces
545561 if (_avatarTouch.getHoldingHands()) {
546562 _joint[ AVATAR_JOINT_RIGHT_FINGERTIPS ].position +=
@@ -549,8 +565,8 @@ void Avatar::updateHandMovementAndTouching(float deltaTime) { · 2 unmodified lines
549565 - _joint[ AVATAR_JOINT_RIGHT_FINGERTIPS ].position
550566 ) * 0.5f;
551567
552− if (distanceBetweenOurHands > 0.2) {
553− float force = 700.0f * deltaTime;
568+ if (distanceBetweenOurHands > 0.3) {
569+ float force = 10.0f * deltaTime;
554570 if (force > 1.0f) {force = 1.0f;}
555571 _velocity += vectorFromMyHandToYourHand * force;
556572 }
@@ -579,37 +595,7 @@ void Avatar::updateHandMovementAndTouching(float deltaTime) { · 22 unmodified lines
579595
580596 void Avatar::updateHead(float deltaTime) {
581597
582− // Get head position data from network for other people
583− if (!_isMine) {
584− _head.leanSideways = getHeadLeanSideways();
585− _head.leanForward = getHeadLeanForward();
586− }
587−
588− //apply the head lean values to the springy position...
589− if (fabs(_head.leanSideways + _head.leanForward) > 0.0f) {
590− glm::vec3 headLean =
591− _orientation.getRight() * _head.leanSideways +
592− _orientation.getFront() * _head.leanForward;
593−
594− // this is not a long-term solution, but it works ok for initial purposes of making the avatar lean
595− _joint[ AVATAR_JOINT_TORSO ].springyPosition += headLean * 0.1f;
596− _joint[ AVATAR_JOINT_CHEST ].springyPosition += headLean * 0.4f;
597− _joint[ AVATAR_JOINT_NECK_BASE ].springyPosition += headLean * 0.7f;
598− _joint[ AVATAR_JOINT_HEAD_BASE ].springyPosition += headLean * 1.0f;
599−
600− _joint[ AVATAR_JOINT_LEFT_COLLAR ].springyPosition += headLean * 0.6f;
601− _joint[ AVATAR_JOINT_LEFT_SHOULDER ].springyPosition += headLean * 0.6f;
602− _joint[ AVATAR_JOINT_LEFT_ELBOW ].springyPosition += headLean * 0.2f;
603− _joint[ AVATAR_JOINT_LEFT_WRIST ].springyPosition += headLean * 0.1f;
604− _joint[ AVATAR_JOINT_LEFT_FINGERTIPS ].springyPosition += headLean * 0.0f;
605−
606− _joint[ AVATAR_JOINT_RIGHT_COLLAR ].springyPosition += headLean * 0.6f;
607− _joint[ AVATAR_JOINT_RIGHT_SHOULDER ].springyPosition += headLean * 0.6f;
608− _joint[ AVATAR_JOINT_RIGHT_ELBOW ].springyPosition += headLean * 0.2f;
609− _joint[ AVATAR_JOINT_RIGHT_WRIST ].springyPosition += headLean * 0.1f;
610− _joint[ AVATAR_JOINT_RIGHT_FINGERTIPS ].springyPosition += headLean * 0.0f;
611− }
612−
598+/*
613599 // Decay head back to center if turned on
614600 if (_isMine && _returnHeadToCenter) {
615601 // Decay back toward center
@@ -712,6 +698,7 @@ void Avatar::updateHead(float deltaTime) { · 96 unmodified lines
712698 const float AUDIO_AVERAGING_SECS = 0.05;
713699 _head.averageLoudness = (1.f - deltaTime / AUDIO_AVERAGING_SECS) * _head.averageLoudness +
714700 (deltaTime / AUDIO_AVERAGING_SECS) * _audioLoudness;
701+*/
715702 }
716703
717704
@@ -894,7 +881,7 @@ void Avatar::render(bool lookingInMirror, glm::vec3 cameraPosition) { · 176 unmodified lines
894881
895882 // render head
896883 if (_displayingHead) {
897− renderHead(lookingInMirror);
884+ _head.render(lookingInMirror, _bodyYaw);
898885 }
899886
900887 // if this is my avatar, then render my interactions with the other avatar
@@ -952,7 +939,10 @@ void Avatar::render(bool lookingInMirror, glm::vec3 cameraPosition) { · 51 unmodified lines
952939 }
953940 }
954941
942+
943+
955944 void Avatar::renderHead(bool lookingInMirror) {
945+/*
956946 int side = 0;
957947
958948 glEnable(GL_DEPTH_TEST);
@@ -1075,7 +1065,7 @@ void Avatar::renderHead(bool lookingInMirror) { · 116 unmodified lines
10751065 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
10761066 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
10771067 gluQuadricOrientation(_sphere, GLU_OUTSIDE);
1078− glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, iris_texture_width, iris_texture_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, &iris_texture[0]);
1068+//glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, iris_texture_width, iris_texture_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, &iris_texture[0]);
10791069 }
10801070
10811071 glPushMatrix();
@@ -1121,8 +1111,10 @@ void Avatar::renderHead(bool lookingInMirror) { · 39 unmodified lines
11211111
11221112
11231113 glPopMatrix();
1114+ */
11241115 }
11251116
1117+
11261118 void Avatar::setHandMovementValues(glm::vec3 handOffset) {
11271119 _movedHandOffset = handOffset;
11281120 }
@@ -1479,11 +1471,6 @@ void Avatar::renderBody() { · 350 unmodified lines
14791471 }
14801472 }
14811473
1482−void Avatar::SetNewHeadTarget(float pitch, float yaw) {
1483− _head.pitchTarget = pitch;
1484− _head.yawTarget = yaw;
1485−}
1486−
14871474 //
14881475 // Process UDP interface data from Android transmitter or Google Glass
14891476 //
@@ -1522,12 +1509,12 @@ void Avatar::processTransmitterData(unsigned char* packetData, int numBytes) { · 32 unmodified lines
15221509 // If first packet received, note time, turn head spring return OFF, get start rotation
15231510 gettimeofday(&_transmitterTimer, NULL);
15241511 if (deviceType == DEVICE_GLASS) {
1525− setHeadReturnToCenter(true);
1526− setHeadSpringScale(10.f);
1512+ _head.setReturnToCenter(true);
1513+ _head.setSpringScale(10.f);
15271514 printLog("Using Google Glass to drive head, springs ON.\n");
15281515
15291516 } else {
1530− setHeadReturnToCenter(false);
1517+ _head.setReturnToCenter(false);
15311518 printLog("Using Transmitter %s to drive head, springs OFF.\n", device);
15321519
15331520 }
@@ -1536,6 +1523,7 @@ void Avatar::processTransmitterData(unsigned char* packetData, int numBytes) { · 2 unmodified lines
15361523
15371524 _transmitterInitialReading = glm::vec3(rot3, rot2, rot1);
15381525 }
1526+
15391527 const int TRANSMITTER_COUNT = 100;
15401528 if (_transmitterPackets % TRANSMITTER_COUNT == 0) {
15411529 // Every 100 packets, record the observed Hz of the transmitter data
interface/src/Avatar.h+2−57
@@ -18,8 +18,7 @@ · 17 unmodified lines
1818 #include "InterfaceConfig.h"
1919 #include "SerialInterface.h"
2020 #include "Balls.h"
21−
22−enum eyeContactTargets {LEFT_EYE, RIGHT_EYE, MOUTH};
21+#include "Head.h"
2322
2423 enum DriveKeys
2524 {
@@ -82,10 +81,8 @@ public: · 56 unmodified lines
8281 Avatar* clone() const;
8382
8483 void reset();
85−
8684 void updateHeadFromGyros(float frametime, SerialInterface * serialInterface, glm::vec3 * gravity);
8785 void setNoise (float mag) {_head.noise = mag;}
88− void setScale(float s) {_head.scale = s; };
8986 void setRenderYaw(float y) {_renderYaw = y;}
9087 void setRenderPitch(float p) {_renderPitch = p;}
9188 float getRenderYaw() {return _renderYaw;}
@@ -124,11 +121,6 @@ public: · 32 unmodified lines
124121 void updateArmIKAndConstraints( float deltaTime );
125122 void setDisplayingHead( bool displayingHead );
126123
127− float getAverageLoudness() {return _head.averageLoudness;};
128− void setAverageLoudness(float al) {_head.averageLoudness = al;};
129−
130− void SetNewHeadTarget(float, float);
131−
132124 // Set what driving keys are being pressed to control thrust levels
133125 void setDriveKeys(int key, bool val) { _driveKeys[key] = val; };
134126 bool getDriveKeys(int key) { return _driveKeys[key]; };
@@ -165,48 +157,7 @@ private: · 30 unmodified lines
165157 bool isCollidable; // when false, the joint position will not register a collision
166158 };
167159
168− struct AvatarHead
169− {
170− float pitchRate;
171− float yawRate;
172− float rollRate;
173− float noise;
174− float eyeballPitch[2];
175− float eyeballYaw [2];
176− float eyebrowPitch[2];
177− float eyebrowRoll [2];
178− float eyeballScaleX;
179− float eyeballScaleY;
180− float eyeballScaleZ;
181− float interPupilDistance;
182− float interBrowDistance;
183− float nominalPupilSize;
184− float pupilSize;
185− float mouthPitch;
186− float mouthYaw;
187− float mouthWidth;
188− float mouthHeight;
189− float leanForward;
190− float leanSideways;
191− float pitchTarget;
192− float yawTarget;
193− float noiseEnvelope;
194− float pupilConverge;
195− float scale;
196− int eyeContact;
197− float browAudioLift;
198− eyeContactTargets eyeContactTarget;
199−
200− // Sound loudness information
201− float lastLoudness;
202− float averageLoudness;
203− float audioAttack;
204−
205− // Strength of return springs
206− float returnSpringScale;
207− };
208−
209− AvatarHead _head;
160+ Head _head;
210161 bool _isMine;
211162 glm::vec3 _TEST_bigSpherePosition;
212163 float _TEST_bigSphereRadius;
@@ -239,7 +190,6 @@ private: · 26 unmodified lines
239190 Balls* _balls;
240191 AvatarTouch _avatarTouch;
241192 bool _displayingHead; // should be false if in first-person view
242− bool _returnHeadToCenter;
243193 float _distanceToNearestAvatar; // How close is the nearest avatar?
244194 glm::vec3 _gravity;
245195
@@ -256,11 +206,6 @@ private: · 10 unmodified lines
256206 void updateCollisionWithSphere( glm::vec3 position, float radius, float deltaTime );
257207 void applyCollisionWithOtherAvatar( Avatar * other, float deltaTime );
258208 void setHeadFromGyros(glm::vec3 * eulerAngles, glm::vec3 * angularVelocity, float deltaTime, float smoothingTime);
259− void setHeadSpringScale(float s) { _head.returnSpringScale = s; }
260−
261− // Do you want head to try to return to center (depends on interface detected)
262− void setHeadReturnToCenter(bool r) { _returnHeadToCenter = r; };
263− const bool getHeadReturnToCenter() const { return _returnHeadToCenter; };
264209 };
265210
266211 #endif
interface/src/Head.cpp+378−0
@@ -0,0 +1,378 @@
1+//
2+// Head.cpp
3+// hifi
4+//
5+// Created by Jeffrey on May, 10, 2013
6+// Copyright (c) 2013 Physical, Inc.. All rights reserved.
7+//
8+
9+#include "Head.h"
10+#include <vector>
11+#include <SharedUtil.h>
12+#include <lodepng.h>
13+
14+using namespace std;
15+
16+const float HEAD_MOTION_DECAY = 0.1;
17+
18+
19+float _browColor [] = {210.0/255.0, 105.0/255.0, 30.0/255.0};
20+float _mouthColor[] = {1, 0, 0};
21+
22+float _BrowRollAngle [5] = {0, 15, 30, -30, -15};
23+float _BrowPitchAngle[3] = {-70, -60, -50};
24+float _eyeColor [3] = {1,1,1};
25+
26+float _MouthWidthChoices[3] = {0.5, 0.77, 0.3};
27+
28+float _browWidth = 0.8;
29+float _browThickness = 0.16;
30+
31+char iris_texture_file[] = "resources/images/green_eye.png";
32+
33+vector<unsigned char> iris_texture;
34+unsigned int iris_texture_width = 512;
35+unsigned int iris_texture_height = 256;
36+
37+Head::Head() {
38+ if (iris_texture.size() == 0) {
39+ switchToResourcesParentIfRequired();
40+ unsigned error = lodepng::decode(iris_texture, iris_texture_width, iris_texture_height, iris_texture_file);
41+ if (error != 0) {
42+ printLog("error %u: %s\n", error, lodepng_error_text(error));
43+ }
44+ }
45+}
46+
47+void Head::initialize() {
48+
49+ audioLoudness = 0.0;
50+ skinColor = glm::vec3(0.0f, 0.0f, 0.0f);
51+ position = glm::vec3(0.0f, 0.0f, 0.0f);
52+ yaw = 0.0f;
53+ pitch = 0.0f;
54+ roll = 0.0f;
55+ pupilSize = 0.10;
56+ interPupilDistance = 0.6;
57+ interBrowDistance = 0.75;
58+ nominalPupilSize = 0.10;
59+ pitchRate = 0.0;
60+ yawRate = 0.0;
61+ rollRate = 0.0;
62+ eyebrowPitch[0] = -30;
63+ eyebrowPitch[1] = -30;
64+ eyebrowRoll [0] = 20;
65+ eyebrowRoll [1] = -20;
66+ mouthPitch = 0;
67+ mouthYaw = 0;
68+ mouthWidth = 1.0;
69+ mouthHeight = 0.2;
70+ eyeballPitch[0] = 0;
71+ eyeballPitch[1] = 0;
72+ eyeballScaleX = 1.2;
73+ eyeballScaleY = 1.5;
74+ eyeballScaleZ = 1.0;
75+ eyeballYaw[0] = 0;
76+ eyeballYaw[1] = 0;
77+ pitchTarget = 0;
78+ yawTarget = 0;
79+ noiseEnvelope = 1.0;
80+ pupilConverge = 10.0;
81+ leanForward = 0.0;
82+ leanSideways = 0.0;
83+ eyeContact = 1;
84+ eyeContactTarget = LEFT_EYE;
85+ scale = 1.0;
86+ audioAttack = 0.0;
87+ averageLoudness = 0.0;
88+ lastLoudness = 0.0;
89+ browAudioLift = 0.0;
90+ noise = 0;
91+ returnSpringScale = 1.0;
92+ sphere = NULL;
93+}
94+
95+void Head::setPositionRotationAndScale(glm::vec3 p, glm::vec3 r, float s) {
96+
97+ position = p;
98+ scale = s;
99+ yaw = r.x;
100+ pitch = r.y;
101+ roll = r.z;
102+}
103+
104+void Head::setSkinColor(glm::vec3 c) {
105+ skinColor = c;
106+}
107+
108+void Head::setAudioLoudness(float loudness) {
109+ audioLoudness = loudness;
110+}
111+
112+
113+void Head::setNewTarget(float pitch, float yaw) {
114+ pitchTarget = pitch;
115+ yawTarget = yaw;
116+}
117+
118+void Head::simulate(float deltaTime, bool isMine) {
119+
120+ // Decay head back to center if turned on
121+ if (isMine && returnHeadToCenter) {
122+ // Decay back toward center
123+ pitch *= (1.0f - HEAD_MOTION_DECAY * returnSpringScale * 2 * deltaTime);
124+ yaw *= (1.0f - HEAD_MOTION_DECAY * returnSpringScale * 2 * deltaTime);
125+ roll *= (1.0f - HEAD_MOTION_DECAY * returnSpringScale * 2 * deltaTime);
126+ }
127+
128+ // For invensense gyro, decay only slightly when roughly centered
129+ if (isMine) {
130+ const float RETURN_RANGE = 15.0;
131+ const float RETURN_STRENGTH = 2.0;
132+ if (fabs(pitch) < RETURN_RANGE) { pitch *= (1.0f - RETURN_STRENGTH * deltaTime); }
133+ if (fabs(yaw) < RETURN_RANGE) { yaw *= (1.0f - RETURN_STRENGTH * deltaTime); }
134+ if (fabs(roll) < RETURN_RANGE) { roll *= (1.0f - RETURN_STRENGTH * deltaTime); }
135+ }
136+
137+ if (noise) {
138+ // Move toward new target
139+ pitch += (pitchTarget - pitch) * 10 * deltaTime; // (1.f - DECAY*deltaTime)*Pitch + ;
140+ yaw += (yawTarget - yaw ) * 10 * deltaTime; // (1.f - DECAY*deltaTime);
141+ roll *= 1.f - (HEAD_MOTION_DECAY * deltaTime);
142+ }
143+
144+ leanForward *= (1.f - HEAD_MOTION_DECAY * 30 * deltaTime);
145+ leanSideways *= (1.f - HEAD_MOTION_DECAY * 30 * deltaTime);
146+
147+ // Update where the avatar's eyes are
148+ //
149+ // First, decide if we are making eye contact or not
150+ if (randFloat() < 0.005) {
151+ eyeContact = !eyeContact;
152+ eyeContact = 1;
153+ if (!eyeContact) {
154+ // If we just stopped making eye contact,move the eyes markedly away
155+ eyeballPitch[0] = eyeballPitch[1] = eyeballPitch[0] + 5.0 + (randFloat() - 0.5) * 10;
156+ eyeballYaw [0] = eyeballYaw [1] = eyeballYaw [0] + 5.0 + (randFloat() - 0.5) * 5;
157+ } else {
158+ // If now making eye contact, turn head to look right at viewer
159+ setNewTarget(0,0);
160+ }
161+ }
162+
163+ const float DEGREES_BETWEEN_VIEWER_EYES = 3;
164+ const float DEGREES_TO_VIEWER_MOUTH = 7;
165+
166+ if (eyeContact) {
167+ // Should we pick a new eye contact target?
168+ if (randFloat() < 0.01) {
169+ // Choose where to look next
170+ if (randFloat() < 0.1) {
171+ eyeContactTarget = MOUTH;
172+ } else {
173+ if (randFloat() < 0.5) eyeContactTarget = LEFT_EYE; else eyeContactTarget = RIGHT_EYE;
174+ }
175+ }
176+ // Set eyeball pitch and yaw to make contact
177+ float eye_target_yaw_adjust = 0;
178+ float eye_target_pitch_adjust = 0;
179+ if (eyeContactTarget == LEFT_EYE) eye_target_yaw_adjust = DEGREES_BETWEEN_VIEWER_EYES;
180+ if (eyeContactTarget == RIGHT_EYE) eye_target_yaw_adjust = -DEGREES_BETWEEN_VIEWER_EYES;
181+ if (eyeContactTarget == MOUTH) eye_target_pitch_adjust = DEGREES_TO_VIEWER_MOUTH;
182+
183+ eyeballPitch[0] = eyeballPitch[1] = -pitch + eye_target_pitch_adjust;
184+ eyeballYaw [0] = eyeballYaw [1] = yaw + eye_target_yaw_adjust;
185+ }
186+
187+ if (noise)
188+ {
189+ pitch += (randFloat() - 0.5) * 0.2 * noiseEnvelope;
190+ yaw += (randFloat() - 0.5) * 0.3 *noiseEnvelope;
191+ //PupilSize += (randFloat() - 0.5) * 0.001*NoiseEnvelope;
192+
193+ if (randFloat() < 0.005) mouthWidth = _MouthWidthChoices[rand()%3];
194+
195+ if (!eyeContact) {
196+ if (randFloat() < 0.01) eyeballPitch[0] = eyeballPitch[1] = (randFloat() - 0.5) * 20;
197+ if (randFloat() < 0.01) eyeballYaw[0] = eyeballYaw[1] = (randFloat()- 0.5) * 10;
198+ }
199+
200+ if ((randFloat() < 0.005) && (fabs(pitchTarget - pitch) < 1.0) && (fabs(yawTarget - yaw) < 1.0)) {
201+ setNewTarget((randFloat()-0.5) * 20.0, (randFloat()-0.5) * 45.0);
202+ }
203+
204+ if (0) {
205+
206+ // Pick new target
207+ pitchTarget = (randFloat() - 0.5) * 45;
208+ yawTarget = (randFloat() - 0.5) * 22;
209+ }
210+ if (randFloat() < 0.01)
211+ {
212+ eyebrowPitch[0] = eyebrowPitch[1] = _BrowPitchAngle[rand()%3];
213+ eyebrowRoll [0] = eyebrowRoll[1] = _BrowRollAngle[rand()%5];
214+ eyebrowRoll [1] *=-1;
215+ }
216+ }
217+
218+ // Update audio trailing average for rendering facial animations
219+ const float AUDIO_AVERAGING_SECS = 0.05;
220+ averageLoudness = (1.f - deltaTime / AUDIO_AVERAGING_SECS) * averageLoudness +
221+ (deltaTime / AUDIO_AVERAGING_SECS) * audioLoudness;
222+}
223+
224+
225+
226+
227+void Head::render(bool lookingInMirror, float bodyYaw) {
228+
229+ int side = 0;
230+
231+ glEnable(GL_DEPTH_TEST);
232+ glEnable(GL_RESCALE_NORMAL);
233+
234+ glPushMatrix();
235+
236+ glTranslatef(position.x, position.y, position.z);
237+
238+ glScalef(scale, scale, scale);
239+
240+ if (lookingInMirror) {
241+ glRotatef(bodyYaw - yaw, 0, 1, 0);
242+ glRotatef(pitch, 1, 0, 0);
243+ glRotatef(-roll, 0, 0, 1);
244+ } else {
245+ glRotatef(bodyYaw + yaw, 0, 1, 0);
246+ glRotatef(pitch, 1, 0, 0);
247+ glRotatef(roll, 0, 0, 1);
248+ }
249+
250+ //glScalef(2.0, 2.0, 2.0);
251+ glColor3f(skinColor.x, skinColor.y, skinColor.z);
252+
253+ glutSolidSphere(1, 30, 30);
254+
255+ // Ears
256+ glPushMatrix();
257+ glTranslatef(1.0, 0, 0);
258+ for(side = 0; side < 2; side++) {
259+ glPushMatrix();
260+ glScalef(0.3, 0.65, .65);
261+ glutSolidSphere(0.5, 30, 30);
262+ glPopMatrix();
263+ glTranslatef(-2.0, 0, 0);
264+ }
265+ glPopMatrix();
266+
267+ // Update audio attack data for facial animation (eyebrows and mouth)
268+ audioAttack = 0.9 * audioAttack + 0.1 * fabs(audioLoudness - lastLoudness);
269+ lastLoudness = audioLoudness;
270+
271+
272+ const float BROW_LIFT_THRESHOLD = 100;
273+ if (audioAttack > BROW_LIFT_THRESHOLD)
274+ browAudioLift += sqrt(audioAttack) / 1000.0;
275+
276+ browAudioLift *= .90;
277+
278+ // Render Eyebrows
279+ glPushMatrix();
280+ glTranslatef(-interBrowDistance / 2.0,0.4,0.45);
281+ for(side = 0; side < 2; side++) {
282+ glColor3fv(_browColor);
283+ glPushMatrix();
284+ glTranslatef(0, 0.35 + browAudioLift, 0);
285+ glRotatef(eyebrowPitch[side]/2.0, 1, 0, 0);
286+ glRotatef(eyebrowRoll[side]/2.0, 0, 0, 1);
287+ glScalef(_browWidth, _browThickness, 1);
288+ glutSolidCube(0.5);
289+ glPopMatrix();
290+ glTranslatef(interBrowDistance, 0, 0);
291+ }
292+ glPopMatrix();
293+
294+ // Mouth
295+ glPushMatrix();
296+ glTranslatef(0,-0.35,0.75);
297+ glColor3f(0,0,0);
298+ glRotatef(mouthPitch, 1, 0, 0);
299+ glRotatef(mouthYaw, 0, 0, 1);
300+ if (averageLoudness > 1.f) {
301+ glScalef(mouthWidth * (.7f + sqrt(averageLoudness) /60.f),
302+ mouthHeight * (1.f + sqrt(averageLoudness) /30.f), 1);
303+ } else {
304+ glScalef(mouthWidth, mouthHeight, 1);
305+ }
306+
307+ glutSolidCube(0.5);
308+ glPopMatrix();
309+
310+ glTranslatef(0, 1.0, 0);
311+
312+ glTranslatef(-interPupilDistance/2.0,-0.68,0.7);
313+ // Right Eye
314+ glRotatef(-10, 1, 0, 0);
315+ glColor3fv(_eyeColor);
316+ glPushMatrix();
317+ {
318+ glTranslatef(interPupilDistance/10.0, 0, 0.05);
319+ glRotatef(20, 0, 0, 1);
320+ glScalef(eyeballScaleX, eyeballScaleY, eyeballScaleZ);
321+ glutSolidSphere(0.25, 30, 30);
322+ }
323+ glPopMatrix();
324+
325+ // Right Pupil
326+ if (sphere == NULL) {
327+ sphere = gluNewQuadric();
328+ gluQuadricTexture(sphere, GL_TRUE);
329+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
330+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
331+ gluQuadricOrientation(sphere, GLU_OUTSIDE);
332+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, iris_texture_width, iris_texture_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, &iris_texture[0]);
333+ }
334+
335+ glPushMatrix();
336+ {
337+ glRotatef(eyeballPitch[1], 1, 0, 0);
338+ glRotatef(eyeballYaw[1] + yaw + pupilConverge, 0, 1, 0);
339+ glTranslatef(0,0,.35);
340+ glRotatef(-75,1,0,0);
341+ glScalef(1.0, 0.4, 1.0);
342+
343+ glEnable(GL_TEXTURE_2D);
344+ gluSphere(sphere, pupilSize, 15, 15);
345+ glDisable(GL_TEXTURE_2D);
346+ }
347+
348+ glPopMatrix();
349+ // Left Eye
350+ glColor3fv(_eyeColor);
351+ glTranslatef(interPupilDistance, 0, 0);
352+ glPushMatrix();
353+ {
354+ glTranslatef(-interPupilDistance/10.0, 0, .05);
355+ glRotatef(-20, 0, 0, 1);
356+ glScalef(eyeballScaleX, eyeballScaleY, eyeballScaleZ);
357+ glutSolidSphere(0.25, 30, 30);
358+ }
359+ glPopMatrix();
360+ // Left Pupil
361+ glPushMatrix();
362+ {
363+ glRotatef(eyeballPitch[0], 1, 0, 0);
364+ glRotatef(eyeballYaw[0] + yaw - pupilConverge, 0, 1, 0);
365+ glTranslatef(0, 0, .35);
366+ glRotatef(-75, 1, 0, 0);
367+ glScalef(1.0, 0.4, 1.0);
368+
369+ glEnable(GL_TEXTURE_2D);
370+ gluSphere(sphere, pupilSize, 15, 15);
371+ glDisable(GL_TEXTURE_2D);
372+ }
373+ glPopMatrix();
374+
375+ glPopMatrix();
376+
377+}
378+
interface/src/Head.h+93−0
@@ -0,0 +1,93 @@
1+//
2+// Head.h
3+// hifi
4+//
5+// Created by Jeffrey on May, 10, 2013
6+// Copyright (c) 2013 Physical, Inc.. All rights reserved.
7+//
8+
9+#ifndef hifi_Head_h
10+#define hifi_Head_h
11+
12+#include <glm/glm.hpp>
13+#include <glm/gtc/quaternion.hpp>
14+#include <AvatarData.h>
15+#include "world.h"
16+#include "InterfaceConfig.h"
17+#include "SerialInterface.h"
18+
19+enum eyeContactTargets {LEFT_EYE, RIGHT_EYE, MOUTH};
20+
21+class Head {
22+public:
23+ Head();
24+
25+ void initialize();
26+ void simulate(float deltaTime, bool isMine);
27+ void setPositionRotationAndScale(glm::vec3 position, glm::vec3 rotation, float scale);
28+ void setSkinColor(glm::vec3 color);
29+ void setAudioLoudness(float loudness);
30+ void render(bool lookingInMirror, float bodyYaw);
31+ void setNewTarget(float, float);
32+ void setSpringScale(float s) { returnSpringScale = s; }
33+
34+ // Do you want head to try to return to center (depends on interface detected)
35+ void setReturnToCenter(bool r) { returnHeadToCenter = r; }
36+ const bool getReturnToCenter() const { return returnHeadToCenter; }
37+
38+ float getAverageLoudness() {return averageLoudness;};
39+ void setAverageLoudness(float al) { averageLoudness = al;};
40+
41+//private:
42+// I am making these public for now - just to get the code moved over quickly!
43+
44+ bool returnHeadToCenter;
45+ float audioLoudness;
46+ glm::vec3 skinColor;
47+ glm::vec3 position;
48+ glm::vec3 rotation;
49+ float yaw;
50+ float pitch;
51+ float roll;
52+ float pitchRate;
53+ float yawRate;
54+ float rollRate;
55+ float noise;
56+ float eyeballPitch[2];
57+ float eyeballYaw [2];
58+ float eyebrowPitch[2];
59+ float eyebrowRoll [2];
60+ float eyeballScaleX;
61+ float eyeballScaleY;
62+ float eyeballScaleZ;
63+ float interPupilDistance;
64+ float interBrowDistance;
65+ float nominalPupilSize;
66+ float pupilSize;
67+ float mouthPitch;
68+ float mouthYaw;
69+ float mouthWidth;
70+ float mouthHeight;
71+ float leanForward;
72+ float leanSideways;
73+ float pitchTarget;
74+ float yawTarget;
75+ float noiseEnvelope;
76+ float pupilConverge;
77+ float scale;
78+ int eyeContact;
79+ float browAudioLift;
80+ eyeContactTargets eyeContactTarget;
81+
82+ // Sound loudness information
83+ float lastLoudness;
84+ float averageLoudness;
85+ float audioAttack;
86+
87+ GLUquadric* sphere;
88+
89+ // Strength of return springs
90+ float returnSpringScale;
91+};
92+
93+#endif
interface/src/main.cpp+38−9
@@ -102,6 +102,13 @@ int screenHeight = 800; · 101 unmodified lines
102102 int fullscreen = 0;
103103 float aspectRatio = 1.0f;
104104
105+bool USING_MOUSE_VIEW_SHIFT = false;
106+float MOUSE_VIEW_SHIFT_RATE = 40.0f;
107+float MOUSE_VIEW_SHIFT_YAW_MARGIN = (float)(WIDTH * 0.2f);
108+float MOUSE_VIEW_SHIFT_PITCH_MARGIN = (float)(HEIGHT * 0.2f);
109+float MOUSE_VIEW_SHIFT_YAW_LIMIT = 45.0;
110+float MOUSE_VIEW_SHIFT_PITCH_LIMIT = 30.0;
111+
105112 //CameraMode defaultCameraMode = CAMERA_MODE_FIRST_PERSON;
106113 CameraMode defaultCameraMode = CAMERA_MODE_THIRD_PERSON;
107114
@@ -206,7 +213,6 @@ double elapsedTime; · 98 unmodified lines
206213 timeval applicationStartupTime;
207214 bool justStarted = true;
208215
209−
210216 // Every second, check the frame rates and other stuff
211217 void Timer(int extra) {
212218 gettimeofday(&timerEnd, NULL);
@@ -370,9 +376,7 @@ void sendVoxelEditMessage(PACKET_HEADER header, VoxelDetail& detail) { · 157 unmodified lines
370376 }
371377 }
372378
373−//
374379 // Using gyro data, update both view frustum and avatar head position
375−//
376380 void updateAvatar(float deltaTime) {
377381
378382 // Update my avatar's head position from gyros
@@ -409,7 +413,34 @@ void updateAvatar(float deltaTime) { · 30 unmodified lines
409413 const float RENDER_PITCH_MULTIPLY = 4.f;
410414 myAvatar.setRenderPitch((1.f - renderPitchSpring * deltaTime) * myAvatar.getRenderPitch() +
411415 renderPitchSpring * deltaTime * -myAvatar.getHeadPitch() * RENDER_PITCH_MULTIPLY);
412−
416+ }
417+
418+
419+ if (USING_MOUSE_VIEW_SHIFT)
420+ {
421+ //make it so that when your mouse hits the edge of the screen, the camera shifts
422+ float rightBoundary = (float)WIDTH - MOUSE_VIEW_SHIFT_YAW_MARGIN;
423+ float bottomBoundary = (float)HEIGHT - MOUSE_VIEW_SHIFT_PITCH_MARGIN;
424+
425+ if (mouseX > rightBoundary) {
426+ float f = (mouseX - rightBoundary) / ( (float)WIDTH - rightBoundary);
427+ mouseViewShiftYaw += MOUSE_VIEW_SHIFT_RATE * f * deltaTime;
428+ if (mouseViewShiftYaw > MOUSE_VIEW_SHIFT_YAW_LIMIT) { mouseViewShiftYaw = MOUSE_VIEW_SHIFT_YAW_LIMIT; }
429+ } else if (mouseX < MOUSE_VIEW_SHIFT_YAW_MARGIN) {
430+ float f = 1.0 - (mouseX / MOUSE_VIEW_SHIFT_YAW_MARGIN);
431+ mouseViewShiftYaw -= MOUSE_VIEW_SHIFT_RATE * f * deltaTime;
432+ if (mouseViewShiftYaw < -MOUSE_VIEW_SHIFT_YAW_LIMIT) { mouseViewShiftYaw = -MOUSE_VIEW_SHIFT_YAW_LIMIT; }
433+ }
434+ if (mouseY < MOUSE_VIEW_SHIFT_PITCH_MARGIN) {
435+ float f = 1.0 - (mouseY / MOUSE_VIEW_SHIFT_PITCH_MARGIN);
436+ mouseViewShiftPitch += MOUSE_VIEW_SHIFT_RATE * f * deltaTime;
437+ if ( mouseViewShiftPitch > MOUSE_VIEW_SHIFT_PITCH_LIMIT ) { mouseViewShiftPitch = MOUSE_VIEW_SHIFT_PITCH_LIMIT; }
438+ }
439+ else if (mouseY > bottomBoundary) {
440+ float f = (mouseY - bottomBoundary) / ((float)HEIGHT - bottomBoundary);
441+ mouseViewShiftPitch -= MOUSE_VIEW_SHIFT_RATE * f * deltaTime;
442+ if (mouseViewShiftPitch < -MOUSE_VIEW_SHIFT_PITCH_LIMIT) { mouseViewShiftPitch = -MOUSE_VIEW_SHIFT_PITCH_LIMIT; }
443+ }
413444 }
414445
415446 if (OculusManager::isConnected()) {
@@ -1032,7 +1063,7 @@ void display(void) · 616 unmodified lines
10321063 myCamera.setDistance (1.5f);
10331064 myCamera.setTightness (8.0f);
10341065 myCamera.setTargetPosition(myAvatar.getHeadPosition());
1035− myCamera.setTargetRotation(myAvatar.getBodyYaw(), 0.0f, 0.0f);
1066+ myCamera.setTargetRotation(myAvatar.getBodyYaw() - mouseViewShiftYaw, mouseViewShiftPitch, 0.0f);
10361067 }
10371068
10381069 // important...
@@ -1540,9 +1571,6 @@ void specialkey(int k, int x, int y) { · 501 unmodified lines
15401571 if (glutGetModifiers() == GLUT_ACTIVE_SHIFT) myAvatar.setDriveKeys(RIGHT, 1);
15411572 else myAvatar.setDriveKeys(ROT_RIGHT, 1);
15421573 }
1543−#ifndef _WIN32
1544− audio.setWalkingState(true);
1545−#endif
15461574 }
15471575 }
15481576
@@ -1800,6 +1828,8 @@ void idle(void) { · 251 unmodified lines
18001828 // walking triggers the handControl to stop
18011829 if (myAvatar.getMode() == AVATAR_MODE_WALKING) {
18021830 handControl.stop();
1831+ mouseViewShiftYaw *= 0.9;
1832+ mouseViewShiftPitch *= 0.9;
18031833 }
18041834
18051835 // Read serial port interface devices
@@ -1836,7 +1866,6 @@ void idle(void) { · 30 unmodified lines
18361866 glutPostRedisplay();
18371867 lastTimeIdle = check;
18381868 }
1839−
18401869 }
18411870
18421871 void reshape(int width, int height) {