resolve conflicts on merge with upstream/master

This commit is contained in:
Stephen Birarda 2013-09-30 10:12:54 -07:00
commit ef1ff01188
32 changed files with 1387 additions and 412 deletions

View file

@ -84,7 +84,7 @@ and features are available via menus in the Interface application.
Other components Other components
======== ========
voxel-server, animation-server, audio-mixer, avatar-mixer, domain-server, assignment-client, animation-server, domain-server,
pairing-server and space-server are architectural components that will allow pairing-server and space-server are architectural components that will allow
you to run the full stack of the virtual world should you choose to. you to run the full stack of the virtual world should you choose to.

View file

@ -16,7 +16,10 @@
#include "Agent.h" #include "Agent.h"
#include "voxels/VoxelScriptingInterface.h" #include "voxels/VoxelScriptingInterface.h"
Agent::Agent(const unsigned char* dataBuffer, int numBytes) : Assignment(dataBuffer, numBytes) { Agent::Agent(const unsigned char* dataBuffer, int numBytes) :
Assignment(dataBuffer, numBytes),
_shouldStop(false)
{
} }

View file

@ -0,0 +1,10 @@
#version 120
attribute float voxelSizeIn;
varying float voxelSize;
void main(void) {
gl_FrontColor = gl_Color;
gl_Position = gl_Vertex; // don't call ftransform(), because we do projection in geometry shader
voxelSize = voxelSizeIn;
}

View file

@ -0,0 +1,77 @@
#version 120
#extension GL_ARB_geometry_shader4 : enable
//
// VOXEL GEOMETRY SHADER
//
// Input: gl_VerticesIn/gl_PositionIn
// GL_POINTS
// Assumes vertex shader has not transformed coordinates
// Each gl_PositionIn is the corner of voxel
// varying voxelSize - which is the voxel size
//
// Note: In vertex shader doesn't do any transform. Therefore passing the 3D world coordinates xyz to us
//
// Output: GL_TRIANGLE_STRIP
//
// Issues:
// how do we need to handle lighting of these colors??
// how do we handle normals?
// check for size=0 and don't output the primitive
//
varying in float voxelSize[1];
const int VERTICES_PER_FACE = 4;
const int COORD_PER_VERTEX = 3;
const int COORD_PER_FACE = COORD_PER_VERTEX * VERTICES_PER_FACE;
void faceOfVoxel(vec4 corner, float scale, float[COORD_PER_FACE] facePoints, vec4 color, vec4 normal) {
for (int v = 0; v < VERTICES_PER_FACE; v++ ) {
vec4 vertex = corner;
for (int c = 0; c < COORD_PER_VERTEX; c++ ) {
int cIndex = c + (v * COORD_PER_VERTEX);
vertex[c] += (facePoints[cIndex] * scale);
}
gl_FrontColor = color * (gl_LightModel.ambient + gl_LightSource[0].ambient +
gl_LightSource[0].diffuse * max(0.0, dot(normal, gl_LightSource[0].position)));
gl_Position = gl_ModelViewProjectionMatrix * vertex;
EmitVertex();
}
EndPrimitive();
}
void main() {
//increment variable
int i;
vec4 corner;
float scale;
float bottomFace[COORD_PER_FACE] = float[COORD_PER_FACE]( 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 1 );
float topFace[COORD_PER_FACE] = float[COORD_PER_FACE]( 1, 1, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1 );
float rightFace[COORD_PER_FACE] = float[COORD_PER_FACE]( 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 1 );
float leftFace[COORD_PER_FACE] = float[COORD_PER_FACE]( 1, 0, 0, 1, 1, 0, 1, 0, 1, 1, 1, 1 );
float frontFace[COORD_PER_FACE] = float[COORD_PER_FACE]( 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0 );
float backFace[COORD_PER_FACE] = float[COORD_PER_FACE]( 1, 0, 1, 0, 0, 1, 1, 1, 1, 0, 1, 1 );
vec4 bottomNormal = vec4(0.0, -1, 0.0, 0.0);
vec4 topNormal = vec4(0.0, 1, 0.0, 0.0);
vec4 rightNormal = vec4( -1, 0.0, 0.0, 0.0);
vec4 leftNormal = vec4( 1, 0.0, 0.0, 0.0);
vec4 frontNormal = vec4(0.0, 0.0, -1, 0.0);
vec4 backNormal = vec4(0.0, 0.0, 1, 0.0);
for(i = 0; i < gl_VerticesIn; i++) {
corner = gl_PositionIn[i];
scale = voxelSize[i];
faceOfVoxel(corner, scale, bottomFace, gl_FrontColorIn[i], bottomNormal);
faceOfVoxel(corner, scale, topFace, gl_FrontColorIn[i], topNormal );
faceOfVoxel(corner, scale, rightFace, gl_FrontColorIn[i], rightNormal );
faceOfVoxel(corner, scale, leftFace, gl_FrontColorIn[i], leftNormal );
faceOfVoxel(corner, scale, frontFace, gl_FrontColorIn[i], frontNormal );
faceOfVoxel(corner, scale, backFace, gl_FrontColorIn[i], backNormal );
}
}

View file

@ -116,7 +116,7 @@ Application::Application(int& argc, char** argv, timeval &startup_time) :
_nudgeStarted(false), _nudgeStarted(false),
_lookingAlongX(false), _lookingAlongX(false),
_lookingAwayFromOrigin(true), _lookingAwayFromOrigin(true),
_isLookingAtOtherAvatar(false), _lookatTargetAvatar(NULL),
_lookatIndicatorScale(1.0f), _lookatIndicatorScale(1.0f),
_perfStatsOn(false), _perfStatsOn(false),
_chatEntryOn(false), _chatEntryOn(false),
@ -151,6 +151,7 @@ Application::Application(int& argc, char** argv, timeval &startup_time) :
NodeList::getInstance()->addHook(&_voxels); NodeList::getInstance()->addHook(&_voxels);
NodeList::getInstance()->addHook(this); NodeList::getInstance()->addHook(this);
NodeList::getInstance()->addDomainListener(this);
// network receive thread and voxel parsing thread are both controlled by the --nonblocking command line // network receive thread and voxel parsing thread are both controlled by the --nonblocking command line
@ -231,6 +232,7 @@ Application::Application(int& argc, char** argv, timeval &startup_time) :
Application::~Application() { Application::~Application() {
NodeList::getInstance()->removeHook(&_voxels); NodeList::getInstance()->removeHook(&_voxels);
NodeList::getInstance()->removeHook(this); NodeList::getInstance()->removeHook(this);
NodeList::getInstance()->removeDomainListener(this);
_sharedVoxelSystem.changeTree(new VoxelTree); _sharedVoxelSystem.changeTree(new VoxelTree);
@ -309,8 +311,6 @@ void Application::initializeGL() {
char title[50]; char title[50];
sprintf(title, "Interface: %4.2f seconds\n", startupTime); sprintf(title, "Interface: %4.2f seconds\n", startupTime);
qDebug("%s", title); qDebug("%s", title);
_window->setWindowTitle(title);
const char LOGSTASH_INTERFACE_START_TIME_KEY[] = "interface-start-time"; const char LOGSTASH_INTERFACE_START_TIME_KEY[] = "interface-start-time";
// ask the Logstash class to record the startup time // ask the Logstash class to record the startup time
@ -389,6 +389,7 @@ void Application::paintGL() {
} else { } else {
_glowEffect.prepare(); _glowEffect.prepare();
glMatrixMode(GL_MODELVIEW); glMatrixMode(GL_MODELVIEW);
glPushMatrix(); glPushMatrix();
@ -541,6 +542,10 @@ void Application::keyPressEvent(QKeyEvent* event) {
_myAvatar.setDriveKeys(UP, 1); _myAvatar.setDriveKeys(UP, 1);
} }
break; break;
case Qt::Key_Asterisk:
Menu::getInstance()->triggerOption(MenuOption::Stars);
break;
case Qt::Key_C: case Qt::Key_C:
if (isShifted) { if (isShifted) {
@ -810,8 +815,6 @@ void Application::keyPressEvent(QKeyEvent* event) {
case Qt::Key_F: case Qt::Key_F:
if (isShifted) { if (isShifted) {
Menu::getInstance()->triggerOption(MenuOption::DisplayFrustum); Menu::getInstance()->triggerOption(MenuOption::DisplayFrustum);
} else {
Menu::getInstance()->triggerOption(MenuOption::Fullscreen);
} }
break; break;
case Qt::Key_V: case Qt::Key_V:
@ -974,7 +977,7 @@ void Application::mousePressEvent(QMouseEvent* event) {
maybeEditVoxelUnderCursor(); maybeEditVoxelUnderCursor();
if (!_palette.isActive() && (!_isHoverVoxel || _isLookingAtOtherAvatar)) { if (!_palette.isActive() && (!_isHoverVoxel || _lookatTargetAvatar)) {
_pieMenu.mousePressEvent(_mouseX, _mouseY); _pieMenu.mousePressEvent(_mouseX, _mouseY);
} }
@ -998,6 +1001,17 @@ void Application::mousePressEvent(QMouseEvent* event) {
_audio.startCollisionSound(1.0, frequency, 0.0, HOVER_VOXEL_DECAY); _audio.startCollisionSound(1.0, frequency, 0.0, HOVER_VOXEL_DECAY);
_isHoverVoxelSounding = true; _isHoverVoxelSounding = true;
const float PERCENTAGE_TO_MOVE_TOWARD = 0.90f;
glm::vec3 newTarget = getMouseVoxelWorldCoordinates(_hoverVoxel);
glm::vec3 myPosition = _myAvatar.getPosition();
// If there is not an action tool set (add, delete, color), move to this voxel
if (!(Menu::getInstance()->isOptionChecked(MenuOption::VoxelAddMode) ||
Menu::getInstance()->isOptionChecked(MenuOption::VoxelDeleteMode) ||
Menu::getInstance()->isOptionChecked(MenuOption::VoxelColorMode))) {
_myAvatar.setMoveTarget(myPosition + (newTarget - myPosition) * PERCENTAGE_TO_MOVE_TOWARD);
}
} }
} else if (event->button() == Qt::RightButton && Menu::getInstance()->isVoxelModeActionChecked()) { } else if (event->button() == Qt::RightButton && Menu::getInstance()->isVoxelModeActionChecked()) {
@ -1503,7 +1517,7 @@ void Application::setListenModeSingleSource() {
glm::vec3 eyePositionIgnored; glm::vec3 eyePositionIgnored;
uint16_t nodeID; uint16_t nodeID;
if (isLookingAtOtherAvatar(mouseRayOrigin, mouseRayDirection, eyePositionIgnored, nodeID)) { if (findLookatTargetAvatar(mouseRayOrigin, mouseRayDirection, eyePositionIgnored, nodeID)) {
_audio.addListenSource(nodeID); _audio.addListenSource(nodeID);
} }
} }
@ -1518,7 +1532,6 @@ void Application::initDisplay() {
} }
void Application::init() { void Application::init() {
_voxels.init();
_sharedVoxelSystemViewFrustum.setPosition(glm::vec3(TREE_SCALE / 2.0f, _sharedVoxelSystemViewFrustum.setPosition(glm::vec3(TREE_SCALE / 2.0f,
TREE_SCALE / 2.0f, TREE_SCALE / 2.0f,
3.0f * TREE_SCALE / 2.0f)); 3.0f * TREE_SCALE / 2.0f));
@ -1539,6 +1552,7 @@ void Application::init() {
_glowEffect.init(); _glowEffect.init();
_ambientOcclusionEffect.init(); _ambientOcclusionEffect.init();
_voxelShader.init();
_handControl.setScreenDimensions(_glWidget->width(), _glWidget->height()); _handControl.setScreenDimensions(_glWidget->width(), _glWidget->height());
@ -1569,9 +1583,15 @@ void Application::init() {
if (Menu::getInstance()->getAudioJitterBufferSamples() != 0) { if (Menu::getInstance()->getAudioJitterBufferSamples() != 0) {
_audio.setJitterBufferSamples(Menu::getInstance()->getAudioJitterBufferSamples()); _audio.setJitterBufferSamples(Menu::getInstance()->getAudioJitterBufferSamples());
} }
qDebug("Loaded settings.\n"); qDebug("Loaded settings.\n");
// Set up VoxelSystem after loading preferences so we can get the desired max voxel count
_voxels.setMaxVoxels(Menu::getInstance()->getMaxVoxels());
_voxels.setUseVoxelShader(Menu::getInstance()->isOptionChecked(MenuOption::UseVoxelShader));
_voxels.setUseByteNormals(Menu::getInstance()->isOptionChecked(MenuOption::UseByteNormals));
_voxels.init();
Avatar::sendAvatarURLsMessage(_myAvatar.getVoxels()->getVoxelURL(), _myAvatar.getHead().getBlendFace().getModelURL()); Avatar::sendAvatarURLsMessage(_myAvatar.getVoxels()->getVoxelURL(), _myAvatar.getHead().getBlendFace().getModelURL());
_palette.init(_glWidget->width(), _glWidget->height()); _palette.init(_glWidget->width(), _glWidget->height());
@ -1596,12 +1616,16 @@ const float MAX_AVATAR_EDIT_VELOCITY = 1.0f;
const float MAX_VOXEL_EDIT_DISTANCE = 20.0f; const float MAX_VOXEL_EDIT_DISTANCE = 20.0f;
const float HEAD_SPHERE_RADIUS = 0.07; const float HEAD_SPHERE_RADIUS = 0.07;
static uint16_t DEFAULT_NODE_ID_REF = 1; static uint16_t DEFAULT_NODE_ID_REF = 1;
void Application::updateLookatTargetAvatar(const glm::vec3& mouseRayOrigin, const glm::vec3& mouseRayDirection,
Avatar* Application::isLookingAtOtherAvatar(glm::vec3& mouseRayOrigin, glm::vec3& mouseRayDirection, glm::vec3& eyePosition) {
glm::vec3& eyePosition, uint16_t& nodeID = DEFAULT_NODE_ID_REF) {
_lookatTargetAvatar = findLookatTargetAvatar(mouseRayOrigin, mouseRayDirection, eyePosition, DEFAULT_NODE_ID_REF);
}
Avatar* Application::findLookatTargetAvatar(const glm::vec3& mouseRayOrigin, const glm::vec3& mouseRayDirection,
glm::vec3& eyePosition, uint16_t& nodeID = DEFAULT_NODE_ID_REF) {
NodeList* nodeList = NodeList::getInstance(); NodeList* nodeList = NodeList::getInstance();
for (NodeList::iterator node = nodeList->begin(); node != nodeList->end(); node++) { for (NodeList::iterator node = nodeList->begin(); node != nodeList->end(); node++) {
@ -1739,6 +1763,11 @@ void Application::update(float deltaTime) {
// Update faceshift // Update faceshift
_faceshift.update(); _faceshift.update();
// Copy angular velocity if measured by faceshift, to the head
if (_faceshift.isActive()) {
_myAvatar.getHead().setAngularVelocity(_faceshift.getHeadAngularVelocity());
}
// if we have faceshift, use that to compute the lookat direction // if we have faceshift, use that to compute the lookat direction
glm::vec3 lookAtRayOrigin = mouseRayOrigin, lookAtRayDirection = mouseRayDirection; glm::vec3 lookAtRayOrigin = mouseRayOrigin, lookAtRayDirection = mouseRayDirection;
@ -1748,8 +1777,8 @@ void Application::update(float deltaTime) {
_faceshift.getEstimatedEyePitch(), _faceshift.getEstimatedEyeYaw(), 0.0f))) * glm::vec3(0.0f, 0.0f, -1.0f); _faceshift.getEstimatedEyePitch(), _faceshift.getEstimatedEyeYaw(), 0.0f))) * glm::vec3(0.0f, 0.0f, -1.0f);
} }
_isLookingAtOtherAvatar = isLookingAtOtherAvatar(lookAtRayOrigin, lookAtRayDirection, lookAtSpot); updateLookatTargetAvatar(lookAtRayOrigin, lookAtRayDirection, lookAtSpot);
if (_isLookingAtOtherAvatar) { if (_lookatTargetAvatar) {
// If the mouse is over another avatar's head... // If the mouse is over another avatar's head...
_myAvatar.getHead().setLookAtPosition(lookAtSpot); _myAvatar.getHead().setLookAtPosition(lookAtSpot);
} else if (_isHoverVoxel && !_faceshift.isActive()) { } else if (_isHoverVoxel && !_faceshift.isActive()) {
@ -1921,7 +1950,7 @@ void Application::update(float deltaTime) {
if (!avatar->isInitialized()) { if (!avatar->isInitialized()) {
avatar->init(); avatar->init();
} }
avatar->simulate(deltaTime, NULL, 0.f); avatar->simulate(deltaTime, NULL);
avatar->setMouseRay(mouseRayOrigin, mouseRayDirection); avatar->setMouseRay(mouseRayOrigin, mouseRayDirection);
} }
node->unlock(); node->unlock();
@ -1936,9 +1965,9 @@ void Application::update(float deltaTime) {
} }
if (Menu::getInstance()->isOptionChecked(MenuOption::TransmitterDrive) && _myTransmitter.isConnected()) { if (Menu::getInstance()->isOptionChecked(MenuOption::TransmitterDrive) && _myTransmitter.isConnected()) {
_myAvatar.simulate(deltaTime, &_myTransmitter, Menu::getInstance()->getGyroCameraSensitivity()); _myAvatar.simulate(deltaTime, &_myTransmitter);
} else { } else {
_myAvatar.simulate(deltaTime, NULL, Menu::getInstance()->getGyroCameraSensitivity()); _myAvatar.simulate(deltaTime, NULL);
} }
// no transmitter drive implies transmitter pick // no transmitter drive implies transmitter pick
@ -2063,15 +2092,15 @@ void Application::updateAvatar(float deltaTime) {
const float MIDPOINT_OF_SCREEN = 0.5; const float MIDPOINT_OF_SCREEN = 0.5;
// Only use gyro to set lookAt if mouse hasn't selected an avatar // Only use gyro to set lookAt if mouse hasn't selected an avatar
if (!_isLookingAtOtherAvatar) { if (!_lookatTargetAvatar) {
// Set lookAtPosition if an avatar is at the center of the screen // Set lookAtPosition if an avatar is at the center of the screen
glm::vec3 screenCenterRayOrigin, screenCenterRayDirection; glm::vec3 screenCenterRayOrigin, screenCenterRayDirection;
_viewFrustum.computePickRay(MIDPOINT_OF_SCREEN, MIDPOINT_OF_SCREEN, screenCenterRayOrigin, screenCenterRayDirection); _viewFrustum.computePickRay(MIDPOINT_OF_SCREEN, MIDPOINT_OF_SCREEN, screenCenterRayOrigin, screenCenterRayDirection);
glm::vec3 eyePosition; glm::vec3 eyePosition;
_isLookingAtOtherAvatar = isLookingAtOtherAvatar(screenCenterRayOrigin, screenCenterRayDirection, eyePosition); updateLookatTargetAvatar(screenCenterRayOrigin, screenCenterRayDirection, eyePosition);
if (_isLookingAtOtherAvatar) { if (_lookatTargetAvatar) {
glm::vec3 myLookAtFromMouse(eyePosition); glm::vec3 myLookAtFromMouse(eyePosition);
_myAvatar.getHead().setLookAtPosition(myLookAtFromMouse); _myAvatar.getHead().setLookAtPosition(myLookAtFromMouse);
} }
@ -2540,7 +2569,7 @@ void Application::displaySide(Camera& whichCamera) {
Menu::getInstance()->isOptionChecked(MenuOption::AvatarAsBalls)); Menu::getInstance()->isOptionChecked(MenuOption::AvatarAsBalls));
_myAvatar.setDisplayingLookatVectors(Menu::getInstance()->isOptionChecked(MenuOption::LookAtVectors)); _myAvatar.setDisplayingLookatVectors(Menu::getInstance()->isOptionChecked(MenuOption::LookAtVectors));
if (Menu::getInstance()->isOptionChecked(MenuOption::LookAtIndicator) && _isLookingAtOtherAvatar) { if (Menu::getInstance()->isOptionChecked(MenuOption::LookAtIndicator) && _lookatTargetAvatar) {
renderLookatIndicator(_lookatOtherPosition, whichCamera); renderLookatIndicator(_lookatOtherPosition, whichCamera);
} }
} }
@ -2561,7 +2590,7 @@ void Application::displaySide(Camera& whichCamera) {
if (Menu::getInstance()->isOptionChecked(MenuOption::DisplayFrustum)) { if (Menu::getInstance()->isOptionChecked(MenuOption::DisplayFrustum)) {
renderViewFrustum(_viewFrustum); renderViewFrustum(_viewFrustum);
} }
// render voxel fades if they exist // render voxel fades if they exist
if (_voxelFades.size() > 0) { if (_voxelFades.size() > 0) {
for(std::vector<VoxelFade>::iterator fade = _voxelFades.begin(); fade != _voxelFades.end();) { for(std::vector<VoxelFade>::iterator fade = _voxelFades.begin(); fade != _voxelFades.end();) {
@ -2806,28 +2835,39 @@ void Application::displayStats() {
std::stringstream voxelStats; std::stringstream voxelStats;
voxelStats.precision(4); voxelStats.precision(4);
voxelStats << "Voxels Rendered: " << _voxels.getVoxelsRendered() / 1000.f << "K Updated: " << _voxels.getVoxelsUpdated()/1000.f << "K"; voxelStats << "Voxels Rendered: " << _voxels.getVoxelsRendered() / 1000.f << "K " <<
"Updated: " << _voxels.getVoxelsUpdated()/1000.f << "K " <<
"Max: " << _voxels.getMaxVoxels()/1000.f << "K ";
drawtext(10, statsVerticalOffset + 230, 0.10f, 0, 1.0, 0, (char *)voxelStats.str().c_str()); drawtext(10, statsVerticalOffset + 230, 0.10f, 0, 1.0, 0, (char *)voxelStats.str().c_str());
voxelStats.str("");
voxelStats << "Voxels Memory RAM: " << _voxels.getVoxelMemoryUsageRAM() / 1000000.f << "MB " <<
"VBO: " << _voxels.getVoxelMemoryUsageVBO() / 1000000.f << "MB ";
if (_voxels.hasVoxelMemoryUsageGPU()) {
voxelStats << "GPU: " << _voxels.getVoxelMemoryUsageGPU() / 1000000.f << "MB ";
}
drawtext(10, statsVerticalOffset + 250, 0.10f, 0, 1.0, 0, (char *)voxelStats.str().c_str());
voxelStats.str(""); voxelStats.str("");
char* voxelDetails = _voxelSceneStats.getItemValue(VoxelSceneStats::ITEM_VOXELS); char* voxelDetails = _voxelSceneStats.getItemValue(VoxelSceneStats::ITEM_VOXELS);
voxelStats << "Voxels Sent from Server: " << voxelDetails; voxelStats << "Voxels Sent from Server: " << voxelDetails;
drawtext(10, statsVerticalOffset + 250, 0.10f, 0, 1.0, 0, (char *)voxelStats.str().c_str()); drawtext(10, statsVerticalOffset + 270, 0.10f, 0, 1.0, 0, (char *)voxelStats.str().c_str());
voxelStats.str(""); voxelStats.str("");
voxelDetails = _voxelSceneStats.getItemValue(VoxelSceneStats::ITEM_ELAPSED); voxelDetails = _voxelSceneStats.getItemValue(VoxelSceneStats::ITEM_ELAPSED);
voxelStats << "Scene Send Time from Server: " << voxelDetails; voxelStats << "Scene Send Time from Server: " << voxelDetails;
drawtext(10, statsVerticalOffset + 270, 0.10f, 0, 1.0, 0, (char *)voxelStats.str().c_str()); drawtext(10, statsVerticalOffset + 290, 0.10f, 0, 1.0, 0, (char *)voxelStats.str().c_str());
voxelStats.str(""); voxelStats.str("");
voxelDetails = _voxelSceneStats.getItemValue(VoxelSceneStats::ITEM_ENCODE); voxelDetails = _voxelSceneStats.getItemValue(VoxelSceneStats::ITEM_ENCODE);
voxelStats << "Encode Time on Server: " << voxelDetails; voxelStats << "Encode Time on Server: " << voxelDetails;
drawtext(10, statsVerticalOffset + 290, 0.10f, 0, 1.0, 0, (char *)voxelStats.str().c_str()); drawtext(10, statsVerticalOffset + 310, 0.10f, 0, 1.0, 0, (char *)voxelStats.str().c_str());
voxelStats.str(""); voxelStats.str("");
voxelDetails = _voxelSceneStats.getItemValue(VoxelSceneStats::ITEM_MODE); voxelDetails = _voxelSceneStats.getItemValue(VoxelSceneStats::ITEM_MODE);
voxelStats << "Sending Mode: " << voxelDetails; voxelStats << "Sending Mode: " << voxelDetails;
drawtext(10, statsVerticalOffset + 310, 0.10f, 0, 1.0, 0, (char *)voxelStats.str().c_str()); drawtext(10, statsVerticalOffset + 330, 0.10f, 0, 1.0, 0, (char *)voxelStats.str().c_str());
Node *avatarMixer = NodeList::getInstance()->soloNodeOfType(NODE_TYPE_AVATAR_MIXER); Node *avatarMixer = NodeList::getInstance()->soloNodeOfType(NODE_TYPE_AVATAR_MIXER);
char avatarMixerStats[200]; char avatarMixerStats[200];
@ -2840,7 +2880,7 @@ void Application::displayStats() {
sprintf(avatarMixerStats, "No Avatar Mixer"); sprintf(avatarMixerStats, "No Avatar Mixer");
} }
drawtext(10, statsVerticalOffset + 330, 0.10f, 0, 1.0, 0, avatarMixerStats); drawtext(10, statsVerticalOffset + 350, 0.10f, 0, 1.0, 0, avatarMixerStats);
drawtext(10, statsVerticalOffset + 450, 0.10f, 0, 1.0, 0, (char *)LeapManager::statusString().c_str()); drawtext(10, statsVerticalOffset + 450, 0.10f, 0, 1.0, 0, (char *)LeapManager::statusString().c_str());
if (_perfStatsOn) { if (_perfStatsOn) {
@ -3342,10 +3382,7 @@ void Application::toggleFollowMode() {
mouseRayOrigin, mouseRayDirection); mouseRayOrigin, mouseRayDirection);
glm::vec3 eyePositionIgnored; glm::vec3 eyePositionIgnored;
uint16_t nodeIDIgnored; uint16_t nodeIDIgnored;
Avatar* leadingAvatar = isLookingAtOtherAvatar(mouseRayOrigin, Avatar* leadingAvatar = findLookatTargetAvatar(mouseRayOrigin, mouseRayDirection, eyePositionIgnored, nodeIDIgnored);
mouseRayDirection,
eyePositionIgnored,
nodeIDIgnored);
_myAvatar.follow(leadingAvatar); _myAvatar.follow(leadingAvatar);
} }
@ -3396,6 +3433,11 @@ void Application::attachNewHeadToNode(Node* newNode) {
} }
} }
void Application::domainChanged(QString domain) {
qDebug("Application title set to: %s.\n", domain.toStdString().c_str());
_window->setWindowTitle(domain);
}
void Application::nodeAdded(Node* node) { void Application::nodeAdded(Node* node) {
} }
@ -3418,6 +3460,8 @@ void Application::nodeKilled(Node* node) {
fade.voxelDetails.s = fade.voxelDetails.s * slightly_smaller; fade.voxelDetails.s = fade.voxelDetails.s * slightly_smaller;
_voxelFades.push_back(fade); _voxelFades.push_back(fade);
} }
} else if (node->getLinkedData() == _lookatTargetAvatar) {
_lookatTargetAvatar = NULL;
} }
} }

View file

@ -51,6 +51,7 @@
#include "renderer/AmbientOcclusionEffect.h" #include "renderer/AmbientOcclusionEffect.h"
#include "renderer/GeometryCache.h" #include "renderer/GeometryCache.h"
#include "renderer/GlowEffect.h" #include "renderer/GlowEffect.h"
#include "renderer/VoxelShader.h"
#include "renderer/TextureCache.h" #include "renderer/TextureCache.h"
#include "ui/BandwidthDialog.h" #include "ui/BandwidthDialog.h"
#include "ui/ChatEntry.h" #include "ui/ChatEntry.h"
@ -76,7 +77,7 @@ static const float NODE_KILLED_RED = 1.0f;
static const float NODE_KILLED_GREEN = 0.0f; static const float NODE_KILLED_GREEN = 0.0f;
static const float NODE_KILLED_BLUE = 0.0f; static const float NODE_KILLED_BLUE = 0.0f;
class Application : public QApplication, public NodeListHook, public PacketSenderNotify { class Application : public QApplication, public NodeListHook, public PacketSenderNotify, public DomainChangeListener {
Q_OBJECT Q_OBJECT
friend class VoxelPacketProcessor; friend class VoxelPacketProcessor;
@ -130,6 +131,8 @@ public:
TextureCache* getTextureCache() { return &_textureCache; } TextureCache* getTextureCache() { return &_textureCache; }
GlowEffect* getGlowEffect() { return &_glowEffect; } GlowEffect* getGlowEffect() { return &_glowEffect; }
Avatar* getLookatTargetAvatar() const { return _lookatTargetAvatar; }
static void controlledBroadcastToNodes(unsigned char* broadcastData, size_t dataBytes, static void controlledBroadcastToNodes(unsigned char* broadcastData, size_t dataBytes,
const char* nodeTypes, int numNodeTypes); const char* nodeTypes, int numNodeTypes);
@ -142,6 +145,10 @@ public:
virtual void nodeAdded(Node* node); virtual void nodeAdded(Node* node);
virtual void nodeKilled(Node* node); virtual void nodeKilled(Node* node);
virtual void packetSentNotification(ssize_t length); virtual void packetSentNotification(ssize_t length);
virtual void domainChanged(QString domain);
VoxelShader& getVoxelShader() { return _voxelShader; }
public slots: public slots:
void sendAvatarFaceVideoMessage(int frameCount, const QByteArray& data); void sendAvatarFaceVideoMessage(int frameCount, const QByteArray& data);
@ -195,8 +202,11 @@ private:
void init(); void init();
void update(float deltaTime); void update(float deltaTime);
Avatar* isLookingAtOtherAvatar(glm::vec3& mouseRayOrigin, glm::vec3& mouseRayDirection,
glm::vec3& eyePosition, uint16_t& nodeID); void updateLookatTargetAvatar(const glm::vec3& mouseRayOrigin, const glm::vec3& mouseRayDirection,
glm::vec3& eyePosition);
Avatar* findLookatTargetAvatar(const glm::vec3& mouseRayOrigin, const glm::vec3& mouseRayDirection,
glm::vec3& eyePosition, uint16_t& nodeID);
bool isLookingAtMyAvatar(Avatar* avatar); bool isLookingAtMyAvatar(Avatar* avatar);
void renderLookatIndicator(glm::vec3 pointOfInterest, Camera& whichCamera); void renderLookatIndicator(glm::vec3 pointOfInterest, Camera& whichCamera);
@ -315,7 +325,7 @@ private:
bool _lookingAwayFromOrigin; bool _lookingAwayFromOrigin;
glm::vec3 _nudgeGuidePosition; glm::vec3 _nudgeGuidePosition;
bool _isLookingAtOtherAvatar; Avatar* _lookatTargetAvatar;
glm::vec3 _lookatOtherPosition; glm::vec3 _lookatOtherPosition;
float _lookatIndicatorScale; float _lookatIndicatorScale;
@ -341,6 +351,7 @@ private:
GlowEffect _glowEffect; GlowEffect _glowEffect;
AmbientOcclusionEffect _ambientOcclusionEffect; AmbientOcclusionEffect _ambientOcclusionEffect;
VoxelShader _voxelShader;
#ifndef _WIN32 #ifndef _WIN32
Audio _audio; Audio _audio;

View file

@ -17,6 +17,7 @@
#include <QFormLayout> #include <QFormLayout>
#include <QLineEdit> #include <QLineEdit>
#include <QMainWindow> #include <QMainWindow>
#include <QSlider>
#include <QStandardPaths> #include <QStandardPaths>
#include "Application.h" #include "Application.h"
@ -48,7 +49,8 @@ Menu::Menu() :
_frustumDrawMode(FRUSTUM_DRAW_MODE_ALL), _frustumDrawMode(FRUSTUM_DRAW_MODE_ALL),
_viewFrustumOffset(DEFAULT_FRUSTUM_OFFSET), _viewFrustumOffset(DEFAULT_FRUSTUM_OFFSET),
_voxelModeActionsGroup(NULL), _voxelModeActionsGroup(NULL),
_voxelStatsDialog(NULL) _voxelStatsDialog(NULL),
_maxVoxels(DEFAULT_MAX_VOXELS_PER_SYSTEM)
{ {
Application *appInstance = Application::getInstance(); Application *appInstance = Application::getInstance();
@ -188,50 +190,36 @@ Menu::Menu() :
addCheckableActionToQMenuAndActionHash(viewMenu, addCheckableActionToQMenuAndActionHash(viewMenu,
MenuOption::Fullscreen, MenuOption::Fullscreen,
Qt::Key_F, Qt::CTRL | Qt::META | Qt::Key_F,
false, false,
appInstance, appInstance,
SLOT(setFullscreen(bool))); SLOT(setFullscreen(bool)));
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::FirstPerson, Qt::Key_P, true); addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::FirstPerson, Qt::Key_P, true);
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::Mirror, Qt::Key_H);
addActionToQMenuAndActionHash(viewMenu, QMenu* avatarSizeMenu = viewMenu->addMenu("Avatar Size");
addActionToQMenuAndActionHash(avatarSizeMenu,
MenuOption::IncreaseAvatarSize, MenuOption::IncreaseAvatarSize,
Qt::Key_Plus, Qt::Key_Plus,
appInstance->getAvatar(), appInstance->getAvatar(),
SLOT(increaseSize())); SLOT(increaseSize()));
addActionToQMenuAndActionHash(viewMenu, addActionToQMenuAndActionHash(avatarSizeMenu,
MenuOption::DecreaseAvatarSize, MenuOption::DecreaseAvatarSize,
Qt::Key_Minus, Qt::Key_Minus,
appInstance->getAvatar(), appInstance->getAvatar(),
SLOT(decreaseSize())); SLOT(decreaseSize()));
addActionToQMenuAndActionHash(viewMenu, addActionToQMenuAndActionHash(avatarSizeMenu,
MenuOption::ResetAvatarSize, MenuOption::ResetAvatarSize,
0, 0,
appInstance->getAvatar(), appInstance->getAvatar(),
SLOT(resetSize())); SLOT(resetSize()));
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::Mirror, Qt::Key_H);
addCheckableActionToQMenuAndActionHash(viewMenu,
MenuOption::SkeletonTracking,
0,
false,
appInstance->getWebcam(),
SLOT(setSkeletonTrackingOn(bool)));
addCheckableActionToQMenuAndActionHash(viewMenu,
MenuOption::LEDTracking,
0,
false,
appInstance->getWebcam()->getGrabber(),
SLOT(setLEDTrackingOn(bool)));
addCheckableActionToQMenuAndActionHash(viewMenu, addCheckableActionToQMenuAndActionHash(viewMenu,
MenuOption::OffAxisProjection, MenuOption::OffAxisProjection,
0, 0,
false); false);
addDisabledActionAndSeparator(viewMenu, "Stats"); addDisabledActionAndSeparator(viewMenu, "Stats");
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::Stats, Qt::Key_Slash); addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::Stats, Qt::Key_Slash);
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::Log, Qt::CTRL | Qt::Key_L); addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::Log, Qt::CTRL | Qt::Key_L);
@ -241,50 +229,126 @@ Menu::Menu() :
addActionToQMenuAndActionHash(viewMenu, MenuOption::VoxelStats, 0, this, SLOT(voxelStatsDetails())); addActionToQMenuAndActionHash(viewMenu, MenuOption::VoxelStats, 0, this, SLOT(voxelStatsDetails()));
QMenu* developerMenu = addMenu("Developer"); QMenu* developerMenu = addMenu("Developer");
addDisabledActionAndSeparator(developerMenu, "Rendering");
QMenu* renderOptionsMenu = developerMenu->addMenu("Rendering Options");
addCheckableActionToQMenuAndActionHash(developerMenu,
addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::Stars, Qt::Key_Asterisk, true);
addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::Atmosphere, Qt::SHIFT | Qt::Key_A, true);
addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::GroundPlane, 0, true);
addActionToQMenuAndActionHash(renderOptionsMenu,
MenuOption::GlowMode,
0,
appInstance->getGlowEffect(),
SLOT(cycleRenderMode()));
QMenu* voxelOptionsMenu = developerMenu->addMenu("Voxel Options");
addCheckableActionToQMenuAndActionHash(voxelOptionsMenu,
MenuOption::Voxels, MenuOption::Voxels,
Qt::SHIFT | Qt::Key_V, Qt::SHIFT | Qt::Key_V,
true, true,
appInstance, appInstance,
SLOT(setRenderVoxels(bool))); SLOT(setRenderVoxels(bool)));
addCheckableActionToQMenuAndActionHash(developerMenu, MenuOption::VoxelTextures); addCheckableActionToQMenuAndActionHash(voxelOptionsMenu, MenuOption::VoxelTextures);
addCheckableActionToQMenuAndActionHash(developerMenu, MenuOption::AmbientOcclusion); addCheckableActionToQMenuAndActionHash(voxelOptionsMenu, MenuOption::AmbientOcclusion);
addCheckableActionToQMenuAndActionHash(developerMenu, MenuOption::Stars, 0, true); addCheckableActionToQMenuAndActionHash(voxelOptionsMenu, MenuOption::UseVoxelShader, 0,
addCheckableActionToQMenuAndActionHash(developerMenu, MenuOption::Atmosphere, Qt::SHIFT | Qt::Key_A, true); false, this, SLOT(switchVoxelShader()));
addCheckableActionToQMenuAndActionHash(developerMenu, MenuOption::GroundPlane, 0, true);
addCheckableActionToQMenuAndActionHash(developerMenu, MenuOption::Avatars, 0, true); addCheckableActionToQMenuAndActionHash(voxelOptionsMenu, MenuOption::UseByteNormals, 0,
addCheckableActionToQMenuAndActionHash(developerMenu, MenuOption::AvatarAsBalls); false, Application::getInstance()->getVoxels(), SLOT(setUseByteNormals(bool)));
addCheckableActionToQMenuAndActionHash(voxelOptionsMenu, MenuOption::UseGlobalNormals, 0,
false, Application::getInstance()->getVoxels(), SLOT(setUseGlobalNormals(bool)));
addActionToQMenuAndActionHash(developerMenu, QMenu* avatarOptionsMenu = developerMenu->addMenu("Avatar Options");
addCheckableActionToQMenuAndActionHash(avatarOptionsMenu, MenuOption::Avatars, 0, true);
addCheckableActionToQMenuAndActionHash(avatarOptionsMenu, MenuOption::AvatarAsBalls);
addActionToQMenuAndActionHash(avatarOptionsMenu,
MenuOption::VoxelMode, MenuOption::VoxelMode,
0, 0,
appInstance->getAvatar()->getVoxels(), appInstance->getAvatar()->getVoxels(),
SLOT(cycleMode())); SLOT(cycleMode()));
addActionToQMenuAndActionHash(developerMenu, addActionToQMenuAndActionHash(avatarOptionsMenu,
MenuOption::FaceMode, MenuOption::FaceMode,
0, 0,
&appInstance->getAvatar()->getHead().getFace(), &appInstance->getAvatar()->getHead().getFace(),
SLOT(cycleRenderMode())); SLOT(cycleRenderMode()));
addActionToQMenuAndActionHash(developerMenu, addCheckableActionToQMenuAndActionHash(avatarOptionsMenu, MenuOption::UsePerlinFace, 0, false);
MenuOption::GlowMode, addCheckableActionToQMenuAndActionHash(avatarOptionsMenu, MenuOption::LookAtVectors, 0, true);
addCheckableActionToQMenuAndActionHash(avatarOptionsMenu, MenuOption::LookAtIndicator, 0, true);
addCheckableActionToQMenuAndActionHash(avatarOptionsMenu,
MenuOption::FaceshiftTCP,
0,
false,
appInstance->getFaceshift(),
SLOT(setTCPEnabled(bool)));
QMenu* webcamOptionsMenu = developerMenu->addMenu("Webcam Options");
addCheckableActionToQMenuAndActionHash(webcamOptionsMenu,
MenuOption::Webcam,
0,
false,
appInstance->getWebcam(),
SLOT(setEnabled(bool)));
addActionToQMenuAndActionHash(webcamOptionsMenu,
MenuOption::WebcamMode,
0, 0,
appInstance->getGlowEffect(), appInstance->getWebcam()->getGrabber(),
SLOT(cycleRenderMode())); SLOT(cycleVideoSendMode()));
addCheckableActionToQMenuAndActionHash(webcamOptionsMenu,
MenuOption::WebcamTexture,
0,
false,
appInstance->getWebcam()->getGrabber(),
SLOT(setDepthOnly(bool)));
QMenu* raveGloveOptionsMenu = developerMenu->addMenu("Rave Glove Options");
addCheckableActionToQMenuAndActionHash(raveGloveOptionsMenu, MenuOption::SimulateLeapHand);
addCheckableActionToQMenuAndActionHash(raveGloveOptionsMenu, MenuOption::TestRaveGlove);
QMenu* gyroOptionsMenu = developerMenu->addMenu("Gyro Options");
addCheckableActionToQMenuAndActionHash(gyroOptionsMenu, MenuOption::GyroLook, 0, true);
addCheckableActionToQMenuAndActionHash(gyroOptionsMenu, MenuOption::HeadMouse);
QMenu* trackingOptionsMenu = developerMenu->addMenu("Tracking Options");
addCheckableActionToQMenuAndActionHash(trackingOptionsMenu,
MenuOption::SkeletonTracking,
0,
false,
appInstance->getWebcam(),
SLOT(setSkeletonTrackingOn(bool)));
addCheckableActionToQMenuAndActionHash(developerMenu, MenuOption::UsePerlinFace, 0, false); addCheckableActionToQMenuAndActionHash(trackingOptionsMenu,
addCheckableActionToQMenuAndActionHash(developerMenu, MenuOption::LookAtVectors, 0, true); MenuOption::LEDTracking,
addCheckableActionToQMenuAndActionHash(developerMenu, MenuOption::LookAtIndicator, 0, true); 0,
addCheckableActionToQMenuAndActionHash(developerMenu, MenuOption::FrameTimer); false,
appInstance->getWebcam()->getGrabber(),
SLOT(setLEDTrackingOn(bool)));
addDisabledActionAndSeparator(developerMenu, "Testing"); addDisabledActionAndSeparator(developerMenu, "Testing");
QMenu* timingMenu = developerMenu->addMenu("Timing and Statistics Tools");
addCheckableActionToQMenuAndActionHash(timingMenu, MenuOption::TestPing, 0, true);
addCheckableActionToQMenuAndActionHash(timingMenu, MenuOption::FrameTimer);
addActionToQMenuAndActionHash(timingMenu, MenuOption::RunTimingTests, 0, this, SLOT(runTests()));
addActionToQMenuAndActionHash(timingMenu,
MenuOption::TreeStats,
Qt::SHIFT | Qt::Key_S,
appInstance->getVoxels(),
SLOT(collectStatsForTreesAndVBOs()));
QMenu* frustumMenu = developerMenu->addMenu("View Frustum Debugging Tools"); QMenu* frustumMenu = developerMenu->addMenu("View Frustum Debugging Tools");
addCheckableActionToQMenuAndActionHash(frustumMenu, MenuOption::DisplayFrustum, Qt::SHIFT | Qt::Key_F); addCheckableActionToQMenuAndActionHash(frustumMenu, MenuOption::DisplayFrustum, Qt::SHIFT | Qt::Key_F);
addActionToQMenuAndActionHash(frustumMenu, addActionToQMenuAndActionHash(frustumMenu,
MenuOption::FrustumRenderMode, MenuOption::FrustumRenderMode,
Qt::SHIFT | Qt::Key_R, Qt::SHIFT | Qt::Key_R,
@ -292,12 +356,6 @@ Menu::Menu() :
SLOT(cycleFrustumRenderMode())); SLOT(cycleFrustumRenderMode()));
updateFrustumRenderModeAction(); updateFrustumRenderModeAction();
addActionToQMenuAndActionHash(developerMenu, MenuOption::RunTimingTests, 0, this, SLOT(runTests()));
addActionToQMenuAndActionHash(developerMenu,
MenuOption::TreeStats,
Qt::SHIFT | Qt::Key_S,
appInstance->getVoxels(),
SLOT(collectStatsForTreesAndVBOs()));
QMenu* renderDebugMenu = developerMenu->addMenu("Render Debugging Tools"); QMenu* renderDebugMenu = developerMenu->addMenu("Render Debugging Tools");
addCheckableActionToQMenuAndActionHash(renderDebugMenu, MenuOption::PipelineWarnings); addCheckableActionToQMenuAndActionHash(renderDebugMenu, MenuOption::PipelineWarnings);
@ -337,18 +395,6 @@ Menu::Menu() :
appInstance->getVoxels(), appInstance->getVoxels(),
SLOT(falseColorizeInView())); SLOT(falseColorizeInView()));
addActionToQMenuAndActionHash(renderDebugMenu,
MenuOption::FalseColorOccluded,
0,
appInstance->getVoxels(),
SLOT(falseColorizeOccluded()));
addActionToQMenuAndActionHash(renderDebugMenu,
MenuOption::FalseColorOccludedV2,
0,
appInstance->getVoxels(),
SLOT(falseColorizeOccludedV2()));
addActionToQMenuAndActionHash(renderDebugMenu, addActionToQMenuAndActionHash(renderDebugMenu,
MenuOption::FalseColorBySource, MenuOption::FalseColorBySource,
0, 0,
@ -361,32 +407,21 @@ Menu::Menu() :
appInstance->getVoxels(), appInstance->getVoxels(),
SLOT(trueColorize())); SLOT(trueColorize()));
addDisabledActionAndSeparator(renderDebugMenu, "Coverage Maps");
addCheckableActionToQMenuAndActionHash(developerMenu, addActionToQMenuAndActionHash(renderDebugMenu,
MenuOption::Webcam, MenuOption::FalseColorOccluded,
0,
false,
appInstance->getWebcam(),
SLOT(setEnabled(bool)));
addActionToQMenuAndActionHash(developerMenu,
MenuOption::WebcamMode,
0, 0,
appInstance->getWebcam()->getGrabber(), appInstance->getVoxels(),
SLOT(cycleVideoSendMode())); SLOT(falseColorizeOccluded()));
addCheckableActionToQMenuAndActionHash(developerMenu,
MenuOption::WebcamTexture,
0,
false,
appInstance->getWebcam()->getGrabber(),
SLOT(setDepthOnly(bool)));
addCheckableActionToQMenuAndActionHash(developerMenu, addActionToQMenuAndActionHash(renderDebugMenu,
MenuOption::FaceshiftTCP, MenuOption::FalseColorOccludedV2,
0, 0,
false, appInstance->getVoxels(),
appInstance->getFaceshift(), SLOT(falseColorizeOccludedV2()));
SLOT(setTCPEnabled(bool)));
addCheckableActionToQMenuAndActionHash(renderDebugMenu, MenuOption::CoverageMap, Qt::SHIFT | Qt::CTRL | Qt::Key_O);
addCheckableActionToQMenuAndActionHash(renderDebugMenu, MenuOption::CoverageMapV2, Qt::SHIFT | Qt::CTRL | Qt::Key_P);
QMenu* audioDebugMenu = developerMenu->addMenu("Audio Debugging Tools"); QMenu* audioDebugMenu = developerMenu->addMenu("Audio Debugging Tools");
addCheckableActionToQMenuAndActionHash(audioDebugMenu, MenuOption::EchoAudio); addCheckableActionToQMenuAndActionHash(audioDebugMenu, MenuOption::EchoAudio);
@ -406,47 +441,38 @@ Menu::Menu() :
appInstance, appInstance,
SLOT(setListenModeSingleSource())); SLOT(setListenModeSingleSource()));
addCheckableActionToQMenuAndActionHash(developerMenu, MenuOption::TestPing, 0, true); QMenu* voxelProtoOptionsMenu = developerMenu->addMenu("Voxel Server Protocol Options");
addCheckableActionToQMenuAndActionHash(developerMenu, addCheckableActionToQMenuAndActionHash(voxelProtoOptionsMenu,
MenuOption::SendVoxelColors, MenuOption::SendVoxelColors,
0, 0,
true, true,
appInstance->getAvatar(), appInstance->getAvatar(),
SLOT(setWantColor(bool))); SLOT(setWantColor(bool)));
addCheckableActionToQMenuAndActionHash(developerMenu, addCheckableActionToQMenuAndActionHash(voxelProtoOptionsMenu,
MenuOption::LowRes, MenuOption::LowRes,
0, 0,
true, true,
appInstance->getAvatar(), appInstance->getAvatar(),
SLOT(setWantLowResMoving(bool))); SLOT(setWantLowResMoving(bool)));
addCheckableActionToQMenuAndActionHash(developerMenu, addCheckableActionToQMenuAndActionHash(voxelProtoOptionsMenu,
MenuOption::DeltaSending, MenuOption::DeltaSending,
0, 0,
true, true,
appInstance->getAvatar(), appInstance->getAvatar(),
SLOT(setWantDelta(bool))); SLOT(setWantDelta(bool)));
addCheckableActionToQMenuAndActionHash(developerMenu, addCheckableActionToQMenuAndActionHash(voxelProtoOptionsMenu,
MenuOption::OcclusionCulling, MenuOption::OcclusionCulling,
Qt::SHIFT | Qt::Key_C, Qt::SHIFT | Qt::Key_C,
true, true,
appInstance->getAvatar(), appInstance->getAvatar(),
SLOT(setWantOcclusionCulling(bool))); SLOT(setWantOcclusionCulling(bool)));
addCheckableActionToQMenuAndActionHash(developerMenu, MenuOption::CoverageMap, Qt::SHIFT | Qt::CTRL | Qt::Key_O);
addCheckableActionToQMenuAndActionHash(developerMenu, MenuOption::CoverageMapV2, Qt::SHIFT | Qt::CTRL | Qt::Key_P);
addCheckableActionToQMenuAndActionHash(developerMenu, MenuOption::SimulateLeapHand);
addCheckableActionToQMenuAndActionHash(developerMenu, MenuOption::TestRaveGlove);
addCheckableActionToQMenuAndActionHash(developerMenu, MenuOption::GyroLook, 0, true);
addCheckableActionToQMenuAndActionHash(developerMenu, MenuOption::HeadMouse);
addDisabledActionAndSeparator(developerMenu, "Voxels");
addCheckableActionToQMenuAndActionHash(developerMenu, MenuOption::DestructiveAddVoxel);
addCheckableActionToQMenuAndActionHash(voxelProtoOptionsMenu, MenuOption::DestructiveAddVoxel);
#ifndef Q_OS_MAC #ifndef Q_OS_MAC
QMenu* helpMenu = addMenu("Help"); QMenu* helpMenu = addMenu("Help");
QAction* helpAction = helpMenu->addAction(MenuOption::AboutApp); QAction* helpAction = helpMenu->addAction(MenuOption::AboutApp);
@ -465,9 +491,9 @@ void Menu::loadSettings(QSettings* settings) {
settings = Application::getInstance()->getSettings(); settings = Application::getInstance()->getSettings();
} }
_gyroCameraSensitivity = loadSetting(settings, "gyroCameraSensitivity", 0.5f);
_audioJitterBufferSamples = loadSetting(settings, "audioJitterBufferSamples", 0); _audioJitterBufferSamples = loadSetting(settings, "audioJitterBufferSamples", 0);
_fieldOfView = loadSetting(settings, "fieldOfView", DEFAULT_FIELD_OF_VIEW_DEGREES); _fieldOfView = loadSetting(settings, "fieldOfView", DEFAULT_FIELD_OF_VIEW_DEGREES);
_maxVoxels = loadSetting(settings, "maxVoxels", DEFAULT_MAX_VOXELS_PER_SYSTEM);
settings->beginGroup("View Frustum Offset Camera"); settings->beginGroup("View Frustum Offset Camera");
// in case settings is corrupt or missing loadSetting() will check for NaN // in case settings is corrupt or missing loadSetting() will check for NaN
@ -488,9 +514,9 @@ void Menu::saveSettings(QSettings* settings) {
settings = Application::getInstance()->getSettings(); settings = Application::getInstance()->getSettings();
} }
settings->setValue("gyroCameraSensitivity", _gyroCameraSensitivity);
settings->setValue("audioJitterBufferSamples", _audioJitterBufferSamples); settings->setValue("audioJitterBufferSamples", _audioJitterBufferSamples);
settings->setValue("fieldOfView", _fieldOfView); settings->setValue("fieldOfView", _fieldOfView);
settings->setValue("maxVoxels", _maxVoxels);
settings->beginGroup("View Frustum Offset Camera"); settings->beginGroup("View Frustum Offset Camera");
settings->setValue("viewFrustumOffsetYaw", _viewFrustumOffset.yaw); settings->setValue("viewFrustumOffsetYaw", _viewFrustumOffset.yaw);
settings->setValue("viewFrustumOffsetPitch", _viewFrustumOffset.pitch); settings->setValue("viewFrustumOffsetPitch", _viewFrustumOffset.pitch);
@ -738,9 +764,6 @@ void Menu::editPreferences() {
QFormLayout* form = new QFormLayout(); QFormLayout* form = new QFormLayout();
layout->addLayout(form, 1); layout->addLayout(form, 1);
QLineEdit* domainServerLineEdit = lineEditForDomainHostname();
form->addRow("Domain server:", domainServerLineEdit);
QLineEdit* avatarURL = new QLineEdit(applicationInstance->getAvatar()->getVoxels()->getVoxelURL().toString()); QLineEdit* avatarURL = new QLineEdit(applicationInstance->getAvatar()->getVoxels()->getVoxelURL().toString());
avatarURL->setMinimumWidth(QLINE_MINIMUM_WIDTH); avatarURL->setMinimumWidth(QLINE_MINIMUM_WIDTH);
form->addRow("Avatar URL:", avatarURL); form->addRow("Avatar URL:", avatarURL);
@ -749,16 +772,16 @@ void Menu::editPreferences() {
faceURL->setMinimumWidth(QLINE_MINIMUM_WIDTH); faceURL->setMinimumWidth(QLINE_MINIMUM_WIDTH);
form->addRow("Face URL:", faceURL); form->addRow("Face URL:", faceURL);
QSlider* pupilDilation = new QSlider(Qt::Horizontal);
pupilDilation->setValue(applicationInstance->getAvatar()->getHead().getPupilDilation() * pupilDilation->maximum());
form->addRow("Pupil Dilation:", pupilDilation);
QSpinBox* fieldOfView = new QSpinBox(); QSpinBox* fieldOfView = new QSpinBox();
fieldOfView->setMaximum(180); fieldOfView->setMaximum(180);
fieldOfView->setMinimum(1); fieldOfView->setMinimum(1);
fieldOfView->setValue(_fieldOfView); fieldOfView->setValue(_fieldOfView);
form->addRow("Vertical Field of View (Degrees):", fieldOfView); form->addRow("Vertical Field of View (Degrees):", fieldOfView);
QDoubleSpinBox* gyroCameraSensitivity = new QDoubleSpinBox();
gyroCameraSensitivity->setValue(_gyroCameraSensitivity);
form->addRow("Gyro Camera Sensitivity (0 - 1):", gyroCameraSensitivity);
QDoubleSpinBox* leanScale = new QDoubleSpinBox(); QDoubleSpinBox* leanScale = new QDoubleSpinBox();
leanScale->setValue(applicationInstance->getAvatar()->getLeanScale()); leanScale->setValue(applicationInstance->getAvatar()->getLeanScale());
form->addRow("Lean Scale:", leanScale); form->addRow("Lean Scale:", leanScale);
@ -768,6 +791,16 @@ void Menu::editPreferences() {
audioJitterBufferSamples->setMinimum(-10000); audioJitterBufferSamples->setMinimum(-10000);
audioJitterBufferSamples->setValue(_audioJitterBufferSamples); audioJitterBufferSamples->setValue(_audioJitterBufferSamples);
form->addRow("Audio Jitter Buffer Samples (0 for automatic):", audioJitterBufferSamples); form->addRow("Audio Jitter Buffer Samples (0 for automatic):", audioJitterBufferSamples);
QSpinBox* maxVoxels = new QSpinBox();
const int MAX_MAX_VOXELS = 5000000;
const int MIN_MAX_VOXELS = 0;
const int STEP_MAX_VOXELS = 50000;
maxVoxels->setMaximum(MAX_MAX_VOXELS);
maxVoxels->setMinimum(MIN_MAX_VOXELS);
maxVoxels->setSingleStep(STEP_MAX_VOXELS);
maxVoxels->setValue(_maxVoxels);
form->addRow("Maximum Voxels:", maxVoxels);
QDialogButtonBox* buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); QDialogButtonBox* buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
dialog.connect(buttons, SIGNAL(accepted()), SLOT(accept())); dialog.connect(buttons, SIGNAL(accepted()), SLOT(accept()));
@ -780,8 +813,6 @@ void Menu::editPreferences() {
return; return;
} }
updateDSHostname(domainServerLineEdit->text());
QUrl avatarVoxelURL(avatarURL->text()); QUrl avatarVoxelURL(avatarURL->text());
applicationInstance->getAvatar()->getVoxels()->setVoxelURL(avatarVoxelURL); applicationInstance->getAvatar()->getVoxels()->setVoxelURL(avatarVoxelURL);
@ -790,7 +821,10 @@ void Menu::editPreferences() {
Avatar::sendAvatarURLsMessage(avatarVoxelURL, faceModelURL); Avatar::sendAvatarURLsMessage(avatarVoxelURL, faceModelURL);
_gyroCameraSensitivity = gyroCameraSensitivity->value(); applicationInstance->getAvatar()->getHead().setPupilDilation(pupilDilation->value() / (float)pupilDilation->maximum());
_maxVoxels = maxVoxels->value();
applicationInstance->getVoxels()->setMaxVoxels(_maxVoxels);
applicationInstance->getAvatar()->setLeanScale(leanScale->value()); applicationInstance->getAvatar()->setLeanScale(leanScale->value());
@ -990,3 +1024,8 @@ void Menu::updateFrustumRenderModeAction() {
break; break;
} }
} }
void Menu::switchVoxelShader() {
Application::getInstance()->getVoxels()->setUseVoxelShader(isOptionChecked(MenuOption::UseVoxelShader));
}

View file

@ -49,11 +49,12 @@ public:
float getAudioJitterBufferSamples() const { return _audioJitterBufferSamples; } float getAudioJitterBufferSamples() const { return _audioJitterBufferSamples; }
float getFieldOfView() const { return _fieldOfView; } float getFieldOfView() const { return _fieldOfView; }
float getGyroCameraSensitivity() const { return _gyroCameraSensitivity; }
BandwidthDialog* getBandwidthDialog() const { return _bandwidthDialog; } BandwidthDialog* getBandwidthDialog() const { return _bandwidthDialog; }
FrustumDrawMode getFrustumDrawMode() const { return _frustumDrawMode; } FrustumDrawMode getFrustumDrawMode() const { return _frustumDrawMode; }
ViewFrustumOffset getViewFrustumOffset() const { return _viewFrustumOffset; } ViewFrustumOffset getViewFrustumOffset() const { return _viewFrustumOffset; }
VoxelStatsDialog* getVoxelStatsDialog() const { return _voxelStatsDialog; } VoxelStatsDialog* getVoxelStatsDialog() const { return _voxelStatsDialog; }
int getMaxVoxels() const { return _maxVoxels; }
void handleViewFrustumOffsetKeyModifier(int key); void handleViewFrustumOffsetKeyModifier(int key);
@ -78,6 +79,7 @@ private slots:
void chooseVoxelPaintColor(); void chooseVoxelPaintColor();
void runTests(); void runTests();
void resetSwatchColors(); void resetSwatchColors();
void switchVoxelShader();
private: private:
static Menu* _instance; static Menu* _instance;
@ -111,10 +113,10 @@ private:
BandwidthDialog* _bandwidthDialog; BandwidthDialog* _bandwidthDialog;
float _fieldOfView; /// in Degrees, doesn't apply to HMD like Oculus float _fieldOfView; /// in Degrees, doesn't apply to HMD like Oculus
FrustumDrawMode _frustumDrawMode; FrustumDrawMode _frustumDrawMode;
float _gyroCameraSensitivity;
ViewFrustumOffset _viewFrustumOffset; ViewFrustumOffset _viewFrustumOffset;
QActionGroup* _voxelModeActionsGroup; QActionGroup* _voxelModeActionsGroup;
VoxelStatsDialog* _voxelStatsDialog; VoxelStatsDialog* _voxelStatsDialog;
int _maxVoxels;
}; };
namespace MenuOption { namespace MenuOption {
@ -201,9 +203,9 @@ namespace MenuOption {
const QString TransmitterDrive = "Transmitter Drive"; const QString TransmitterDrive = "Transmitter Drive";
const QString UsePerlinFace = "Use Perlin's Face"; const QString UsePerlinFace = "Use Perlin's Face";
const QString Quit = "Quit"; const QString Quit = "Quit";
const QString Webcam = "Webcam"; const QString UseVoxelShader = "Use Voxel Shader";
const QString WebcamMode = "Cycle Webcam Send Mode"; const QString UseByteNormals = "Use Byte Normals";
const QString WebcamTexture = "Webcam Texture"; const QString UseGlobalNormals = "Use Global Normals";
const QString Voxels = "Voxels"; const QString Voxels = "Voxels";
const QString VoxelAddMode = "Add Voxel Mode"; const QString VoxelAddMode = "Add Voxel Mode";
const QString VoxelColorMode = "Color Voxel Mode"; const QString VoxelColorMode = "Color Voxel Mode";
@ -214,6 +216,9 @@ namespace MenuOption {
const QString VoxelSelectMode = "Select Voxel Mode"; const QString VoxelSelectMode = "Select Voxel Mode";
const QString VoxelStats = "Voxel Stats"; const QString VoxelStats = "Voxel Stats";
const QString VoxelTextures = "Voxel Textures"; const QString VoxelTextures = "Voxel Textures";
const QString Webcam = "Webcam";
const QString WebcamMode = "Cycle Webcam Send Mode";
const QString WebcamTexture = "Webcam Texture";
} }
#endif /* defined(__hifi__Menu__) */ #endif /* defined(__hifi__Menu__) */

View file

@ -33,9 +33,11 @@
#include "VoxelConstants.h" #include "VoxelConstants.h"
#include "VoxelSystem.h" #include "VoxelSystem.h"
float identityVertices[] = { 0,0,0, 1,0,0, 1,1,0, 0,1,0, 0,0,1, 1,0,1, 1,1,1, 0,1,1, float identityVerticesGlobalNormals[] = { 0,0,0, 1,0,0, 1,1,0, 0,1,0, 0,0,1, 1,0,1, 1,1,1, 0,1,1 };
0,0,0, 1,0,0, 1,1,0, 0,1,0, 0,0,1, 1,0,1, 1,1,1, 0,1,1,
0,0,0, 1,0,0, 1,1,0, 0,1,0, 0,0,1, 1,0,1, 1,1,1, 0,1,1 }; float identityVertices[] = { 0,0,0, 1,0,0, 1,1,0, 0,1,0, 0,0,1, 1,0,1, 1,1,1, 0,1,1, //0-7
0,0,0, 1,0,0, 1,1,0, 0,1,0, 0,0,1, 1,0,1, 1,1,1, 0,1,1, //8-15
0,0,0, 1,0,0, 1,1,0, 0,1,0, 0,0,1, 1,0,1, 1,1,1, 0,1,1 }; // 16-23
GLfloat identityNormals[] = { 0,0,-1, 0,0,-1, 0,0,-1, 0,0,-1, GLfloat identityNormals[] = { 0,0,-1, 0,0,-1, 0,0,-1, 0,0,-1,
0,0,+1, 0,0,+1, 0,0,+1, 0,0,+1, 0,0,+1, 0,0,+1, 0,0,+1, 0,0,+1,
@ -51,6 +53,13 @@ GLubyte identityIndices[] = { 0,2,1, 0,3,2, // Z-
10,11,15, 10,15,14, // Y+ 10,11,15, 10,15,14, // Y+
4,5,6, 4,6,7 }; // Z+ 4,5,6, 4,6,7 }; // Z+
GLubyte identityIndicesTop[] = { 2, 3, 7, 2, 7, 6 };
GLubyte identityIndicesBottom[] = { 0, 1, 5, 0, 5, 4 };
GLubyte identityIndicesLeft[] = { 0, 7, 3, 0, 4, 7 };
GLubyte identityIndicesRight[] = { 1, 2, 6, 1, 6, 5 };
GLubyte identityIndicesFront[] = { 0, 2, 1, 0, 3, 2 };
GLubyte identityIndicesBack[] = { 4, 5, 6, 4, 6, 7 };
VoxelSystem::VoxelSystem(float treeScale, int maxVoxels) VoxelSystem::VoxelSystem(float treeScale, int maxVoxels)
: NodeData(NULL), : NodeData(NULL),
_treeScale(treeScale), _treeScale(treeScale),
@ -74,6 +83,21 @@ VoxelSystem::VoxelSystem(float treeScale, int maxVoxels)
connect(_tree, SIGNAL(importSize(float,float,float)), SIGNAL(importSize(float,float,float))); connect(_tree, SIGNAL(importSize(float,float,float)), SIGNAL(importSize(float,float,float)));
connect(_tree, SIGNAL(importProgress(int)), SIGNAL(importProgress(int))); connect(_tree, SIGNAL(importProgress(int)), SIGNAL(importProgress(int)));
_useVoxelShader = false;
_useByteNormals = false;
_useGlobalNormals = false;
_writeVoxelShaderData = NULL;
_readVoxelShaderData = NULL;
_readVerticesArray = NULL;
_writeVerticesArray = NULL;
_readColorsArray = NULL;
_writeColorsArray = NULL;
_writeVoxelDirtyArray = NULL;
_readVoxelDirtyArray = NULL;
} }
void VoxelSystem::nodeDeleted(VoxelNode* node) { void VoxelSystem::nodeDeleted(VoxelNode* node) {
@ -119,21 +143,7 @@ void VoxelSystem::clearFreeBufferIndexes() {
} }
VoxelSystem::~VoxelSystem() { VoxelSystem::~VoxelSystem() {
if (_initialized) { cleanupVoxelMemory();
// Destroy glBuffers
glDeleteBuffers(1, &_vboVerticesID);
glDeleteBuffers(1, &_vboNormalsID);
glDeleteBuffers(1, &_vboColorsID);
glDeleteBuffers(1, &_vboIndicesID);
delete[] _readVerticesArray;
delete[] _writeVerticesArray;
delete[] _readColorsArray;
delete[] _writeColorsArray;
delete[] _writeVoxelDirtyArray;
delete[] _readVoxelDirtyArray;
}
delete _tree; delete _tree;
pthread_mutex_destroy(&_bufferWriteLock); pthread_mutex_destroy(&_bufferWriteLock);
pthread_mutex_destroy(&_treeLock); pthread_mutex_destroy(&_treeLock);
@ -141,6 +151,321 @@ VoxelSystem::~VoxelSystem() {
VoxelNode::removeDeleteHook(this); VoxelNode::removeDeleteHook(this);
} }
void VoxelSystem::setUseByteNormals(bool useByteNormals) {
pthread_mutex_lock(&_bufferWriteLock);
bool wasInitialized = _initialized;
if (wasInitialized) {
clearAllNodesBufferIndex();
cleanupVoxelMemory();
}
_useByteNormals = useByteNormals;
if (wasInitialized) {
init();
}
pthread_mutex_unlock(&_bufferWriteLock);
if (wasInitialized) {
forceRedrawEntireTree();
}
}
void VoxelSystem::setUseGlobalNormals(bool useGlobalNormals) {
pthread_mutex_lock(&_bufferWriteLock);
bool wasInitialized = _initialized;
if (wasInitialized) {
clearAllNodesBufferIndex();
cleanupVoxelMemory();
}
_useGlobalNormals = useGlobalNormals;
if (wasInitialized) {
init();
}
pthread_mutex_unlock(&_bufferWriteLock);
if (wasInitialized) {
forceRedrawEntireTree();
}
}
void VoxelSystem::setMaxVoxels(int maxVoxels) {
pthread_mutex_lock(&_bufferWriteLock);
bool wasInitialized = _initialized;
if (wasInitialized) {
clearAllNodesBufferIndex();
cleanupVoxelMemory();
}
_maxVoxels = maxVoxels;
if (wasInitialized) {
init();
}
pthread_mutex_unlock(&_bufferWriteLock);
if (wasInitialized) {
forceRedrawEntireTree();
}
}
void VoxelSystem::setUseVoxelShader(bool useVoxelShader) {
pthread_mutex_lock(&_bufferWriteLock);
bool wasInitialized = _initialized;
if (wasInitialized) {
clearAllNodesBufferIndex();
cleanupVoxelMemory();
}
_useVoxelShader = useVoxelShader;
if (wasInitialized) {
init();
}
pthread_mutex_unlock(&_bufferWriteLock);
if (wasInitialized) {
forceRedrawEntireTree();
}
}
void VoxelSystem::cleanupVoxelMemory() {
if (_initialized) {
if (_useVoxelShader) {
// these are used when in VoxelShader mode.
glDeleteBuffers(1, &_vboVoxelsID);
glDeleteBuffers(1, &_vboVoxelsIndicesID);
delete[] _writeVoxelShaderData;
delete[] _readVoxelShaderData;
} else {
// Destroy glBuffers
glDeleteBuffers(1, &_vboVerticesID);
glDeleteBuffers(1, &_vboColorsID);
if (!_useGlobalNormals) {
glDeleteBuffers(1, &_vboNormalsID);
glDeleteBuffers(1, &_vboIndicesID);
} else {
glDeleteBuffers(1, &_vboIndicesTop);
glDeleteBuffers(1, &_vboIndicesBottom);
glDeleteBuffers(1, &_vboIndicesLeft);
glDeleteBuffers(1, &_vboIndicesRight);
glDeleteBuffers(1, &_vboIndicesFront);
glDeleteBuffers(1, &_vboIndicesBack);
}
delete[] _readVerticesArray;
delete[] _writeVerticesArray;
delete[] _readColorsArray;
delete[] _writeColorsArray;
}
delete[] _writeVoxelDirtyArray;
delete[] _readVoxelDirtyArray;
}
_initialized = false; // no longer initialized
}
void VoxelSystem::setupFaceIndices(GLuint& faceVBOID, GLubyte faceIdentityIndices[]) {
GLuint* indicesArray = new GLuint[INDICES_PER_FACE * _maxVoxels];
// populate the indicesArray
// this will not change given new voxels, so we can set it all up now
for (int n = 0; n < _maxVoxels; n++) {
// fill the indices array
int voxelIndexOffset = n * INDICES_PER_FACE;
GLuint* currentIndicesPos = indicesArray + voxelIndexOffset;
int startIndex = (n * GLOBAL_NORMALS_VERTICES_PER_VOXEL);
for (int i = 0; i < INDICES_PER_FACE; i++) {
// add indices for this side of the cube
currentIndicesPos[i] = startIndex + faceIdentityIndices[i];
}
}
glGenBuffers(1, &faceVBOID);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, faceVBOID);
glBufferData(GL_ELEMENT_ARRAY_BUFFER,
INDICES_PER_FACE * sizeof(GLuint) * _maxVoxels,
indicesArray, GL_STATIC_DRAW);
_memoryUsageVBO += INDICES_PER_FACE * sizeof(GLuint) * _maxVoxels;
// delete the indices and normals arrays that are no longer needed
delete[] indicesArray;
}
void VoxelSystem::initVoxelMemory() {
_initialMemoryUsageGPU = getFreeMemoryGPU();
_memoryUsageRAM = 0;
_memoryUsageVBO = 0; // our VBO allocations as we know them
if (_useVoxelShader) {
qDebug("Using Voxel Shader...\n");
GLuint* indicesArray = new GLuint[_maxVoxels];
// populate the indicesArray
// this will not change given new voxels, so we can set it all up now
for (int n = 0; n < _maxVoxels; n++) {
indicesArray[n] = n;
}
// bind the indices VBO to the actual indices array
glGenBuffers(1, &_vboVoxelsIndicesID);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _vboVoxelsIndicesID);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(GLuint) * _maxVoxels, indicesArray, GL_STATIC_DRAW);
_memoryUsageVBO += sizeof(GLuint) * _maxVoxels;
glGenBuffers(1, &_vboVoxelsID);
glBindBuffer(GL_ARRAY_BUFFER, _vboVoxelsID);
glBufferData(GL_ARRAY_BUFFER, _maxVoxels * sizeof(VoxelShaderVBOData), NULL, GL_DYNAMIC_DRAW);
_memoryUsageVBO += _maxVoxels * sizeof(VoxelShaderVBOData);
// delete the indices and normals arrays that are no longer needed
delete[] indicesArray;
// we will track individual dirty sections with these arrays of bools
_writeVoxelDirtyArray = new bool[_maxVoxels];
memset(_writeVoxelDirtyArray, false, _maxVoxels * sizeof(bool));
_memoryUsageRAM += (_maxVoxels * sizeof(bool));
_readVoxelDirtyArray = new bool[_maxVoxels];
memset(_readVoxelDirtyArray, false, _maxVoxels * sizeof(bool));
_memoryUsageRAM += (_maxVoxels * sizeof(bool));
// prep the data structures for incoming voxel data
_writeVoxelShaderData = new VoxelShaderVBOData[_maxVoxels];
_memoryUsageRAM += (sizeof(VoxelShaderVBOData) * _maxVoxels);
_readVoxelShaderData = new VoxelShaderVBOData[_maxVoxels];
_memoryUsageRAM += (sizeof(VoxelShaderVBOData) * _maxVoxels);
} else {
if (_useGlobalNormals) {
// Global Normals mode uses a technique of not including normals on any voxel vertices, and instead
// rendering the voxel faces in 6 passes that use a global call to glNormal3f()
qDebug("Using Global Normals...\n");
setupFaceIndices(_vboIndicesTop, identityIndicesTop);
setupFaceIndices(_vboIndicesBottom, identityIndicesBottom);
setupFaceIndices(_vboIndicesLeft, identityIndicesLeft);
setupFaceIndices(_vboIndicesRight, identityIndicesRight);
setupFaceIndices(_vboIndicesFront, identityIndicesFront);
setupFaceIndices(_vboIndicesBack, identityIndicesBack);
} else {
if (_useByteNormals) {
qDebug("Using Byte Normals...\n");
GLbyte* normalsArray = new GLbyte[VERTEX_POINTS_PER_VOXEL * _maxVoxels];
GLbyte* normalsArrayEndPointer = normalsArray;
// populate the normalsArray
for (int n = 0; n < _maxVoxels; n++) {
for (int i = 0; i < VERTEX_POINTS_PER_VOXEL; i++) {
*(normalsArrayEndPointer++) = (identityNormals[i] * CHAR_MAX);
}
}
// VBO for the normalsArray
glGenBuffers(1, &_vboNormalsID);
glBindBuffer(GL_ARRAY_BUFFER, _vboNormalsID);
glBufferData(GL_ARRAY_BUFFER,
VERTEX_POINTS_PER_VOXEL * sizeof(GLbyte) * _maxVoxels,
normalsArray, GL_STATIC_DRAW);
_memoryUsageVBO += VERTEX_POINTS_PER_VOXEL * sizeof(GLbyte) * _maxVoxels;
// delete the indices and normals arrays that are no longer needed
delete[] normalsArray;
} else {
qDebug("Using Float Normals...\n");
GLfloat* normalsArray = new GLfloat[VERTEX_POINTS_PER_VOXEL * _maxVoxels];
GLfloat* normalsArrayEndPointer = normalsArray;
// populate the normalsArray
for (int n = 0; n < _maxVoxels; n++) {
for (int i = 0; i < VERTEX_POINTS_PER_VOXEL; i++) {
*(normalsArrayEndPointer++) = identityNormals[i];
}
}
// VBO for the normalsArray
glGenBuffers(1, &_vboNormalsID);
glBindBuffer(GL_ARRAY_BUFFER, _vboNormalsID);
glBufferData(GL_ARRAY_BUFFER,
VERTEX_POINTS_PER_VOXEL * sizeof(GLfloat) * _maxVoxels,
normalsArray, GL_STATIC_DRAW);
_memoryUsageVBO += VERTEX_POINTS_PER_VOXEL * sizeof(GLfloat) * _maxVoxels;
// delete the indices and normals arrays that are no longer needed
delete[] normalsArray;
}
GLuint* indicesArray = new GLuint[INDICES_PER_VOXEL * _maxVoxels];
// populate the indicesArray
// this will not change given new voxels, so we can set it all up now
for (int n = 0; n < _maxVoxels; n++) {
// fill the indices array
int voxelIndexOffset = n * INDICES_PER_VOXEL;
GLuint* currentIndicesPos = indicesArray + voxelIndexOffset;
int startIndex = (n * VERTICES_PER_VOXEL);
for (int i = 0; i < INDICES_PER_VOXEL; i++) {
// add indices for this side of the cube
currentIndicesPos[i] = startIndex + identityIndices[i];
}
}
// VBO for the indicesArray
glGenBuffers(1, &_vboIndicesID);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _vboIndicesID);
glBufferData(GL_ELEMENT_ARRAY_BUFFER,
INDICES_PER_VOXEL * sizeof(GLuint) * _maxVoxels,
indicesArray, GL_STATIC_DRAW);
_memoryUsageVBO += INDICES_PER_VOXEL * sizeof(GLuint) * _maxVoxels;
// delete the indices and normals arrays that are no longer needed
delete[] indicesArray;
}
// Depending on if we're using per vertex normals, we will need more or less vertex points per voxel
int vertexPointsPerVoxel = _useGlobalNormals ? GLOBAL_NORMALS_VERTEX_POINTS_PER_VOXEL : VERTEX_POINTS_PER_VOXEL;
glGenBuffers(1, &_vboVerticesID);
glBindBuffer(GL_ARRAY_BUFFER, _vboVerticesID);
glBufferData(GL_ARRAY_BUFFER, vertexPointsPerVoxel * sizeof(GLfloat) * _maxVoxels, NULL, GL_DYNAMIC_DRAW);
_memoryUsageVBO += vertexPointsPerVoxel * sizeof(GLfloat) * _maxVoxels;
// VBO for colorsArray
glGenBuffers(1, &_vboColorsID);
glBindBuffer(GL_ARRAY_BUFFER, _vboColorsID);
glBufferData(GL_ARRAY_BUFFER, vertexPointsPerVoxel * sizeof(GLubyte) * _maxVoxels, NULL, GL_DYNAMIC_DRAW);
_memoryUsageVBO += vertexPointsPerVoxel * sizeof(GLubyte) * _maxVoxels;
// we will track individual dirty sections with these arrays of bools
_writeVoxelDirtyArray = new bool[_maxVoxels];
memset(_writeVoxelDirtyArray, false, _maxVoxels * sizeof(bool));
_memoryUsageRAM += (sizeof(bool) * _maxVoxels);
_readVoxelDirtyArray = new bool[_maxVoxels];
memset(_readVoxelDirtyArray, false, _maxVoxels * sizeof(bool));
_memoryUsageRAM += (sizeof(bool) * _maxVoxels);
// prep the data structures for incoming voxel data
_writeVerticesArray = new GLfloat[vertexPointsPerVoxel * _maxVoxels];
_memoryUsageRAM += (sizeof(GLfloat) * vertexPointsPerVoxel * _maxVoxels);
_readVerticesArray = new GLfloat[vertexPointsPerVoxel * _maxVoxels];
_memoryUsageRAM += (sizeof(GLfloat) * vertexPointsPerVoxel * _maxVoxels);
_writeColorsArray = new GLubyte[vertexPointsPerVoxel * _maxVoxels];
_memoryUsageRAM += (sizeof(GLubyte) * vertexPointsPerVoxel * _maxVoxels);
_readColorsArray = new GLubyte[vertexPointsPerVoxel * _maxVoxels];
_memoryUsageRAM += (sizeof(GLubyte) * vertexPointsPerVoxel * _maxVoxels);
// create our simple fragment shader if we're the first system to init
if (!_perlinModulateProgram.isLinked()) {
switchToResourcesParentIfRequired();
_perlinModulateProgram.addShaderFromSourceFile(QGLShader::Vertex, "resources/shaders/perlin_modulate.vert");
_perlinModulateProgram.addShaderFromSourceFile(QGLShader::Fragment, "resources/shaders/perlin_modulate.frag");
_perlinModulateProgram.link();
_perlinModulateProgram.bind();
_perlinModulateProgram.setUniformValue("permutationNormalTexture", 0);
_perlinModulateProgram.release();
}
}
}
void VoxelSystem::loadVoxelsFile(const char* fileName, bool wantColorRandomizer) { void VoxelSystem::loadVoxelsFile(const char* fileName, bool wantColorRandomizer) {
_tree->loadVoxelsFile(fileName, wantColorRandomizer); _tree->loadVoxelsFile(fileName, wantColorRandomizer);
setupNewVoxelsForDrawing(); setupNewVoxelsForDrawing();
@ -398,18 +723,27 @@ void VoxelSystem::copyWrittenDataToReadArraysPartialVBOs() {
void VoxelSystem::copyWrittenDataSegmentToReadArrays(glBufferIndex segmentStart, glBufferIndex segmentEnd) { void VoxelSystem::copyWrittenDataSegmentToReadArrays(glBufferIndex segmentStart, glBufferIndex segmentEnd) {
int segmentLength = (segmentEnd - segmentStart) + 1; int segmentLength = (segmentEnd - segmentStart) + 1;
if (_useVoxelShader) {
GLsizeiptr segmentSizeBytes = segmentLength * sizeof(VoxelShaderVBOData);
void* readDataAt = &_readVoxelShaderData[segmentStart];
void* writeDataAt = &_writeVoxelShaderData[segmentStart];
memcpy(readDataAt, writeDataAt, segmentSizeBytes);
} else {
// Depending on if we're using per vertex normals, we will need more or less vertex points per voxel
int vertexPointsPerVoxel = _useGlobalNormals ? GLOBAL_NORMALS_VERTEX_POINTS_PER_VOXEL : VERTEX_POINTS_PER_VOXEL;
GLintptr segmentStartAt = segmentStart * VERTEX_POINTS_PER_VOXEL * sizeof(GLfloat); GLintptr segmentStartAt = segmentStart * vertexPointsPerVoxel * sizeof(GLfloat);
GLsizeiptr segmentSizeBytes = segmentLength * VERTEX_POINTS_PER_VOXEL * sizeof(GLfloat); GLsizeiptr segmentSizeBytes = segmentLength * vertexPointsPerVoxel * sizeof(GLfloat);
GLfloat* readVerticesAt = _readVerticesArray + (segmentStart * VERTEX_POINTS_PER_VOXEL); GLfloat* readVerticesAt = _readVerticesArray + (segmentStart * vertexPointsPerVoxel);
GLfloat* writeVerticesAt = _writeVerticesArray + (segmentStart * VERTEX_POINTS_PER_VOXEL); GLfloat* writeVerticesAt = _writeVerticesArray + (segmentStart * vertexPointsPerVoxel);
memcpy(readVerticesAt, writeVerticesAt, segmentSizeBytes); memcpy(readVerticesAt, writeVerticesAt, segmentSizeBytes);
segmentStartAt = segmentStart * VERTEX_POINTS_PER_VOXEL * sizeof(GLubyte); segmentStartAt = segmentStart * vertexPointsPerVoxel * sizeof(GLubyte);
segmentSizeBytes = segmentLength * VERTEX_POINTS_PER_VOXEL * sizeof(GLubyte); segmentSizeBytes = segmentLength * vertexPointsPerVoxel * sizeof(GLubyte);
GLubyte* readColorsAt = _readColorsArray + (segmentStart * VERTEX_POINTS_PER_VOXEL); GLubyte* readColorsAt = _readColorsArray + (segmentStart * vertexPointsPerVoxel);
GLubyte* writeColorsAt = _writeColorsArray + (segmentStart * VERTEX_POINTS_PER_VOXEL); GLubyte* writeColorsAt = _writeColorsArray + (segmentStart * vertexPointsPerVoxel);
memcpy(readColorsAt, writeColorsAt, segmentSizeBytes); memcpy(readColorsAt, writeColorsAt, segmentSizeBytes);
}
} }
void VoxelSystem::copyWrittenDataToReadArrays(bool fullVBOs) { void VoxelSystem::copyWrittenDataToReadArrays(bool fullVBOs) {
@ -532,11 +866,34 @@ int VoxelSystem::updateNodeInArraysAsPartialVBO(VoxelNode* node) {
void VoxelSystem::updateNodeInArrays(glBufferIndex nodeIndex, const glm::vec3& startVertex, void VoxelSystem::updateNodeInArrays(glBufferIndex nodeIndex, const glm::vec3& startVertex,
float voxelScale, const nodeColor& color) { float voxelScale, const nodeColor& color) {
for (int j = 0; j < VERTEX_POINTS_PER_VOXEL; j++ ) {
GLfloat* writeVerticesAt = _writeVerticesArray + (nodeIndex * VERTEX_POINTS_PER_VOXEL); if (_useVoxelShader) {
GLubyte* writeColorsAt = _writeColorsArray + (nodeIndex * VERTEX_POINTS_PER_VOXEL); if (_writeVoxelShaderData) {
*(writeVerticesAt+j) = startVertex[j % 3] + (identityVertices[j] * voxelScale); VoxelShaderVBOData* writeVerticesAt = &_writeVoxelShaderData[nodeIndex];
*(writeColorsAt +j) = color[j % 3]; writeVerticesAt->x = startVertex.x * TREE_SCALE;
writeVerticesAt->y = startVertex.y * TREE_SCALE;
writeVerticesAt->z = startVertex.z * TREE_SCALE;
writeVerticesAt->s = voxelScale * TREE_SCALE;
writeVerticesAt->r = color[RED_INDEX];
writeVerticesAt->g = color[GREEN_INDEX];
writeVerticesAt->b = color[BLUE_INDEX];
}
} else {
if (_writeVerticesArray && _writeColorsArray) {
int vertexPointsPerVoxel = _useGlobalNormals ? GLOBAL_NORMALS_VERTEX_POINTS_PER_VOXEL : VERTEX_POINTS_PER_VOXEL;
for (int j = 0; j < vertexPointsPerVoxel; j++ ) {
GLfloat* writeVerticesAt = _writeVerticesArray + (nodeIndex * vertexPointsPerVoxel);
GLubyte* writeColorsAt = _writeColorsArray + (nodeIndex * vertexPointsPerVoxel);
if (_useGlobalNormals) {
*(writeVerticesAt+j) = startVertex[j % 3] + (identityVerticesGlobalNormals[j] * voxelScale);
} else {
*(writeVerticesAt+j) = startVertex[j % 3] + (identityVertices[j] * voxelScale);
}
*(writeColorsAt +j) = color[j % 3];
}
}
} }
} }
@ -563,86 +920,8 @@ void VoxelSystem::init() {
_voxelsInWriteArrays = 0; _voxelsInWriteArrays = 0;
_voxelsInReadArrays = 0; _voxelsInReadArrays = 0;
GLuint* indicesArray = new GLuint[INDICES_PER_VOXEL * _maxVoxels];
// populate the indicesArray
// this will not change given new voxels, so we can set it all up now
for (int n = 0; n < _maxVoxels; n++) {
// fill the indices array
int voxelIndexOffset = n * INDICES_PER_VOXEL;
GLuint* currentIndicesPos = indicesArray + voxelIndexOffset;
int startIndex = (n * VERTICES_PER_VOXEL);
for (int i = 0; i < INDICES_PER_VOXEL; i++) {
// add indices for this side of the cube
currentIndicesPos[i] = startIndex + identityIndices[i];
}
}
GLfloat* normalsArray = new GLfloat[VERTEX_POINTS_PER_VOXEL * _maxVoxels];
GLfloat* normalsArrayEndPointer = normalsArray;
// populate the normalsArray
for (int n = 0; n < _maxVoxels; n++) {
for (int i = 0; i < VERTEX_POINTS_PER_VOXEL; i++) {
*(normalsArrayEndPointer++) = identityNormals[i];
}
}
// VBO for the verticesArray // VBO for the verticesArray
glGenBuffers(1, &_vboVerticesID); initVoxelMemory();
glBindBuffer(GL_ARRAY_BUFFER, _vboVerticesID);
glBufferData(GL_ARRAY_BUFFER, VERTEX_POINTS_PER_VOXEL * sizeof(GLfloat) * _maxVoxels, NULL, GL_DYNAMIC_DRAW);
// VBO for the normalsArray
glGenBuffers(1, &_vboNormalsID);
glBindBuffer(GL_ARRAY_BUFFER, _vboNormalsID);
glBufferData(GL_ARRAY_BUFFER,
VERTEX_POINTS_PER_VOXEL * sizeof(GLfloat) * _maxVoxels,
normalsArray, GL_STATIC_DRAW);
// VBO for colorsArray
glGenBuffers(1, &_vboColorsID);
glBindBuffer(GL_ARRAY_BUFFER, _vboColorsID);
glBufferData(GL_ARRAY_BUFFER, VERTEX_POINTS_PER_VOXEL * sizeof(GLubyte) * _maxVoxels, NULL, GL_DYNAMIC_DRAW);
// VBO for the indicesArray
glGenBuffers(1, &_vboIndicesID);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _vboIndicesID);
glBufferData(GL_ELEMENT_ARRAY_BUFFER,
INDICES_PER_VOXEL * sizeof(GLuint) * _maxVoxels,
indicesArray, GL_STATIC_DRAW);
// delete the indices and normals arrays that are no longer needed
delete[] indicesArray;
delete[] normalsArray;
// we will track individual dirty sections with these arrays of bools
_writeVoxelDirtyArray = new bool[_maxVoxels];
memset(_writeVoxelDirtyArray, false, _maxVoxels * sizeof(bool));
_readVoxelDirtyArray = new bool[_maxVoxels];
memset(_readVoxelDirtyArray, false, _maxVoxels * sizeof(bool));
// prep the data structures for incoming voxel data
_writeVerticesArray = new GLfloat[VERTEX_POINTS_PER_VOXEL * _maxVoxels];
_readVerticesArray = new GLfloat[VERTEX_POINTS_PER_VOXEL * _maxVoxels];
_writeColorsArray = new GLubyte[VERTEX_POINTS_PER_VOXEL * _maxVoxels];
_readColorsArray = new GLubyte[VERTEX_POINTS_PER_VOXEL * _maxVoxels];
// create our simple fragment shader if we're the first system to init
if (!_perlinModulateProgram.isLinked()) {
switchToResourcesParentIfRequired();
_perlinModulateProgram.addShaderFromSourceFile(QGLShader::Vertex, "resources/shaders/perlin_modulate.vert");
_perlinModulateProgram.addShaderFromSourceFile(QGLShader::Fragment, "resources/shaders/perlin_modulate.frag");
_perlinModulateProgram.link();
_perlinModulateProgram.bind();
_perlinModulateProgram.setUniformValue("permutationNormalTexture", 0);
_perlinModulateProgram.release();
}
_initialized = true; _initialized = true;
} }
@ -714,17 +993,28 @@ void VoxelSystem::updateVBOs() {
} }
void VoxelSystem::updateVBOSegment(glBufferIndex segmentStart, glBufferIndex segmentEnd) { void VoxelSystem::updateVBOSegment(glBufferIndex segmentStart, glBufferIndex segmentEnd) {
int segmentLength = (segmentEnd - segmentStart) + 1; if (_useVoxelShader) {
GLintptr segmentStartAt = segmentStart * VERTEX_POINTS_PER_VOXEL * sizeof(GLfloat); int segmentLength = (segmentEnd - segmentStart) + 1;
GLsizeiptr segmentSizeBytes = segmentLength * VERTEX_POINTS_PER_VOXEL * sizeof(GLfloat); GLintptr segmentStartAt = segmentStart * sizeof(VoxelShaderVBOData);
GLfloat* readVerticesFrom = _readVerticesArray + (segmentStart * VERTEX_POINTS_PER_VOXEL); GLsizeiptr segmentSizeBytes = segmentLength * sizeof(VoxelShaderVBOData);
glBindBuffer(GL_ARRAY_BUFFER, _vboVerticesID); void* readVerticesFrom = &_readVoxelShaderData[segmentStart];
glBufferSubData(GL_ARRAY_BUFFER, segmentStartAt, segmentSizeBytes, readVerticesFrom);
segmentStartAt = segmentStart * VERTEX_POINTS_PER_VOXEL * sizeof(GLubyte); glBindBuffer(GL_ARRAY_BUFFER, _vboVoxelsID);
segmentSizeBytes = segmentLength * VERTEX_POINTS_PER_VOXEL * sizeof(GLubyte); glBufferSubData(GL_ARRAY_BUFFER, segmentStartAt, segmentSizeBytes, readVerticesFrom);
GLubyte* readColorsFrom = _readColorsArray + (segmentStart * VERTEX_POINTS_PER_VOXEL); } else {
glBindBuffer(GL_ARRAY_BUFFER, _vboColorsID); int vertexPointsPerVoxel = _useGlobalNormals ? GLOBAL_NORMALS_VERTEX_POINTS_PER_VOXEL : VERTEX_POINTS_PER_VOXEL;
glBufferSubData(GL_ARRAY_BUFFER, segmentStartAt, segmentSizeBytes, readColorsFrom); int segmentLength = (segmentEnd - segmentStart) + 1;
GLintptr segmentStartAt = segmentStart * vertexPointsPerVoxel * sizeof(GLfloat);
GLsizeiptr segmentSizeBytes = segmentLength * vertexPointsPerVoxel * sizeof(GLfloat);
GLfloat* readVerticesFrom = _readVerticesArray + (segmentStart * vertexPointsPerVoxel);
glBindBuffer(GL_ARRAY_BUFFER, _vboVerticesID);
glBufferSubData(GL_ARRAY_BUFFER, segmentStartAt, segmentSizeBytes, readVerticesFrom);
segmentStartAt = segmentStart * vertexPointsPerVoxel * sizeof(GLubyte);
segmentSizeBytes = segmentLength * vertexPointsPerVoxel * sizeof(GLubyte);
GLubyte* readColorsFrom = _readColorsArray + (segmentStart * vertexPointsPerVoxel);
glBindBuffer(GL_ARRAY_BUFFER, _vboColorsID);
glBufferSubData(GL_ARRAY_BUFFER, segmentStartAt, segmentSizeBytes, readColorsFrom);
}
} }
void VoxelSystem::render(bool texture) { void VoxelSystem::render(bool texture) {
@ -734,43 +1024,116 @@ void VoxelSystem::render(bool texture) {
pthread_mutex_lock(&_bufferWriteLock); pthread_mutex_lock(&_bufferWriteLock);
updateVBOs(); updateVBOs();
if (_useVoxelShader) {
// tell OpenGL where to find vertex and color information Application::getInstance()->getVoxelShader().begin();
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_NORMAL_ARRAY);
glEnableClientState(GL_COLOR_ARRAY);
glBindBuffer(GL_ARRAY_BUFFER, _vboVerticesID); //Define this somewhere in your header file
glVertexPointer(3, GL_FLOAT, 0, 0); #define BUFFER_OFFSET(i) ((void*)(i))
glBindBuffer(GL_ARRAY_BUFFER, _vboNormalsID); glBindBuffer(GL_ARRAY_BUFFER, _vboVoxelsID);
glNormalPointer(GL_FLOAT, 0, 0); glEnableClientState(GL_VERTEX_ARRAY);
glVertexPointer(3, GL_FLOAT, sizeof(VoxelShaderVBOData), BUFFER_OFFSET(0)); //The starting point of the VBO, for the vertices
int loc = Application::getInstance()->getVoxelShader().attributeLocation("voxelSizeIn");
glEnableVertexAttribArray(loc);
glVertexAttribPointer(loc, 1, GL_FLOAT, false, sizeof(VoxelShaderVBOData), BUFFER_OFFSET(3*sizeof(float)));
glEnableClientState(GL_COLOR_ARRAY);
glColorPointer(3, GL_UNSIGNED_BYTE, sizeof(VoxelShaderVBOData), BUFFER_OFFSET(4*sizeof(float)));//The starting point of colors
glBindBuffer(GL_ARRAY_BUFFER, _vboColorsID); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _vboVoxelsIndicesID);
glColorPointer(3, GL_UNSIGNED_BYTE, 0, 0); glDrawElements(GL_POINTS, _voxelsInReadArrays, GL_UNSIGNED_INT, BUFFER_OFFSET(0)); //The starting point of the IBO
applyScaleAndBindProgram(texture); // deactivate vertex and color arrays after drawing
glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_COLOR_ARRAY);
// bind with 0 to switch back to normal operation
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
Application::getInstance()->getVoxelShader().end();
} else {
// tell OpenGL where to find vertex and color information
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_COLOR_ARRAY);
glBindBuffer(GL_ARRAY_BUFFER, _vboVerticesID);
glVertexPointer(3, GL_FLOAT, 0, 0);
glBindBuffer(GL_ARRAY_BUFFER, _vboColorsID);
glColorPointer(3, GL_UNSIGNED_BYTE, 0, 0);
if (!_useGlobalNormals) {
glEnableClientState(GL_NORMAL_ARRAY);
glBindBuffer(GL_ARRAY_BUFFER, _vboNormalsID);
glNormalPointer((_useByteNormals ? GL_BYTE : GL_FLOAT), 0, 0);
} else {
glNormal3f(0,1.0f,0); // hack for now
}
applyScaleAndBindProgram(texture);
// for performance, enable backface culling // for performance, enable backface culling
glEnable(GL_CULL_FACE); glEnable(GL_CULL_FACE);
// draw the number of voxels we have if (!_useGlobalNormals) {
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _vboIndicesID); // draw the number of voxels we have
glDrawRangeElementsEXT(GL_TRIANGLES, 0, VERTICES_PER_VOXEL * _voxelsInReadArrays - 1, glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _vboIndicesID);
36 * _voxelsInReadArrays, GL_UNSIGNED_INT, 0); glDrawRangeElementsEXT(GL_TRIANGLES, 0, VERTICES_PER_VOXEL * _voxelsInReadArrays - 1,
36 * _voxelsInReadArrays, GL_UNSIGNED_INT, 0);
} else {
// draw voxels in 6 passes
glDisable(GL_CULL_FACE); glNormal3f(0,1.0f,0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _vboIndicesTop);
glDrawRangeElementsEXT(GL_TRIANGLES, 0, INDICES_PER_FACE * _voxelsInReadArrays - 1,
INDICES_PER_FACE * _voxelsInReadArrays, GL_UNSIGNED_INT, 0);
removeScaleAndReleaseProgram(texture); glNormal3f(0,-1.0f,0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _vboIndicesBottom);
glDrawRangeElementsEXT(GL_TRIANGLES, 0, INDICES_PER_FACE * _voxelsInReadArrays - 1,
INDICES_PER_FACE * _voxelsInReadArrays, GL_UNSIGNED_INT, 0);
glNormal3f(-1.0f,0,0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _vboIndicesLeft);
glDrawRangeElementsEXT(GL_TRIANGLES, 0, INDICES_PER_FACE * _voxelsInReadArrays - 1,
INDICES_PER_FACE * _voxelsInReadArrays, GL_UNSIGNED_INT, 0);
glNormal3f(1.0f,0,0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _vboIndicesRight);
glDrawRangeElementsEXT(GL_TRIANGLES, 0, INDICES_PER_FACE * _voxelsInReadArrays - 1,
INDICES_PER_FACE * _voxelsInReadArrays, GL_UNSIGNED_INT, 0);
glNormal3f(0,0,-1.0f);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _vboIndicesFront);
glDrawRangeElementsEXT(GL_TRIANGLES, 0, INDICES_PER_FACE * _voxelsInReadArrays - 1,
INDICES_PER_FACE * _voxelsInReadArrays, GL_UNSIGNED_INT, 0);
glNormal3f(0,0,1.0f);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _vboIndicesBack);
glDrawRangeElementsEXT(GL_TRIANGLES, 0, INDICES_PER_FACE * _voxelsInReadArrays - 1,
INDICES_PER_FACE * _voxelsInReadArrays, GL_UNSIGNED_INT, 0);
}
glDisable(GL_CULL_FACE);
removeScaleAndReleaseProgram(texture);
// deactivate vertex and color arrays after drawing // deactivate vertex and color arrays after drawing
glDisableClientState(GL_VERTEX_ARRAY); glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_NORMAL_ARRAY); glDisableClientState(GL_COLOR_ARRAY);
glDisableClientState(GL_COLOR_ARRAY);
// bind with 0 to switch back to normal operation if (!_useGlobalNormals) {
glBindBuffer(GL_ARRAY_BUFFER, 0); glDisableClientState(GL_NORMAL_ARRAY);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); }
// bind with 0 to switch back to normal operation
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
}
pthread_mutex_unlock(&_bufferWriteLock); pthread_mutex_unlock(&_bufferWriteLock);
} }
@ -804,6 +1167,34 @@ void VoxelSystem::killLocalVoxels() {
} }
bool VoxelSystem::clearAllNodesBufferIndexOperation(VoxelNode* node, void* extraData) {
_nodeCount++;
node->setBufferIndex(GLBUFFER_INDEX_UNKNOWN);
return true;
}
void VoxelSystem::clearAllNodesBufferIndex() {
_nodeCount = 0;
_tree->recurseTreeWithOperation(clearAllNodesBufferIndexOperation);
qDebug("clearing buffer index of %d nodes\n", _nodeCount);
_tree->setDirtyBit();
setupNewVoxelsForDrawing();
}
bool VoxelSystem::forceRedrawEntireTreeOperation(VoxelNode* node, void* extraData) {
_nodeCount++;
node->setDirtyBit();
return true;
}
void VoxelSystem::forceRedrawEntireTree() {
_nodeCount = 0;
_tree->recurseTreeWithOperation(forceRedrawEntireTreeOperation);
qDebug("forcing redraw of %d nodes\n", _nodeCount);
_tree->setDirtyBit();
setupNewVoxelsForDrawing();
}
bool VoxelSystem::randomColorOperation(VoxelNode* node, void* extraData) { bool VoxelSystem::randomColorOperation(VoxelNode* node, void* extraData) {
_nodeCount++; _nodeCount++;
if (node->isColored()) { if (node->isColored()) {
@ -1203,7 +1594,7 @@ public:
duplicateVBOIndex(0), duplicateVBOIndex(0),
leafNodes(0) leafNodes(0)
{ {
memset(hasIndexFound, false, MAX_VOXELS_PER_SYSTEM * sizeof(bool)); memset(hasIndexFound, false, DEFAULT_MAX_VOXELS_PER_SYSTEM * sizeof(bool));
}; };
unsigned long totalNodes; unsigned long totalNodes;
@ -1218,7 +1609,7 @@ public:
unsigned long expectedMax; unsigned long expectedMax;
bool hasIndexFound[MAX_VOXELS_PER_SYSTEM]; bool hasIndexFound[DEFAULT_MAX_VOXELS_PER_SYSTEM];
}; };
bool VoxelSystem::collectStatsForTreesAndVBOsOperation(VoxelNode* node, void* extraData) { bool VoxelSystem::collectStatsForTreesAndVBOsOperation(VoxelNode* node, void* extraData) {
@ -1293,7 +1684,7 @@ void VoxelSystem::collectStatsForTreesAndVBOs() {
glBufferIndex minInVBO = GLBUFFER_INDEX_UNKNOWN; glBufferIndex minInVBO = GLBUFFER_INDEX_UNKNOWN;
glBufferIndex maxInVBO = 0; glBufferIndex maxInVBO = 0;
for (glBufferIndex i = 0; i < MAX_VOXELS_PER_SYSTEM; i++) { for (glBufferIndex i = 0; i < DEFAULT_MAX_VOXELS_PER_SYSTEM; i++) {
if (args.hasIndexFound[i]) { if (args.hasIndexFound[i]) {
minInVBO = std::min(minInVBO,i); minInVBO = std::min(minInVBO,i);
maxInVBO = std::max(maxInVBO,i); maxInVBO = std::max(maxInVBO,i);
@ -1622,7 +2013,7 @@ void VoxelSystem::falseColorizeOccludedV2() {
void VoxelSystem::nodeAdded(Node* node) { void VoxelSystem::nodeAdded(Node* node) {
if (node->getType() == NODE_TYPE_VOXEL_SERVER) { if (node->getType() == NODE_TYPE_VOXEL_SERVER) {
uint16_t nodeID = node->getNodeID(); uint16_t nodeID = node->getNodeID();
printf("VoxelSystem... voxel server %u added...\n", nodeID); qDebug("VoxelSystem... voxel server %u added...\n", nodeID);
_voxelServerCount++; _voxelServerCount++;
} }
} }
@ -1645,7 +2036,7 @@ void VoxelSystem::nodeKilled(Node* node) {
if (node->getType() == NODE_TYPE_VOXEL_SERVER) { if (node->getType() == NODE_TYPE_VOXEL_SERVER) {
_voxelServerCount--; _voxelServerCount--;
uint16_t nodeID = node->getNodeID(); uint16_t nodeID = node->getNodeID();
printf("VoxelSystem... voxel server %u removed...\n", nodeID); qDebug("VoxelSystem... voxel server %u removed...\n", nodeID);
if (_voxelServerCount > 0) { if (_voxelServerCount > 0) {
// Kill any voxels from the local tree that match this nodeID // Kill any voxels from the local tree that match this nodeID
@ -1661,3 +2052,58 @@ void VoxelSystem::nodeKilled(Node* node) {
} }
unsigned long VoxelSystem::getFreeMemoryGPU() {
// We can't ask all GPUs how much memory they have in use, but we can ask them about how much is free.
// So, we can record the free memory before we create our VBOs and the free memory after, and get a basic
// idea how how much we're using.
_hasMemoryUsageGPU = false; // assume the worst
unsigned long freeMemory = 0;
const int NUM_RESULTS = 4; // see notes, these APIs return up to 4 results
GLint results[NUM_RESULTS] = { 0, 0, 0, 0};
// ATI
// http://www.opengl.org/registry/specs/ATI/meminfo.txt
//
// TEXTURE_FREE_MEMORY_ATI 0x87FC
// RENDERBUFFER_FREE_MEMORY_ATI 0x87FD
const GLenum VBO_FREE_MEMORY_ATI = 0x87FB;
glGetIntegerv(VBO_FREE_MEMORY_ATI, &results[0]);
GLenum errorATI = glGetError();
if (errorATI == GL_NO_ERROR) {
_hasMemoryUsageGPU = true;
freeMemory = results[0];
} else {
// NVIDIA
// http://developer.download.nvidia.com/opengl/specs/GL_NVX_gpu_memory_info.txt
//
//const GLenum GPU_MEMORY_INFO_DEDICATED_VIDMEM_NVX = 0x9047;
//const GLenum GPU_MEMORY_INFO_EVICTION_COUNT_NVX = 0x904A;
//const GLenum GPU_MEMORY_INFO_EVICTED_MEMORY_NVX = 0x904B;
//const GLenum GPU_MEMORY_INFO_TOTAL_AVAILABLE_MEMORY_NVX = 0x9048;
const GLenum GPU_MEMORY_INFO_CURRENT_AVAILABLE_VIDMEM_NVX = 0x9049;
results[0] = 0;
glGetIntegerv(GPU_MEMORY_INFO_CURRENT_AVAILABLE_VIDMEM_NVX, &results[0]);
freeMemory += results[0];
GLenum errorNVIDIA = glGetError();
if (errorNVIDIA == GL_NO_ERROR) {
_hasMemoryUsageGPU = true;
freeMemory = results[0];
}
}
const unsigned long BYTES_PER_KBYTE = 1024;
return freeMemory * BYTES_PER_KBYTE; // API results in KB, we want it in bytes
}
unsigned long VoxelSystem::getVoxelMemoryUsageGPU() {
unsigned long currentFreeMemory = getFreeMemoryGPU();
return (_initialMemoryUsageGPU - currentFreeMemory);
}

View file

@ -23,15 +23,24 @@
#include "Camera.h" #include "Camera.h"
#include "Util.h" #include "Util.h"
#include "world.h" #include "world.h"
#include "renderer/VoxelShader.h"
class ProgramObject; class ProgramObject;
const int NUM_CHILDREN = 8; const int NUM_CHILDREN = 8;
struct VoxelShaderVBOData
{
float x, y, z; // position
float s; // size
unsigned char r,g,b; // color
};
class VoxelSystem : public NodeData, public VoxelNodeDeleteHook, public NodeListHook { class VoxelSystem : public NodeData, public VoxelNodeDeleteHook, public NodeListHook {
Q_OBJECT Q_OBJECT
public: public:
VoxelSystem(float treeScale = TREE_SCALE, int maxVoxels = MAX_VOXELS_PER_SYSTEM); VoxelSystem(float treeScale = TREE_SCALE, int maxVoxels = DEFAULT_MAX_VOXELS_PER_SYSTEM);
~VoxelSystem(); ~VoxelSystem();
void setDataSourceID(int dataSourceID) { _dataSourceID = dataSourceID; } void setDataSourceID(int dataSourceID) { _dataSourceID = dataSourceID; }
@ -56,6 +65,14 @@ public:
bool readFromSquareARGB32Pixels(const char* filename); bool readFromSquareARGB32Pixels(const char* filename);
bool readFromSchematicFile(const char* filename); bool readFromSchematicFile(const char* filename);
void setUseVoxelShader(bool useVoxelShader);
void setMaxVoxels(int maxVoxels);
long int getMaxVoxels() const { return _maxVoxels; }
unsigned long getVoxelMemoryUsageRAM() const { return _memoryUsageRAM; }
unsigned long getVoxelMemoryUsageVBO() const { return _memoryUsageVBO; }
bool hasVoxelMemoryUsageGPU() const { return _hasMemoryUsageGPU; }
unsigned long getVoxelMemoryUsageGPU();
long int getVoxelsCreated(); long int getVoxelsCreated();
long int getVoxelsColored(); long int getVoxelsColored();
long int getVoxelsBytesRead(); long int getVoxelsBytesRead();
@ -113,8 +130,12 @@ public slots:
void falseColorizeOccluded(); void falseColorizeOccluded();
void falseColorizeOccludedV2(); void falseColorizeOccludedV2();
void falseColorizeBySource(); void falseColorizeBySource();
void forceRedrawEntireTree();
void clearAllNodesBufferIndex();
void cancelImport(); void cancelImport();
void setUseByteNormals(bool useByteNormals);
void setUseGlobalNormals(bool useGlobalNormals);
protected: protected:
float _treeScale; float _treeScale;
@ -158,6 +179,8 @@ private:
static bool falseColorizeOccludedV2Operation(VoxelNode* node, void* extraData); static bool falseColorizeOccludedV2Operation(VoxelNode* node, void* extraData);
static bool falseColorizeBySourceOperation(VoxelNode* node, void* extraData); static bool falseColorizeBySourceOperation(VoxelNode* node, void* extraData);
static bool killSourceVoxelsOperation(VoxelNode* node, void* extraData); static bool killSourceVoxelsOperation(VoxelNode* node, void* extraData);
static bool forceRedrawEntireTreeOperation(VoxelNode* node, void* extraData);
static bool clearAllNodesBufferIndexOperation(VoxelNode* node, void* extraData);
int updateNodeInArraysAsFullVBO(VoxelNode* node); int updateNodeInArraysAsFullVBO(VoxelNode* node);
int updateNodeInArraysAsPartialVBO(VoxelNode* node); int updateNodeInArraysAsPartialVBO(VoxelNode* node);
@ -167,6 +190,8 @@ private:
void updateVBOs(); void updateVBOs();
unsigned long getFreeMemoryGPU();
// these are kinda hacks, used by getDistanceFromViewRangeOperation() probably shouldn't be here // these are kinda hacks, used by getDistanceFromViewRangeOperation() probably shouldn't be here
static float _maxDistance; static float _maxDistance;
static float _minDistance; static float _minDistance;
@ -190,10 +215,30 @@ private:
uint64_t _lastViewCulling; uint64_t _lastViewCulling;
int _lastViewCullingElapsed; int _lastViewCullingElapsed;
void initVoxelMemory();
void cleanupVoxelMemory();
bool _useByteNormals;
bool _useGlobalNormals;
bool _useVoxelShader;
GLuint _vboVoxelsID; /// when using voxel shader, we'll use this VBO
GLuint _vboVoxelsIndicesID; /// when using voxel shader, we'll use this VBO for our indexes
VoxelShaderVBOData* _writeVoxelShaderData;
VoxelShaderVBOData* _readVoxelShaderData;
GLuint _vboVerticesID; GLuint _vboVerticesID;
GLuint _vboNormalsID; GLuint _vboNormalsID;
GLuint _vboColorsID; GLuint _vboColorsID;
GLuint _vboIndicesID; GLuint _vboIndicesID;
GLuint _vboIndicesTop;
GLuint _vboIndicesBottom;
GLuint _vboIndicesLeft;
GLuint _vboIndicesRight;
GLuint _vboIndicesFront;
GLuint _vboIndicesBack;
pthread_mutex_t _bufferWriteLock; pthread_mutex_t _bufferWriteLock;
pthread_mutex_t _treeLock; pthread_mutex_t _treeLock;
@ -201,6 +246,8 @@ private:
ViewFrustum _lastStableViewFrustum; ViewFrustum _lastStableViewFrustum;
ViewFrustum* _viewFrustum; ViewFrustum* _viewFrustum;
void setupFaceIndices(GLuint& faceVBOID, GLubyte faceIdentityIndices[]);
int newTreeToArrays(VoxelNode *currentNode); int newTreeToArrays(VoxelNode *currentNode);
void cleanupRemovedVoxels(); void cleanupRemovedVoxels();
@ -224,6 +271,10 @@ private:
int _dataSourceID; int _dataSourceID;
int _voxelServerCount; int _voxelServerCount;
unsigned long _memoryUsageRAM;
unsigned long _memoryUsageVBO;
unsigned long _initialMemoryUsageGPU;
bool _hasMemoryUsageGPU;
}; };
#endif #endif

View file

@ -28,7 +28,6 @@
using namespace std; using namespace std;
const bool BALLS_ON = false; const bool BALLS_ON = false;
const bool USING_AVATAR_GRAVITY = true;
const glm::vec3 DEFAULT_UP_DIRECTION(0.0f, 1.0f, 0.0f); const glm::vec3 DEFAULT_UP_DIRECTION(0.0f, 1.0f, 0.0f);
const float YAW_MAG = 500.0; const float YAW_MAG = 500.0;
const float MY_HAND_HOLDING_PULL = 0.2; const float MY_HAND_HOLDING_PULL = 0.2;
@ -289,7 +288,7 @@ void Avatar::follow(Avatar* leadingAvatar) {
} }
} }
void Avatar::simulate(float deltaTime, Transmitter* transmitter, float gyroCameraSensitivity) { void Avatar::simulate(float deltaTime, Transmitter* transmitter) {
glm::quat orientation = getOrientation(); glm::quat orientation = getOrientation();
glm::vec3 front = orientation * IDENTITY_FRONT; glm::vec3 front = orientation * IDENTITY_FRONT;
@ -388,11 +387,22 @@ void Avatar::simulate(float deltaTime, Transmitter* transmitter, float gyroCamer
} }
} }
// head scale grows when avatar is looked at
if (Application::getInstance()->getLookatTargetAvatar() == this) {
const float BASE_MAX_SCALE = 3.0f;
const float GROW_SPEED = 0.1f;
_head.setScale(min(BASE_MAX_SCALE * glm::distance(_position, Application::getInstance()->getCamera()->getPosition()),
_head.getScale() + deltaTime * GROW_SPEED));
} else {
const float SHRINK_SPEED = 100.0f;
_head.setScale(max(_scale, _head.getScale() - deltaTime * SHRINK_SPEED));
}
_head.setBodyRotation(glm::vec3(_bodyPitch, _bodyYaw, _bodyRoll)); _head.setBodyRotation(glm::vec3(_bodyPitch, _bodyYaw, _bodyRoll));
_head.setPosition(_bodyBall[ BODY_BALL_HEAD_BASE ].position); _head.setPosition(_bodyBall[ BODY_BALL_HEAD_BASE ].position);
_head.setScale(_scale);
_head.setSkinColor(glm::vec3(SKIN_COLOR[0], SKIN_COLOR[1], SKIN_COLOR[2])); _head.setSkinColor(glm::vec3(SKIN_COLOR[0], SKIN_COLOR[1], SKIN_COLOR[2]));
_head.simulate(deltaTime, false, gyroCameraSensitivity); _head.simulate(deltaTime, false);
_hand.simulate(deltaTime, false); _hand.simulate(deltaTime, false);
// use speed and angular velocity to determine walking vs. standing // use speed and angular velocity to determine walking vs. standing
@ -759,6 +769,7 @@ void Avatar::loadData(QSettings* settings) {
_voxels.setVoxelURL(settings->value("voxelURL").toUrl()); _voxels.setVoxelURL(settings->value("voxelURL").toUrl());
_head.getBlendFace().setModelURL(settings->value("faceModelURL").toUrl()); _head.getBlendFace().setModelURL(settings->value("faceModelURL").toUrl());
_head.setPupilDilation(settings->value("pupilDilation", 0.0f).toFloat());
_leanScale = loadSetting(settings, "leanScale", 0.05f); _leanScale = loadSetting(settings, "leanScale", 0.05f);
@ -811,6 +822,7 @@ void Avatar::saveData(QSettings* set) {
set->setValue("voxelURL", _voxels.getVoxelURL()); set->setValue("voxelURL", _voxels.getVoxelURL());
set->setValue("faceModelURL", _head.getBlendFace().getModelURL()); set->setValue("faceModelURL", _head.getBlendFace().getModelURL());
set->setValue("pupilDilation", _head.getPupilDilation());
set->setValue("leanScale", _leanScale); set->setValue("leanScale", _leanScale);
set->setValue("scale", _newScale); set->setValue("scale", _newScale);

View file

@ -135,7 +135,7 @@ public:
~Avatar(); ~Avatar();
void init(); void init();
void simulate(float deltaTime, Transmitter* transmitter, float gyroCameraSensitivity); void simulate(float deltaTime, Transmitter* transmitter);
void follow(Avatar* leadingAvatar); void follow(Avatar* leadingAvatar);
void render(bool lookingInMirror, bool renderAvatarBalls); void render(bool lookingInMirror, bool renderAvatarBalls);

View file

@ -28,7 +28,7 @@ BlendFace::~BlendFace() {
} }
ProgramObject BlendFace::_eyeProgram; ProgramObject BlendFace::_eyeProgram;
GLuint BlendFace::_eyeTextureID; DilatedTextureCache BlendFace::_eyeTextureCache("resources/images/eye.png", 50, 210);
void BlendFace::init() { void BlendFace::init() {
if (!_eyeProgram.isLinked()) { if (!_eyeProgram.isLinked()) {
@ -40,8 +40,6 @@ void BlendFace::init() {
_eyeProgram.bind(); _eyeProgram.bind();
_eyeProgram.setUniformValue("texture", 0); _eyeProgram.setUniformValue("texture", 0);
_eyeProgram.release(); _eyeProgram.release();
_eyeTextureID = Application::getInstance()->getTextureCache()->getFileTextureID("resources/images/eye.png");
} }
} }
@ -92,7 +90,9 @@ bool BlendFace::render(float alpha) {
// use texture coordinates only for the eye, for now // use texture coordinates only for the eye, for now
glEnableClientState(GL_TEXTURE_COORD_ARRAY); glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glBindTexture(GL_TEXTURE_2D, _eyeTextureID); _eyeTexture = _eyeTextureCache.getTexture(_owningHead->getPupilDilation());
glBindTexture(GL_TEXTURE_2D, _eyeTexture->getID());
glEnable(GL_TEXTURE_2D); glEnable(GL_TEXTURE_2D);
_eyeProgram.bind(); _eyeProgram.bind();

View file

@ -15,6 +15,7 @@
#include "InterfaceConfig.h" #include "InterfaceConfig.h"
#include "renderer/FBXReader.h" #include "renderer/FBXReader.h"
#include "renderer/ProgramObject.h" #include "renderer/ProgramObject.h"
#include "renderer/TextureCache.h"
class QNetworkReply; class QNetworkReply;
@ -62,8 +63,10 @@ private:
QVector<glm::vec3> _blendedVertices; QVector<glm::vec3> _blendedVertices;
QVector<glm::vec3> _blendedNormals; QVector<glm::vec3> _blendedNormals;
QSharedPointer<Texture> _eyeTexture;
static ProgramObject _eyeProgram; static ProgramObject _eyeProgram;
static GLuint _eyeTextureID; static DilatedTextureCache _eyeTextureCache;
}; };
#endif /* defined(__interface__BlendFace__) */ #endif /* defined(__interface__BlendFace__) */

View file

@ -29,7 +29,7 @@ const float EYEBALL_RADIUS = 0.017;
const float EYELID_RADIUS = 0.019; const float EYELID_RADIUS = 0.019;
const float EYEBALL_COLOR[3] = { 0.9f, 0.9f, 0.8f }; const float EYEBALL_COLOR[3] = { 0.9f, 0.9f, 0.8f };
const float HAIR_SPRING_FORCE = 15.0f; const float HAIR_SPRING_FORCE = 15.0f;
const float HAIR_TORQUE_FORCE = 0.2f; const float HAIR_TORQUE_FORCE = 0.2f;
const float HAIR_GRAVITY_FORCE = 0.001f; const float HAIR_GRAVITY_FORCE = 0.001f;
const float HAIR_DRAG = 10.0f; const float HAIR_DRAG = 10.0f;
@ -46,7 +46,7 @@ const float IRIS_PROTRUSION = 0.0145f;
const char IRIS_TEXTURE_FILENAME[] = "resources/images/iris.png"; const char IRIS_TEXTURE_FILENAME[] = "resources/images/iris.png";
ProgramObject Head::_irisProgram; ProgramObject Head::_irisProgram;
GLuint Head::_irisTextureID; DilatedTextureCache Head::_irisTextureCache(IRIS_TEXTURE_FILENAME, 53, 127);
int Head::_eyePositionLocation; int Head::_eyePositionLocation;
Head::Head(Avatar* owningAvatar) : Head::Head(Avatar* owningAvatar) :
@ -71,6 +71,7 @@ Head::Head(Avatar* owningAvatar) :
_audioAttack(0.0f), _audioAttack(0.0f),
_returnSpringScale(1.0f), _returnSpringScale(1.0f),
_bodyRotation(0.0f, 0.0f, 0.0f), _bodyRotation(0.0f, 0.0f, 0.0f),
_angularVelocity(0,0,0),
_renderLookatVectors(false), _renderLookatVectors(false),
_mohawkInitialized(false), _mohawkInitialized(false),
_saccade(0.0f, 0.0f, 0.0f), _saccade(0.0f, 0.0f, 0.0f),
@ -102,13 +103,6 @@ void Head::init() {
_irisProgram.setUniformValue("texture", 0); _irisProgram.setUniformValue("texture", 0);
_eyePositionLocation = _irisProgram.uniformLocation("eyePosition"); _eyePositionLocation = _irisProgram.uniformLocation("eyePosition");
_irisTextureID = Application::getInstance()->getTextureCache()->getFileTextureID(IRIS_TEXTURE_FILENAME);
glBindTexture(GL_TEXTURE_2D, _irisTextureID);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
glBindTexture(GL_TEXTURE_2D, 0);
} }
_blendFace.init(); _blendFace.init();
} }
@ -139,7 +133,7 @@ void Head::resetHairPhysics() {
} }
void Head::simulate(float deltaTime, bool isMine, float gyroCameraSensitivity) { void Head::simulate(float deltaTime, bool isMine) {
// Update audio trailing average for rendering facial animations // Update audio trailing average for rendering facial animations
Faceshift* faceshift = Application::getInstance()->getFaceshift(); Faceshift* faceshift = Application::getInstance()->getFaceshift();
@ -237,44 +231,6 @@ void Head::simulate(float deltaTime, bool isMine, float gyroCameraSensitivity) {
// based on the nature of the lookat position, determine if the eyes can look / are looking at it. // based on the nature of the lookat position, determine if the eyes can look / are looking at it.
if (USING_PHYSICAL_MOHAWK) { if (USING_PHYSICAL_MOHAWK) {
updateHairPhysics(deltaTime); updateHairPhysics(deltaTime);
}
// Update camera pitch and yaw independently from motion of head (for gyro-based interface)
if (isMine && _cameraFollowsHead && (gyroCameraSensitivity > 0.f)) {
// If we are using gyros and using gyroLook, have the camera follow head but with a null region
// to create stable rendering view with small head movements.
const float CAMERA_FOLLOW_HEAD_RATE_START = 0.1f;
const float CAMERA_FOLLOW_HEAD_RATE_MAX = 1.0f;
const float CAMERA_FOLLOW_HEAD_RATE_RAMP_RATE = 1.05f;
const float CAMERA_STOP_TOLERANCE_DEGREES = 0.5f;
const float PITCH_START_RANGE = 20.f;
const float YAW_START_RANGE = 10.f;
float pitchStartTolerance = PITCH_START_RANGE
* (1.f - gyroCameraSensitivity)
+ (2.f * CAMERA_STOP_TOLERANCE_DEGREES);
float yawStartTolerance = YAW_START_RANGE
* (1.f - gyroCameraSensitivity)
+ (2.f * CAMERA_STOP_TOLERANCE_DEGREES);
float cameraHeadAngleDifference = glm::length(glm::vec2(_pitch - _cameraPitch, _yaw - _cameraYaw));
if (_isCameraMoving) {
_cameraFollowHeadRate = glm::clamp(_cameraFollowHeadRate * CAMERA_FOLLOW_HEAD_RATE_RAMP_RATE,
0.f,
CAMERA_FOLLOW_HEAD_RATE_MAX);
_cameraPitch += (_pitch - _cameraPitch) * _cameraFollowHeadRate;
_cameraYaw += (_yaw - _cameraYaw) * _cameraFollowHeadRate;
if (cameraHeadAngleDifference < CAMERA_STOP_TOLERANCE_DEGREES) {
_isCameraMoving = false;
}
} else {
if ((fabs(_pitch - _cameraPitch) > pitchStartTolerance) ||
(fabs(_yaw - _cameraYaw) > yawStartTolerance)) {
_isCameraMoving = true;
_cameraFollowHeadRate = CAMERA_FOLLOW_HEAD_RATE_START;
}
}
} }
} }
@ -667,7 +623,12 @@ void Head::renderEyeBalls() {
glPopMatrix(); glPopMatrix();
_irisProgram.bind(); _irisProgram.bind();
glBindTexture(GL_TEXTURE_2D, _irisTextureID);
_irisTexture = _irisTextureCache.getTexture(_pupilDilation);
glBindTexture(GL_TEXTURE_2D, _irisTexture->getID());
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
glEnable(GL_TEXTURE_2D); glEnable(GL_TEXTURE_2D);
// render left iris // render left iris

View file

@ -24,6 +24,7 @@
#include "PerlinFace.h" #include "PerlinFace.h"
#include "world.h" #include "world.h"
#include "devices/SerialInterface.h" #include "devices/SerialInterface.h"
#include "renderer/TextureCache.h"
enum eyeContactTargets { enum eyeContactTargets {
LEFT_EYE, LEFT_EYE,
@ -43,7 +44,7 @@ public:
void init(); void init();
void reset(); void reset();
void simulate(float deltaTime, bool isMine, float gyroCameraSensitivity); void simulate(float deltaTime, bool isMine);
void render(float alpha, bool isMine); void render(float alpha, bool isMine);
void renderMohawk(); void renderMohawk();
@ -63,6 +64,8 @@ public:
glm::quat getOrientation() const; glm::quat getOrientation() const;
glm::quat getCameraOrientation () const; glm::quat getCameraOrientation () const;
const glm::vec3& getAngularVelocity() const { return _angularVelocity; }
void setAngularVelocity(glm::vec3 angularVelocity) { _angularVelocity = angularVelocity; }
float getScale() const { return _scale; } float getScale() const { return _scale; }
glm::vec3 getPosition() const { return _position; } glm::vec3 getPosition() const { return _position; }
@ -115,6 +118,7 @@ private:
float _audioAttack; float _audioAttack;
float _returnSpringScale; //strength of return springs float _returnSpringScale; //strength of return springs
glm::vec3 _bodyRotation; glm::vec3 _bodyRotation;
glm::vec3 _angularVelocity;
bool _renderLookatVectors; bool _renderLookatVectors;
BendyLine _hairTuft[NUM_HAIR_TUFTS]; BendyLine _hairTuft[NUM_HAIR_TUFTS];
bool _mohawkInitialized; bool _mohawkInitialized;
@ -135,8 +139,10 @@ private:
PerlinFace _perlinFace; PerlinFace _perlinFace;
BlendFace _blendFace; BlendFace _blendFace;
QSharedPointer<Texture> _irisTexture;
static ProgramObject _irisProgram; static ProgramObject _irisProgram;
static GLuint _irisTextureID; static DilatedTextureCache _irisTextureCache;
static int _eyePositionLocation; static int _eyePositionLocation;
// private methods // private methods

View file

@ -23,7 +23,6 @@
using namespace std; using namespace std;
const bool USING_AVATAR_GRAVITY = true;
const glm::vec3 DEFAULT_UP_DIRECTION(0.0f, 1.0f, 0.0f); const glm::vec3 DEFAULT_UP_DIRECTION(0.0f, 1.0f, 0.0f);
const float YAW_MAG = 500.0; const float YAW_MAG = 500.0;
const float COLLISION_RADIUS_SCALAR = 1.2; // pertains to avatar-to-avatar collisions const float COLLISION_RADIUS_SCALAR = 1.2; // pertains to avatar-to-avatar collisions
@ -51,7 +50,9 @@ MyAvatar::MyAvatar(Node* owningNode) :
_lastCollisionPosition(0, 0, 0), _lastCollisionPosition(0, 0, 0),
_speedBrakes(false), _speedBrakes(false),
_isThrustOn(false), _isThrustOn(false),
_thrustMultiplier(1.0f) _thrustMultiplier(1.0f),
_moveTarget(0,0,0),
_moveTargetStepCounter(0)
{ {
for (int i = 0; i < MAX_DRIVE_KEYS; i++) { for (int i = 0; i < MAX_DRIVE_KEYS; i++) {
_driveKeys[i] = false; _driveKeys[i] = false;
@ -65,7 +66,12 @@ void MyAvatar::reset() {
_hand.reset(); _hand.reset();
} }
void MyAvatar::simulate(float deltaTime, Transmitter* transmitter, float gyroCameraSensitivity) { void MyAvatar::setMoveTarget(const glm::vec3 moveTarget) {
_moveTarget = moveTarget;
_moveTargetStepCounter = 0;
}
void MyAvatar::simulate(float deltaTime, Transmitter* transmitter) {
glm::quat orientation = getOrientation(); glm::quat orientation = getOrientation();
glm::vec3 front = orientation * IDENTITY_FRONT; glm::vec3 front = orientation * IDENTITY_FRONT;
@ -160,16 +166,16 @@ void MyAvatar::simulate(float deltaTime, Transmitter* transmitter, float gyroCam
_avatarTouch.simulate(deltaTime); _avatarTouch.simulate(deltaTime);
// apply gravity // apply gravity
if (USING_AVATAR_GRAVITY) { // For gravity, always move the avatar by the amount driven by gravity, so that the collision
// For gravity, always move the avatar by the amount driven by gravity, so that the collision // routines will detect it and collide every frame when pulled by gravity to a surface
// routines will detect it and collide every frame when pulled by gravity to a surface const float MIN_DISTANCE_AFTER_COLLISION_FOR_GRAVITY = 0.02f;
const float MIN_DISTANCE_AFTER_COLLISION_FOR_GRAVITY = 0.02f; if (glm::length(_position - _lastCollisionPosition) > MIN_DISTANCE_AFTER_COLLISION_FOR_GRAVITY) {
if (glm::length(_position - _lastCollisionPosition) > MIN_DISTANCE_AFTER_COLLISION_FOR_GRAVITY) { _velocity += _scale * _gravity * (GRAVITY_EARTH * deltaTime);
_velocity += _scale * _gravity * (GRAVITY_EARTH * deltaTime);
}
} }
// Only collide if we are not moving to a target
if (_isCollisionsOn && (glm::length(_moveTarget) < EPSILON)) {
if (_isCollisionsOn) {
Camera* myCamera = Application::getInstance()->getCamera(); Camera* myCamera = Application::getInstance()->getCamera();
if (myCamera->getMode() == CAMERA_MODE_FIRST_PERSON && !OculusManager::isConnected()) { if (myCamera->getMode() == CAMERA_MODE_FIRST_PERSON && !OculusManager::isConnected()) {
@ -211,23 +217,32 @@ void MyAvatar::simulate(float deltaTime, Transmitter* transmitter, float gyroCam
const float STATIC_FRICTION_STRENGTH = _scale * 20.f; const float STATIC_FRICTION_STRENGTH = _scale * 20.f;
applyStaticFriction(deltaTime, _velocity, MAX_STATIC_FRICTION_VELOCITY, STATIC_FRICTION_STRENGTH); applyStaticFriction(deltaTime, _velocity, MAX_STATIC_FRICTION_VELOCITY, STATIC_FRICTION_STRENGTH);
// Damp avatar velocity
const float LINEAR_DAMPING_STRENGTH = 0.5f; const float LINEAR_DAMPING_STRENGTH = 0.5f;
const float SPEED_BRAKE_POWER = _scale * 10.0f; const float SPEED_BRAKE_POWER = _scale * 10.0f;
// Note: PER changed squared damping strength to zero const float SQUARED_DAMPING_STRENGTH = 0.007f;
const float SQUARED_DAMPING_STRENGTH = 0.0f;
float linearDamping = LINEAR_DAMPING_STRENGTH;
const float AVATAR_DAMPING_FACTOR = 120.f;
if (_distanceToNearestAvatar < _scale * PERIPERSONAL_RADIUS) {
linearDamping *= 1.f + AVATAR_DAMPING_FACTOR * (PERIPERSONAL_RADIUS - _distanceToNearestAvatar);
}
if (_speedBrakes) { if (_speedBrakes) {
applyDamping(deltaTime, _velocity, LINEAR_DAMPING_STRENGTH * SPEED_BRAKE_POWER, SQUARED_DAMPING_STRENGTH * SPEED_BRAKE_POWER); applyDamping(deltaTime, _velocity, linearDamping * SPEED_BRAKE_POWER, SQUARED_DAMPING_STRENGTH * SPEED_BRAKE_POWER);
} else { } else {
applyDamping(deltaTime, _velocity, LINEAR_DAMPING_STRENGTH, SQUARED_DAMPING_STRENGTH); applyDamping(deltaTime, _velocity, linearDamping, SQUARED_DAMPING_STRENGTH);
} }
// pitch and roll the body as a function of forward speed and turning delta // pitch and roll the body as a function of forward speed and turning delta
const float BODY_PITCH_WHILE_WALKING = -20.0; const float HIGH_VELOCITY = 10.f;
const float BODY_ROLL_WHILE_TURNING = 0.2; if (glm::length(_velocity) < HIGH_VELOCITY) {
float forwardComponentOfVelocity = glm::dot(getBodyFrontDirection(), _velocity); const float BODY_PITCH_WHILE_WALKING = -20.0;
orientation = orientation * glm::quat(glm::radians(glm::vec3( const float BODY_ROLL_WHILE_TURNING = 0.2;
BODY_PITCH_WHILE_WALKING * deltaTime * forwardComponentOfVelocity, 0.0f, float forwardComponentOfVelocity = glm::dot(getBodyFrontDirection(), _velocity);
BODY_ROLL_WHILE_TURNING * deltaTime * _speed * _bodyYawDelta))); orientation = orientation * glm::quat(glm::radians(glm::vec3(
BODY_PITCH_WHILE_WALKING * deltaTime * forwardComponentOfVelocity, 0.0f,
BODY_ROLL_WHILE_TURNING * deltaTime * _speed * _bodyYawDelta)));
}
// these forces keep the body upright... // these forces keep the body upright...
const float BODY_UPRIGHT_FORCE = _scale * 10.0; const float BODY_UPRIGHT_FORCE = _scale * 10.0;
@ -303,7 +318,7 @@ void MyAvatar::simulate(float deltaTime, Transmitter* transmitter, float gyroCam
_head.setPosition(_bodyBall[ BODY_BALL_HEAD_BASE ].position); _head.setPosition(_bodyBall[ BODY_BALL_HEAD_BASE ].position);
_head.setScale(_scale); _head.setScale(_scale);
_head.setSkinColor(glm::vec3(SKIN_COLOR[0], SKIN_COLOR[1], SKIN_COLOR[2])); _head.setSkinColor(glm::vec3(SKIN_COLOR[0], SKIN_COLOR[1], SKIN_COLOR[2]));
_head.simulate(deltaTime, true, gyroCameraSensitivity); _head.simulate(deltaTime, true);
_hand.simulate(deltaTime, true); _hand.simulate(deltaTime, true);
const float WALKING_SPEED_THRESHOLD = 0.2f; const float WALKING_SPEED_THRESHOLD = 0.2f;
@ -318,7 +333,21 @@ void MyAvatar::simulate(float deltaTime, Transmitter* transmitter, float gyroCam
const float MOVING_SPEED_THRESHOLD = 0.01f; const float MOVING_SPEED_THRESHOLD = 0.01f;
_moving = _speed > MOVING_SPEED_THRESHOLD; _moving = _speed > MOVING_SPEED_THRESHOLD;
// update position by velocity, and subtract the change added earlier for gravity // If a move target is set, update position explicitly
const float MOVE_FINISHED_TOLERANCE = 0.1f;
const float MOVE_SPEED_FACTOR = 2.f;
const int MOVE_TARGET_MAX_STEPS = 250;
if ((glm::length(_moveTarget) > EPSILON) && (_moveTargetStepCounter < MOVE_TARGET_MAX_STEPS)) {
if (glm::length(_position - _moveTarget) > MOVE_FINISHED_TOLERANCE) {
_position += (_moveTarget - _position) * (deltaTime * MOVE_SPEED_FACTOR);
_moveTargetStepCounter++;
} else {
// Move completed
_moveTarget = glm::vec3(0,0,0);
_moveTargetStepCounter = 0;
}
}
_position += _velocity * deltaTime; _position += _velocity * deltaTime;
// Zero thrust out now that we've added it to velocity in this frame // Zero thrust out now that we've added it to velocity in this frame
@ -337,7 +366,13 @@ void MyAvatar::updateFromGyrosAndOrWebcam(bool gyroLook,
if (faceshift->isActive()) { if (faceshift->isActive()) {
estimatedPosition = faceshift->getHeadTranslation(); estimatedPosition = faceshift->getHeadTranslation();
estimatedRotation = safeEulerAngles(faceshift->getHeadRotation()); estimatedRotation = safeEulerAngles(faceshift->getHeadRotation());
// Rotate the body if the head is turned quickly
glm::vec3 headAngularVelocity = faceshift->getHeadAngularVelocity();
const float FACESHIFT_YAW_VIEW_SENSITIVITY = 20.f;
const float FACESHIFT_MIN_YAW_VELOCITY = 1.0f;
if (fabs(headAngularVelocity.y) > FACESHIFT_MIN_YAW_VELOCITY) {
_bodyYawDelta += headAngularVelocity.y * FACESHIFT_YAW_VIEW_SENSITIVITY;
}
} else if (gyros->isActive()) { } else if (gyros->isActive()) {
estimatedRotation = gyros->getEstimatedRotation(); estimatedRotation = gyros->getEstimatedRotation();
@ -639,15 +674,21 @@ void MyAvatar::updateThrust(float deltaTime, Transmitter * transmitter) {
// If thrust keys are being held down, slowly increase thrust to allow reaching great speeds // If thrust keys are being held down, slowly increase thrust to allow reaching great speeds
if (_driveKeys[FWD] || _driveKeys[BACK] || _driveKeys[RIGHT] || _driveKeys[LEFT] || _driveKeys[UP] || _driveKeys[DOWN]) { if (_driveKeys[FWD] || _driveKeys[BACK] || _driveKeys[RIGHT] || _driveKeys[LEFT] || _driveKeys[UP] || _driveKeys[DOWN]) {
const float THRUST_INCREASE_RATE = 1.0; const float THRUST_INCREASE_RATE = 1.05;
_thrustMultiplier *= 1.f + deltaTime * THRUST_INCREASE_RATE; const float MAX_THRUST_MULTIPLIER = 75.0;
//printf("m = %.3f\n", _thrustMultiplier);
if (_thrustMultiplier < MAX_THRUST_MULTIPLIER) {
_thrustMultiplier *= 1.f + deltaTime * THRUST_INCREASE_RATE;
}
} else { } else {
_thrustMultiplier = 1.f; _thrustMultiplier = 1.f;
} }
// Add one time jumping force if requested // Add one time jumping force if requested
if (_shouldJump) { if (_shouldJump) {
_thrust += _scale * THRUST_JUMP * up; if (glm::length(_gravity) > EPSILON) {
_thrust += _scale * THRUST_JUMP * up;
}
_shouldJump = false; _shouldJump = false;
} }

View file

@ -16,7 +16,7 @@ public:
MyAvatar(Node* owningNode = NULL); MyAvatar(Node* owningNode = NULL);
void reset(); void reset();
void simulate(float deltaTime, Transmitter* transmitter, float gyroCameraSensitivity); void simulate(float deltaTime, Transmitter* transmitter);
void updateFromGyrosAndOrWebcam(bool gyroLook, float pitchFromTouch); void updateFromGyrosAndOrWebcam(bool gyroLook, float pitchFromTouch);
void render(bool lookingInMirror, bool renderAvatarBalls); void render(bool lookingInMirror, bool renderAvatarBalls);
void renderScreenTint(ScreenTintLayer layer, Camera& whichCamera); void renderScreenTint(ScreenTintLayer layer, Camera& whichCamera);
@ -31,6 +31,7 @@ public:
void setOrientation(const glm::quat& orientation); void setOrientation(const glm::quat& orientation);
void setNewScale(const float scale); void setNewScale(const float scale);
void setWantCollisionsOn(bool wantCollisionsOn) { _isCollisionsOn = wantCollisionsOn; } void setWantCollisionsOn(bool wantCollisionsOn) { _isCollisionsOn = wantCollisionsOn; }
void setMoveTarget(const glm::vec3 moveTarget);
// getters // getters
float getNewScale() const { return _newScale; } float getNewScale() const { return _newScale; }
@ -73,6 +74,8 @@ public:
bool _isThrustOn; bool _isThrustOn;
float _thrustMultiplier; float _thrustMultiplier;
float _collisionRadius; float _collisionRadius;
glm::vec3 _moveTarget;
int _moveTargetStepCounter;
// private methods // private methods
float getBallRenderAlpha(int ball, bool lookingInMirror) const; float getBallRenderAlpha(int ball, bool lookingInMirror) const;

View file

@ -248,7 +248,11 @@ void PerlinFace::render() {
Head::_irisProgram.bind(); Head::_irisProgram.bind();
glBindTexture(GL_TEXTURE_2D, Head::_irisTextureID); _owningHead->_irisTexture = Head::_irisTextureCache.getTexture(_owningHead->_pupilDilation);
glBindTexture(GL_TEXTURE_2D, _owningHead->_irisTexture->getID());
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
glEnable(GL_TEXTURE_2D); glEnable(GL_TEXTURE_2D);
orientation = _owningHead->getOrientation(); orientation = _owningHead->getOrientation();

View file

@ -142,8 +142,20 @@ void Faceshift::receive(const QByteArray& buffer) {
case fsMsg::MSG_OUT_TRACKING_STATE: { case fsMsg::MSG_OUT_TRACKING_STATE: {
const fsTrackingData& data = static_cast<fsMsgTrackingState*>(msg.get())->tracking_data(); const fsTrackingData& data = static_cast<fsMsgTrackingState*>(msg.get())->tracking_data();
if ((_tracking = data.m_trackingSuccessful)) { if ((_tracking = data.m_trackingSuccessful)) {
_headRotation = glm::quat(data.m_headRotation.w, -data.m_headRotation.x, glm::quat newRotation = glm::quat(data.m_headRotation.w, -data.m_headRotation.x,
data.m_headRotation.y, -data.m_headRotation.z); data.m_headRotation.y, -data.m_headRotation.z);
// Compute angular velocity of the head
glm::quat r = newRotation * glm::inverse(_headRotation);
float theta = 2 * acos(r.w);
if (theta > EPSILON) {
float rMag = glm::length(glm::vec3(r.x, r.y, r.z));
float AVERAGE_FACESHIFT_FRAME_TIME = 0.033f;
_headAngularVelocity = theta / AVERAGE_FACESHIFT_FRAME_TIME * glm::vec3(r.x, r.y, r.z) / rMag;
} else {
_headAngularVelocity = glm::vec3(0,0,0);
}
_headRotation = newRotation;
const float TRANSLATION_SCALE = 0.02f; const float TRANSLATION_SCALE = 0.02f;
_headTranslation = glm::vec3(data.m_headTranslation.x, data.m_headTranslation.y, _headTranslation = glm::vec3(data.m_headTranslation.x, data.m_headTranslation.y,
-data.m_headTranslation.z) * TRANSLATION_SCALE; -data.m_headTranslation.z) * TRANSLATION_SCALE;

View file

@ -30,6 +30,7 @@ public:
bool isActive() const; bool isActive() const;
const glm::quat& getHeadRotation() const { return _headRotation; } const glm::quat& getHeadRotation() const { return _headRotation; }
const glm::vec3& getHeadAngularVelocity() const { return _headAngularVelocity; }
const glm::vec3& getHeadTranslation() const { return _headTranslation; } const glm::vec3& getHeadTranslation() const { return _headTranslation; }
float getEyeGazeLeftPitch() const { return _eyeGazeLeftPitch; } float getEyeGazeLeftPitch() const { return _eyeGazeLeftPitch; }
@ -88,6 +89,7 @@ private:
uint64_t _lastTrackingStateReceived; uint64_t _lastTrackingStateReceived;
glm::quat _headRotation; glm::quat _headRotation;
glm::vec3 _headAngularVelocity;
glm::vec3 _headTranslation; glm::vec3 _headTranslation;
float _eyeGazeLeftPitch; float _eyeGazeLeftPitch;

View file

@ -154,3 +154,44 @@ QOpenGLFramebufferObject* TextureCache::createFramebufferObject() {
return fbo; return fbo;
} }
Texture::Texture() {
glGenTextures(1, &_id);
}
Texture::~Texture() {
glDeleteTextures(1, &_id);
}
DilatedTextureCache::DilatedTextureCache(const QString& filename, int innerRadius, int outerRadius) :
_innerRadius(innerRadius),
_outerRadius(outerRadius)
{
switchToResourcesParentIfRequired();
_image = QImage(filename).convertToFormat(QImage::Format_ARGB32);
}
QSharedPointer<Texture> DilatedTextureCache::getTexture(float dilation) {
QSharedPointer<Texture> texture = _textures.value(dilation);
if (texture.isNull()) {
texture = QSharedPointer<Texture>(new Texture());
QImage dilatedImage = _image;
QPainter painter;
painter.begin(&dilatedImage);
QPainterPath path;
qreal radius = glm::mix(_innerRadius, _outerRadius, dilation);
path.addEllipse(QPointF(_image.width() / 2.0, _image.height() / 2.0), radius, radius);
painter.fillPath(path, Qt::black);
painter.end();
glBindTexture(GL_TEXTURE_2D, texture->getID());
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, dilatedImage.width(), dilatedImage.height(), 1,
GL_BGRA, GL_UNSIGNED_BYTE, dilatedImage.constBits());
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glBindTexture(GL_TEXTURE_2D, 0);
_textures.insert(dilation, texture);
}
return texture;
}

View file

@ -10,7 +10,11 @@
#define __interface__TextureCache__ #define __interface__TextureCache__
#include <QHash> #include <QHash>
#include <QImage>
#include <QMap>
#include <QObject> #include <QObject>
#include <QSharedPointer>
#include <QWeakPointer>
#include "InterfaceConfig.h" #include "InterfaceConfig.h"
@ -62,4 +66,36 @@ private:
QOpenGLFramebufferObject* _tertiaryFramebufferObject; QOpenGLFramebufferObject* _tertiaryFramebufferObject;
}; };
/// A simple object wrapper for an OpenGL texture.
class Texture {
public:
Texture();
~Texture();
GLuint getID() const { return _id; }
private:
GLuint _id;
};
/// Caches textures according to pupillary dilation.
class DilatedTextureCache {
public:
DilatedTextureCache(const QString& filename, int innerRadius, int outerRadius);
/// Returns a pointer to a texture with the requested amount of dilation.
QSharedPointer<Texture> getTexture(float dilation);
private:
QImage _image;
int _innerRadius;
int _outerRadius;
QMap<float, QWeakPointer<Texture> > _textures;
};
#endif /* defined(__interface__TextureCache__) */ #endif /* defined(__interface__TextureCache__) */

View file

@ -0,0 +1,69 @@
//
// VoxelShader.cpp
// interface
//
// Created by Brad Hefta-Gaub on 9/22/13.
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
// include this before QOpenGLFramebufferObject, which includes an earlier version of OpenGL
#include "InterfaceConfig.h"
#include <QOpenGLFramebufferObject>
#include "Application.h"
#include "VoxelShader.h"
#include "ProgramObject.h"
#include "RenderUtil.h"
VoxelShader::VoxelShader()
: _initialized(false)
{
_program = NULL;
}
VoxelShader::~VoxelShader() {
if (_initialized) {
delete _program;
}
}
ProgramObject* VoxelShader::createGeometryShaderProgram(const QString& name) {
ProgramObject* program = new ProgramObject();
program->addShaderFromSourceFile(QGLShader::Vertex, "resources/shaders/passthrough.vert" );
program->addShaderFromSourceFile(QGLShader::Geometry, "resources/shaders/" + name + ".geom" );
program->setGeometryInputType(GL_POINTS);
program->setGeometryOutputType(GL_TRIANGLE_STRIP);
const int VERTICES_PER_FACE = 4;
const int FACES_PER_VOXEL = 6;
const int VERTICES_PER_VOXEL = VERTICES_PER_FACE * FACES_PER_VOXEL;
program->setGeometryOutputVertexCount(VERTICES_PER_VOXEL);
program->link();
return program;
}
void VoxelShader::init() {
if (_initialized) {
qDebug("[ERROR] TestProgram is already initialized.\n");
return;
}
switchToResourcesParentIfRequired();
_program = createGeometryShaderProgram("voxel");
_initialized = true;
}
void VoxelShader::begin() {
_program->bind();
}
void VoxelShader::end() {
_program->release();
}
int VoxelShader::attributeLocation(const char * name) const {
if (_program) {
return _program->attributeLocation(name);
} else {
return -1;
}
}

View file

@ -0,0 +1,46 @@
//
// VoxelShader.h
// interface
//
// Created by Brad Hefta-Gaub on 9/23/13.
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
//
#ifndef __interface__VoxelShader__
#define __interface__VoxelShader__
#include <QObject>
class ProgramObject;
/// A generic full screen glow effect.
class VoxelShader : public QObject {
Q_OBJECT
public:
VoxelShader();
~VoxelShader();
void init();
/// Starts using the voxel geometry shader effect.
void begin();
/// Stops using the voxel geometry shader effect.
void end();
/// Gets access to attributes from the shader program
int attributeLocation(const char * name) const;
static ProgramObject* createGeometryShaderProgram(const QString& name);
public slots:
private:
bool _initialized;
ProgramObject* _program;
};
#endif /* defined(__interface__VoxelShader__) */

View file

@ -211,6 +211,9 @@ int AvatarData::getBroadcastData(unsigned char* destinationBuffer) {
destinationBuffer += _headData->_blendshapeCoefficients.size() * sizeof(float); destinationBuffer += _headData->_blendshapeCoefficients.size() * sizeof(float);
} }
// pupil dilation
destinationBuffer += packFloatToByte(destinationBuffer, _headData->_pupilDilation, 1.0f);
// leap hand data // leap hand data
destinationBuffer += _handData->encodeRemoteData(destinationBuffer); destinationBuffer += _handData->encodeRemoteData(destinationBuffer);
@ -345,7 +348,10 @@ int AvatarData::parseData(unsigned char* sourceBuffer, int numBytes) {
_headData->_blendshapeCoefficients.size() * sizeof(float)); _headData->_blendshapeCoefficients.size() * sizeof(float));
sourceBuffer += _headData->_blendshapeCoefficients.size() * sizeof(float); sourceBuffer += _headData->_blendshapeCoefficients.size() * sizeof(float);
} }
// pupil dilation
sourceBuffer += unpackFloatFromByte(sourceBuffer, _headData->_pupilDilation, 1.0f);
// leap hand data // leap hand data
if (sourceBuffer - startPosition < numBytes) { if (sourceBuffer - startPosition < numBytes) {
// check passed, bytes match // check passed, bytes match

View file

@ -46,6 +46,9 @@ public:
const std::vector<float>& getBlendshapeCoefficients() const { return _blendshapeCoefficients; } const std::vector<float>& getBlendshapeCoefficients() const { return _blendshapeCoefficients; }
float getPupilDilation() const { return _pupilDilation; }
void setPupilDilation(float pupilDilation) { _pupilDilation = pupilDilation; }
void addYaw(float yaw); void addYaw(float yaw);
void addPitch(float pitch); void addPitch(float pitch);
void addRoll(float roll); void addRoll(float roll);
@ -70,6 +73,7 @@ protected:
float _averageLoudness; float _averageLoudness;
float _browAudioLift; float _browAudioLift;
std::vector<float> _blendshapeCoefficients; std::vector<float> _blendshapeCoefficients;
float _pupilDilation;
AvatarData* _owningAvatar; AvatarData* _owningAvatar;
private: private:

View file

@ -109,6 +109,7 @@ void NodeList::setDomainHostname(const QString& domainHostname) {
// reset our _domainIP to the null address so that a lookup happens on next check in // reset our _domainIP to the null address so that a lookup happens on next check in
_domainIP.clear(); _domainIP.clear();
notifyDomainChanged();
} }
void NodeList::timePingReply(sockaddr *nodeAddress, unsigned char *packetData) { void NodeList::timePingReply(sockaddr *nodeAddress, unsigned char *packetData) {
@ -586,6 +587,7 @@ void NodeList::loadData(QSettings *settings) {
if (domainServerHostname.size() > 0) { if (domainServerHostname.size() > 0) {
_domainHostname = domainServerHostname; _domainHostname = domainServerHostname;
notifyDomainChanged();
} }
settings->endGroup(); settings->endGroup();
@ -679,6 +681,21 @@ void NodeListIterator::skipDeadAndStopIncrement() {
} }
} }
void NodeList::addDomainListener(DomainChangeListener* listener) {
_domainListeners.push_back(listener);
QString domain = _domainHostname.isEmpty() ? _domainIP.toString() : _domainHostname;
listener->domainChanged(domain);
}
void NodeList::removeDomainListener(DomainChangeListener* listener) {
for (int i = 0; i < _domainListeners.size(); i++) {
if (_domainListeners[i] == listener) {
_domainListeners.erase(_domainListeners.begin() + i);
return;
}
}
}
void NodeList::addHook(NodeListHook* hook) { void NodeList::addHook(NodeListHook* hook) {
_hooks.push_back(hook); _hooks.push_back(hook);
} }
@ -705,3 +722,9 @@ void NodeList::notifyHooksOfKilledNode(Node* node) {
_hooks[i]->nodeKilled(node); _hooks[i]->nodeKilled(node);
} }
} }
void NodeList::notifyDomainChanged() {
for (int i = 0; i < _domainListeners.size(); i++) {
_domainListeners[i]->domainChanged(_domainHostname);
}
}

View file

@ -56,6 +56,11 @@ public:
virtual void nodeKilled(Node* node) = 0; virtual void nodeKilled(Node* node) = 0;
}; };
class DomainChangeListener {
public:
virtual void domainChanged(QString domain) = 0;
};
class NodeList { class NodeList {
public: public:
static NodeList* createInstance(char ownerType, unsigned short int socketListenPort = 0); static NodeList* createInstance(char ownerType, unsigned short int socketListenPort = 0);
@ -135,6 +140,9 @@ public:
void notifyHooksOfAddedNode(Node* node); void notifyHooksOfAddedNode(Node* node);
void notifyHooksOfKilledNode(Node* node); void notifyHooksOfKilledNode(Node* node);
void addDomainListener(DomainChangeListener* listener);
void removeDomainListener(DomainChangeListener* listener);
private: private:
static NodeList* _sharedInstance; static NodeList* _sharedInstance;
@ -166,6 +174,11 @@ private:
void timePingReply(sockaddr *nodeAddress, unsigned char *packetData); void timePingReply(sockaddr *nodeAddress, unsigned char *packetData);
std::vector<NodeListHook*> _hooks; std::vector<NodeListHook*> _hooks;
std::vector<DomainChangeListener*> _domainListeners;
void resetDomainData(char domainField[], const char* domainData);
void notifyDomainChanged();
void domainLookup();
}; };
class NodeListIterator : public std::iterator<std::input_iterator_tag, Node> { class NodeListIterator : public std::iterator<std::input_iterator_tag, Node> {

View file

@ -20,7 +20,7 @@ PACKET_VERSION versionForPacketType(PACKET_TYPE type) {
return 1; return 1;
case PACKET_TYPE_HEAD_DATA: case PACKET_TYPE_HEAD_DATA:
return 7; return 8;
case PACKET_TYPE_AVATAR_URLS: case PACKET_TYPE_AVATAR_URLS:
return 1; return 1;

View file

@ -32,12 +32,18 @@ const float VOXEL_SIZE_SCALE = TREE_SCALE * 400.0f;
const int NUMBER_OF_CHILDREN = 8; const int NUMBER_OF_CHILDREN = 8;
const int MAX_VOXEL_PACKET_SIZE = 1492; const int MAX_VOXEL_PACKET_SIZE = 1492;
const int MAX_TREE_SLICE_BYTES = 26; const int MAX_TREE_SLICE_BYTES = 26;
const int MAX_VOXELS_PER_SYSTEM = 200000; const int DEFAULT_MAX_VOXELS_PER_SYSTEM = 200000;
const int VERTICES_PER_VOXEL = 24; const int VERTICES_PER_VOXEL = 24; // 6 sides * 4 corners per side
const int VERTEX_POINTS_PER_VOXEL = 3 * VERTICES_PER_VOXEL; const int VERTEX_POINTS_PER_VOXEL = 3 * VERTICES_PER_VOXEL; // xyz for each VERTICE_PER_VOXEL
const int INDICES_PER_VOXEL = 3 * 12; const int INDICES_PER_VOXEL = 3 * 12; // 6 sides * 2 triangles per size * 3 vertices per triangle
const int COLOR_VALUES_PER_VOXEL = NUMBER_OF_COLORS * VERTICES_PER_VOXEL; const int COLOR_VALUES_PER_VOXEL = NUMBER_OF_COLORS * VERTICES_PER_VOXEL;
const int VERTICES_PER_FACE = 4; // 6 sides * 4 corners per side
const int INDICES_PER_FACE = 3 * 2; // 1 side * 2 triangles per size * 3 vertices per triangle
const int GLOBAL_NORMALS_VERTICES_PER_VOXEL = 8; // no need for 3 copies because they don't include normal
const int GLOBAL_NORMALS_VERTEX_POINTS_PER_VOXEL = 3 * GLOBAL_NORMALS_VERTICES_PER_VOXEL;
const int GLOBAL_NORMALS_COLOR_VALUES_PER_VOXEL = NUMBER_OF_COLORS * GLOBAL_NORMALS_VERTICES_PER_VOXEL;
typedef unsigned long int glBufferIndex; typedef unsigned long int glBufferIndex;
const glBufferIndex GLBUFFER_INDEX_UNKNOWN = ULONG_MAX; const glBufferIndex GLBUFFER_INDEX_UNKNOWN = ULONG_MAX;

View file

@ -70,6 +70,7 @@ public:
void printDebugDetails(const char* label) const; void printDebugDetails(const char* label) const;
bool isDirty() const { return _isDirty; } bool isDirty() const { return _isDirty; }
void clearDirtyBit() { _isDirty = false; } void clearDirtyBit() { _isDirty = false; }
void setDirtyBit() { _isDirty = true; }
bool hasChangedSince(uint64_t time) const { return (_lastChanged > time); } bool hasChangedSince(uint64_t time) const { return (_lastChanged > time); }
void markWithChangedTime() { _lastChanged = usecTimestampNow(); } void markWithChangedTime() { _lastChanged = usecTimestampNow(); }
uint64_t getLastChanged() const { return _lastChanged; } uint64_t getLastChanged() const { return _lastChanged; }