diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index e669a2a527..3c565d5eea 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -138,6 +138,7 @@ Application::Application(int& argc, char** argv) : _mouseY(0), _mousePressed(false), _mouseVoxelScale(1.0f / 1024.0f), + _justEditedVoxel(false), _paintOn(false), _dominantColor(0), _perfStatsOn(false), @@ -306,7 +307,6 @@ void Application::paintGL() { _myCamera.setTargetRotation(_myAvatar.getBodyYaw() - 180.0f, 0.0f, 0.0f); - } else { if (_myCamera.getMode() == CAMERA_MODE_FIRST_PERSON) { _myCamera.setTargetPosition(_myAvatar.getSpringyHeadPosition()); @@ -739,8 +739,13 @@ void Application::mouseMoveEvent(QMouseEvent* event) { // detect drag glm::vec3 mouseVoxelPos(_mouseVoxel.x, _mouseVoxel.y, _mouseVoxel.z); - if (_colorVoxelMode->isChecked() && event->buttons().testFlag(Qt::LeftButton) && mouseVoxelPos != _lastMouseVoxelPos) { - addVoxelUnderCursor(); + if (!_justEditedVoxel && mouseVoxelPos != _lastMouseVoxelPos) { + if (event->buttons().testFlag(Qt::LeftButton)) { + maybeEditVoxelUnderCursor(); + + } else if (event->buttons().testFlag(Qt::RightButton) && checkedVoxelModeAction() != 0) { + deleteVoxelUnderCursor(); + } } } @@ -749,13 +754,8 @@ void Application::mousePressEvent(QMouseEvent* event) { _mouseX = event->x(); _mouseY = event->y(); _mousePressed = true; - - if (_addVoxelMode->isChecked() || _colorVoxelMode->isChecked()) { - addVoxelUnderCursor(); + maybeEditVoxelUnderCursor(); - } else if (_deleteVoxelMode->isChecked()) { - deleteVoxelUnderCursor(); - } } else if (event->button() == Qt::RightButton && checkedVoxelModeAction() != 0) { deleteVoxelUnderCursor(); } @@ -928,6 +928,12 @@ void Application::idle() { _mouseVoxel.green = paintColor.green(); _mouseVoxel.blue = paintColor.blue(); } + + // if we just edited, use the currently selected voxel as the "last" for drag detection + if (_justEditedVoxel) { + _lastMouseVoxelPos = glm::vec3(_mouseVoxel.x, _mouseVoxel.y, _mouseVoxel.z); + _justEditedVoxel = false; + } } // walking triggers the handControl to stop @@ -1207,6 +1213,8 @@ void Application::initMenu() { (_renderVoxels = renderMenu->addAction("Voxels"))->setCheckable(true); _renderVoxels->setChecked(true); _renderVoxels->setShortcut(Qt::Key_V); + (_renderVoxelTextures = renderMenu->addAction("Voxel Textures"))->setCheckable(true); + _renderVoxelTextures->setChecked(true); (_renderStarsOn = renderMenu->addAction("Stars"))->setCheckable(true); _renderStarsOn->setChecked(true); _renderStarsOn->setShortcut(Qt::Key_Asterisk); @@ -1684,7 +1692,7 @@ void Application::displaySide(Camera& whichCamera) { // Draw voxels if (_renderVoxels->isChecked()) { - _voxels.render(); + _voxels.render(_renderVoxelTextures->isChecked()); } // indicate what we'll be adding/removing in mouse mode, if anything @@ -2026,18 +2034,22 @@ void Application::shiftPaintingColor() { _paintingVoxel.blue = (_dominantColor == 2) ? randIntInRange(200, 255) : randIntInRange(40, 100); } -void Application::addVoxelUnderCursor() { - if (_mouseVoxel.s != 0) { - PACKET_HEADER message = (_destructiveAddVoxel->isChecked() ? - PACKET_HEADER_SET_VOXEL_DESTRUCTIVE : PACKET_HEADER_SET_VOXEL); - sendVoxelEditMessage(message, _mouseVoxel); +void Application::maybeEditVoxelUnderCursor() { + if (_addVoxelMode->isChecked() || _colorVoxelMode->isChecked()) { + if (_mouseVoxel.s != 0) { + PACKET_HEADER message = (_destructiveAddVoxel->isChecked() ? + PACKET_HEADER_SET_VOXEL_DESTRUCTIVE : PACKET_HEADER_SET_VOXEL); + sendVoxelEditMessage(message, _mouseVoxel); + + // create the voxel locally so it appears immediately + _voxels.createVoxel(_mouseVoxel.x, _mouseVoxel.y, _mouseVoxel.z, _mouseVoxel.s, + _mouseVoxel.red, _mouseVoxel.green, _mouseVoxel.blue, _destructiveAddVoxel->isChecked()); - // create the voxel locally so it appears immediately - _voxels.createVoxel(_mouseVoxel.x, _mouseVoxel.y, _mouseVoxel.z, _mouseVoxel.s, - _mouseVoxel.red, _mouseVoxel.green, _mouseVoxel.blue, _destructiveAddVoxel->isChecked()); - - // remember the position for drag detection - _lastMouseVoxelPos = glm::vec3(_mouseVoxel.x, _mouseVoxel.y, _mouseVoxel.z); + // remember the position for drag detection + _justEditedVoxel = true; + } + } else if (_deleteVoxelMode->isChecked()) { + deleteVoxelUnderCursor(); } } @@ -2049,7 +2061,7 @@ void Application::deleteVoxelUnderCursor() { //_voxels.deleteVoxelAt(_mouseVoxel.x, _mouseVoxel.y, _mouseVoxel.z, _mouseVoxel.s); // remember the position for drag detection - _lastMouseVoxelPos = glm::vec3(_mouseVoxel.x, _mouseVoxel.y, _mouseVoxel.z); + _justEditedVoxel = true; } } diff --git a/interface/src/Application.h b/interface/src/Application.h index 3e3dbb97b8..95f448d120 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -117,7 +117,7 @@ private: void setupPaintingVoxel(); void shiftPaintingColor(); - void addVoxelUnderCursor(); + void maybeEditVoxelUnderCursor(); void deleteVoxelUnderCursor(); void resetSensors(); @@ -136,6 +136,7 @@ private: QAction* _gyroLook; // Whether to allow the gyro data from head to move your view QAction* _mouseLook; // Whether the have the mouse near edge of screen move your view QAction* _renderVoxels; // Whether to render voxels + QAction* _renderVoxelTextures; // Whether to render noise textures on voxels QAction* _renderStarsOn; // Whether to display the stars QAction* _renderAtmosphereOn; // Whether to display the atmosphere QAction* _renderAvatarsOn; // Whether to render avatars @@ -188,6 +189,7 @@ private: Oscilloscope _audioScope; Avatar _myAvatar; // The rendered avatar of oneself + Camera _myCamera; // My view onto the world Camera _viewFrustumOffsetCamera; // The camera we use to sometimes show the view frustum from an offset mode @@ -204,6 +206,7 @@ private: VoxelDetail _mouseVoxel; // details of the voxel under the mouse cursor float _mouseVoxelScale; // the scale for adding/removing voxels glm::vec3 _lastMouseVoxelPos; // the position of the last mouse voxel edit + bool _justEditedVoxel; // set when we've just added/deleted/colored a voxel bool _paintOn; // Whether to paint voxels as you fly around unsigned char _dominantColor; // The dominant color of the voxel we're painting diff --git a/interface/src/Avatar.cpp b/interface/src/Avatar.cpp index 78977e4572..befedcf524 100644 --- a/interface/src/Avatar.cpp +++ b/interface/src/Avatar.cpp @@ -50,14 +50,16 @@ const float PERIPERSONAL_RADIUS = 1.0f; const float AVATAR_BRAKING_STRENGTH = 40.0f; const float JOINT_TOUCH_RANGE = 0.0005f; -float skinColor [] = {1.0, 0.84, 0.66}; -float lightBlue [] = {0.7, 0.8, 1.0}; +float skinColor [] = {1.0, 0.84, 0.66}; +float darkSkinColor[] = {0.8, 0.74, 0.6 }; +float lightBlue [] = {0.7, 0.8, 1.0 }; bool usingBigSphereCollisionTest = true; float chatMessageScale = 0.0015; float chatMessageHeight = 0.45; + Avatar::Avatar(bool isMine) { _orientation.setToIdentity(); @@ -393,17 +395,20 @@ void Avatar::simulate(float deltaTime) { _joint[ AVATAR_JOINT_HEAD_BASE ].radius ); - _head.setBodyYaw(_bodyYaw); - - //the following is still being prototyped (making the eyes look at a specific location), it should be finished by 5/20/13 + setLookatPosition(glm::vec3(0.0f, 0.0f, 0.0f)); //default lookat position is 0,0,0 + if (_interactingOther) { _head.setLooking(true); - _head.setLookatPosition(_interactingOther->getSpringyHeadPosition()); -//_head.setLookatPosition(_interactingOther->getApproximateEyePosition()); + + if (_isMine) { + setLookatPosition(_interactingOther->getSpringyHeadPosition()); + } } else { _head.setLooking(false); } - + + _head.setBodyYaw(_bodyYaw); + _head.setLookatPosition(_lookatPosition); _head.setAudioLoudness(_audioLoudness); _head.setSkinColor(glm::vec3(skinColor[0], skinColor[1], skinColor[2])); _head.simulate(deltaTime, _isMine); @@ -692,19 +697,6 @@ void Avatar::render(bool lookingInMirror, glm::vec3 cameraPosition) { _cameraPosition = cameraPosition; // store this for use in various parts of the code - // 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); - - /* - // show avatar position - glColor4f(0.5f, 0.5f, 0.5f, 0.6); - glPushMatrix(); - glTranslatef(_position.x, _position.y, _position.z); - glScalef(0.03, 0.03, 0.03); - glutSolidSphere(1, 10, 10); - glPopMatrix(); - */ - if (usingBigSphereCollisionTest) { // show TEST big sphere glColor4f(0.5f, 0.6f, 0.8f, 0.7); @@ -715,10 +707,12 @@ void Avatar::render(bool lookingInMirror, glm::vec3 cameraPosition) { glPopMatrix(); } + // 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); + //render body renderBody(lookingInMirror); - // if this is my avatar, then render my interactions with the other avatar if (_isMine) { _avatarTouch.render(_cameraPosition); @@ -1039,10 +1033,12 @@ void Avatar::updateBodySprings(float deltaTime) { _joint[b].springyVelocity = glm::vec3(0.0f, 0.0f, 0.0f); } + /* //apply forces from touch... if (_joint[b].touchForce > 0.0) { _joint[b].springyVelocity += _mouseRayDirection * _joint[b].touchForce * 0.7f; } + */ //update position by velocity... _joint[b].springyPosition += _joint[b].springyVelocity * deltaTime; @@ -1133,14 +1129,27 @@ void Avatar::renderBody(bool lookingInMirror) { glPopMatrix(); } } - - // Render lines connecting the joint positions - glColor3f(0.4f, 0.5f, 0.6f); - glLineWidth(3.0); for (int b = 1; b < NUM_AVATAR_JOINTS; b++) { if (_joint[b].parent != AVATAR_JOINT_NULL) if (b != AVATAR_JOINT_HEAD_TOP) { + + /* + // Render cone sections connecting the joint positions + glColor3fv(darkSkinColor); + renderJointConnectingCone + ( + _joint[_joint[b].parent ].springyPosition, + _joint[b ].springyPosition, + _joint[_joint[b].parent ].radius, + _joint[b ].radius + ); + */ + + + // Render lines connecting the joint positions + glColor3f(0.4f, 0.5f, 0.6f); + glLineWidth(3.0); glBegin(GL_LINE_STRIP); glVertex3fv(&_joint[ _joint[ b ].parent ].springyPosition.x); glVertex3fv(&_joint[ b ].springyPosition.x); @@ -1378,3 +1387,47 @@ void Avatar::readAvatarDataFromFile() { fclose(avatarFile); } } + +void Avatar::renderJointConnectingCone(glm::vec3 position1, glm::vec3 position2, float radius1, float radius2) { + + glBegin(GL_TRIANGLES); + + int num = 5; + + glm::vec3 axis = glm::normalize(position2 - position1); + float length = glm::length(axis); + + if (length > 0.0f) { + + glm::vec3 perpSin = glm::vec3(axis.y, axis.z, axis.x); + glm::vec3 perpCos = glm::vec3(axis.z, axis.x, axis.y); + + for (int i = 0; i < num; i ++) { + + float angle1 = ((float)i / (float)num) * PI * 2.0; + float angle2 = ((float)(i+1) / (float)num) * PI * 2.0; + + glm::vec3 p1a = position1 + perpSin * sin(angle1) * radius1; + glm::vec3 p1b = position1 + perpCos * cos(angle2) * radius1; + + glm::vec3 p2a = position2 + perpSin * sin(angle1) * radius2; + glm::vec3 p2b = position2 + perpCos * cos(angle2) * radius2; + + glVertex3f(p1a.x, p1a.y, p1a.z); + glVertex3f(p1b.x, p1b.y, p1b.z); + glVertex3f(p2a.x, p2a.y, p2a.z); + + /* + glVertex3f(p1b.x, p1b.y, p1b.z); + glVertex3f(p2a.x, p2a.y, p2a.z); + glVertex3f(p2b.x, p2b.y, p2b.z); + */ + } + } + + glEnd(); +} + + + + diff --git a/interface/src/Avatar.h b/interface/src/Avatar.h index f7dcb8853c..61fda4edda 100644 --- a/interface/src/Avatar.h +++ b/interface/src/Avatar.h @@ -15,6 +15,7 @@ #include #include "world.h" #include "AvatarTouch.h" +#include "AvatarRenderer.h" #include "InterfaceConfig.h" #include "SerialInterface.h" #include "Balls.h" @@ -217,6 +218,7 @@ private: void applyCollisionWithOtherAvatar( Avatar * other, float deltaTime ); void setHeadFromGyros(glm::vec3 * eulerAngles, glm::vec3 * angularVelocity, float deltaTime, float smoothingTime); void checkForMouseRayTouching(); + void renderJointConnectingCone(glm::vec3 position1, glm::vec3 position2, float radius1, float radius2); }; #endif diff --git a/interface/src/AvatarRenderer.cpp b/interface/src/AvatarRenderer.cpp index 45ce46084f..45c10c4441 100644 --- a/interface/src/AvatarRenderer.cpp +++ b/interface/src/AvatarRenderer.cpp @@ -10,54 +10,61 @@ #include "AvatarRenderer.h" #include "InterfaceConfig.h" +/* AvatarRenderer::AvatarRenderer() { } // this method renders the avatar -void AvatarRenderer::render(Avatar *avatarToRender, bool lookingInMirror, glm::vec3 cameraPosition) { +void AvatarRenderer::render() { - avatar = avatarToRender; - /* // show avatar position glColor4f(0.5f, 0.5f, 0.5f, 0.6); glPushMatrix(); - glm::vec3 j( avatar->getJointPosition( AVATAR_JOINT_PELVIS ) ); + glm::vec3 j( getJointPosition( AVATAR_JOINT_PELVIS ) ); glTranslatef(j.x, j.y, j.z); glScalef(0.08, 0.08, 0.08); glutSolidSphere(1, 10, 10); glPopMatrix(); - */ - //renderDiskShadow(avatar->getJointPosition( AVATAR_JOINT_PELVIS ), glm::vec3(0.0f, 1.0f, 0.0f), 0.1f, 0.2f); + renderDiskShadow(getJointPosition( AVATAR_JOINT_PELVIS ), glm::vec3(0.0f, 1.0f, 0.0f), 0.1f, 0.2f); - //renderBody(); + //renderBody(lookingInMirror); } - - - - - void AvatarRenderer::renderBody() { -/* // Render joint positions as spheres for (int b = 0; b < NUM_AVATAR_JOINTS; b++) { - if (b != AVATAR_JOINT_HEAD_BASE) { // the head is rendered as a special case in "renderHead" + if (b == AVATAR_JOINT_HEAD_BASE) { // the head is rendered as a special case + if (_displayingHead) { + _head.render(lookingInMirror); + } + } else { //show direction vectors of the bone orientation //renderOrientationDirections(_joint[b].springyPosition, _joint[b].orientation, _joint[b].radius * 2.0); - glm::vec3 j( avatar->getJointPosition( AVATAR_JOINT_PELVIS ) ); - glColor3fv(skinColor); + glColor3fv(_avatar->skinColor); glPushMatrix(); - glTranslatef(j.x, j.y, j.z); - glutSolidSphere(_joint[b].radius, 20.0f, 20.0f); + glTranslatef(_avatar->[b].springyPosition.x, _avatar->_joint[b].springyPosition.y, _avatar->_joint[b].springyPosition.z); + glutSolidSphere(_avatar->_joint[b].radius, 20.0f, 20.0f); + glPopMatrix(); + } + + if (_joint[b].touchForce > 0.0f) { + + float alpha = _joint[b].touchForce * 0.2; + float r = _joint[b].radius * 1.1f + 0.005f; + glColor4f(0.5f, 0.2f, 0.2f, alpha); + glPushMatrix(); + glTranslatef(_joint[b].springyPosition.x, _joint[b].springyPosition.y, _joint[b].springyPosition.z); + glScalef(r, r, r); + glutSolidSphere(1, 20, 20); glPopMatrix(); } } - + // Render lines connecting the joint positions glColor3f(0.4f, 0.5f, 0.6f); glLineWidth(3.0); @@ -71,5 +78,6 @@ void AvatarRenderer::renderBody() { glEnd(); } } - */ } +*/ + diff --git a/interface/src/AvatarRenderer.h b/interface/src/AvatarRenderer.h index e0e9d7bbb3..36a4d58e0c 100644 --- a/interface/src/AvatarRenderer.h +++ b/interface/src/AvatarRenderer.h @@ -11,16 +11,17 @@ #include "Avatar.h" #include -class AvatarRenderer { +/* +class AvatarRenderer : public Avatar { public: AvatarRenderer(); - void render(Avatar *avatarToRender, bool lookingInMirror, glm::vec3 cameraPosition ); + void render(); private: - Avatar *avatar; void renderBody(); }; +*/ #endif diff --git a/interface/src/Head.cpp b/interface/src/Head.cpp index 82a6943fd1..3b751651b2 100644 --- a/interface/src/Head.cpp +++ b/interface/src/Head.cpp @@ -15,12 +15,16 @@ using namespace std; const float HEAD_MOTION_DECAY = 0.1; const float MINIMUM_EYE_ROTATION = 0.7f; // based on a dot product: 1.0 is straight ahead, 0.0 is 90 degrees off +const float EYEBALL_RADIUS = 0.02; +const float IRIS_RADIUS = 0.007; +const float IRIS_PROTRUSION = 0.018f; + 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 _BrowRollAngle [5] = { 0.0f, 15.0f, 30.0f, -30.0f, -15.0f}; +float _BrowPitchAngle[3] = {-70.0f, -60.0f, -50.0f}; +float _eyeColor [3] = { 0.9f, 0.9f, 0.8f}; float _MouthWidthChoices[3] = {0.5, 0.77, 0.3}; @@ -66,12 +70,13 @@ Head::Head() : _bodyYaw(0.0f), _eyeContactTarget(LEFT_EYE) { - _eyebrowPitch[0] = -30; - _eyebrowPitch[1] = -30; - _eyebrowRoll [0] = 20; - _eyebrowRoll [1] = -20; + _eyebrowPitch[0] = -30; + _eyebrowPitch[1] = -30; + _eyebrowRoll [0] = 20; + _eyebrowRoll [1] = -20; } + void Head::setPositionRotationAndScale(glm::vec3 p, glm::vec3 r, float s) { _position = p; _scale = s; @@ -219,16 +224,17 @@ void Head::updateEyePositions() { + _orientation.getFront() * frontShift; } + void Head::setLooking(bool looking) { - _looking = looking; + _lookingAtSomething = looking; glm::vec3 averageEyePosition = _leftEyePosition + (_rightEyePosition - _leftEyePosition ) * ONE_HALF; glm::vec3 targetLookatAxis = glm::normalize(_lookatPosition - averageEyePosition); float dot = glm::dot(targetLookatAxis, _orientation.getFront()); if (dot < MINIMUM_EYE_ROTATION) { - _looking = false; + _lookingAtSomething = false; } } @@ -245,9 +251,8 @@ void Head::render(bool lookingInMirror) { glPushMatrix(); - glTranslatef(_position.x, _position.y, _position.z); - - glScalef(_scale, _scale, _scale); + glTranslatef(_position.x, _position.y, _position.z); //translate to head position + glScalef(_scale, _scale, _scale); //scale to head size if (lookingInMirror) { glRotatef(_bodyYaw - _yaw, 0, 1, 0); @@ -326,13 +331,14 @@ void Head::render(bool lookingInMirror) { glPopMatrix(); - //a new version of eyeballs that has the ability to look at specific targets in the world (algo still not finished yet) renderEyeBalls(); - if (_looking) { + /* + if (_lookingAtSomething) { // Render lines originating from the eyes and converging on the lookatPosition debugRenderLookatVectors(_leftEyePosition, _rightEyePosition, _lookatPosition); } + */ } void Head::renderEyeBalls() { @@ -345,7 +351,7 @@ void Head::renderEyeBalls() { } } - // setup the texutre to be used on each eye + // setup the texutre to be used on each iris GLUquadric* irisQuadric = gluNewQuadric(); gluQuadricTexture(irisQuadric, GL_TRUE); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); @@ -353,60 +359,90 @@ void Head::renderEyeBalls() { gluQuadricOrientation(irisQuadric, GLU_OUTSIDE); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, IRIS_TEXTURE_WIDTH, IRIS_TEXTURE_HEIGHT, 0, GL_RGBA, GL_UNSIGNED_BYTE, &::irisTexture[0]); - - // left eyeball + + // render white ball of left eyeball glPushMatrix(); glColor3fv(_eyeColor); glTranslatef(_leftEyePosition.x, _leftEyePosition.y, _leftEyePosition.z); - gluSphere(irisQuadric, 0.02, 30, 30); + gluSphere(irisQuadric, EYEBALL_RADIUS, 30, 30); glPopMatrix(); - // left iris + // render left iris glPushMatrix(); { - glTranslatef(_leftEyePosition.x, _leftEyePosition.y, _leftEyePosition.z); - glm::vec3 targetLookatAxis = glm::normalize(_lookatPosition - _leftEyePosition); - - if (!_looking) { - targetLookatAxis = _orientation.getFront(); - } + glTranslatef(_leftEyePosition.x, _leftEyePosition.y, _leftEyePosition.z); //translate to eyeball position glPushMatrix(); - glm::vec3 rotationAxis = glm::cross(targetLookatAxis, glm::vec3(0.0f, 1.0f, 0.0f)); - float angle = 180.0f - angleBetween(targetLookatAxis, glm::vec3(0.0f, 1.0f, 0.0f)); - glRotatef(angle, rotationAxis.x, rotationAxis.y, rotationAxis.z); - glTranslatef( 0.0f, -0.018f, 0.0f);//push the iris out a bit (otherwise - inside of eyeball!) + + if (_lookingAtSomething) { + + //rotate the eyeball to aim towards the lookat position + glm::vec3 targetLookatAxis = glm::normalize(_lookatPosition - _leftEyePosition); // the lookat direction + glm::vec3 rotationAxis = glm::cross(targetLookatAxis, IDENTITY_UP); + float angle = 180.0f - angleBetween(targetLookatAxis, IDENTITY_UP); + glRotatef(angle, rotationAxis.x, rotationAxis.y, rotationAxis.z); + glRotatef(180.0, 0.0f, 1.0f, 0.0f); //adjust roll to correct after previous rotations + } else { + + //rotate the eyeball to aim straight ahead + glm::vec3 rotationAxisToHeadFront = glm::cross(_orientation.getFront(), IDENTITY_UP); + float angleToHeadFront = 180.0f - angleBetween(_orientation.getFront(), IDENTITY_UP); + glRotatef(angleToHeadFront, rotationAxisToHeadFront.x, rotationAxisToHeadFront.y, rotationAxisToHeadFront.z); + + //set the amount of roll (for correction after previous rotations) + float rollRotation = angleBetween(_orientation.getFront(), IDENTITY_FRONT); + float dot = glm::dot(_orientation.getFront(), -IDENTITY_RIGHT); + if ( dot < 0.0f ) { rollRotation = -rollRotation; } + glRotatef(rollRotation, 0.0f, 1.0f, 0.0f); //roll the iris or correct roll about the lookat vector + } + + glTranslatef( 0.0f, -IRIS_PROTRUSION, 0.0f);//push the iris out a bit (otherwise - inside of eyeball!) glScalef( 1.0f, 0.5f, 1.0f); // flatten the iris glEnable(GL_TEXTURE_2D); - gluSphere(irisQuadric, 0.007, 15, 15); + gluSphere(irisQuadric, IRIS_RADIUS, 15, 15); glDisable(GL_TEXTURE_2D); glPopMatrix(); } glPopMatrix(); - //right eyeball + //render white ball of right eyeball glPushMatrix(); glColor3fv(_eyeColor); glTranslatef(_rightEyePosition.x, _rightEyePosition.y, _rightEyePosition.z); - gluSphere(irisQuadric, 0.02, 30, 30); + gluSphere(irisQuadric, EYEBALL_RADIUS, 30, 30); glPopMatrix(); - //right iris + // render right iris glPushMatrix(); { - glTranslatef(_rightEyePosition.x, _rightEyePosition.y, _rightEyePosition.z); - glm::vec3 targetLookatAxis = glm::normalize(_lookatPosition - _rightEyePosition); - - if (!_looking) { - targetLookatAxis = _orientation.getFront(); - } + glTranslatef(_rightEyePosition.x, _rightEyePosition.y, _rightEyePosition.z); //translate to eyeball position glPushMatrix(); - glm::vec3 rotationAxis = glm::cross(targetLookatAxis, glm::vec3(0.0f, 1.0f, 0.0f)); - float angle = 180.0f - angleBetween(targetLookatAxis, glm::vec3(0.0f, 1.0f, 0.0f)); - glRotatef(angle, rotationAxis.x, rotationAxis.y, rotationAxis.z); - glTranslatef( 0.0f, -0.018f, 0.0f);//push the iris out a bit (otherwise - inside of eyeball!) + + if (_lookingAtSomething) { + + //rotate the eyeball to aim towards the lookat position + glm::vec3 targetLookatAxis = glm::normalize(_lookatPosition - _rightEyePosition); + glm::vec3 rotationAxis = glm::cross(targetLookatAxis, IDENTITY_UP); + float angle = 180.0f - angleBetween(targetLookatAxis, IDENTITY_UP); + glRotatef(angle, rotationAxis.x, rotationAxis.y, rotationAxis.z); + glRotatef(180.0f, 0.0f, 1.0f, 0.0f); //adjust roll to correct after previous rotations + } else { + + //rotate the eyeball to aim straight ahead + glm::vec3 rotationAxisToHeadFront = glm::cross(_orientation.getFront(), IDENTITY_UP); + float angleToHeadFront = 180.0f - angleBetween(_orientation.getFront(), IDENTITY_UP); + glRotatef(angleToHeadFront, rotationAxisToHeadFront.x, rotationAxisToHeadFront.y, rotationAxisToHeadFront.z); + + //set the amount of roll (for correction after previous rotations) + float rollRotation = angleBetween(_orientation.getFront(), IDENTITY_FRONT); + float dot = glm::dot(_orientation.getFront(), -IDENTITY_RIGHT); + if ( dot < 0.0f ) { rollRotation = -rollRotation; } + glRotatef(rollRotation, 0.0f, 1.0f, 0.0f); //roll the iris or correct roll about the lookat vector + } + + glTranslatef( 0.0f, -IRIS_PROTRUSION, 0.0f);//push the iris out a bit (otherwise - inside of eyeball!) glScalef( 1.0f, 0.5f, 1.0f); // flatten the iris glEnable(GL_TEXTURE_2D); - gluSphere(irisQuadric, 0.007, 15, 15); + gluSphere(irisQuadric, IRIS_RADIUS, 15, 15); glDisable(GL_TEXTURE_2D); glPopMatrix(); } diff --git a/interface/src/Head.h b/interface/src/Head.h index 8f5afd052a..4ee20e3cfd 100644 --- a/interface/src/Head.h +++ b/interface/src/Head.h @@ -81,7 +81,7 @@ private: float _scale; int _eyeContact; float _browAudioLift; - bool _looking; + bool _lookingAtSomething; glm::vec3 _gravity; float _lastLoudness; float _averageLoudness; diff --git a/interface/src/VoxelSystem.cpp b/interface/src/VoxelSystem.cpp index bc3f927d22..be79acae0f 100644 --- a/interface/src/VoxelSystem.cpp +++ b/interface/src/VoxelSystem.cpp @@ -46,7 +46,8 @@ GLubyte identityIndices[] = { 0,2,1, 0,3,2, // Z- . VoxelSystem::VoxelSystem() { _voxelsInReadArrays = _voxelsInWriteArrays = _voxelsUpdated = 0; - _renderFullVBO = true; + _writeRenderFullVBO = true; + _readRenderFullVBO = true; _tree = new VoxelTree(); pthread_mutex_init(&_bufferWriteLock, NULL); pthread_mutex_init(&_treeLock, NULL); @@ -57,7 +58,8 @@ VoxelSystem::~VoxelSystem() { delete[] _writeVerticesArray; delete[] _readColorsArray; delete[] _writeColorsArray; - delete[] _voxelDirtyArray; + delete[] _writeVoxelDirtyArray; + delete[] _readVoxelDirtyArray; delete _tree; pthread_mutex_destroy(&_bufferWriteLock); pthread_mutex_destroy(&_treeLock); @@ -188,14 +190,15 @@ void VoxelSystem::setupNewVoxelsForDrawing() { _lastViewCullingElapsed = (endViewCulling - start) / 1000.0; } + bool didWriteFullVBO = _writeRenderFullVBO; if (_tree->isDirty()) { static char buffer[64] = { 0 }; if (_renderWarningsOn) { - sprintf(buffer, "newTreeToArrays() _renderFullVBO=%s", (_renderFullVBO ? "yes" : "no")); + sprintf(buffer, "newTreeToArrays() _writeRenderFullVBO=%s", (_writeRenderFullVBO ? "yes" : "no")); }; PerformanceWarning warn(_renderWarningsOn, buffer); _callsToTreesToArrays++; - if (_renderFullVBO) { + if (_writeRenderFullVBO) { _voxelsInWriteArrays = 0; // reset our VBO } _voxelsUpdated = newTreeToArrays(_tree->rootNode); @@ -203,16 +206,22 @@ void VoxelSystem::setupNewVoxelsForDrawing() { // since we called treeToArrays, we can assume that our VBO is in sync, and so partial updates to the VBOs are // ok again, until/unless we call removeOutOfView() - _renderFullVBO = false; + _writeRenderFullVBO = false; } else { _voxelsUpdated = 0; } + + // lock on the buffer write lock so we can't modify the data when the GPU is reading it + pthread_mutex_lock(&_bufferWriteLock); + if (_voxelsUpdated) { _voxelsDirty=true; } // copy the newly written data to the arrays designated for reading, only does something if _voxelsDirty && _voxelsUpdated - copyWrittenDataToReadArrays(); + copyWrittenDataToReadArrays(didWriteFullVBO); + + pthread_mutex_unlock(&_bufferWriteLock); double end = usecTimestampNow(); double elapsedmsec = (end - start) / 1000.0; @@ -226,30 +235,32 @@ void VoxelSystem::cleanupRemovedVoxels() { while (!_removedVoxels.isEmpty()) { delete _removedVoxels.extract(); } - _renderFullVBO = true; // if we remove voxels, we must update our full VBOs + _writeRenderFullVBO = true; // if we remove voxels, we must update our full VBOs } } void VoxelSystem::copyWrittenDataToReadArraysFullVBOs() { - // lock on the buffer write lock so we can't modify the data when the GPU is reading it - pthread_mutex_lock(&_bufferWriteLock); int bytesOfVertices = (_voxelsInWriteArrays * VERTEX_POINTS_PER_VOXEL) * sizeof(GLfloat); int bytesOfColors = (_voxelsInWriteArrays * VERTEX_POINTS_PER_VOXEL) * sizeof(GLubyte); memcpy(_readVerticesArray, _writeVerticesArray, bytesOfVertices); memcpy(_readColorsArray, _writeColorsArray, bytesOfColors ); _voxelsInReadArrays = _voxelsInWriteArrays; - pthread_mutex_unlock(&_bufferWriteLock); + + // clear our dirty flags + memset(_writeVoxelDirtyArray, false, _voxelsInWriteArrays * sizeof(bool)); + + // let the reader know to get the full array + _readRenderFullVBO = true; } void VoxelSystem::copyWrittenDataToReadArraysPartialVBOs() { - // lock on the buffer write lock so we can't modify the data when the GPU is reading it - pthread_mutex_lock(&_bufferWriteLock); - glBufferIndex segmentStart = 0; glBufferIndex segmentEnd = 0; bool inSegment = false; for (glBufferIndex i = 0; i < _voxelsInWriteArrays; i++) { - bool thisVoxelDirty = _voxelDirtyArray[i]; + bool thisVoxelDirty = _writeVoxelDirtyArray[i]; + _readVoxelDirtyArray[i] |= thisVoxelDirty; + _writeVoxelDirtyArray[i] = false; if (!inSegment) { if (thisVoxelDirty) { segmentStart = i; @@ -298,14 +309,12 @@ void VoxelSystem::copyWrittenDataToReadArraysPartialVBOs() { // update our length _voxelsInReadArrays = _voxelsInWriteArrays; - - pthread_mutex_unlock(&_bufferWriteLock); } -void VoxelSystem::copyWrittenDataToReadArrays() { +void VoxelSystem::copyWrittenDataToReadArrays(bool fullVBOs) { PerformanceWarning warn(_renderWarningsOn, "copyWrittenDataToReadArrays()"); if (_voxelsDirty && _voxelsUpdated) { - if (_renderFullVBO) { + if (fullVBOs) { copyWrittenDataToReadArraysFullVBOs(); } else { copyWrittenDataToReadArraysPartialVBOs(); @@ -335,7 +344,7 @@ int VoxelSystem::newTreeToArrays(VoxelNode* node) { } } } - if (_renderFullVBO) { + if (_writeRenderFullVBO) { voxelsUpdated += updateNodeInArraysAsFullVBO(node); } else { voxelsUpdated += updateNodeInArraysAsPartialVBO(node); @@ -373,7 +382,7 @@ int VoxelSystem::updateNodeInArraysAsFullVBO(VoxelNode* node) { *(writeColorsAt +j) = node->getColor()[j % 3]; } node->setBufferIndex(nodeIndex); - _voxelDirtyArray[nodeIndex] = true; // just in case we switch to Partial mode + _writeVoxelDirtyArray[nodeIndex] = true; // just in case we switch to Partial mode _voxelsInWriteArrays++; // our know vertices in the arrays return 1; // rendered } @@ -410,7 +419,7 @@ int VoxelSystem::updateNodeInArraysAsPartialVBO(VoxelNode* node) { node->setBufferIndex(nodeIndex); _voxelsInWriteArrays++; } - _voxelDirtyArray[nodeIndex] = true; + _writeVoxelDirtyArray[nodeIndex] = true; // populate the array with points for the 8 vertices // and RGB color for each added vertex @@ -440,9 +449,11 @@ void VoxelSystem::init() { _voxelsInReadArrays = 0; _unusedArraySpace = 0; - // we will track individual dirty sections with this array of bools - _voxelDirtyArray = new bool[MAX_VOXELS_PER_SYSTEM]; - memset(_voxelDirtyArray, false, MAX_VOXELS_PER_SYSTEM * sizeof(bool)); + // we will track individual dirty sections with these arrays of bools + _writeVoxelDirtyArray = new bool[MAX_VOXELS_PER_SYSTEM]; + memset(_writeVoxelDirtyArray, false, MAX_VOXELS_PER_SYSTEM * sizeof(bool)); + _readVoxelDirtyArray = new bool[MAX_VOXELS_PER_SYSTEM]; + memset(_readVoxelDirtyArray, false, MAX_VOXELS_PER_SYSTEM * sizeof(bool)); // prep the data structures for incoming voxel data _writeVerticesArray = new GLfloat[VERTEX_POINTS_PER_VOXEL * MAX_VOXELS_PER_SYSTEM]; @@ -539,7 +550,7 @@ void VoxelSystem::init() { void VoxelSystem::updateFullVBOs() { glBufferIndex segmentStart = 0; - glBufferIndex segmentEnd = _voxelsInWriteArrays; + glBufferIndex segmentEnd = _voxelsInReadArrays; int segmentLength = (segmentEnd - segmentStart) + 1; GLintptr segmentStartAt = segmentStart * VERTEX_POINTS_PER_VOXEL * sizeof(GLfloat); @@ -553,21 +564,21 @@ void VoxelSystem::updateFullVBOs() { glBindBuffer(GL_ARRAY_BUFFER, _vboColorsID); glBufferSubData(GL_ARRAY_BUFFER, segmentStartAt, segmentSizeBytes, readColorsFrom); - // consider the _voxelDirtyArray[] clean! - memset(_voxelDirtyArray, false, _voxelsInWriteArrays * sizeof(bool)); + // consider the _readVoxelDirtyArray[] clean! + memset(_readVoxelDirtyArray, false, _voxelsInReadArrays * sizeof(bool)); } void VoxelSystem::updatePartialVBOs() { glBufferIndex segmentStart = 0; glBufferIndex segmentEnd = 0; bool inSegment = false; - for (glBufferIndex i = 0; i < _voxelsInWriteArrays; i++) { - bool thisVoxelDirty = _voxelDirtyArray[i]; + for (glBufferIndex i = 0; i < _voxelsInReadArrays; i++) { + bool thisVoxelDirty = _readVoxelDirtyArray[i]; if (!inSegment) { if (thisVoxelDirty) { segmentStart = i; inSegment = true; - _voxelDirtyArray[i] = false; // consider us clean! + _readVoxelDirtyArray[i] = false; // consider us clean! } } else { if (!thisVoxelDirty) { @@ -587,13 +598,13 @@ void VoxelSystem::updatePartialVBOs() { glBindBuffer(GL_ARRAY_BUFFER, _vboColorsID); glBufferSubData(GL_ARRAY_BUFFER, segmentStartAt, segmentSizeBytes, readColorsFrom); } - _voxelDirtyArray[i] = false; // consider us clean! + _readVoxelDirtyArray[i] = false; // consider us clean! } } // if we got to the end of the array, and we're in an active dirty segment... if (inSegment) { - segmentEnd = _voxelsInWriteArrays - 1; + segmentEnd = _voxelsInReadArrays - 1; inSegment = false; int segmentLength = (segmentEnd - segmentStart) + 1; GLintptr segmentStartAt = segmentStart * VERTEX_POINTS_PER_VOXEL * sizeof(GLfloat); @@ -612,22 +623,27 @@ void VoxelSystem::updatePartialVBOs() { void VoxelSystem::updateVBOs() { static char buffer[40] = { 0 }; if (_renderWarningsOn) { - sprintf(buffer, "updateVBOs() _renderFullVBO=%s", (_renderFullVBO ? "yes" : "no")); + sprintf(buffer, "updateVBOs() _readRenderFullVBO=%s", (_readRenderFullVBO ? "yes" : "no")); }; PerformanceWarning warn(_renderWarningsOn, buffer); // would like to include _callsToTreesToArrays if (_voxelsDirty) { - if (_renderFullVBO) { + if (_readRenderFullVBO) { updateFullVBOs(); } else { updatePartialVBOs(); } _voxelsDirty = false; + _readRenderFullVBO = false; } _callsToTreesToArrays = 0; // clear it } -void VoxelSystem::render() { +void VoxelSystem::render(bool texture) { PerformanceWarning warn(_renderWarningsOn, "render()"); + + // get the lock so that the update thread won't change anything + pthread_mutex_lock(&_bufferWriteLock); + glPushMatrix(); updateVBOs(); // tell OpenGL where to find vertex and color information @@ -644,9 +660,11 @@ void VoxelSystem::render() { glBindBuffer(GL_ARRAY_BUFFER, _vboColorsID); glColorPointer(3, GL_UNSIGNED_BYTE, 0, 0); - _perlinModulateProgram->bind(); - glBindTexture(GL_TEXTURE_2D, _permutationNormalTextureID); - + if (texture) { + _perlinModulateProgram->bind(); + glBindTexture(GL_TEXTURE_2D, _permutationNormalTextureID); + } + // for performance, disable blending and enable backface culling glDisable(GL_BLEND); glEnable(GL_CULL_FACE); @@ -659,9 +677,11 @@ void VoxelSystem::render() { glEnable(GL_BLEND); glDisable(GL_CULL_FACE); - _perlinModulateProgram->release(); - glBindTexture(GL_TEXTURE_2D, 0); - + if (texture) { + _perlinModulateProgram->release(); + glBindTexture(GL_TEXTURE_2D, 0); + } + // deactivate vertex and color arrays after drawing glDisableClientState(GL_VERTEX_ARRAY); glDisableClientState(GL_NORMAL_ARRAY); @@ -673,6 +693,8 @@ void VoxelSystem::render() { // scale back down to 1 so heads aren't massive glPopMatrix(); + + pthread_mutex_unlock(&_bufferWriteLock); } int VoxelSystem::_nodeCount = 0; @@ -1021,7 +1043,7 @@ void VoxelSystem::collectStatsForTreesAndVBOs() { glBufferIndex maxDirty = 0; for (glBufferIndex i = 0; i < _voxelsInWriteArrays; i++) { - if (_voxelDirtyArray[i]) { + if (_writeVoxelDirtyArray[i]) { minDirty = std::min(minDirty,i); maxDirty = std::max(maxDirty,i); } diff --git a/interface/src/VoxelSystem.h b/interface/src/VoxelSystem.h index 1f2597763c..9997444d37 100644 --- a/interface/src/VoxelSystem.h +++ b/interface/src/VoxelSystem.h @@ -36,7 +36,7 @@ public: void init(); void simulate(float deltaTime) { }; - void render(); + void render(bool texture); unsigned long getVoxelsUpdated() const {return _voxelsUpdated;}; unsigned long getVoxelsRendered() const {return _voxelsInReadArrays;}; @@ -121,13 +121,15 @@ private: GLubyte* _readColorsArray; GLfloat* _writeVerticesArray; GLubyte* _writeColorsArray; - bool* _voxelDirtyArray; + bool* _writeVoxelDirtyArray; + bool* _readVoxelDirtyArray; unsigned long _voxelsUpdated; unsigned long _voxelsInWriteArrays; unsigned long _voxelsInReadArrays; unsigned long _unusedArraySpace; - bool _renderFullVBO; + bool _writeRenderFullVBO; + bool _readRenderFullVBO; double _setupNewVoxelsForDrawingLastElapsed; double _setupNewVoxelsForDrawingLastFinished; @@ -151,7 +153,7 @@ private: void cleanupRemovedVoxels(); void setupNewVoxelsForDrawing(); - void copyWrittenDataToReadArrays(); + void copyWrittenDataToReadArrays(bool fullVBOs); bool _voxelsDirty; diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index b43e8f1e93..b6e8c4be6a 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -35,6 +35,7 @@ int unpackFloatAngleFromTwoByte(uint16_t* byteAnglePointer, float* destinationPo AvatarData::AvatarData() : _handPosition(0,0,0), + _lookatPosition(0,0,0), _bodyYaw(-90.0), _bodyPitch(0.0), _bodyRoll(0.0), @@ -91,6 +92,10 @@ int AvatarData::getBroadcastData(unsigned char* destinationBuffer) { // Hand Position memcpy(destinationBuffer, &_handPosition, sizeof(float) * 3); destinationBuffer += sizeof(float) * 3; + + // Lookat Position + memcpy(destinationBuffer, &_lookatPosition, sizeof(_lookatPosition)); + destinationBuffer += sizeof(_lookatPosition); // Hand State (0 = not grabbing, 1 = grabbing) memcpy(destinationBuffer, &_handState, sizeof(char)); @@ -175,6 +180,10 @@ int AvatarData::parseData(unsigned char* sourceBuffer, int numBytes) { memcpy(&_handPosition, sourceBuffer, sizeof(float) * 3); sourceBuffer += sizeof(float) * 3; + // Lookat Position + memcpy(&_lookatPosition, sourceBuffer, sizeof(_lookatPosition)); + sourceBuffer += sizeof(_lookatPosition); + // Hand State memcpy(&_handState, sourceBuffer, sizeof(char)); sourceBuffer += sizeof(char); diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index 68411fa3ee..41335c33fe 100644 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -32,8 +32,10 @@ public: AvatarData(); const glm::vec3& getPosition() const { return _position; } - void setPosition(const glm::vec3 position) { _position = position; } - void setHandPosition(const glm::vec3 handPosition) { _handPosition = handPosition; } + + void setPosition (const glm::vec3 position ) { _position = position; } + void setHandPosition (const glm::vec3 handPosition ) { _handPosition = handPosition; } + void setLookatPosition(const glm::vec3 lookatPosition) { _lookatPosition = lookatPosition; } int getBroadcastData(unsigned char* destinationBuffer); int parseData(unsigned char* sourceBuffer, int numBytes); @@ -116,6 +118,7 @@ protected: glm::vec3 _position; glm::vec3 _handPosition; + glm::vec3 _lookatPosition; // Body rotation float _bodyYaw;