mirror of
https://github.com/overte-org/overte.git
synced 2025-04-22 11:53:28 +02:00
Merge branch 'master' of https://github.com/worklist/hifi into avatardata_optimization
This commit is contained in:
commit
6e70f3b187
15 changed files with 712 additions and 194 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");
|
||||
|
@ -1143,6 +1148,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 +1500,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 +1543,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 +1611,9 @@ 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);
|
||||
|
||||
_networkAccessManager = new QNetworkAccessManager(this);
|
||||
_settings = new QSettings("High Fidelity", "Interface", this);
|
||||
}
|
||||
|
||||
void Application::updateFrustumRenderModeAction() {
|
||||
|
@ -1613,8 +1648,6 @@ void Application::initDisplay() {
|
|||
|
||||
void Application::init() {
|
||||
_voxels.init();
|
||||
_voxels.setViewerAvatar(&_myAvatar);
|
||||
_voxels.setCamera(&_myCamera);
|
||||
|
||||
_environment.init();
|
||||
|
||||
|
@ -1625,11 +1658,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();
|
||||
|
@ -1910,7 +1946,7 @@ void Application::displayOculus(Camera& whichCamera) {
|
|||
|
||||
glPopMatrix();
|
||||
}
|
||||
|
||||
|
||||
void Application::displaySide(Camera& whichCamera) {
|
||||
// transform by eye offset
|
||||
|
||||
|
@ -2493,7 +2529,9 @@ QAction* Application::checkedVoxelModeAction() const {
|
|||
|
||||
void Application::attachNewHeadToAgent(Agent* newAgent) {
|
||||
if (newAgent->getLinkedData() == NULL) {
|
||||
newAgent->setLinkedData(new Avatar(newAgent));
|
||||
Avatar* newAvatar = new Avatar(newAgent);
|
||||
newAvatar->init();
|
||||
newAgent->setLinkedData(newAvatar);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -37,6 +37,8 @@ class QGLWidget;
|
|||
class QKeyEvent;
|
||||
class QMainWindow;
|
||||
class QMouseEvent;
|
||||
class QNetworkAccessManager;
|
||||
class QSettings;
|
||||
class QWheelEvent;
|
||||
|
||||
class Agent;
|
||||
|
@ -65,10 +67,13 @@ public:
|
|||
|
||||
Avatar* getAvatar() { return &_myAvatar; }
|
||||
Camera* getCamera() { return &_myCamera; }
|
||||
ViewFrustum* getViewFrustum() { return &_viewFrustum; }
|
||||
VoxelSystem* getVoxels() { return &_voxels; }
|
||||
Environment* getEnvironment() { return &_environment; }
|
||||
bool shouldEchoAudio() { return _echoAudioMode->isChecked(); }
|
||||
|
||||
QNetworkAccessManager* getNetworkAccessManager() { return _networkAccessManager; }
|
||||
|
||||
/*!
|
||||
@fn getSettingBool
|
||||
@brief A function for getting boolean settings from the settings file.
|
||||
|
@ -126,6 +131,8 @@ private slots:
|
|||
void idle();
|
||||
void terminate();
|
||||
|
||||
void editPreferences();
|
||||
|
||||
void pair();
|
||||
|
||||
void setHead(bool head);
|
||||
|
@ -235,6 +242,9 @@ private:
|
|||
QAction* _fullScreenMode; // whether we are in full screen mode
|
||||
QAction* _frustumRenderModeAction;
|
||||
|
||||
QNetworkAccessManager* _networkAccessManager;
|
||||
QSettings* _settings;
|
||||
|
||||
SerialInterface _serialPort;
|
||||
bool _displayLevels;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
@ -88,7 +87,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 +264,10 @@ Avatar::~Avatar() {
|
|||
delete _balls;
|
||||
}
|
||||
|
||||
void Avatar::init() {
|
||||
_voxels.init();
|
||||
}
|
||||
|
||||
void Avatar::reset() {
|
||||
_head.reset();
|
||||
}
|
||||
|
@ -1015,20 +1019,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 +1074,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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1126,6 +1142,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,6 +1250,11 @@ void Avatar::setHeadFromGyros(glm::vec3* eulerAngles, glm::vec3* angularVelocity
|
|||
}
|
||||
}
|
||||
|
||||
void Avatar::getBodyBallTransform(AvatarJointID jointID, glm::vec3& position, glm::quat& rotation) const {
|
||||
position = _bodyBall[jointID].position;
|
||||
rotation = _bodyBall[jointID].rotation;
|
||||
}
|
||||
|
||||
void Avatar::writeAvatarDataToFile() {
|
||||
Application::getInstance()->setSetting("avatarPos", _position);
|
||||
Application::getInstance()->setSetting("avatarRotation", glm::vec3(_bodyYaw, _bodyPitch, _bodyRoll));
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#include <AvatarData.h>
|
||||
#include "world.h"
|
||||
#include "AvatarTouch.h"
|
||||
#include "AvatarVoxelSystem.h"
|
||||
#include "InterfaceConfig.h"
|
||||
#include "SerialInterface.h"
|
||||
#include "Balls.h"
|
||||
|
@ -80,6 +81,7 @@ public:
|
|||
Avatar(Agent* owningAgent = NULL);
|
||||
~Avatar();
|
||||
|
||||
void init();
|
||||
void reset();
|
||||
void simulate(float deltaTime, Transmitter* transmitter);
|
||||
void updateHeadFromGyros(float frametime, SerialInterface * serialInterface);
|
||||
|
@ -98,6 +100,7 @@ public:
|
|||
void setOrientation (const glm::quat& orientation);
|
||||
|
||||
//getters
|
||||
const Skeleton& getSkeleton () const { return _skeleton;}
|
||||
float getHeadYawRate () const { return _head.yawRate;}
|
||||
float getBodyYaw () const { return _bodyYaw;}
|
||||
bool getIsNearInteractingOther () const { return _avatarTouch.getAbleToReachOtherAvatar();}
|
||||
|
@ -116,6 +119,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 +129,9 @@ public:
|
|||
void addThrust(glm::vec3 newThrust) { _thrust += newThrust; };
|
||||
glm::vec3 getThrust() { return _thrust; };
|
||||
|
||||
// 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 +147,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)
|
||||
|
@ -181,6 +190,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__) */
|
|
@ -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
|
||||
|
|
|
@ -72,6 +72,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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue