mirror of
https://github.com/JulianGro/overte.git
synced 2025-04-25 17:14:59 +02:00
Merge branch 'master' of https://github.com/worklist/hifi into voxel_animation
This commit is contained in:
commit
15587f8b23
23 changed files with 923 additions and 605 deletions
|
@ -63,12 +63,12 @@ if (APPLE)
|
|||
|
||||
endif (APPLE)
|
||||
|
||||
find_package(Qt4 REQUIRED QtCore QtGui QtOpenGL)
|
||||
find_package(Qt4 REQUIRED QtCore QtGui QtNetwork QtOpenGL)
|
||||
include(${QT_USE_FILE})
|
||||
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -isystem ${QT_QTGUI_INCLUDE_DIR}")
|
||||
|
||||
# run qt moc on qt-enabled headers
|
||||
qt4_wrap_cpp(INTERFACE_SRCS src/Application.h)
|
||||
qt4_wrap_cpp(INTERFACE_SRCS src/Application.h src/AvatarVoxelSystem.h)
|
||||
|
||||
# create the executable, make it a bundle on OS X
|
||||
add_executable(${TARGET_NAME} MACOSX_BUNDLE ${INTERFACE_SRCS})
|
||||
|
|
34
interface/resources/shaders/skin_voxels.vert
Normal file
34
interface/resources/shaders/skin_voxels.vert
Normal file
|
@ -0,0 +1,34 @@
|
|||
#version 120
|
||||
|
||||
//
|
||||
// skin_voxels.vert
|
||||
// vertex shader
|
||||
//
|
||||
// Created by Andrzej Kapolka on 5/31/13.
|
||||
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
const int MAX_BONES = 32;
|
||||
const int INDICES_PER_VERTEX = 4;
|
||||
|
||||
uniform mat4 boneMatrices[MAX_BONES];
|
||||
|
||||
attribute vec4 boneIndices;
|
||||
attribute vec4 boneWeights;
|
||||
|
||||
void main(void) {
|
||||
vec4 position = vec4(0.0, 0.0, 0.0, 0.0);
|
||||
vec4 normal = vec4(0.0, 0.0, 0.0, 0.0);
|
||||
for (int i = 0; i < INDICES_PER_VERTEX; i++) {
|
||||
mat4 boneMatrix = boneMatrices[int(boneIndices[i])];
|
||||
float boneWeight = boneWeights[i];
|
||||
position += boneMatrix * gl_Vertex * boneWeight;
|
||||
normal += boneMatrix * vec4(gl_Normal, 0.0) * boneWeight;
|
||||
}
|
||||
position = gl_ModelViewProjectionMatrix * position;
|
||||
normal = normalize(gl_ModelViewMatrix * normal);
|
||||
|
||||
gl_FrontColor = gl_Color * (gl_LightModel.ambient + gl_LightSource[0].ambient +
|
||||
gl_LightSource[0].diffuse * max(0.0, dot(normal, gl_LightSource[0].position)));
|
||||
gl_Position = position;
|
||||
}
|
|
@ -21,16 +21,23 @@
|
|||
#include <glm/gtx/quaternion.hpp>
|
||||
|
||||
#include <QActionGroup>
|
||||
#include <QBoxLayout>
|
||||
#include <QColorDialog>
|
||||
#include <QDialogButtonBox>
|
||||
#include <QDesktopWidget>
|
||||
#include <QFormLayout>
|
||||
#include <QGLWidget>
|
||||
#include <QKeyEvent>
|
||||
#include <QLineEdit>
|
||||
#include <QMainWindow>
|
||||
#include <QMenuBar>
|
||||
#include <QMouseEvent>
|
||||
#include <QNetworkAccessManager>
|
||||
#include <QWheelEvent>
|
||||
#include <QSettings>
|
||||
#include <QShortcut>
|
||||
#include <QTimer>
|
||||
#include <QUrl>
|
||||
#include <QtDebug>
|
||||
#include <QFileDialog>
|
||||
#include <QDesktopServices>
|
||||
|
@ -161,8 +168,6 @@ Application::Application(int& argc, char** argv, timeval &startup_time) :
|
|||
_window->setWindowTitle("Interface");
|
||||
printLog("Interface Startup:\n");
|
||||
|
||||
_voxels.setViewFrustum(&_viewFrustum);
|
||||
|
||||
unsigned int listenPort = AGENT_SOCKET_LISTEN_PORT;
|
||||
const char** constArgv = const_cast<const char**>(argv);
|
||||
const char* portStr = getCmdOption(argc, constArgv, "--listenPort");
|
||||
|
@ -220,6 +225,8 @@ Application::Application(int& argc, char** argv, timeval &startup_time) :
|
|||
_glWidget->setMouseTracking(true);
|
||||
|
||||
// initialization continues in initializeGL when OpenGL context is ready
|
||||
|
||||
QCoreApplication::setOrganizationDomain("highfidelity.io"); // Used by QSettings on OS X
|
||||
}
|
||||
|
||||
void Application::initializeGL() {
|
||||
|
@ -272,8 +279,6 @@ void Application::initializeGL() {
|
|||
connect(idleTimer, SIGNAL(timeout()), SLOT(idle()));
|
||||
idleTimer->start(0);
|
||||
|
||||
readSettings();
|
||||
|
||||
if (_justStarted) {
|
||||
float startupTime = (usecTimestampNow() - usecTimestamp(&_applicationStartupTime))/1000000.0;
|
||||
_justStarted = false;
|
||||
|
@ -735,177 +740,6 @@ void Application::wheelEvent(QWheelEvent* event) {
|
|||
}
|
||||
}
|
||||
|
||||
const char AVATAR_DATA_FILENAME[] = "avatar.ifd";
|
||||
|
||||
void Application::readSettingsFile() {
|
||||
FILE* settingsFile = fopen(AVATAR_DATA_FILENAME, "rt");
|
||||
|
||||
if (settingsFile) {
|
||||
char line[LINE_MAX];
|
||||
|
||||
while (fgets(line, LINE_MAX, settingsFile) != NULL)
|
||||
{
|
||||
if (strcmp(line, " \n") > 0) {
|
||||
char* token = NULL;
|
||||
char* settingLine = NULL;
|
||||
char* toFree = NULL;
|
||||
|
||||
settingLine = strdup(line);
|
||||
|
||||
if (settingLine != NULL) {
|
||||
toFree = settingLine;
|
||||
|
||||
int i = 0;
|
||||
|
||||
char key[128];
|
||||
char value[128];
|
||||
|
||||
|
||||
while ((token = strsep(&settingLine, "=")) != NULL)
|
||||
{
|
||||
switch (i) {
|
||||
case 0:
|
||||
strcpy(key, token);
|
||||
_settingsTable[key] = "";
|
||||
break;
|
||||
|
||||
case 1:
|
||||
strcpy(value, token);
|
||||
_settingsTable[key] = token;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
free(toFree);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fclose(settingsFile);
|
||||
}
|
||||
}
|
||||
|
||||
void Application::saveSettingsFile() {
|
||||
FILE* settingsFile = fopen(AVATAR_DATA_FILENAME, "wt");
|
||||
|
||||
if (settingsFile) {
|
||||
for (std::map<std::string, std::string>::iterator i = _settingsTable.begin(); i != _settingsTable.end(); i++)
|
||||
{
|
||||
fprintf(settingsFile, "\n%s=%s", i->first.data(), i->second.data());
|
||||
}
|
||||
}
|
||||
|
||||
fclose(settingsFile);
|
||||
}
|
||||
|
||||
bool Application::getSetting(const char* setting, bool& value, const bool defaultSetting) const {
|
||||
std::map<std::string, std::string>::const_iterator iter = _settingsTable.find(setting);
|
||||
|
||||
if (iter != _settingsTable.end()) {
|
||||
int readBool;
|
||||
|
||||
int res = sscanf(iter->second.data(), "%d", &readBool);
|
||||
|
||||
const char EXPECTED_ITEMS = 1;
|
||||
|
||||
if (res == EXPECTED_ITEMS) {
|
||||
if (readBool == 1) {
|
||||
value = true;
|
||||
} else if (readBool == 0) {
|
||||
value = false;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
value = defaultSetting;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Application::getSetting(const char* setting, float& value, const float defaultSetting) const {
|
||||
std::map<std::string, std::string>::const_iterator iter = _settingsTable.find(setting);
|
||||
|
||||
if (iter != _settingsTable.end()) {
|
||||
float readFloat;
|
||||
|
||||
int res = sscanf(iter->second.data(), "%f", &readFloat);
|
||||
|
||||
const char EXPECTED_ITEMS = 1;
|
||||
|
||||
if (res == EXPECTED_ITEMS) {
|
||||
if (!isnan(readFloat)) {
|
||||
value = readFloat;
|
||||
} else {
|
||||
value = defaultSetting;
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
value = defaultSetting;
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
value = defaultSetting;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Application::getSetting(const char* setting, glm::vec3& value, const glm::vec3& defaultSetting) const {
|
||||
std::map<std::string, std::string>::const_iterator iter = _settingsTable.find(setting);
|
||||
|
||||
if (iter != _settingsTable.end()) {
|
||||
glm::vec3 readVec;
|
||||
|
||||
int res = sscanf(iter->second.data(), "%f,%f,%f", &readVec.x, &readVec.y, &readVec.z);
|
||||
|
||||
const char EXPECTED_ITEMS = 3;
|
||||
|
||||
if (res == EXPECTED_ITEMS) {
|
||||
if (!isnan(readVec.x) && !isnan(readVec.y) && !isnan(readVec.z)) {
|
||||
value = readVec;
|
||||
} else {
|
||||
value = defaultSetting;
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
value = defaultSetting;
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
value = defaultSetting;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
const short MAX_SETTINGS_LENGTH = 128;
|
||||
|
||||
void Application::setSetting(const char* setting, const bool value) {
|
||||
char settingValues[MAX_SETTINGS_LENGTH];
|
||||
sprintf(settingValues, "%d", value);
|
||||
_settingsTable[setting] = settingValues;
|
||||
}
|
||||
|
||||
void Application::setSetting(const char* setting, const float value) {
|
||||
char settingValues[MAX_SETTINGS_LENGTH];
|
||||
sprintf(settingValues, "%f", value);
|
||||
_settingsTable[setting] = settingValues;
|
||||
}
|
||||
|
||||
void Application::setSetting(const char* setting, const glm::vec3& value) {
|
||||
char settingValues[MAX_SETTINGS_LENGTH];
|
||||
sprintf(settingValues, "%f,%f,%f", value.x, value.y, value.z);
|
||||
_settingsTable[setting] = settingValues;
|
||||
}
|
||||
|
||||
// Every second, check the frame rates and other stuff
|
||||
void Application::timer() {
|
||||
gettimeofday(&_timerEnd, NULL);
|
||||
|
@ -1078,6 +912,9 @@ void Application::idle() {
|
|||
for(AgentList::iterator agent = agentList->begin(); agent != agentList->end(); agent++) {
|
||||
if (agent->getLinkedData() != NULL) {
|
||||
Avatar *avatar = (Avatar *)agent->getLinkedData();
|
||||
if (!avatar->isInitialized()) {
|
||||
avatar->init();
|
||||
}
|
||||
avatar->simulate(deltaTime, NULL);
|
||||
avatar->setMouseRay(mouseRayOrigin, mouseRayDirection);
|
||||
}
|
||||
|
@ -1135,7 +972,10 @@ void Application::terminate() {
|
|||
// Close serial port
|
||||
// close(serial_fd);
|
||||
|
||||
saveSettings();
|
||||
if (_autosave) {
|
||||
saveSettings();
|
||||
_settings->sync();
|
||||
}
|
||||
|
||||
if (_enableNetworkThread) {
|
||||
_stopNetworkReceiveThread = true;
|
||||
|
@ -1143,6 +983,32 @@ void Application::terminate() {
|
|||
}
|
||||
}
|
||||
|
||||
void Application::editPreferences() {
|
||||
QDialog dialog(_glWidget);
|
||||
dialog.setWindowTitle("Interface Preferences");
|
||||
QBoxLayout* layout = new QBoxLayout(QBoxLayout::TopToBottom);
|
||||
dialog.setLayout(layout);
|
||||
|
||||
QFormLayout* form = new QFormLayout();
|
||||
layout->addLayout(form, 1);
|
||||
|
||||
QLineEdit* avatarURL = new QLineEdit(_settings->value("avatarURL").toString());
|
||||
avatarURL->setMinimumWidth(400);
|
||||
form->addRow("Avatar URL:", avatarURL);
|
||||
|
||||
QDialogButtonBox* buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
|
||||
dialog.connect(buttons, SIGNAL(accepted()), SLOT(accept()));
|
||||
dialog.connect(buttons, SIGNAL(rejected()), SLOT(reject()));
|
||||
layout->addWidget(buttons);
|
||||
|
||||
if (dialog.exec() != QDialog::Accepted) {
|
||||
return;
|
||||
}
|
||||
QUrl url(avatarURL->text());
|
||||
_settings->setValue("avatarURL", url);
|
||||
_myAvatar.getVoxels()->loadVoxelsFromURL(url);
|
||||
}
|
||||
|
||||
void Application::pair() {
|
||||
PairingHandler::sendPairRequest();
|
||||
}
|
||||
|
@ -1469,6 +1335,9 @@ void Application::initMenu() {
|
|||
QMenu* fileMenu = menuBar->addMenu("File");
|
||||
fileMenu->addAction("Quit", this, SLOT(quit()), Qt::CTRL | Qt::Key_Q);
|
||||
|
||||
QMenu* editMenu = menuBar->addMenu("Edit");
|
||||
editMenu->addAction("Preferences...", this, SLOT(editPreferences()));
|
||||
|
||||
QMenu* pairMenu = menuBar->addMenu("Pair");
|
||||
pairMenu->addAction("Pair", this, SLOT(pair()));
|
||||
|
||||
|
@ -1509,9 +1378,7 @@ void Application::initMenu() {
|
|||
renderMenu->addAction("First Person", this, SLOT(setRenderFirstPerson(bool)), Qt::Key_P)->setCheckable(true);
|
||||
|
||||
QMenu* toolsMenu = menuBar->addMenu("Tools");
|
||||
|
||||
(_renderStatsOn = toolsMenu->addAction("Stats"))->setCheckable(true);
|
||||
|
||||
_renderStatsOn->setShortcut(Qt::Key_Slash);
|
||||
(_logOn = toolsMenu->addAction("Log"))->setCheckable(true);
|
||||
_logOn->setChecked(false);
|
||||
|
@ -1579,6 +1446,17 @@ void Application::initMenu() {
|
|||
debugMenu->addAction("Wants Res-In", this, SLOT(setWantsResIn(bool)))->setCheckable(true);
|
||||
debugMenu->addAction("Wants Monochrome", this, SLOT(setWantsMonochrome(bool)))->setCheckable(true);
|
||||
debugMenu->addAction("Wants View Delta Sending", this, SLOT(setWantsDelta(bool)))->setCheckable(true);
|
||||
|
||||
QMenu* settingsMenu = menuBar->addMenu("Settings");
|
||||
(_settingsAutosave = settingsMenu->addAction("Autosave", this, SLOT(setAutosave(bool))))->setCheckable(true);
|
||||
_settingsAutosave->setChecked(true);
|
||||
settingsMenu->addAction("Load settings", this, SLOT(loadSettings()));
|
||||
settingsMenu->addAction("Save settings", this, SLOT(saveSettings()));
|
||||
settingsMenu->addAction("Import settings", this, SLOT(importSettings()));
|
||||
settingsMenu->addAction("Export settings", this, SLOT(exportSettings()));
|
||||
|
||||
_networkAccessManager = new QNetworkAccessManager(this);
|
||||
_settings = new QSettings("High Fidelity", "Interface", this);
|
||||
}
|
||||
|
||||
void Application::updateFrustumRenderModeAction() {
|
||||
|
@ -1613,8 +1491,6 @@ void Application::initDisplay() {
|
|||
|
||||
void Application::init() {
|
||||
_voxels.init();
|
||||
_voxels.setViewerAvatar(&_myAvatar);
|
||||
_voxels.setCamera(&_myCamera);
|
||||
|
||||
_environment.init();
|
||||
|
||||
|
@ -1625,11 +1501,14 @@ void Application::init() {
|
|||
|
||||
_stars.readInput(STAR_FILE, STAR_CACHE_FILE, 0);
|
||||
|
||||
_myAvatar.init();
|
||||
_myAvatar.setPosition(START_LOCATION);
|
||||
_myCamera.setMode(CAMERA_MODE_THIRD_PERSON );
|
||||
_myCamera.setModeShiftRate(1.0f);
|
||||
_myAvatar.setDisplayingLookatVectors(false);
|
||||
|
||||
_myAvatar.getVoxels()->loadVoxelsFromURL(_settings->value("avatarURL").toUrl());
|
||||
|
||||
QCursor::setPos(_headMouseX, _headMouseY);
|
||||
|
||||
OculusManager::connect();
|
||||
|
@ -1639,6 +1518,8 @@ void Application::init() {
|
|||
|
||||
gettimeofday(&_timerStart, NULL);
|
||||
gettimeofday(&_lastTimeIdle, NULL);
|
||||
|
||||
loadSettings();
|
||||
}
|
||||
|
||||
void Application::updateAvatar(float deltaTime) {
|
||||
|
@ -1910,7 +1791,7 @@ void Application::displayOculus(Camera& whichCamera) {
|
|||
|
||||
glPopMatrix();
|
||||
}
|
||||
|
||||
|
||||
void Application::displaySide(Camera& whichCamera) {
|
||||
// transform by eye offset
|
||||
|
||||
|
@ -2020,6 +1901,9 @@ void Application::displaySide(Camera& whichCamera) {
|
|||
for (AgentList::iterator agent = agentList->begin(); agent != agentList->end(); agent++) {
|
||||
if (agent->getLinkedData() != NULL && agent->getType() == AGENT_TYPE_AVATAR) {
|
||||
Avatar *avatar = (Avatar *)agent->getLinkedData();
|
||||
if (!avatar->isInitialized()) {
|
||||
avatar->init();
|
||||
}
|
||||
avatar->render(false);
|
||||
}
|
||||
}
|
||||
|
@ -2552,95 +2436,82 @@ void* Application::networkReceive(void* args) {
|
|||
return NULL;
|
||||
}
|
||||
|
||||
void Application::saveSettings()
|
||||
{
|
||||
// Handle any persistent settings saving here when we get a call to terminate.
|
||||
// This should probably be moved to a map stored in memory at some point to cache settings.
|
||||
_myAvatar.writeAvatarDataToFile();
|
||||
|
||||
setSetting("_gyroLook", _gyroLook->isChecked());
|
||||
|
||||
setSetting("_mouseLook", _mouseLook->isChecked());
|
||||
|
||||
setSetting("_transmitterDrives", _transmitterDrives->isChecked());
|
||||
|
||||
setSetting("_renderVoxels", _renderVoxels->isChecked());
|
||||
|
||||
setSetting("_renderVoxelTextures", _renderVoxelTextures->isChecked());
|
||||
|
||||
setSetting("_renderStarsOn", _renderStarsOn->isChecked());
|
||||
|
||||
setSetting("_renderAtmosphereOn", _renderAtmosphereOn->isChecked());
|
||||
|
||||
setSetting("_renderAvatarsOn", _renderAvatarsOn->isChecked());
|
||||
|
||||
setSetting("_renderStatsOn", _renderStatsOn->isChecked());
|
||||
|
||||
setSetting("_renderFrameTimerOn", _renderFrameTimerOn->isChecked());
|
||||
|
||||
setSetting("_renderLookatOn", _renderLookatOn->isChecked());
|
||||
|
||||
setSetting("_logOn", _logOn->isChecked());
|
||||
|
||||
setSetting("_frustumOn", _frustumOn->isChecked());
|
||||
|
||||
setSetting("_viewFrustumFromOffset", _viewFrustumFromOffset->isChecked());
|
||||
|
||||
setSetting("_cameraFrustum", _cameraFrustum->isChecked());
|
||||
|
||||
saveSettingsFile();
|
||||
void Application::scanMenuBar(settingsAction modifySetting, QSettings* set) {
|
||||
if (!_window->menuBar()) {
|
||||
return;
|
||||
}
|
||||
|
||||
QList<QMenu*> menus = _window->menuBar()->findChildren<QMenu *>();
|
||||
|
||||
for (QList<QMenu *>::const_iterator it = menus.begin(); menus.end() != it; ++it) {
|
||||
scanMenu(*it, modifySetting, set);
|
||||
}
|
||||
}
|
||||
|
||||
void Application::readSettings()
|
||||
{
|
||||
readSettingsFile();
|
||||
_myAvatar.readAvatarDataFromFile();
|
||||
|
||||
bool settingState;
|
||||
getSetting("_gyroLook", settingState, _gyroLook->isChecked());
|
||||
_gyroLook->setChecked(settingState);
|
||||
|
||||
getSetting("_mouseLook", settingState, _mouseLook->isChecked());
|
||||
_mouseLook->setChecked(settingState);
|
||||
|
||||
getSetting("_transmitterDrives", settingState, _transmitterDrives->isChecked());
|
||||
_transmitterDrives->setChecked(settingState);
|
||||
|
||||
getSetting("_renderVoxels", settingState, _renderVoxels->isChecked());
|
||||
_renderVoxels->setChecked(settingState);
|
||||
|
||||
getSetting("_renderVoxelTextures", settingState, _renderVoxelTextures->isChecked());
|
||||
_renderVoxelTextures->setChecked(settingState);
|
||||
|
||||
getSetting("_renderStarsOn", settingState, _renderStarsOn->isChecked());
|
||||
_renderStarsOn->setChecked(settingState);
|
||||
|
||||
getSetting("_renderAtmosphereOn", settingState, _renderAtmosphereOn->isChecked());
|
||||
_renderAtmosphereOn->setChecked(settingState);
|
||||
|
||||
getSetting("_renderAvatarsOn", settingState, _renderAvatarsOn->isChecked());
|
||||
_renderAvatarsOn->setChecked(settingState);
|
||||
|
||||
getSetting("_renderStatsOn", settingState, _renderStatsOn->isChecked());
|
||||
_renderStatsOn->setChecked(settingState);
|
||||
|
||||
getSetting("_renderFrameTimerOn", settingState, _renderFrameTimerOn->isChecked());
|
||||
_renderFrameTimerOn->setChecked(settingState);
|
||||
|
||||
getSetting("_renderLookatOn", settingState, _renderLookatOn->isChecked());
|
||||
_renderLookatOn->setChecked(settingState);
|
||||
|
||||
getSetting("_logOn", settingState, _logOn->isChecked());
|
||||
_logOn->setChecked(settingState);
|
||||
|
||||
getSetting("_frustumOn", settingState, _frustumOn->isChecked());
|
||||
_frustumOn->setChecked(settingState);
|
||||
|
||||
getSetting("_viewFrustumFromOffset", settingState, _viewFrustumFromOffset->isChecked());
|
||||
_viewFrustumFromOffset->setChecked(settingState);
|
||||
|
||||
getSetting("_cameraFrustum", settingState, _cameraFrustum->isChecked());
|
||||
_cameraFrustum->setChecked(settingState);
|
||||
|
||||
void Application::scanMenu(QMenu* menu, settingsAction modifySetting, QSettings* set) {
|
||||
QList<QAction*> actions = menu->actions();
|
||||
|
||||
set->beginGroup(menu->title());
|
||||
for (QList<QAction *>::const_iterator it = actions.begin(); actions.end() != it; ++it) {
|
||||
if ((*it)->menu()) {
|
||||
scanMenu((*it)->menu(), modifySetting, set);
|
||||
}
|
||||
if ((*it)->isCheckable()) {
|
||||
modifySetting(set, *it);
|
||||
}
|
||||
}
|
||||
set->endGroup();
|
||||
}
|
||||
|
||||
void Application::loadAction(QSettings* set, QAction* action) {
|
||||
action->setChecked(set->value(action->text(), action->isChecked()).toBool());
|
||||
}
|
||||
|
||||
void Application::saveAction(QSettings* set, QAction* action) {
|
||||
set->setValue(action->text(), action->isChecked());
|
||||
}
|
||||
|
||||
void Application::setAutosave(bool wantsAutosave) {
|
||||
_autosave = wantsAutosave;
|
||||
}
|
||||
|
||||
void Application::loadSettings(QSettings* set) {
|
||||
if (!set) set = getSettings();
|
||||
|
||||
scanMenuBar(&Application::loadAction, set);
|
||||
getAvatar()->loadData(set);
|
||||
}
|
||||
|
||||
void Application::saveSettings(QSettings* set) {
|
||||
if (!set) set = getSettings();
|
||||
|
||||
scanMenuBar(&Application::saveAction, set);
|
||||
getAvatar()->saveData(set);
|
||||
}
|
||||
|
||||
void Application::importSettings() {
|
||||
QString locationDir(QDesktopServices::displayName(QDesktopServices::DesktopLocation));
|
||||
QString fileName = QFileDialog::getOpenFileName(_window,
|
||||
tr("Open .ini config file"),
|
||||
locationDir,
|
||||
tr("Text files (*.ini)"));
|
||||
if (fileName != "") {
|
||||
QSettings tmp(fileName, QSettings::IniFormat);
|
||||
loadSettings(&tmp);
|
||||
}
|
||||
}
|
||||
|
||||
void Application::exportSettings() {
|
||||
QString locationDir(QDesktopServices::displayName(QDesktopServices::DesktopLocation));
|
||||
QString fileName = QFileDialog::getSaveFileName(_window,
|
||||
tr("Save .ini config file"),
|
||||
locationDir,
|
||||
tr("Text files (*.ini)"));
|
||||
if (fileName != "") {
|
||||
QSettings tmp(fileName, QSettings::IniFormat);
|
||||
saveSettings(&tmp);
|
||||
tmp.sync();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -15,6 +15,8 @@
|
|||
|
||||
#include <QApplication>
|
||||
#include <QAction>
|
||||
#include <QSettings>
|
||||
#include <QList>
|
||||
|
||||
#include <AgentList.h>
|
||||
|
||||
|
@ -37,6 +39,8 @@ class QGLWidget;
|
|||
class QKeyEvent;
|
||||
class QMainWindow;
|
||||
class QMouseEvent;
|
||||
class QNetworkAccessManager;
|
||||
class QSettings;
|
||||
class QWheelEvent;
|
||||
|
||||
class Agent;
|
||||
|
@ -65,67 +69,22 @@ public:
|
|||
|
||||
Avatar* getAvatar() { return &_myAvatar; }
|
||||
Camera* getCamera() { return &_myCamera; }
|
||||
ViewFrustum* getViewFrustum() { return &_viewFrustum; }
|
||||
VoxelSystem* getVoxels() { return &_voxels; }
|
||||
QSettings* getSettings() { return _settings; }
|
||||
Environment* getEnvironment() { return &_environment; }
|
||||
bool shouldEchoAudio() { return _echoAudioMode->isChecked(); }
|
||||
|
||||
/*!
|
||||
@fn getSettingBool
|
||||
@brief A function for getting boolean settings from the settings file.
|
||||
@param settingName The desired setting to get the value for.
|
||||
@param boolSetting The referenced variable where the setting will be stored.
|
||||
@param defaultSetting The default setting to assign to boolSetting if this function fails to find the appropriate setting. Defaults to false.
|
||||
*/
|
||||
bool getSetting(const char* setting, bool &value, const bool defaultSetting = false) const;
|
||||
QNetworkAccessManager* getNetworkAccessManager() { return _networkAccessManager; }
|
||||
|
||||
/*!
|
||||
@fn getSettingFloat
|
||||
@brief A function for getting float settings from the settings file.
|
||||
@param settingName The desired setting to get the value for.
|
||||
@param floatSetting The referenced variable where the setting will be stored.
|
||||
@param defaultSetting The default setting to assign to boolSetting if this function fails to find the appropriate setting. Defaults to 0.0f.
|
||||
*/
|
||||
bool getSetting(const char* setting, float &value, const float defaultSetting = 0.0f) const;
|
||||
|
||||
/*!
|
||||
@fn getSettingVec3
|
||||
@brief A function for getting boolean settings from the settings file.
|
||||
@param settingName The desired setting to get the value for.
|
||||
@param vecSetting The referenced variable where the setting will be stored.
|
||||
@param defaultSetting The default setting to assign to boolSetting if this function fails to find the appropriate setting. Defaults to <0.0f, 0.0f, 0.0f>
|
||||
*/
|
||||
bool getSetting(const char* setting, glm::vec3 &value, const glm::vec3& defaultSetting = glm::vec3(0.0f, 0.0f, 0.0f)) const;
|
||||
|
||||
/*!
|
||||
@fn setSettingBool
|
||||
@brief A function for setting boolean setting values when saving the settings file.
|
||||
@param settingName The desired setting to populate a value for.
|
||||
@param boolSetting The value to set.
|
||||
*/
|
||||
void setSetting(const char* setting, const bool value);
|
||||
|
||||
/*!
|
||||
@fn setSettingFloat
|
||||
@brief A function for setting boolean setting values when saving the settings file.
|
||||
@param settingName The desired setting to populate a value for.
|
||||
@param floatSetting The value to set.
|
||||
*/
|
||||
void setSetting(const char* setting, const float value);
|
||||
|
||||
/*!
|
||||
@fn setSettingVec3
|
||||
@brief A function for setting boolean setting values when saving the settings file.
|
||||
@param settingName The desired setting to populate a value for.
|
||||
@param vecSetting The value to set.
|
||||
*/
|
||||
void setSetting(const char* setting, const glm::vec3& value);
|
||||
|
||||
private slots:
|
||||
|
||||
void timer();
|
||||
void idle();
|
||||
void terminate();
|
||||
|
||||
void editPreferences();
|
||||
|
||||
void pair();
|
||||
|
||||
void setHead(bool head);
|
||||
|
@ -154,12 +113,16 @@ private slots:
|
|||
void decreaseVoxelSize();
|
||||
void increaseVoxelSize();
|
||||
void chooseVoxelPaintColor();
|
||||
void setAutosave(bool wantsAutosave);
|
||||
void loadSettings(QSettings* set = NULL);
|
||||
void saveSettings(QSettings* set = NULL);
|
||||
void importSettings();
|
||||
void exportSettings();
|
||||
void exportVoxels();
|
||||
void importVoxels();
|
||||
void cutVoxels();
|
||||
void copyVoxels();
|
||||
void pasteVoxels();
|
||||
|
||||
private:
|
||||
|
||||
static bool sendVoxelsOperation(VoxelNode* node, void* extraData);
|
||||
|
@ -196,13 +159,13 @@ private:
|
|||
static void attachNewHeadToAgent(Agent *newAgent);
|
||||
static void* networkReceive(void* args);
|
||||
|
||||
// These two functions are technically not necessary, but they help keep things in one place.
|
||||
void readSettings(); //! This function is largely to help consolidate getting settings in one place.
|
||||
void saveSettings(); //! This function is to consolidate any settings setting in one place.
|
||||
|
||||
void readSettingsFile(); //! This function reads data from the settings file, splitting data into key value pairs using '=' as a delimiter.
|
||||
void saveSettingsFile(); //! This function writes all changes in the settings table to the settings file, serializing all settings added through the setSetting functions.
|
||||
|
||||
// methodes handling menu settings
|
||||
typedef void(*settingsAction)(QSettings*, QAction*);
|
||||
static void loadAction(QSettings* set, QAction* action);
|
||||
static void saveAction(QSettings* set, QAction* action);
|
||||
void scanMenuBar(settingsAction modifySetting, QSettings* set);
|
||||
void scanMenu(QMenu* menu, settingsAction modifySetting, QSettings* set);
|
||||
|
||||
QMainWindow* _window;
|
||||
QGLWidget* _glWidget;
|
||||
|
||||
|
@ -234,6 +197,9 @@ private:
|
|||
QAction* _cameraFrustum; // which frustum to look at
|
||||
QAction* _fullScreenMode; // whether we are in full screen mode
|
||||
QAction* _frustumRenderModeAction;
|
||||
QAction* _settingsAutosave; // Whether settings are saved automatically
|
||||
|
||||
QNetworkAccessManager* _networkAccessManager;
|
||||
|
||||
SerialInterface _serialPort;
|
||||
bool _displayLevels;
|
||||
|
@ -326,11 +292,8 @@ private:
|
|||
int _bytesPerSecond;
|
||||
int _bytesCount;
|
||||
|
||||
/*!
|
||||
* Store settings in a map, storing keys and values as strings.
|
||||
* Interpret values as needed on demand. through the appropriate getters and setters.
|
||||
*/
|
||||
std::map<std::string, std::string> _settingsTable;
|
||||
QSettings* _settings; // Contain Menu settings and Avatar data
|
||||
bool _autosave; // True if the autosave is on.
|
||||
};
|
||||
|
||||
#endif /* defined(__interface__Application__) */
|
||||
|
|
|
@ -55,7 +55,6 @@ const float SKIN_COLOR[] = {1.0, 0.84, 0.66};
|
|||
const float DARK_SKIN_COLOR[] = {0.9, 0.78, 0.63};
|
||||
const int NUM_BODY_CONE_SIDES = 9;
|
||||
|
||||
|
||||
bool usingBigSphereCollisionTest = true;
|
||||
|
||||
float chatMessageScale = 0.0015;
|
||||
|
@ -63,6 +62,7 @@ float chatMessageHeight = 0.20;
|
|||
|
||||
Avatar::Avatar(Agent* owningAgent) :
|
||||
AvatarData(owningAgent),
|
||||
_initialized(false),
|
||||
_head(this),
|
||||
_ballSpringsInitialized(false),
|
||||
_TEST_bigSphereRadius(0.5f),
|
||||
|
@ -88,7 +88,8 @@ Avatar::Avatar(Agent* owningAgent) :
|
|||
_mouseRayDirection(0.0f, 0.0f, 0.0f),
|
||||
_interactingOther(NULL),
|
||||
_cumulativeMouseYaw(0.0f),
|
||||
_isMouseTurningRight(false)
|
||||
_isMouseTurningRight(false),
|
||||
_voxels(this)
|
||||
{
|
||||
|
||||
// give the pointer to our head to inherited _headData variable from AvatarData
|
||||
|
@ -264,6 +265,11 @@ Avatar::~Avatar() {
|
|||
delete _balls;
|
||||
}
|
||||
|
||||
void Avatar::init() {
|
||||
_voxels.init();
|
||||
_initialized = true;
|
||||
}
|
||||
|
||||
void Avatar::reset() {
|
||||
_head.reset();
|
||||
}
|
||||
|
@ -423,9 +429,9 @@ void Avatar::simulate(float deltaTime, Transmitter* transmitter) {
|
|||
}
|
||||
|
||||
glm::quat orientation = getOrientation();
|
||||
glm::vec3 front = orientation * AVATAR_FRONT;
|
||||
glm::vec3 right = orientation * AVATAR_RIGHT;
|
||||
glm::vec3 up = orientation * AVATAR_UP;
|
||||
glm::vec3 front = orientation * IDENTITY_FRONT;
|
||||
glm::vec3 right = orientation * IDENTITY_RIGHT;
|
||||
glm::vec3 up = orientation * IDENTITY_UP;
|
||||
|
||||
// driving the avatar around should only apply if this is my avatar (as opposed to an avatar being driven remotely)
|
||||
const float THRUST_MAG = 600.0f;
|
||||
|
@ -645,9 +651,9 @@ void Avatar::updateHandMovementAndTouching(float deltaTime) {
|
|||
glm::quat orientation = getOrientation();
|
||||
|
||||
// reset hand and arm positions according to hand movement
|
||||
glm::vec3 right = orientation * AVATAR_RIGHT;
|
||||
glm::vec3 up = orientation * AVATAR_UP;
|
||||
glm::vec3 front = orientation * AVATAR_FRONT;
|
||||
glm::vec3 right = orientation * IDENTITY_RIGHT;
|
||||
glm::vec3 up = orientation * IDENTITY_UP;
|
||||
glm::vec3 front = orientation * IDENTITY_FRONT;
|
||||
|
||||
glm::vec3 transformedHandMovement
|
||||
= right * _movedHandOffset.x * 2.0f
|
||||
|
@ -1015,20 +1021,24 @@ void Avatar::updateBodyBalls(float deltaTime) {
|
|||
if (glm::length(_position - _bodyBall[BODY_BALL_PELVIS].position) > BEYOND_BODY_SPRING_RANGE) {
|
||||
resetBodyBalls();
|
||||
}
|
||||
glm::quat orientation = getOrientation();
|
||||
glm::vec3 jointDirection = orientation * JOINT_DIRECTION;
|
||||
for (int b = 0; b < NUM_AVATAR_BODY_BALLS; b++) {
|
||||
|
||||
glm::vec3 springVector;
|
||||
float length = 0.0f;
|
||||
if (_ballSpringsInitialized) {
|
||||
|
||||
// apply spring forces
|
||||
glm::vec3 springVector(_bodyBall[b].position);
|
||||
|
||||
springVector = _bodyBall[b].position;
|
||||
|
||||
if (b == BODY_BALL_PELVIS) {
|
||||
springVector -= _position;
|
||||
} else {
|
||||
springVector -= _bodyBall[_bodyBall[b].parentBall].position;
|
||||
}
|
||||
|
||||
float length = glm::length(springVector);
|
||||
length = glm::length(springVector);
|
||||
|
||||
if (length > 0.0f) { // to avoid divide by zero
|
||||
glm::vec3 springDirection = springVector / length;
|
||||
|
@ -1066,6 +1076,14 @@ void Avatar::updateBodyBalls(float deltaTime) {
|
|||
|
||||
// update position by velocity...
|
||||
_bodyBall[b].position += _bodyBall[b].velocity * deltaTime;
|
||||
|
||||
// update rotation
|
||||
const float SMALL_SPRING_LENGTH = 0.001f; // too-small springs can change direction rapidly
|
||||
if (_skeleton.joint[b].parent == AVATAR_JOINT_NULL || length < SMALL_SPRING_LENGTH) {
|
||||
_bodyBall[b].rotation = orientation * _skeleton.joint[_bodyBall[b].parentJoint].absoluteBindPoseRotation;
|
||||
} else {
|
||||
_bodyBall[b].rotation = rotationBetween(jointDirection, springVector) * orientation;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1107,14 +1125,14 @@ void Avatar::updateArmIKAndConstraints(float deltaTime) {
|
|||
|
||||
glm::quat Avatar::computeRotationFromBodyToWorldUp(float proportion) const {
|
||||
glm::quat orientation = getOrientation();
|
||||
glm::vec3 currentUp = orientation * AVATAR_UP;
|
||||
glm::vec3 currentUp = orientation * IDENTITY_UP;
|
||||
float angle = glm::degrees(acosf(glm::clamp(glm::dot(currentUp, _worldUpDirection), -1.0f, 1.0f)));
|
||||
if (angle < EPSILON) {
|
||||
return glm::quat();
|
||||
}
|
||||
glm::vec3 axis;
|
||||
if (angle > 179.99f) { // 180 degree rotation; must use another axis
|
||||
axis = orientation * AVATAR_RIGHT;
|
||||
axis = orientation * IDENTITY_RIGHT;
|
||||
} else {
|
||||
axis = glm::normalize(glm::cross(currentUp, _worldUpDirection));
|
||||
}
|
||||
|
@ -1126,6 +1144,9 @@ void Avatar::renderBody(bool lookingInMirror) {
|
|||
const float RENDER_OPAQUE_BEYOND = 1.0f; // Meters beyond which body is shown opaque
|
||||
const float RENDER_TRANSLUCENT_BEYOND = 0.5f;
|
||||
|
||||
// Render the body's voxels
|
||||
_voxels.render(false);
|
||||
|
||||
// Render the body as balls and cones
|
||||
for (int b = 0; b < NUM_AVATAR_BODY_BALLS; b++) {
|
||||
float distanceToCamera = glm::length(_cameraPosition - _bodyBall[b].position);
|
||||
|
@ -1231,28 +1252,43 @@ void Avatar::setHeadFromGyros(glm::vec3* eulerAngles, glm::vec3* angularVelocity
|
|||
}
|
||||
}
|
||||
|
||||
void Avatar::writeAvatarDataToFile() {
|
||||
Application::getInstance()->setSetting("avatarPos", _position);
|
||||
Application::getInstance()->setSetting("avatarRotation", glm::vec3(_bodyYaw, _bodyPitch, _bodyRoll));
|
||||
void Avatar::loadData(QSettings* set) {
|
||||
set->beginGroup("Avatar");
|
||||
|
||||
_bodyYaw = set->value("bodyYaw", _bodyYaw).toFloat();
|
||||
_bodyPitch = set->value("bodyPitch", _bodyPitch).toFloat();
|
||||
_bodyRoll = set->value("bodyRoll", _bodyRoll).toFloat();
|
||||
|
||||
_position.x = set->value("position_x", _position.x).toFloat();
|
||||
_position.y = set->value("position_y", _position.y).toFloat();
|
||||
_position.z = set->value("position_z", _position.z).toFloat();
|
||||
|
||||
set->endGroup();
|
||||
}
|
||||
|
||||
void Avatar::readAvatarDataFromFile() {
|
||||
glm::vec3 readPosition;
|
||||
glm::vec3 readRotation;
|
||||
|
||||
Application::getInstance()->getSetting("avatarPos", readPosition, glm::vec3(6.1f, 0, 1.4f));
|
||||
Application::getInstance()->getSetting("avatarRotation", readRotation, glm::vec3(0, 0, 0));
|
||||
|
||||
_bodyYaw = readRotation.x;
|
||||
_bodyPitch = readRotation.y;
|
||||
_bodyRoll = readRotation.z;
|
||||
_position = readPosition;
|
||||
void Avatar::getBodyBallTransform(AvatarJointID jointID, glm::vec3& position, glm::quat& rotation) const {
|
||||
position = _bodyBall[jointID].position;
|
||||
rotation = _bodyBall[jointID].rotation;
|
||||
}
|
||||
|
||||
void Avatar::saveData(QSettings* set) {
|
||||
set->beginGroup("Avatar");
|
||||
|
||||
set->setValue("bodyYaw", _bodyYaw);
|
||||
set->setValue("bodyPitch", _bodyPitch);
|
||||
set->setValue("bodyRoll", _bodyRoll);
|
||||
|
||||
set->setValue("position_x", _position.x);
|
||||
set->setValue("position_y", _position.y);
|
||||
set->setValue("position_z", _position.z);
|
||||
|
||||
set->endGroup();
|
||||
}
|
||||
|
||||
// render a makeshift cone section that serves as a body part connecting joint spheres
|
||||
void Avatar::renderJointConnectingCone(glm::vec3 position1, glm::vec3 position2, float radius1, float radius2) {
|
||||
|
||||
glBegin(GL_TRIANGLES);
|
||||
glBegin(GL_TRIANGLES);
|
||||
|
||||
glm::vec3 axis = position2 - position1;
|
||||
float length = glm::length(axis);
|
||||
|
|
|
@ -11,8 +11,10 @@
|
|||
#include <glm/glm.hpp>
|
||||
#include <glm/gtc/quaternion.hpp>
|
||||
#include <AvatarData.h>
|
||||
#include <QSettings>
|
||||
#include "world.h"
|
||||
#include "AvatarTouch.h"
|
||||
#include "AvatarVoxelSystem.h"
|
||||
#include "InterfaceConfig.h"
|
||||
#include "SerialInterface.h"
|
||||
#include "Balls.h"
|
||||
|
@ -80,6 +82,7 @@ public:
|
|||
Avatar(Agent* owningAgent = NULL);
|
||||
~Avatar();
|
||||
|
||||
void init();
|
||||
void reset();
|
||||
void simulate(float deltaTime, Transmitter* transmitter);
|
||||
void updateHeadFromGyros(float frametime, SerialInterface * serialInterface);
|
||||
|
@ -98,14 +101,16 @@ public:
|
|||
void setOrientation (const glm::quat& orientation);
|
||||
|
||||
//getters
|
||||
bool isInitialized () const { return _initialized;}
|
||||
const Skeleton& getSkeleton () const { return _skeleton;}
|
||||
float getHeadYawRate () const { return _head.yawRate;}
|
||||
float getBodyYaw () const { return _bodyYaw;}
|
||||
bool getIsNearInteractingOther () const { return _avatarTouch.getAbleToReachOtherAvatar();}
|
||||
const glm::vec3& getHeadJointPosition () const { return _skeleton.joint[ AVATAR_JOINT_HEAD_BASE ].position;}
|
||||
const glm::vec3& getBallPosition (AvatarJointID j) const { return _bodyBall[j].position;}
|
||||
glm::vec3 getBodyRightDirection () const { return getOrientation() * AVATAR_RIGHT; }
|
||||
glm::vec3 getBodyUpDirection () const { return getOrientation() * AVATAR_UP; }
|
||||
glm::vec3 getBodyFrontDirection () const { return getOrientation() * AVATAR_FRONT; }
|
||||
glm::vec3 getBodyRightDirection () const { return getOrientation() * IDENTITY_RIGHT; }
|
||||
glm::vec3 getBodyUpDirection () const { return getOrientation() * IDENTITY_UP; }
|
||||
glm::vec3 getBodyFrontDirection () const { return getOrientation() * IDENTITY_FRONT; }
|
||||
const glm::vec3& getVelocity () const { return _velocity;}
|
||||
float getSpeed () const { return _speed;}
|
||||
float getHeight () const { return _height;}
|
||||
|
@ -116,6 +121,8 @@ public:
|
|||
glm::quat getOrientation () const;
|
||||
glm::quat getWorldAlignedOrientation() const;
|
||||
|
||||
AvatarVoxelSystem* getVoxels() { return &_voxels; }
|
||||
|
||||
// Set what driving keys are being pressed to control thrust levels
|
||||
void setDriveKeys(int key, bool val) { _driveKeys[key] = val; };
|
||||
bool getDriveKeys(int key) { return _driveKeys[key]; };
|
||||
|
@ -124,6 +131,13 @@ public:
|
|||
void addThrust(glm::vec3 newThrust) { _thrust += newThrust; };
|
||||
glm::vec3 getThrust() { return _thrust; };
|
||||
|
||||
// get/set avatar data
|
||||
void saveData(QSettings* set);
|
||||
void loadData(QSettings* set);
|
||||
|
||||
// Get the position/rotation of a single body ball
|
||||
void getBodyBallTransform(AvatarJointID jointID, glm::vec3& position, glm::quat& rotation) const;
|
||||
|
||||
//read/write avatar data
|
||||
void writeAvatarDataToFile();
|
||||
void readAvatarDataFromFile();
|
||||
|
@ -139,6 +153,7 @@ private:
|
|||
glm::vec3 parentOffset; // a 3D vector in the frame of reference of the parent skeletal joint
|
||||
AvatarBodyBallID parentBall; // the ball to which this ball is constrained for spring forces
|
||||
glm::vec3 position; // the actual dynamic position of the ball at any given time
|
||||
glm::quat rotation; // the rotation of the ball
|
||||
glm::vec3 velocity; // the velocity of the ball
|
||||
float springLength; // the ideal length of the spring between this ball and its parentBall
|
||||
float jointTightness; // how tightly the ball position attempts to stay at its ideal position (determined by parentOffset)
|
||||
|
@ -147,6 +162,7 @@ private:
|
|||
float touchForce; // a scalar determining the amount that the cursor (or hand) is penetrating the ball
|
||||
};
|
||||
|
||||
bool _initialized;
|
||||
Head _head;
|
||||
Skeleton _skeleton;
|
||||
bool _ballSpringsInitialized;
|
||||
|
@ -181,6 +197,8 @@ private:
|
|||
float _cumulativeMouseYaw;
|
||||
bool _isMouseTurningRight;
|
||||
|
||||
AvatarVoxelSystem _voxels;
|
||||
|
||||
// private methods...
|
||||
glm::vec3 caclulateAverageEyePosition() { return _head.caclulateAverageEyePosition(); } // get the position smack-dab between the eyes (for lookat)
|
||||
glm::quat computeRotationFromBodyToWorldUp(float proportion = 1.0f) const;
|
||||
|
|
261
interface/src/AvatarVoxelSystem.cpp
Normal file
261
interface/src/AvatarVoxelSystem.cpp
Normal file
|
@ -0,0 +1,261 @@
|
|||
//
|
||||
// AvatarVoxelSystem.cpp
|
||||
// interface
|
||||
//
|
||||
// Created by Andrzej Kapolka on 5/31/13.
|
||||
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#include <QNetworkReply>
|
||||
#include <QUrl>
|
||||
|
||||
#include <GeometryUtil.h>
|
||||
|
||||
#include "Application.h"
|
||||
#include "Avatar.h"
|
||||
#include "AvatarVoxelSystem.h"
|
||||
#include "renderer/ProgramObject.h"
|
||||
|
||||
const float AVATAR_TREE_SCALE = 1.0f;
|
||||
const int MAX_VOXELS_PER_AVATAR = 2000;
|
||||
const int BONE_ELEMENTS_PER_VOXEL = BONE_ELEMENTS_PER_VERTEX * VERTICES_PER_VOXEL;
|
||||
|
||||
AvatarVoxelSystem::AvatarVoxelSystem(Avatar* avatar) :
|
||||
VoxelSystem(AVATAR_TREE_SCALE, MAX_VOXELS_PER_AVATAR),
|
||||
_avatar(avatar), _voxelReply(0) {
|
||||
}
|
||||
|
||||
AvatarVoxelSystem::~AvatarVoxelSystem() {
|
||||
delete[] _readBoneIndicesArray;
|
||||
delete[] _readBoneWeightsArray;
|
||||
delete[] _writeBoneIndicesArray;
|
||||
delete[] _writeBoneWeightsArray;
|
||||
}
|
||||
|
||||
ProgramObject* AvatarVoxelSystem::_skinProgram = 0;
|
||||
int AvatarVoxelSystem::_boneMatricesLocation;
|
||||
int AvatarVoxelSystem::_boneIndicesLocation;
|
||||
int AvatarVoxelSystem::_boneWeightsLocation;
|
||||
|
||||
void AvatarVoxelSystem::init() {
|
||||
VoxelSystem::init();
|
||||
|
||||
// prep the data structures for incoming voxel data
|
||||
_writeBoneIndicesArray = new GLubyte[BONE_ELEMENTS_PER_VOXEL * _maxVoxels];
|
||||
_readBoneIndicesArray = new GLubyte[BONE_ELEMENTS_PER_VOXEL * _maxVoxels];
|
||||
|
||||
_writeBoneWeightsArray = new GLfloat[BONE_ELEMENTS_PER_VOXEL * _maxVoxels];
|
||||
_readBoneWeightsArray = new GLfloat[BONE_ELEMENTS_PER_VOXEL * _maxVoxels];
|
||||
|
||||
// VBO for the boneIndicesArray
|
||||
glGenBuffers(1, &_vboBoneIndicesID);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, _vboBoneIndicesID);
|
||||
glBufferData(GL_ARRAY_BUFFER, BONE_ELEMENTS_PER_VOXEL * sizeof(GLubyte) * _maxVoxels, NULL, GL_DYNAMIC_DRAW);
|
||||
|
||||
// VBO for the boneWeightsArray
|
||||
glGenBuffers(1, &_vboBoneWeightsID);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, _vboBoneWeightsID);
|
||||
glBufferData(GL_ARRAY_BUFFER, BONE_ELEMENTS_PER_VOXEL * sizeof(GLfloat) * _maxVoxels, NULL, GL_DYNAMIC_DRAW);
|
||||
|
||||
// load our skin program if this is the first avatar system to initialize
|
||||
if (_skinProgram != 0) {
|
||||
return;
|
||||
}
|
||||
_skinProgram = new ProgramObject();
|
||||
_skinProgram->addShaderFromSourceFile(QGLShader::Vertex, "resources/shaders/skin_voxels.vert");
|
||||
_skinProgram->link();
|
||||
|
||||
_boneMatricesLocation = _skinProgram->uniformLocation("boneMatrices");
|
||||
_boneIndicesLocation = _skinProgram->attributeLocation("boneIndices");
|
||||
_boneWeightsLocation = _skinProgram->attributeLocation("boneWeights");
|
||||
}
|
||||
|
||||
void AvatarVoxelSystem::removeOutOfView() {
|
||||
// no-op for now
|
||||
}
|
||||
|
||||
void AvatarVoxelSystem::loadVoxelsFromURL(const QUrl& url) {
|
||||
// cancel any current download
|
||||
if (_voxelReply != 0) {
|
||||
delete _voxelReply;
|
||||
}
|
||||
|
||||
killLocalVoxels();
|
||||
|
||||
// load the URL data asynchronously
|
||||
if (!url.isValid()) {
|
||||
return;
|
||||
}
|
||||
_voxelReply = Application::getInstance()->getNetworkAccessManager()->get(QNetworkRequest(url));
|
||||
connect(_voxelReply, SIGNAL(readyRead()), SLOT(readVoxelDataFromReply()));
|
||||
connect(_voxelReply, SIGNAL(error(QNetworkReply::NetworkError)), SLOT(handleVoxelReplyError()));
|
||||
}
|
||||
|
||||
void AvatarVoxelSystem::updateNodeInArrays(glBufferIndex nodeIndex, const glm::vec3& startVertex,
|
||||
float voxelScale, const nodeColor& color) {
|
||||
VoxelSystem::updateNodeInArrays(nodeIndex, startVertex, voxelScale, color);
|
||||
|
||||
GLubyte* writeBoneIndicesAt = _writeBoneIndicesArray + (nodeIndex * BONE_ELEMENTS_PER_VOXEL);
|
||||
GLfloat* writeBoneWeightsAt = _writeBoneWeightsArray + (nodeIndex * BONE_ELEMENTS_PER_VOXEL);
|
||||
for (int i = 0; i < VERTICES_PER_VOXEL; i++) {
|
||||
BoneIndices boneIndices;
|
||||
glm::vec4 boneWeights;
|
||||
computeBoneIndicesAndWeights(computeVoxelVertex(startVertex, voxelScale, i), boneIndices, boneWeights);
|
||||
for (int j = 0; j < BONE_ELEMENTS_PER_VERTEX; j++) {
|
||||
*(writeBoneIndicesAt + i * BONE_ELEMENTS_PER_VERTEX + j) = boneIndices[j];
|
||||
*(writeBoneWeightsAt + i * BONE_ELEMENTS_PER_VERTEX + j) = boneWeights[j];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AvatarVoxelSystem::copyWrittenDataSegmentToReadArrays(glBufferIndex segmentStart, glBufferIndex segmentEnd) {
|
||||
VoxelSystem::copyWrittenDataSegmentToReadArrays(segmentStart, segmentEnd);
|
||||
|
||||
int segmentLength = (segmentEnd - segmentStart) + 1;
|
||||
GLintptr segmentStartAt = segmentStart * BONE_ELEMENTS_PER_VOXEL * sizeof(GLubyte);
|
||||
GLsizeiptr segmentSizeBytes = segmentLength * BONE_ELEMENTS_PER_VOXEL * sizeof(GLubyte);
|
||||
GLubyte* readBoneIndicesAt = _readBoneIndicesArray + (segmentStart * BONE_ELEMENTS_PER_VOXEL);
|
||||
GLubyte* writeBoneIndicesAt = _writeBoneIndicesArray + (segmentStart * BONE_ELEMENTS_PER_VOXEL);
|
||||
memcpy(readBoneIndicesAt, writeBoneIndicesAt, segmentSizeBytes);
|
||||
|
||||
segmentStartAt = segmentStart * BONE_ELEMENTS_PER_VOXEL * sizeof(GLfloat);
|
||||
segmentSizeBytes = segmentLength * BONE_ELEMENTS_PER_VOXEL * sizeof(GLfloat);
|
||||
GLfloat* readBoneWeightsAt = _readBoneWeightsArray + (segmentStart * BONE_ELEMENTS_PER_VOXEL);
|
||||
GLfloat* writeBoneWeightsAt = _writeBoneWeightsArray + (segmentStart * BONE_ELEMENTS_PER_VOXEL);
|
||||
memcpy(readBoneWeightsAt, writeBoneWeightsAt, segmentSizeBytes);
|
||||
}
|
||||
|
||||
void AvatarVoxelSystem::updateVBOSegment(glBufferIndex segmentStart, glBufferIndex segmentEnd) {
|
||||
VoxelSystem::updateVBOSegment(segmentStart, segmentEnd);
|
||||
|
||||
int segmentLength = (segmentEnd - segmentStart) + 1;
|
||||
GLintptr segmentStartAt = segmentStart * BONE_ELEMENTS_PER_VOXEL * sizeof(GLubyte);
|
||||
GLsizeiptr segmentSizeBytes = segmentLength * BONE_ELEMENTS_PER_VOXEL * sizeof(GLubyte);
|
||||
GLubyte* readBoneIndicesFrom = _readBoneIndicesArray + (segmentStart * BONE_ELEMENTS_PER_VOXEL);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, _vboBoneIndicesID);
|
||||
glBufferSubData(GL_ARRAY_BUFFER, segmentStartAt, segmentSizeBytes, readBoneIndicesFrom);
|
||||
|
||||
segmentStartAt = segmentStart * BONE_ELEMENTS_PER_VOXEL * sizeof(GLfloat);
|
||||
segmentSizeBytes = segmentLength * BONE_ELEMENTS_PER_VOXEL * sizeof(GLfloat);
|
||||
GLfloat* readBoneWeightsFrom = _readBoneWeightsArray + (segmentStart * BONE_ELEMENTS_PER_VOXEL);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, _vboBoneWeightsID);
|
||||
glBufferSubData(GL_ARRAY_BUFFER, segmentStartAt, segmentSizeBytes, readBoneWeightsFrom);
|
||||
}
|
||||
|
||||
void AvatarVoxelSystem::applyScaleAndBindProgram(bool texture) {
|
||||
_skinProgram->bind();
|
||||
|
||||
// the base matrix includes centering and scale
|
||||
QMatrix4x4 baseMatrix;
|
||||
baseMatrix.scale(_treeScale);
|
||||
baseMatrix.translate(-0.5f, -0.5f, -0.5f);
|
||||
|
||||
// bone matrices include joint transforms
|
||||
QMatrix4x4 boneMatrices[NUM_AVATAR_JOINTS];
|
||||
for (int i = 0; i < NUM_AVATAR_JOINTS; i++) {
|
||||
glm::vec3 position;
|
||||
glm::quat orientation;
|
||||
_avatar->getBodyBallTransform((AvatarJointID)i, position, orientation);
|
||||
boneMatrices[i].translate(position.x, position.y, position.z);
|
||||
orientation = orientation * glm::inverse(_avatar->getSkeleton().joint[i].absoluteBindPoseRotation);
|
||||
boneMatrices[i].rotate(QQuaternion(orientation.w, orientation.x, orientation.y, orientation.z));
|
||||
const glm::vec3& bindPosition = _avatar->getSkeleton().joint[i].absoluteBindPosePosition;
|
||||
boneMatrices[i].translate(-bindPosition.x, -bindPosition.y, -bindPosition.z);
|
||||
boneMatrices[i] *= baseMatrix;
|
||||
}
|
||||
_skinProgram->setUniformValueArray(_boneMatricesLocation, boneMatrices, NUM_AVATAR_JOINTS);
|
||||
|
||||
glBindBuffer(GL_ARRAY_BUFFER, _vboBoneIndicesID);
|
||||
glVertexAttribPointer(_boneIndicesLocation, BONE_ELEMENTS_PER_VERTEX, GL_UNSIGNED_BYTE, false, 0, 0);
|
||||
_skinProgram->enableAttributeArray(_boneIndicesLocation);
|
||||
|
||||
glBindBuffer(GL_ARRAY_BUFFER, _vboBoneWeightsID);
|
||||
_skinProgram->setAttributeBuffer(_boneWeightsLocation, GL_FLOAT, 0, BONE_ELEMENTS_PER_VERTEX);
|
||||
_skinProgram->enableAttributeArray(_boneWeightsLocation);
|
||||
}
|
||||
|
||||
void AvatarVoxelSystem::removeScaleAndReleaseProgram(bool texture) {
|
||||
_skinProgram->release();
|
||||
_skinProgram->disableAttributeArray(_boneIndicesLocation);
|
||||
_skinProgram->disableAttributeArray(_boneWeightsLocation);
|
||||
}
|
||||
|
||||
void AvatarVoxelSystem::readVoxelDataFromReply() {
|
||||
// for now, just wait until we have the full business
|
||||
if (!_voxelReply->isFinished()) {
|
||||
return;
|
||||
}
|
||||
QByteArray entirety = _voxelReply->readAll();
|
||||
_voxelReply->deleteLater();
|
||||
_voxelReply = 0;
|
||||
_tree->readBitstreamToTree((unsigned char*)entirety.data(), entirety.size(), WANT_COLOR, NO_EXISTS_BITS);
|
||||
setupNewVoxelsForDrawing();
|
||||
}
|
||||
|
||||
void AvatarVoxelSystem::handleVoxelReplyError() {
|
||||
printLog("%s\n", _voxelReply->errorString().toAscii().constData());
|
||||
|
||||
_voxelReply->deleteLater();
|
||||
_voxelReply = 0;
|
||||
}
|
||||
|
||||
class IndexDistance {
|
||||
public:
|
||||
IndexDistance(GLubyte index = AVATAR_JOINT_PELVIS, float distance = FLT_MAX) : index(index), distance(distance) { }
|
||||
|
||||
GLubyte index;
|
||||
float distance;
|
||||
};
|
||||
|
||||
void AvatarVoxelSystem::computeBoneIndicesAndWeights(const glm::vec3& vertex, BoneIndices& indices, glm::vec4& weights) const {
|
||||
// transform into joint space
|
||||
glm::vec3 jointVertex = (vertex - glm::vec3(0.5f, 0.5f, 0.5f)) * AVATAR_TREE_SCALE;
|
||||
|
||||
// find the nearest four joints (TODO: use a better data structure for the pose positions to speed this up)
|
||||
IndexDistance nearest[BONE_ELEMENTS_PER_VERTEX];
|
||||
const Skeleton& skeleton = _avatar->getSkeleton();
|
||||
for (int i = 0; i < NUM_AVATAR_JOINTS; i++) {
|
||||
AvatarJointID parent = skeleton.joint[i].parent;
|
||||
float distance = glm::length(computeVectorFromPointToSegment(jointVertex,
|
||||
skeleton.joint[parent == AVATAR_JOINT_NULL ? i : parent].absoluteBindPosePosition,
|
||||
skeleton.joint[i].absoluteBindPosePosition));
|
||||
if (distance > skeleton.joint[i].bindRadius) {
|
||||
continue;
|
||||
}
|
||||
for (int j = 0; j < BONE_ELEMENTS_PER_VERTEX; j++) {
|
||||
if (distance < nearest[j].distance) {
|
||||
// move the rest of the indices down
|
||||
for (int k = BONE_ELEMENTS_PER_VERTEX - 1; k > j; k--) {
|
||||
nearest[k] = nearest[k - 1];
|
||||
}
|
||||
nearest[j] = IndexDistance(i, distance);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// compute the weights based on inverse distance
|
||||
float totalWeight = 0.0f;
|
||||
for (int i = 0; i < BONE_ELEMENTS_PER_VERTEX; i++) {
|
||||
indices[i] = nearest[i].index;
|
||||
if (nearest[i].distance != FLT_MAX) {
|
||||
weights[i] = 1.0f / glm::max(nearest[i].distance, EPSILON);
|
||||
totalWeight += weights[i];
|
||||
|
||||
} else {
|
||||
weights[i] = 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
// if it's not attached to anything, consider it attached to the hip
|
||||
if (totalWeight == 0.0f) {
|
||||
weights[0] = 1.0f;
|
||||
return;
|
||||
}
|
||||
|
||||
// ortherwise, normalize the weights
|
||||
for (int i = 0; i < BONE_ELEMENTS_PER_VERTEX; i++) {
|
||||
weights[i] /= totalWeight;
|
||||
}
|
||||
}
|
74
interface/src/AvatarVoxelSystem.h
Normal file
74
interface/src/AvatarVoxelSystem.h
Normal file
|
@ -0,0 +1,74 @@
|
|||
//
|
||||
// AvatarVoxelSystem.h
|
||||
// interface
|
||||
//
|
||||
// Created by Andrzej Kapolka on 5/31/13.
|
||||
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef __interface__AvatarVoxelSystem__
|
||||
#define __interface__AvatarVoxelSystem__
|
||||
|
||||
#include <QObject>
|
||||
|
||||
#include "VoxelSystem.h"
|
||||
|
||||
const int BONE_ELEMENTS_PER_VERTEX = 4;
|
||||
typedef GLubyte BoneIndices[BONE_ELEMENTS_PER_VERTEX];
|
||||
|
||||
class QNetworkReply;
|
||||
class QUrl;
|
||||
|
||||
class Avatar;
|
||||
|
||||
class AvatarVoxelSystem : public QObject, public VoxelSystem {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
AvatarVoxelSystem(Avatar* avatar);
|
||||
virtual ~AvatarVoxelSystem();
|
||||
|
||||
virtual void init();
|
||||
|
||||
virtual void removeOutOfView();
|
||||
|
||||
void loadVoxelsFromURL(const QUrl& url);
|
||||
|
||||
protected:
|
||||
|
||||
virtual void updateNodeInArrays(glBufferIndex nodeIndex, const glm::vec3& startVertex,
|
||||
float voxelScale, const nodeColor& color);
|
||||
virtual void copyWrittenDataSegmentToReadArrays(glBufferIndex segmentStart, glBufferIndex segmentEnd);
|
||||
virtual void updateVBOSegment(glBufferIndex segmentStart, glBufferIndex segmentEnd);
|
||||
virtual void applyScaleAndBindProgram(bool texture);
|
||||
virtual void removeScaleAndReleaseProgram(bool texture);
|
||||
|
||||
private slots:
|
||||
|
||||
void readVoxelDataFromReply();
|
||||
void handleVoxelReplyError();
|
||||
|
||||
private:
|
||||
|
||||
void computeBoneIndicesAndWeights(const glm::vec3& vertex, BoneIndices& indices, glm::vec4& weights) const;
|
||||
|
||||
Avatar* _avatar;
|
||||
|
||||
GLubyte* _readBoneIndicesArray;
|
||||
GLfloat* _readBoneWeightsArray;
|
||||
GLubyte* _writeBoneIndicesArray;
|
||||
GLfloat* _writeBoneWeightsArray;
|
||||
|
||||
GLuint _vboBoneIndicesID;
|
||||
GLuint _vboBoneWeightsID;
|
||||
|
||||
QNetworkReply* _voxelReply;
|
||||
|
||||
static ProgramObject* _skinProgram;
|
||||
static int _boneMatricesLocation;
|
||||
static int _boneIndicesLocation;
|
||||
static int _boneWeightsLocation;
|
||||
};
|
||||
|
||||
#endif /* defined(__interface__AvatarVoxelSystem__) */
|
|
@ -171,9 +171,9 @@ void Head::determineIfLookingAtSomething() {
|
|||
void Head::calculateGeometry() {
|
||||
//generate orientation directions
|
||||
glm::quat orientation = getOrientation();
|
||||
glm::vec3 right = orientation * AVATAR_RIGHT;
|
||||
glm::vec3 up = orientation * AVATAR_UP;
|
||||
glm::vec3 front = orientation * AVATAR_FRONT;
|
||||
glm::vec3 right = orientation * IDENTITY_RIGHT;
|
||||
glm::vec3 up = orientation * IDENTITY_UP;
|
||||
glm::vec3 front = orientation * IDENTITY_FRONT;
|
||||
|
||||
//calculate the eye positions
|
||||
_leftEyePosition = _position
|
||||
|
@ -346,9 +346,9 @@ void Head::renderMouth() {
|
|||
float s = sqrt(_averageLoudness);
|
||||
|
||||
glm::quat orientation = getOrientation();
|
||||
glm::vec3 right = orientation * AVATAR_RIGHT;
|
||||
glm::vec3 up = orientation * AVATAR_UP;
|
||||
glm::vec3 front = orientation * AVATAR_FRONT;
|
||||
glm::vec3 right = orientation * IDENTITY_RIGHT;
|
||||
glm::vec3 up = orientation * IDENTITY_UP;
|
||||
glm::vec3 front = orientation * IDENTITY_FRONT;
|
||||
|
||||
glm::vec3 r = right * _scale * (0.30f + s * 0.0014f );
|
||||
glm::vec3 u = up * _scale * (0.05f + s * 0.0040f );
|
||||
|
@ -414,9 +414,9 @@ void Head::renderEyeBrows() {
|
|||
glm::vec3 rightBottom = _leftEyePosition;
|
||||
|
||||
glm::quat orientation = getOrientation();
|
||||
glm::vec3 right = orientation * AVATAR_RIGHT;
|
||||
glm::vec3 up = orientation * AVATAR_UP;
|
||||
glm::vec3 front = orientation * AVATAR_FRONT;
|
||||
glm::vec3 right = orientation * IDENTITY_RIGHT;
|
||||
glm::vec3 up = orientation * IDENTITY_UP;
|
||||
glm::vec3 front = orientation * IDENTITY_FRONT;
|
||||
|
||||
glm::vec3 r = right * length;
|
||||
glm::vec3 u = up * height;
|
||||
|
@ -501,20 +501,20 @@ void Head::renderEyeBalls() {
|
|||
|
||||
//rotate the eyeball to aim towards the lookat position
|
||||
glm::vec3 targetLookatAxis = glm::normalize(_lookAtPosition - _leftEyePosition); // the lookat direction
|
||||
glm::vec3 rotationAxis = glm::cross(targetLookatAxis, AVATAR_UP);
|
||||
float angle = 180.0f - angleBetween(targetLookatAxis, AVATAR_UP);
|
||||
glm::vec3 rotationAxis = glm::cross(targetLookatAxis, IDENTITY_UP);
|
||||
float angle = 180.0f - angleBetween(targetLookatAxis, IDENTITY_UP);
|
||||
glRotatef(angle, rotationAxis.x, rotationAxis.y, rotationAxis.z);
|
||||
glRotatef(180.0, 0.0f, 1.0f, 0.0f); //adjust roll to correct after previous rotations
|
||||
} else {
|
||||
|
||||
//rotate the eyeball to aim straight ahead
|
||||
glm::vec3 rotationAxisToHeadFront = glm::cross(front, AVATAR_UP);
|
||||
float angleToHeadFront = 180.0f - angleBetween(front, AVATAR_UP);
|
||||
glm::vec3 rotationAxisToHeadFront = glm::cross(front, IDENTITY_UP);
|
||||
float angleToHeadFront = 180.0f - angleBetween(front, IDENTITY_UP);
|
||||
glRotatef(angleToHeadFront, rotationAxisToHeadFront.x, rotationAxisToHeadFront.y, rotationAxisToHeadFront.z);
|
||||
|
||||
//set the amount of roll (for correction after previous rotations)
|
||||
float rollRotation = angleBetween(front, AVATAR_FRONT);
|
||||
float dot = glm::dot(front, -AVATAR_RIGHT);
|
||||
float rollRotation = angleBetween(front, IDENTITY_FRONT);
|
||||
float dot = glm::dot(front, -IDENTITY_RIGHT);
|
||||
if ( dot < 0.0f ) { rollRotation = -rollRotation; }
|
||||
glRotatef(rollRotation, 0.0f, 1.0f, 0.0f); //roll the iris or correct roll about the lookat vector
|
||||
}
|
||||
|
@ -545,21 +545,21 @@ void Head::renderEyeBalls() {
|
|||
|
||||
//rotate the eyeball to aim towards the lookat position
|
||||
glm::vec3 targetLookatAxis = glm::normalize(_lookAtPosition - _rightEyePosition);
|
||||
glm::vec3 rotationAxis = glm::cross(targetLookatAxis, AVATAR_UP);
|
||||
float angle = 180.0f - angleBetween(targetLookatAxis, AVATAR_UP);
|
||||
glm::vec3 rotationAxis = glm::cross(targetLookatAxis, IDENTITY_UP);
|
||||
float angle = 180.0f - angleBetween(targetLookatAxis, IDENTITY_UP);
|
||||
glRotatef(angle, rotationAxis.x, rotationAxis.y, rotationAxis.z);
|
||||
glRotatef(180.0f, 0.0f, 1.0f, 0.0f); //adjust roll to correct after previous rotations
|
||||
|
||||
} else {
|
||||
|
||||
//rotate the eyeball to aim straight ahead
|
||||
glm::vec3 rotationAxisToHeadFront = glm::cross(front, AVATAR_UP);
|
||||
float angleToHeadFront = 180.0f - angleBetween(front, AVATAR_UP);
|
||||
glm::vec3 rotationAxisToHeadFront = glm::cross(front, IDENTITY_UP);
|
||||
float angleToHeadFront = 180.0f - angleBetween(front, IDENTITY_UP);
|
||||
glRotatef(angleToHeadFront, rotationAxisToHeadFront.x, rotationAxisToHeadFront.y, rotationAxisToHeadFront.z);
|
||||
|
||||
//set the amount of roll (for correction after previous rotations)
|
||||
float rollRotation = angleBetween(front, AVATAR_FRONT);
|
||||
float dot = glm::dot(front, -AVATAR_RIGHT);
|
||||
float rollRotation = angleBetween(front, IDENTITY_FRONT);
|
||||
float dot = glm::dot(front, -IDENTITY_RIGHT);
|
||||
if ( dot < 0.0f ) { rollRotation = -rollRotation; }
|
||||
glRotatef(rollRotation, 0.0f, 1.0f, 0.0f); //roll the iris or correct roll about the lookat vector
|
||||
}
|
||||
|
@ -595,9 +595,8 @@ void Head::renderLookatVectors(glm::vec3 leftEyePosition, glm::vec3 rightEyePosi
|
|||
void Head::updateHairPhysics(float deltaTime) {
|
||||
|
||||
glm::quat orientation = getOrientation();
|
||||
glm::vec3 right = orientation * AVATAR_RIGHT;
|
||||
glm::vec3 up = orientation * AVATAR_UP;
|
||||
glm::vec3 front = orientation * AVATAR_FRONT;
|
||||
glm::vec3 up = orientation * IDENTITY_UP;
|
||||
glm::vec3 front = orientation * IDENTITY_FRONT;
|
||||
|
||||
for (int t = 0; t < NUM_HAIR_TUFTS; t ++) {
|
||||
|
||||
|
|
|
@ -49,9 +49,9 @@ public:
|
|||
glm::quat getOrientation() const;
|
||||
glm::quat getWorldAlignedOrientation () const;
|
||||
|
||||
glm::vec3 getRightDirection() const { return getOrientation() * AVATAR_RIGHT; }
|
||||
glm::vec3 getUpDirection () const { return getOrientation() * AVATAR_UP; }
|
||||
glm::vec3 getFrontDirection() const { return getOrientation() * AVATAR_FRONT; }
|
||||
glm::vec3 getRightDirection() const { return getOrientation() * IDENTITY_RIGHT; }
|
||||
glm::vec3 getUpDirection () const { return getOrientation() * IDENTITY_UP; }
|
||||
glm::vec3 getFrontDirection() const { return getOrientation() * IDENTITY_FRONT; }
|
||||
|
||||
const bool getReturnToCenter() const { return _returnHeadToCenter; } // Do you want head to try to return to center (depends on interface detected)
|
||||
float getAverageLoudness() {return _averageLoudness;};
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
|
||||
|
||||
#include "Skeleton.h"
|
||||
#include "Util.h"
|
||||
|
||||
const float BODY_SPRING_DEFAULT_TIGHTNESS = 1000.0f;
|
||||
const float FLOATING_HEIGHT = 0.13f;
|
||||
|
@ -18,8 +19,9 @@ void Skeleton::initialize() {
|
|||
joint[b].parent = AVATAR_JOINT_NULL;
|
||||
joint[b].position = glm::vec3(0.0, 0.0, 0.0);
|
||||
joint[b].defaultPosePosition = glm::vec3(0.0, 0.0, 0.0);
|
||||
joint[b].rotation = glm::quat(0.0f, 0.0f, 0.0f, 0.0f);
|
||||
joint[b].rotation = glm::quat(1.0f, 0.0f, 0.0f, 0.0f);
|
||||
joint[b].length = 0.0;
|
||||
joint[b].bindRadius = 1.0f / 8;
|
||||
}
|
||||
|
||||
// specify the parental hierarchy
|
||||
|
@ -48,6 +50,35 @@ void Skeleton::initialize() {
|
|||
joint[ AVATAR_JOINT_RIGHT_HEEL ].parent = AVATAR_JOINT_RIGHT_KNEE;
|
||||
joint[ AVATAR_JOINT_RIGHT_TOES ].parent = AVATAR_JOINT_RIGHT_HEEL;
|
||||
|
||||
// specify the bind pose position
|
||||
joint[ AVATAR_JOINT_PELVIS ].bindPosePosition = glm::vec3( 0.0, 0.0, 0.0 );
|
||||
joint[ AVATAR_JOINT_TORSO ].bindPosePosition = glm::vec3( 0.0, 0.09, -0.01 );
|
||||
joint[ AVATAR_JOINT_CHEST ].bindPosePosition = glm::vec3( 0.0, 0.09, -0.01 );
|
||||
joint[ AVATAR_JOINT_NECK_BASE ].bindPosePosition = glm::vec3( 0.0, 0.14, 0.01 );
|
||||
joint[ AVATAR_JOINT_HEAD_BASE ].bindPosePosition = glm::vec3( 0.0, 0.04, 0.00 );
|
||||
|
||||
joint[ AVATAR_JOINT_LEFT_COLLAR ].bindPosePosition = glm::vec3( -0.06, 0.04, 0.01 );
|
||||
joint[ AVATAR_JOINT_LEFT_SHOULDER ].bindPosePosition = glm::vec3( -0.05, 0.0, 0.01 );
|
||||
joint[ AVATAR_JOINT_LEFT_ELBOW ].bindPosePosition = glm::vec3( -0.16, 0.0, 0.0 );
|
||||
joint[ AVATAR_JOINT_LEFT_WRIST ].bindPosePosition = glm::vec3( -0.12, 0.0, 0.0 );
|
||||
joint[ AVATAR_JOINT_LEFT_FINGERTIPS ].bindPosePosition = glm::vec3( -0.1, 0.0, 0.0 );
|
||||
|
||||
joint[ AVATAR_JOINT_RIGHT_COLLAR ].bindPosePosition = glm::vec3( 0.06, 0.04, 0.01 );
|
||||
joint[ AVATAR_JOINT_RIGHT_SHOULDER ].bindPosePosition = glm::vec3( 0.05, 0.0, 0.01 );
|
||||
joint[ AVATAR_JOINT_RIGHT_ELBOW ].bindPosePosition = glm::vec3( 0.16, 0.0, 0.0 );
|
||||
joint[ AVATAR_JOINT_RIGHT_WRIST ].bindPosePosition = glm::vec3( 0.12, 0.0, 0.0 );
|
||||
joint[ AVATAR_JOINT_RIGHT_FINGERTIPS ].bindPosePosition = glm::vec3( 0.1, 0.0, 0.0 );
|
||||
|
||||
joint[ AVATAR_JOINT_LEFT_HIP ].bindPosePosition = glm::vec3( -0.05, 0.0, 0.02 );
|
||||
joint[ AVATAR_JOINT_LEFT_KNEE ].bindPosePosition = glm::vec3( 0.00, -0.25, 0.00 );
|
||||
joint[ AVATAR_JOINT_LEFT_HEEL ].bindPosePosition = glm::vec3( 0.00, -0.23, 0.00 );
|
||||
joint[ AVATAR_JOINT_LEFT_TOES ].bindPosePosition = glm::vec3( 0.00, 0.00, -0.06 );
|
||||
|
||||
joint[ AVATAR_JOINT_RIGHT_HIP ].bindPosePosition = glm::vec3( 0.05, 0.0, 0.02 );
|
||||
joint[ AVATAR_JOINT_RIGHT_KNEE ].bindPosePosition = glm::vec3( 0.00, -0.25, 0.00 );
|
||||
joint[ AVATAR_JOINT_RIGHT_HEEL ].bindPosePosition = glm::vec3( 0.00, -0.23, 0.00 );
|
||||
joint[ AVATAR_JOINT_RIGHT_TOES ].bindPosePosition = glm::vec3( 0.00, 0.00, -0.06 );
|
||||
|
||||
// specify the default pose position
|
||||
joint[ AVATAR_JOINT_PELVIS ].defaultPosePosition = glm::vec3( 0.0, 0.0, 0.0 );
|
||||
joint[ AVATAR_JOINT_TORSO ].defaultPosePosition = glm::vec3( 0.0, 0.09, -0.01 );
|
||||
|
@ -77,9 +108,18 @@ void Skeleton::initialize() {
|
|||
joint[ AVATAR_JOINT_RIGHT_HEEL ].defaultPosePosition = glm::vec3( -0.01, -0.22, 0.08 );
|
||||
joint[ AVATAR_JOINT_RIGHT_TOES ].defaultPosePosition = glm::vec3( 0.00, -0.03, -0.05 );
|
||||
|
||||
// calculate bone length
|
||||
// calculate bone length, absolute bind positions/rotations
|
||||
for (int b = 0; b < NUM_AVATAR_JOINTS; b++) {
|
||||
joint[b].length = glm::length(joint[b].defaultPosePosition);
|
||||
|
||||
if (joint[b].parent == AVATAR_JOINT_NULL) {
|
||||
joint[b].absoluteBindPosePosition = joint[b].bindPosePosition;
|
||||
joint[b].absoluteBindPoseRotation = glm::quat();
|
||||
} else {
|
||||
joint[b].absoluteBindPosePosition = joint[ joint[b].parent ].absoluteBindPosePosition +
|
||||
joint[b].bindPosePosition;
|
||||
joint[b].absoluteBindPoseRotation = rotationBetween(JOINT_DIRECTION, joint[b].bindPosePosition);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -13,35 +13,36 @@
|
|||
|
||||
enum AvatarJointID
|
||||
{
|
||||
AVATAR_JOINT_NULL = -1,
|
||||
AVATAR_JOINT_PELVIS,
|
||||
AVATAR_JOINT_TORSO,
|
||||
AVATAR_JOINT_CHEST,
|
||||
AVATAR_JOINT_NECK_BASE,
|
||||
AVATAR_JOINT_HEAD_BASE,
|
||||
AVATAR_JOINT_HEAD_TOP,
|
||||
AVATAR_JOINT_LEFT_COLLAR,
|
||||
AVATAR_JOINT_LEFT_SHOULDER,
|
||||
AVATAR_JOINT_LEFT_ELBOW,
|
||||
AVATAR_JOINT_LEFT_WRIST,
|
||||
AVATAR_JOINT_LEFT_FINGERTIPS,
|
||||
AVATAR_JOINT_RIGHT_COLLAR,
|
||||
AVATAR_JOINT_RIGHT_SHOULDER,
|
||||
AVATAR_JOINT_RIGHT_ELBOW,
|
||||
AVATAR_JOINT_RIGHT_WRIST,
|
||||
AVATAR_JOINT_RIGHT_FINGERTIPS,
|
||||
AVATAR_JOINT_LEFT_HIP,
|
||||
AVATAR_JOINT_LEFT_KNEE,
|
||||
AVATAR_JOINT_LEFT_HEEL,
|
||||
AVATAR_JOINT_LEFT_TOES,
|
||||
AVATAR_JOINT_RIGHT_HIP,
|
||||
AVATAR_JOINT_RIGHT_KNEE,
|
||||
AVATAR_JOINT_RIGHT_HEEL,
|
||||
AVATAR_JOINT_RIGHT_TOES,
|
||||
AVATAR_JOINT_NULL = -1,
|
||||
AVATAR_JOINT_PELVIS,
|
||||
AVATAR_JOINT_TORSO,
|
||||
AVATAR_JOINT_CHEST,
|
||||
AVATAR_JOINT_NECK_BASE,
|
||||
AVATAR_JOINT_HEAD_BASE,
|
||||
AVATAR_JOINT_HEAD_TOP,
|
||||
AVATAR_JOINT_LEFT_COLLAR,
|
||||
AVATAR_JOINT_LEFT_SHOULDER,
|
||||
AVATAR_JOINT_LEFT_ELBOW,
|
||||
AVATAR_JOINT_LEFT_WRIST,
|
||||
AVATAR_JOINT_LEFT_FINGERTIPS,
|
||||
AVATAR_JOINT_RIGHT_COLLAR,
|
||||
AVATAR_JOINT_RIGHT_SHOULDER,
|
||||
AVATAR_JOINT_RIGHT_ELBOW,
|
||||
AVATAR_JOINT_RIGHT_WRIST,
|
||||
AVATAR_JOINT_RIGHT_FINGERTIPS,
|
||||
AVATAR_JOINT_LEFT_HIP,
|
||||
AVATAR_JOINT_LEFT_KNEE,
|
||||
AVATAR_JOINT_LEFT_HEEL,
|
||||
AVATAR_JOINT_LEFT_TOES,
|
||||
AVATAR_JOINT_RIGHT_HIP,
|
||||
AVATAR_JOINT_RIGHT_KNEE,
|
||||
AVATAR_JOINT_RIGHT_HEEL,
|
||||
AVATAR_JOINT_RIGHT_TOES,
|
||||
|
||||
NUM_AVATAR_JOINTS
|
||||
NUM_AVATAR_JOINTS
|
||||
};
|
||||
|
||||
const glm::vec3 JOINT_DIRECTION = glm::vec3(1.0f, 0.0f, 0.0f);
|
||||
|
||||
class Skeleton {
|
||||
public:
|
||||
|
@ -59,14 +60,18 @@ public:
|
|||
|
||||
struct AvatarJoint
|
||||
{
|
||||
AvatarJointID parent; // which joint is this joint connected to?
|
||||
glm::vec3 position; // the position at the "end" of the joint - in global space
|
||||
glm::vec3 defaultPosePosition; // the parent relative position when the avatar is in the default pose
|
||||
glm::quat rotation; // the parent-relative rotation (orientation) of the joint as a quaternion
|
||||
float length; // the length of vector between the joint and its parent
|
||||
AvatarJointID parent; // which joint is this joint connected to?
|
||||
glm::vec3 position; // the position at the "end" of the joint - in global space
|
||||
glm::vec3 defaultPosePosition; // the parent relative position when the avatar is in the default pose
|
||||
glm::vec3 bindPosePosition; // the parent relative position when the avatar is in the "T-pose"
|
||||
glm::vec3 absoluteBindPosePosition; // the absolute position when the avatar is in the "T-pose"
|
||||
glm::quat absoluteBindPoseRotation; // the absolute rotation when the avatar is in the "T-pose"
|
||||
float bindRadius; // the radius of the bone capsule that envelops the vertices to bind
|
||||
glm::quat rotation; // the parent-relative rotation (orientation) of the joint as a quaternion
|
||||
float length; // the length of vector connecting the joint and its parent
|
||||
};
|
||||
|
||||
AvatarJoint joint[ NUM_AVATAR_JOINTS ];
|
||||
AvatarJoint joint[ NUM_AVATAR_JOINTS ];
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -21,6 +21,8 @@
|
|||
#include "world.h"
|
||||
#include "Util.h"
|
||||
|
||||
#include "VoxelConstants.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
// no clue which versions are affected...
|
||||
|
@ -72,6 +74,27 @@ float angleBetween(const glm::vec3& v1, const glm::vec3& v2) {
|
|||
return acos((glm::dot(v1, v2)) / (glm::length(v1) * glm::length(v2))) * 180.f / PI;
|
||||
}
|
||||
|
||||
// Helper function return the rotation from the first vector onto the second
|
||||
glm::quat rotationBetween(const glm::vec3& v1, const glm::vec3& v2) {
|
||||
float angle = angleBetween(v1, v2);
|
||||
if (isnan(angle) || angle < EPSILON) {
|
||||
return glm::quat();
|
||||
}
|
||||
glm::vec3 axis = glm::cross(v1, v2);
|
||||
if (angle > 179.99f) { // 180 degree rotation; must use another axis
|
||||
axis = glm::cross(v1, glm::vec3(1.0f, 0.0f, 0.0f));
|
||||
float axisLength = glm::length(axis);
|
||||
if (axisLength < EPSILON) { // parallel to x; y will work
|
||||
axis = glm::normalize(glm::cross(v1, glm::vec3(0.0f, 1.0f, 0.0f)));
|
||||
} else {
|
||||
axis /= axisLength;
|
||||
}
|
||||
} else {
|
||||
axis = glm::normalize(glm::cross(v1, v2));
|
||||
}
|
||||
return glm::angleAxis(angle, axis);
|
||||
}
|
||||
|
||||
// Safe version of glm::eulerAngles; uses the factorization method described in David Eberly's
|
||||
// http://www.geometrictools.com/Documentation/EulerAngles.pdf (via Clyde,
|
||||
// https://github.com/threerings/clyde/blob/master/src/main/java/com/threerings/math/Quaternion.java)
|
||||
|
@ -394,9 +417,9 @@ void renderCircle(glm::vec3 position, float radius, glm::vec3 surfaceNormal, int
|
|||
|
||||
|
||||
void renderOrientationDirections(glm::vec3 position, const glm::quat& orientation, float size) {
|
||||
glm::vec3 pRight = position + orientation * AVATAR_RIGHT * size;
|
||||
glm::vec3 pUp = position + orientation * AVATAR_UP * size;
|
||||
glm::vec3 pFront = position + orientation * AVATAR_FRONT * size;
|
||||
glm::vec3 pRight = position + orientation * IDENTITY_RIGHT * size;
|
||||
glm::vec3 pUp = position + orientation * IDENTITY_UP * size;
|
||||
glm::vec3 pFront = position + orientation * IDENTITY_FRONT * size;
|
||||
|
||||
glColor3f(1.0f, 0.0f, 0.0f);
|
||||
glBegin(GL_LINE_STRIP);
|
||||
|
|
|
@ -45,6 +45,8 @@ void drawVector(glm::vec3* vector);
|
|||
|
||||
float angleBetween(const glm::vec3& v1, const glm::vec3& v2);
|
||||
|
||||
glm::quat rotationBetween(const glm::vec3& v1, const glm::vec3& v2);
|
||||
|
||||
glm::vec3 safeEulerAngles(const glm::quat& q);
|
||||
|
||||
glm::quat safeMix(const glm::quat& q1, const glm::quat& q2, float alpha);
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#include <PerfStat.h>
|
||||
#include <OctalCode.h>
|
||||
#include <pthread.h>
|
||||
#include "Application.h"
|
||||
#include "Log.h"
|
||||
#include "VoxelConstants.h"
|
||||
#include "InterfaceConfig.h"
|
||||
|
@ -37,14 +38,15 @@ GLfloat identityNormals[] = { 0,0,-1, 0,0,-1, 0,0,-1, 0,0,-1,
|
|||
-1,0,0, +1,0,0, +1,0,0, -1,0,0,
|
||||
-1,0,0, +1,0,0, +1,0,0, -1,0,0 };
|
||||
|
||||
GLubyte identityIndices[] = { 0,2,1, 0,3,2, // Z- .
|
||||
GLubyte identityIndices[] = { 0,2,1, 0,3,2, // Z-
|
||||
8,9,13, 8,13,12, // Y-
|
||||
16,23,19, 16,20,23, // X-
|
||||
17,18,22, 17,22,21, // X+
|
||||
10,11,15, 10,15,14, // Y+
|
||||
4,5,6, 4,6,7 }; // Z+ .
|
||||
4,5,6, 4,6,7 }; // Z+
|
||||
|
||||
VoxelSystem::VoxelSystem() : AgentData(NULL) {
|
||||
VoxelSystem::VoxelSystem(float treeScale, int maxVoxels) :
|
||||
AgentData(NULL), _treeScale(treeScale), _maxVoxels(maxVoxels) {
|
||||
_voxelsInReadArrays = _voxelsInWriteArrays = _voxelsUpdated = 0;
|
||||
_writeRenderFullVBO = true;
|
||||
_readRenderFullVBO = true;
|
||||
|
@ -241,10 +243,8 @@ void VoxelSystem::cleanupRemovedVoxels() {
|
|||
}
|
||||
|
||||
void VoxelSystem::copyWrittenDataToReadArraysFullVBOs() {
|
||||
int bytesOfVertices = (_voxelsInWriteArrays * VERTEX_POINTS_PER_VOXEL) * sizeof(GLfloat);
|
||||
int bytesOfColors = (_voxelsInWriteArrays * VERTEX_POINTS_PER_VOXEL) * sizeof(GLubyte);
|
||||
memcpy(_readVerticesArray, _writeVerticesArray, bytesOfVertices);
|
||||
memcpy(_readColorsArray, _writeColorsArray, bytesOfColors );
|
||||
copyWrittenDataSegmentToReadArrays(0, _voxelsInWriteArrays - 1);
|
||||
|
||||
_voxelsInReadArrays = _voxelsInWriteArrays;
|
||||
|
||||
// clear our dirty flags
|
||||
|
@ -271,47 +271,37 @@ void VoxelSystem::copyWrittenDataToReadArraysPartialVBOs() {
|
|||
if (!thisVoxelDirty) {
|
||||
// If we got here because because this voxel is NOT dirty, so the last dirty voxel was the one before
|
||||
// this one and so that's where the "segment" ends
|
||||
segmentEnd = i - 1;
|
||||
copyWrittenDataSegmentToReadArrays(segmentStart, i - 1);
|
||||
inSegment = false;
|
||||
int segmentLength = (segmentEnd - segmentStart) + 1;
|
||||
|
||||
GLintptr segmentStartAt = segmentStart * VERTEX_POINTS_PER_VOXEL * sizeof(GLfloat);
|
||||
GLsizeiptr segmentSizeBytes = segmentLength * VERTEX_POINTS_PER_VOXEL * sizeof(GLfloat);
|
||||
GLfloat* readVerticesAt = _readVerticesArray + (segmentStart * VERTEX_POINTS_PER_VOXEL);
|
||||
GLfloat* writeVerticesAt = _writeVerticesArray + (segmentStart * VERTEX_POINTS_PER_VOXEL);
|
||||
memcpy(readVerticesAt, writeVerticesAt, segmentSizeBytes);
|
||||
|
||||
segmentStartAt = segmentStart * VERTEX_POINTS_PER_VOXEL * sizeof(GLubyte);
|
||||
segmentSizeBytes = segmentLength * VERTEX_POINTS_PER_VOXEL * sizeof(GLubyte);
|
||||
GLubyte* readColorsAt = _readColorsArray + (segmentStart * VERTEX_POINTS_PER_VOXEL);
|
||||
GLubyte* writeColorsAt = _writeColorsArray + (segmentStart * VERTEX_POINTS_PER_VOXEL);
|
||||
memcpy(readColorsAt, writeColorsAt, segmentSizeBytes);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if we got to the end of the array, and we're in an active dirty segment...
|
||||
if (inSegment) {
|
||||
segmentEnd = _voxelsInWriteArrays - 1;
|
||||
int segmentLength = (segmentEnd - segmentStart) + 1;
|
||||
|
||||
GLintptr segmentStartAt = segmentStart * VERTEX_POINTS_PER_VOXEL * sizeof(GLfloat);
|
||||
GLsizeiptr segmentSizeBytes = segmentLength * VERTEX_POINTS_PER_VOXEL * sizeof(GLfloat);
|
||||
GLfloat* readVerticesAt = _readVerticesArray + (segmentStart * VERTEX_POINTS_PER_VOXEL);
|
||||
GLfloat* writeVerticesAt = _writeVerticesArray + (segmentStart * VERTEX_POINTS_PER_VOXEL);
|
||||
memcpy(readVerticesAt, writeVerticesAt, segmentSizeBytes);
|
||||
|
||||
segmentStartAt = segmentStart * VERTEX_POINTS_PER_VOXEL * sizeof(GLubyte);
|
||||
segmentSizeBytes = segmentLength * VERTEX_POINTS_PER_VOXEL * sizeof(GLubyte);
|
||||
GLubyte* readColorsAt = _readColorsArray + (segmentStart * VERTEX_POINTS_PER_VOXEL);
|
||||
GLubyte* writeColorsAt = _writeColorsArray + (segmentStart * VERTEX_POINTS_PER_VOXEL);
|
||||
memcpy(readColorsAt, writeColorsAt, segmentSizeBytes);
|
||||
copyWrittenDataSegmentToReadArrays(segmentStart, _voxelsInWriteArrays - 1);
|
||||
}
|
||||
|
||||
// update our length
|
||||
_voxelsInReadArrays = _voxelsInWriteArrays;
|
||||
}
|
||||
|
||||
void VoxelSystem::copyWrittenDataSegmentToReadArrays(glBufferIndex segmentStart, glBufferIndex segmentEnd) {
|
||||
int segmentLength = (segmentEnd - segmentStart) + 1;
|
||||
|
||||
GLintptr segmentStartAt = segmentStart * VERTEX_POINTS_PER_VOXEL * sizeof(GLfloat);
|
||||
GLsizeiptr segmentSizeBytes = segmentLength * VERTEX_POINTS_PER_VOXEL * sizeof(GLfloat);
|
||||
GLfloat* readVerticesAt = _readVerticesArray + (segmentStart * VERTEX_POINTS_PER_VOXEL);
|
||||
GLfloat* writeVerticesAt = _writeVerticesArray + (segmentStart * VERTEX_POINTS_PER_VOXEL);
|
||||
memcpy(readVerticesAt, writeVerticesAt, segmentSizeBytes);
|
||||
|
||||
segmentStartAt = segmentStart * VERTEX_POINTS_PER_VOXEL * sizeof(GLubyte);
|
||||
segmentSizeBytes = segmentLength * VERTEX_POINTS_PER_VOXEL * sizeof(GLubyte);
|
||||
GLubyte* readColorsAt = _readColorsArray + (segmentStart * VERTEX_POINTS_PER_VOXEL);
|
||||
GLubyte* writeColorsAt = _writeColorsArray + (segmentStart * VERTEX_POINTS_PER_VOXEL);
|
||||
memcpy(readColorsAt, writeColorsAt, segmentSizeBytes);
|
||||
}
|
||||
|
||||
void VoxelSystem::copyWrittenDataToReadArrays(bool fullVBOs) {
|
||||
PerformanceWarning warn(_renderWarningsOn, "copyWrittenDataToReadArrays()");
|
||||
if (_voxelsDirty && _voxelsUpdated) {
|
||||
|
@ -324,12 +314,11 @@ void VoxelSystem::copyWrittenDataToReadArrays(bool fullVBOs) {
|
|||
}
|
||||
|
||||
int VoxelSystem::newTreeToArrays(VoxelNode* node) {
|
||||
assert(_viewFrustum); // you must set up _viewFrustum before calling this
|
||||
int voxelsUpdated = 0;
|
||||
bool shouldRender = false; // assume we don't need to render it
|
||||
// if it's colored, we might need to render it!
|
||||
if (node->isColored()) {
|
||||
float distanceToNode = node->distanceToCamera(*_viewFrustum);
|
||||
float distanceToNode = node->distanceToCamera(*Application::getInstance()->getViewFrustum());
|
||||
float boundary = boundaryDistanceForRenderLevel(node->getLevel());
|
||||
float childBoundary = boundaryDistanceForRenderLevel(node->getLevel() + 1);
|
||||
bool inBoundary = (distanceToNode <= boundary);
|
||||
|
@ -364,7 +353,7 @@ int VoxelSystem::newTreeToArrays(VoxelNode* node) {
|
|||
|
||||
int VoxelSystem::updateNodeInArraysAsFullVBO(VoxelNode* node) {
|
||||
// If we've run out of room, then just bail...
|
||||
if (_voxelsInWriteArrays >= MAX_VOXELS_PER_SYSTEM) {
|
||||
if (_voxelsInWriteArrays >= _maxVoxels) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -375,12 +364,7 @@ int VoxelSystem::updateNodeInArraysAsFullVBO(VoxelNode* node) {
|
|||
|
||||
// populate the array with points for the 8 vertices
|
||||
// and RGB color for each added vertex
|
||||
for (int j = 0; j < VERTEX_POINTS_PER_VOXEL; j++ ) {
|
||||
GLfloat* writeVerticesAt = _writeVerticesArray + (nodeIndex * VERTEX_POINTS_PER_VOXEL);
|
||||
GLubyte* writeColorsAt = _writeColorsArray + (nodeIndex * VERTEX_POINTS_PER_VOXEL);
|
||||
*(writeVerticesAt+j) = startVertex[j % 3] + (identityVertices[j] * voxelScale);
|
||||
*(writeColorsAt +j) = node->getColor()[j % 3];
|
||||
}
|
||||
updateNodeInArrays(nodeIndex, startVertex, voxelScale, node->getColor());
|
||||
node->setBufferIndex(nodeIndex);
|
||||
_writeVoxelDirtyArray[nodeIndex] = true; // just in case we switch to Partial mode
|
||||
_voxelsInWriteArrays++; // our know vertices in the arrays
|
||||
|
@ -394,7 +378,7 @@ int VoxelSystem::updateNodeInArraysAsFullVBO(VoxelNode* node) {
|
|||
|
||||
int VoxelSystem::updateNodeInArraysAsPartialVBO(VoxelNode* node) {
|
||||
// If we've run out of room, then just bail...
|
||||
if (_voxelsInWriteArrays >= MAX_VOXELS_PER_SYSTEM) {
|
||||
if (_voxelsInWriteArrays >= _maxVoxels) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -426,17 +410,31 @@ int VoxelSystem::updateNodeInArraysAsPartialVBO(VoxelNode* node) {
|
|||
|
||||
// populate the array with points for the 8 vertices
|
||||
// and RGB color for each added vertex
|
||||
for (int j = 0; j < VERTEX_POINTS_PER_VOXEL; j++ ) {
|
||||
GLfloat* writeVerticesAt = _writeVerticesArray + (nodeIndex * VERTEX_POINTS_PER_VOXEL);
|
||||
GLubyte* writeColorsAt = _writeColorsArray + (nodeIndex * VERTEX_POINTS_PER_VOXEL);
|
||||
*(writeVerticesAt+j) = startVertex[j % 3] + (identityVertices[j] * voxelScale);
|
||||
*(writeColorsAt +j) = node->getColor()[j % 3];
|
||||
}
|
||||
updateNodeInArrays(nodeIndex, startVertex, voxelScale, node->getColor());
|
||||
|
||||
return 1; // updated!
|
||||
}
|
||||
return 0; // not-updated
|
||||
}
|
||||
|
||||
void VoxelSystem::updateNodeInArrays(glBufferIndex nodeIndex, const glm::vec3& startVertex,
|
||||
float voxelScale, const nodeColor& color) {
|
||||
for (int j = 0; j < VERTEX_POINTS_PER_VOXEL; j++ ) {
|
||||
GLfloat* writeVerticesAt = _writeVerticesArray + (nodeIndex * VERTEX_POINTS_PER_VOXEL);
|
||||
GLubyte* writeColorsAt = _writeColorsArray + (nodeIndex * VERTEX_POINTS_PER_VOXEL);
|
||||
*(writeVerticesAt+j) = startVertex[j % 3] + (identityVertices[j] * voxelScale);
|
||||
*(writeColorsAt +j) = color[j % 3];
|
||||
}
|
||||
}
|
||||
|
||||
glm::vec3 VoxelSystem::computeVoxelVertex(const glm::vec3& startVertex, float voxelScale, int index) const {
|
||||
const float* identityVertex = identityVertices + index * 3;
|
||||
return startVertex + glm::vec3(identityVertex[0], identityVertex[1], identityVertex[2]) * voxelScale;
|
||||
}
|
||||
|
||||
ProgramObject* VoxelSystem::_perlinModulateProgram = 0;
|
||||
GLuint VoxelSystem::_permutationNormalTextureID = 0;
|
||||
|
||||
void VoxelSystem::init() {
|
||||
|
||||
_renderWarningsOn = false;
|
||||
|
@ -452,23 +450,23 @@ void VoxelSystem::init() {
|
|||
_unusedArraySpace = 0;
|
||||
|
||||
// we will track individual dirty sections with these arrays of bools
|
||||
_writeVoxelDirtyArray = new bool[MAX_VOXELS_PER_SYSTEM];
|
||||
memset(_writeVoxelDirtyArray, false, MAX_VOXELS_PER_SYSTEM * sizeof(bool));
|
||||
_readVoxelDirtyArray = new bool[MAX_VOXELS_PER_SYSTEM];
|
||||
memset(_readVoxelDirtyArray, false, MAX_VOXELS_PER_SYSTEM * sizeof(bool));
|
||||
_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 * MAX_VOXELS_PER_SYSTEM];
|
||||
_readVerticesArray = new GLfloat[VERTEX_POINTS_PER_VOXEL * MAX_VOXELS_PER_SYSTEM];
|
||||
_writeVerticesArray = new GLfloat[VERTEX_POINTS_PER_VOXEL * _maxVoxels];
|
||||
_readVerticesArray = new GLfloat[VERTEX_POINTS_PER_VOXEL * _maxVoxels];
|
||||
|
||||
_writeColorsArray = new GLubyte[VERTEX_POINTS_PER_VOXEL * MAX_VOXELS_PER_SYSTEM];
|
||||
_readColorsArray = new GLubyte[VERTEX_POINTS_PER_VOXEL * MAX_VOXELS_PER_SYSTEM];
|
||||
_writeColorsArray = new GLubyte[VERTEX_POINTS_PER_VOXEL * _maxVoxels];
|
||||
_readColorsArray = new GLubyte[VERTEX_POINTS_PER_VOXEL * _maxVoxels];
|
||||
|
||||
GLuint* indicesArray = new GLuint[INDICES_PER_VOXEL * MAX_VOXELS_PER_SYSTEM];
|
||||
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 < MAX_VOXELS_PER_SYSTEM; n++) {
|
||||
for (int n = 0; n < _maxVoxels; n++) {
|
||||
// fill the indices array
|
||||
int voxelIndexOffset = n * INDICES_PER_VOXEL;
|
||||
GLuint* currentIndicesPos = indicesArray + voxelIndexOffset;
|
||||
|
@ -480,11 +478,11 @@ void VoxelSystem::init() {
|
|||
}
|
||||
}
|
||||
|
||||
GLfloat* normalsArray = new GLfloat[VERTEX_POINTS_PER_VOXEL * MAX_VOXELS_PER_SYSTEM];
|
||||
GLfloat* normalsArray = new GLfloat[VERTEX_POINTS_PER_VOXEL * _maxVoxels];
|
||||
GLfloat* normalsArrayEndPointer = normalsArray;
|
||||
|
||||
// populate the normalsArray
|
||||
for (int n = 0; n < MAX_VOXELS_PER_SYSTEM; n++) {
|
||||
for (int n = 0; n < _maxVoxels; n++) {
|
||||
for (int i = 0; i < VERTEX_POINTS_PER_VOXEL; i++) {
|
||||
*(normalsArrayEndPointer++) = identityNormals[i];
|
||||
}
|
||||
|
@ -493,32 +491,35 @@ void VoxelSystem::init() {
|
|||
// VBO for the verticesArray
|
||||
glGenBuffers(1, &_vboVerticesID);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, _vboVerticesID);
|
||||
glBufferData(GL_ARRAY_BUFFER, VERTEX_POINTS_PER_VOXEL * sizeof(GLfloat) * MAX_VOXELS_PER_SYSTEM, NULL, GL_DYNAMIC_DRAW);
|
||||
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) * MAX_VOXELS_PER_SYSTEM,
|
||||
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) * MAX_VOXELS_PER_SYSTEM, NULL, GL_DYNAMIC_DRAW);
|
||||
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) * MAX_VOXELS_PER_SYSTEM,
|
||||
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;
|
||||
|
||||
// create our simple fragment shader
|
||||
// create our simple fragment shader if we're the first system to init
|
||||
if (_perlinModulateProgram != 0) {
|
||||
return;
|
||||
}
|
||||
switchToResourcesParentIfRequired();
|
||||
_perlinModulateProgram = new ProgramObject();
|
||||
_perlinModulateProgram->addShaderFromSourceFile(QGLShader::Vertex, "resources/shaders/perlin_modulate.vert");
|
||||
|
@ -551,20 +552,7 @@ void VoxelSystem::init() {
|
|||
}
|
||||
|
||||
void VoxelSystem::updateFullVBOs() {
|
||||
glBufferIndex segmentStart = 0;
|
||||
glBufferIndex segmentEnd = _voxelsInReadArrays;
|
||||
|
||||
int segmentLength = (segmentEnd - segmentStart) + 1;
|
||||
GLintptr segmentStartAt = segmentStart * VERTEX_POINTS_PER_VOXEL * sizeof(GLfloat);
|
||||
GLsizeiptr segmentSizeBytes = segmentLength * VERTEX_POINTS_PER_VOXEL * sizeof(GLfloat);
|
||||
GLfloat* readVerticesFrom = _readVerticesArray + (segmentStart * VERTEX_POINTS_PER_VOXEL);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, _vboVerticesID);
|
||||
glBufferSubData(GL_ARRAY_BUFFER, segmentStartAt, segmentSizeBytes, readVerticesFrom);
|
||||
segmentStartAt = segmentStart * VERTEX_POINTS_PER_VOXEL * sizeof(GLubyte);
|
||||
segmentSizeBytes = segmentLength * VERTEX_POINTS_PER_VOXEL * sizeof(GLubyte);
|
||||
GLubyte* readColorsFrom = _readColorsArray + (segmentStart * VERTEX_POINTS_PER_VOXEL);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, _vboColorsID);
|
||||
glBufferSubData(GL_ARRAY_BUFFER, segmentStartAt, segmentSizeBytes, readColorsFrom);
|
||||
updateVBOSegment(0, _voxelsInReadArrays);
|
||||
|
||||
// consider the _readVoxelDirtyArray[] clean!
|
||||
memset(_readVoxelDirtyArray, false, _voxelsInReadArrays * sizeof(bool));
|
||||
|
@ -586,39 +574,17 @@ void VoxelSystem::updatePartialVBOs() {
|
|||
if (!thisVoxelDirty) {
|
||||
// If we got here because because this voxel is NOT dirty, so the last dirty voxel was the one before
|
||||
// this one and so that's where the "segment" ends
|
||||
segmentEnd = i - 1;
|
||||
updateVBOSegment(segmentStart, i - 1);
|
||||
inSegment = false;
|
||||
int segmentLength = (segmentEnd - segmentStart) + 1;
|
||||
GLintptr segmentStartAt = segmentStart * VERTEX_POINTS_PER_VOXEL * sizeof(GLfloat);
|
||||
GLsizeiptr segmentSizeBytes = segmentLength * VERTEX_POINTS_PER_VOXEL * sizeof(GLfloat);
|
||||
GLfloat* readVerticesFrom = _readVerticesArray + (segmentStart * VERTEX_POINTS_PER_VOXEL);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, _vboVerticesID);
|
||||
glBufferSubData(GL_ARRAY_BUFFER, segmentStartAt, segmentSizeBytes, readVerticesFrom);
|
||||
segmentStartAt = segmentStart * VERTEX_POINTS_PER_VOXEL * sizeof(GLubyte);
|
||||
segmentSizeBytes = segmentLength * VERTEX_POINTS_PER_VOXEL * sizeof(GLubyte);
|
||||
GLubyte* readColorsFrom = _readColorsArray + (segmentStart * VERTEX_POINTS_PER_VOXEL);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, _vboColorsID);
|
||||
glBufferSubData(GL_ARRAY_BUFFER, segmentStartAt, segmentSizeBytes, readColorsFrom);
|
||||
}
|
||||
_readVoxelDirtyArray[i] = false; // consider us clean!
|
||||
}
|
||||
}
|
||||
|
||||
// if we got to the end of the array, and we're in an active dirty segment...
|
||||
if (inSegment) {
|
||||
segmentEnd = _voxelsInReadArrays - 1;
|
||||
if (inSegment) {
|
||||
updateVBOSegment(segmentStart, _voxelsInReadArrays - 1);
|
||||
inSegment = false;
|
||||
int segmentLength = (segmentEnd - segmentStart) + 1;
|
||||
GLintptr segmentStartAt = segmentStart * VERTEX_POINTS_PER_VOXEL * sizeof(GLfloat);
|
||||
GLsizeiptr segmentSizeBytes = segmentLength * VERTEX_POINTS_PER_VOXEL * sizeof(GLfloat);
|
||||
GLfloat* readVerticesFrom = _readVerticesArray + (segmentStart * VERTEX_POINTS_PER_VOXEL);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, _vboVerticesID);
|
||||
glBufferSubData(GL_ARRAY_BUFFER, segmentStartAt, segmentSizeBytes, readVerticesFrom);
|
||||
segmentStartAt = segmentStart * VERTEX_POINTS_PER_VOXEL * sizeof(GLubyte);
|
||||
segmentSizeBytes = segmentLength * VERTEX_POINTS_PER_VOXEL * sizeof(GLubyte);
|
||||
GLubyte* readColorsFrom = _readColorsArray + (segmentStart * VERTEX_POINTS_PER_VOXEL);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, _vboColorsID);
|
||||
glBufferSubData(GL_ARRAY_BUFFER, segmentStartAt, segmentSizeBytes, readColorsFrom);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -640,14 +606,28 @@ void VoxelSystem::updateVBOs() {
|
|||
_callsToTreesToArrays = 0; // clear it
|
||||
}
|
||||
|
||||
void VoxelSystem::updateVBOSegment(glBufferIndex segmentStart, glBufferIndex segmentEnd) {
|
||||
int segmentLength = (segmentEnd - segmentStart) + 1;
|
||||
GLintptr segmentStartAt = segmentStart * VERTEX_POINTS_PER_VOXEL * sizeof(GLfloat);
|
||||
GLsizeiptr segmentSizeBytes = segmentLength * VERTEX_POINTS_PER_VOXEL * sizeof(GLfloat);
|
||||
GLfloat* readVerticesFrom = _readVerticesArray + (segmentStart * VERTEX_POINTS_PER_VOXEL);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, _vboVerticesID);
|
||||
glBufferSubData(GL_ARRAY_BUFFER, segmentStartAt, segmentSizeBytes, readVerticesFrom);
|
||||
segmentStartAt = segmentStart * VERTEX_POINTS_PER_VOXEL * sizeof(GLubyte);
|
||||
segmentSizeBytes = segmentLength * VERTEX_POINTS_PER_VOXEL * sizeof(GLubyte);
|
||||
GLubyte* readColorsFrom = _readColorsArray + (segmentStart * VERTEX_POINTS_PER_VOXEL);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, _vboColorsID);
|
||||
glBufferSubData(GL_ARRAY_BUFFER, segmentStartAt, segmentSizeBytes, readColorsFrom);
|
||||
}
|
||||
|
||||
void VoxelSystem::render(bool texture) {
|
||||
PerformanceWarning warn(_renderWarningsOn, "render()");
|
||||
|
||||
// get the lock so that the update thread won't change anything
|
||||
pthread_mutex_lock(&_bufferWriteLock);
|
||||
|
||||
glPushMatrix();
|
||||
updateVBOs();
|
||||
|
||||
// tell OpenGL where to find vertex and color information
|
||||
glEnableClientState(GL_VERTEX_ARRAY);
|
||||
glEnableClientState(GL_NORMAL_ARRAY);
|
||||
|
@ -662,10 +642,7 @@ void VoxelSystem::render(bool texture) {
|
|||
glBindBuffer(GL_ARRAY_BUFFER, _vboColorsID);
|
||||
glColorPointer(3, GL_UNSIGNED_BYTE, 0, 0);
|
||||
|
||||
if (texture) {
|
||||
_perlinModulateProgram->bind();
|
||||
glBindTexture(GL_TEXTURE_2D, _permutationNormalTextureID);
|
||||
}
|
||||
applyScaleAndBindProgram(texture);
|
||||
|
||||
// for performance, disable blending and enable backface culling
|
||||
glDisable(GL_BLEND);
|
||||
|
@ -673,17 +650,13 @@ void VoxelSystem::render(bool texture) {
|
|||
|
||||
// draw the number of voxels we have
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _vboIndicesID);
|
||||
glScalef(TREE_SCALE, TREE_SCALE, TREE_SCALE);
|
||||
glDrawRangeElementsEXT(GL_TRIANGLES, 0, VERTICES_PER_VOXEL * _voxelsInReadArrays - 1,
|
||||
36 * _voxelsInReadArrays, GL_UNSIGNED_INT, 0);
|
||||
|
||||
glEnable(GL_BLEND);
|
||||
glDisable(GL_CULL_FACE);
|
||||
|
||||
if (texture) {
|
||||
_perlinModulateProgram->release();
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
}
|
||||
removeScaleAndReleaseProgram(texture);
|
||||
|
||||
// deactivate vertex and color arrays after drawing
|
||||
glDisableClientState(GL_VERTEX_ARRAY);
|
||||
|
@ -693,11 +666,28 @@ void VoxelSystem::render(bool texture) {
|
|||
// bind with 0 to switch back to normal operation
|
||||
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
|
||||
|
||||
pthread_mutex_unlock(&_bufferWriteLock);
|
||||
}
|
||||
|
||||
void VoxelSystem::applyScaleAndBindProgram(bool texture) {
|
||||
glPushMatrix();
|
||||
glScalef(_treeScale, _treeScale, _treeScale);
|
||||
|
||||
if (texture) {
|
||||
_perlinModulateProgram->bind();
|
||||
glBindTexture(GL_TEXTURE_2D, _permutationNormalTextureID);
|
||||
}
|
||||
}
|
||||
|
||||
void VoxelSystem::removeScaleAndReleaseProgram(bool texture) {
|
||||
// scale back down to 1 so heads aren't massive
|
||||
glPopMatrix();
|
||||
|
||||
pthread_mutex_unlock(&_bufferWriteLock);
|
||||
if (texture) {
|
||||
_perlinModulateProgram->release();
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
}
|
||||
}
|
||||
|
||||
int VoxelSystem::_nodeCount = 0;
|
||||
|
@ -867,7 +857,7 @@ bool VoxelSystem::removeOutOfViewOperation(VoxelNode* node, void* extraData) {
|
|||
for (int i = 0; i < NUMBER_OF_CHILDREN; i++) {
|
||||
VoxelNode* childNode = node->getChildAtIndex(i);
|
||||
if (childNode) {
|
||||
ViewFrustum::location inFrustum = childNode->inFrustum(*thisVoxelSystem->_viewFrustum);
|
||||
ViewFrustum::location inFrustum = childNode->inFrustum(*Application::getInstance()->getViewFrustum());
|
||||
switch (inFrustum) {
|
||||
case ViewFrustum::OUTSIDE: {
|
||||
args->nodesOutside++;
|
||||
|
@ -919,9 +909,9 @@ bool VoxelSystem::hasViewChanged() {
|
|||
}
|
||||
|
||||
// If our viewFrustum has changed since our _lastKnowViewFrustum
|
||||
if (_viewFrustum && !_lastStableViewFrustum.matches(_viewFrustum)) {
|
||||
if (!_lastStableViewFrustum.matches(Application::getInstance()->getViewFrustum())) {
|
||||
result = true;
|
||||
_lastStableViewFrustum = *_viewFrustum; // save last stable
|
||||
_lastStableViewFrustum = *Application::getInstance()->getViewFrustum(); // save last stable
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
|
|
@ -16,7 +16,6 @@
|
|||
#include <AgentData.h>
|
||||
#include <VoxelTree.h>
|
||||
#include <ViewFrustum.h>
|
||||
#include "Avatar.h"
|
||||
#include "Camera.h"
|
||||
#include "Util.h"
|
||||
#include "world.h"
|
||||
|
@ -27,22 +26,18 @@ const int NUM_CHILDREN = 8;
|
|||
|
||||
class VoxelSystem : public AgentData {
|
||||
public:
|
||||
VoxelSystem();
|
||||
VoxelSystem(float treeScale = TREE_SCALE, int maxVoxels = MAX_VOXELS_PER_SYSTEM);
|
||||
~VoxelSystem();
|
||||
|
||||
int parseData(unsigned char* sourceBuffer, int numBytes);
|
||||
|
||||
void setViewFrustum(ViewFrustum* viewFrustum) { _viewFrustum = viewFrustum; };
|
||||
|
||||
void init();
|
||||
virtual void init();
|
||||
void simulate(float deltaTime) { };
|
||||
void render(bool texture);
|
||||
|
||||
unsigned long getVoxelsUpdated() const {return _voxelsUpdated;};
|
||||
unsigned long getVoxelsRendered() const {return _voxelsInReadArrays;};
|
||||
|
||||
void setViewerAvatar(Avatar *newViewerAvatar) { _viewerAvatar = newViewerAvatar; };
|
||||
void setCamera(Camera* newCamera) { _camera = newCamera; };
|
||||
void loadVoxelsFile(const char* fileName,bool wantColorRandomizer);
|
||||
void writeToSVOFile(const char* filename, VoxelNode* node) const;
|
||||
bool readFromSVOFile(const char* filename);
|
||||
|
@ -66,7 +61,7 @@ public:
|
|||
void setRenderPipelineWarnings(bool on) { _renderWarningsOn = on; };
|
||||
bool getRenderPipelineWarnings() const { return _renderWarningsOn; };
|
||||
|
||||
void removeOutOfView();
|
||||
virtual void removeOutOfView();
|
||||
bool hasViewChanged();
|
||||
bool isViewChanging();
|
||||
|
||||
|
@ -88,6 +83,22 @@ public:
|
|||
|
||||
void copySubTreeIntoNewTree(VoxelNode* startNode, VoxelTree* destinationTree, bool rebaseToRoot);
|
||||
void copyFromTreeIntoSubTree(VoxelTree* sourceTree, VoxelNode* destinationNode);
|
||||
|
||||
protected:
|
||||
float _treeScale;
|
||||
int _maxVoxels;
|
||||
VoxelTree* _tree;
|
||||
|
||||
glm::vec3 computeVoxelVertex(const glm::vec3& startVertex, float voxelScale, int index) const;
|
||||
|
||||
void setupNewVoxelsForDrawing();
|
||||
|
||||
virtual void updateNodeInArrays(glBufferIndex nodeIndex, const glm::vec3& startVertex,
|
||||
float voxelScale, const nodeColor& color);
|
||||
virtual void copyWrittenDataSegmentToReadArrays(glBufferIndex segmentStart, glBufferIndex segmentEnd);
|
||||
virtual void updateVBOSegment(glBufferIndex segmentStart, glBufferIndex segmentEnd);
|
||||
virtual void applyScaleAndBindProgram(bool texture);
|
||||
virtual void removeScaleAndReleaseProgram(bool texture);
|
||||
|
||||
private:
|
||||
// disallow copying of VoxelSystem objects
|
||||
|
@ -116,13 +127,12 @@ private:
|
|||
void copyWrittenDataToReadArraysFullVBOs();
|
||||
void copyWrittenDataToReadArraysPartialVBOs();
|
||||
|
||||
void updateVBOs();
|
||||
|
||||
// these are kinda hacks, used by getDistanceFromViewRangeOperation() probably shouldn't be here
|
||||
static float _maxDistance;
|
||||
static float _minDistance;
|
||||
|
||||
Avatar* _viewerAvatar;
|
||||
Camera* _camera;
|
||||
VoxelTree* _tree;
|
||||
GLfloat* _readVerticesArray;
|
||||
GLubyte* _readColorsArray;
|
||||
GLfloat* _writeVerticesArray;
|
||||
|
@ -130,8 +140,8 @@ private:
|
|||
bool* _writeVoxelDirtyArray;
|
||||
bool* _readVoxelDirtyArray;
|
||||
unsigned long _voxelsUpdated;
|
||||
unsigned long _voxelsInWriteArrays;
|
||||
unsigned long _voxelsInReadArrays;
|
||||
unsigned long _voxelsInWriteArrays;
|
||||
unsigned long _unusedArraySpace;
|
||||
|
||||
bool _writeRenderFullVBO;
|
||||
|
@ -149,26 +159,21 @@ private:
|
|||
pthread_mutex_t _bufferWriteLock;
|
||||
pthread_mutex_t _treeLock;
|
||||
|
||||
ProgramObject* _perlinModulateProgram;
|
||||
GLuint _permutationNormalTextureID;
|
||||
|
||||
ViewFrustum* _viewFrustum;
|
||||
ViewFrustum _lastKnowViewFrustum;
|
||||
ViewFrustum _lastStableViewFrustum;
|
||||
|
||||
int newTreeToArrays(VoxelNode *currentNode);
|
||||
void cleanupRemovedVoxels();
|
||||
|
||||
void setupNewVoxelsForDrawing();
|
||||
void copyWrittenDataToReadArrays(bool fullVBOs);
|
||||
|
||||
void updateFullVBOs(); // all voxels in the VBO
|
||||
void updatePartialVBOs(); // multiple segments, only dirty voxels
|
||||
|
||||
bool _voxelsDirty;
|
||||
|
||||
public:
|
||||
void updateVBOs();
|
||||
void updateFullVBOs(); // all voxels in the VBO
|
||||
void updatePartialVBOs(); // multiple segments, only dirty voxels
|
||||
|
||||
static ProgramObject* _perlinModulateProgram;
|
||||
static GLuint _permutationNormalTextureID;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -15,4 +15,7 @@ include(${MACRO_DIR}/IncludeGLM.cmake)
|
|||
include_glm(${TARGET_NAME} ${ROOT_DIR})
|
||||
|
||||
include(${MACRO_DIR}/LinkHifiLibrary.cmake)
|
||||
link_hifi_library(shared ${TARGET_NAME} ${ROOT_DIR})
|
||||
link_hifi_library(shared ${TARGET_NAME} ${ROOT_DIR})
|
||||
|
||||
# link in the hifi voxels library
|
||||
link_hifi_library(voxels ${TARGET_NAME} ${ROOT_DIR})
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
#include <PacketHeaders.h>
|
||||
|
||||
#include "AvatarData.h"
|
||||
#include <VoxelConstants.h>
|
||||
|
||||
using namespace std;
|
||||
|
||||
|
@ -75,8 +76,9 @@ int AvatarData::getBroadcastData(unsigned char* destinationBuffer) {
|
|||
memcpy(destinationBuffer, &_headData->_leanForward, sizeof(_headData->_leanForward));
|
||||
destinationBuffer += sizeof(_headData->_leanForward);
|
||||
|
||||
// Hand Position
|
||||
memcpy(destinationBuffer, &_handPosition, sizeof(float) * 3);
|
||||
// Hand Position - is relative to body position
|
||||
glm::vec3 handPositionRelative = _handPosition - _position;
|
||||
memcpy(destinationBuffer, &handPositionRelative, sizeof(float) * 3);
|
||||
destinationBuffer += sizeof(float) * 3;
|
||||
|
||||
// Lookat Position
|
||||
|
@ -158,8 +160,10 @@ int AvatarData::parseData(unsigned char* sourceBuffer, int numBytes) {
|
|||
memcpy(&_headData->_leanForward, sourceBuffer, sizeof(_headData->_leanForward));
|
||||
sourceBuffer += sizeof(_headData->_leanForward);
|
||||
|
||||
// Hand Position
|
||||
memcpy(&_handPosition, sourceBuffer, sizeof(float) * 3);
|
||||
// Hand Position - is relative to body position
|
||||
glm::vec3 handPositionRelative;
|
||||
memcpy(&handPositionRelative, sourceBuffer, sizeof(float) * 3);
|
||||
_handPosition = _position + handPositionRelative;
|
||||
sourceBuffer += sizeof(float) * 3;
|
||||
|
||||
// Lookat Position
|
||||
|
@ -202,9 +206,7 @@ int AvatarData::parseData(unsigned char* sourceBuffer, int numBytes) {
|
|||
}
|
||||
|
||||
glm::vec3 AvatarData::calculateCameraDirection() const {
|
||||
const glm::vec3 IDENTITY_FRONT = glm::vec3( 0.0f, 0.0f, 1.0f);
|
||||
glm::mat4 rotationMatrix = glm::mat4_cast(_cameraOrientation);
|
||||
glm::vec3 direction = glm::vec3(glm::vec4(IDENTITY_FRONT, 0.0f) * rotationMatrix);
|
||||
glm::vec3 direction = glm::vec3(_cameraOrientation * glm::vec4(IDENTITY_FRONT, 0.0f));
|
||||
return direction;
|
||||
}
|
||||
|
||||
|
@ -323,4 +325,4 @@ int unpackFloatFromByte(unsigned char* buffer, float& value, float scaleBy) {
|
|||
memcpy(&holder, buffer, sizeof(holder));
|
||||
value = ((float)holder / (float) 255) * scaleBy;
|
||||
return sizeof(holder);
|
||||
}
|
||||
}
|
|
@ -10,6 +10,7 @@
|
|||
#define __hifi__AvatarData__
|
||||
|
||||
#include <string>
|
||||
#include <inttypes.h>
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
#include <glm/gtc/quaternion.hpp>
|
||||
|
@ -22,15 +23,8 @@ const int WANT_COLOR_AT_BIT = 1;
|
|||
const int WANT_DELTA_AT_BIT = 2;
|
||||
const int KEY_STATE_START_BIT = 3; // 4th and 5th bits
|
||||
const int HAND_STATE_START_BIT = 5; // 6th and 7th bits
|
||||
|
||||
const float MAX_AUDIO_LOUDNESS = 1000.0; // close enough for mouth animation
|
||||
|
||||
|
||||
// this is where the coordinate system is represented
|
||||
const glm::vec3 AVATAR_RIGHT = glm::vec3(1.0f, 0.0f, 0.0f);
|
||||
const glm::vec3 AVATAR_UP = glm::vec3(0.0f, 1.0f, 0.0f);
|
||||
const glm::vec3 AVATAR_FRONT = glm::vec3(0.0f, 0.0f, -1.0f);
|
||||
|
||||
enum KeyState
|
||||
{
|
||||
NO_KEY_DOWN = 0,
|
||||
|
@ -164,5 +158,4 @@ int unpackClipValueFromTwoByte(unsigned char* buffer, float& clipValue);
|
|||
int packFloatToByte(unsigned char* buffer, float value, float scaleBy);
|
||||
int unpackFloatFromByte(unsigned char* buffer, float& value, float scaleBy);
|
||||
|
||||
|
||||
#endif /* defined(__hifi__AvatarData__) */
|
||||
|
|
|
@ -106,7 +106,7 @@ int getSemiNibbleAt(unsigned char& byte, int bitIndex) {
|
|||
}
|
||||
|
||||
void setSemiNibbleAt(unsigned char& byte, int bitIndex, int value) {
|
||||
assert(value <= 3 && value >= 0);
|
||||
//assert(value <= 3 && value >= 0);
|
||||
byte += ((value & 3) << (7 - bitIndex)); // semi-nibbles store 00, 01, 10, or 11
|
||||
}
|
||||
|
||||
|
|
|
@ -292,13 +292,16 @@ glm::vec3 AABox::getClosestPointOnFace(const glm::vec4& origin, const glm::vec4&
|
|||
glm::vec4 diagonals[] = { secondAxisMinPlane + thirdAxisMaxPlane + offset,
|
||||
secondAxisMaxPlane + thirdAxisMaxPlane + offset };
|
||||
|
||||
float minDistance = FLT_MAX;
|
||||
for (int i = 0; i < sizeof(diagonals) / sizeof(diagonals[0]); i++) {
|
||||
float divisor = glm::dot(direction, diagonals[i]);
|
||||
if (fabs(divisor) < EPSILON) {
|
||||
continue; // segment is parallel to diagonal plane
|
||||
}
|
||||
float directionalDistance = -glm::dot(origin, diagonals[i]) / divisor;
|
||||
return getClosestPointOnFace(glm::vec3(origin + direction * directionalDistance), face);
|
||||
minDistance = glm::min(-glm::dot(origin, diagonals[i]) / divisor, minDistance);
|
||||
}
|
||||
if (minDistance != FLT_MAX) {
|
||||
return getClosestPointOnFace(glm::vec3(origin + direction * minDistance), face);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#include <glm/gtx/transform.hpp>
|
||||
|
||||
#include "ViewFrustum.h"
|
||||
#include "VoxelConstants.h"
|
||||
#include "SharedUtil.h"
|
||||
#include "Log.h"
|
||||
|
||||
|
@ -38,20 +39,10 @@ ViewFrustum::ViewFrustum() :
|
|||
_nearBottomRight(0,0,0) { }
|
||||
|
||||
void ViewFrustum::setOrientation(const glm::quat& orientationAsQuaternion) {
|
||||
glm::quat quat;
|
||||
quat = quat * orientationAsQuaternion;
|
||||
|
||||
// this is where the coordinate system is represented
|
||||
const glm::vec3 IDENTITY_RIGHT = glm::vec3(-1.0f, 0.0f, 0.0f);
|
||||
const glm::vec3 IDENTITY_UP = glm::vec3( 0.0f, 1.0f, 0.0f);
|
||||
const glm::vec3 IDENTITY_FRONT = glm::vec3( 0.0f, 0.0f, 1.0f);
|
||||
|
||||
glm::mat4 rotationMatrix = glm::mat4_cast(quat);
|
||||
|
||||
_orientation = orientationAsQuaternion;
|
||||
_right = glm::vec3(glm::vec4(IDENTITY_RIGHT, 0.0f) * rotationMatrix);
|
||||
_up = glm::vec3(glm::vec4(IDENTITY_UP, 0.0f) * rotationMatrix);
|
||||
_direction = glm::vec3(glm::vec4(IDENTITY_FRONT, 0.0f) * rotationMatrix);
|
||||
_right = glm::vec3(orientationAsQuaternion * glm::vec4(IDENTITY_RIGHT, 0.0f));
|
||||
_up = glm::vec3(orientationAsQuaternion * glm::vec4(IDENTITY_UP, 0.0f));
|
||||
_direction = glm::vec3(orientationAsQuaternion * glm::vec4(IDENTITY_FRONT, 0.0f));
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -15,6 +15,11 @@
|
|||
#include <limits.h>
|
||||
#include <OctalCode.h>
|
||||
|
||||
// this is where the coordinate system is represented
|
||||
const glm::vec3 IDENTITY_RIGHT = glm::vec3( 1.0f, 0.0f, 0.0f);
|
||||
const glm::vec3 IDENTITY_UP = glm::vec3( 0.0f, 1.0f, 0.0f);
|
||||
const glm::vec3 IDENTITY_FRONT = glm::vec3( 0.0f, 0.0f,-1.0f);
|
||||
|
||||
const int TREE_SCALE = 128;
|
||||
|
||||
const int NUMBER_OF_CHILDREN = 8;
|
||||
|
|
Loading…
Reference in a new issue