253 lines · 11.9 KB · C++
RawDownload
////  Hair.cpp//  interface/src////  Created by Philip on June 26, 2014//  Copyright 2014 High Fidelity, Inc.////  Distributed under the Apache License, Version 2.0.//  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html////  Creates single flexible verlet-integrated strands that can be used for hair/fur/grass #include "Hair.h" #include "Util.h"#include "world.h" const float HAIR_DAMPING = 0.99f;const float CONSTRAINT_RELAXATION = 10.0f;const float HAIR_ACCELERATION_COUPLING = 0.045f;const float HAIR_ANGULAR_VELOCITY_COUPLING = 0.001f;const float HAIR_ANGULAR_ACCELERATION_COUPLING = 0.001f;const float HAIR_MAX_LINEAR_ACCELERATION = 4.0f;const float HAIR_STIFFNESS = 0.00f;const glm::vec3 HAIR_COLOR1(0.98f, 0.76f, 0.075f);const glm::vec3 HAIR_COLOR2(0.912f, 0.184f, 0.101f); Hair::Hair(int strands,           int links,           float radius,           float linkLength,           float hairThickness) :    _strands(strands),    _links(links),    _linkLength(linkLength),    _hairThickness(hairThickness),    _radius(radius),    _acceleration(0.0f),    _angularVelocity(0.0f),    _angularAcceleration(0.0f),    _gravity(DEFAULT_GRAVITY),    _loudness(0.0f){    _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    glm::vec3 thisVertex;    for (int strand = 0; strand < _strands; strand++) {        float strandAngle = randFloat() * PI;         float azimuth = (float)strand / (float)_strands * PI * 2.0f;        float elevation = 0.0f;                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;                }            }            _hairOriginalPosition[vertexIndex] = _hairLastPosition[vertexIndex] = _hairPosition[vertexIndex] = thisVertex;                        _hairQuadDelta[vertexIndex] = glm::vec3(cos(strandAngle) * _hairThickness, 0.0f, sin(strandAngle) * _hairThickness);            _hairQuadDelta[vertexIndex] *= ((float)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);            }        }    }} Hair::~Hair() {    delete[] _hairPosition;    delete[] _hairOriginalPosition;    delete[] _hairLastPosition;    delete[] _hairQuadDelta;    delete[] _hairNormals;    delete[] _hairColors;    delete[] _hairIsMoveable;    delete[] _hairConstraints;} const float SOUND_THRESHOLD = 40.0f; 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;    }        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 collisions with sphere                if (glm::length(_hairPosition[vertexIndex]) < _radius) {                    _hairPosition[vertexIndex] += glm::normalize(_hairPosition[vertexIndex]) *                    (_radius - glm::length(_hairPosition[vertexIndex]));                } */                                //  Collide with a conical body descending from the root of the hair                glm::vec3 thisVertex = _hairPosition[vertexIndex];                float depth = -thisVertex.y;                thisVertex.y = 0.0f;                const float BODY_CONE_ANGLE = 0.30;                if (glm::length(thisVertex) < depth * BODY_CONE_ANGLE) {                    _hairPosition[vertexIndex] += glm::normalize(thisVertex) * (depth * BODY_CONE_ANGLE - glm::length(thisVertex));                }                                //  Add random thing driven by loudness                float loudnessFactor = (_loudness > SOUND_THRESHOLD) ? logf(_loudness - SOUND_THRESHOLD) / 2000.0f : 0.0f;                 const float QUIESCENT_LOUDNESS = 0.0f;                _hairPosition[vertexIndex] += randVector() * (QUIESCENT_LOUDNESS + loudnessFactor) * ((float)link / (float)_links);                 //  Add gravity                const float SCALE_GRAVITY = 0.001f;                _hairPosition[vertexIndex] += _gravity * deltaTime * SCALE_GRAVITY;                                //  Add linear acceleration                _hairPosition[vertexIndex] -= acceleration * HAIR_ACCELERATION_COUPLING * deltaTime;                                //  Add stiffness to return to original position                _hairPosition[vertexIndex] += (_hairOriginalPosition[vertexIndex] - _hairPosition[vertexIndex])                * powf(1.0f - (float)link / _links, 2.0f) * HAIR_STIFFNESS;                                //  Add angular acceleration                const float ANGULAR_VELOCITY_MIN = 0.001f;                if (glm::length(_angularVelocity) > ANGULAR_VELOCITY_MIN) {                    glm::vec3 yawVector = _hairPosition[vertexIndex];                    glm::vec3 angularVelocity = _angularVelocity * HAIR_ANGULAR_VELOCITY_COUPLING;                    glm::vec3 angularAcceleration = _angularAcceleration * HAIR_ANGULAR_ACCELERATION_COUPLING;                    yawVector.y = 0.0f;                    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.0f, 0.0f, 0.0f) * glm::angleAxis(angle, glm::vec3(0, 1, 0));                        _hairPosition[vertexIndex] -= delta * radius * (angularVelocity.y - angularAcceleration.y) * deltaTime;                    }                    glm::vec3 pitchVector = _hairPosition[vertexIndex];                    pitchVector.x = 0.0f;                    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.0f) * glm::angleAxis(angle, glm::vec3(1, 0, 0));                        _hairPosition[vertexIndex] -= delta * radius * (angularVelocity.x - angularAcceleration.x) * deltaTime;                    }                    glm::vec3 rollVector = _hairPosition[vertexIndex];                    rollVector.z = 0.0f;                    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.0f) * glm::angleAxis(angle, glm::vec3(0, 0, 1));                        _hairPosition[vertexIndex] -= delta * radius * (angularVelocity.z - angularAcceleration.z) * deltaTime;                    }                }                                //  Impose link constraints                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 verlet pass                _hairLastPosition[vertexIndex] = thisPosition;            }        }    }} void Hair::render() {    //    //  Before calling this function, translate/rotate to the origin of the owning object    //    float loudnessFactor = (_loudness > SOUND_THRESHOLD) ? logf(_loudness - SOUND_THRESHOLD) / 16.0f : 0.0f;    const int SPARKLE_EVERY = 5;    const float HAIR_SETBACK = 0.0f;    int sparkleIndex = (int) (randFloat() * SPARKLE_EVERY);    glPushMatrix();    glTranslatef(0.0f, 0.0f, HAIR_SETBACK);    glBegin(GL_QUADS);    for (int strand = 0; strand < _strands; strand++) {        for (int link = 0; link < _links - 1; link++) {            int vertexIndex = strand * _links + link;            glm::vec3 thisColor = _hairColors[vertexIndex];            if (sparkleIndex % SPARKLE_EVERY == 0) {                thisColor.x += (1.0f - thisColor.x) * loudnessFactor;                thisColor.y += (1.0f - thisColor.y) * loudnessFactor;                thisColor.z += (1.0f - thisColor.z) * loudnessFactor;            }            glColor3fv(&thisColor.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);            sparkleIndex++;        }    }    glEnd();    glPopMatrix();}