mirror of
https://github.com/Armored-Dragon/overte.git
synced 2025-03-11 16:13:16 +01:00
Merge branch 'master' of https://github.com/worklist/hifi into keep_local_voxels
Conflicts: interface/src/Menu.h
This commit is contained in:
commit
fbcf66b232
25 changed files with 660 additions and 177 deletions
|
@ -102,6 +102,7 @@ Application::Application(int& argc, char** argv, timeval &startup_time) :
|
|||
_voxelImporter(_window),
|
||||
_wantToKillLocalVoxels(false),
|
||||
_audioScope(256, 200, true),
|
||||
_profile(QString()),
|
||||
_mouseX(0),
|
||||
_mouseY(0),
|
||||
_touchAvgX(0.0f),
|
||||
|
@ -181,10 +182,8 @@ Application::Application(int& argc, char** argv, timeval &startup_time) :
|
|||
|
||||
_settings = new QSettings(this);
|
||||
|
||||
// check if there is a saved domain server hostname
|
||||
// this must be done now instead of with the other setting checks to allow manual override with
|
||||
// --domain or --local options
|
||||
NodeList::getInstance()->loadData(_settings);
|
||||
// call Menu getInstance static method to set up the menu
|
||||
_window->setMenuBar(Menu::getInstance());
|
||||
|
||||
// Check to see if the user passed in a command line option for loading a local
|
||||
// Voxel File.
|
||||
|
@ -206,9 +205,6 @@ Application::Application(int& argc, char** argv, timeval &startup_time) :
|
|||
NodeList::getInstance()->startSilentNodeRemovalThread();
|
||||
|
||||
_window->setCentralWidget(_glWidget);
|
||||
|
||||
// call Menu getInstance static method to set up the menu
|
||||
_window->setMenuBar(Menu::getInstance());
|
||||
|
||||
_networkAccessManager = new QNetworkAccessManager(this);
|
||||
QNetworkDiskCache* cache = new QNetworkDiskCache(_networkAccessManager);
|
||||
|
@ -243,7 +239,6 @@ Application::~Application() {
|
|||
|
||||
delete _oculusProgram;
|
||||
delete _settings;
|
||||
delete _networkAccessManager;
|
||||
delete _followMode;
|
||||
delete _glWidget;
|
||||
}
|
||||
|
@ -352,7 +347,7 @@ void Application::paintGL() {
|
|||
|
||||
} else if (_myCamera.getMode() == CAMERA_MODE_FIRST_PERSON) {
|
||||
_myCamera.setTightness(0.0f); // In first person, camera follows head exactly without delay
|
||||
_myCamera.setTargetPosition(_myAvatar.getUprightEyeLevelPosition());
|
||||
_myCamera.setTargetPosition(_myAvatar.getEyeLevelPosition());
|
||||
_myCamera.setTargetRotation(_myAvatar.getHead().getCameraOrientation());
|
||||
|
||||
} else if (_myCamera.getMode() == CAMERA_MODE_THIRD_PERSON) {
|
||||
|
@ -455,6 +450,12 @@ void Application::updateProjectionMatrix() {
|
|||
glMatrixMode(GL_MODELVIEW);
|
||||
}
|
||||
|
||||
void Application::resetProfile(const QString& username) {
|
||||
// call the destructor on the old profile and construct a new one
|
||||
(&_profile)->~Profile();
|
||||
new (&_profile) Profile(username);
|
||||
}
|
||||
|
||||
void Application::controlledBroadcastToNodes(unsigned char* broadcastData, size_t dataBytes,
|
||||
const char* nodeTypes, int numNodeTypes) {
|
||||
Application* self = getInstance();
|
||||
|
@ -1158,6 +1159,9 @@ void Application::timer() {
|
|||
|
||||
// ask the node list to check in with the domain server
|
||||
NodeList::getInstance()->sendDomainServerCheckIn();
|
||||
|
||||
// give the MyAvatar object position to the Profile so it can propagate to the data-server
|
||||
_profile.updatePosition(_myAvatar.getPosition());
|
||||
}
|
||||
|
||||
static glm::vec3 getFaceVector(BoxFace face) {
|
||||
|
@ -1986,6 +1990,11 @@ void Application::update(float deltaTime) {
|
|||
_myAvatar.simulate(deltaTime, NULL);
|
||||
}
|
||||
|
||||
// Simulate particle cloud movements
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::ParticleCloud)) {
|
||||
_cloud.simulate(deltaTime);
|
||||
}
|
||||
|
||||
// no transmitter drive implies transmitter pick
|
||||
if (!Menu::getInstance()->isOptionChecked(MenuOption::TransmitterDrive) && _myTransmitter.isConnected()) {
|
||||
_transmitterPickStart = _myAvatar.getSkeleton().joint[AVATAR_JOINT_CHEST].position;
|
||||
|
@ -2043,7 +2052,7 @@ void Application::update(float deltaTime) {
|
|||
if (_faceshift.isActive()) {
|
||||
const float EYE_OFFSET_SCALE = 0.025f;
|
||||
glm::vec3 position = _faceshift.getHeadTranslation() * EYE_OFFSET_SCALE;
|
||||
_myCamera.setEyeOffsetPosition(glm::vec3(position.x * xSign, position.y, position.z));
|
||||
_myCamera.setEyeOffsetPosition(glm::vec3(position.x * xSign, position.y, -position.z));
|
||||
updateProjectionMatrix();
|
||||
|
||||
} else if (_webcam.isActive()) {
|
||||
|
@ -2082,8 +2091,7 @@ void Application::updateAvatar(float deltaTime) {
|
|||
_yawFromTouch = 0.f;
|
||||
|
||||
// Update my avatar's state from gyros and/or webcam
|
||||
_myAvatar.updateFromGyrosAndOrWebcam(Menu::getInstance()->isOptionChecked(MenuOption::GyroLook),
|
||||
_pitchFromTouch);
|
||||
_myAvatar.updateFromGyrosAndOrWebcam(_pitchFromTouch, Menu::getInstance()->isOptionChecked(MenuOption::TurnWithHead));
|
||||
|
||||
// Update head mouse from faceshift if active
|
||||
if (_faceshift.isActive()) {
|
||||
|
@ -2502,7 +2510,11 @@ void Application::displaySide(Camera& whichCamera) {
|
|||
glDisable(GL_NORMALIZE);
|
||||
|
||||
//renderGroundPlaneGrid(EDGE_SIZE_GROUND_PLANE, _audio.getCollisionSoundMagnitude());
|
||||
}
|
||||
}
|
||||
// Draw Cloud Particles
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::ParticleCloud)) {
|
||||
_cloud.render();
|
||||
}
|
||||
// Draw voxels
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::Voxels)) {
|
||||
PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings),
|
||||
|
@ -2512,6 +2524,7 @@ void Application::displaySide(Camera& whichCamera) {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
// restore default, white specular
|
||||
glMaterialfv(GL_FRONT, GL_SPECULAR, WHITE_SPECULAR_COLOR);
|
||||
|
||||
|
@ -3508,9 +3521,13 @@ void Application::attachNewHeadToNode(Node* newNode) {
|
|||
void Application::domainChanged(QString domain) {
|
||||
qDebug("Application title set to: %s.\n", domain.toStdString().c_str());
|
||||
_window->setWindowTitle(domain);
|
||||
|
||||
// update the user's last domain in their Profile (which will propagate to data-server)
|
||||
_profile.updateDomain(domain);
|
||||
}
|
||||
|
||||
void Application::nodeAdded(Node* node) {
|
||||
|
||||
}
|
||||
|
||||
void Application::nodeKilled(Node* node) {
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
|
||||
#include "BandwidthMeter.h"
|
||||
#include "Camera.h"
|
||||
#include "Cloud.h"
|
||||
#include "Environment.h"
|
||||
#include "GLCanvas.h"
|
||||
#include "PacketHeaders.h"
|
||||
|
@ -111,7 +112,6 @@ public:
|
|||
|
||||
QGLWidget* getGLWidget() { return _glWidget; }
|
||||
MyAvatar* getAvatar() { return &_myAvatar; }
|
||||
Profile* getProfile() { return &_profile; }
|
||||
Audio* getAudio() { return &_audio; }
|
||||
Camera* getCamera() { return &_myCamera; }
|
||||
ViewFrustum* getViewFrustum() { return &_viewFrustum; }
|
||||
|
@ -135,6 +135,9 @@ public:
|
|||
|
||||
Avatar* getLookatTargetAvatar() const { return _lookatTargetAvatar; }
|
||||
|
||||
Profile* getProfile() { return &_profile; }
|
||||
void resetProfile(const QString& username);
|
||||
|
||||
static void controlledBroadcastToNodes(unsigned char* broadcastData, size_t dataBytes,
|
||||
const char* nodeTypes, int numNodeTypes);
|
||||
|
||||
|
@ -263,6 +266,8 @@ private:
|
|||
|
||||
Stars _stars;
|
||||
|
||||
Cloud _cloud;
|
||||
|
||||
VoxelSystem _voxels;
|
||||
VoxelTree _clipboard; // if I copy/paste
|
||||
VoxelImporter _voxelImporter;
|
||||
|
|
85
interface/src/Cloud.cpp
Normal file
85
interface/src/Cloud.cpp
Normal file
|
@ -0,0 +1,85 @@
|
|||
//
|
||||
// Cloud.cpp
|
||||
// interface
|
||||
//
|
||||
// Created by Philip Rosedale on 11/17/12.
|
||||
// Copyright (c) 2012 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#include <iostream>
|
||||
#include <InterfaceConfig.h>
|
||||
#include "Cloud.h"
|
||||
#include "Util.h"
|
||||
#include "Field.h"
|
||||
|
||||
const int NUM_PARTICLES = 100000;
|
||||
const float FIELD_COUPLE = 0.001f;
|
||||
const bool RENDER_FIELD = false;
|
||||
|
||||
Cloud::Cloud() {
|
||||
glm::vec3 box = glm::vec3(PARTICLE_WORLD_SIZE);
|
||||
_bounds = box;
|
||||
_count = NUM_PARTICLES;
|
||||
_particles = new Particle[_count];
|
||||
_field = new Field(PARTICLE_WORLD_SIZE, FIELD_COUPLE);
|
||||
|
||||
for (int i = 0; i < _count; i++) {
|
||||
_particles[i].position = randVector() * box;
|
||||
const float INIT_VEL_SCALE = 0.03f;
|
||||
_particles[i].velocity = randVector() * ((float)PARTICLE_WORLD_SIZE * INIT_VEL_SCALE);
|
||||
_particles[i].color = randVector();
|
||||
}
|
||||
}
|
||||
|
||||
void Cloud::render() {
|
||||
if (RENDER_FIELD) {
|
||||
_field->render();
|
||||
}
|
||||
|
||||
glPointSize(3.0f);
|
||||
glDisable(GL_TEXTURE_2D);
|
||||
glEnable(GL_POINT_SMOOTH);
|
||||
|
||||
glEnableClientState(GL_VERTEX_ARRAY);
|
||||
glEnableClientState(GL_COLOR_ARRAY);
|
||||
glVertexPointer(3, GL_FLOAT, 3 * sizeof(glm::vec3), &_particles[0].position);
|
||||
glColorPointer(3, GL_FLOAT, 3 * sizeof(glm::vec3), &_particles[0].color);
|
||||
glDrawArrays(GL_POINTS, 0, NUM_PARTICLES);
|
||||
glDisableClientState(GL_VERTEX_ARRAY);
|
||||
glDisableClientState(GL_COLOR_ARRAY);
|
||||
|
||||
}
|
||||
|
||||
void Cloud::simulate (float deltaTime) {
|
||||
unsigned int i;
|
||||
_field->simulate(deltaTime);
|
||||
for (i = 0; i < _count; ++i) {
|
||||
|
||||
// Update position
|
||||
_particles[i].position += _particles[i].velocity * deltaTime;
|
||||
|
||||
// Decay Velocity (Drag)
|
||||
const float CONSTANT_DAMPING = 0.15f;
|
||||
_particles[i].velocity *= (1.f - CONSTANT_DAMPING * deltaTime);
|
||||
|
||||
// Interact with Field
|
||||
_field->interact(deltaTime, _particles[i].position, _particles[i].velocity);
|
||||
|
||||
// Update color to velocity
|
||||
_particles[i].color = (glm::normalize(_particles[i].velocity) * 0.5f) + 0.5f;
|
||||
|
||||
// Bounce at bounds
|
||||
if ((_particles[i].position.x > _bounds.x) || (_particles[i].position.x < 0.f)) {
|
||||
_particles[i].position.x = glm::clamp(_particles[i].position.x, 0.f, _bounds.x);
|
||||
_particles[i].velocity.x *= -1.f;
|
||||
}
|
||||
if ((_particles[i].position.y > _bounds.y) || (_particles[i].position.y < 0.f)) {
|
||||
_particles[i].position.y = glm::clamp(_particles[i].position.y, 0.f, _bounds.y);
|
||||
_particles[i].velocity.y *= -1.f;
|
||||
}
|
||||
if ((_particles[i].position.z > _bounds.z) || (_particles[i].position.z < 0.f)) {
|
||||
_particles[i].position.z = glm::clamp(_particles[i].position.z, 0.f, _bounds.z);
|
||||
_particles[i].velocity.z *= -1.f;
|
||||
}
|
||||
}
|
||||
}
|
32
interface/src/Cloud.h
Normal file
32
interface/src/Cloud.h
Normal file
|
@ -0,0 +1,32 @@
|
|||
//
|
||||
// Cloud.h
|
||||
// interface
|
||||
//
|
||||
// Created by Philip Rosedale on 11/17/12.
|
||||
// Copyright (c) 2012 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef __interface__Cloud__
|
||||
#define __interface__Cloud__
|
||||
|
||||
#include "Field.h"
|
||||
|
||||
#define PARTICLE_WORLD_SIZE 256.0
|
||||
|
||||
class Cloud {
|
||||
public:
|
||||
Cloud();
|
||||
void simulate(float deltaTime);
|
||||
void render();
|
||||
|
||||
private:
|
||||
struct Particle {
|
||||
glm::vec3 position, velocity, color;
|
||||
}* _particles;
|
||||
|
||||
unsigned int _count;
|
||||
glm::vec3 _bounds;
|
||||
Field* _field;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -18,19 +18,16 @@
|
|||
|
||||
#include "DataServerClient.h"
|
||||
|
||||
|
||||
std::map<unsigned char*, int> DataServerClient::_unmatchedPackets;
|
||||
|
||||
const char MULTI_KEY_VALUE_SEPARATOR = '|';
|
||||
|
||||
const char DATA_SERVER_HOSTNAME[] = "data.highfidelity.io";
|
||||
const unsigned short DATA_SERVER_PORT = 3282;
|
||||
const sockaddr_in DATA_SERVER_SOCKET = socketForHostnameAndHostOrderPort(DATA_SERVER_HOSTNAME, DATA_SERVER_PORT);
|
||||
|
||||
void DataServerClient::putValueForKey(const char* key, const char* value) {
|
||||
Profile* userProfile = Application::getInstance()->getProfile();
|
||||
QString clientString = userProfile->getUUID().isNull()
|
||||
? userProfile->getUsername()
|
||||
: uuidStringWithoutCurlyBraces(userProfile->getUUID().toString());
|
||||
|
||||
void DataServerClient::putValueForKey(const QString& key, const char* value) {
|
||||
QString clientString = Application::getInstance()->getProfile()->getUserString();
|
||||
if (!clientString.isEmpty()) {
|
||||
|
||||
unsigned char* putPacket = new unsigned char[MAX_PACKET_SIZE];
|
||||
|
@ -43,9 +40,12 @@ void DataServerClient::putValueForKey(const char* key, const char* value) {
|
|||
numPacketBytes += clientString.toLocal8Bit().size();
|
||||
putPacket[numPacketBytes++] = '\0';
|
||||
|
||||
// pack a 1 to designate that we are putting a single value
|
||||
putPacket[numPacketBytes++] = 1;
|
||||
|
||||
// pack the key, null terminated
|
||||
strcpy((char*) putPacket + numPacketBytes, key);
|
||||
numPacketBytes += strlen(key);
|
||||
strcpy((char*) putPacket + numPacketBytes, key.toLocal8Bit().constData());
|
||||
numPacketBytes += key.size();
|
||||
putPacket[numPacketBytes++] = '\0';
|
||||
|
||||
// pack the value, null terminated
|
||||
|
@ -54,48 +54,54 @@ void DataServerClient::putValueForKey(const char* key, const char* value) {
|
|||
putPacket[numPacketBytes++] = '\0';
|
||||
|
||||
// add the putPacket to our vector of unconfirmed packets, will be deleted once put is confirmed
|
||||
_unmatchedPackets.insert(std::pair<unsigned char*, int>(putPacket, numPacketBytes));
|
||||
// _unmatchedPackets.insert(std::pair<unsigned char*, int>(putPacket, numPacketBytes));
|
||||
|
||||
// send this put request to the data server
|
||||
NodeList::getInstance()->getNodeSocket()->send((sockaddr*) &DATA_SERVER_SOCKET, putPacket, numPacketBytes);
|
||||
}
|
||||
}
|
||||
|
||||
void DataServerClient::getValueForKeyAndUUID(const char* key, const QUuid &uuid) {
|
||||
void DataServerClient::getValueForKeyAndUUID(const QString& key, const QUuid &uuid) {
|
||||
getValuesForKeysAndUUID(QStringList(key), uuid);
|
||||
}
|
||||
|
||||
void DataServerClient::getValuesForKeysAndUUID(const QStringList& keys, const QUuid& uuid) {
|
||||
if (!uuid.isNull()) {
|
||||
getValueForKeyAndUserString(key, uuidStringWithoutCurlyBraces(uuid));
|
||||
getValuesForKeysAndUserString(keys, uuidStringWithoutCurlyBraces(uuid));
|
||||
}
|
||||
}
|
||||
|
||||
void DataServerClient::getValueForKeyAndUserString(const char* key, const QString& userString) {
|
||||
unsigned char* getPacket = new unsigned char[MAX_PACKET_SIZE];
|
||||
|
||||
// setup the header for this packet
|
||||
int numPacketBytes = populateTypeAndVersion(getPacket, PACKET_TYPE_DATA_SERVER_GET);
|
||||
|
||||
// pack the user string (could be username or UUID string), null-terminate
|
||||
memcpy(getPacket + numPacketBytes, userString.toLocal8Bit().constData(), userString.toLocal8Bit().size());
|
||||
numPacketBytes += userString.toLocal8Bit().size();
|
||||
getPacket[numPacketBytes++] = '\0';
|
||||
|
||||
// pack the key, null terminated
|
||||
strcpy((char*) getPacket + numPacketBytes, key);
|
||||
int numKeyBytes = strlen(key);
|
||||
|
||||
if (numKeyBytes > 0) {
|
||||
numPacketBytes += numKeyBytes;
|
||||
void DataServerClient::getValuesForKeysAndUserString(const QStringList& keys, const QString& userString) {
|
||||
if (!userString.isEmpty() && keys.size() <= UCHAR_MAX) {
|
||||
unsigned char* getPacket = new unsigned char[MAX_PACKET_SIZE];
|
||||
|
||||
// setup the header for this packet
|
||||
int numPacketBytes = populateTypeAndVersion(getPacket, PACKET_TYPE_DATA_SERVER_GET);
|
||||
|
||||
// pack the user string (could be username or UUID string), null-terminate
|
||||
memcpy(getPacket + numPacketBytes, userString.toLocal8Bit().constData(), userString.toLocal8Bit().size());
|
||||
numPacketBytes += userString.toLocal8Bit().size();
|
||||
getPacket[numPacketBytes++] = '\0';
|
||||
|
||||
// pack one byte to designate the number of keys
|
||||
getPacket[numPacketBytes++] = keys.size();
|
||||
|
||||
QString keyString = keys.join(MULTI_KEY_VALUE_SEPARATOR);
|
||||
|
||||
// pack the key string, null terminated
|
||||
strcpy((char*) getPacket + numPacketBytes, keyString.toLocal8Bit().constData());
|
||||
numPacketBytes += keyString.size() + sizeof('\0');
|
||||
|
||||
// add the getPacket to our vector of uncofirmed packets, will be deleted once we get a response from the nameserver
|
||||
// _unmatchedPackets.insert(std::pair<unsigned char*, int>(getPacket, numPacketBytes));
|
||||
|
||||
// send the get to the data server
|
||||
NodeList::getInstance()->getNodeSocket()->send((sockaddr*) &DATA_SERVER_SOCKET, getPacket, numPacketBytes);
|
||||
}
|
||||
|
||||
// add the getPacket to our vector of uncofirmed packets, will be deleted once we get a response from the nameserver
|
||||
_unmatchedPackets.insert(std::pair<unsigned char*, int>(getPacket, numPacketBytes));
|
||||
|
||||
// send the get to the data server
|
||||
NodeList::getInstance()->getNodeSocket()->send((sockaddr*) &DATA_SERVER_SOCKET, getPacket, numPacketBytes);
|
||||
}
|
||||
|
||||
void DataServerClient::getClientValueForKey(const char* key) {
|
||||
getValueForKeyAndUserString(key, Application::getInstance()->getProfile()->getUsername());
|
||||
void DataServerClient::getClientValueForKey(const QString& key) {
|
||||
getValuesForKeysAndUserString(QStringList(key), Application::getInstance()->getProfile()->getUserString());
|
||||
}
|
||||
|
||||
void DataServerClient::processConfirmFromDataServer(unsigned char* packetData, int numPacketBytes) {
|
||||
|
@ -112,45 +118,63 @@ void DataServerClient::processSendFromDataServer(unsigned char* packetData, int
|
|||
|
||||
QUuid userUUID(userString);
|
||||
|
||||
char* dataKeyPosition = (char*) packetData + numHeaderBytes + strlen(userStringPosition) + sizeof('\0');
|
||||
char* dataValuePosition = dataKeyPosition + strlen(dataKeyPosition) + sizeof(char);
|
||||
char* keysPosition = (char*) packetData + numHeaderBytes + strlen(userStringPosition)
|
||||
+ sizeof('\0') + sizeof(unsigned char);
|
||||
char* valuesPosition = keysPosition + strlen(keysPosition) + sizeof('\0');
|
||||
|
||||
QString dataValueString(QByteArray(dataValuePosition,
|
||||
numPacketBytes - ((unsigned char*) dataValuePosition - packetData)));
|
||||
QStringList keyList = QString(keysPosition).split(MULTI_KEY_VALUE_SEPARATOR);
|
||||
QStringList valueList = QString(valuesPosition).split(MULTI_KEY_VALUE_SEPARATOR);
|
||||
|
||||
if (userUUID.isNull()) {
|
||||
// the user string was a username
|
||||
// for now assume this means that it is for our avatar
|
||||
|
||||
if (strcmp(dataKeyPosition, DataServerKey::FaceMeshURL) == 0) {
|
||||
// pull the user's face mesh and set it on the Avatar instance
|
||||
|
||||
qDebug("Changing user's face model URL to %s\n", dataValueString.toLocal8Bit().constData());
|
||||
Application::getInstance()->getProfile()->setFaceModelURL(QUrl(dataValueString));
|
||||
} else if (strcmp(dataKeyPosition, DataServerKey::UUID) == 0) {
|
||||
// this is the user's UUID - set it on the profile
|
||||
Application::getInstance()->getProfile()->setUUID(dataValueString);
|
||||
}
|
||||
} else {
|
||||
// user string was UUID, find matching avatar and associate data
|
||||
if (strcmp(dataKeyPosition, DataServerKey::FaceMeshURL) == 0) {
|
||||
NodeList* nodeList = NodeList::getInstance();
|
||||
for (NodeList::iterator node = nodeList->begin(); node != nodeList->end(); node++) {
|
||||
if (node->getLinkedData() != NULL && node->getType() == NODE_TYPE_AGENT) {
|
||||
Avatar* avatar = (Avatar *) node->getLinkedData();
|
||||
|
||||
if (avatar->getUUID() == userUUID) {
|
||||
QMetaObject::invokeMethod(&avatar->getHead().getBlendFace(),
|
||||
"setModelURL",
|
||||
Q_ARG(QUrl, QUrl(dataValueString)));
|
||||
// user string was UUID, find matching avatar and associate data
|
||||
for (int i = 0; i < keyList.size(); i++) {
|
||||
if (valueList[i] != " ") {
|
||||
if (keyList[i] == DataServerKey::FaceMeshURL) {
|
||||
|
||||
if (userUUID.isNull() || userUUID == Application::getInstance()->getProfile()->getUUID()) {
|
||||
qDebug("Changing user's face model URL to %s\n", valueList[0].toLocal8Bit().constData());
|
||||
Application::getInstance()->getProfile()->setFaceModelURL(QUrl(valueList[0]));
|
||||
} else {
|
||||
// mesh URL for a UUID, find avatar in our list
|
||||
NodeList* nodeList = NodeList::getInstance();
|
||||
for (NodeList::iterator node = nodeList->begin(); node != nodeList->end(); node++) {
|
||||
if (node->getLinkedData() != NULL && node->getType() == NODE_TYPE_AGENT) {
|
||||
Avatar* avatar = (Avatar *) node->getLinkedData();
|
||||
|
||||
if (avatar->getUUID() == userUUID) {
|
||||
QMetaObject::invokeMethod(&avatar->getHead().getBlendFace(),
|
||||
"setModelURL",
|
||||
Q_ARG(QUrl, QUrl(valueList[0])));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (keyList[i] == DataServerKey::Domain) {
|
||||
qDebug() << "Changing domain hostname to" << valueList[i].toLocal8Bit().constData() <<
|
||||
"to go to" << userString << "\n";
|
||||
NodeList::getInstance()->setDomainHostname(valueList[i]);
|
||||
} else if (keyList[i] == DataServerKey::Position) {
|
||||
QStringList coordinateItems = valueList[i].split(',');
|
||||
|
||||
if (coordinateItems.size() == 3) {
|
||||
qDebug() << "Changing position to" << valueList[i].toLocal8Bit().constData() <<
|
||||
"to go to" << userString << "\n";
|
||||
|
||||
glm::vec3 newPosition(coordinateItems[0].toFloat(),
|
||||
coordinateItems[1].toFloat(),
|
||||
coordinateItems[2].toFloat());
|
||||
Application::getInstance()->getAvatar()->setPosition(newPosition);
|
||||
}
|
||||
|
||||
|
||||
} else if (keyList[i] == DataServerKey::UUID) {
|
||||
// this is the user's UUID - set it on the profile
|
||||
Application::getInstance()->getProfile()->setUUID(valueList[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// remove the matched packet from our map so it isn't re-sent to the data-server
|
||||
removeMatchedPacketFromMap(packetData, numPacketBytes);
|
||||
// removeMatchedPacketFromMap(packetData, numPacketBytes);
|
||||
}
|
||||
|
||||
void DataServerClient::processMessageFromDataServer(unsigned char* packetData, int numPacketBytes) {
|
||||
|
|
|
@ -17,22 +17,29 @@
|
|||
|
||||
class DataServerClient {
|
||||
public:
|
||||
static void putValueForKey(const char* key, const char* value);
|
||||
static void getValueForKeyAndUUID(const char* key, const QUuid& uuid);
|
||||
static void getValueForKeyAndUserString(const char* key, const QString& userString);
|
||||
static void getClientValueForKey(const char* key);
|
||||
static void putValueForKey(const QString& key, const char* value);
|
||||
static void getClientValueForKey(const QString& key);
|
||||
|
||||
static void getValueForKeyAndUUID(const QString& key, const QUuid& uuid);
|
||||
static void getValuesForKeysAndUUID(const QStringList& keys, const QUuid& uuid);
|
||||
static void getValuesForKeysAndUserString(const QStringList& keys, const QString& userString);
|
||||
|
||||
static void processConfirmFromDataServer(unsigned char* packetData, int numPacketBytes);
|
||||
static void processSendFromDataServer(unsigned char* packetData, int numPacketBytes);
|
||||
static void processMessageFromDataServer(unsigned char* packetData, int numPacketBytes);
|
||||
static void removeMatchedPacketFromMap(unsigned char* packetData, int numPacketBytes);
|
||||
static void resendUnmatchedPackets();
|
||||
private:
|
||||
|
||||
|
||||
static std::map<unsigned char*, int> _unmatchedPackets;
|
||||
};
|
||||
|
||||
namespace DataServerKey {
|
||||
const char FaceMeshURL[] = "mesh";
|
||||
const char UUID[] = "uuid";
|
||||
const QString Domain = "domain";
|
||||
const QString FaceMeshURL = "mesh";
|
||||
const QString Position = "position";
|
||||
const QString UUID = "uuid";
|
||||
}
|
||||
|
||||
#endif /* defined(__hifi__DataServerClient__) */
|
||||
|
|
100
interface/src/Field.cpp
Normal file
100
interface/src/Field.cpp
Normal file
|
@ -0,0 +1,100 @@
|
|||
//
|
||||
// Field.cpp
|
||||
// interface
|
||||
//
|
||||
// Created by Philip Rosedale on 8/23/12.
|
||||
// Copyright (c) 2012 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
// A vector-valued field over an array of elements arranged as a 3D lattice
|
||||
|
||||
#include "Field.h"
|
||||
|
||||
int Field::value(float *value, float *pos) {
|
||||
int index = (int)(pos[0] / _worldSize * 10.0) +
|
||||
(int)(pos[1] / _worldSize * 10.0) * 10 +
|
||||
(int)(pos[2] / _worldSize * 10.0) * 100;
|
||||
|
||||
if ((index >= 0) && (index < FIELD_ELEMENTS)) {
|
||||
value[0] = _field[index].val.x;
|
||||
value[1] = _field[index].val.y;
|
||||
value[2] = _field[index].val.z;
|
||||
return 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
Field::Field(float worldSize, float coupling) {
|
||||
_worldSize = worldSize;
|
||||
_coupling = coupling;
|
||||
//float fx, fy, fz;
|
||||
for (int i = 0; i < FIELD_ELEMENTS; i++) {
|
||||
const float FIELD_INITIAL_MAG = 0.0f;
|
||||
_field[i].val = randVector() * FIELD_INITIAL_MAG * _worldSize;
|
||||
_field[i].center.x = ((float)(i % 10) + 0.5f);
|
||||
_field[i].center.y = ((float)(i % 100 / 10) + 0.5f);
|
||||
_field[i].center.z = ((float)(i / 100) + 0.5f);
|
||||
_field[i].center *= _worldSize / 10.f;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void Field::add(float* add, float *pos) {
|
||||
int index = (int)(pos[0] / _worldSize * 10.0) +
|
||||
(int)(pos[1] / _worldSize * 10.0) * 10 +
|
||||
(int)(pos[2] / _worldSize * 10.0) * 100;
|
||||
|
||||
if ((index >= 0) && (index < FIELD_ELEMENTS)) {
|
||||
_field[index].val.x += add[0];
|
||||
_field[index].val.y += add[1];
|
||||
_field[index].val.z += add[2];
|
||||
}
|
||||
}
|
||||
|
||||
void Field::interact(float deltaTime, const glm::vec3& pos, glm::vec3& vel) {
|
||||
|
||||
int index = (int)(pos.x / _worldSize * 10.0) +
|
||||
(int)(pos.y / _worldSize*10.0) * 10 +
|
||||
(int)(pos.z / _worldSize*10.0) * 100;
|
||||
if ((index >= 0) && (index < FIELD_ELEMENTS)) {
|
||||
vel += _field[index].val * deltaTime; // Particle influenced by field
|
||||
_field[index].val += vel * deltaTime * _coupling; // Field influenced by particle
|
||||
}
|
||||
}
|
||||
|
||||
void Field::simulate(float deltaTime) {
|
||||
glm::vec3 neighbors, add, diff;
|
||||
|
||||
for (int i = 0; i < FIELD_ELEMENTS; i++) {
|
||||
const float CONSTANT_DAMPING = 0.5f;
|
||||
_field[i].val *= (1.f - CONSTANT_DAMPING * deltaTime);
|
||||
}
|
||||
}
|
||||
|
||||
void Field::render() {
|
||||
int i;
|
||||
float scale_view = 0.05f * _worldSize;
|
||||
|
||||
glDisable(GL_LIGHTING);
|
||||
glBegin(GL_LINES);
|
||||
for (i = 0; i < FIELD_ELEMENTS; i++) {
|
||||
glColor3f(0, 1, 0);
|
||||
glVertex3fv(&_field[i].center.x);
|
||||
glVertex3f(_field[i].center.x + _field[i].val.x * scale_view,
|
||||
_field[i].center.y + _field[i].val.y * scale_view,
|
||||
_field[i].center.z + _field[i].val.z * scale_view);
|
||||
}
|
||||
glEnd();
|
||||
|
||||
glColor3f(0, 1, 0);
|
||||
glPointSize(4.0);
|
||||
glEnable(GL_POINT_SMOOTH);
|
||||
glBegin(GL_POINTS);
|
||||
for (i = 0; i < FIELD_ELEMENTS; i++) {
|
||||
glVertex3fv(&_field[i].center.x);
|
||||
}
|
||||
glEnd();
|
||||
}
|
||||
|
||||
|
||||
|
46
interface/src/Field.h
Normal file
46
interface/src/Field.h
Normal file
|
@ -0,0 +1,46 @@
|
|||
//
|
||||
// Field.h
|
||||
// interface
|
||||
//
|
||||
// Created by Philip Rosedale on 8/23/12.
|
||||
// Copyright (c) 2012 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef __interface__Field__
|
||||
#define __interface__Field__
|
||||
|
||||
#include <iostream>
|
||||
#include <glm/glm.hpp>
|
||||
#include "InterfaceConfig.h"
|
||||
#include "world.h"
|
||||
#include "Util.h"
|
||||
|
||||
const int FIELD_ELEMENTS = 1000;
|
||||
|
||||
/// Field is a lattice of vectors uniformly distributed in 3D with FIELD_ELEMENTS^(1/3) per side
|
||||
class Field {
|
||||
public:
|
||||
struct FieldElement {
|
||||
glm::vec3 val;
|
||||
glm::vec3 center;
|
||||
glm::vec3 fld;
|
||||
} _field[FIELD_ELEMENTS];
|
||||
|
||||
Field(float worldSize, float coupling);
|
||||
/// The field value at a position in space, given simply as the value of the enclosing cell
|
||||
int value(float *ret, float *pos);
|
||||
/// Visualize the field as vector lines drawn at each center
|
||||
void render();
|
||||
/// Add to the field value cell enclosing a location
|
||||
void add(float* add, float *loc);
|
||||
/// A particle with a position and velocity interacts with the field given the coupling
|
||||
/// constant passed when creating the field.
|
||||
void interact(float deltaTime, const glm::vec3& pos, glm::vec3& vel);
|
||||
/// Field evolves over timestep
|
||||
void simulate(float deltaTime);
|
||||
private:
|
||||
float _worldSize;
|
||||
float _coupling;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -20,6 +20,8 @@
|
|||
InfoView::InfoView(bool forced) :
|
||||
_forced(forced) {
|
||||
|
||||
setWindowFlags(Qt::WindowStaysOnTopHint | Qt::CustomizeWindowHint);
|
||||
|
||||
switchToResourcesParentIfRequired();
|
||||
QString absPath = QFileInfo("resources/html/interface-welcome-allsvg.html").absoluteFilePath();
|
||||
QUrl url = QUrl::fromLocalFile(absPath);
|
||||
|
|
|
@ -67,6 +67,12 @@ Menu::Menu() :
|
|||
SLOT(aboutApp())))->setMenuRole(QAction::AboutRole);
|
||||
#endif
|
||||
|
||||
(addActionToQMenuAndActionHash(fileMenu,
|
||||
MenuOption::Login,
|
||||
0,
|
||||
this,
|
||||
SLOT(login())));
|
||||
|
||||
(addActionToQMenuAndActionHash(fileMenu,
|
||||
MenuOption::Preferences,
|
||||
Qt::CTRL | Qt::Key_Comma,
|
||||
|
@ -93,6 +99,11 @@ Menu::Menu() :
|
|||
Qt::CTRL | Qt::SHIFT | Qt::Key_L,
|
||||
this,
|
||||
SLOT(goToLocation()));
|
||||
addActionToQMenuAndActionHash(fileMenu,
|
||||
MenuOption::GoToUser,
|
||||
Qt::CTRL | Qt::SHIFT | Qt::Key_U,
|
||||
this,
|
||||
SLOT(goToUser()));
|
||||
|
||||
|
||||
addDisabledActionAndSeparator(fileMenu, "Settings");
|
||||
|
@ -212,7 +223,13 @@ Menu::Menu() :
|
|||
addCheckableActionToQMenuAndActionHash(viewMenu,
|
||||
MenuOption::OffAxisProjection,
|
||||
0,
|
||||
false);
|
||||
true);
|
||||
addCheckableActionToQMenuAndActionHash(viewMenu,
|
||||
MenuOption::TurnWithHead,
|
||||
0,
|
||||
true);
|
||||
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::HeadMouse, 0, false);
|
||||
|
||||
|
||||
addDisabledActionAndSeparator(viewMenu, "Stats");
|
||||
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::Stats, Qt::Key_Slash);
|
||||
|
@ -234,6 +251,9 @@ Menu::Menu() :
|
|||
0,
|
||||
appInstance->getGlowEffect(),
|
||||
SLOT(cycleRenderMode()));
|
||||
|
||||
addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::ParticleCloud, 0, false);
|
||||
|
||||
|
||||
QMenu* voxelOptionsMenu = developerMenu->addMenu("Voxel Options");
|
||||
|
||||
|
@ -317,12 +337,6 @@ Menu::Menu() :
|
|||
addCheckableActionToQMenuAndActionHash(raveGloveOptionsMenu, MenuOption::SimulateLeapHand);
|
||||
addCheckableActionToQMenuAndActionHash(raveGloveOptionsMenu, MenuOption::TestRaveGlove);
|
||||
|
||||
|
||||
QMenu* gyroOptionsMenu = developerMenu->addMenu("Gyro Options");
|
||||
addCheckableActionToQMenuAndActionHash(gyroOptionsMenu, MenuOption::GyroLook, 0, true);
|
||||
addCheckableActionToQMenuAndActionHash(gyroOptionsMenu, MenuOption::HeadMouse);
|
||||
|
||||
|
||||
QMenu* trackingOptionsMenu = developerMenu->addMenu("Tracking Options");
|
||||
addCheckableActionToQMenuAndActionHash(trackingOptionsMenu,
|
||||
MenuOption::SkeletonTracking,
|
||||
|
@ -509,9 +523,10 @@ void Menu::loadSettings(QSettings* settings) {
|
|||
settings->endGroup();
|
||||
|
||||
scanMenuBar(&loadAction, settings);
|
||||
Application::getInstance()->getProfile()->loadData(settings);
|
||||
Application::getInstance()->getAvatar()->loadData(settings);
|
||||
Application::getInstance()->getSwatch()->loadData(settings);
|
||||
Application::getInstance()->getProfile()->loadData(settings);
|
||||
NodeList::getInstance()->loadData(settings);
|
||||
}
|
||||
|
||||
void Menu::saveSettings(QSettings* settings) {
|
||||
|
@ -532,10 +547,8 @@ void Menu::saveSettings(QSettings* settings) {
|
|||
|
||||
scanMenuBar(&saveAction, settings);
|
||||
Application::getInstance()->getAvatar()->saveData(settings);
|
||||
Application::getInstance()->getProfile()->saveData(settings);
|
||||
Application::getInstance()->getSwatch()->saveData(settings);
|
||||
|
||||
// ask the NodeList to save its data
|
||||
Application::getInstance()->getProfile()->saveData(settings);
|
||||
NodeList::getInstance()->saveData(settings);
|
||||
}
|
||||
|
||||
|
@ -751,6 +764,40 @@ QLineEdit* lineEditForDomainHostname() {
|
|||
return domainServerLineEdit;
|
||||
}
|
||||
|
||||
|
||||
void Menu::login() {
|
||||
Application* applicationInstance = Application::getInstance();
|
||||
QDialog dialog(applicationInstance->getGLWidget());
|
||||
dialog.setWindowTitle("Login");
|
||||
QBoxLayout* layout = new QBoxLayout(QBoxLayout::TopToBottom);
|
||||
dialog.setLayout(layout);
|
||||
|
||||
QFormLayout* form = new QFormLayout();
|
||||
layout->addLayout(form, 1);
|
||||
|
||||
QString username = applicationInstance->getProfile()->getUsername();
|
||||
QLineEdit* usernameLineEdit = new QLineEdit(username);
|
||||
usernameLineEdit->setMinimumWidth(QLINE_MINIMUM_WIDTH);
|
||||
form->addRow("Username:", usernameLineEdit);
|
||||
|
||||
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);
|
||||
|
||||
int ret = dialog.exec();
|
||||
applicationInstance->getWindow()->activateWindow();
|
||||
if (ret != QDialog::Accepted) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (usernameLineEdit->text() != username) {
|
||||
// there has been a username change
|
||||
// ask for a profile reset with the new username
|
||||
applicationInstance->resetProfile(usernameLineEdit->text());
|
||||
}
|
||||
}
|
||||
|
||||
void Menu::editPreferences() {
|
||||
Application* applicationInstance = Application::getInstance();
|
||||
|
||||
|
@ -762,11 +809,6 @@ void Menu::editPreferences() {
|
|||
QFormLayout* form = new QFormLayout();
|
||||
layout->addLayout(form, 1);
|
||||
|
||||
QString avatarUsername = applicationInstance->getProfile()->getUsername();
|
||||
QLineEdit* avatarUsernameEdit = new QLineEdit(avatarUsername);
|
||||
avatarUsernameEdit->setMinimumWidth(QLINE_MINIMUM_WIDTH);
|
||||
form->addRow("Username:", avatarUsernameEdit);
|
||||
|
||||
QLineEdit* avatarURL = new QLineEdit(applicationInstance->getAvatar()->getVoxels()->getVoxelURL().toString());
|
||||
avatarURL->setMinimumWidth(QLINE_MINIMUM_WIDTH);
|
||||
form->addRow("Avatar URL:", avatarURL);
|
||||
|
@ -819,17 +861,6 @@ void Menu::editPreferences() {
|
|||
|
||||
QUrl faceModelURL(faceURLEdit->text());
|
||||
|
||||
|
||||
if (avatarUsernameEdit->text() != avatarUsername) {
|
||||
// there has been a username change - set the new UUID on the avatar instance
|
||||
applicationInstance->getProfile()->setUsername(avatarUsernameEdit->text());
|
||||
|
||||
if (faceModelURL.toString() == faceURLString && !avatarUsernameEdit->text().isEmpty()) {
|
||||
// if there was no change to the face model URL then ask the data-server for what it is
|
||||
DataServerClient::getClientValueForKey(DataServerKey::FaceMeshURL);
|
||||
}
|
||||
}
|
||||
|
||||
if (faceModelURL.toString() != faceURLString) {
|
||||
// change the faceModelURL in the profile, it will also update this user's BlendFace
|
||||
applicationInstance->getProfile()->setFaceModelURL(faceModelURL);
|
||||
|
@ -948,6 +979,37 @@ void Menu::goToLocation() {
|
|||
}
|
||||
}
|
||||
|
||||
void Menu::goToUser() {
|
||||
Application* applicationInstance = Application::getInstance();
|
||||
QDialog dialog(applicationInstance->getGLWidget());
|
||||
dialog.setWindowTitle("Go To User");
|
||||
QBoxLayout* layout = new QBoxLayout(QBoxLayout::TopToBottom);
|
||||
dialog.setLayout(layout);
|
||||
|
||||
QFormLayout* form = new QFormLayout();
|
||||
layout->addLayout(form, 1);
|
||||
|
||||
QLineEdit* usernameLineEdit = new QLineEdit();
|
||||
usernameLineEdit->setMinimumWidth(QLINE_MINIMUM_WIDTH);
|
||||
form->addRow("", usernameLineEdit);
|
||||
|
||||
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);
|
||||
|
||||
int ret = dialog.exec();
|
||||
applicationInstance->getWindow()->activateWindow();
|
||||
if (ret != QDialog::Accepted) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!usernameLineEdit->text().isEmpty()) {
|
||||
// there's a username entered by the user, make a request to the data-server
|
||||
DataServerClient::getValuesForKeysAndUserString((QStringList() << DataServerKey::Domain << DataServerKey::Position),
|
||||
usernameLineEdit->text());
|
||||
}
|
||||
}
|
||||
|
||||
void Menu::bandwidthDetails() {
|
||||
|
||||
|
|
|
@ -69,9 +69,11 @@ public slots:
|
|||
|
||||
private slots:
|
||||
void aboutApp();
|
||||
void login();
|
||||
void editPreferences();
|
||||
void goToDomain();
|
||||
void goToLocation();
|
||||
void goToUser();
|
||||
void bandwidthDetailsClosed();
|
||||
void voxelStatsDetailsClosed();
|
||||
void cycleFrustumRenderMode();
|
||||
|
@ -120,7 +122,6 @@ private:
|
|||
};
|
||||
|
||||
namespace MenuOption {
|
||||
|
||||
const QString AboutApp = "About Interface";
|
||||
const QString AmbientOcclusion = "Ambient Occlusion";
|
||||
const QString Avatars = "Avatars";
|
||||
|
@ -165,6 +166,7 @@ namespace MenuOption {
|
|||
const QString GoToDomain = "Go To Domain...";
|
||||
const QString GoToLocation = "Go To Location...";
|
||||
const QString HideOutOfView = "Hide Out of View Voxels";
|
||||
const QString GoToUser = "Go To User...";
|
||||
const QString ImportVoxels = "Import Voxels";
|
||||
const QString ImportVoxelsClipboard = "Import Voxels to Clipboard";
|
||||
const QString IncreaseAvatarSize = "Increase Avatar Size";
|
||||
|
@ -173,11 +175,12 @@ namespace MenuOption {
|
|||
const QString GoHome = "Go Home";
|
||||
const QString Gravity = "Use Gravity";
|
||||
const QString GroundPlane = "Ground Plane";
|
||||
const QString GyroLook = "Smooth Gyro Look";
|
||||
const QString ParticleCloud = "Particle Cloud";
|
||||
const QString ListenModeNormal = "Listen Mode Normal";
|
||||
const QString ListenModePoint = "Listen Mode Point";
|
||||
const QString ListenModeSingleSource = "Listen Mode Single Source";
|
||||
const QString Log = "Log";
|
||||
const QString Login = "Login";
|
||||
const QString LookAtIndicator = "Look-at Indicator";
|
||||
const QString LookAtVectors = "Look-at Vectors";
|
||||
const QString LowRes = "Lower Resolution While Moving";
|
||||
|
@ -185,6 +188,7 @@ namespace MenuOption {
|
|||
const QString NudgeVoxels = "Nudge";
|
||||
const QString OcclusionCulling = "Occlusion Culling";
|
||||
const QString OffAxisProjection = "Off-Axis Projection";
|
||||
const QString TurnWithHead = "Turn using Head";
|
||||
const QString Oscilloscope = "Audio Oscilloscope";
|
||||
const QString Pair = "Pair";
|
||||
const QString PasteVoxels = "Paste";
|
||||
|
|
|
@ -703,6 +703,13 @@ void Avatar::renderBody(bool lookingInMirror, bool renderAvatarBalls) {
|
|||
}
|
||||
} else if (renderAvatarBalls || !_voxels.getVoxelURL().isValid()) {
|
||||
// Render the body as balls and cones
|
||||
glm::vec3 skinColor(SKIN_COLOR[0], SKIN_COLOR[1], SKIN_COLOR[2]);
|
||||
glm::vec3 darkSkinColor(DARK_SKIN_COLOR[0], DARK_SKIN_COLOR[1], DARK_SKIN_COLOR[2]);
|
||||
if (_head.getBlendFace().isActive()) {
|
||||
skinColor = glm::vec3(_head.getBlendFace().computeAverageColor());
|
||||
const float SKIN_DARKENING = 0.9f;
|
||||
darkSkinColor = skinColor * SKIN_DARKENING;
|
||||
}
|
||||
for (int b = 0; b < NUM_AVATAR_BODY_BALLS; b++) {
|
||||
float alpha = getBallRenderAlpha(b, lookingInMirror);
|
||||
|
||||
|
@ -720,9 +727,9 @@ void Avatar::renderBody(bool lookingInMirror, bool renderAvatarBalls) {
|
|||
}
|
||||
} else if (alpha > 0.0f) {
|
||||
// Render the body ball sphere
|
||||
glColor3f(SKIN_COLOR[0] + _bodyBall[b].touchForce * 0.3f,
|
||||
SKIN_COLOR[1] - _bodyBall[b].touchForce * 0.2f,
|
||||
SKIN_COLOR[2] - _bodyBall[b].touchForce * 0.1f);
|
||||
glColor3f(skinColor.r + _bodyBall[b].touchForce * 0.3f,
|
||||
skinColor.g - _bodyBall[b].touchForce * 0.2f,
|
||||
skinColor.b - _bodyBall[b].touchForce * 0.1f);
|
||||
|
||||
if (b == BODY_BALL_NECK_BASE && _head.getBlendFace().isActive()) {
|
||||
continue; // don't render the neck if we have a face model
|
||||
|
@ -747,7 +754,7 @@ void Avatar::renderBody(bool lookingInMirror, bool renderAvatarBalls) {
|
|||
&& (b != BODY_BALL_LEFT_SHOULDER)
|
||||
&& (b != BODY_BALL_RIGHT_COLLAR)
|
||||
&& (b != BODY_BALL_RIGHT_SHOULDER)) {
|
||||
glColor3fv(DARK_SKIN_COLOR);
|
||||
glColor3fv((const GLfloat*)&darkSkinColor);
|
||||
|
||||
float r2 = _bodyBall[b].radius * 0.8;
|
||||
renderJointConnectingCone(_bodyBall[_bodyBall[b].parentBall].position, _bodyBall[b].position, r2, r2);
|
||||
|
|
|
@ -342,6 +342,10 @@ bool BlendFace::getEyePositions(glm::vec3& firstEyePosition, glm::vec3& secondEy
|
|||
return false;
|
||||
}
|
||||
|
||||
glm::vec4 BlendFace::computeAverageColor() const {
|
||||
return _geometry ? _geometry->computeAverageColor() : glm::vec4(1.0f, 1.0f, 1.0f, 1.0f);
|
||||
}
|
||||
|
||||
void BlendFace::setModelURL(const QUrl& url) {
|
||||
// don't recreate the geometry if it's the same URL
|
||||
if (_modelURL == url) {
|
||||
|
|
|
@ -45,6 +45,9 @@ public:
|
|||
/// \return whether or not both eye meshes were found
|
||||
bool getEyePositions(glm::vec3& firstEyePosition, glm::vec3& secondEyePosition, bool upright = false) const;
|
||||
|
||||
/// Returns the average color of all meshes in the geometry.
|
||||
glm::vec4 computeAverageColor() const;
|
||||
|
||||
private:
|
||||
|
||||
void deleteGeometry();
|
||||
|
|
|
@ -83,8 +83,6 @@ Head::Head(Avatar* owningAvatar) :
|
|||
_mousePitch(0.f),
|
||||
_cameraYaw(_yaw),
|
||||
_isCameraMoving(false),
|
||||
_cameraFollowsHead(false),
|
||||
_cameraFollowHeadRate(0.0f),
|
||||
_face(this),
|
||||
_perlinFace(this),
|
||||
_blendFace(this)
|
||||
|
@ -142,8 +140,10 @@ void Head::simulate(float deltaTime, bool isMine) {
|
|||
|
||||
// Update audio trailing average for rendering facial animations
|
||||
Faceshift* faceshift = Application::getInstance()->getFaceshift();
|
||||
_isFaceshiftConnected = faceshift != NULL;
|
||||
|
||||
if (isMine) {
|
||||
_isFaceshiftConnected = faceshift->isActive();
|
||||
}
|
||||
|
||||
if (isMine && faceshift->isActive()) {
|
||||
const float EYE_OPEN_SCALE = 0.5f;
|
||||
_leftEyeBlink = faceshift->getLeftBlink() - EYE_OPEN_SCALE * faceshift->getLeftEyeOpen();
|
||||
|
@ -157,7 +157,7 @@ void Head::simulate(float deltaTime, bool isMine) {
|
|||
_browAudioLift = faceshift->getBrowUpCenter() * BROW_HEIGHT_SCALE;
|
||||
_blendshapeCoefficients = faceshift->getBlendshapeCoefficients();
|
||||
|
||||
} else if (!_isFaceshiftConnected) {
|
||||
} else if (!_isFaceshiftConnected) {
|
||||
// Update eye saccades
|
||||
const float AVERAGE_MICROSACCADE_INTERVAL = 0.50f;
|
||||
const float AVERAGE_SACCADE_INTERVAL = 4.0f;
|
||||
|
|
|
@ -57,7 +57,6 @@ public:
|
|||
void setAverageLoudness(float averageLoudness) { _averageLoudness = averageLoudness; }
|
||||
void setReturnToCenter (bool returnHeadToCenter) { _returnHeadToCenter = returnHeadToCenter; }
|
||||
void setRenderLookatVectors(bool onOff) { _renderLookatVectors = onOff; }
|
||||
void setCameraFollowsHead(bool cameraFollowsHead) { _cameraFollowsHead = cameraFollowsHead; }
|
||||
|
||||
float getMousePitch() const { return _mousePitch; }
|
||||
void setMousePitch(float mousePitch) { _mousePitch = mousePitch; }
|
||||
|
@ -133,8 +132,6 @@ private:
|
|||
float _mousePitch;
|
||||
float _cameraYaw;
|
||||
bool _isCameraMoving;
|
||||
bool _cameraFollowsHead;
|
||||
float _cameraFollowHeadRate;
|
||||
Face _face;
|
||||
PerlinFace _perlinFace;
|
||||
BlendFace _blendFace;
|
||||
|
|
|
@ -357,8 +357,7 @@ void MyAvatar::simulate(float deltaTime, Transmitter* transmitter) {
|
|||
}
|
||||
|
||||
// Update avatar head rotation with sensor data
|
||||
void MyAvatar::updateFromGyrosAndOrWebcam(bool gyroLook,
|
||||
float pitchFromTouch) {
|
||||
void MyAvatar::updateFromGyrosAndOrWebcam(float pitchFromTouch, bool turnWithHead) {
|
||||
Faceshift* faceshift = Application::getInstance()->getFaceshift();
|
||||
SerialInterface* gyros = Application::getInstance()->getSerialHeadSensor();
|
||||
Webcam* webcam = Application::getInstance()->getWebcam();
|
||||
|
@ -368,11 +367,13 @@ void MyAvatar::updateFromGyrosAndOrWebcam(bool gyroLook,
|
|||
estimatedPosition = faceshift->getHeadTranslation();
|
||||
estimatedRotation = safeEulerAngles(faceshift->getHeadRotation());
|
||||
// Rotate the body if the head is turned quickly
|
||||
glm::vec3 headAngularVelocity = faceshift->getHeadAngularVelocity();
|
||||
const float FACESHIFT_YAW_VIEW_SENSITIVITY = 20.f;
|
||||
const float FACESHIFT_MIN_YAW_VELOCITY = 1.0f;
|
||||
if (fabs(headAngularVelocity.y) > FACESHIFT_MIN_YAW_VELOCITY) {
|
||||
_bodyYawDelta += headAngularVelocity.y * FACESHIFT_YAW_VIEW_SENSITIVITY;
|
||||
if (turnWithHead) {
|
||||
glm::vec3 headAngularVelocity = faceshift->getHeadAngularVelocity();
|
||||
const float FACESHIFT_YAW_VIEW_SENSITIVITY = 20.f;
|
||||
const float FACESHIFT_MIN_YAW_VELOCITY = 1.0f;
|
||||
if (fabs(headAngularVelocity.y) > FACESHIFT_MIN_YAW_VELOCITY) {
|
||||
_bodyYawDelta += headAngularVelocity.y * FACESHIFT_YAW_VIEW_SENSITIVITY;
|
||||
}
|
||||
}
|
||||
} else if (gyros->isActive()) {
|
||||
estimatedRotation = gyros->getEstimatedRotation();
|
||||
|
@ -429,7 +430,6 @@ void MyAvatar::updateFromGyrosAndOrWebcam(bool gyroLook,
|
|||
_head.setPitch(estimatedRotation.x * AVATAR_HEAD_PITCH_MAGNIFY);
|
||||
_head.setYaw(estimatedRotation.y * AVATAR_HEAD_YAW_MAGNIFY);
|
||||
_head.setRoll(estimatedRotation.z * AVATAR_HEAD_ROLL_MAGNIFY);
|
||||
_head.setCameraFollowsHead(gyroLook);
|
||||
|
||||
// Update torso lean distance based on accelerometer data
|
||||
const float TORSO_LENGTH = _scale * 0.5f;
|
||||
|
@ -577,10 +577,10 @@ glm::vec3 MyAvatar::getUprightHeadPosition() const {
|
|||
return _position + getWorldAlignedOrientation() * glm::vec3(0.0f, _pelvisToHeadLength, 0.0f);
|
||||
}
|
||||
|
||||
glm::vec3 MyAvatar::getUprightEyeLevelPosition() const {
|
||||
const float EYE_UP_OFFSET = 0.36f;
|
||||
glm::vec3 up = getWorldAlignedOrientation() * IDENTITY_UP;
|
||||
return _position + up * _scale * BODY_BALL_RADIUS_HEAD_BASE * EYE_UP_OFFSET + glm::vec3(0.0f, _pelvisToHeadLength, 0.0f);
|
||||
glm::vec3 MyAvatar::getEyeLevelPosition() const {
|
||||
const float EYE_UP_OFFSET = 0.36f;
|
||||
return _position + getWorldAlignedOrientation() * _skeleton.joint[AVATAR_JOINT_TORSO].rotation *
|
||||
glm::vec3(0.0f, _pelvisToHeadLength + _scale * BODY_BALL_RADIUS_HEAD_BASE * EYE_UP_OFFSET, 0.0f);
|
||||
}
|
||||
|
||||
float MyAvatar::getBallRenderAlpha(int ball, bool lookingInMirror) const {
|
||||
|
@ -608,6 +608,13 @@ void MyAvatar::renderBody(bool lookingInMirror, bool renderAvatarBalls) {
|
|||
}
|
||||
} else if (renderAvatarBalls || !_voxels.getVoxelURL().isValid()) {
|
||||
// Render the body as balls and cones
|
||||
glm::vec3 skinColor(SKIN_COLOR[0], SKIN_COLOR[1], SKIN_COLOR[2]);
|
||||
glm::vec3 darkSkinColor(DARK_SKIN_COLOR[0], DARK_SKIN_COLOR[1], DARK_SKIN_COLOR[2]);
|
||||
if (_head.getBlendFace().isActive()) {
|
||||
skinColor = glm::vec3(_head.getBlendFace().computeAverageColor());
|
||||
const float SKIN_DARKENING = 0.9f;
|
||||
darkSkinColor = skinColor * SKIN_DARKENING;
|
||||
}
|
||||
for (int b = 0; b < NUM_AVATAR_BODY_BALLS; b++) {
|
||||
float alpha = getBallRenderAlpha(b, lookingInMirror);
|
||||
|
||||
|
@ -628,14 +635,14 @@ void MyAvatar::renderBody(bool lookingInMirror, bool renderAvatarBalls) {
|
|||
if (b == BODY_BALL_RIGHT_ELBOW
|
||||
|| b == BODY_BALL_RIGHT_WRIST
|
||||
|| b == BODY_BALL_RIGHT_FINGERTIPS ) {
|
||||
glColor3f(SKIN_COLOR[0] + _bodyBall[b].touchForce * 0.3f,
|
||||
SKIN_COLOR[1] - _bodyBall[b].touchForce * 0.2f,
|
||||
SKIN_COLOR[2] - _bodyBall[b].touchForce * 0.1f);
|
||||
glColor3f(skinColor.r + _bodyBall[b].touchForce * 0.3f,
|
||||
skinColor.g - _bodyBall[b].touchForce * 0.2f,
|
||||
skinColor.b - _bodyBall[b].touchForce * 0.1f);
|
||||
} else {
|
||||
glColor4f(SKIN_COLOR[0] + _bodyBall[b].touchForce * 0.3f,
|
||||
SKIN_COLOR[1] - _bodyBall[b].touchForce * 0.2f,
|
||||
SKIN_COLOR[2] - _bodyBall[b].touchForce * 0.1f,
|
||||
alpha);
|
||||
glColor4f(skinColor.r + _bodyBall[b].touchForce * 0.3f,
|
||||
skinColor.g - _bodyBall[b].touchForce * 0.2f,
|
||||
skinColor.b - _bodyBall[b].touchForce * 0.1f,
|
||||
alpha);
|
||||
}
|
||||
|
||||
if (b == BODY_BALL_NECK_BASE && _head.getBlendFace().isActive()) {
|
||||
|
@ -661,7 +668,7 @@ void MyAvatar::renderBody(bool lookingInMirror, bool renderAvatarBalls) {
|
|||
&& (b != BODY_BALL_LEFT_SHOULDER)
|
||||
&& (b != BODY_BALL_RIGHT_COLLAR)
|
||||
&& (b != BODY_BALL_RIGHT_SHOULDER)) {
|
||||
glColor3fv(DARK_SKIN_COLOR);
|
||||
glColor3fv((const GLfloat*)&darkSkinColor);
|
||||
|
||||
float r2 = _bodyBall[b].radius * 0.8;
|
||||
|
||||
|
|
|
@ -19,7 +19,7 @@ public:
|
|||
|
||||
void reset();
|
||||
void simulate(float deltaTime, Transmitter* transmitter);
|
||||
void updateFromGyrosAndOrWebcam(bool gyroLook, float pitchFromTouch);
|
||||
void updateFromGyrosAndOrWebcam(float pitchFromTouch, bool turnWithHead);
|
||||
void render(bool lookingInMirror, bool renderAvatarBalls);
|
||||
void renderScreenTint(ScreenTintLayer layer, Camera& whichCamera);
|
||||
|
||||
|
@ -48,7 +48,7 @@ public:
|
|||
Avatar* getLeadingAvatar() const { return _leadingAvatar; }
|
||||
glm::vec3 getGravity() const { return _gravity; }
|
||||
glm::vec3 getUprightHeadPosition() const;
|
||||
glm::vec3 getUprightEyeLevelPosition() const;
|
||||
glm::vec3 getEyeLevelPosition() const;
|
||||
|
||||
// get/set avatar data
|
||||
void saveData(QSettings* settings);
|
||||
|
|
|
@ -8,33 +8,32 @@
|
|||
|
||||
#include <QtCore/QSettings>
|
||||
|
||||
#include <UUID.h>
|
||||
|
||||
#include "Profile.h"
|
||||
#include "DataServerClient.h"
|
||||
|
||||
Profile::Profile() :
|
||||
_username(),
|
||||
Profile::Profile(const QString &username) :
|
||||
_username(username),
|
||||
_uuid(),
|
||||
_faceModelURL()
|
||||
_lastDomain(),
|
||||
_lastPosition(0.0, 0.0, 0.0),
|
||||
_faceModelURL()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void Profile::clear() {
|
||||
_username.clear();
|
||||
_uuid = QUuid();
|
||||
_faceModelURL.clear();
|
||||
}
|
||||
|
||||
void Profile::setUsername(const QString &username) {
|
||||
this->clear();
|
||||
_username = username;
|
||||
|
||||
if (!_username.isEmpty()) {
|
||||
// we've been given a new username, ask the data-server for our UUID
|
||||
// we've been given a new username, ask the data-server for profile
|
||||
DataServerClient::getClientValueForKey(DataServerKey::UUID);
|
||||
}
|
||||
}
|
||||
|
||||
QString Profile::getUserString() const {
|
||||
if (_uuid.isNull()) {
|
||||
return _username;
|
||||
} else {
|
||||
return uuidStringWithoutCurlyBraces(_uuid);
|
||||
}
|
||||
}
|
||||
|
||||
void Profile::setUUID(const QUuid& uuid) {
|
||||
_uuid = uuid;
|
||||
|
||||
|
@ -50,6 +49,42 @@ void Profile::setFaceModelURL(const QUrl& faceModelURL) {
|
|||
Q_ARG(QUrl, _faceModelURL));
|
||||
}
|
||||
|
||||
void Profile::updateDomain(const QString& domain) {
|
||||
if (_lastDomain != domain) {
|
||||
_lastDomain = domain;
|
||||
|
||||
// send the changed domain to the data-server
|
||||
DataServerClient::putValueForKey(DataServerKey::Domain, domain.toLocal8Bit().constData());
|
||||
}
|
||||
}
|
||||
|
||||
void Profile::updatePosition(const glm::vec3 position) {
|
||||
if (_lastPosition != position) {
|
||||
|
||||
static timeval lastPositionSend = {};
|
||||
const uint64_t DATA_SERVER_POSITION_UPDATE_INTERVAL_USECS = 5 * 1000 * 1000;
|
||||
const float DATA_SERVER_POSITION_CHANGE_THRESHOLD_METERS = 1;
|
||||
|
||||
if (usecTimestampNow() - usecTimestamp(&lastPositionSend) >= DATA_SERVER_POSITION_UPDATE_INTERVAL_USECS &&
|
||||
(fabsf(_lastPosition.x - position.x) >= DATA_SERVER_POSITION_CHANGE_THRESHOLD_METERS ||
|
||||
fabsf(_lastPosition.y - position.y) >= DATA_SERVER_POSITION_CHANGE_THRESHOLD_METERS ||
|
||||
fabsf(_lastPosition.z - position.z) >= DATA_SERVER_POSITION_CHANGE_THRESHOLD_METERS)) {
|
||||
|
||||
// if it has been 5 seconds since the last position change and the user has moved >= the threshold
|
||||
// in at least one of the axis then send the position update to the data-server
|
||||
|
||||
_lastPosition = position;
|
||||
|
||||
// update the lastPositionSend to now
|
||||
gettimeofday(&lastPositionSend, NULL);
|
||||
|
||||
// send the changed position to the data-server
|
||||
QString positionString = QString("%1,%2,%3").arg(position.x).arg(position.y).arg(position.z);
|
||||
DataServerClient::putValueForKey(DataServerKey::Position, positionString.toLocal8Bit().constData());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Profile::saveData(QSettings* settings) {
|
||||
settings->beginGroup("Profile");
|
||||
|
||||
|
|
|
@ -13,26 +13,35 @@
|
|||
#include <QtCore/QUrl>
|
||||
#include <QtCore/QUuid>
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
class Profile {
|
||||
public:
|
||||
Profile();
|
||||
Profile(const QString& username);
|
||||
|
||||
void setUsername(const QString& username);
|
||||
QString& getUsername() { return _username; }
|
||||
QString getUserString() const;
|
||||
|
||||
const QString& getUsername() const { return _username; }
|
||||
|
||||
void setUUID(const QUuid& uuid);
|
||||
QUuid& getUUID() { return _uuid; }
|
||||
const QUuid& getUUID() { return _uuid; }
|
||||
|
||||
void setFaceModelURL(const QUrl& faceModelURL);
|
||||
QUrl& getFaceModelURL() { return _faceModelURL; }
|
||||
const QUrl& getFaceModelURL() const { return _faceModelURL; }
|
||||
|
||||
void clear();
|
||||
void updateDomain(const QString& domain);
|
||||
void updatePosition(const glm::vec3 position);
|
||||
|
||||
QString getLastDomain() const { return _lastDomain; }
|
||||
const glm::vec3& getLastPosition() const { return _lastPosition; }
|
||||
|
||||
void saveData(QSettings* settings);
|
||||
void loadData(QSettings* settings);
|
||||
private:
|
||||
QString _username;
|
||||
QUuid _uuid;
|
||||
QString _lastDomain;
|
||||
glm::vec3 _lastPosition;
|
||||
QUrl _faceModelURL;
|
||||
};
|
||||
|
||||
|
|
|
@ -288,6 +288,23 @@ NetworkGeometry::~NetworkGeometry() {
|
|||
}
|
||||
}
|
||||
|
||||
glm::vec4 NetworkGeometry::computeAverageColor() const {
|
||||
glm::vec4 totalColor;
|
||||
int totalVertices = 0;
|
||||
for (int i = 0; i < _meshes.size(); i++) {
|
||||
if (_geometry.meshes.at(i).isEye) {
|
||||
continue; // skip eyes
|
||||
}
|
||||
glm::vec4 color = glm::vec4(_geometry.meshes.at(i).diffuseColor, 1.0f);
|
||||
if (_meshes.at(i).diffuseTexture) {
|
||||
color *= _meshes.at(i).diffuseTexture->getAverageColor();
|
||||
}
|
||||
totalColor += color * _geometry.meshes.at(i).vertices.size();
|
||||
totalVertices += _geometry.meshes.at(i).vertices.size();
|
||||
}
|
||||
return (totalVertices == 0) ? glm::vec4(1.0f, 1.0f, 1.0f, 1.0f) : totalColor / totalVertices;
|
||||
}
|
||||
|
||||
void NetworkGeometry::handleModelReplyError() {
|
||||
qDebug() << _modelReply->errorString() << "\n";
|
||||
|
||||
|
|
|
@ -62,6 +62,9 @@ public:
|
|||
const FBXGeometry& getFBXGeometry() const { return _geometry; }
|
||||
const QVector<NetworkMesh>& getMeshes() const { return _meshes; }
|
||||
|
||||
/// Returns the average color of all meshes in the geometry.
|
||||
glm::vec4 computeAverageColor() const;
|
||||
|
||||
private slots:
|
||||
|
||||
void handleModelReplyError();
|
||||
|
|
|
@ -173,7 +173,7 @@ Texture::~Texture() {
|
|||
glDeleteTextures(1, &_id);
|
||||
}
|
||||
|
||||
NetworkTexture::NetworkTexture(const QUrl& url) : _reply(NULL) {
|
||||
NetworkTexture::NetworkTexture(const QUrl& url) : _reply(NULL), _averageColor(1.0f, 1.0f, 1.0f, 1.0f) {
|
||||
if (!url.isValid()) {
|
||||
return;
|
||||
}
|
||||
|
@ -206,6 +206,21 @@ void NetworkTexture::handleDownloadProgress(qint64 bytesReceived, qint64 bytesTo
|
|||
_reply = NULL;
|
||||
|
||||
QImage image = QImage::fromData(entirety).convertToFormat(QImage::Format_ARGB32);
|
||||
|
||||
// sum up the colors for the average
|
||||
glm::vec4 accumulated;
|
||||
for (int y = 0; y < image.height(); y++) {
|
||||
for (int x = 0; x < image.width(); x++) {
|
||||
QRgb pixel = image.pixel(x, y);
|
||||
accumulated.r += qRed(pixel);
|
||||
accumulated.g += qGreen(pixel);
|
||||
accumulated.b += qBlue(pixel);
|
||||
accumulated.a += qAlpha(pixel);
|
||||
}
|
||||
}
|
||||
const float EIGHT_BIT_MAXIMUM = 255.0f;
|
||||
_averageColor = accumulated / (image.width() * image.height() * EIGHT_BIT_MAXIMUM);
|
||||
|
||||
imageLoaded(image);
|
||||
glBindTexture(GL_TEXTURE_2D, getID());
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, image.width(), image.height(), 1,
|
||||
|
|
|
@ -99,6 +99,9 @@ public:
|
|||
NetworkTexture(const QUrl& url);
|
||||
~NetworkTexture();
|
||||
|
||||
/// Returns the average color over the entire texture.
|
||||
const glm::vec4& getAverageColor() const { return _averageColor; }
|
||||
|
||||
protected:
|
||||
|
||||
virtual void imageLoaded(const QImage& image);
|
||||
|
@ -111,6 +114,7 @@ private slots:
|
|||
private:
|
||||
|
||||
QNetworkReply* _reply;
|
||||
glm::vec4 _averageColor;
|
||||
};
|
||||
|
||||
/// Caches derived, dilated textures.
|
||||
|
|
|
@ -4,9 +4,7 @@
|
|||
//
|
||||
// Created by Philip Rosedale on 8/23/12.
|
||||
// Copyright (c) 2012 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
// Simulation happens in positive cube with edge of size WORLD_SIZE
|
||||
//
|
||||
|
||||
#ifndef __interface__world__
|
||||
#define __interface__world__
|
||||
|
|
Loading…
Reference in a new issue