This commit is contained in:
Philip Rosedale 2013-06-25 21:08:37 -07:00
commit 0b37ce5375
6 changed files with 225 additions and 9 deletions

View file

@ -33,6 +33,7 @@
#include "ViewFrustum.h"
#include "VoxelSystem.h"
#include "Webcam.h"
#include "renderer/GeometryCache.h"
#include "ui/ChatEntry.h"
class QAction;
@ -89,6 +90,7 @@ public:
bool shouldDynamicallySetJitterBuffer() { return _audioJitterBufferSamples == 0; }
QNetworkAccessManager* getNetworkAccessManager() { return _networkAccessManager; }
GeometryCache* getGeometryCache() { return &_geometryCache; }
private slots:
@ -318,6 +320,8 @@ private:
int _scaleInLocation;
int _hmdWarpParamLocation;
GeometryCache _geometryCache;
#ifndef _WIN32
Audio _audio;
#endif

View file

@ -10,11 +10,13 @@
//
#include <glm/glm.hpp>
#include "Util.h"
#include "sharedUtil.h"
#include "world.h"
#include "InterfaceConfig.h"
#include <SharedUtil.h>
#include "Balls.h"
#include "InterfaceConfig.h"
#include "Util.h"
#include "world.h"
const float INITIAL_AREA = 0.2f;
const float BALL_RADIUS = 0.025f;
@ -29,7 +31,7 @@ Balls::Balls(int numberOfBalls) {
_balls[i].velocity = glm::vec3(0, 0, 0);
_balls[i].radius = BALL_RADIUS;
for (unsigned int j = 0; j < NUMBER_SPRINGS; ++j) {
_balls[i].links[j] = NULL;
_balls[i].links[j] = 0;
}
}
_color = INITIAL_COLOR;

View file

@ -27,7 +27,8 @@ const float EAR_RIGHT_OFFSET = 1.0;
const float MOUTH_UP_OFFSET = -0.3f;
const float HEAD_MOTION_DECAY = 0.1;
const float MINIMUM_EYE_ROTATION_DOT = 0.5f; // based on a dot product: 1.0 is straight ahead, 0.0 is 90 degrees off
const float EYEBALL_RADIUS = 0.017;
const float EYEBALL_RADIUS = 0.017;
const float EYELID_RADIUS = 0.019;
const float EYEBALL_COLOR[3] = { 0.9f, 0.9f, 0.8f };
const float HAIR_SPRING_FORCE = 10.0f;
const float HAIR_TORQUE_FORCE = 0.1f;
@ -71,7 +72,12 @@ Head::Head(Avatar* owningAvatar) :
_mohawkTriangleFan(NULL),
_mohawkColors(NULL),
_saccade(0.0f, 0.0f, 0.0f),
_saccadeTarget(0.0f, 0.0f, 0.0f)
_saccadeTarget(0.0f, 0.0f, 0.0f),
_leftEyeBlink(0.0f),
_rightEyeBlink(0.0f),
_leftEyeBlinkVelocity(0.0f),
_rightEyeBlinkVelocity(0.0f),
_timeWithoutTalking(0.0f)
{
if (USING_PHYSICAL_MOHAWK) {
resetHairPhysics();
@ -142,12 +148,22 @@ void Head::simulate(float deltaTime, bool isMine) {
_saccadeTarget = SACCADE_MAGNITUDE * randVector();
}
_saccade += (_saccadeTarget - _saccade) * 0.50f;
// Update audio trailing average for rendering facial animations
const float AUDIO_AVERAGING_SECS = 0.05;
_averageLoudness = (1.f - deltaTime / AUDIO_AVERAGING_SECS) * _averageLoudness +
(deltaTime / AUDIO_AVERAGING_SECS) * _audioLoudness;
// Detect transition from talking to not; force blink after that and a delay
bool forceBlink = false;
const float TALKING_LOUDNESS = 100.0f;
const float BLINK_AFTER_TALKING = 0.25f;
if (_averageLoudness > TALKING_LOUDNESS) {
_timeWithoutTalking = 0.0f;
} else if (_timeWithoutTalking < BLINK_AFTER_TALKING && (_timeWithoutTalking += deltaTime) >= BLINK_AFTER_TALKING) {
forceBlink = true;
}
// Update audio attack data for facial animation (eyebrows and mouth)
_audioAttack = 0.9 * _audioAttack + 0.1 * fabs(_audioLoudness - _lastLoudness);
@ -162,6 +178,36 @@ void Head::simulate(float deltaTime, bool isMine) {
_browAudioLift *= 0.7f;
// update eyelid blinking
const float BLINK_SPEED = 5.0f;
const float FULLY_OPEN = 0.0f;
const float FULLY_CLOSED = 1.0f;
if (_leftEyeBlinkVelocity == 0.0f && _rightEyeBlinkVelocity == 0.0f) {
// no blinking when brows are raised; blink less with increasing loudness
const float ROOT_LOUDNESS_TO_BLINK_INTERVAL = 0.75f;
if (forceBlink || (_browAudioLift < EPSILON && shouldDo(
sqrtf(_averageLoudness) * ROOT_LOUDNESS_TO_BLINK_INTERVAL, deltaTime))) {
_leftEyeBlinkVelocity = BLINK_SPEED;
_rightEyeBlinkVelocity = BLINK_SPEED;
}
} else {
_leftEyeBlink = glm::clamp(_leftEyeBlink + _leftEyeBlinkVelocity * deltaTime, FULLY_OPEN, FULLY_CLOSED);
_rightEyeBlink = glm::clamp(_rightEyeBlink + _rightEyeBlinkVelocity * deltaTime, FULLY_OPEN, FULLY_CLOSED);
if (_leftEyeBlink == FULLY_CLOSED) {
_leftEyeBlinkVelocity = -BLINK_SPEED;
} else if (_leftEyeBlink == FULLY_OPEN) {
_leftEyeBlinkVelocity = 0.0f;
}
if (_rightEyeBlink == FULLY_CLOSED) {
_rightEyeBlinkVelocity = -BLINK_SPEED;
} else if (_rightEyeBlink == FULLY_OPEN) {
_rightEyeBlinkVelocity = 0.0f;
}
}
// based on the nature of the lookat position, determine if the eyes can look / are looking at it.
if (USING_PHYSICAL_MOHAWK) {
updateHairPhysics(deltaTime);
@ -534,6 +580,37 @@ void Head::renderEyeBalls() {
_irisProgram->release();
glBindTexture(GL_TEXTURE_2D, 0);
glDisable(GL_TEXTURE_2D);
glEnable(GL_RESCALE_NORMAL);
glColor4f(_skinColor.x, _skinColor.y, _skinColor.z, _renderAlpha);
// left eyelid
glPushMatrix(); {
glTranslatef(_leftEyePosition.x, _leftEyePosition.y, _leftEyePosition.z); //translate to eyeball position
glm::vec3 rotationAxis = glm::axis(orientation);
glRotatef(glm::angle(orientation), rotationAxis.x, rotationAxis.y, rotationAxis.z);
glScalef(EYELID_RADIUS, EYELID_RADIUS, EYELID_RADIUS);
glRotatef(-90 * _leftEyeBlink, 1, 0, 0);
Application::getInstance()->getGeometryCache()->renderHemisphere(15, 10);
glRotatef(180 * _leftEyeBlink, 1, 0, 0);
Application::getInstance()->getGeometryCache()->renderHemisphere(15, 10);
}
glPopMatrix();
// right eyelid
glPushMatrix(); {
glTranslatef(_rightEyePosition.x, _rightEyePosition.y, _rightEyePosition.z); //translate to eyeball position
glm::vec3 rotationAxis = glm::axis(orientation);
glRotatef(glm::angle(orientation), rotationAxis.x, rotationAxis.y, rotationAxis.z);
glScalef(EYELID_RADIUS, EYELID_RADIUS, EYELID_RADIUS);
glRotatef(-90 * _rightEyeBlink, 1, 0, 0);
Application::getInstance()->getGeometryCache()->renderHemisphere(15, 10);
glRotatef(180 * _rightEyeBlink, 1, 0, 0);
Application::getInstance()->getGeometryCache()->renderHemisphere(15, 10);
}
glPopMatrix();
glDisable(GL_RESCALE_NORMAL);
}
void Head::renderLookatVectors(glm::vec3 leftEyePosition, glm::vec3 rightEyePosition, glm::vec3 lookatPosition) {

View file

@ -106,6 +106,11 @@ private:
glm::vec3* _mohawkColors;
glm::vec3 _saccade;
glm::vec3 _saccadeTarget;
float _leftEyeBlink;
float _rightEyeBlink;
float _leftEyeBlinkVelocity;
float _rightEyeBlinkVelocity;
float _timeWithoutTalking;
static ProgramObject* _irisProgram;
static GLuint _irisTextureID;

View file

@ -0,0 +1,97 @@
//
// GeometryCache.cpp
// interface
//
// Created by Andrzej Kapolka on 6/21/13.
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
#include <cmath>
#include "GeometryCache.h"
#include "world.h"
GeometryCache::~GeometryCache() {
foreach (const VerticesIndices& vbo, _hemisphereVBOs) {
glDeleteBuffers(1, &vbo.first);
glDeleteBuffers(1, &vbo.second);
}
}
void GeometryCache::renderHemisphere(int slices, int stacks) {
VerticesIndices& vbo = _hemisphereVBOs[SlicesStacks(slices, stacks)];
int vertices = slices * (stacks - 1) + 1;
int indices = slices * 2 * 3 * (stacks - 2) + slices * 3;
if (vbo.first == 0) {
GLfloat* vertexData = new GLfloat[vertices * 3];
GLfloat* vertex = vertexData;
for (int i = 0; i < stacks - 1; i++) {
float phi = PIf * 0.5f * i / (stacks - 1);
float z = sinf(phi), radius = cosf(phi);
for (int j = 0; j < slices; j++) {
float theta = PIf * 2.0f * j / slices;
*(vertex++) = sinf(theta) * radius;
*(vertex++) = cosf(theta) * radius;
*(vertex++) = z;
}
}
*(vertex++) = 0.0f;
*(vertex++) = 0.0f;
*(vertex++) = 1.0f;
glGenBuffers(1, &vbo.first);
glBindBuffer(GL_ARRAY_BUFFER, vbo.first);
const int BYTES_PER_VERTEX = 3 * sizeof(GLfloat);
glBufferData(GL_ARRAY_BUFFER, vertices * BYTES_PER_VERTEX, vertexData, GL_STATIC_DRAW);
delete[] vertexData;
GLushort* indexData = new GLushort[indices];
GLushort* index = indexData;
for (int i = 0; i < stacks - 2; i++) {
GLushort bottom = i * slices;
GLushort top = bottom + slices;
for (int j = 0; j < slices; j++) {
int next = (j + 1) % slices;
*(index++) = bottom + j;
*(index++) = top + next;
*(index++) = top + j;
*(index++) = bottom + j;
*(index++) = bottom + next;
*(index++) = top + next;
}
}
GLushort bottom = (stacks - 2) * slices;
GLushort top = bottom + slices;
for (int i = 0; i < slices; i++) {
*(index++) = bottom + i;
*(index++) = bottom + (i + 1) % slices;
*(index++) = top;
}
glGenBuffers(1, &vbo.second);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vbo.second);
const int BYTES_PER_INDEX = sizeof(GLushort);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices * BYTES_PER_INDEX, indexData, GL_STATIC_DRAW);
delete[] indexData;
} else {
glBindBuffer(GL_ARRAY_BUFFER, vbo.first);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vbo.second);
}
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_NORMAL_ARRAY);
glVertexPointer(3, GL_FLOAT, 0, 0);
glNormalPointer(GL_FLOAT, 0, 0);
glDrawRangeElementsEXT(GL_TRIANGLES, 0, vertices - 1, indices, GL_UNSIGNED_SHORT, 0);
glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_NORMAL_ARRAY);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
}

View file

@ -0,0 +1,31 @@
//
// GeometryCache.h
// interface
//
// Created by Andrzej Kapolka on 6/21/13.
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
//
#ifndef __interface__GeometryCache__
#define __interface__GeometryCache__
#include <QHash>
#include "InterfaceConfig.h"
class GeometryCache {
public:
~GeometryCache();
void renderHemisphere(int slices, int stacks);
private:
typedef QPair<int, int> SlicesStacks;
typedef QPair<GLuint, GLuint> VerticesIndices;
QHash<SlicesStacks, VerticesIndices> _hemisphereVBOs;
};
#endif /* defined(__interface__GeometryCache__) */