From b86890033c2fde9f2ef2b9bd0530216cf0663c4c Mon Sep 17 00:00:00 2001 From: Philip Rosedale Date: Fri, 11 Jul 2014 19:35:17 -0500 Subject: [PATCH] Moved hair to separate class --- interface/src/Hair.cpp | 203 +++++++++++++++++++++++- interface/src/Hair.h | 33 +++- interface/src/avatar/Avatar.cpp | 254 +++--------------------------- interface/src/avatar/Avatar.h | 14 +- interface/src/avatar/MyAvatar.cpp | 22 ++- 5 files changed, 262 insertions(+), 264 deletions(-) diff --git a/interface/src/Hair.cpp b/interface/src/Hair.cpp index a23312eba1..635ea7ee0f 100644 --- a/interface/src/Hair.cpp +++ b/interface/src/Hair.cpp @@ -15,21 +15,212 @@ #include "Util.h" #include "world.h" +const float HAIR_DAMPING = 0.99f; +const float CONSTRAINT_RELAXATION = 10.0f; +const glm::vec3 HAIR_GRAVITY(0.0f, -0.007f, 0.0f); +const float HAIR_ACCELERATION_COUPLING = 0.025f; +const float HAIR_ANGULAR_VELOCITY_COUPLING = 0.10f; +const float HAIR_MAX_LINEAR_ACCELERATION = 4.0f; +const float HAIR_STIFFNESS = 0.0000f; +const glm::vec3 HAIR_COLOR1(0.98f, 0.92f, 0.843f); +const glm::vec3 HAIR_COLOR2(0.545f, 0.533f, 0.47f); +const glm::vec3 WIND_DIRECTION(0.5f, -1.0f, 0.0f); +const float MAX_WIND_STRENGTH = 0.02f; -Hair::Hair() { - qDebug() << "Creating Hair"; + +Hair::Hair(int strands, + int links, + float radius, + float linkLength, + float hairThickness) : + _acceleration(0.0f), + _angularVelocity(0.0f), + _gravity(0.0f) +{ + _strands = strands; + _links = links; + _radius = radius; + _linkLength = linkLength; + _hairThickness = hairThickness; + _hairPosition = new glm::vec3[_strands * _links]; + _hairOriginalPosition = new glm::vec3[_strands * _links]; + _hairLastPosition = new glm::vec3[_strands * _links]; + _hairQuadDelta = new glm::vec3[_strands * _links]; + _hairNormals = new glm::vec3[_strands * _links]; + _hairColors = new glm::vec3[_strands * _links]; + _hairIsMoveable = new int[_strands * _links]; + _hairConstraints = new int[_strands * _links * HAIR_CONSTRAINTS]; // Hair can link to two others + const float FACE_WIDTH = PI / 4.0f; + glm::vec3 thisVertex; + for (int strand = 0; strand < _strands; strand++) { + float strandAngle = randFloat() * PI; + float azimuth = FACE_WIDTH / 2.0f + (randFloat() * (2.0 * PI - FACE_WIDTH)); + float elevation = PI_OVER_TWO - (randFloat() * 0.75 * PI); + glm::vec3 thisStrand(sinf(azimuth) * cosf(elevation), sinf(elevation), -cosf(azimuth) * cosf(elevation)); + thisStrand *= _radius; + + for (int link = 0; link < _links; link++) { + int vertexIndex = strand * _links + link; + // Clear constraints + for (int link2 = 0; link2 < HAIR_CONSTRAINTS; link2++) { + _hairConstraints[vertexIndex * HAIR_CONSTRAINTS + link2] = -1; + } + if (vertexIndex % _links == 0) { + // start of strand + thisVertex = thisStrand; + } else { + thisVertex+= glm::normalize(thisStrand) * _linkLength; + // Set constraints to vertex before and maybe vertex after in strand + _hairConstraints[vertexIndex * HAIR_CONSTRAINTS] = vertexIndex - 1; + if (link < (_links - 1)) { + _hairConstraints[vertexIndex * HAIR_CONSTRAINTS + 1] = vertexIndex + 1; + } + } + _hairPosition[vertexIndex] = thisVertex; + _hairLastPosition[vertexIndex] = _hairPosition[vertexIndex]; + _hairOriginalPosition[vertexIndex] = _hairPosition[vertexIndex]; + + _hairQuadDelta[vertexIndex] = glm::vec3(cos(strandAngle) * _hairThickness, 0.f, sin(strandAngle) * _hairThickness); + _hairQuadDelta[vertexIndex] *= 1.f - (link / _links); + _hairNormals[vertexIndex] = glm::normalize(randVector()); + if (randFloat() < elevation / PI_OVER_TWO) { + _hairColors[vertexIndex] = HAIR_COLOR1 * ((float)(link + 1) / (float)_links); + } else { + _hairColors[vertexIndex] = HAIR_COLOR2 * ((float)(link + 1) / (float)_links); + } + } + } } void Hair::simulate(float deltaTime) { + deltaTime = glm::clamp(deltaTime, 0.0f, 1.0f / 30.0f); + glm::vec3 acceleration = _acceleration; + if (glm::length(acceleration) > HAIR_MAX_LINEAR_ACCELERATION) { + acceleration = glm::normalize(acceleration) * HAIR_MAX_LINEAR_ACCELERATION; + } + + float windIntensity = randFloat() * MAX_WIND_STRENGTH; + + for (int strand = 0; strand < _strands; strand++) { + for (int link = 0; link < _links; link++) { + int vertexIndex = strand * _links + link; + if (vertexIndex % _links == 0) { + // Base Joint - no integration + } else { + // + // Vertlet Integration + // + // Add velocity from last position, with damping + glm::vec3 thisPosition = _hairPosition[vertexIndex]; + glm::vec3 diff = thisPosition - _hairLastPosition[vertexIndex]; + _hairPosition[vertexIndex] += diff * HAIR_DAMPING; + // Resolve collision with head sphere + if (glm::length(_hairPosition[vertexIndex]) < _radius) { + _hairPosition[vertexIndex] += glm::normalize(_hairPosition[vertexIndex]) * + (_radius - glm::length(_hairPosition[vertexIndex])); + } + // Resolve collision with hands + /* + if (glm::length(_hairPosition[vertexIndex] - leftHandPosition) < FINGER_RADIUS) { + _hairPosition[vertexIndex] += glm::normalize(_hairPosition[vertexIndex] - leftHandPosition) * + (FINGER_RADIUS - glm::length(_hairPosition[vertexIndex] - leftHandPosition)); + } + if (glm::length(_hairPosition[vertexIndex] - rightHandPosition) < FINGER_RADIUS) { + _hairPosition[vertexIndex] += glm::normalize(_hairPosition[vertexIndex] - rightHandPosition) * + (FINGER_RADIUS - glm::length(_hairPosition[vertexIndex] - rightHandPosition)); + } + */ + + + // Add a little gravity + _hairPosition[vertexIndex] += HAIR_GRAVITY * deltaTime; + + // Add linear acceleration of the avatar body + _hairPosition[vertexIndex] -= acceleration * HAIR_ACCELERATION_COUPLING * deltaTime; + + // Add stiffness (like hair care products do) + _hairPosition[vertexIndex] += (_hairOriginalPosition[vertexIndex] - _hairPosition[vertexIndex]) + * powf(1.f - link / _links, 2.f) * HAIR_STIFFNESS; + + // Add some wind + glm::vec3 wind = WIND_DIRECTION * windIntensity; + _hairPosition[vertexIndex] += wind * deltaTime; + + const float ANGULAR_VELOCITY_MIN = 0.001f; + // Add angular acceleration of the avatar body + if (glm::length(_angularVelocity) > ANGULAR_VELOCITY_MIN) { + glm::vec3 yawVector = _hairPosition[vertexIndex]; + yawVector.y = 0.f; + if (glm::length(yawVector) > EPSILON) { + float radius = glm::length(yawVector); + yawVector = glm::normalize(yawVector); + float angle = atan2f(yawVector.x, -yawVector.z) + PI; + glm::vec3 delta = glm::vec3(-1.f, 0.f, 0.f) * glm::angleAxis(angle, glm::vec3(0, 1, 0)); + _hairPosition[vertexIndex] -= delta * radius * _angularVelocity.y * HAIR_ANGULAR_VELOCITY_COUPLING * deltaTime; + } + glm::vec3 pitchVector = _hairPosition[vertexIndex]; + pitchVector.x = 0.f; + if (glm::length(pitchVector) > EPSILON) { + float radius = glm::length(pitchVector); + pitchVector = glm::normalize(pitchVector); + float angle = atan2f(pitchVector.y, -pitchVector.z) + PI; + glm::vec3 delta = glm::vec3(0.0f, 1.0f, 0.f) * glm::angleAxis(angle, glm::vec3(1, 0, 0)); + _hairPosition[vertexIndex] -= delta * radius * _angularVelocity.x * HAIR_ANGULAR_VELOCITY_COUPLING * deltaTime; + } + glm::vec3 rollVector = _hairPosition[vertexIndex]; + rollVector.z = 0.f; + if (glm::length(rollVector) > EPSILON) { + float radius = glm::length(rollVector); + pitchVector = glm::normalize(rollVector); + float angle = atan2f(rollVector.x, rollVector.y) + PI; + glm::vec3 delta = glm::vec3(-1.0f, 0.0f, 0.f) * glm::angleAxis(angle, glm::vec3(0, 0, 1)); + _hairPosition[vertexIndex] -= delta * radius * _angularVelocity.z * HAIR_ANGULAR_VELOCITY_COUPLING * deltaTime; + } + } + + // Iterate length constraints to other links + for (int link = 0; link < HAIR_CONSTRAINTS; link++) { + if (_hairConstraints[vertexIndex * HAIR_CONSTRAINTS + link] > -1) { + // If there is a constraint, try to enforce it + glm::vec3 vectorBetween = _hairPosition[_hairConstraints[vertexIndex * HAIR_CONSTRAINTS + link]] - _hairPosition[vertexIndex]; + _hairPosition[vertexIndex] += glm::normalize(vectorBetween) * (glm::length(vectorBetween) - _linkLength) * CONSTRAINT_RELAXATION * deltaTime; + } + } + // Store start position for next vertlet pass + _hairLastPosition[vertexIndex] = thisPosition; + } + } + } + } void Hair::render() { // // Before calling this function, translate/rotate to the origin of the owning object - glPushMatrix(); - glColor3f(1.0f, 1.0f, 0.0f); - glutSolidSphere(1.0f, 15, 15); - glPopMatrix(); + // + glBegin(GL_QUADS); + for (int strand = 0; strand < _strands; strand++) { + for (int link = 0; link < _links - 1; link++) { + int vertexIndex = strand * _links + link; + glColor3fv(&_hairColors[vertexIndex].x); + glNormal3fv(&_hairNormals[vertexIndex].x); + glVertex3f(_hairPosition[vertexIndex].x - _hairQuadDelta[vertexIndex].x, + _hairPosition[vertexIndex].y - _hairQuadDelta[vertexIndex].y, + _hairPosition[vertexIndex].z - _hairQuadDelta[vertexIndex].z); + glVertex3f(_hairPosition[vertexIndex].x + _hairQuadDelta[vertexIndex].x, + _hairPosition[vertexIndex].y + _hairQuadDelta[vertexIndex].y, + _hairPosition[vertexIndex].z + _hairQuadDelta[vertexIndex].z); + + glVertex3f(_hairPosition[vertexIndex + 1].x + _hairQuadDelta[vertexIndex].x, + _hairPosition[vertexIndex + 1].y + _hairQuadDelta[vertexIndex].y, + _hairPosition[vertexIndex + 1].z + _hairQuadDelta[vertexIndex].z); + glVertex3f(_hairPosition[vertexIndex + 1].x - _hairQuadDelta[vertexIndex].x, + _hairPosition[vertexIndex + 1].y - _hairQuadDelta[vertexIndex].y, + _hairPosition[vertexIndex + 1].z - _hairQuadDelta[vertexIndex].z); + } + } + glEnd(); } + diff --git a/interface/src/Hair.h b/interface/src/Hair.h index e4dc3778c3..dfb90cae21 100644 --- a/interface/src/Hair.h +++ b/interface/src/Hair.h @@ -21,14 +21,45 @@ #include "InterfaceConfig.h" #include "Util.h" +const int HAIR_CONSTRAINTS = 2; + +const int DEFAULT_HAIR_STRANDS = 50; +const int DEFAULT_HAIR_LINKS = 10; +const float DEFAULT_HAIR_RADIUS = 0.25; +const float DEFAULT_HAIR_LINK_LENGTH = 0.05; +const float DEFAULT_HAIR_THICKNESS = 0.015; class Hair { public: - Hair(); + Hair(int strands = DEFAULT_HAIR_STRANDS, + int links = DEFAULT_HAIR_LINKS, + float radius = DEFAULT_HAIR_RADIUS, + float linkLength = DEFAULT_HAIR_LINK_LENGTH, + float hairThickness = DEFAULT_HAIR_THICKNESS); void simulate(float deltaTime); void render(); + void setAcceleration(const glm::vec3& acceleration) { _acceleration = acceleration; } + void setAngularVelocity(const glm::vec3& angularVelocity) { _angularVelocity = angularVelocity; } + void setGravity(const glm::vec3& gravity) { _gravity = gravity; } private: + int _strands; + int _links; + float _linkLength; + float _hairThickness; + float _radius; + glm::vec3* _hairPosition; + glm::vec3* _hairOriginalPosition; + glm::vec3* _hairLastPosition; + glm::vec3* _hairQuadDelta; + glm::vec3* _hairNormals; + glm::vec3* _hairColors; + int* _hairIsMoveable; + int* _hairConstraints; + glm::vec3 _acceleration; + glm::vec3 _angularVelocity; + glm::vec3 _gravity; + }; diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index 4efb03b3f5..a826ad699f 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -46,6 +46,7 @@ const float DISPLAYNAME_BACKGROUND_ALPHA = 0.4f; Avatar::Avatar() : AvatarData(), + _hair(), _skeletonModel(this), _bodyYawDelta(0.0f), _velocity(0.0f, 0.0f, 0.0f), @@ -60,9 +61,9 @@ Avatar::Avatar() : _mouseRayDirection(0.0f, 0.0f, 0.0f), _moving(false), _collisionGroups(0), + _numLocalLights(1), _initialized(false), - _shouldRenderBillboard(true), - _numLocalLights(1) + _shouldRenderBillboard(true) { // we may have been created in the network thread, but we live in the main thread moveToThread(Application::getInstance()->thread()); @@ -82,8 +83,7 @@ void Avatar::init() { _skeletonModel.init(); _initialized = true; _shouldRenderBillboard = (getLODDistance() >= BILLBOARD_LOD_DISTANCE); - initializeHair(); - + for (int i = 0; i < MAX_LOCAL_LIGHTS; i++) { _localLightColors[i] = glm::vec3(0.0f, 0.0f, 0.0f); _localLightDirections[i] = glm::vec3(0.0f, 0.0f, 0.0f); @@ -168,13 +168,11 @@ void Avatar::simulate(float deltaTime) { } if (Menu::getInstance()->isOptionChecked(MenuOption::StringHair)) { PerformanceTimer perfTimer("hair"); - simulateHair(deltaTime); + _hair.setAcceleration(getAcceleration() * getHead()->getFinalOrientationInWorldFrame()); + _hair.setAngularVelocity(getAngularVelocity() + getHead()->getAngularVelocity() * getHead()->getFinalOrientationInWorldFrame()); + _hair.setGravity(Application::getInstance()->getEnvironment()->getGravity(getPosition()) * getHead()->getFinalOrientationInWorldFrame()); + _hair.simulate(deltaTime); } - - foreach (Hair* hair, _hairs) { - hair->simulate(deltaTime); - } - } // update position by velocity, and subtract the change added earlier for gravity @@ -426,233 +424,17 @@ void Avatar::renderBody(RenderMode renderMode, float glowLevel) { getHand()->render(false, modelRenderMode); } getHead()->render(1.0f, modelRenderMode); + if (Menu::getInstance()->isOptionChecked(MenuOption::StringHair)) { - renderHair(); - foreach (Hair* hair, _hairs) { - hair->render(); - } - } -} - -// -// Constants for the Hair Simulation -// - -const float HAIR_LENGTH = 0.2f; -const float HAIR_LINK_LENGTH = HAIR_LENGTH / HAIR_LINKS; -const float HAIR_DAMPING = 0.99f; -const float HEAD_RADIUS = 0.21f; -const float CONSTRAINT_RELAXATION = 10.0f; -const glm::vec3 HAIR_GRAVITY(0.0f, -0.007f, 0.0f); -const float HAIR_ACCELERATION_COUPLING = 0.025f; -const float HAIR_ANGULAR_VELOCITY_COUPLING = 0.10f; -const float HAIR_MAX_LINEAR_ACCELERATION = 4.0f; -const float HAIR_THICKNESS = 0.015f; -const float HAIR_STIFFNESS = 0.0000f; -const glm::vec3 HAIR_COLOR1(0.98f, 0.92f, 0.843f); -const glm::vec3 HAIR_COLOR2(0.545f, 0.533f, 0.47f); -const glm::vec3 WIND_DIRECTION(0.5f, -1.0f, 0.0f); -const float MAX_WIND_STRENGTH = 0.02f; -const float FINGER_LENGTH = 0.25f; -const float FINGER_RADIUS = 0.10f; - -void Avatar::renderHair() { - // - // Render the avatar's moveable hair - // - - glm::vec3 headPosition = getHead()->getPosition(); - glPushMatrix(); - glTranslatef(headPosition.x, headPosition.y, headPosition.z); - const glm::quat& rotation = getHead()->getFinalOrientationInWorldFrame(); - glm::vec3 axis = glm::axis(rotation); - glRotatef(glm::degrees(glm::angle(rotation)), axis.x, axis.y, axis.z); - - glBegin(GL_QUADS); - for (int strand = 0; strand < HAIR_STRANDS; strand++) { - for (int link = 0; link < HAIR_LINKS - 1; link++) { - int vertexIndex = strand * HAIR_LINKS + link; - glColor3fv(&_hairColors[vertexIndex].x); - glNormal3fv(&_hairNormals[vertexIndex].x); - glVertex3f(_hairPosition[vertexIndex].x - _hairQuadDelta[vertexIndex].x, - _hairPosition[vertexIndex].y - _hairQuadDelta[vertexIndex].y, - _hairPosition[vertexIndex].z - _hairQuadDelta[vertexIndex].z); - glVertex3f(_hairPosition[vertexIndex].x + _hairQuadDelta[vertexIndex].x, - _hairPosition[vertexIndex].y + _hairQuadDelta[vertexIndex].y, - _hairPosition[vertexIndex].z + _hairQuadDelta[vertexIndex].z); - - glVertex3f(_hairPosition[vertexIndex + 1].x + _hairQuadDelta[vertexIndex].x, - _hairPosition[vertexIndex + 1].y + _hairQuadDelta[vertexIndex].y, - _hairPosition[vertexIndex + 1].z + _hairQuadDelta[vertexIndex].z); - glVertex3f(_hairPosition[vertexIndex + 1].x - _hairQuadDelta[vertexIndex].x, - _hairPosition[vertexIndex + 1].y - _hairQuadDelta[vertexIndex].y, - _hairPosition[vertexIndex + 1].z - _hairQuadDelta[vertexIndex].z); - } - } - glEnd(); - - glPopMatrix(); - -} - -void Avatar::simulateHair(float deltaTime) { - - deltaTime = glm::clamp(deltaTime, 0.0f, 1.0f / 30.0f); - glm::vec3 acceleration = getAcceleration(); - if (glm::length(acceleration) > HAIR_MAX_LINEAR_ACCELERATION) { - acceleration = glm::normalize(acceleration) * HAIR_MAX_LINEAR_ACCELERATION; - } - const glm::quat& rotation = getHead()->getFinalOrientationInWorldFrame(); - acceleration = acceleration * rotation; - glm::vec3 angularVelocity = getAngularVelocity() + getHead()->getAngularVelocity(); - - // Get hand positions to allow touching hair - glm::vec3 leftHandPosition, rightHandPosition; - getSkeletonModel().getLeftHandPosition(leftHandPosition); - getSkeletonModel().getRightHandPosition(rightHandPosition); - leftHandPosition -= getHead()->getPosition(); - rightHandPosition -= getHead()->getPosition(); - glm::quat leftRotation, rightRotation; - getSkeletonModel().getJointRotationInWorldFrame(getSkeletonModel().getLeftHandJointIndex(), leftRotation); - getSkeletonModel().getJointRotationInWorldFrame(getSkeletonModel().getRightHandJointIndex(), rightRotation); - leftHandPosition += glm::vec3(0.0f, FINGER_LENGTH, 0.0f) * glm::inverse(leftRotation); - rightHandPosition += glm::vec3(0.0f, FINGER_LENGTH, 0.0f) * glm::inverse(rightRotation); - leftHandPosition = leftHandPosition * rotation; - rightHandPosition = rightHandPosition * rotation; - - float windIntensity = randFloat() * MAX_WIND_STRENGTH; - - for (int strand = 0; strand < HAIR_STRANDS; strand++) { - for (int link = 0; link < HAIR_LINKS; link++) { - int vertexIndex = strand * HAIR_LINKS + link; - if (vertexIndex % HAIR_LINKS == 0) { - // Base Joint - no integration - } else { - // - // Vertlet Integration - // - // Add velocity from last position, with damping - glm::vec3 thisPosition = _hairPosition[vertexIndex]; - glm::vec3 diff = thisPosition - _hairLastPosition[vertexIndex]; - _hairPosition[vertexIndex] += diff * HAIR_DAMPING; - // Resolve collision with head sphere - if (glm::length(_hairPosition[vertexIndex]) < HEAD_RADIUS) { - _hairPosition[vertexIndex] += glm::normalize(_hairPosition[vertexIndex]) * - (HEAD_RADIUS - glm::length(_hairPosition[vertexIndex])); - } - // Resolve collision with hands - if (glm::length(_hairPosition[vertexIndex] - leftHandPosition) < FINGER_RADIUS) { - _hairPosition[vertexIndex] += glm::normalize(_hairPosition[vertexIndex] - leftHandPosition) * - (FINGER_RADIUS - glm::length(_hairPosition[vertexIndex] - leftHandPosition)); - } - if (glm::length(_hairPosition[vertexIndex] - rightHandPosition) < FINGER_RADIUS) { - _hairPosition[vertexIndex] += glm::normalize(_hairPosition[vertexIndex] - rightHandPosition) * - (FINGER_RADIUS - glm::length(_hairPosition[vertexIndex] - rightHandPosition)); - } - - - // Add a little gravity - _hairPosition[vertexIndex] += HAIR_GRAVITY * rotation * deltaTime; - - // Add linear acceleration of the avatar body - _hairPosition[vertexIndex] -= acceleration * HAIR_ACCELERATION_COUPLING * deltaTime; - - // Add stiffness (like hair care products do) - _hairPosition[vertexIndex] += (_hairOriginalPosition[vertexIndex] - _hairPosition[vertexIndex]) - * powf(1.f - link / HAIR_LINKS, 2.f) * HAIR_STIFFNESS; - - // Add some wind - glm::vec3 wind = WIND_DIRECTION * windIntensity; - _hairPosition[vertexIndex] += wind * deltaTime; - - const float ANGULAR_VELOCITY_MIN = 0.001f; - // Add angular acceleration of the avatar body - if (glm::length(angularVelocity) > ANGULAR_VELOCITY_MIN) { - glm::vec3 yawVector = _hairPosition[vertexIndex]; - yawVector.y = 0.f; - if (glm::length(yawVector) > EPSILON) { - float radius = glm::length(yawVector); - yawVector = glm::normalize(yawVector); - float angle = atan2f(yawVector.x, -yawVector.z) + PI; - glm::vec3 delta = glm::vec3(-1.f, 0.f, 0.f) * glm::angleAxis(angle, glm::vec3(0, 1, 0)); - _hairPosition[vertexIndex] -= delta * radius * angularVelocity.y * HAIR_ANGULAR_VELOCITY_COUPLING * deltaTime; - } - glm::vec3 pitchVector = _hairPosition[vertexIndex]; - pitchVector.x = 0.f; - if (glm::length(pitchVector) > EPSILON) { - float radius = glm::length(pitchVector); - pitchVector = glm::normalize(pitchVector); - float angle = atan2f(pitchVector.y, -pitchVector.z) + PI; - glm::vec3 delta = glm::vec3(0.0f, 1.0f, 0.f) * glm::angleAxis(angle, glm::vec3(1, 0, 0)); - _hairPosition[vertexIndex] -= delta * radius * angularVelocity.x * HAIR_ANGULAR_VELOCITY_COUPLING * deltaTime; - } - glm::vec3 rollVector = _hairPosition[vertexIndex]; - rollVector.z = 0.f; - if (glm::length(rollVector) > EPSILON) { - float radius = glm::length(rollVector); - pitchVector = glm::normalize(rollVector); - float angle = atan2f(rollVector.x, rollVector.y) + PI; - glm::vec3 delta = glm::vec3(-1.0f, 0.0f, 0.f) * glm::angleAxis(angle, glm::vec3(0, 0, 1)); - _hairPosition[vertexIndex] -= delta * radius * angularVelocity.z * HAIR_ANGULAR_VELOCITY_COUPLING * deltaTime; - } - } - - // Iterate length constraints to other links - for (int link = 0; link < HAIR_MAX_CONSTRAINTS; link++) { - if (_hairConstraints[vertexIndex * HAIR_MAX_CONSTRAINTS + link] > -1) { - // If there is a constraint, try to enforce it - glm::vec3 vectorBetween = _hairPosition[_hairConstraints[vertexIndex * HAIR_MAX_CONSTRAINTS + link]] - _hairPosition[vertexIndex]; - _hairPosition[vertexIndex] += glm::normalize(vectorBetween) * (glm::length(vectorBetween) - HAIR_LINK_LENGTH) * CONSTRAINT_RELAXATION * deltaTime; - } - } - // Store start position for next vertlet pass - _hairLastPosition[vertexIndex] = thisPosition; - } - } - } -} - -void Avatar::initializeHair() { - const float FACE_WIDTH = PI / 4.0f; - glm::vec3 thisVertex; - for (int strand = 0; strand < HAIR_STRANDS; strand++) { - float strandAngle = randFloat() * PI; - float azimuth = FACE_WIDTH / 2.0f + (randFloat() * (2.0 * PI - FACE_WIDTH)); - float elevation = PI_OVER_TWO - (randFloat() * 0.75 * PI); - glm::vec3 thisStrand(sinf(azimuth) * cosf(elevation), sinf(elevation), -cosf(azimuth) * cosf(elevation)); - thisStrand *= HEAD_RADIUS; - - for (int link = 0; link < HAIR_LINKS; link++) { - int vertexIndex = strand * HAIR_LINKS + link; - // Clear constraints - for (int link2 = 0; link2 < HAIR_MAX_CONSTRAINTS; link2++) { - _hairConstraints[vertexIndex * HAIR_MAX_CONSTRAINTS + link2] = -1; - } - if (vertexIndex % HAIR_LINKS == 0) { - // start of strand - thisVertex = thisStrand; - } else { - thisVertex+= glm::normalize(thisStrand) * HAIR_LINK_LENGTH; - // Set constraints to vertex before and maybe vertex after in strand - _hairConstraints[vertexIndex * HAIR_MAX_CONSTRAINTS] = vertexIndex - 1; - if (link < (HAIR_LINKS - 1)) { - _hairConstraints[vertexIndex * HAIR_MAX_CONSTRAINTS + 1] = vertexIndex + 1; - } - } - _hairPosition[vertexIndex] = thisVertex; - _hairLastPosition[vertexIndex] = _hairPosition[vertexIndex]; - _hairOriginalPosition[vertexIndex] = _hairPosition[vertexIndex]; - - _hairQuadDelta[vertexIndex] = glm::vec3(cos(strandAngle) * HAIR_THICKNESS, 0.f, sin(strandAngle) * HAIR_THICKNESS); - _hairQuadDelta[vertexIndex] *= 1.f - (link / HAIR_LINKS); - _hairNormals[vertexIndex] = glm::normalize(randVector()); - if (randFloat() < elevation / PI_OVER_TWO) { - _hairColors[vertexIndex] = HAIR_COLOR1 * ((float)(link + 1) / (float)HAIR_LINKS); - } else { - _hairColors[vertexIndex] = HAIR_COLOR2 * ((float)(link + 1) / (float)HAIR_LINKS); - } - - } + // Render Hair + glPushMatrix(); + glm::vec3 headPosition = getHead()->getPosition(); + glTranslatef(headPosition.x, headPosition.y, headPosition.z); + const glm::quat& rotation = getHead()->getFinalOrientationInWorldFrame(); + glm::vec3 axis = glm::axis(rotation); + glRotatef(glm::degrees(glm::angle(rotation)), axis.x, axis.y, axis.z); + _hair.render(); + glPopMatrix(); } } diff --git a/interface/src/avatar/Avatar.h b/interface/src/avatar/Avatar.h index 8c14fc9ed0..8dbeda25dc 100755 --- a/interface/src/avatar/Avatar.h +++ b/interface/src/avatar/Avatar.h @@ -164,7 +164,7 @@ signals: void collisionWithAvatar(const QUuid& myUUID, const QUuid& theirUUID, const CollisionInfo& collision); protected: - QVector _hairs; + Hair _hair; SkeletonModel _skeletonModel; QVector _attachmentModels; float _bodyYawDelta; @@ -210,18 +210,6 @@ protected: virtual void renderAttachments(RenderMode renderMode); virtual void updateJointMappings(); - - glm::vec3 _hairPosition[HAIR_STRANDS * HAIR_LINKS]; - glm::vec3 _hairOriginalPosition[HAIR_STRANDS * HAIR_LINKS]; - glm::vec3 _hairLastPosition[HAIR_STRANDS * HAIR_LINKS]; - glm::vec3 _hairQuadDelta[HAIR_STRANDS * HAIR_LINKS]; - glm::vec3 _hairNormals[HAIR_STRANDS * HAIR_LINKS]; - glm::vec3 _hairColors[HAIR_STRANDS * HAIR_LINKS]; - int _hairIsMoveable[HAIR_STRANDS * HAIR_LINKS]; - int _hairConstraints[HAIR_STRANDS * HAIR_LINKS * 2]; // Hair can link to two others - void renderHair(); - void simulateHair(float deltaTime); - void initializeHair(); private: diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 04e7b628c9..93463556cc 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -185,10 +185,10 @@ void MyAvatar::simulate(float deltaTime) { { PerformanceTimer perfTimer("hair"); if (Menu::getInstance()->isOptionChecked(MenuOption::StringHair)) { - simulateHair(deltaTime); - foreach (Hair* hair, _hairs) { - hair->simulate(deltaTime); - } + _hair.setAcceleration(getAcceleration() * getHead()->getFinalOrientationInWorldFrame()); + _hair.setAngularVelocity(getAngularVelocity() + getHead()->getAngularVelocity() * getHead()->getFinalOrientationInWorldFrame()); + _hair.setGravity(getGravity() * getHead()->getFinalOrientationInWorldFrame()); + _hair.simulate(deltaTime); } } @@ -884,11 +884,17 @@ void MyAvatar::renderBody(RenderMode renderMode, float glowLevel) { const glm::vec3 cameraPos = camera->getPosition() + (camera->getRotation() * glm::vec3(0.0f, 0.0f, 1.0f)) * camera->getDistance(); if (shouldRenderHead(cameraPos, renderMode)) { getHead()->render(1.0f, modelRenderMode); + if (Menu::getInstance()->isOptionChecked(MenuOption::StringHair)) { - renderHair(); - foreach (Hair* hair, _hairs) { - hair->render(); - } + // Render Hair + glPushMatrix(); + glm::vec3 headPosition = getHead()->getPosition(); + glTranslatef(headPosition.x, headPosition.y, headPosition.z); + const glm::quat& rotation = getHead()->getFinalOrientationInWorldFrame(); + glm::vec3 axis = glm::axis(rotation); + glRotatef(glm::degrees(glm::angle(rotation)), axis.x, axis.y, axis.z); + _hair.render(); + glPopMatrix(); } } getHand()->render(true, modelRenderMode);