option to turn on/off string hair

This commit is contained in:
Philip Rosedale 2014-06-25 15:14:17 -07:00
parent 40dee3b39e
commit 58d460da09
6 changed files with 110 additions and 49 deletions

67
examples/concertCamera.js Normal file
View file

@ -0,0 +1,67 @@
//
// concertCamera.js
//
// Created by Philip Rosedale on June 24, 2014
// Copyright 2014 High Fidelity, Inc.
//
// Move a camera through a series of pre-set locations by pressing number keys
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
var oldMode;
var avatarPosition;
var cameraNumber = 0;
var freeCamera = false;
var cameraLocations = [ {x: 7972.0, y: 241.6, z: 7304.1}, {x: 7973.7, y: 241.6, z: 7304.1}, {x: 7975.5, y: 241.6, z: 7304.1}, {x: 7972.3, y: 241.6, z: 7303.3}, {x: 7971.4, y: 241.6, z: 7304.3} ];
var cameraLookAts = [ {x: 7971.1, y: 241.6, z: 7304.1}, {x: 7972.1, y: 241.6, z: 7304.1}, {x: 7972.1, y: 241.6, z: 7304.1}, {x: 7972.1, y: 241.6, z: 7304.1}, {x: 7972.1, y: 241.6, z: 7304.1} ];
function saveCameraState() {
oldMode = Camera.getMode();
avatarPosition = MyAvatar.position;
Camera.setModeShiftPeriod(0.0);
Camera.setMode("independent");
}
function restoreCameraState() {
Camera.stopLooking();
Camera.setMode(oldMode);
}
function update(deltaTime) {
if (freeCamera) {
var delta = Vec3.subtract(MyAvatar.position, avatarPosition);
if (Vec3.length(delta) > 0.05) {
cameraNumber = 0;
freeCamera = false;
restoreCameraState();
}
}
}
function keyPressEvent(event) {
var choice = parseInt(event.text);
if ((choice > 0) && (choice <= cameraLocations.length)) {
print("camera " + choice);
if (!freeCamera) {
saveCameraState();
freeCamera = true;
}
Camera.setMode("independent");
Camera.setPosition(cameraLocations[choice - 1]);
Camera.keepLookingAt(cameraLookAts[choice - 1]);
}
if (event.text == "0") {
// Show camera location in log
var cameraLocation = Camera.getPosition();
print(cameraLocation.x + ", " + cameraLocation.y + ", " + cameraLocation.z);
}
}
Script.update.connect(update);
Controller.keyPressEvent.connect(keyPressEvent);

View file

@ -345,6 +345,7 @@ Menu::Menu() :
addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::Metavoxels, 0, true);
addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::BuckyBalls, 0, false);
addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::StringHair, 0, false);
addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::Particles, 0, true);
addActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::LodTools, Qt::SHIFT | Qt::Key_L, this, SLOT(lodTools()));

View file

@ -319,6 +319,7 @@ namespace MenuOption {
const QString Bandwidth = "Bandwidth Display";
const QString BandwidthDetails = "Bandwidth Details";
const QString BuckyBalls = "Bucky Balls";
const QString StringHair = "String Hair";
const QString CascadedShadows = "Cascaded";
const QString Chat = "Chat...";
const QString ChatCircling = "Chat Circling";

View file

@ -140,7 +140,9 @@ void Avatar::simulate(float deltaTime) {
head->setScale(_scale);
head->simulate(deltaTime, false, _shouldRenderBillboard);
simulateHair(deltaTime);
if (Menu::getInstance()->isOptionChecked(MenuOption::StringHair)) {
simulateHair(deltaTime);
}
}
// update position by velocity, and subtract the change added earlier for gravity
@ -376,26 +378,32 @@ void Avatar::renderBody(RenderMode renderMode, float glowLevel) {
getHand()->render(false, modelRenderMode);
}
getHead()->render(1.0f, modelRenderMode);
renderHair();
if (Menu::getInstance()->isOptionChecked(MenuOption::StringHair)) {
renderHair();
}
}
//
// 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.18f;
const float CONSTRAINT_RELAXATION = 20.0f;
const glm::vec3 HAIR_GRAVITY(0.f, -0.015f, 0.f);
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.f;
const float HAIR_THICKNESS = 0.020f;
const float HAIR_STIFFNESS = 0.0006f;
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.f);
const float MAX_WIND_STRENGTH = 0.01f;
const float FINGER_LENGTH = 0.25;
const float FINGER_RADIUS = 0.10;
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() {
//
@ -403,26 +411,6 @@ void Avatar::renderHair() {
//
glm::vec3 headPosition = getHead()->getPosition();
/*
glm::vec3 leftHandPosition, rightHandPosition;
getSkeletonModel().getLeftHandPosition(leftHandPosition);
getSkeletonModel().getRightHandPosition(rightHandPosition);
glm::quat leftRotation, rightRotation;
getSkeletonModel().getJointRotationInWorldFrame(getSkeletonModel().getRightHandJointIndex(), rightRotation);
rightHandPosition += glm::vec3(0.0f, FINGER_LENGTH, 0.f) * glm::inverse(rightRotation);
glPushMatrix();
glTranslatef(leftHandPosition.x, leftHandPosition.y, leftHandPosition.z);
glColor4f(1.0f, 0.0f, 0.0f, 0.5f);
glutSolidSphere(FINGER_RADIUS, 20, 20);
glPopMatrix();
glPushMatrix();
glTranslatef(rightHandPosition.x, rightHandPosition.y, rightHandPosition.z);
glColor4f(1.0f, 0.0f, 0.0f, 0.5f);
glutSolidSphere(FINGER_RADIUS, 20, 20);
glPopMatrix();
*/
glPushMatrix();
glTranslatef(headPosition.x, headPosition.y, headPosition.z);
const glm::quat& rotation = getHead()->getFinalOrientationInWorldFrame();
@ -458,7 +446,7 @@ void Avatar::renderHair() {
void Avatar::simulateHair(float deltaTime) {
deltaTime = glm::clamp(deltaTime, 0.f, 1.f / 30.f);
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;
@ -476,8 +464,8 @@ void Avatar::simulateHair(float deltaTime) {
glm::quat leftRotation, rightRotation;
getSkeletonModel().getJointRotationInWorldFrame(getSkeletonModel().getLeftHandJointIndex(), leftRotation);
getSkeletonModel().getJointRotationInWorldFrame(getSkeletonModel().getRightHandJointIndex(), rightRotation);
leftHandPosition += glm::vec3(0.0f, FINGER_LENGTH, 0.f) * glm::inverse(leftRotation);
rightHandPosition += glm::vec3(0.0f, FINGER_LENGTH, 0.f) * glm::inverse(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;
@ -499,9 +487,9 @@ void Avatar::simulateHair(float deltaTime) {
// Resolve collision with head sphere
if (glm::length(_hairPosition[vertexIndex]) < HEAD_RADIUS) {
_hairPosition[vertexIndex] += glm::normalize(_hairPosition[vertexIndex]) *
(HEAD_RADIUS - glm::length(_hairPosition[vertexIndex])); // * COLLISION_RELAXATION * deltaTime;
(HEAD_RADIUS - glm::length(_hairPosition[vertexIndex]));
}
// Collide with hands
// 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));
@ -518,7 +506,7 @@ void Avatar::simulateHair(float deltaTime) {
// Add linear acceleration of the avatar body
_hairPosition[vertexIndex] -= acceleration * HAIR_ACCELERATION_COUPLING * deltaTime;
// Add stiffness (product)
// Add stiffness (like hair care products do)
_hairPosition[vertexIndex] += (_hairOriginalPosition[vertexIndex] - _hairPosition[vertexIndex])
* powf(1.f - link / HAIR_LINKS, 2.f) * HAIR_STIFFNESS;
@ -580,13 +568,8 @@ void Avatar::initializeHair() {
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);
/*if ((azimuth > FACE_WIDTH) || (azimuth < -FACE_WIDTH)) {
elevation = randFloat() * PI_OVER_TWO;
} else {
elevation = (PI_OVER_TWO / 2.f) + randFloat() * (PI_OVER_TWO / 2.f);
}*/
glm::vec3 thisStrand(sinf(azimuth) * cosf(elevation), sinf(elevation), -cosf(azimuth) * cosf(elevation));
thisStrand *= HEAD_RADIUS + 0.01f;
thisStrand *= HEAD_RADIUS;
for (int link = 0; link < HAIR_LINKS; link++) {
int vertexIndex = strand * HAIR_LINKS + link;
@ -610,6 +593,7 @@ void Avatar::initializeHair() {
_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);

View file

@ -32,9 +32,9 @@ static const float RESCALING_TOLERANCE = .02f;
extern const float CHAT_MESSAGE_SCALE;
extern const float CHAT_MESSAGE_HEIGHT;
const int HAIR_STRANDS = 200; // Number of strands of hair
const int HAIR_LINKS = 10; // Number of links in a hair strand
const int HAIR_MAX_CONSTRAINTS = 2;
const int HAIR_STRANDS = 150; // Number of strands of hair
const int HAIR_LINKS = 10; // Number of links in a hair strand
const int HAIR_MAX_CONSTRAINTS = 2; // Hair verlet is connected to at most how many others
enum DriveKeys {
FWD = 0,

View file

@ -195,7 +195,12 @@ void MyAvatar::simulate(float deltaTime) {
head->simulate(deltaTime, true);
}
simulateHair(deltaTime);
{
PerformanceTimer perfTimer("MyAvatar::simulate/hair Simulate");
if (Menu::getInstance()->isOptionChecked(MenuOption::StringHair)) {
simulateHair(deltaTime);
}
}
{
PerformanceTimer perfTimer("MyAvatar::simulate/ragdoll");
@ -370,6 +375,7 @@ void MyAvatar::render(const glm::vec3& cameraPosition, RenderMode renderMode) {
if (!_shouldRender) {
return; // exit early
}
Avatar::render(cameraPosition, renderMode);
// don't display IK constraints in shadow mode
@ -852,7 +858,9 @@ void MyAvatar::renderBody(RenderMode renderMode, float glowLevel) {
// Render head so long as the camera isn't inside it
if (shouldRenderHead(Application::getInstance()->getCamera()->getPosition(), renderMode)) {
getHead()->render(1.0f, modelRenderMode);
renderHair();
if (Menu::getInstance()->isOptionChecked(MenuOption::StringHair)) {
renderHair();
}
}
getHand()->render(true, modelRenderMode);
}