mirror of
https://github.com/lubosz/overte.git
synced 2025-08-14 13:32:22 +02:00
Merge branch 'master' of https://github.com/worklist/hifi into 19426
This commit is contained in:
commit
a87346d39c
67 changed files with 2342 additions and 892 deletions
|
@ -15,7 +15,6 @@ set(CMAKE_AUTOMOC ON)
|
||||||
add_subdirectory(animation-server)
|
add_subdirectory(animation-server)
|
||||||
add_subdirectory(assignment-client)
|
add_subdirectory(assignment-client)
|
||||||
add_subdirectory(domain-server)
|
add_subdirectory(domain-server)
|
||||||
add_subdirectory(eve)
|
|
||||||
add_subdirectory(interface)
|
add_subdirectory(interface)
|
||||||
add_subdirectory(injector)
|
add_subdirectory(injector)
|
||||||
add_subdirectory(pairing-server)
|
add_subdirectory(pairing-server)
|
||||||
|
|
|
@ -594,8 +594,12 @@ void* animateVoxels(void* args) {
|
||||||
|
|
||||||
timeval lastSendTime;
|
timeval lastSendTime;
|
||||||
|
|
||||||
|
bool firstTime = true;
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
gettimeofday(&lastSendTime, NULL);
|
gettimeofday(&lastSendTime, NULL);
|
||||||
|
|
||||||
|
int packetsStarting = ::voxelEditPacketSender->packetsToSendCount();
|
||||||
|
|
||||||
// some animations
|
// some animations
|
||||||
//sendVoxelBlinkMessage();
|
//sendVoxelBlinkMessage();
|
||||||
|
@ -619,12 +623,22 @@ void* animateVoxels(void* args) {
|
||||||
if (::buildStreet) {
|
if (::buildStreet) {
|
||||||
doBuildStreet();
|
doBuildStreet();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
::voxelEditPacketSender->releaseQueuedMessages();
|
||||||
|
int packetsEnding = ::voxelEditPacketSender->packetsToSendCount();
|
||||||
|
|
||||||
if (::voxelEditPacketSender) {
|
if (firstTime) {
|
||||||
::voxelEditPacketSender->releaseQueuedMessages();
|
int packetsPerSecond = std::max(ACTUAL_FPS, (packetsEnding - packetsStarting) * (ACTUAL_FPS));
|
||||||
if (::nonThreadedPacketSender) {
|
|
||||||
::voxelEditPacketSender->process();
|
std::cout << "Setting PPS to " << packetsPerSecond << "\n";
|
||||||
}
|
|
||||||
|
::voxelEditPacketSender->setPacketsPerSecond(packetsPerSecond);
|
||||||
|
firstTime = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (::nonThreadedPacketSender) {
|
||||||
|
::voxelEditPacketSender->process();
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t end = usecTimestampNow();
|
uint64_t end = usecTimestampNow();
|
||||||
|
@ -715,14 +729,13 @@ int main(int argc, const char * argv[])
|
||||||
|
|
||||||
// Create out VoxelEditPacketSender
|
// Create out VoxelEditPacketSender
|
||||||
::voxelEditPacketSender = new VoxelEditPacketSender;
|
::voxelEditPacketSender = new VoxelEditPacketSender;
|
||||||
if (::voxelEditPacketSender) {
|
::voxelEditPacketSender->initialize(!::nonThreadedPacketSender);
|
||||||
::voxelEditPacketSender->initialize(!::nonThreadedPacketSender);
|
|
||||||
if (::jurisdictionListener) {
|
if (::jurisdictionListener) {
|
||||||
::voxelEditPacketSender->setVoxelServerJurisdictions(::jurisdictionListener->getJurisdictions());
|
::voxelEditPacketSender->setVoxelServerJurisdictions(::jurisdictionListener->getJurisdictions());
|
||||||
}
|
}
|
||||||
if (::nonThreadedPacketSender) {
|
if (::nonThreadedPacketSender) {
|
||||||
::voxelEditPacketSender->setProcessCallIntervalHint(ANIMATE_VOXELS_INTERVAL_USECS);
|
::voxelEditPacketSender->setProcessCallIntervalHint(ANIMATE_VOXELS_INTERVAL_USECS);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
srand((unsigned)time(0));
|
srand((unsigned)time(0));
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
|
|
||||||
#include <AvatarData.h>
|
#include <AvatarData.h>
|
||||||
#include <NodeList.h>
|
#include <NodeList.h>
|
||||||
|
#include <UUID.h>
|
||||||
#include <VoxelConstants.h>
|
#include <VoxelConstants.h>
|
||||||
|
|
||||||
#include "Agent.h"
|
#include "Agent.h"
|
||||||
|
@ -50,7 +51,7 @@ void Agent::run() {
|
||||||
// figure out the URL for the script for this agent assignment
|
// figure out the URL for the script for this agent assignment
|
||||||
QString scriptURLString("http://%1:8080/assignment/%2");
|
QString scriptURLString("http://%1:8080/assignment/%2");
|
||||||
scriptURLString = scriptURLString.arg(NodeList::getInstance()->getDomainIP().toString(),
|
scriptURLString = scriptURLString.arg(NodeList::getInstance()->getDomainIP().toString(),
|
||||||
this->getUUIDStringWithoutCurlyBraces());
|
uuidStringWithoutCurlyBraces(_uuid));
|
||||||
|
|
||||||
// setup curl for script download
|
// setup curl for script download
|
||||||
CURLcode curlResult;
|
CURLcode curlResult;
|
||||||
|
|
|
@ -98,7 +98,7 @@ void AvatarMixer::run() {
|
||||||
|
|
||||||
nodeList->startSilentNodeRemovalThread();
|
nodeList->startSilentNodeRemovalThread();
|
||||||
|
|
||||||
sockaddr* nodeAddress = new sockaddr;
|
sockaddr nodeAddress = {};
|
||||||
ssize_t receivedBytes = 0;
|
ssize_t receivedBytes = 0;
|
||||||
|
|
||||||
unsigned char* packetData = new unsigned char[MAX_PACKET_SIZE];
|
unsigned char* packetData = new unsigned char[MAX_PACKET_SIZE];
|
||||||
|
@ -107,8 +107,6 @@ void AvatarMixer::run() {
|
||||||
Node* avatarNode = NULL;
|
Node* avatarNode = NULL;
|
||||||
|
|
||||||
timeval lastDomainServerCheckIn = {};
|
timeval lastDomainServerCheckIn = {};
|
||||||
// we only need to hear back about avatar nodes from the DS
|
|
||||||
nodeList->setNodeTypesOfInterest(&NODE_TYPE_AGENT, 1);
|
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
|
|
||||||
|
@ -122,7 +120,7 @@ void AvatarMixer::run() {
|
||||||
NodeList::getInstance()->sendDomainServerCheckIn(_uuid.toRfc4122().constData());
|
NodeList::getInstance()->sendDomainServerCheckIn(_uuid.toRfc4122().constData());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (nodeList->getNodeSocket()->receive(nodeAddress, packetData, &receivedBytes) &&
|
if (nodeList->getNodeSocket()->receive(&nodeAddress, packetData, &receivedBytes) &&
|
||||||
packetVersionMatch(packetData)) {
|
packetVersionMatch(packetData)) {
|
||||||
switch (packetData[0]) {
|
switch (packetData[0]) {
|
||||||
case PACKET_TYPE_HEAD_DATA:
|
case PACKET_TYPE_HEAD_DATA:
|
||||||
|
@ -130,12 +128,12 @@ void AvatarMixer::run() {
|
||||||
unpackNodeId(packetData + numBytesForPacketHeader(packetData), &nodeID);
|
unpackNodeId(packetData + numBytesForPacketHeader(packetData), &nodeID);
|
||||||
|
|
||||||
// add or update the node in our list
|
// add or update the node in our list
|
||||||
avatarNode = nodeList->addOrUpdateNode(nodeAddress, nodeAddress, NODE_TYPE_AGENT, nodeID);
|
avatarNode = nodeList->addOrUpdateNode(&nodeAddress, &nodeAddress, NODE_TYPE_AGENT, nodeID);
|
||||||
|
|
||||||
// parse positional data from an node
|
// parse positional data from an node
|
||||||
nodeList->updateNodeWithData(avatarNode, packetData, receivedBytes);
|
nodeList->updateNodeWithData(avatarNode, packetData, receivedBytes);
|
||||||
case PACKET_TYPE_INJECT_AUDIO:
|
case PACKET_TYPE_INJECT_AUDIO:
|
||||||
broadcastAvatarData(nodeList, nodeAddress);
|
broadcastAvatarData(nodeList, &nodeAddress);
|
||||||
break;
|
break;
|
||||||
case PACKET_TYPE_AVATAR_URLS:
|
case PACKET_TYPE_AVATAR_URLS:
|
||||||
case PACKET_TYPE_AVATAR_FACE_VIDEO:
|
case PACKET_TYPE_AVATAR_FACE_VIDEO:
|
||||||
|
@ -151,7 +149,7 @@ void AvatarMixer::run() {
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
// hand this off to the NodeList
|
// hand this off to the NodeList
|
||||||
nodeList->processNodeData(nodeAddress, packetData, receivedBytes);
|
nodeList->processNodeData(&nodeAddress, packetData, receivedBytes);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -66,11 +66,12 @@ void childClient() {
|
||||||
// create a request assignment, accept assignments defined by the overidden type
|
// create a request assignment, accept assignments defined by the overidden type
|
||||||
Assignment requestAssignment(Assignment::RequestCommand, ::overiddenAssignmentType);
|
Assignment requestAssignment(Assignment::RequestCommand, ::overiddenAssignmentType);
|
||||||
|
|
||||||
|
// if we're here we have no assignment, so send a request
|
||||||
|
qDebug() << "Waiting for assignment -" << requestAssignment << "\n";
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
if (usecTimestampNow() - usecTimestamp(&lastRequest) >= ASSIGNMENT_REQUEST_INTERVAL_USECS) {
|
if (usecTimestampNow() - usecTimestamp(&lastRequest) >= ASSIGNMENT_REQUEST_INTERVAL_USECS) {
|
||||||
gettimeofday(&lastRequest, NULL);
|
gettimeofday(&lastRequest, NULL);
|
||||||
// if we're here we have no assignment, so send a request
|
|
||||||
qDebug() << "Sending an assignment request -" << requestAssignment << "\n";
|
|
||||||
nodeList->sendAssignment(requestAssignment);
|
nodeList->sendAssignment(requestAssignment);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
|
|
||||||
#include <PacketHeaders.h>
|
#include <PacketHeaders.h>
|
||||||
#include <SharedUtil.h>
|
#include <SharedUtil.h>
|
||||||
|
#include <UUID.h>
|
||||||
|
|
||||||
#include "DomainServer.h"
|
#include "DomainServer.h"
|
||||||
|
|
||||||
|
@ -62,7 +63,7 @@ void DomainServer::civetwebUploadHandler(struct mg_connection *connection, const
|
||||||
QString newPath(ASSIGNMENT_SCRIPT_HOST_LOCATION);
|
QString newPath(ASSIGNMENT_SCRIPT_HOST_LOCATION);
|
||||||
newPath += "/";
|
newPath += "/";
|
||||||
// append the UUID for this script as the new filename, remove the curly braces
|
// append the UUID for this script as the new filename, remove the curly braces
|
||||||
newPath += scriptAssignment->getUUIDStringWithoutCurlyBraces();
|
newPath += uuidStringWithoutCurlyBraces(scriptAssignment->getUUID());
|
||||||
|
|
||||||
// rename the saved script to the GUID of the assignment and move it to the script host locaiton
|
// rename the saved script to the GUID of the assignment and move it to the script host locaiton
|
||||||
rename(path, newPath.toLocal8Bit().constData());
|
rename(path, newPath.toLocal8Bit().constData());
|
||||||
|
|
|
@ -1,21 +0,0 @@
|
||||||
cmake_minimum_required(VERSION 2.8)
|
|
||||||
|
|
||||||
set(ROOT_DIR ..)
|
|
||||||
set(MACRO_DIR ${ROOT_DIR}/cmake/macros)
|
|
||||||
|
|
||||||
# setup for find modules
|
|
||||||
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/../cmake/modules/")
|
|
||||||
|
|
||||||
set(TARGET_NAME eve)
|
|
||||||
|
|
||||||
include(${MACRO_DIR}/SetupHifiProject.cmake)
|
|
||||||
setup_hifi_project(${TARGET_NAME} TRUE)
|
|
||||||
|
|
||||||
include(${MACRO_DIR}/IncludeGLM.cmake)
|
|
||||||
include_glm(${TARGET_NAME} ${ROOT_DIR})
|
|
||||||
|
|
||||||
# link the required hifi libraries
|
|
||||||
include(${MACRO_DIR}/LinkHifiLibrary.cmake)
|
|
||||||
link_hifi_library(shared ${TARGET_NAME} ${ROOT_DIR})
|
|
||||||
link_hifi_library(avatars ${TARGET_NAME} ${ROOT_DIR})
|
|
||||||
link_hifi_library(audio ${TARGET_NAME} ${ROOT_DIR})
|
|
Binary file not shown.
212
eve/src/main.cpp
212
eve/src/main.cpp
|
@ -1,212 +0,0 @@
|
||||||
//
|
|
||||||
// main.cpp
|
|
||||||
// eve
|
|
||||||
//
|
|
||||||
// Created by Stephen Birarda on 4/22/13.
|
|
||||||
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#include <cstring>
|
|
||||||
#include <sys/time.h>
|
|
||||||
#include <cstring>
|
|
||||||
|
|
||||||
#include <SharedUtil.h>
|
|
||||||
#include <NodeTypes.h>
|
|
||||||
#include <PacketHeaders.h>
|
|
||||||
#include <NodeList.h>
|
|
||||||
#include <AvatarData.h>
|
|
||||||
#include <AudioInjectionManager.h>
|
|
||||||
#include <AudioInjector.h>
|
|
||||||
|
|
||||||
const int EVE_NODE_LISTEN_PORT = 55441;
|
|
||||||
|
|
||||||
const float RANDOM_POSITION_MAX_DIMENSION = 10.0f;
|
|
||||||
|
|
||||||
const float DATA_SEND_INTERVAL_MSECS = 15;
|
|
||||||
const float MIN_AUDIO_SEND_INTERVAL_SECS = 10;
|
|
||||||
const int MIN_ITERATIONS_BETWEEN_AUDIO_SENDS = (MIN_AUDIO_SEND_INTERVAL_SECS * 1000) / DATA_SEND_INTERVAL_MSECS;
|
|
||||||
const int MAX_AUDIO_SEND_INTERVAL_SECS = 15;
|
|
||||||
const float MAX_ITERATIONS_BETWEEN_AUDIO_SENDS = (MAX_AUDIO_SEND_INTERVAL_SECS * 1000) / DATA_SEND_INTERVAL_MSECS;
|
|
||||||
|
|
||||||
const int ITERATIONS_BEFORE_HAND_GRAB = 100;
|
|
||||||
const int HAND_GRAB_DURATION_ITERATIONS = 50;
|
|
||||||
const int HAND_TIMER_SLEEP_ITERATIONS = 50;
|
|
||||||
|
|
||||||
const float EVE_PELVIS_HEIGHT = 0.565925f;
|
|
||||||
|
|
||||||
const float AUDIO_INJECT_PROXIMITY = 0.4f;
|
|
||||||
const int EVE_VOLUME_BYTE = 190;
|
|
||||||
|
|
||||||
const char EVE_AUDIO_FILENAME[] = "/etc/highfidelity/eve/resources/eve.raw";
|
|
||||||
|
|
||||||
bool stopReceiveNodeDataThread;
|
|
||||||
|
|
||||||
void *receiveNodeData(void *args) {
|
|
||||||
sockaddr senderAddress;
|
|
||||||
ssize_t bytesReceived;
|
|
||||||
unsigned char incomingPacket[MAX_PACKET_SIZE];
|
|
||||||
|
|
||||||
NodeList* nodeList = NodeList::getInstance();
|
|
||||||
|
|
||||||
while (!::stopReceiveNodeDataThread) {
|
|
||||||
if (nodeList->getNodeSocket()->receive(&senderAddress, incomingPacket, &bytesReceived) &&
|
|
||||||
packetVersionMatch(incomingPacket)) {
|
|
||||||
switch (incomingPacket[0]) {
|
|
||||||
case PACKET_TYPE_BULK_AVATAR_DATA:
|
|
||||||
// this is the positional data for other nodes
|
|
||||||
// pass that off to the nodeList processBulkNodeData method
|
|
||||||
nodeList->processBulkNodeData(&senderAddress, incomingPacket, bytesReceived);
|
|
||||||
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
// have the nodeList handle list of nodes from DS, replies from other nodes, etc.
|
|
||||||
nodeList->processNodeData(&senderAddress, incomingPacket, bytesReceived);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pthread_exit(0);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
void createAvatarDataForNode(Node* node) {
|
|
||||||
if (!node->getLinkedData()) {
|
|
||||||
node->setLinkedData(new AvatarData(node));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(int argc, const char* argv[]) {
|
|
||||||
// new seed for random audio sleep times
|
|
||||||
srand(time(0));
|
|
||||||
|
|
||||||
// create an NodeList instance to handle communication with other nodes
|
|
||||||
NodeList* nodeList = NodeList::createInstance(NODE_TYPE_AGENT, EVE_NODE_LISTEN_PORT);
|
|
||||||
|
|
||||||
// start the node list thread that will kill off nodes when they stop talking
|
|
||||||
nodeList->startSilentNodeRemovalThread();
|
|
||||||
|
|
||||||
pthread_t receiveNodeDataThread;
|
|
||||||
pthread_create(&receiveNodeDataThread, NULL, receiveNodeData, NULL);
|
|
||||||
|
|
||||||
// create an AvatarData object, "eve"
|
|
||||||
AvatarData eve;
|
|
||||||
|
|
||||||
// move eve away from the origin
|
|
||||||
// pick a random point inside a 10x10 grid
|
|
||||||
|
|
||||||
eve.setPosition(glm::vec3(randFloatInRange(0, RANDOM_POSITION_MAX_DIMENSION),
|
|
||||||
EVE_PELVIS_HEIGHT, // this should be the same as the avatar's pelvis standing height
|
|
||||||
randFloatInRange(0, RANDOM_POSITION_MAX_DIMENSION)));
|
|
||||||
|
|
||||||
// face any instance of eve down the z-axis
|
|
||||||
eve.setBodyYaw(0);
|
|
||||||
|
|
||||||
// put her hand out so somebody can shake it
|
|
||||||
eve.setHandPosition(glm::vec3(eve.getPosition()[0] - 0.2,
|
|
||||||
0.5,
|
|
||||||
eve.getPosition()[2] + 0.1));
|
|
||||||
|
|
||||||
// prepare the audio injection manager by giving it a handle to our node socket
|
|
||||||
AudioInjectionManager::setInjectorSocket(nodeList->getNodeSocket());
|
|
||||||
|
|
||||||
// read eve's audio data
|
|
||||||
AudioInjector eveAudioInjector(EVE_AUDIO_FILENAME);
|
|
||||||
|
|
||||||
// lower Eve's volume by setting the attentuation modifier (this is a value out of 255)
|
|
||||||
eveAudioInjector.setVolume(EVE_VOLUME_BYTE);
|
|
||||||
|
|
||||||
// set the position of the audio injector
|
|
||||||
eveAudioInjector.setPosition(eve.getPosition());
|
|
||||||
|
|
||||||
// register the callback for node data creation
|
|
||||||
nodeList->linkedDataCreateCallback = createAvatarDataForNode;
|
|
||||||
|
|
||||||
unsigned char broadcastPacket[MAX_PACKET_SIZE];
|
|
||||||
int numHeaderBytes = populateTypeAndVersion(broadcastPacket, PACKET_TYPE_HEAD_DATA);
|
|
||||||
|
|
||||||
timeval thisSend;
|
|
||||||
int numMicrosecondsSleep = 0;
|
|
||||||
|
|
||||||
int handStateTimer = 0;
|
|
||||||
|
|
||||||
timeval lastDomainServerCheckIn = {};
|
|
||||||
|
|
||||||
// eve wants to hear about an avatar mixer and an audio mixer from the domain server
|
|
||||||
const char EVE_NODE_TYPES_OF_INTEREST[] = {NODE_TYPE_AVATAR_MIXER, NODE_TYPE_AUDIO_MIXER};
|
|
||||||
NodeList::getInstance()->setNodeTypesOfInterest(EVE_NODE_TYPES_OF_INTEREST, sizeof(EVE_NODE_TYPES_OF_INTEREST));
|
|
||||||
|
|
||||||
while (true) {
|
|
||||||
// send a check in packet to the domain server if DOMAIN_SERVER_CHECK_IN_USECS has elapsed
|
|
||||||
if (usecTimestampNow() - usecTimestamp(&lastDomainServerCheckIn) >= DOMAIN_SERVER_CHECK_IN_USECS) {
|
|
||||||
gettimeofday(&lastDomainServerCheckIn, NULL);
|
|
||||||
NodeList::getInstance()->sendDomainServerCheckIn();
|
|
||||||
}
|
|
||||||
|
|
||||||
// update the thisSend timeval to the current time
|
|
||||||
gettimeofday(&thisSend, NULL);
|
|
||||||
|
|
||||||
// find the current avatar mixer
|
|
||||||
Node* avatarMixer = nodeList->soloNodeOfType(NODE_TYPE_AVATAR_MIXER);
|
|
||||||
|
|
||||||
// make sure we actually have an avatar mixer with an active socket
|
|
||||||
if (nodeList->getOwnerID() != UNKNOWN_NODE_ID && avatarMixer && avatarMixer->getActiveSocket() != NULL) {
|
|
||||||
unsigned char* packetPosition = broadcastPacket + numHeaderBytes;
|
|
||||||
packetPosition += packNodeId(packetPosition, nodeList->getOwnerID());
|
|
||||||
|
|
||||||
// use the getBroadcastData method in the AvatarData class to populate the broadcastPacket buffer
|
|
||||||
packetPosition += eve.getBroadcastData(packetPosition);
|
|
||||||
|
|
||||||
// use the UDPSocket instance attached to our node list to send avatar data to mixer
|
|
||||||
nodeList->getNodeSocket()->send(avatarMixer->getActiveSocket(), broadcastPacket, packetPosition - broadcastPacket);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!eveAudioInjector.isInjectingAudio()) {
|
|
||||||
// enumerate the other nodes to decide if one is close enough that eve should talk
|
|
||||||
for (NodeList::iterator node = nodeList->begin(); node != nodeList->end(); node++) {
|
|
||||||
AvatarData* avatarData = (AvatarData*) node->getLinkedData();
|
|
||||||
|
|
||||||
if (avatarData) {
|
|
||||||
glm::vec3 tempVector = eve.getPosition() - avatarData->getPosition();
|
|
||||||
float squareDistance = glm::dot(tempVector, tempVector);
|
|
||||||
|
|
||||||
if (squareDistance <= AUDIO_INJECT_PROXIMITY) {
|
|
||||||
// look for an audio mixer in our node list
|
|
||||||
Node* audioMixer = NodeList::getInstance()->soloNodeOfType(NODE_TYPE_AUDIO_MIXER);
|
|
||||||
|
|
||||||
if (audioMixer) {
|
|
||||||
// update the destination socket for the AIM, in case the mixer has changed
|
|
||||||
AudioInjectionManager::setDestinationSocket(*audioMixer->getPublicSocket());
|
|
||||||
|
|
||||||
// we have an active audio mixer we can send data to
|
|
||||||
AudioInjectionManager::threadInjector(&eveAudioInjector);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// simulate the effect of pressing and un-pressing the mouse button/pad
|
|
||||||
handStateTimer++;
|
|
||||||
|
|
||||||
if (handStateTimer == ITERATIONS_BEFORE_HAND_GRAB) {
|
|
||||||
eve.setHandState(1);
|
|
||||||
} else if (handStateTimer == ITERATIONS_BEFORE_HAND_GRAB + HAND_GRAB_DURATION_ITERATIONS) {
|
|
||||||
eve.setHandState(0);
|
|
||||||
} else if (handStateTimer >= ITERATIONS_BEFORE_HAND_GRAB + HAND_GRAB_DURATION_ITERATIONS + HAND_TIMER_SLEEP_ITERATIONS) {
|
|
||||||
handStateTimer = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// sleep for the correct amount of time to have data send be consistently timed
|
|
||||||
if ((numMicrosecondsSleep = (DATA_SEND_INTERVAL_MSECS * 1000) - (usecTimestampNow() - usecTimestamp(&thisSend))) > 0) {
|
|
||||||
usleep(numMicrosecondsSleep);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// stop the receive node data thread
|
|
||||||
stopReceiveNodeDataThread = true;
|
|
||||||
pthread_join(receiveNodeDataThread, NULL);
|
|
||||||
|
|
||||||
// stop the node list's threads
|
|
||||||
nodeList->stopSilentNodeRemovalThread();
|
|
||||||
}
|
|
|
@ -58,6 +58,7 @@
|
||||||
#include <VoxelSceneStats.h>
|
#include <VoxelSceneStats.h>
|
||||||
|
|
||||||
#include "Application.h"
|
#include "Application.h"
|
||||||
|
#include "DataServerClient.h"
|
||||||
#include "LogDisplay.h"
|
#include "LogDisplay.h"
|
||||||
#include "Menu.h"
|
#include "Menu.h"
|
||||||
#include "Swatch.h"
|
#include "Swatch.h"
|
||||||
|
@ -101,6 +102,7 @@ Application::Application(int& argc, char** argv, timeval &startup_time) :
|
||||||
_voxelImporter(_window),
|
_voxelImporter(_window),
|
||||||
_wantToKillLocalVoxels(false),
|
_wantToKillLocalVoxels(false),
|
||||||
_audioScope(256, 200, true),
|
_audioScope(256, 200, true),
|
||||||
|
_profile(QString()),
|
||||||
_mouseX(0),
|
_mouseX(0),
|
||||||
_mouseY(0),
|
_mouseY(0),
|
||||||
_touchAvgX(0.0f),
|
_touchAvgX(0.0f),
|
||||||
|
@ -180,10 +182,8 @@ Application::Application(int& argc, char** argv, timeval &startup_time) :
|
||||||
|
|
||||||
_settings = new QSettings(this);
|
_settings = new QSettings(this);
|
||||||
|
|
||||||
// check if there is a saved domain server hostname
|
// call Menu getInstance static method to set up the menu
|
||||||
// this must be done now instead of with the other setting checks to allow manual override with
|
_window->setMenuBar(Menu::getInstance());
|
||||||
// --domain or --local options
|
|
||||||
NodeList::getInstance()->loadData(_settings);
|
|
||||||
|
|
||||||
// Check to see if the user passed in a command line option for loading a local
|
// Check to see if the user passed in a command line option for loading a local
|
||||||
// Voxel File.
|
// Voxel File.
|
||||||
|
@ -205,9 +205,6 @@ Application::Application(int& argc, char** argv, timeval &startup_time) :
|
||||||
NodeList::getInstance()->startSilentNodeRemovalThread();
|
NodeList::getInstance()->startSilentNodeRemovalThread();
|
||||||
|
|
||||||
_window->setCentralWidget(_glWidget);
|
_window->setCentralWidget(_glWidget);
|
||||||
|
|
||||||
// call Menu getInstance static method to set up the menu
|
|
||||||
_window->setMenuBar(Menu::getInstance());
|
|
||||||
|
|
||||||
_networkAccessManager = new QNetworkAccessManager(this);
|
_networkAccessManager = new QNetworkAccessManager(this);
|
||||||
QNetworkDiskCache* cache = new QNetworkDiskCache(_networkAccessManager);
|
QNetworkDiskCache* cache = new QNetworkDiskCache(_networkAccessManager);
|
||||||
|
@ -242,7 +239,6 @@ Application::~Application() {
|
||||||
|
|
||||||
delete _oculusProgram;
|
delete _oculusProgram;
|
||||||
delete _settings;
|
delete _settings;
|
||||||
delete _networkAccessManager;
|
|
||||||
delete _followMode;
|
delete _followMode;
|
||||||
delete _glWidget;
|
delete _glWidget;
|
||||||
}
|
}
|
||||||
|
@ -324,13 +320,22 @@ void Application::initializeGL() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Application::paintGL() {
|
void Application::paintGL() {
|
||||||
PerfStat("display");
|
PerformanceWarning::setSuppressShortTimings(Menu::getInstance()->isOptionChecked(MenuOption::SuppressShortTimings));
|
||||||
|
PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "Application::paintGL()");
|
||||||
|
|
||||||
glEnable(GL_LINE_SMOOTH);
|
glEnable(GL_LINE_SMOOTH);
|
||||||
|
|
||||||
if (_myCamera.getMode() == CAMERA_MODE_MIRROR) {
|
if (_myCamera.getMode() == CAMERA_MODE_MIRROR) {
|
||||||
_myCamera.setTightness (100.0f);
|
_myCamera.setTightness (100.0f);
|
||||||
_myCamera.setTargetPosition(_myAvatar.getUprightHeadPosition());
|
glm::vec3 targetPosition = _myAvatar.getUprightHeadPosition();
|
||||||
|
if (_myAvatar.getHead().getBlendFace().isActive()) {
|
||||||
|
// make sure we're aligned to the blend face eyes
|
||||||
|
glm::vec3 leftEyePosition, rightEyePosition;
|
||||||
|
if (_myAvatar.getHead().getBlendFace().getEyePositions(leftEyePosition, rightEyePosition, true)) {
|
||||||
|
targetPosition = (leftEyePosition + rightEyePosition) * 0.5f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_myCamera.setTargetPosition(targetPosition);
|
||||||
_myCamera.setTargetRotation(_myAvatar.getWorldAlignedOrientation() * glm::quat(glm::vec3(0.0f, PIf, 0.0f)));
|
_myCamera.setTargetRotation(_myAvatar.getWorldAlignedOrientation() * glm::quat(glm::vec3(0.0f, PIf, 0.0f)));
|
||||||
|
|
||||||
} else if (OculusManager::isConnected()) {
|
} else if (OculusManager::isConnected()) {
|
||||||
|
@ -342,7 +347,7 @@ void Application::paintGL() {
|
||||||
|
|
||||||
} else if (_myCamera.getMode() == CAMERA_MODE_FIRST_PERSON) {
|
} else if (_myCamera.getMode() == CAMERA_MODE_FIRST_PERSON) {
|
||||||
_myCamera.setTightness(0.0f); // In first person, camera follows head exactly without delay
|
_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());
|
_myCamera.setTargetRotation(_myAvatar.getHead().getCameraOrientation());
|
||||||
|
|
||||||
} else if (_myCamera.getMode() == CAMERA_MODE_THIRD_PERSON) {
|
} else if (_myCamera.getMode() == CAMERA_MODE_THIRD_PERSON) {
|
||||||
|
@ -445,6 +450,12 @@ void Application::updateProjectionMatrix() {
|
||||||
glMatrixMode(GL_MODELVIEW);
|
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,
|
void Application::controlledBroadcastToNodes(unsigned char* broadcastData, size_t dataBytes,
|
||||||
const char* nodeTypes, int numNodeTypes) {
|
const char* nodeTypes, int numNodeTypes) {
|
||||||
Application* self = getInstance();
|
Application* self = getInstance();
|
||||||
|
@ -1153,6 +1164,9 @@ void Application::timer() {
|
||||||
|
|
||||||
// ask the node list to check in with the domain server
|
// ask the node list to check in with the domain server
|
||||||
NodeList::getInstance()->sendDomainServerCheckIn();
|
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) {
|
static glm::vec3 getFaceVector(BoxFace face) {
|
||||||
|
@ -1186,6 +1200,7 @@ void Application::idle() {
|
||||||
|
|
||||||
double timeSinceLastUpdate = diffclock(&_lastTimeUpdated, &check);
|
double timeSinceLastUpdate = diffclock(&_lastTimeUpdated, &check);
|
||||||
if (timeSinceLastUpdate > IDLE_SIMULATE_MSECS) {
|
if (timeSinceLastUpdate > IDLE_SIMULATE_MSECS) {
|
||||||
|
|
||||||
const float BIGGEST_DELTA_TIME_SECS = 0.25f;
|
const float BIGGEST_DELTA_TIME_SECS = 0.25f;
|
||||||
update(glm::clamp((float)timeSinceLastUpdate / 1000.f, 0.f, BIGGEST_DELTA_TIME_SECS));
|
update(glm::clamp((float)timeSinceLastUpdate / 1000.f, 0.f, BIGGEST_DELTA_TIME_SECS));
|
||||||
_glWidget->updateGL();
|
_glWidget->updateGL();
|
||||||
|
@ -1254,13 +1269,14 @@ void Application::processAvatarURLsMessage(unsigned char* packetData, size_t dat
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
QDataStream in(QByteArray((char*)packetData, dataBytes));
|
QDataStream in(QByteArray((char*)packetData, dataBytes));
|
||||||
QUrl voxelURL, faceURL;
|
QUrl voxelURL;
|
||||||
in >> voxelURL;
|
in >> voxelURL;
|
||||||
in >> faceURL;
|
|
||||||
|
|
||||||
// invoke the set URL functions on the simulate/render thread
|
// invoke the set URL functions on the simulate/render thread
|
||||||
QMetaObject::invokeMethod(avatar->getVoxels(), "setVoxelURL", Q_ARG(QUrl, voxelURL));
|
QMetaObject::invokeMethod(avatar->getVoxels(), "setVoxelURL", Q_ARG(QUrl, voxelURL));
|
||||||
QMetaObject::invokeMethod(&avatar->getHead().getBlendFace(), "setModelURL", Q_ARG(QUrl, faceURL));
|
|
||||||
|
// use this timing to as the data-server for an updated mesh for this avatar (if we have UUID)
|
||||||
|
DataServerClient::getValueForKeyAndUUID(DataServerKey::FaceMeshURL, avatar->getUUID());
|
||||||
}
|
}
|
||||||
|
|
||||||
void Application::processAvatarFaceVideoMessage(unsigned char* packetData, size_t dataBytes) {
|
void Application::processAvatarFaceVideoMessage(unsigned char* packetData, size_t dataBytes) {
|
||||||
|
@ -1587,15 +1603,21 @@ void Application::init() {
|
||||||
_audio.setJitterBufferSamples(Menu::getInstance()->getAudioJitterBufferSamples());
|
_audio.setJitterBufferSamples(Menu::getInstance()->getAudioJitterBufferSamples());
|
||||||
}
|
}
|
||||||
qDebug("Loaded settings.\n");
|
qDebug("Loaded settings.\n");
|
||||||
|
|
||||||
|
if (!_profile.getUsername().isEmpty()) {
|
||||||
|
// we have a username for this avatar, ask the data-server for the mesh URL for this avatar
|
||||||
|
DataServerClient::getClientValueForKey(DataServerKey::FaceMeshURL);
|
||||||
|
}
|
||||||
|
|
||||||
// Set up VoxelSystem after loading preferences so we can get the desired max voxel count
|
// Set up VoxelSystem after loading preferences so we can get the desired max voxel count
|
||||||
_voxels.setMaxVoxels(Menu::getInstance()->getMaxVoxels());
|
_voxels.setMaxVoxels(Menu::getInstance()->getMaxVoxels());
|
||||||
_voxels.setUseVoxelShader(Menu::getInstance()->isOptionChecked(MenuOption::UseVoxelShader));
|
_voxels.setUseVoxelShader(Menu::getInstance()->isOptionChecked(MenuOption::UseVoxelShader));
|
||||||
|
_voxels.setVoxelsAsPoints(Menu::getInstance()->isOptionChecked(MenuOption::VoxelsAsPoints));
|
||||||
_voxels.setUseFastVoxelPipeline(Menu::getInstance()->isOptionChecked(MenuOption::FastVoxelPipeline));
|
_voxels.setUseFastVoxelPipeline(Menu::getInstance()->isOptionChecked(MenuOption::FastVoxelPipeline));
|
||||||
_voxels.init();
|
_voxels.init();
|
||||||
|
|
||||||
|
|
||||||
Avatar::sendAvatarURLsMessage(_myAvatar.getVoxels()->getVoxelURL(), _myAvatar.getHead().getBlendFace().getModelURL());
|
Avatar::sendAvatarURLsMessage(_myAvatar.getVoxels()->getVoxelURL());
|
||||||
|
|
||||||
_palette.init(_glWidget->width(), _glWidget->height());
|
_palette.init(_glWidget->width(), _glWidget->height());
|
||||||
_palette.addAction(Menu::getInstance()->getActionForOption(MenuOption::VoxelAddMode), 0, 0);
|
_palette.addAction(Menu::getInstance()->getActionForOption(MenuOption::VoxelAddMode), 0, 0);
|
||||||
|
@ -1639,9 +1661,9 @@ Avatar* Application::findLookatTargetAvatar(const glm::vec3& mouseRayOrigin, con
|
||||||
glm::vec3 headPosition = avatar->getHead().getPosition();
|
glm::vec3 headPosition = avatar->getHead().getPosition();
|
||||||
float distance;
|
float distance;
|
||||||
if (rayIntersectsSphere(mouseRayOrigin, mouseRayDirection, headPosition,
|
if (rayIntersectsSphere(mouseRayOrigin, mouseRayDirection, headPosition,
|
||||||
HEAD_SPHERE_RADIUS * avatar->getScale(), distance)) {
|
HEAD_SPHERE_RADIUS * avatar->getHead().getScale(), distance)) {
|
||||||
eyePosition = avatar->getHead().getEyePosition();
|
eyePosition = avatar->getHead().getEyePosition();
|
||||||
_lookatIndicatorScale = avatar->getScale();
|
_lookatIndicatorScale = avatar->getHead().getScale();
|
||||||
_lookatOtherPosition = headPosition;
|
_lookatOtherPosition = headPosition;
|
||||||
nodeID = avatar->getOwningNode()->getNodeID();
|
nodeID = avatar->getOwningNode()->getNodeID();
|
||||||
return avatar;
|
return avatar;
|
||||||
|
@ -1782,8 +1804,8 @@ void Application::update(float deltaTime) {
|
||||||
_faceshift.getEstimatedEyePitch(), _faceshift.getEstimatedEyeYaw(), 0.0f))) * glm::vec3(0.0f, 0.0f, -1.0f);
|
_faceshift.getEstimatedEyePitch(), _faceshift.getEstimatedEyeYaw(), 0.0f))) * glm::vec3(0.0f, 0.0f, -1.0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
updateLookatTargetAvatar(lookAtRayOrigin, lookAtRayDirection, lookAtSpot);
|
updateLookatTargetAvatar(mouseRayOrigin, mouseRayDirection, lookAtSpot);
|
||||||
if (_lookatTargetAvatar) {
|
if (_lookatTargetAvatar && !_faceshift.isActive()) {
|
||||||
// If the mouse is over another avatar's head...
|
// If the mouse is over another avatar's head...
|
||||||
_myAvatar.getHead().setLookAtPosition(lookAtSpot);
|
_myAvatar.getHead().setLookAtPosition(lookAtSpot);
|
||||||
} else if (_isHoverVoxel && !_faceshift.isActive()) {
|
} else if (_isHoverVoxel && !_faceshift.isActive()) {
|
||||||
|
@ -1975,6 +1997,11 @@ void Application::update(float deltaTime) {
|
||||||
_myAvatar.simulate(deltaTime, NULL);
|
_myAvatar.simulate(deltaTime, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Simulate particle cloud movements
|
||||||
|
if (Menu::getInstance()->isOptionChecked(MenuOption::ParticleCloud)) {
|
||||||
|
_cloud.simulate(deltaTime);
|
||||||
|
}
|
||||||
|
|
||||||
// no transmitter drive implies transmitter pick
|
// no transmitter drive implies transmitter pick
|
||||||
if (!Menu::getInstance()->isOptionChecked(MenuOption::TransmitterDrive) && _myTransmitter.isConnected()) {
|
if (!Menu::getInstance()->isOptionChecked(MenuOption::TransmitterDrive) && _myTransmitter.isConnected()) {
|
||||||
_transmitterPickStart = _myAvatar.getSkeleton().joint[AVATAR_JOINT_CHEST].position;
|
_transmitterPickStart = _myAvatar.getSkeleton().joint[AVATAR_JOINT_CHEST].position;
|
||||||
|
@ -2032,7 +2059,7 @@ void Application::update(float deltaTime) {
|
||||||
if (_faceshift.isActive()) {
|
if (_faceshift.isActive()) {
|
||||||
const float EYE_OFFSET_SCALE = 0.025f;
|
const float EYE_OFFSET_SCALE = 0.025f;
|
||||||
glm::vec3 position = _faceshift.getHeadTranslation() * EYE_OFFSET_SCALE;
|
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();
|
updateProjectionMatrix();
|
||||||
|
|
||||||
} else if (_webcam.isActive()) {
|
} else if (_webcam.isActive()) {
|
||||||
|
@ -2071,8 +2098,7 @@ void Application::updateAvatar(float deltaTime) {
|
||||||
_yawFromTouch = 0.f;
|
_yawFromTouch = 0.f;
|
||||||
|
|
||||||
// Update my avatar's state from gyros and/or webcam
|
// Update my avatar's state from gyros and/or webcam
|
||||||
_myAvatar.updateFromGyrosAndOrWebcam(Menu::getInstance()->isOptionChecked(MenuOption::GyroLook),
|
_myAvatar.updateFromGyrosAndOrWebcam(_pitchFromTouch, Menu::getInstance()->isOptionChecked(MenuOption::TurnWithHead));
|
||||||
_pitchFromTouch);
|
|
||||||
|
|
||||||
// Update head mouse from faceshift if active
|
// Update head mouse from faceshift if active
|
||||||
if (_faceshift.isActive()) {
|
if (_faceshift.isActive()) {
|
||||||
|
@ -2172,8 +2198,7 @@ void Application::updateAvatar(float deltaTime) {
|
||||||
// once in a while, send my urls
|
// once in a while, send my urls
|
||||||
const float AVATAR_URLS_SEND_INTERVAL = 1.0f; // seconds
|
const float AVATAR_URLS_SEND_INTERVAL = 1.0f; // seconds
|
||||||
if (shouldDo(AVATAR_URLS_SEND_INTERVAL, deltaTime)) {
|
if (shouldDo(AVATAR_URLS_SEND_INTERVAL, deltaTime)) {
|
||||||
Avatar::sendAvatarURLsMessage(_myAvatar.getVoxels()->getVoxelURL(),
|
Avatar::sendAvatarURLsMessage(_myAvatar.getVoxels()->getVoxelURL());
|
||||||
_myAvatar.getHead().getBlendFace().getModelURL());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2386,6 +2411,7 @@ void Application::computeOffAxisFrustum(float& left, float& right, float& bottom
|
||||||
}
|
}
|
||||||
|
|
||||||
void Application::displaySide(Camera& whichCamera) {
|
void Application::displaySide(Camera& whichCamera) {
|
||||||
|
PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "Application::displaySide()");
|
||||||
// transform by eye offset
|
// transform by eye offset
|
||||||
|
|
||||||
// flip x if in mirror mode (also requires reversing winding order for backface culling)
|
// flip x if in mirror mode (also requires reversing winding order for backface culling)
|
||||||
|
@ -2417,6 +2443,8 @@ void Application::displaySide(Camera& whichCamera) {
|
||||||
setupWorldLight(whichCamera);
|
setupWorldLight(whichCamera);
|
||||||
|
|
||||||
if (Menu::getInstance()->isOptionChecked(MenuOption::Stars)) {
|
if (Menu::getInstance()->isOptionChecked(MenuOption::Stars)) {
|
||||||
|
PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings),
|
||||||
|
"Application::displaySide() ... stars...");
|
||||||
if (!_stars.getFileLoaded()) {
|
if (!_stars.getFileLoaded()) {
|
||||||
_stars.readInput(STAR_FILE, STAR_CACHE_FILE, 0);
|
_stars.readInput(STAR_FILE, STAR_CACHE_FILE, 0);
|
||||||
}
|
}
|
||||||
|
@ -2442,6 +2470,8 @@ void Application::displaySide(Camera& whichCamera) {
|
||||||
|
|
||||||
// draw the sky dome
|
// draw the sky dome
|
||||||
if (Menu::getInstance()->isOptionChecked(MenuOption::Atmosphere)) {
|
if (Menu::getInstance()->isOptionChecked(MenuOption::Atmosphere)) {
|
||||||
|
PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings),
|
||||||
|
"Application::displaySide() ... atmosphere...");
|
||||||
_environment.renderAtmospheres(whichCamera);
|
_environment.renderAtmospheres(whichCamera);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2464,6 +2494,9 @@ void Application::displaySide(Camera& whichCamera) {
|
||||||
|
|
||||||
//draw a grid ground plane....
|
//draw a grid ground plane....
|
||||||
if (Menu::getInstance()->isOptionChecked(MenuOption::GroundPlane)) {
|
if (Menu::getInstance()->isOptionChecked(MenuOption::GroundPlane)) {
|
||||||
|
PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings),
|
||||||
|
"Application::displaySide() ... ground plane...");
|
||||||
|
|
||||||
// draw grass plane with fog
|
// draw grass plane with fog
|
||||||
glEnable(GL_FOG);
|
glEnable(GL_FOG);
|
||||||
glEnable(GL_NORMALIZE);
|
glEnable(GL_NORMALIZE);
|
||||||
|
@ -2484,17 +2517,29 @@ void Application::displaySide(Camera& whichCamera) {
|
||||||
glDisable(GL_NORMALIZE);
|
glDisable(GL_NORMALIZE);
|
||||||
|
|
||||||
//renderGroundPlaneGrid(EDGE_SIZE_GROUND_PLANE, _audio.getCollisionSoundMagnitude());
|
//renderGroundPlaneGrid(EDGE_SIZE_GROUND_PLANE, _audio.getCollisionSoundMagnitude());
|
||||||
}
|
}
|
||||||
|
// Draw Cloud Particles
|
||||||
|
if (Menu::getInstance()->isOptionChecked(MenuOption::ParticleCloud)) {
|
||||||
|
_cloud.render();
|
||||||
|
}
|
||||||
// Draw voxels
|
// Draw voxels
|
||||||
if (Menu::getInstance()->isOptionChecked(MenuOption::Voxels)) {
|
if (Menu::getInstance()->isOptionChecked(MenuOption::Voxels)) {
|
||||||
_voxels.render(Menu::getInstance()->isOptionChecked(MenuOption::VoxelTextures));
|
PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings),
|
||||||
|
"Application::displaySide() ... voxels...");
|
||||||
|
if (!Menu::getInstance()->isOptionChecked(MenuOption::DontRenderVoxels)) {
|
||||||
|
_voxels.render(Menu::getInstance()->isOptionChecked(MenuOption::VoxelTextures));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// restore default, white specular
|
// restore default, white specular
|
||||||
glMaterialfv(GL_FRONT, GL_SPECULAR, WHITE_SPECULAR_COLOR);
|
glMaterialfv(GL_FRONT, GL_SPECULAR, WHITE_SPECULAR_COLOR);
|
||||||
|
|
||||||
// indicate what we'll be adding/removing in mouse mode, if anything
|
// indicate what we'll be adding/removing in mouse mode, if anything
|
||||||
if (_mouseVoxel.s != 0) {
|
if (_mouseVoxel.s != 0) {
|
||||||
|
PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings),
|
||||||
|
"Application::displaySide() ... voxels TOOLS UX...");
|
||||||
|
|
||||||
glDisable(GL_LIGHTING);
|
glDisable(GL_LIGHTING);
|
||||||
glPushMatrix();
|
glPushMatrix();
|
||||||
glScalef(TREE_SCALE, TREE_SCALE, TREE_SCALE);
|
glScalef(TREE_SCALE, TREE_SCALE, TREE_SCALE);
|
||||||
|
@ -2540,6 +2585,9 @@ void Application::displaySide(Camera& whichCamera) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Menu::getInstance()->isOptionChecked(MenuOption::VoxelSelectMode) && _pasteMode) {
|
if (Menu::getInstance()->isOptionChecked(MenuOption::VoxelSelectMode) && _pasteMode) {
|
||||||
|
PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings),
|
||||||
|
"Application::displaySide() ... PASTE Preview...");
|
||||||
|
|
||||||
glPushMatrix();
|
glPushMatrix();
|
||||||
glTranslatef(_mouseVoxel.x * TREE_SCALE,
|
glTranslatef(_mouseVoxel.x * TREE_SCALE,
|
||||||
_mouseVoxel.y * TREE_SCALE,
|
_mouseVoxel.y * TREE_SCALE,
|
||||||
|
@ -2555,6 +2603,10 @@ void Application::displaySide(Camera& whichCamera) {
|
||||||
_myAvatar.renderScreenTint(SCREEN_TINT_BEFORE_AVATARS, whichCamera);
|
_myAvatar.renderScreenTint(SCREEN_TINT_BEFORE_AVATARS, whichCamera);
|
||||||
|
|
||||||
if (Menu::getInstance()->isOptionChecked(MenuOption::Avatars)) {
|
if (Menu::getInstance()->isOptionChecked(MenuOption::Avatars)) {
|
||||||
|
PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings),
|
||||||
|
"Application::displaySide() ... Avatars...");
|
||||||
|
|
||||||
|
|
||||||
// Render avatars of other nodes
|
// Render avatars of other nodes
|
||||||
NodeList* nodeList = NodeList::getInstance();
|
NodeList* nodeList = NodeList::getInstance();
|
||||||
|
|
||||||
|
@ -2599,16 +2651,22 @@ void Application::displaySide(Camera& whichCamera) {
|
||||||
|
|
||||||
// render the ambient occlusion effect if enabled
|
// render the ambient occlusion effect if enabled
|
||||||
if (Menu::getInstance()->isOptionChecked(MenuOption::AmbientOcclusion)) {
|
if (Menu::getInstance()->isOptionChecked(MenuOption::AmbientOcclusion)) {
|
||||||
|
PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings),
|
||||||
|
"Application::displaySide() ... AmbientOcclusion...");
|
||||||
_ambientOcclusionEffect.render();
|
_ambientOcclusionEffect.render();
|
||||||
}
|
}
|
||||||
|
|
||||||
// brad's frustum for debugging
|
// brad's frustum for debugging
|
||||||
if (Menu::getInstance()->isOptionChecked(MenuOption::DisplayFrustum)) {
|
if (Menu::getInstance()->isOptionChecked(MenuOption::DisplayFrustum)) {
|
||||||
|
PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings),
|
||||||
|
"Application::displaySide() ... renderViewFrustum...");
|
||||||
renderViewFrustum(_viewFrustum);
|
renderViewFrustum(_viewFrustum);
|
||||||
}
|
}
|
||||||
|
|
||||||
// render voxel fades if they exist
|
// render voxel fades if they exist
|
||||||
if (_voxelFades.size() > 0) {
|
if (_voxelFades.size() > 0) {
|
||||||
|
PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings),
|
||||||
|
"Application::displaySide() ... voxel fades...");
|
||||||
for(std::vector<VoxelFade>::iterator fade = _voxelFades.begin(); fade != _voxelFades.end();) {
|
for(std::vector<VoxelFade>::iterator fade = _voxelFades.begin(); fade != _voxelFades.end();) {
|
||||||
fade->render();
|
fade->render();
|
||||||
if(fade->isDone()) {
|
if(fade->isDone()) {
|
||||||
|
@ -2618,11 +2676,18 @@ void Application::displaySide(Camera& whichCamera) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
renderFollowIndicator();
|
{
|
||||||
|
PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings),
|
||||||
|
"Application::displaySide() ... renderFollowIndicator...");
|
||||||
|
renderFollowIndicator();
|
||||||
|
}
|
||||||
|
|
||||||
// render transmitter pick ray, if non-empty
|
// render transmitter pick ray, if non-empty
|
||||||
if (_transmitterPickStart != _transmitterPickEnd) {
|
if (_transmitterPickStart != _transmitterPickEnd) {
|
||||||
|
PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings),
|
||||||
|
"Application::displaySide() ... transmitter pick ray...");
|
||||||
|
|
||||||
Glower glower;
|
Glower glower;
|
||||||
const float TRANSMITTER_PICK_COLOR[] = { 1.0f, 1.0f, 0.0f };
|
const float TRANSMITTER_PICK_COLOR[] = { 1.0f, 1.0f, 0.0f };
|
||||||
glColor3fv(TRANSMITTER_PICK_COLOR);
|
glColor3fv(TRANSMITTER_PICK_COLOR);
|
||||||
|
@ -2644,6 +2709,8 @@ void Application::displaySide(Camera& whichCamera) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Application::displayOverlay() {
|
void Application::displayOverlay() {
|
||||||
|
PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "Application::displayOverlay()");
|
||||||
|
|
||||||
// Render 2D overlay: I/O level bar graphs and text
|
// Render 2D overlay: I/O level bar graphs and text
|
||||||
glMatrixMode(GL_PROJECTION);
|
glMatrixMode(GL_PROJECTION);
|
||||||
glPushMatrix();
|
glPushMatrix();
|
||||||
|
@ -3461,9 +3528,16 @@ void Application::attachNewHeadToNode(Node* newNode) {
|
||||||
void Application::domainChanged(QString domain) {
|
void Application::domainChanged(QString domain) {
|
||||||
qDebug("Application title set to: %s.\n", domain.toStdString().c_str());
|
qDebug("Application title set to: %s.\n", domain.toStdString().c_str());
|
||||||
_window->setWindowTitle(domain);
|
_window->setWindowTitle(domain);
|
||||||
|
|
||||||
|
// update the user's last domain in their Profile (which will propagate to data-server)
|
||||||
|
_profile.updateDomain(domain);
|
||||||
|
|
||||||
|
// reset the environment so that we don't erroneously end up with multiple
|
||||||
|
_environment.resetToDefault();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Application::nodeAdded(Node* node) {
|
void Application::nodeAdded(Node* node) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Application::nodeKilled(Node* node) {
|
void Application::nodeKilled(Node* node) {
|
||||||
|
@ -3574,6 +3648,12 @@ void* Application::networkReceive(void* args) {
|
||||||
case PACKET_TYPE_AVATAR_FACE_VIDEO:
|
case PACKET_TYPE_AVATAR_FACE_VIDEO:
|
||||||
processAvatarFaceVideoMessage(app->_incomingPacket, bytesReceived);
|
processAvatarFaceVideoMessage(app->_incomingPacket, bytesReceived);
|
||||||
break;
|
break;
|
||||||
|
case PACKET_TYPE_DATA_SERVER_GET:
|
||||||
|
case PACKET_TYPE_DATA_SERVER_PUT:
|
||||||
|
case PACKET_TYPE_DATA_SERVER_SEND:
|
||||||
|
case PACKET_TYPE_DATA_SERVER_CONFIRM:
|
||||||
|
DataServerClient::processMessageFromDataServer(app->_incomingPacket, bytesReceived);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
NodeList::getInstance()->processNodeData(&senderAddress, app->_incomingPacket, bytesReceived);
|
NodeList::getInstance()->processNodeData(&senderAddress, app->_incomingPacket, bytesReceived);
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -29,6 +29,7 @@
|
||||||
|
|
||||||
#include "BandwidthMeter.h"
|
#include "BandwidthMeter.h"
|
||||||
#include "Camera.h"
|
#include "Camera.h"
|
||||||
|
#include "Cloud.h"
|
||||||
#include "Environment.h"
|
#include "Environment.h"
|
||||||
#include "GLCanvas.h"
|
#include "GLCanvas.h"
|
||||||
#include "PacketHeaders.h"
|
#include "PacketHeaders.h"
|
||||||
|
@ -44,6 +45,7 @@
|
||||||
#include "VoxelImporter.h"
|
#include "VoxelImporter.h"
|
||||||
#include "avatar/Avatar.h"
|
#include "avatar/Avatar.h"
|
||||||
#include "avatar/MyAvatar.h"
|
#include "avatar/MyAvatar.h"
|
||||||
|
#include "avatar/Profile.h"
|
||||||
#include "avatar/HandControl.h"
|
#include "avatar/HandControl.h"
|
||||||
#include "devices/Faceshift.h"
|
#include "devices/Faceshift.h"
|
||||||
#include "devices/SerialInterface.h"
|
#include "devices/SerialInterface.h"
|
||||||
|
@ -133,6 +135,9 @@ public:
|
||||||
|
|
||||||
Avatar* getLookatTargetAvatar() const { return _lookatTargetAvatar; }
|
Avatar* getLookatTargetAvatar() const { return _lookatTargetAvatar; }
|
||||||
|
|
||||||
|
Profile* getProfile() { return &_profile; }
|
||||||
|
void resetProfile(const QString& username);
|
||||||
|
|
||||||
static void controlledBroadcastToNodes(unsigned char* broadcastData, size_t dataBytes,
|
static void controlledBroadcastToNodes(unsigned char* broadcastData, size_t dataBytes,
|
||||||
const char* nodeTypes, int numNodeTypes);
|
const char* nodeTypes, int numNodeTypes);
|
||||||
|
|
||||||
|
@ -261,6 +266,8 @@ private:
|
||||||
|
|
||||||
Stars _stars;
|
Stars _stars;
|
||||||
|
|
||||||
|
Cloud _cloud;
|
||||||
|
|
||||||
VoxelSystem _voxels;
|
VoxelSystem _voxels;
|
||||||
VoxelTree _clipboard; // if I copy/paste
|
VoxelTree _clipboard; // if I copy/paste
|
||||||
VoxelImporter _voxelImporter;
|
VoxelImporter _voxelImporter;
|
||||||
|
@ -275,6 +282,7 @@ private:
|
||||||
Oscilloscope _audioScope;
|
Oscilloscope _audioScope;
|
||||||
|
|
||||||
MyAvatar _myAvatar; // The rendered avatar of oneself
|
MyAvatar _myAvatar; // The rendered avatar of oneself
|
||||||
|
Profile _profile; // The data-server linked profile for this user
|
||||||
|
|
||||||
Transmitter _myTransmitter; // Gets UDP data from transmitter app used to animate the avatar
|
Transmitter _myTransmitter; // Gets UDP data from transmitter app used to animate the avatar
|
||||||
|
|
||||||
|
|
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
|
222
interface/src/DataServerClient.cpp
Normal file
222
interface/src/DataServerClient.cpp
Normal file
|
@ -0,0 +1,222 @@
|
||||||
|
//
|
||||||
|
// DataServerClient.cpp
|
||||||
|
// hifi
|
||||||
|
//
|
||||||
|
// Created by Stephen Birarda on 10/7/13.
|
||||||
|
// Copyright (c) 2013 HighFidelity, Inc. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include <QtCore/QUrl>
|
||||||
|
|
||||||
|
#include <NodeList.h>
|
||||||
|
#include <PacketHeaders.h>
|
||||||
|
#include <UDPSocket.h>
|
||||||
|
#include <UUID.h>
|
||||||
|
|
||||||
|
#include "Application.h"
|
||||||
|
#include "avatar/Profile.h"
|
||||||
|
|
||||||
|
#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 QString& key, const char* value) {
|
||||||
|
QString clientString = Application::getInstance()->getProfile()->getUserString();
|
||||||
|
if (!clientString.isEmpty()) {
|
||||||
|
|
||||||
|
unsigned char* putPacket = new unsigned char[MAX_PACKET_SIZE];
|
||||||
|
|
||||||
|
// setup the header for this packet
|
||||||
|
int numPacketBytes = populateTypeAndVersion(putPacket, PACKET_TYPE_DATA_SERVER_PUT);
|
||||||
|
|
||||||
|
// pack the client UUID, null terminated
|
||||||
|
memcpy(putPacket + numPacketBytes, clientString.toLocal8Bit().constData(), clientString.toLocal8Bit().size());
|
||||||
|
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.toLocal8Bit().constData());
|
||||||
|
numPacketBytes += key.size();
|
||||||
|
putPacket[numPacketBytes++] = '\0';
|
||||||
|
|
||||||
|
// pack the value, null terminated
|
||||||
|
strcpy((char*) putPacket + numPacketBytes, value);
|
||||||
|
numPacketBytes += strlen(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));
|
||||||
|
|
||||||
|
// send this put request to the data server
|
||||||
|
NodeList::getInstance()->getNodeSocket()->send((sockaddr*) &DATA_SERVER_SOCKET, putPacket, numPacketBytes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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()) {
|
||||||
|
getValuesForKeysAndUserString(keys, uuidStringWithoutCurlyBraces(uuid));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DataServerClient::getClientValueForKey(const QString& key) {
|
||||||
|
getValuesForKeysAndUserString(QStringList(key), Application::getInstance()->getProfile()->getUserString());
|
||||||
|
}
|
||||||
|
|
||||||
|
void DataServerClient::processConfirmFromDataServer(unsigned char* packetData, int numPacketBytes) {
|
||||||
|
removeMatchedPacketFromMap(packetData, numPacketBytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DataServerClient::processSendFromDataServer(unsigned char* packetData, int numPacketBytes) {
|
||||||
|
// pull the user string from the packet so we know who to associate this with
|
||||||
|
int numHeaderBytes = numBytesForPacketHeader(packetData);
|
||||||
|
|
||||||
|
char* userStringPosition = (char*) packetData + numHeaderBytes;
|
||||||
|
|
||||||
|
QString userString(QByteArray(userStringPosition, strlen(userStringPosition)));
|
||||||
|
|
||||||
|
QUuid userUUID(userString);
|
||||||
|
|
||||||
|
char* keysPosition = (char*) packetData + numHeaderBytes + strlen(userStringPosition)
|
||||||
|
+ sizeof('\0') + sizeof(unsigned char);
|
||||||
|
char* valuesPosition = keysPosition + strlen(keysPosition) + sizeof('\0');
|
||||||
|
|
||||||
|
QStringList keyList = QString(keysPosition).split(MULTI_KEY_VALUE_SEPARATOR);
|
||||||
|
QStringList valueList = QString(valuesPosition).split(MULTI_KEY_VALUE_SEPARATOR);
|
||||||
|
|
||||||
|
// 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 && keyList[i + 1] == DataServerKey::Position
|
||||||
|
&& valueList[i] != " " && valueList[i + 1] != " ") {
|
||||||
|
|
||||||
|
QStringList coordinateItems = valueList[i + 1].split(',');
|
||||||
|
|
||||||
|
if (coordinateItems.size() == 3) {
|
||||||
|
|
||||||
|
qDebug() << "Changing domain to" << valueList[i].toLocal8Bit().constData() <<
|
||||||
|
"and position to" << valueList[i + 1].toLocal8Bit().constData() <<
|
||||||
|
"to go to" << userString << "\n";
|
||||||
|
|
||||||
|
NodeList::getInstance()->setDomainHostname(valueList[i]);
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DataServerClient::processMessageFromDataServer(unsigned char* packetData, int numPacketBytes) {
|
||||||
|
switch (packetData[0]) {
|
||||||
|
case PACKET_TYPE_DATA_SERVER_SEND:
|
||||||
|
processSendFromDataServer(packetData, numPacketBytes);
|
||||||
|
break;
|
||||||
|
case PACKET_TYPE_DATA_SERVER_CONFIRM:
|
||||||
|
processConfirmFromDataServer(packetData, numPacketBytes);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DataServerClient::removeMatchedPacketFromMap(unsigned char* packetData, int numPacketBytes) {
|
||||||
|
for (std::map<unsigned char*, int>::iterator mapIterator = _unmatchedPackets.begin();
|
||||||
|
mapIterator != _unmatchedPackets.end();
|
||||||
|
++mapIterator) {
|
||||||
|
if (memcmp(mapIterator->first + sizeof(PACKET_TYPE),
|
||||||
|
packetData + sizeof(PACKET_TYPE),
|
||||||
|
numPacketBytes - sizeof(PACKET_TYPE)) == 0) {
|
||||||
|
|
||||||
|
// this is a match - remove the confirmed packet from the vector and delete associated member
|
||||||
|
// so it isn't sent back out
|
||||||
|
delete[] mapIterator->first;
|
||||||
|
_unmatchedPackets.erase(mapIterator);
|
||||||
|
|
||||||
|
// we've matched the packet - bail out
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DataServerClient::resendUnmatchedPackets() {
|
||||||
|
for (std::map<unsigned char*, int>::iterator mapIterator = _unmatchedPackets.begin();
|
||||||
|
mapIterator != _unmatchedPackets.end();
|
||||||
|
++mapIterator) {
|
||||||
|
// send the unmatched packet to the data server
|
||||||
|
NodeList::getInstance()->getNodeSocket()->send((sockaddr*) &DATA_SERVER_SOCKET,
|
||||||
|
mapIterator->first,
|
||||||
|
mapIterator->second);
|
||||||
|
}
|
||||||
|
}
|
45
interface/src/DataServerClient.h
Normal file
45
interface/src/DataServerClient.h
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
//
|
||||||
|
// DataServerClient.h
|
||||||
|
// hifi
|
||||||
|
//
|
||||||
|
// Created by Stephen Birarda on 10/7/13.
|
||||||
|
// Copyright (c) 2013 HighFidelity, Inc. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef __hifi__DataServerClient__
|
||||||
|
#define __hifi__DataServerClient__
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
|
||||||
|
#include <QtCore/QUuid>
|
||||||
|
|
||||||
|
#include "Application.h"
|
||||||
|
|
||||||
|
class DataServerClient {
|
||||||
|
public:
|
||||||
|
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 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) :
|
InfoView::InfoView(bool forced) :
|
||||||
_forced(forced) {
|
_forced(forced) {
|
||||||
|
|
||||||
|
setWindowFlags(Qt::WindowStaysOnTopHint | Qt::CustomizeWindowHint);
|
||||||
|
|
||||||
switchToResourcesParentIfRequired();
|
switchToResourcesParentIfRequired();
|
||||||
QString absPath = QFileInfo("resources/html/interface-welcome-allsvg.html").absoluteFilePath();
|
QString absPath = QFileInfo("resources/html/interface-welcome-allsvg.html").absoluteFilePath();
|
||||||
QUrl url = QUrl::fromLocalFile(absPath);
|
QUrl url = QUrl::fromLocalFile(absPath);
|
||||||
|
|
|
@ -19,8 +19,12 @@
|
||||||
#include <QMainWindow>
|
#include <QMainWindow>
|
||||||
#include <QSlider>
|
#include <QSlider>
|
||||||
#include <QStandardPaths>
|
#include <QStandardPaths>
|
||||||
|
#include <QUuid>
|
||||||
|
|
||||||
|
#include <UUID.h>
|
||||||
|
|
||||||
#include "Application.h"
|
#include "Application.h"
|
||||||
|
#include "DataServerClient.h"
|
||||||
#include "PairingHandler.h"
|
#include "PairingHandler.h"
|
||||||
#include "Menu.h"
|
#include "Menu.h"
|
||||||
#include "Util.h"
|
#include "Util.h"
|
||||||
|
@ -63,6 +67,12 @@ Menu::Menu() :
|
||||||
SLOT(aboutApp())))->setMenuRole(QAction::AboutRole);
|
SLOT(aboutApp())))->setMenuRole(QAction::AboutRole);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
(addActionToQMenuAndActionHash(fileMenu,
|
||||||
|
MenuOption::Login,
|
||||||
|
0,
|
||||||
|
this,
|
||||||
|
SLOT(login())));
|
||||||
|
|
||||||
(addActionToQMenuAndActionHash(fileMenu,
|
(addActionToQMenuAndActionHash(fileMenu,
|
||||||
MenuOption::Preferences,
|
MenuOption::Preferences,
|
||||||
Qt::CTRL | Qt::Key_Comma,
|
Qt::CTRL | Qt::Key_Comma,
|
||||||
|
@ -89,6 +99,11 @@ Menu::Menu() :
|
||||||
Qt::CTRL | Qt::SHIFT | Qt::Key_L,
|
Qt::CTRL | Qt::SHIFT | Qt::Key_L,
|
||||||
this,
|
this,
|
||||||
SLOT(goToLocation()));
|
SLOT(goToLocation()));
|
||||||
|
addActionToQMenuAndActionHash(fileMenu,
|
||||||
|
MenuOption::GoToUser,
|
||||||
|
Qt::CTRL | Qt::SHIFT | Qt::Key_U,
|
||||||
|
this,
|
||||||
|
SLOT(goToUser()));
|
||||||
|
|
||||||
|
|
||||||
addDisabledActionAndSeparator(fileMenu, "Settings");
|
addDisabledActionAndSeparator(fileMenu, "Settings");
|
||||||
|
@ -208,7 +223,13 @@ Menu::Menu() :
|
||||||
addCheckableActionToQMenuAndActionHash(viewMenu,
|
addCheckableActionToQMenuAndActionHash(viewMenu,
|
||||||
MenuOption::OffAxisProjection,
|
MenuOption::OffAxisProjection,
|
||||||
0,
|
0,
|
||||||
false);
|
true);
|
||||||
|
addCheckableActionToQMenuAndActionHash(viewMenu,
|
||||||
|
MenuOption::TurnWithHead,
|
||||||
|
0,
|
||||||
|
true);
|
||||||
|
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::HeadMouse, 0, false);
|
||||||
|
|
||||||
|
|
||||||
addDisabledActionAndSeparator(viewMenu, "Stats");
|
addDisabledActionAndSeparator(viewMenu, "Stats");
|
||||||
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::Stats, Qt::Key_Slash);
|
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::Stats, Qt::Key_Slash);
|
||||||
|
@ -230,6 +251,9 @@ Menu::Menu() :
|
||||||
0,
|
0,
|
||||||
appInstance->getGlowEffect(),
|
appInstance->getGlowEffect(),
|
||||||
SLOT(cycleRenderMode()));
|
SLOT(cycleRenderMode()));
|
||||||
|
|
||||||
|
addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::ParticleCloud, 0, false);
|
||||||
|
|
||||||
|
|
||||||
QMenu* voxelOptionsMenu = developerMenu->addMenu("Voxel Options");
|
QMenu* voxelOptionsMenu = developerMenu->addMenu("Voxel Options");
|
||||||
|
|
||||||
|
@ -239,12 +263,24 @@ Menu::Menu() :
|
||||||
true,
|
true,
|
||||||
appInstance,
|
appInstance,
|
||||||
SLOT(setRenderVoxels(bool)));
|
SLOT(setRenderVoxels(bool)));
|
||||||
addCheckableActionToQMenuAndActionHash(voxelOptionsMenu, MenuOption::VoxelTextures);
|
addCheckableActionToQMenuAndActionHash(voxelOptionsMenu, MenuOption::DontRenderVoxels);
|
||||||
addCheckableActionToQMenuAndActionHash(voxelOptionsMenu, MenuOption::AmbientOcclusion);
|
addCheckableActionToQMenuAndActionHash(voxelOptionsMenu, MenuOption::DontCallOpenGLForVoxels);
|
||||||
addCheckableActionToQMenuAndActionHash(voxelOptionsMenu, MenuOption::UseVoxelShader, 0,
|
|
||||||
false, this, SLOT(switchVoxelShader()));
|
_useVoxelShader = addCheckableActionToQMenuAndActionHash(voxelOptionsMenu, MenuOption::UseVoxelShader, 0,
|
||||||
|
false, appInstance->getVoxels(), SLOT(setUseVoxelShader(bool)));
|
||||||
|
|
||||||
|
addCheckableActionToQMenuAndActionHash(voxelOptionsMenu, MenuOption::VoxelsAsPoints, 0,
|
||||||
|
false, appInstance->getVoxels(), SLOT(setVoxelsAsPoints(bool)));
|
||||||
|
|
||||||
addCheckableActionToQMenuAndActionHash(voxelOptionsMenu, MenuOption::FastVoxelPipeline, 0,
|
addCheckableActionToQMenuAndActionHash(voxelOptionsMenu, MenuOption::FastVoxelPipeline, 0,
|
||||||
false, appInstance->getVoxels(), SLOT(setUseFastVoxelPipeline(bool)));
|
false, appInstance->getVoxels(), SLOT(setUseFastVoxelPipeline(bool)));
|
||||||
|
|
||||||
|
addCheckableActionToQMenuAndActionHash(voxelOptionsMenu, MenuOption::DontRemoveOutOfView);
|
||||||
|
addCheckableActionToQMenuAndActionHash(voxelOptionsMenu, MenuOption::HideOutOfView);
|
||||||
|
addCheckableActionToQMenuAndActionHash(voxelOptionsMenu, MenuOption::ConstantCulling);
|
||||||
|
addCheckableActionToQMenuAndActionHash(voxelOptionsMenu, MenuOption::AutomaticallyAuditTree);
|
||||||
|
addCheckableActionToQMenuAndActionHash(voxelOptionsMenu, MenuOption::VoxelTextures);
|
||||||
|
addCheckableActionToQMenuAndActionHash(voxelOptionsMenu, MenuOption::AmbientOcclusion);
|
||||||
|
|
||||||
|
|
||||||
QMenu* avatarOptionsMenu = developerMenu->addMenu("Avatar Options");
|
QMenu* avatarOptionsMenu = developerMenu->addMenu("Avatar Options");
|
||||||
|
@ -301,12 +337,6 @@ Menu::Menu() :
|
||||||
addCheckableActionToQMenuAndActionHash(raveGloveOptionsMenu, MenuOption::SimulateLeapHand);
|
addCheckableActionToQMenuAndActionHash(raveGloveOptionsMenu, MenuOption::SimulateLeapHand);
|
||||||
addCheckableActionToQMenuAndActionHash(raveGloveOptionsMenu, MenuOption::TestRaveGlove);
|
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");
|
QMenu* trackingOptionsMenu = developerMenu->addMenu("Tracking Options");
|
||||||
addCheckableActionToQMenuAndActionHash(trackingOptionsMenu,
|
addCheckableActionToQMenuAndActionHash(trackingOptionsMenu,
|
||||||
MenuOption::SkeletonTracking,
|
MenuOption::SkeletonTracking,
|
||||||
|
@ -346,6 +376,7 @@ Menu::Menu() :
|
||||||
|
|
||||||
QMenu* renderDebugMenu = developerMenu->addMenu("Render Debugging Tools");
|
QMenu* renderDebugMenu = developerMenu->addMenu("Render Debugging Tools");
|
||||||
addCheckableActionToQMenuAndActionHash(renderDebugMenu, MenuOption::PipelineWarnings);
|
addCheckableActionToQMenuAndActionHash(renderDebugMenu, MenuOption::PipelineWarnings);
|
||||||
|
addCheckableActionToQMenuAndActionHash(renderDebugMenu, MenuOption::SuppressShortTimings);
|
||||||
|
|
||||||
addActionToQMenuAndActionHash(renderDebugMenu,
|
addActionToQMenuAndActionHash(renderDebugMenu,
|
||||||
MenuOption::KillLocalVoxels,
|
MenuOption::KillLocalVoxels,
|
||||||
|
@ -494,6 +525,8 @@ void Menu::loadSettings(QSettings* settings) {
|
||||||
scanMenuBar(&loadAction, settings);
|
scanMenuBar(&loadAction, settings);
|
||||||
Application::getInstance()->getAvatar()->loadData(settings);
|
Application::getInstance()->getAvatar()->loadData(settings);
|
||||||
Application::getInstance()->getSwatch()->loadData(settings);
|
Application::getInstance()->getSwatch()->loadData(settings);
|
||||||
|
Application::getInstance()->getProfile()->loadData(settings);
|
||||||
|
NodeList::getInstance()->loadData(settings);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Menu::saveSettings(QSettings* settings) {
|
void Menu::saveSettings(QSettings* settings) {
|
||||||
|
@ -515,8 +548,7 @@ void Menu::saveSettings(QSettings* settings) {
|
||||||
scanMenuBar(&saveAction, settings);
|
scanMenuBar(&saveAction, settings);
|
||||||
Application::getInstance()->getAvatar()->saveData(settings);
|
Application::getInstance()->getAvatar()->saveData(settings);
|
||||||
Application::getInstance()->getSwatch()->saveData(settings);
|
Application::getInstance()->getSwatch()->saveData(settings);
|
||||||
|
Application::getInstance()->getProfile()->saveData(settings);
|
||||||
// ask the NodeList to save its data
|
|
||||||
NodeList::getInstance()->saveData(settings);
|
NodeList::getInstance()->saveData(settings);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -698,20 +730,8 @@ void updateDSHostname(const QString& domainServerHostname) {
|
||||||
newHostname = domainServerHostname;
|
newHostname = domainServerHostname;
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if the domain server hostname is new
|
// give our nodeList the new domain-server hostname
|
||||||
if (NodeList::getInstance()->getDomainHostname() != newHostname) {
|
NodeList::getInstance()->setDomainHostname(newHostname);
|
||||||
|
|
||||||
NodeList::getInstance()->clear();
|
|
||||||
|
|
||||||
// kill the local voxels
|
|
||||||
Application::getInstance()->getVoxels()->killLocalVoxels();
|
|
||||||
|
|
||||||
// reset the environment to default
|
|
||||||
Application::getInstance()->getEnvironment()->resetToDefault();
|
|
||||||
|
|
||||||
// set the new hostname
|
|
||||||
NodeList::getInstance()->setDomainHostname(newHostname);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const int QLINE_MINIMUM_WIDTH = 400;
|
const int QLINE_MINIMUM_WIDTH = 400;
|
||||||
|
@ -732,8 +752,43 @@ QLineEdit* lineEditForDomainHostname() {
|
||||||
return domainServerLineEdit;
|
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() {
|
void Menu::editPreferences() {
|
||||||
Application* applicationInstance = Application::getInstance();
|
Application* applicationInstance = Application::getInstance();
|
||||||
|
|
||||||
QDialog dialog(applicationInstance->getGLWidget());
|
QDialog dialog(applicationInstance->getGLWidget());
|
||||||
dialog.setWindowTitle("Interface Preferences");
|
dialog.setWindowTitle("Interface Preferences");
|
||||||
QBoxLayout* layout = new QBoxLayout(QBoxLayout::TopToBottom);
|
QBoxLayout* layout = new QBoxLayout(QBoxLayout::TopToBottom);
|
||||||
|
@ -746,9 +801,10 @@ void Menu::editPreferences() {
|
||||||
avatarURL->setMinimumWidth(QLINE_MINIMUM_WIDTH);
|
avatarURL->setMinimumWidth(QLINE_MINIMUM_WIDTH);
|
||||||
form->addRow("Avatar URL:", avatarURL);
|
form->addRow("Avatar URL:", avatarURL);
|
||||||
|
|
||||||
QLineEdit* faceURL = new QLineEdit(applicationInstance->getAvatar()->getHead().getBlendFace().getModelURL().toString());
|
QString faceURLString = applicationInstance->getProfile()->getFaceModelURL().toString();
|
||||||
faceURL->setMinimumWidth(QLINE_MINIMUM_WIDTH);
|
QLineEdit* faceURLEdit = new QLineEdit(faceURLString);
|
||||||
form->addRow("Face URL:", faceURL);
|
faceURLEdit->setMinimumWidth(QLINE_MINIMUM_WIDTH);
|
||||||
|
form->addRow("Face URL:", faceURLEdit);
|
||||||
|
|
||||||
QSlider* pupilDilation = new QSlider(Qt::Horizontal);
|
QSlider* pupilDilation = new QSlider(Qt::Horizontal);
|
||||||
pupilDilation->setValue(applicationInstance->getAvatar()->getHead().getPupilDilation() * pupilDilation->maximum());
|
pupilDilation->setValue(applicationInstance->getAvatar()->getHead().getPupilDilation() * pupilDilation->maximum());
|
||||||
|
@ -791,13 +847,21 @@ void Menu::editPreferences() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QUrl faceModelURL(faceURLEdit->text());
|
||||||
|
|
||||||
|
if (faceModelURL.toString() != faceURLString) {
|
||||||
|
// change the faceModelURL in the profile, it will also update this user's BlendFace
|
||||||
|
applicationInstance->getProfile()->setFaceModelURL(faceModelURL);
|
||||||
|
|
||||||
|
// send the new face mesh URL to the data-server (if we have a client UUID)
|
||||||
|
DataServerClient::putValueForKey(DataServerKey::FaceMeshURL,
|
||||||
|
faceModelURL.toString().toLocal8Bit().constData());
|
||||||
|
}
|
||||||
|
|
||||||
QUrl avatarVoxelURL(avatarURL->text());
|
QUrl avatarVoxelURL(avatarURL->text());
|
||||||
applicationInstance->getAvatar()->getVoxels()->setVoxelURL(avatarVoxelURL);
|
applicationInstance->getAvatar()->getVoxels()->setVoxelURL(avatarVoxelURL);
|
||||||
|
|
||||||
QUrl faceModelURL(faceURL->text());
|
Avatar::sendAvatarURLsMessage(avatarVoxelURL);
|
||||||
applicationInstance->getAvatar()->getHead().getBlendFace().setModelURL(faceModelURL);
|
|
||||||
|
|
||||||
Avatar::sendAvatarURLsMessage(avatarVoxelURL, faceModelURL);
|
|
||||||
|
|
||||||
applicationInstance->getAvatar()->getHead().setPupilDilation(pupilDilation->value() / (float)pupilDilation->maximum());
|
applicationInstance->getAvatar()->getHead().setPupilDilation(pupilDilation->value() / (float)pupilDilation->maximum());
|
||||||
|
|
||||||
|
@ -903,6 +967,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() {
|
void Menu::bandwidthDetails() {
|
||||||
|
|
||||||
|
@ -1003,7 +1098,4 @@ void Menu::updateFrustumRenderModeAction() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Menu::switchVoxelShader() {
|
|
||||||
Application::getInstance()->getVoxels()->setUseVoxelShader(isOptionChecked(MenuOption::UseVoxelShader));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
|
@ -54,6 +54,7 @@ public:
|
||||||
ViewFrustumOffset getViewFrustumOffset() const { return _viewFrustumOffset; }
|
ViewFrustumOffset getViewFrustumOffset() const { return _viewFrustumOffset; }
|
||||||
VoxelStatsDialog* getVoxelStatsDialog() const { return _voxelStatsDialog; }
|
VoxelStatsDialog* getVoxelStatsDialog() const { return _voxelStatsDialog; }
|
||||||
int getMaxVoxels() const { return _maxVoxels; }
|
int getMaxVoxels() const { return _maxVoxels; }
|
||||||
|
QAction* getUseVoxelShader() const { return _useVoxelShader; }
|
||||||
|
|
||||||
|
|
||||||
void handleViewFrustumOffsetKeyModifier(int key);
|
void handleViewFrustumOffsetKeyModifier(int key);
|
||||||
|
@ -68,9 +69,11 @@ public slots:
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void aboutApp();
|
void aboutApp();
|
||||||
|
void login();
|
||||||
void editPreferences();
|
void editPreferences();
|
||||||
void goToDomain();
|
void goToDomain();
|
||||||
void goToLocation();
|
void goToLocation();
|
||||||
|
void goToUser();
|
||||||
void bandwidthDetailsClosed();
|
void bandwidthDetailsClosed();
|
||||||
void voxelStatsDetailsClosed();
|
void voxelStatsDetailsClosed();
|
||||||
void cycleFrustumRenderMode();
|
void cycleFrustumRenderMode();
|
||||||
|
@ -78,7 +81,6 @@ private slots:
|
||||||
void chooseVoxelPaintColor();
|
void chooseVoxelPaintColor();
|
||||||
void runTests();
|
void runTests();
|
||||||
void resetSwatchColors();
|
void resetSwatchColors();
|
||||||
void switchVoxelShader();
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static Menu* _instance;
|
static Menu* _instance;
|
||||||
|
@ -116,18 +118,20 @@ private:
|
||||||
QActionGroup* _voxelModeActionsGroup;
|
QActionGroup* _voxelModeActionsGroup;
|
||||||
VoxelStatsDialog* _voxelStatsDialog;
|
VoxelStatsDialog* _voxelStatsDialog;
|
||||||
int _maxVoxels;
|
int _maxVoxels;
|
||||||
|
QAction* _useVoxelShader;
|
||||||
};
|
};
|
||||||
|
|
||||||
namespace MenuOption {
|
namespace MenuOption {
|
||||||
|
|
||||||
const QString AboutApp = "About Interface";
|
const QString AboutApp = "About Interface";
|
||||||
const QString AmbientOcclusion = "Ambient Occlusion";
|
const QString AmbientOcclusion = "Ambient Occlusion";
|
||||||
const QString Avatars = "Avatars";
|
const QString Avatars = "Avatars";
|
||||||
const QString AvatarAsBalls = "Avatar as Balls";
|
const QString AvatarAsBalls = "Avatar as Balls";
|
||||||
const QString Atmosphere = "Atmosphere";
|
const QString Atmosphere = "Atmosphere";
|
||||||
|
const QString AutomaticallyAuditTree = "Automatically Audit Tree Stats";
|
||||||
const QString Bandwidth = "Bandwidth Display";
|
const QString Bandwidth = "Bandwidth Display";
|
||||||
const QString BandwidthDetails = "Bandwidth Details";
|
const QString BandwidthDetails = "Bandwidth Details";
|
||||||
const QString Collisions = "Collisions";
|
const QString Collisions = "Collisions";
|
||||||
|
const QString ConstantCulling = "Constant Culling";
|
||||||
const QString CopyVoxels = "Copy";
|
const QString CopyVoxels = "Copy";
|
||||||
const QString CoverageMap = "Render Coverage Map";
|
const QString CoverageMap = "Render Coverage Map";
|
||||||
const QString CoverageMapV2 = "Render Coverage Map V2";
|
const QString CoverageMapV2 = "Render Coverage Map V2";
|
||||||
|
@ -138,6 +142,9 @@ namespace MenuOption {
|
||||||
const QString DestructiveAddVoxel = "Create Voxel is Destructive";
|
const QString DestructiveAddVoxel = "Create Voxel is Destructive";
|
||||||
const QString DeltaSending = "Delta Sending";
|
const QString DeltaSending = "Delta Sending";
|
||||||
const QString DisplayFrustum = "Display Frustum";
|
const QString DisplayFrustum = "Display Frustum";
|
||||||
|
const QString DontRenderVoxels = "Don't call _voxels.render()";
|
||||||
|
const QString DontCallOpenGLForVoxels = "Don't call glDrawElements()/glDrawRangeElementsEXT() for Voxels";
|
||||||
|
const QString DontRemoveOutOfView = "Don't Remove Out of View Voxels";
|
||||||
const QString EchoAudio = "Echo Audio";
|
const QString EchoAudio = "Echo Audio";
|
||||||
const QString ExportVoxels = "Export Voxels";
|
const QString ExportVoxels = "Export Voxels";
|
||||||
const QString HeadMouse = "Head Mouse";
|
const QString HeadMouse = "Head Mouse";
|
||||||
|
@ -158,6 +165,8 @@ namespace MenuOption {
|
||||||
const QString GlowMode = "Cycle Glow Mode";
|
const QString GlowMode = "Cycle Glow Mode";
|
||||||
const QString GoToDomain = "Go To Domain...";
|
const QString GoToDomain = "Go To Domain...";
|
||||||
const QString GoToLocation = "Go To Location...";
|
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 ImportVoxels = "Import Voxels";
|
||||||
const QString ImportVoxelsClipboard = "Import Voxels to Clipboard";
|
const QString ImportVoxelsClipboard = "Import Voxels to Clipboard";
|
||||||
const QString IncreaseAvatarSize = "Increase Avatar Size";
|
const QString IncreaseAvatarSize = "Increase Avatar Size";
|
||||||
|
@ -166,11 +175,12 @@ namespace MenuOption {
|
||||||
const QString GoHome = "Go Home";
|
const QString GoHome = "Go Home";
|
||||||
const QString Gravity = "Use Gravity";
|
const QString Gravity = "Use Gravity";
|
||||||
const QString GroundPlane = "Ground Plane";
|
const QString GroundPlane = "Ground Plane";
|
||||||
const QString GyroLook = "Smooth Gyro Look";
|
const QString ParticleCloud = "Particle Cloud";
|
||||||
const QString ListenModeNormal = "Listen Mode Normal";
|
const QString ListenModeNormal = "Listen Mode Normal";
|
||||||
const QString ListenModePoint = "Listen Mode Point";
|
const QString ListenModePoint = "Listen Mode Point";
|
||||||
const QString ListenModeSingleSource = "Listen Mode Single Source";
|
const QString ListenModeSingleSource = "Listen Mode Single Source";
|
||||||
const QString Log = "Log";
|
const QString Log = "Log";
|
||||||
|
const QString Login = "Login";
|
||||||
const QString LookAtIndicator = "Look-at Indicator";
|
const QString LookAtIndicator = "Look-at Indicator";
|
||||||
const QString LookAtVectors = "Look-at Vectors";
|
const QString LookAtVectors = "Look-at Vectors";
|
||||||
const QString LowRes = "Lower Resolution While Moving";
|
const QString LowRes = "Lower Resolution While Moving";
|
||||||
|
@ -178,6 +188,7 @@ namespace MenuOption {
|
||||||
const QString NudgeVoxels = "Nudge";
|
const QString NudgeVoxels = "Nudge";
|
||||||
const QString OcclusionCulling = "Occlusion Culling";
|
const QString OcclusionCulling = "Occlusion Culling";
|
||||||
const QString OffAxisProjection = "Off-Axis Projection";
|
const QString OffAxisProjection = "Off-Axis Projection";
|
||||||
|
const QString TurnWithHead = "Turn using Head";
|
||||||
const QString Oscilloscope = "Audio Oscilloscope";
|
const QString Oscilloscope = "Audio Oscilloscope";
|
||||||
const QString Pair = "Pair";
|
const QString Pair = "Pair";
|
||||||
const QString PasteVoxels = "Paste";
|
const QString PasteVoxels = "Paste";
|
||||||
|
@ -193,6 +204,7 @@ namespace MenuOption {
|
||||||
const QString ShowTrueColors = "Show TRUE Colors";
|
const QString ShowTrueColors = "Show TRUE Colors";
|
||||||
const QString SimulateLeapHand = "Simulate Leap Hand";
|
const QString SimulateLeapHand = "Simulate Leap Hand";
|
||||||
const QString SkeletonTracking = "Skeleton Tracking";
|
const QString SkeletonTracking = "Skeleton Tracking";
|
||||||
|
const QString SuppressShortTimings = "Suppress Timings Less than 10ms";
|
||||||
const QString LEDTracking = "LED Tracking";
|
const QString LEDTracking = "LED Tracking";
|
||||||
const QString Stars = "Stars";
|
const QString Stars = "Stars";
|
||||||
const QString Stats = "Stats";
|
const QString Stats = "Stats";
|
||||||
|
@ -203,8 +215,7 @@ namespace MenuOption {
|
||||||
const QString UsePerlinFace = "Use Perlin's Face";
|
const QString UsePerlinFace = "Use Perlin's Face";
|
||||||
const QString Quit = "Quit";
|
const QString Quit = "Quit";
|
||||||
const QString UseVoxelShader = "Use Voxel Shader";
|
const QString UseVoxelShader = "Use Voxel Shader";
|
||||||
const QString UseByteNormals = "Use Byte Normals";
|
const QString VoxelsAsPoints = "Draw Voxels as Points";
|
||||||
const QString UseGlobalNormals = "Use Global Normals";
|
|
||||||
const QString Voxels = "Voxels";
|
const QString Voxels = "Voxels";
|
||||||
const QString VoxelAddMode = "Add Voxel Mode";
|
const QString VoxelAddMode = "Add Voxel Mode";
|
||||||
const QString VoxelColorMode = "Color Voxel Mode";
|
const QString VoxelColorMode = "Color Voxel Mode";
|
||||||
|
|
|
@ -47,15 +47,14 @@ void VoxelPacketProcessor::processPacket(sockaddr& senderAddress, unsigned char*
|
||||||
if (Menu::getInstance()->isOptionChecked(MenuOption::Voxels)) {
|
if (Menu::getInstance()->isOptionChecked(MenuOption::Voxels)) {
|
||||||
Node* voxelServer = NodeList::getInstance()->nodeWithAddress(&senderAddress);
|
Node* voxelServer = NodeList::getInstance()->nodeWithAddress(&senderAddress);
|
||||||
if (voxelServer && socketMatch(voxelServer->getActiveSocket(), &senderAddress)) {
|
if (voxelServer && socketMatch(voxelServer->getActiveSocket(), &senderAddress)) {
|
||||||
voxelServer->lock();
|
int nodeID = voxelServer->getNodeID();
|
||||||
if (packetData[0] == PACKET_TYPE_ENVIRONMENT_DATA) {
|
if (packetData[0] == PACKET_TYPE_ENVIRONMENT_DATA) {
|
||||||
app->_environment.parseData(&senderAddress, packetData, messageLength);
|
app->_environment.parseData(&senderAddress, packetData, messageLength);
|
||||||
} else {
|
} else {
|
||||||
app->_voxels.setDataSourceID(voxelServer->getNodeID());
|
app->_voxels.setDataSourceID(nodeID);
|
||||||
app->_voxels.parseData(packetData, messageLength);
|
app->_voxels.parseData(packetData, messageLength);
|
||||||
app->_voxels.setDataSourceID(UNKNOWN_NODE_ID);
|
app->_voxels.setDataSourceID(UNKNOWN_NODE_ID);
|
||||||
}
|
}
|
||||||
voxelServer->unlock();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -65,8 +65,6 @@ public:
|
||||||
bool readFromSquareARGB32Pixels(const char* filename);
|
bool readFromSquareARGB32Pixels(const char* filename);
|
||||||
bool readFromSchematicFile(const char* filename);
|
bool readFromSchematicFile(const char* filename);
|
||||||
|
|
||||||
void setUseVoxelShader(bool useVoxelShader);
|
|
||||||
|
|
||||||
void setMaxVoxels(int maxVoxels);
|
void setMaxVoxels(int maxVoxels);
|
||||||
long int getMaxVoxels() const { return _maxVoxels; }
|
long int getMaxVoxels() const { return _maxVoxels; }
|
||||||
unsigned long getVoxelMemoryUsageRAM() const { return _memoryUsageRAM; }
|
unsigned long getVoxelMemoryUsageRAM() const { return _memoryUsageRAM; }
|
||||||
|
@ -83,6 +81,7 @@ public:
|
||||||
void killLocalVoxels();
|
void killLocalVoxels();
|
||||||
|
|
||||||
virtual void removeOutOfView();
|
virtual void removeOutOfView();
|
||||||
|
virtual void hideOutOfView();
|
||||||
bool hasViewChanged();
|
bool hasViewChanged();
|
||||||
bool isViewChanging();
|
bool isViewChanging();
|
||||||
|
|
||||||
|
@ -137,6 +136,8 @@ public slots:
|
||||||
void cancelImport();
|
void cancelImport();
|
||||||
|
|
||||||
void setUseFastVoxelPipeline(bool useFastVoxelPipeline);
|
void setUseFastVoxelPipeline(bool useFastVoxelPipeline);
|
||||||
|
void setUseVoxelShader(bool useVoxelShader);
|
||||||
|
void setVoxelsAsPoints(bool voxelsAsPoints);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
float _treeScale;
|
float _treeScale;
|
||||||
|
@ -144,13 +145,14 @@ protected:
|
||||||
VoxelTree* _tree;
|
VoxelTree* _tree;
|
||||||
|
|
||||||
void setupNewVoxelsForDrawing();
|
void setupNewVoxelsForDrawing();
|
||||||
void setupNewVoxelsForDrawingSingleNode();
|
static const bool DONT_BAIL_EARLY; // by default we will bail early, if you want to force not bailing, then use this
|
||||||
|
void setupNewVoxelsForDrawingSingleNode(bool allowBailEarly = true);
|
||||||
void checkForCulling();
|
void checkForCulling();
|
||||||
|
|
||||||
glm::vec3 computeVoxelVertex(const glm::vec3& startVertex, float voxelScale, int index) const;
|
glm::vec3 computeVoxelVertex(const glm::vec3& startVertex, float voxelScale, int index) const;
|
||||||
|
|
||||||
|
|
||||||
virtual void updateNodeInArrays(glBufferIndex nodeIndex, const glm::vec3& startVertex,
|
virtual void updateArraysDetails(glBufferIndex nodeIndex, const glm::vec3& startVertex,
|
||||||
float voxelScale, const nodeColor& color);
|
float voxelScale, const nodeColor& color);
|
||||||
virtual void copyWrittenDataSegmentToReadArrays(glBufferIndex segmentStart, glBufferIndex segmentEnd);
|
virtual void copyWrittenDataSegmentToReadArrays(glBufferIndex segmentStart, glBufferIndex segmentEnd);
|
||||||
virtual void updateVBOSegment(glBufferIndex segmentStart, glBufferIndex segmentEnd);
|
virtual void updateVBOSegment(glBufferIndex segmentStart, glBufferIndex segmentEnd);
|
||||||
|
@ -184,9 +186,13 @@ private:
|
||||||
static bool killSourceVoxelsOperation(VoxelNode* node, void* extraData);
|
static bool killSourceVoxelsOperation(VoxelNode* node, void* extraData);
|
||||||
static bool forceRedrawEntireTreeOperation(VoxelNode* node, void* extraData);
|
static bool forceRedrawEntireTreeOperation(VoxelNode* node, void* extraData);
|
||||||
static bool clearAllNodesBufferIndexOperation(VoxelNode* node, void* extraData);
|
static bool clearAllNodesBufferIndexOperation(VoxelNode* node, void* extraData);
|
||||||
|
static bool hideOutOfViewOperation(VoxelNode* node, void* extraData);
|
||||||
|
static bool hideOutOfViewUnrollOperation(VoxelNode* node, void* extraData);
|
||||||
|
static bool hideAllSubTreeOperation(VoxelNode* node, void* extraData);
|
||||||
|
static bool showAllSubTreeOperation(VoxelNode* node, void* extraData);
|
||||||
|
|
||||||
int updateNodeInArraysAsFullVBO(VoxelNode* node);
|
int updateNodeInArrays(VoxelNode* node, bool reuseIndex, bool forceDraw);
|
||||||
int updateNodeInArraysAsPartialVBO(VoxelNode* node);
|
int forceRemoveNodeFromArrays(VoxelNode* node);
|
||||||
|
|
||||||
void copyWrittenDataToReadArraysFullVBOs();
|
void copyWrittenDataToReadArraysFullVBOs();
|
||||||
void copyWrittenDataToReadArraysPartialVBOs();
|
void copyWrittenDataToReadArraysPartialVBOs();
|
||||||
|
@ -216,12 +222,16 @@ private:
|
||||||
int _setupNewVoxelsForDrawingLastElapsed;
|
int _setupNewVoxelsForDrawingLastElapsed;
|
||||||
uint64_t _setupNewVoxelsForDrawingLastFinished;
|
uint64_t _setupNewVoxelsForDrawingLastFinished;
|
||||||
uint64_t _lastViewCulling;
|
uint64_t _lastViewCulling;
|
||||||
|
uint64_t _lastAudit;
|
||||||
int _lastViewCullingElapsed;
|
int _lastViewCullingElapsed;
|
||||||
|
|
||||||
void initVoxelMemory();
|
void initVoxelMemory();
|
||||||
void cleanupVoxelMemory();
|
void cleanupVoxelMemory();
|
||||||
|
|
||||||
bool _useVoxelShader;
|
bool _useVoxelShader;
|
||||||
|
bool _voxelsAsPoints;
|
||||||
|
bool _voxelShaderModeWhenVoxelsAsPointsEnabled;
|
||||||
|
|
||||||
GLuint _vboVoxelsID; /// when using voxel shader, we'll use this VBO
|
GLuint _vboVoxelsID; /// when using voxel shader, we'll use this VBO
|
||||||
GLuint _vboVoxelsIndicesID; /// when using voxel shader, we'll use this VBO for our indexes
|
GLuint _vboVoxelsIndicesID; /// when using voxel shader, we'll use this VBO for our indexes
|
||||||
VoxelShaderVBOData* _writeVoxelShaderData;
|
VoxelShaderVBOData* _writeVoxelShaderData;
|
||||||
|
@ -260,6 +270,7 @@ private:
|
||||||
|
|
||||||
int _hookID;
|
int _hookID;
|
||||||
std::vector<glBufferIndex> _freeIndexes;
|
std::vector<glBufferIndex> _freeIndexes;
|
||||||
|
pthread_mutex_t _freeIndexLock;
|
||||||
|
|
||||||
void freeBufferIndex(glBufferIndex index);
|
void freeBufferIndex(glBufferIndex index);
|
||||||
void clearFreeBufferIndexes();
|
void clearFreeBufferIndexes();
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
|
|
||||||
#include "Application.h"
|
#include "Application.h"
|
||||||
#include "Avatar.h"
|
#include "Avatar.h"
|
||||||
|
#include "DataServerClient.h"
|
||||||
#include "Hand.h"
|
#include "Hand.h"
|
||||||
#include "Head.h"
|
#include "Head.h"
|
||||||
#include "Physics.h"
|
#include "Physics.h"
|
||||||
|
@ -59,7 +60,7 @@ const int NUM_BODY_CONE_SIDES = 9;
|
||||||
const float chatMessageScale = 0.0015;
|
const float chatMessageScale = 0.0015;
|
||||||
const float chatMessageHeight = 0.20;
|
const float chatMessageHeight = 0.20;
|
||||||
|
|
||||||
void Avatar::sendAvatarURLsMessage(const QUrl& voxelURL, const QUrl& faceURL) {
|
void Avatar::sendAvatarURLsMessage(const QUrl& voxelURL) {
|
||||||
uint16_t ownerID = NodeList::getInstance()->getOwnerID();
|
uint16_t ownerID = NodeList::getInstance()->getOwnerID();
|
||||||
|
|
||||||
if (ownerID == UNKNOWN_NODE_ID) {
|
if (ownerID == UNKNOWN_NODE_ID) {
|
||||||
|
@ -76,7 +77,6 @@ void Avatar::sendAvatarURLsMessage(const QUrl& voxelURL, const QUrl& faceURL) {
|
||||||
|
|
||||||
QDataStream out(&message, QIODevice::WriteOnly | QIODevice::Append);
|
QDataStream out(&message, QIODevice::WriteOnly | QIODevice::Append);
|
||||||
out << voxelURL;
|
out << voxelURL;
|
||||||
out << faceURL;
|
|
||||||
|
|
||||||
Application::controlledBroadcastToNodes((unsigned char*)message.data(), message.size(), &NODE_TYPE_AVATAR_MIXER, 1);
|
Application::controlledBroadcastToNodes((unsigned char*)message.data(), message.size(), &NODE_TYPE_AVATAR_MIXER, 1);
|
||||||
}
|
}
|
||||||
|
@ -102,11 +102,16 @@ Avatar::Avatar(Node* owningNode) :
|
||||||
_leadingAvatar(NULL),
|
_leadingAvatar(NULL),
|
||||||
_voxels(this),
|
_voxels(this),
|
||||||
_moving(false),
|
_moving(false),
|
||||||
|
_hoverOnDuration(0.0f),
|
||||||
|
_hoverOffDuration(0.0f),
|
||||||
_initialized(false),
|
_initialized(false),
|
||||||
_handHoldingPosition(0.0f, 0.0f, 0.0f),
|
_handHoldingPosition(0.0f, 0.0f, 0.0f),
|
||||||
_maxArmLength(0.0f),
|
_maxArmLength(0.0f),
|
||||||
_pelvisStandingHeight(0.0f)
|
_pelvisStandingHeight(0.0f)
|
||||||
{
|
{
|
||||||
|
// we may have been created in the network thread, but we live in the main thread
|
||||||
|
moveToThread(Application::getInstance()->thread());
|
||||||
|
|
||||||
// give the pointer to our head to inherited _headData variable from AvatarData
|
// give the pointer to our head to inherited _headData variable from AvatarData
|
||||||
_headData = &_head;
|
_headData = &_head;
|
||||||
_handData = &_hand;
|
_handData = &_hand;
|
||||||
|
@ -388,15 +393,27 @@ void Avatar::simulate(float deltaTime, Transmitter* transmitter) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// head scale grows when avatar is looked at
|
// head scale grows when avatar is looked at
|
||||||
|
const float BASE_MAX_SCALE = 3.0f;
|
||||||
|
float maxScale = BASE_MAX_SCALE * glm::distance(_position, Application::getInstance()->getCamera()->getPosition());
|
||||||
if (Application::getInstance()->getLookatTargetAvatar() == this) {
|
if (Application::getInstance()->getLookatTargetAvatar() == this) {
|
||||||
const float BASE_MAX_SCALE = 3.0f;
|
_hoverOnDuration += deltaTime;
|
||||||
const float GROW_SPEED = 0.1f;
|
_hoverOffDuration = 0.0f;
|
||||||
_head.setScale(min(BASE_MAX_SCALE * glm::distance(_position, Application::getInstance()->getCamera()->getPosition()),
|
|
||||||
_head.getScale() + deltaTime * GROW_SPEED));
|
const float GROW_DELAY = 1.0f;
|
||||||
|
const float GROW_RATE = 0.25f;
|
||||||
|
if (_hoverOnDuration > GROW_DELAY) {
|
||||||
|
_head.setScale(glm::mix(_head.getScale(), maxScale, GROW_RATE));
|
||||||
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
const float SHRINK_SPEED = 100.0f;
|
_hoverOnDuration = 0.0f;
|
||||||
_head.setScale(max(_scale, _head.getScale() - deltaTime * SHRINK_SPEED));
|
_hoverOffDuration += deltaTime;
|
||||||
|
|
||||||
|
const float SHRINK_DELAY = 1.0f;
|
||||||
|
const float SHRINK_RATE = 0.25f;
|
||||||
|
if (_hoverOffDuration > SHRINK_DELAY) {
|
||||||
|
_head.setScale(glm::mix(_head.getScale(), 1.0f, SHRINK_RATE));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_head.setBodyRotation(glm::vec3(_bodyPitch, _bodyYaw, _bodyRoll));
|
_head.setBodyRotation(glm::vec3(_bodyPitch, _bodyYaw, _bodyRoll));
|
||||||
|
@ -465,15 +482,16 @@ void Avatar::render(bool lookingInMirror, bool renderAvatarBalls) {
|
||||||
renderDiskShadow(_position, glm::vec3(0.0f, 1.0f, 0.0f), _scale * 0.1f, 0.2f);
|
renderDiskShadow(_position, glm::vec3(0.0f, 1.0f, 0.0f), _scale * 0.1f, 0.2f);
|
||||||
|
|
||||||
{
|
{
|
||||||
// glow when moving
|
// glow when moving in the distance
|
||||||
Glower glower(_moving ? 1.0f : 0.0f);
|
glm::vec3 toTarget = _position - Application::getInstance()->getAvatar()->getPosition();
|
||||||
|
const float GLOW_DISTANCE = 5.0f;
|
||||||
|
Glower glower(_moving && glm::length(toTarget) > GLOW_DISTANCE ? 1.0f : 0.0f);
|
||||||
|
|
||||||
// render body
|
// render body
|
||||||
renderBody(lookingInMirror, renderAvatarBalls);
|
renderBody(lookingInMirror, renderAvatarBalls);
|
||||||
|
|
||||||
// render sphere when far away
|
// render sphere when far away
|
||||||
const float MAX_ANGLE = 10.f;
|
const float MAX_ANGLE = 10.f;
|
||||||
glm::vec3 toTarget = _position - Application::getInstance()->getAvatar()->getPosition();
|
|
||||||
glm::vec3 delta = _height * (_head.getCameraOrientation() * IDENTITY_UP) / 2.f;
|
glm::vec3 delta = _height * (_head.getCameraOrientation() * IDENTITY_UP) / 2.f;
|
||||||
float angle = abs(angleBetween(toTarget + delta, toTarget - delta));
|
float angle = abs(angleBetween(toTarget + delta, toTarget - delta));
|
||||||
|
|
||||||
|
@ -688,6 +706,13 @@ void Avatar::renderBody(bool lookingInMirror, bool renderAvatarBalls) {
|
||||||
}
|
}
|
||||||
} else if (renderAvatarBalls || !_voxels.getVoxelURL().isValid()) {
|
} else if (renderAvatarBalls || !_voxels.getVoxelURL().isValid()) {
|
||||||
// Render the body as balls and cones
|
// 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++) {
|
for (int b = 0; b < NUM_AVATAR_BODY_BALLS; b++) {
|
||||||
float alpha = getBallRenderAlpha(b, lookingInMirror);
|
float alpha = getBallRenderAlpha(b, lookingInMirror);
|
||||||
|
|
||||||
|
@ -705,9 +730,9 @@ void Avatar::renderBody(bool lookingInMirror, bool renderAvatarBalls) {
|
||||||
}
|
}
|
||||||
} else if (alpha > 0.0f) {
|
} else if (alpha > 0.0f) {
|
||||||
// Render the body ball sphere
|
// Render the body ball sphere
|
||||||
glColor3f(SKIN_COLOR[0] + _bodyBall[b].touchForce * 0.3f,
|
glColor3f(skinColor.r + _bodyBall[b].touchForce * 0.3f,
|
||||||
SKIN_COLOR[1] - _bodyBall[b].touchForce * 0.2f,
|
skinColor.g - _bodyBall[b].touchForce * 0.2f,
|
||||||
SKIN_COLOR[2] - _bodyBall[b].touchForce * 0.1f);
|
skinColor.b - _bodyBall[b].touchForce * 0.1f);
|
||||||
|
|
||||||
if (b == BODY_BALL_NECK_BASE && _head.getBlendFace().isActive()) {
|
if (b == BODY_BALL_NECK_BASE && _head.getBlendFace().isActive()) {
|
||||||
continue; // don't render the neck if we have a face model
|
continue; // don't render the neck if we have a face model
|
||||||
|
@ -732,7 +757,7 @@ void Avatar::renderBody(bool lookingInMirror, bool renderAvatarBalls) {
|
||||||
&& (b != BODY_BALL_LEFT_SHOULDER)
|
&& (b != BODY_BALL_LEFT_SHOULDER)
|
||||||
&& (b != BODY_BALL_RIGHT_COLLAR)
|
&& (b != BODY_BALL_RIGHT_COLLAR)
|
||||||
&& (b != BODY_BALL_RIGHT_SHOULDER)) {
|
&& (b != BODY_BALL_RIGHT_SHOULDER)) {
|
||||||
glColor3fv(DARK_SKIN_COLOR);
|
glColor3fv((const GLfloat*)&darkSkinColor);
|
||||||
|
|
||||||
float r2 = _bodyBall[b].radius * 0.8;
|
float r2 = _bodyBall[b].radius * 0.8;
|
||||||
renderJointConnectingCone(_bodyBall[_bodyBall[b].parentBall].position, _bodyBall[b].position, r2, r2);
|
renderJointConnectingCone(_bodyBall[_bodyBall[b].parentBall].position, _bodyBall[b].position, r2, r2);
|
||||||
|
@ -751,31 +776,6 @@ void Avatar::renderBody(bool lookingInMirror, bool renderAvatarBalls) {
|
||||||
_hand.render(lookingInMirror);
|
_hand.render(lookingInMirror);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void Avatar::loadData(QSettings* settings) {
|
|
||||||
settings->beginGroup("Avatar");
|
|
||||||
|
|
||||||
// in case settings is corrupt or missing loadSetting() will check for NaN
|
|
||||||
_bodyYaw = loadSetting(settings, "bodyYaw", 0.0f);
|
|
||||||
_bodyPitch = loadSetting(settings, "bodyPitch", 0.0f);
|
|
||||||
_bodyRoll = loadSetting(settings, "bodyRoll", 0.0f);
|
|
||||||
_position.x = loadSetting(settings, "position_x", 0.0f);
|
|
||||||
_position.y = loadSetting(settings, "position_y", 0.0f);
|
|
||||||
_position.z = loadSetting(settings, "position_z", 0.0f);
|
|
||||||
|
|
||||||
_voxels.setVoxelURL(settings->value("voxelURL").toUrl());
|
|
||||||
_head.getBlendFace().setModelURL(settings->value("faceModelURL").toUrl());
|
|
||||||
_head.setPupilDilation(settings->value("pupilDilation", 0.0f).toFloat());
|
|
||||||
|
|
||||||
_leanScale = loadSetting(settings, "leanScale", 0.05f);
|
|
||||||
|
|
||||||
_newScale = loadSetting(settings, "scale", 1.0f);
|
|
||||||
setScale(_scale);
|
|
||||||
Application::getInstance()->getCamera()->setScale(_scale);
|
|
||||||
|
|
||||||
settings->endGroup();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Avatar::getBodyBallTransform(AvatarJointID jointID, glm::vec3& position, glm::quat& rotation) const {
|
void Avatar::getBodyBallTransform(AvatarJointID jointID, glm::vec3& position, glm::quat& rotation) const {
|
||||||
position = _bodyBall[jointID].position;
|
position = _bodyBall[jointID].position;
|
||||||
rotation = _bodyBall[jointID].rotation;
|
rotation = _bodyBall[jointID].rotation;
|
||||||
|
@ -805,27 +805,6 @@ int Avatar::parseData(unsigned char* sourceBuffer, int numBytes) {
|
||||||
return bytesRead;
|
return bytesRead;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Avatar::saveData(QSettings* set) {
|
|
||||||
set->beginGroup("Avatar");
|
|
||||||
|
|
||||||
set->setValue("bodyYaw", _bodyYaw);
|
|
||||||
set->setValue("bodyPitch", _bodyPitch);
|
|
||||||
set->setValue("bodyRoll", _bodyRoll);
|
|
||||||
|
|
||||||
set->setValue("position_x", _position.x);
|
|
||||||
set->setValue("position_y", _position.y);
|
|
||||||
set->setValue("position_z", _position.z);
|
|
||||||
|
|
||||||
set->setValue("voxelURL", _voxels.getVoxelURL());
|
|
||||||
set->setValue("faceModelURL", _head.getBlendFace().getModelURL());
|
|
||||||
set->setValue("pupilDilation", _head.getPupilDilation());
|
|
||||||
|
|
||||||
set->setValue("leanScale", _leanScale);
|
|
||||||
set->setValue("scale", _newScale);
|
|
||||||
|
|
||||||
set->endGroup();
|
|
||||||
}
|
|
||||||
|
|
||||||
// render a makeshift cone section that serves as a body part connecting joint spheres
|
// render a makeshift cone section that serves as a body part connecting joint spheres
|
||||||
void Avatar::renderJointConnectingCone(glm::vec3 position1, glm::vec3 position2, float radius1, float radius2) {
|
void Avatar::renderJointConnectingCone(glm::vec3 position1, glm::vec3 position2, float radius1, float radius2) {
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,7 @@
|
||||||
#include <glm/glm.hpp>
|
#include <glm/glm.hpp>
|
||||||
#include <glm/gtc/quaternion.hpp>
|
#include <glm/gtc/quaternion.hpp>
|
||||||
|
|
||||||
#include <QSettings>
|
#include <QtCore/QUuid>
|
||||||
|
|
||||||
#include <AvatarData.h>
|
#include <AvatarData.h>
|
||||||
|
|
||||||
|
@ -129,7 +129,7 @@ class Avatar : public AvatarData {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
static void sendAvatarURLsMessage(const QUrl& voxelURL, const QUrl& faceURL);
|
static void sendAvatarURLsMessage(const QUrl& voxelURL);
|
||||||
|
|
||||||
Avatar(Node* owningNode = NULL);
|
Avatar(Node* owningNode = NULL);
|
||||||
~Avatar();
|
~Avatar();
|
||||||
|
@ -155,10 +155,6 @@ public:
|
||||||
glm::quat getOrientation() const;
|
glm::quat getOrientation() const;
|
||||||
glm::quat getWorldAlignedOrientation() const;
|
glm::quat getWorldAlignedOrientation() const;
|
||||||
AvatarVoxelSystem* getVoxels() { return &_voxels; }
|
AvatarVoxelSystem* getVoxels() { return &_voxels; }
|
||||||
|
|
||||||
// get/set avatar data
|
|
||||||
void saveData(QSettings* set);
|
|
||||||
void loadData(QSettings* set);
|
|
||||||
|
|
||||||
// Get the position/rotation of a single body ball
|
// Get the position/rotation of a single body ball
|
||||||
void getBodyBallTransform(AvatarJointID jointID, glm::vec3& position, glm::quat& rotation) const;
|
void getBodyBallTransform(AvatarJointID jointID, glm::vec3& position, glm::quat& rotation) const;
|
||||||
|
@ -226,6 +222,8 @@ protected:
|
||||||
AvatarVoxelSystem _voxels;
|
AvatarVoxelSystem _voxels;
|
||||||
|
|
||||||
bool _moving; ///< set when position is changing
|
bool _moving; ///< set when position is changing
|
||||||
|
float _hoverOnDuration;
|
||||||
|
float _hoverOffDuration;
|
||||||
|
|
||||||
// protected methods...
|
// protected methods...
|
||||||
glm::vec3 getBodyRightDirection() const { return getOrientation() * IDENTITY_RIGHT; }
|
glm::vec3 getBodyRightDirection() const { return getOrientation() * IDENTITY_RIGHT; }
|
||||||
|
@ -247,6 +245,7 @@ private:
|
||||||
float _maxArmLength;
|
float _maxArmLength;
|
||||||
float _pelvisStandingHeight;
|
float _pelvisStandingHeight;
|
||||||
|
|
||||||
|
|
||||||
// private methods...
|
// private methods...
|
||||||
glm::vec3 calculateAverageEyePosition() { return _head.calculateAverageEyePosition(); } // get the position smack-dab between the eyes (for lookat)
|
glm::vec3 calculateAverageEyePosition() { return _head.calculateAverageEyePosition(); } // get the position smack-dab between the eyes (for lookat)
|
||||||
float getBallRenderAlpha(int ball, bool lookingInMirror) const;
|
float getBallRenderAlpha(int ball, bool lookingInMirror) const;
|
||||||
|
|
|
@ -151,9 +151,9 @@ void AvatarVoxelSystem::setVoxelURL(const QUrl& url) {
|
||||||
connect(_voxelReply, SIGNAL(error(QNetworkReply::NetworkError)), SLOT(handleVoxelReplyError()));
|
connect(_voxelReply, SIGNAL(error(QNetworkReply::NetworkError)), SLOT(handleVoxelReplyError()));
|
||||||
}
|
}
|
||||||
|
|
||||||
void AvatarVoxelSystem::updateNodeInArrays(glBufferIndex nodeIndex, const glm::vec3& startVertex,
|
void AvatarVoxelSystem::updateArraysDetails(glBufferIndex nodeIndex, const glm::vec3& startVertex,
|
||||||
float voxelScale, const nodeColor& color) {
|
float voxelScale, const nodeColor& color) {
|
||||||
VoxelSystem::updateNodeInArrays(nodeIndex, startVertex, voxelScale, color);
|
VoxelSystem::updateArraysDetails(nodeIndex, startVertex, voxelScale, color);
|
||||||
|
|
||||||
GLubyte* writeBoneIndicesAt = _writeBoneIndicesArray + (nodeIndex * BONE_ELEMENTS_PER_VOXEL);
|
GLubyte* writeBoneIndicesAt = _writeBoneIndicesArray + (nodeIndex * BONE_ELEMENTS_PER_VOXEL);
|
||||||
GLfloat* writeBoneWeightsAt = _writeBoneWeightsArray + (nodeIndex * BONE_ELEMENTS_PER_VOXEL);
|
GLfloat* writeBoneWeightsAt = _writeBoneWeightsArray + (nodeIndex * BONE_ELEMENTS_PER_VOXEL);
|
||||||
|
|
|
@ -42,7 +42,7 @@ public slots:
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
virtual void updateNodeInArrays(glBufferIndex nodeIndex, const glm::vec3& startVertex,
|
virtual void updateArraysDetails(glBufferIndex nodeIndex, const glm::vec3& startVertex,
|
||||||
float voxelScale, const nodeColor& color);
|
float voxelScale, const nodeColor& color);
|
||||||
virtual void copyWrittenDataSegmentToReadArrays(glBufferIndex segmentStart, glBufferIndex segmentEnd);
|
virtual void copyWrittenDataSegmentToReadArrays(glBufferIndex segmentStart, glBufferIndex segmentEnd);
|
||||||
virtual void updateVBOSegment(glBufferIndex segmentStart, glBufferIndex segmentEnd);
|
virtual void updateVBOSegment(glBufferIndex segmentStart, glBufferIndex segmentEnd);
|
||||||
|
|
|
@ -8,6 +8,8 @@
|
||||||
|
|
||||||
#include <QNetworkReply>
|
#include <QNetworkReply>
|
||||||
|
|
||||||
|
#include <glm/gtx/transform.hpp>
|
||||||
|
|
||||||
#include "Application.h"
|
#include "Application.h"
|
||||||
#include "BlendFace.h"
|
#include "BlendFace.h"
|
||||||
#include "Head.h"
|
#include "Head.h"
|
||||||
|
@ -41,21 +43,117 @@ void BlendFace::init() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void BlendFace::reset() {
|
||||||
|
_resetStates = true;
|
||||||
|
}
|
||||||
|
|
||||||
const glm::vec3 MODEL_TRANSLATION(0.0f, -120.0f, 40.0f); // temporary fudge factor
|
const glm::vec3 MODEL_TRANSLATION(0.0f, -120.0f, 40.0f); // temporary fudge factor
|
||||||
const float MODEL_SCALE = 0.0006f;
|
const float MODEL_SCALE = 0.0006f;
|
||||||
|
|
||||||
bool BlendFace::render(float alpha) {
|
void BlendFace::simulate(float deltaTime) {
|
||||||
if (!isActive()) {
|
if (!isActive()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// set up world vertices on first simulate after load
|
||||||
|
const FBXGeometry& geometry = _geometry->getFBXGeometry();
|
||||||
|
if (_meshStates.isEmpty()) {
|
||||||
|
QVector<glm::vec3> vertices;
|
||||||
|
foreach (const FBXMesh& mesh, geometry.meshes) {
|
||||||
|
MeshState state;
|
||||||
|
if (mesh.springiness > 0.0f) {
|
||||||
|
state.worldSpaceVertices.resize(mesh.vertices.size());
|
||||||
|
state.vertexVelocities.resize(mesh.vertices.size());
|
||||||
|
state.worldSpaceNormals.resize(mesh.vertices.size());
|
||||||
|
}
|
||||||
|
_meshStates.append(state);
|
||||||
|
}
|
||||||
|
_resetStates = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
glm::quat orientation = _owningHead->getOrientation();
|
||||||
|
glm::vec3 scale = glm::vec3(-1.0f, 1.0f, -1.0f) * _owningHead->getScale() * MODEL_SCALE;
|
||||||
|
glm::vec3 offset = MODEL_TRANSLATION - _geometry->getFBXGeometry().neckPivot;
|
||||||
|
glm::mat4 baseTransform = glm::translate(_owningHead->getPosition()) * glm::mat4_cast(orientation) *
|
||||||
|
glm::scale(scale) * glm::translate(offset);
|
||||||
|
|
||||||
|
for (int i = 0; i < _meshStates.size(); i++) {
|
||||||
|
MeshState& state = _meshStates[i];
|
||||||
|
int vertexCount = state.worldSpaceVertices.size();
|
||||||
|
if (vertexCount == 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
glm::vec3* destVertices = state.worldSpaceVertices.data();
|
||||||
|
glm::vec3* destVelocities = state.vertexVelocities.data();
|
||||||
|
glm::vec3* destNormals = state.worldSpaceNormals.data();
|
||||||
|
const FBXMesh& mesh = geometry.meshes.at(i);
|
||||||
|
const glm::vec3* sourceVertices = mesh.vertices.constData();
|
||||||
|
if (!mesh.blendshapes.isEmpty()) {
|
||||||
|
_blendedVertices.resize(max(_blendedVertices.size(), vertexCount));
|
||||||
|
memcpy(_blendedVertices.data(), mesh.vertices.constData(), vertexCount * sizeof(glm::vec3));
|
||||||
|
|
||||||
|
// blend in each coefficient
|
||||||
|
const vector<float>& coefficients = _owningHead->getBlendshapeCoefficients();
|
||||||
|
for (int j = 0; j < coefficients.size(); j++) {
|
||||||
|
float coefficient = coefficients[j];
|
||||||
|
if (coefficient == 0.0f || j >= mesh.blendshapes.size() || mesh.blendshapes[j].vertices.isEmpty()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const glm::vec3* vertex = mesh.blendshapes[j].vertices.constData();
|
||||||
|
for (const int* index = mesh.blendshapes[j].indices.constData(),
|
||||||
|
*end = index + mesh.blendshapes[j].indices.size(); index != end; index++, vertex++) {
|
||||||
|
_blendedVertices[*index] += *vertex * coefficient;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sourceVertices = _blendedVertices.constData();
|
||||||
|
}
|
||||||
|
glm::mat4 transform = baseTransform;
|
||||||
|
if (mesh.isEye) {
|
||||||
|
transform = transform * glm::translate(mesh.pivot) * glm::mat4_cast(glm::inverse(orientation) *
|
||||||
|
_owningHead->getEyeRotation(orientation * ((mesh.pivot + offset) * scale) + _owningHead->getPosition())) *
|
||||||
|
glm::translate(-mesh.pivot);
|
||||||
|
}
|
||||||
|
if (_resetStates) {
|
||||||
|
for (int j = 0; j < vertexCount; j++) {
|
||||||
|
destVertices[j] = glm::vec3(transform * glm::vec4(sourceVertices[j], 1.0f));
|
||||||
|
destVelocities[j] = glm::vec3();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const float SPRINGINESS_MULTIPLIER = 200.0f;
|
||||||
|
const float DAMPING = 5.0f;
|
||||||
|
for (int j = 0; j < vertexCount; j++) {
|
||||||
|
destVelocities[j] += ((glm::vec3(transform * glm::vec4(sourceVertices[j], 1.0f)) - destVertices[j]) *
|
||||||
|
mesh.springiness * SPRINGINESS_MULTIPLIER - destVelocities[j] * DAMPING) * deltaTime;
|
||||||
|
destVertices[j] += destVelocities[j] * deltaTime;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (int j = 0; j < vertexCount; j++) {
|
||||||
|
destNormals[j] = glm::vec3();
|
||||||
|
|
||||||
|
const glm::vec3& middle = destVertices[j];
|
||||||
|
for (QVarLengthArray<QPair<int, int>, 4>::const_iterator connection = mesh.vertexConnections.at(j).constBegin();
|
||||||
|
connection != mesh.vertexConnections.at(j).constEnd(); connection++) {
|
||||||
|
destNormals[j] += glm::normalize(glm::cross(destVertices[connection->second] - middle,
|
||||||
|
destVertices[connection->first] - middle));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_resetStates = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BlendFace::render(float alpha) {
|
||||||
|
if (_meshStates.isEmpty()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// set up blended buffer ids on first render after load
|
// set up blended buffer ids on first render after load/simulate
|
||||||
const FBXGeometry& geometry = _geometry->getFBXGeometry();
|
const FBXGeometry& geometry = _geometry->getFBXGeometry();
|
||||||
const QVector<NetworkMesh>& networkMeshes = _geometry->getMeshes();
|
const QVector<NetworkMesh>& networkMeshes = _geometry->getMeshes();
|
||||||
if (_blendedVertexBufferIDs.isEmpty()) {
|
if (_blendedVertexBufferIDs.isEmpty()) {
|
||||||
foreach (const FBXMesh& mesh, geometry.meshes) {
|
foreach (const FBXMesh& mesh, geometry.meshes) {
|
||||||
GLuint id = 0;
|
GLuint id = 0;
|
||||||
if (!mesh.blendshapes.isEmpty()) {
|
if (!mesh.blendshapes.isEmpty() || mesh.springiness > 0.0f) {
|
||||||
glGenBuffers(1, &id);
|
glGenBuffers(1, &id);
|
||||||
glBindBuffer(GL_ARRAY_BUFFER, id);
|
glBindBuffer(GL_ARRAY_BUFFER, id);
|
||||||
glBufferData(GL_ARRAY_BUFFER, (mesh.vertices.size() + mesh.normals.size()) * sizeof(glm::vec3),
|
glBufferData(GL_ARRAY_BUFFER, (mesh.vertices.size() + mesh.normals.size()) * sizeof(glm::vec3),
|
||||||
|
@ -69,6 +167,9 @@ bool BlendFace::render(float alpha) {
|
||||||
_dilatedTextures.resize(geometry.meshes.size());
|
_dilatedTextures.resize(geometry.meshes.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
glm::mat4 viewMatrix;
|
||||||
|
glGetFloatv(GL_MODELVIEW_MATRIX, (GLfloat*)&viewMatrix);
|
||||||
|
|
||||||
glPushMatrix();
|
glPushMatrix();
|
||||||
glTranslatef(_owningHead->getPosition().x, _owningHead->getPosition().y, _owningHead->getPosition().z);
|
glTranslatef(_owningHead->getPosition().x, _owningHead->getPosition().y, _owningHead->getPosition().z);
|
||||||
glm::quat orientation = _owningHead->getOrientation();
|
glm::quat orientation = _owningHead->getOrientation();
|
||||||
|
@ -88,8 +189,10 @@ bool BlendFace::render(float alpha) {
|
||||||
// enable normalization under the expectation that the GPU can do it faster
|
// enable normalization under the expectation that the GPU can do it faster
|
||||||
glEnable(GL_NORMALIZE);
|
glEnable(GL_NORMALIZE);
|
||||||
glEnable(GL_TEXTURE_2D);
|
glEnable(GL_TEXTURE_2D);
|
||||||
|
glDisable(GL_COLOR_MATERIAL);
|
||||||
|
|
||||||
glColor4f(_owningHead->getSkinColor().r, _owningHead->getSkinColor().g, _owningHead->getSkinColor().b, alpha);
|
// the eye shader uses the color state even though color material is disabled
|
||||||
|
glColor4f(1.0f, 1.0f, 1.0f, alpha);
|
||||||
|
|
||||||
for (int i = 0; i < networkMeshes.size(); i++) {
|
for (int i = 0; i < networkMeshes.size(); i++) {
|
||||||
const NetworkMesh& networkMesh = networkMeshes.at(i);
|
const NetworkMesh& networkMesh = networkMeshes.at(i);
|
||||||
|
@ -105,7 +208,7 @@ bool BlendFace::render(float alpha) {
|
||||||
if (mesh.isEye) {
|
if (mesh.isEye) {
|
||||||
glTranslatef(mesh.pivot.x, mesh.pivot.y, mesh.pivot.z);
|
glTranslatef(mesh.pivot.x, mesh.pivot.y, mesh.pivot.z);
|
||||||
glm::quat rotation = glm::inverse(orientation) * _owningHead->getEyeRotation(orientation *
|
glm::quat rotation = glm::inverse(orientation) * _owningHead->getEyeRotation(orientation *
|
||||||
(mesh.pivot * scale + MODEL_TRANSLATION) + _owningHead->getPosition());
|
((mesh.pivot + offset) * scale) + _owningHead->getPosition());
|
||||||
glm::vec3 rotationAxis = glm::axis(rotation);
|
glm::vec3 rotationAxis = glm::axis(rotation);
|
||||||
glRotatef(glm::angle(rotation), -rotationAxis.x, rotationAxis.y, -rotationAxis.z);
|
glRotatef(glm::angle(rotation), -rotationAxis.x, rotationAxis.y, -rotationAxis.z);
|
||||||
glTranslatef(-mesh.pivot.x, -mesh.pivot.y, -mesh.pivot.z);
|
glTranslatef(-mesh.pivot.x, -mesh.pivot.y, -mesh.pivot.z);
|
||||||
|
@ -118,49 +221,62 @@ bool BlendFace::render(float alpha) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
glMultMatrixf((const GLfloat*)&mesh.transform);
|
// apply material properties
|
||||||
|
glm::vec4 diffuse = glm::vec4(mesh.diffuseColor, alpha);
|
||||||
|
glm::vec4 specular = glm::vec4(mesh.specularColor, alpha);
|
||||||
|
glMaterialfv(GL_FRONT, GL_AMBIENT, (const float*)&diffuse);
|
||||||
|
glMaterialfv(GL_FRONT, GL_DIFFUSE, (const float*)&diffuse);
|
||||||
|
glMaterialfv(GL_FRONT, GL_SPECULAR, (const float*)&specular);
|
||||||
|
glMaterialf(GL_FRONT, GL_SHININESS, mesh.shininess);
|
||||||
|
|
||||||
// all meshes after the first are white
|
glMultMatrixf((const GLfloat*)&mesh.transform);
|
||||||
if (i == 1) {
|
|
||||||
glColor4f(1.0f, 1.0f, 1.0f, alpha);
|
|
||||||
}
|
|
||||||
|
|
||||||
glBindTexture(GL_TEXTURE_2D, texture == NULL ? 0 : texture->getID());
|
glBindTexture(GL_TEXTURE_2D, texture == NULL ? 0 : texture->getID());
|
||||||
|
|
||||||
glBindBuffer(GL_ARRAY_BUFFER, networkMesh.vertexBufferID);
|
glBindBuffer(GL_ARRAY_BUFFER, networkMesh.vertexBufferID);
|
||||||
if (mesh.blendshapes.isEmpty()) {
|
if (mesh.blendshapes.isEmpty() && mesh.springiness == 0.0f) {
|
||||||
glTexCoordPointer(2, GL_FLOAT, 0, (void*)(vertexCount * 2 * sizeof(glm::vec3)));
|
glTexCoordPointer(2, GL_FLOAT, 0, (void*)(vertexCount * 2 * sizeof(glm::vec3)));
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
glTexCoordPointer(2, GL_FLOAT, 0, 0);
|
glTexCoordPointer(2, GL_FLOAT, 0, 0);
|
||||||
|
|
||||||
_blendedVertices.resize(max(_blendedVertices.size(), vertexCount));
|
|
||||||
_blendedNormals.resize(_blendedVertices.size());
|
|
||||||
memcpy(_blendedVertices.data(), mesh.vertices.constData(), vertexCount * sizeof(glm::vec3));
|
|
||||||
memcpy(_blendedNormals.data(), mesh.normals.constData(), vertexCount * sizeof(glm::vec3));
|
|
||||||
|
|
||||||
// blend in each coefficient
|
|
||||||
const vector<float>& coefficients = _owningHead->getBlendshapeCoefficients();
|
|
||||||
for (int j = 0; j < coefficients.size(); j++) {
|
|
||||||
float coefficient = coefficients[j];
|
|
||||||
if (coefficient == 0.0f || j >= mesh.blendshapes.size() || mesh.blendshapes[j].vertices.isEmpty()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
const float NORMAL_COEFFICIENT_SCALE = 0.01f;
|
|
||||||
float normalCoefficient = coefficient * NORMAL_COEFFICIENT_SCALE;
|
|
||||||
const glm::vec3* vertex = mesh.blendshapes[j].vertices.constData();
|
|
||||||
const glm::vec3* normal = mesh.blendshapes[j].normals.constData();
|
|
||||||
for (const int* index = mesh.blendshapes[j].indices.constData(),
|
|
||||||
*end = index + mesh.blendshapes[j].indices.size(); index != end; index++, vertex++, normal++) {
|
|
||||||
_blendedVertices[*index] += *vertex * coefficient;
|
|
||||||
_blendedNormals[*index] += *normal * normalCoefficient;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
glBindBuffer(GL_ARRAY_BUFFER, _blendedVertexBufferIDs.at(i));
|
glBindBuffer(GL_ARRAY_BUFFER, _blendedVertexBufferIDs.at(i));
|
||||||
glBufferSubData(GL_ARRAY_BUFFER, 0, vertexCount * sizeof(glm::vec3), _blendedVertices.constData());
|
|
||||||
glBufferSubData(GL_ARRAY_BUFFER, vertexCount * sizeof(glm::vec3),
|
const MeshState& state = _meshStates.at(i);
|
||||||
vertexCount * sizeof(glm::vec3), _blendedNormals.constData());
|
if (!state.worldSpaceVertices.isEmpty()) {
|
||||||
|
glLoadMatrixf((const GLfloat*)&viewMatrix);
|
||||||
|
|
||||||
|
glBufferSubData(GL_ARRAY_BUFFER, 0, vertexCount * sizeof(glm::vec3), state.worldSpaceVertices.constData());
|
||||||
|
glBufferSubData(GL_ARRAY_BUFFER, vertexCount * sizeof(glm::vec3),
|
||||||
|
vertexCount * sizeof(glm::vec3), state.worldSpaceNormals.constData());
|
||||||
|
|
||||||
|
} else {
|
||||||
|
_blendedVertices.resize(max(_blendedVertices.size(), vertexCount));
|
||||||
|
_blendedNormals.resize(_blendedVertices.size());
|
||||||
|
memcpy(_blendedVertices.data(), mesh.vertices.constData(), vertexCount * sizeof(glm::vec3));
|
||||||
|
memcpy(_blendedNormals.data(), mesh.normals.constData(), vertexCount * sizeof(glm::vec3));
|
||||||
|
|
||||||
|
// blend in each coefficient
|
||||||
|
const vector<float>& coefficients = _owningHead->getBlendshapeCoefficients();
|
||||||
|
for (int j = 0; j < coefficients.size(); j++) {
|
||||||
|
float coefficient = coefficients[j];
|
||||||
|
if (coefficient == 0.0f || j >= mesh.blendshapes.size() || mesh.blendshapes[j].vertices.isEmpty()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const float NORMAL_COEFFICIENT_SCALE = 0.01f;
|
||||||
|
float normalCoefficient = coefficient * NORMAL_COEFFICIENT_SCALE;
|
||||||
|
const glm::vec3* vertex = mesh.blendshapes[j].vertices.constData();
|
||||||
|
const glm::vec3* normal = mesh.blendshapes[j].normals.constData();
|
||||||
|
for (const int* index = mesh.blendshapes[j].indices.constData(),
|
||||||
|
*end = index + mesh.blendshapes[j].indices.size(); index != end; index++, vertex++, normal++) {
|
||||||
|
_blendedVertices[*index] += *vertex * coefficient;
|
||||||
|
_blendedNormals[*index] += *normal * normalCoefficient;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
glBufferSubData(GL_ARRAY_BUFFER, 0, vertexCount * sizeof(glm::vec3), _blendedVertices.constData());
|
||||||
|
glBufferSubData(GL_ARRAY_BUFFER, vertexCount * sizeof(glm::vec3),
|
||||||
|
vertexCount * sizeof(glm::vec3), _blendedNormals.constData());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
glVertexPointer(3, GL_FLOAT, 0, 0);
|
glVertexPointer(3, GL_FLOAT, 0, 0);
|
||||||
glNormalPointer(GL_FLOAT, 0, (void*)(vertexCount * sizeof(glm::vec3)));
|
glNormalPointer(GL_FLOAT, 0, (void*)(vertexCount * sizeof(glm::vec3)));
|
||||||
|
@ -191,15 +307,22 @@ bool BlendFace::render(float alpha) {
|
||||||
|
|
||||||
glPopMatrix();
|
glPopMatrix();
|
||||||
|
|
||||||
|
// restore all the default material settings
|
||||||
|
Application::getInstance()->setupWorldLight(*Application::getInstance()->getCamera());
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void BlendFace::getEyePositions(glm::vec3& firstEyePosition, glm::vec3& secondEyePosition) const {
|
bool BlendFace::getEyePositions(glm::vec3& firstEyePosition, glm::vec3& secondEyePosition, bool upright) const {
|
||||||
if (!isActive()) {
|
if (!isActive()) {
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
glm::vec3 translation = _owningHead->getPosition();
|
||||||
glm::quat orientation = _owningHead->getOrientation();
|
glm::quat orientation = _owningHead->getOrientation();
|
||||||
|
if (upright) {
|
||||||
|
translation = static_cast<MyAvatar*>(_owningHead->_owningAvatar)->getUprightHeadPosition();
|
||||||
|
orientation = static_cast<Avatar*>(_owningHead->_owningAvatar)->getWorldAlignedOrientation();
|
||||||
|
}
|
||||||
glm::vec3 scale(-_owningHead->getScale() * MODEL_SCALE, _owningHead->getScale() * MODEL_SCALE,
|
glm::vec3 scale(-_owningHead->getScale() * MODEL_SCALE, _owningHead->getScale() * MODEL_SCALE,
|
||||||
-_owningHead->getScale() * MODEL_SCALE);
|
-_owningHead->getScale() * MODEL_SCALE);
|
||||||
bool foundFirst = false;
|
bool foundFirst = false;
|
||||||
|
@ -207,16 +330,20 @@ void BlendFace::getEyePositions(glm::vec3& firstEyePosition, glm::vec3& secondEy
|
||||||
const FBXGeometry& geometry = _geometry->getFBXGeometry();
|
const FBXGeometry& geometry = _geometry->getFBXGeometry();
|
||||||
foreach (const FBXMesh& mesh, geometry.meshes) {
|
foreach (const FBXMesh& mesh, geometry.meshes) {
|
||||||
if (mesh.isEye) {
|
if (mesh.isEye) {
|
||||||
glm::vec3 position = orientation * ((mesh.pivot + MODEL_TRANSLATION - geometry.neckPivot) * scale) +
|
glm::vec3 position = orientation * ((mesh.pivot + MODEL_TRANSLATION - geometry.neckPivot) * scale) + translation;
|
||||||
_owningHead->getPosition();
|
|
||||||
if (foundFirst) {
|
if (foundFirst) {
|
||||||
secondEyePosition = position;
|
secondEyePosition = position;
|
||||||
return;
|
return true;
|
||||||
}
|
}
|
||||||
firstEyePosition = position;
|
firstEyePosition = position;
|
||||||
foundFirst = true;
|
foundFirst = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
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) {
|
void BlendFace::setModelURL(const QUrl& url) {
|
||||||
|
@ -238,4 +365,5 @@ void BlendFace::deleteGeometry() {
|
||||||
glDeleteBuffers(1, &id);
|
glDeleteBuffers(1, &id);
|
||||||
}
|
}
|
||||||
_blendedVertexBufferIDs.clear();
|
_blendedVertexBufferIDs.clear();
|
||||||
|
_meshStates.clear();
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,12 +33,20 @@ public:
|
||||||
bool isActive() const { return _geometry && _geometry->isLoaded(); }
|
bool isActive() const { return _geometry && _geometry->isLoaded(); }
|
||||||
|
|
||||||
void init();
|
void init();
|
||||||
|
void reset();
|
||||||
|
void simulate(float deltaTime);
|
||||||
bool render(float alpha);
|
bool render(float alpha);
|
||||||
|
|
||||||
Q_INVOKABLE void setModelURL(const QUrl& url);
|
Q_INVOKABLE void setModelURL(const QUrl& url);
|
||||||
const QUrl& getModelURL() const { return _modelURL; }
|
const QUrl& getModelURL() const { return _modelURL; }
|
||||||
|
|
||||||
void getEyePositions(glm::vec3& firstEyePosition, glm::vec3& secondEyePosition) const;
|
/// Retrieve the positions of up to two eye meshes.
|
||||||
|
/// \param upright if true, retrieve the locations of the eyes in the upright position
|
||||||
|
/// \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:
|
private:
|
||||||
|
|
||||||
|
@ -50,8 +58,17 @@ private:
|
||||||
|
|
||||||
QSharedPointer<NetworkGeometry> _geometry;
|
QSharedPointer<NetworkGeometry> _geometry;
|
||||||
|
|
||||||
|
class MeshState {
|
||||||
|
public:
|
||||||
|
QVector<glm::vec3> worldSpaceVertices;
|
||||||
|
QVector<glm::vec3> vertexVelocities;
|
||||||
|
QVector<glm::vec3> worldSpaceNormals;
|
||||||
|
};
|
||||||
|
|
||||||
|
QVector<MeshState> _meshStates;
|
||||||
QVector<GLuint> _blendedVertexBufferIDs;
|
QVector<GLuint> _blendedVertexBufferIDs;
|
||||||
QVector<QSharedPointer<Texture> > _dilatedTextures;
|
QVector<QSharedPointer<Texture> > _dilatedTextures;
|
||||||
|
bool _resetStates;
|
||||||
|
|
||||||
QVector<glm::vec3> _blendedVertices;
|
QVector<glm::vec3> _blendedVertices;
|
||||||
QVector<glm::vec3> _blendedNormals;
|
QVector<glm::vec3> _blendedNormals;
|
||||||
|
|
|
@ -83,8 +83,6 @@ Head::Head(Avatar* owningAvatar) :
|
||||||
_mousePitch(0.f),
|
_mousePitch(0.f),
|
||||||
_cameraYaw(_yaw),
|
_cameraYaw(_yaw),
|
||||||
_isCameraMoving(false),
|
_isCameraMoving(false),
|
||||||
_cameraFollowsHead(false),
|
|
||||||
_cameraFollowHeadRate(0.0f),
|
|
||||||
_face(this),
|
_face(this),
|
||||||
_perlinFace(this),
|
_perlinFace(this),
|
||||||
_blendFace(this)
|
_blendFace(this)
|
||||||
|
@ -117,6 +115,8 @@ void Head::reset() {
|
||||||
if (USING_PHYSICAL_MOHAWK) {
|
if (USING_PHYSICAL_MOHAWK) {
|
||||||
resetHairPhysics();
|
resetHairPhysics();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_blendFace.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Head::resetHairPhysics() {
|
void Head::resetHairPhysics() {
|
||||||
|
@ -140,8 +140,10 @@ void Head::simulate(float deltaTime, bool isMine) {
|
||||||
|
|
||||||
// Update audio trailing average for rendering facial animations
|
// Update audio trailing average for rendering facial animations
|
||||||
Faceshift* faceshift = Application::getInstance()->getFaceshift();
|
Faceshift* faceshift = Application::getInstance()->getFaceshift();
|
||||||
_isFaceshiftConnected = faceshift != NULL;
|
if (isMine) {
|
||||||
|
_isFaceshiftConnected = faceshift->isActive();
|
||||||
|
}
|
||||||
|
|
||||||
if (isMine && faceshift->isActive()) {
|
if (isMine && faceshift->isActive()) {
|
||||||
const float EYE_OPEN_SCALE = 0.5f;
|
const float EYE_OPEN_SCALE = 0.5f;
|
||||||
_leftEyeBlink = faceshift->getLeftBlink() - EYE_OPEN_SCALE * faceshift->getLeftEyeOpen();
|
_leftEyeBlink = faceshift->getLeftBlink() - EYE_OPEN_SCALE * faceshift->getLeftEyeOpen();
|
||||||
|
@ -155,7 +157,7 @@ void Head::simulate(float deltaTime, bool isMine) {
|
||||||
_browAudioLift = faceshift->getBrowUpCenter() * BROW_HEIGHT_SCALE;
|
_browAudioLift = faceshift->getBrowUpCenter() * BROW_HEIGHT_SCALE;
|
||||||
_blendshapeCoefficients = faceshift->getBlendshapeCoefficients();
|
_blendshapeCoefficients = faceshift->getBlendshapeCoefficients();
|
||||||
|
|
||||||
} else if (!_isFaceshiftConnected) {
|
} else if (!_isFaceshiftConnected) {
|
||||||
// Update eye saccades
|
// Update eye saccades
|
||||||
const float AVERAGE_MICROSACCADE_INTERVAL = 0.50f;
|
const float AVERAGE_MICROSACCADE_INTERVAL = 0.50f;
|
||||||
const float AVERAGE_SACCADE_INTERVAL = 4.0f;
|
const float AVERAGE_SACCADE_INTERVAL = 4.0f;
|
||||||
|
@ -235,6 +237,8 @@ void Head::simulate(float deltaTime, bool isMine) {
|
||||||
if (USING_PHYSICAL_MOHAWK) {
|
if (USING_PHYSICAL_MOHAWK) {
|
||||||
updateHairPhysics(deltaTime);
|
updateHairPhysics(deltaTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_blendFace.simulate(deltaTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Head::calculateGeometry() {
|
void Head::calculateGeometry() {
|
||||||
|
@ -300,19 +304,21 @@ void Head::render(float alpha, bool isMine) {
|
||||||
renderEyeBrows();
|
renderEyeBrows();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (_blendFace.isActive()) {
|
||||||
|
// the blend face may have custom eye meshes
|
||||||
|
_blendFace.getEyePositions(_leftEyePosition, _rightEyePosition);
|
||||||
|
}
|
||||||
|
|
||||||
if (_renderLookatVectors) {
|
if (_renderLookatVectors) {
|
||||||
glm::vec3 firstEyePosition = _leftEyePosition;
|
renderLookatVectors(_leftEyePosition, _rightEyePosition, _lookAtPosition);
|
||||||
glm::vec3 secondEyePosition = _rightEyePosition;
|
|
||||||
if (_blendFace.isActive()) {
|
|
||||||
// the blend face may have custom eye meshes
|
|
||||||
_blendFace.getEyePositions(firstEyePosition, secondEyePosition);
|
|
||||||
}
|
|
||||||
renderLookatVectors(firstEyePosition, secondEyePosition, _lookAtPosition);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Head::setScale (float scale) {
|
void Head::setScale (float scale) {
|
||||||
|
if (_scale == scale) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
_scale = scale;
|
_scale = scale;
|
||||||
|
|
||||||
createMohawk();
|
createMohawk();
|
||||||
|
|
|
@ -57,7 +57,6 @@ public:
|
||||||
void setAverageLoudness(float averageLoudness) { _averageLoudness = averageLoudness; }
|
void setAverageLoudness(float averageLoudness) { _averageLoudness = averageLoudness; }
|
||||||
void setReturnToCenter (bool returnHeadToCenter) { _returnHeadToCenter = returnHeadToCenter; }
|
void setReturnToCenter (bool returnHeadToCenter) { _returnHeadToCenter = returnHeadToCenter; }
|
||||||
void setRenderLookatVectors(bool onOff) { _renderLookatVectors = onOff; }
|
void setRenderLookatVectors(bool onOff) { _renderLookatVectors = onOff; }
|
||||||
void setCameraFollowsHead(bool cameraFollowsHead) { _cameraFollowsHead = cameraFollowsHead; }
|
|
||||||
|
|
||||||
float getMousePitch() const { return _mousePitch; }
|
float getMousePitch() const { return _mousePitch; }
|
||||||
void setMousePitch(float mousePitch) { _mousePitch = mousePitch; }
|
void setMousePitch(float mousePitch) { _mousePitch = mousePitch; }
|
||||||
|
@ -133,8 +132,6 @@ private:
|
||||||
float _mousePitch;
|
float _mousePitch;
|
||||||
float _cameraYaw;
|
float _cameraYaw;
|
||||||
bool _isCameraMoving;
|
bool _isCameraMoving;
|
||||||
bool _cameraFollowsHead;
|
|
||||||
float _cameraFollowHeadRate;
|
|
||||||
Face _face;
|
Face _face;
|
||||||
PerlinFace _perlinFace;
|
PerlinFace _perlinFace;
|
||||||
BlendFace _blendFace;
|
BlendFace _blendFace;
|
||||||
|
@ -158,6 +155,7 @@ private:
|
||||||
void resetHairPhysics();
|
void resetHairPhysics();
|
||||||
void updateHairPhysics(float deltaTime);
|
void updateHairPhysics(float deltaTime);
|
||||||
|
|
||||||
|
friend class BlendFace;
|
||||||
friend class PerlinFace;
|
friend class PerlinFace;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
#include <SharedUtil.h>
|
#include <SharedUtil.h>
|
||||||
|
|
||||||
#include "Application.h"
|
#include "Application.h"
|
||||||
|
#include "DataServerClient.h"
|
||||||
#include "MyAvatar.h"
|
#include "MyAvatar.h"
|
||||||
#include "Physics.h"
|
#include "Physics.h"
|
||||||
#include "devices/OculusManager.h"
|
#include "devices/OculusManager.h"
|
||||||
|
@ -356,8 +357,7 @@ void MyAvatar::simulate(float deltaTime, Transmitter* transmitter) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update avatar head rotation with sensor data
|
// Update avatar head rotation with sensor data
|
||||||
void MyAvatar::updateFromGyrosAndOrWebcam(bool gyroLook,
|
void MyAvatar::updateFromGyrosAndOrWebcam(float pitchFromTouch, bool turnWithHead) {
|
||||||
float pitchFromTouch) {
|
|
||||||
Faceshift* faceshift = Application::getInstance()->getFaceshift();
|
Faceshift* faceshift = Application::getInstance()->getFaceshift();
|
||||||
SerialInterface* gyros = Application::getInstance()->getSerialHeadSensor();
|
SerialInterface* gyros = Application::getInstance()->getSerialHeadSensor();
|
||||||
Webcam* webcam = Application::getInstance()->getWebcam();
|
Webcam* webcam = Application::getInstance()->getWebcam();
|
||||||
|
@ -367,11 +367,13 @@ void MyAvatar::updateFromGyrosAndOrWebcam(bool gyroLook,
|
||||||
estimatedPosition = faceshift->getHeadTranslation();
|
estimatedPosition = faceshift->getHeadTranslation();
|
||||||
estimatedRotation = safeEulerAngles(faceshift->getHeadRotation());
|
estimatedRotation = safeEulerAngles(faceshift->getHeadRotation());
|
||||||
// Rotate the body if the head is turned quickly
|
// Rotate the body if the head is turned quickly
|
||||||
glm::vec3 headAngularVelocity = faceshift->getHeadAngularVelocity();
|
if (turnWithHead) {
|
||||||
const float FACESHIFT_YAW_VIEW_SENSITIVITY = 20.f;
|
glm::vec3 headAngularVelocity = faceshift->getHeadAngularVelocity();
|
||||||
const float FACESHIFT_MIN_YAW_VELOCITY = 1.0f;
|
const float FACESHIFT_YAW_VIEW_SENSITIVITY = 20.f;
|
||||||
if (fabs(headAngularVelocity.y) > FACESHIFT_MIN_YAW_VELOCITY) {
|
const float FACESHIFT_MIN_YAW_VELOCITY = 1.0f;
|
||||||
_bodyYawDelta += headAngularVelocity.y * FACESHIFT_YAW_VIEW_SENSITIVITY;
|
if (fabs(headAngularVelocity.y) > FACESHIFT_MIN_YAW_VELOCITY) {
|
||||||
|
_bodyYawDelta += headAngularVelocity.y * FACESHIFT_YAW_VIEW_SENSITIVITY;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else if (gyros->isActive()) {
|
} else if (gyros->isActive()) {
|
||||||
estimatedRotation = gyros->getEstimatedRotation();
|
estimatedRotation = gyros->getEstimatedRotation();
|
||||||
|
@ -428,7 +430,6 @@ void MyAvatar::updateFromGyrosAndOrWebcam(bool gyroLook,
|
||||||
_head.setPitch(estimatedRotation.x * AVATAR_HEAD_PITCH_MAGNIFY);
|
_head.setPitch(estimatedRotation.x * AVATAR_HEAD_PITCH_MAGNIFY);
|
||||||
_head.setYaw(estimatedRotation.y * AVATAR_HEAD_YAW_MAGNIFY);
|
_head.setYaw(estimatedRotation.y * AVATAR_HEAD_YAW_MAGNIFY);
|
||||||
_head.setRoll(estimatedRotation.z * AVATAR_HEAD_ROLL_MAGNIFY);
|
_head.setRoll(estimatedRotation.z * AVATAR_HEAD_ROLL_MAGNIFY);
|
||||||
_head.setCameraFollowsHead(gyroLook);
|
|
||||||
|
|
||||||
// Update torso lean distance based on accelerometer data
|
// Update torso lean distance based on accelerometer data
|
||||||
const float TORSO_LENGTH = _scale * 0.5f;
|
const float TORSO_LENGTH = _scale * 0.5f;
|
||||||
|
@ -524,6 +525,50 @@ void MyAvatar::renderScreenTint(ScreenTintLayer layer, Camera& whichCamera) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MyAvatar::saveData(QSettings* settings) {
|
||||||
|
settings->beginGroup("Avatar");
|
||||||
|
|
||||||
|
settings->setValue("bodyYaw", _bodyYaw);
|
||||||
|
settings->setValue("bodyPitch", _bodyPitch);
|
||||||
|
settings->setValue("bodyRoll", _bodyRoll);
|
||||||
|
|
||||||
|
settings->setValue("position_x", _position.x);
|
||||||
|
settings->setValue("position_y", _position.y);
|
||||||
|
settings->setValue("position_z", _position.z);
|
||||||
|
|
||||||
|
settings->setValue("voxelURL", _voxels.getVoxelURL());
|
||||||
|
settings->setValue("pupilDilation", _head.getPupilDilation());
|
||||||
|
|
||||||
|
settings->setValue("leanScale", _leanScale);
|
||||||
|
settings->setValue("scale", _newScale);
|
||||||
|
|
||||||
|
settings->endGroup();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MyAvatar::loadData(QSettings* settings) {
|
||||||
|
settings->beginGroup("Avatar");
|
||||||
|
|
||||||
|
// in case settings is corrupt or missing loadSetting() will check for NaN
|
||||||
|
_bodyYaw = loadSetting(settings, "bodyYaw", 0.0f);
|
||||||
|
_bodyPitch = loadSetting(settings, "bodyPitch", 0.0f);
|
||||||
|
_bodyRoll = loadSetting(settings, "bodyRoll", 0.0f);
|
||||||
|
_position.x = loadSetting(settings, "position_x", 0.0f);
|
||||||
|
_position.y = loadSetting(settings, "position_y", 0.0f);
|
||||||
|
_position.z = loadSetting(settings, "position_z", 0.0f);
|
||||||
|
|
||||||
|
_voxels.setVoxelURL(settings->value("voxelURL").toUrl());
|
||||||
|
_head.setPupilDilation(settings->value("pupilDilation", 0.0f).toFloat());
|
||||||
|
|
||||||
|
_leanScale = loadSetting(settings, "leanScale", 0.05f);
|
||||||
|
|
||||||
|
_newScale = loadSetting(settings, "scale", 1.0f);
|
||||||
|
setScale(_scale);
|
||||||
|
Application::getInstance()->getCamera()->setScale(_scale);
|
||||||
|
|
||||||
|
settings->endGroup();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
float MyAvatar::getAbsoluteHeadYaw() const {
|
float MyAvatar::getAbsoluteHeadYaw() const {
|
||||||
return glm::yaw(_head.getOrientation());
|
return glm::yaw(_head.getOrientation());
|
||||||
}
|
}
|
||||||
|
@ -532,10 +577,10 @@ glm::vec3 MyAvatar::getUprightHeadPosition() const {
|
||||||
return _position + getWorldAlignedOrientation() * glm::vec3(0.0f, _pelvisToHeadLength, 0.0f);
|
return _position + getWorldAlignedOrientation() * glm::vec3(0.0f, _pelvisToHeadLength, 0.0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
glm::vec3 MyAvatar::getUprightEyeLevelPosition() const {
|
glm::vec3 MyAvatar::getEyeLevelPosition() const {
|
||||||
const float EYE_UP_OFFSET = 0.36f;
|
const float EYE_UP_OFFSET = 0.36f;
|
||||||
glm::vec3 up = getWorldAlignedOrientation() * IDENTITY_UP;
|
return _position + getWorldAlignedOrientation() * _skeleton.joint[AVATAR_JOINT_TORSO].rotation *
|
||||||
return _position + up * _scale * BODY_BALL_RADIUS_HEAD_BASE * EYE_UP_OFFSET + glm::vec3(0.0f, _pelvisToHeadLength, 0.0f);
|
glm::vec3(0.0f, _pelvisToHeadLength + _scale * BODY_BALL_RADIUS_HEAD_BASE * EYE_UP_OFFSET, 0.0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
float MyAvatar::getBallRenderAlpha(int ball, bool lookingInMirror) const {
|
float MyAvatar::getBallRenderAlpha(int ball, bool lookingInMirror) const {
|
||||||
|
@ -555,9 +600,6 @@ void MyAvatar::renderBody(bool lookingInMirror, bool renderAvatarBalls) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// glow when moving
|
|
||||||
Glower glower(_moving ? 1.0f : 0.0f);
|
|
||||||
|
|
||||||
if (_head.getFace().isFullFrame()) {
|
if (_head.getFace().isFullFrame()) {
|
||||||
// Render the full-frame video
|
// Render the full-frame video
|
||||||
float alpha = getBallRenderAlpha(BODY_BALL_HEAD_BASE, lookingInMirror);
|
float alpha = getBallRenderAlpha(BODY_BALL_HEAD_BASE, lookingInMirror);
|
||||||
|
@ -566,6 +608,13 @@ void MyAvatar::renderBody(bool lookingInMirror, bool renderAvatarBalls) {
|
||||||
}
|
}
|
||||||
} else if (renderAvatarBalls || !_voxels.getVoxelURL().isValid()) {
|
} else if (renderAvatarBalls || !_voxels.getVoxelURL().isValid()) {
|
||||||
// Render the body as balls and cones
|
// 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++) {
|
for (int b = 0; b < NUM_AVATAR_BODY_BALLS; b++) {
|
||||||
float alpha = getBallRenderAlpha(b, lookingInMirror);
|
float alpha = getBallRenderAlpha(b, lookingInMirror);
|
||||||
|
|
||||||
|
@ -586,14 +635,14 @@ void MyAvatar::renderBody(bool lookingInMirror, bool renderAvatarBalls) {
|
||||||
if (b == BODY_BALL_RIGHT_ELBOW
|
if (b == BODY_BALL_RIGHT_ELBOW
|
||||||
|| b == BODY_BALL_RIGHT_WRIST
|
|| b == BODY_BALL_RIGHT_WRIST
|
||||||
|| b == BODY_BALL_RIGHT_FINGERTIPS ) {
|
|| b == BODY_BALL_RIGHT_FINGERTIPS ) {
|
||||||
glColor3f(SKIN_COLOR[0] + _bodyBall[b].touchForce * 0.3f,
|
glColor3f(skinColor.r + _bodyBall[b].touchForce * 0.3f,
|
||||||
SKIN_COLOR[1] - _bodyBall[b].touchForce * 0.2f,
|
skinColor.g - _bodyBall[b].touchForce * 0.2f,
|
||||||
SKIN_COLOR[2] - _bodyBall[b].touchForce * 0.1f);
|
skinColor.b - _bodyBall[b].touchForce * 0.1f);
|
||||||
} else {
|
} else {
|
||||||
glColor4f(SKIN_COLOR[0] + _bodyBall[b].touchForce * 0.3f,
|
glColor4f(skinColor.r + _bodyBall[b].touchForce * 0.3f,
|
||||||
SKIN_COLOR[1] - _bodyBall[b].touchForce * 0.2f,
|
skinColor.g - _bodyBall[b].touchForce * 0.2f,
|
||||||
SKIN_COLOR[2] - _bodyBall[b].touchForce * 0.1f,
|
skinColor.b - _bodyBall[b].touchForce * 0.1f,
|
||||||
alpha);
|
alpha);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (b == BODY_BALL_NECK_BASE && _head.getBlendFace().isActive()) {
|
if (b == BODY_BALL_NECK_BASE && _head.getBlendFace().isActive()) {
|
||||||
|
@ -619,7 +668,7 @@ void MyAvatar::renderBody(bool lookingInMirror, bool renderAvatarBalls) {
|
||||||
&& (b != BODY_BALL_LEFT_SHOULDER)
|
&& (b != BODY_BALL_LEFT_SHOULDER)
|
||||||
&& (b != BODY_BALL_RIGHT_COLLAR)
|
&& (b != BODY_BALL_RIGHT_COLLAR)
|
||||||
&& (b != BODY_BALL_RIGHT_SHOULDER)) {
|
&& (b != BODY_BALL_RIGHT_SHOULDER)) {
|
||||||
glColor3fv(DARK_SKIN_COLOR);
|
glColor3fv((const GLfloat*)&darkSkinColor);
|
||||||
|
|
||||||
float r2 = _bodyBall[b].radius * 0.8;
|
float r2 = _bodyBall[b].radius * 0.8;
|
||||||
|
|
||||||
|
|
|
@ -9,6 +9,8 @@
|
||||||
#ifndef __interface__myavatar__
|
#ifndef __interface__myavatar__
|
||||||
#define __interface__myavatar__
|
#define __interface__myavatar__
|
||||||
|
|
||||||
|
#include <QSettings>
|
||||||
|
|
||||||
#include "Avatar.h"
|
#include "Avatar.h"
|
||||||
|
|
||||||
class MyAvatar : public Avatar {
|
class MyAvatar : public Avatar {
|
||||||
|
@ -17,7 +19,7 @@ public:
|
||||||
|
|
||||||
void reset();
|
void reset();
|
||||||
void simulate(float deltaTime, Transmitter* transmitter);
|
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 render(bool lookingInMirror, bool renderAvatarBalls);
|
||||||
void renderScreenTint(ScreenTintLayer layer, Camera& whichCamera);
|
void renderScreenTint(ScreenTintLayer layer, Camera& whichCamera);
|
||||||
|
|
||||||
|
@ -46,7 +48,11 @@ public:
|
||||||
Avatar* getLeadingAvatar() const { return _leadingAvatar; }
|
Avatar* getLeadingAvatar() const { return _leadingAvatar; }
|
||||||
glm::vec3 getGravity() const { return _gravity; }
|
glm::vec3 getGravity() const { return _gravity; }
|
||||||
glm::vec3 getUprightHeadPosition() const;
|
glm::vec3 getUprightHeadPosition() const;
|
||||||
glm::vec3 getUprightEyeLevelPosition() const;
|
glm::vec3 getEyeLevelPosition() const;
|
||||||
|
|
||||||
|
// get/set avatar data
|
||||||
|
void saveData(QSettings* settings);
|
||||||
|
void loadData(QSettings* settings);
|
||||||
|
|
||||||
// Set what driving keys are being pressed to control thrust levels
|
// Set what driving keys are being pressed to control thrust levels
|
||||||
void setDriveKeys(int key, bool val) { _driveKeys[key] = val; };
|
void setDriveKeys(int key, bool val) { _driveKeys[key] = val; };
|
||||||
|
@ -57,7 +63,7 @@ public:
|
||||||
void addThrust(glm::vec3 newThrust) { _thrust += newThrust; };
|
void addThrust(glm::vec3 newThrust) { _thrust += newThrust; };
|
||||||
glm::vec3 getThrust() { return _thrust; };
|
glm::vec3 getThrust() { return _thrust; };
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool _mousePressed;
|
bool _mousePressed;
|
||||||
float _bodyPitchDelta;
|
float _bodyPitchDelta;
|
||||||
float _bodyRollDelta;
|
float _bodyRollDelta;
|
||||||
|
|
106
interface/src/avatar/Profile.cpp
Normal file
106
interface/src/avatar/Profile.cpp
Normal file
|
@ -0,0 +1,106 @@
|
||||||
|
//
|
||||||
|
// Profile.cpp
|
||||||
|
// hifi
|
||||||
|
//
|
||||||
|
// Created by Stephen Birarda on 10/8/13.
|
||||||
|
// Copyright (c) 2013 HighFidelity, Inc. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include <QtCore/QSettings>
|
||||||
|
|
||||||
|
#include <UUID.h>
|
||||||
|
|
||||||
|
#include "Profile.h"
|
||||||
|
#include "DataServerClient.h"
|
||||||
|
|
||||||
|
Profile::Profile(const QString &username) :
|
||||||
|
_username(username),
|
||||||
|
_uuid(),
|
||||||
|
_lastDomain(),
|
||||||
|
_lastPosition(0.0, 0.0, 0.0),
|
||||||
|
_faceModelURL()
|
||||||
|
{
|
||||||
|
if (!_username.isEmpty()) {
|
||||||
|
// 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;
|
||||||
|
|
||||||
|
// when the UUID is changed we need set it appropriately on our avatar instance
|
||||||
|
Application::getInstance()->getAvatar()->setUUID(_uuid);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Profile::setFaceModelURL(const QUrl& faceModelURL) {
|
||||||
|
_faceModelURL = faceModelURL;
|
||||||
|
|
||||||
|
QMetaObject::invokeMethod(&Application::getInstance()->getAvatar()->getHead().getBlendFace(),
|
||||||
|
"setModelURL",
|
||||||
|
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");
|
||||||
|
|
||||||
|
settings->setValue("username", _username);
|
||||||
|
settings->setValue("UUID", _uuid);
|
||||||
|
settings->setValue("faceModelURL", _faceModelURL);
|
||||||
|
|
||||||
|
settings->endGroup();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Profile::loadData(QSettings* settings) {
|
||||||
|
settings->beginGroup("Profile");
|
||||||
|
|
||||||
|
_username = settings->value("username").toString();
|
||||||
|
this->setUUID(settings->value("UUID").toUuid());
|
||||||
|
_faceModelURL = settings->value("faceModelURL").toUrl();
|
||||||
|
|
||||||
|
settings->endGroup();
|
||||||
|
}
|
48
interface/src/avatar/Profile.h
Normal file
48
interface/src/avatar/Profile.h
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
//
|
||||||
|
// Profile.h
|
||||||
|
// hifi
|
||||||
|
//
|
||||||
|
// Created by Stephen Birarda on 10/8/13.
|
||||||
|
// Copyright (c) 2013 HighFidelity, Inc. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef __hifi__Profile__
|
||||||
|
#define __hifi__Profile__
|
||||||
|
|
||||||
|
#include <QtCore/QString>
|
||||||
|
#include <QtCore/QUrl>
|
||||||
|
#include <QtCore/QUuid>
|
||||||
|
|
||||||
|
#include <glm/glm.hpp>
|
||||||
|
|
||||||
|
class Profile {
|
||||||
|
public:
|
||||||
|
Profile(const QString& username);
|
||||||
|
|
||||||
|
QString getUserString() const;
|
||||||
|
|
||||||
|
const QString& getUsername() const { return _username; }
|
||||||
|
|
||||||
|
void setUUID(const QUuid& uuid);
|
||||||
|
const QUuid& getUUID() { return _uuid; }
|
||||||
|
|
||||||
|
void setFaceModelURL(const QUrl& faceModelURL);
|
||||||
|
const QUrl& getFaceModelURL() const { return _faceModelURL; }
|
||||||
|
|
||||||
|
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;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* defined(__hifi__Profile__) */
|
|
@ -19,12 +19,6 @@
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
FBXNode parseFBX(const QByteArray& data) {
|
|
||||||
QBuffer buffer(const_cast<QByteArray*>(&data));
|
|
||||||
buffer.open(QIODevice::ReadOnly);
|
|
||||||
return parseFBX(&buffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<class T> QVariant readArray(QDataStream& in) {
|
template<class T> QVariant readArray(QDataStream& in) {
|
||||||
quint32 arrayLength;
|
quint32 arrayLength;
|
||||||
quint32 encoding;
|
quint32 encoding;
|
||||||
|
@ -189,6 +183,41 @@ FBXNode parseFBX(QIODevice* device) {
|
||||||
return top;
|
return top;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QVariantHash parseMapping(QIODevice* device) {
|
||||||
|
QVariantHash properties;
|
||||||
|
|
||||||
|
QByteArray line;
|
||||||
|
while (!(line = device->readLine()).isEmpty()) {
|
||||||
|
if ((line = line.trimmed()).startsWith('#')) {
|
||||||
|
continue; // comment
|
||||||
|
}
|
||||||
|
QList<QByteArray> sections = line.split('=');
|
||||||
|
if (sections.size() < 2) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
QByteArray name = sections.at(0).trimmed();
|
||||||
|
if (sections.size() == 2) {
|
||||||
|
properties.insert(name, sections.at(1).trimmed());
|
||||||
|
|
||||||
|
} else if (sections.size() == 3) {
|
||||||
|
QVariantHash heading = properties.value(name).toHash();
|
||||||
|
heading.insert(sections.at(1).trimmed(), sections.at(2).trimmed());
|
||||||
|
properties.insert(name, heading);
|
||||||
|
|
||||||
|
} else if (sections.size() >= 4) {
|
||||||
|
QVariantHash heading = properties.value(name).toHash();
|
||||||
|
QVariantList contents;
|
||||||
|
for (int i = 2; i < sections.size(); i++) {
|
||||||
|
contents.append(sections.at(i).trimmed());
|
||||||
|
}
|
||||||
|
heading.insertMulti(sections.at(1).trimmed(), contents);
|
||||||
|
properties.insert(name, heading);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return properties;
|
||||||
|
}
|
||||||
|
|
||||||
QVector<glm::vec3> createVec3Vector(const QVector<double>& doubleVector) {
|
QVector<glm::vec3> createVec3Vector(const QVector<double>& doubleVector) {
|
||||||
QVector<glm::vec3> values;
|
QVector<glm::vec3> values;
|
||||||
for (const double* it = doubleVector.constData(), *end = it + doubleVector.size(); it != end; ) {
|
for (const double* it = doubleVector.constData(), *end = it + doubleVector.size(); it != end; ) {
|
||||||
|
@ -269,21 +298,9 @@ const char* FACESHIFT_BLENDSHAPES[] = {
|
||||||
""
|
""
|
||||||
};
|
};
|
||||||
|
|
||||||
QHash<QByteArray, int> createBlendshapeMap() {
|
|
||||||
QHash<QByteArray, int> map;
|
|
||||||
for (int i = 0;; i++) {
|
|
||||||
QByteArray name = FACESHIFT_BLENDSHAPES[i];
|
|
||||||
if (name != "") {
|
|
||||||
map.insert(name, i);
|
|
||||||
|
|
||||||
} else {
|
|
||||||
return map;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class Transform {
|
class Transform {
|
||||||
public:
|
public:
|
||||||
|
QByteArray name;
|
||||||
bool inheritScale;
|
bool inheritScale;
|
||||||
glm::mat4 withScale;
|
glm::mat4 withScale;
|
||||||
glm::mat4 withoutScale;
|
glm::mat4 withoutScale;
|
||||||
|
@ -315,11 +332,29 @@ glm::mat4 getGlobalTransform(const QMultiHash<qint64, qint64>& parentMap, const
|
||||||
class ExtractedBlendshape {
|
class ExtractedBlendshape {
|
||||||
public:
|
public:
|
||||||
qint64 id;
|
qint64 id;
|
||||||
int index;
|
|
||||||
FBXBlendshape blendshape;
|
FBXBlendshape blendshape;
|
||||||
};
|
};
|
||||||
|
|
||||||
FBXGeometry extractFBXGeometry(const FBXNode& node) {
|
void printNode(const FBXNode& node, int indent) {
|
||||||
|
QByteArray spaces(indent, ' ');
|
||||||
|
qDebug("%s%s: ", spaces.data(), node.name.data());
|
||||||
|
foreach (const QVariant& property, node.properties) {
|
||||||
|
qDebug() << property;
|
||||||
|
}
|
||||||
|
qDebug() << "\n";
|
||||||
|
foreach (const FBXNode& child, node.children) {
|
||||||
|
printNode(child, indent + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Material {
|
||||||
|
public:
|
||||||
|
glm::vec3 diffuse;
|
||||||
|
glm::vec3 specular;
|
||||||
|
float shininess;
|
||||||
|
};
|
||||||
|
|
||||||
|
FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping) {
|
||||||
QHash<qint64, FBXMesh> meshes;
|
QHash<qint64, FBXMesh> meshes;
|
||||||
QVector<ExtractedBlendshape> blendshapes;
|
QVector<ExtractedBlendshape> blendshapes;
|
||||||
QMultiHash<qint64, qint64> parentMap;
|
QMultiHash<qint64, qint64> parentMap;
|
||||||
|
@ -327,12 +362,38 @@ FBXGeometry extractFBXGeometry(const FBXNode& node) {
|
||||||
QHash<qint64, Transform> localTransforms;
|
QHash<qint64, Transform> localTransforms;
|
||||||
QHash<qint64, glm::mat4> transformLinkMatrices;
|
QHash<qint64, glm::mat4> transformLinkMatrices;
|
||||||
QHash<qint64, QByteArray> textureFilenames;
|
QHash<qint64, QByteArray> textureFilenames;
|
||||||
|
QHash<qint64, Material> materials;
|
||||||
QHash<qint64, qint64> diffuseTextures;
|
QHash<qint64, qint64> diffuseTextures;
|
||||||
QHash<qint64, qint64> bumpTextures;
|
QHash<qint64, qint64> bumpTextures;
|
||||||
|
|
||||||
|
QVariantHash joints = mapping.value("joint").toHash();
|
||||||
|
QByteArray jointEyeLeftName = joints.value("jointEyeLeft", "jointEyeLeft").toByteArray();
|
||||||
|
QByteArray jointEyeRightName = joints.value("jointEyeRight", "jointEyeRight").toByteArray();
|
||||||
|
QByteArray jointNeckName = joints.value("jointNeck", "jointNeck").toByteArray();
|
||||||
qint64 jointEyeLeftID = 0;
|
qint64 jointEyeLeftID = 0;
|
||||||
qint64 jointEyeRightID = 0;
|
qint64 jointEyeRightID = 0;
|
||||||
qint64 jointNeckID = 0;
|
qint64 jointNeckID = 0;
|
||||||
|
|
||||||
|
QVariantHash blendshapeMappings = mapping.value("bs").toHash();
|
||||||
|
QHash<QByteArray, QPair<int, float> > blendshapeIndices;
|
||||||
|
for (int i = 0;; i++) {
|
||||||
|
QByteArray blendshapeName = FACESHIFT_BLENDSHAPES[i];
|
||||||
|
if (blendshapeName.isEmpty()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
QList<QVariant> mappings = blendshapeMappings.values(blendshapeName);
|
||||||
|
if (mappings.isEmpty()) {
|
||||||
|
blendshapeIndices.insert(blendshapeName, QPair<int, float>(i, 1.0f));
|
||||||
|
} else {
|
||||||
|
foreach (const QVariant& mapping, mappings) {
|
||||||
|
QVariantList blendshapeMapping = mapping.toList();
|
||||||
|
blendshapeIndices.insert(blendshapeMapping.at(0).toByteArray(),
|
||||||
|
QPair<int, float>(i, blendshapeMapping.at(1).toFloat()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
QHash<qint64, QPair<int, float> > blendshapeChannelIndices;
|
||||||
|
|
||||||
foreach (const FBXNode& child, node.children) {
|
foreach (const FBXNode& child, node.children) {
|
||||||
if (child.name == "Objects") {
|
if (child.name == "Objects") {
|
||||||
foreach (const FBXNode& object, child.children) {
|
foreach (const FBXNode& object, child.children) {
|
||||||
|
@ -342,6 +403,7 @@ FBXGeometry extractFBXGeometry(const FBXNode& node) {
|
||||||
|
|
||||||
QVector<int> polygonIndices;
|
QVector<int> polygonIndices;
|
||||||
QVector<glm::vec3> normals;
|
QVector<glm::vec3> normals;
|
||||||
|
QVector<int> normalIndices;
|
||||||
QVector<glm::vec2> texCoords;
|
QVector<glm::vec2> texCoords;
|
||||||
QVector<int> texCoordIndices;
|
QVector<int> texCoordIndices;
|
||||||
foreach (const FBXNode& data, object.children) {
|
foreach (const FBXNode& data, object.children) {
|
||||||
|
@ -357,6 +419,9 @@ FBXGeometry extractFBXGeometry(const FBXNode& node) {
|
||||||
if (subdata.name == "Normals") {
|
if (subdata.name == "Normals") {
|
||||||
normals = createVec3Vector(subdata.properties.at(0).value<QVector<double> >());
|
normals = createVec3Vector(subdata.properties.at(0).value<QVector<double> >());
|
||||||
|
|
||||||
|
} else if (subdata.name == "NormalsIndex") {
|
||||||
|
normalIndices = subdata.properties.at(0).value<QVector<int> >();
|
||||||
|
|
||||||
} else if (subdata.name == "MappingInformationType" &&
|
} else if (subdata.name == "MappingInformationType" &&
|
||||||
subdata.properties.at(0) == "ByVertice") {
|
subdata.properties.at(0) == "ByVertice") {
|
||||||
byVertex = true;
|
byVertex = true;
|
||||||
|
@ -380,21 +445,33 @@ FBXGeometry extractFBXGeometry(const FBXNode& node) {
|
||||||
// convert normals from per-index to per-vertex if necessary
|
// convert normals from per-index to per-vertex if necessary
|
||||||
if (mesh.normals.isEmpty()) {
|
if (mesh.normals.isEmpty()) {
|
||||||
mesh.normals.resize(mesh.vertices.size());
|
mesh.normals.resize(mesh.vertices.size());
|
||||||
for (int i = 0, n = polygonIndices.size(); i < n; i++) {
|
if (normalIndices.isEmpty()) {
|
||||||
int index = polygonIndices.at(i);
|
for (int i = 0, n = polygonIndices.size(); i < n; i++) {
|
||||||
mesh.normals[index < 0 ? (-index - 1) : index] = normals.at(i);
|
int index = polygonIndices.at(i);
|
||||||
}
|
mesh.normals[index < 0 ? (-index - 1) : index] = normals.at(i);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (int i = 0, n = polygonIndices.size(); i < n; i++) {
|
||||||
|
int index = polygonIndices.at(i);
|
||||||
|
int normalIndex = normalIndices.at(i);
|
||||||
|
if (normalIndex >= 0) {
|
||||||
|
mesh.normals[index < 0 ? (-index - 1) : index] = normals.at(normalIndex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// same with the tex coords
|
// same with the tex coords
|
||||||
mesh.texCoords.resize(mesh.vertices.size());
|
if (!texCoordIndices.isEmpty()) {
|
||||||
for (int i = 0, n = polygonIndices.size(); i < n; i++) {
|
mesh.texCoords.resize(mesh.vertices.size());
|
||||||
int index = polygonIndices.at(i);
|
for (int i = 0, n = polygonIndices.size(); i < n; i++) {
|
||||||
int texCoordIndex = texCoordIndices.at(i);
|
int index = polygonIndices.at(i);
|
||||||
if (texCoordIndex >= 0) {
|
int texCoordIndex = texCoordIndices.at(i);
|
||||||
mesh.texCoords[index < 0 ? (-index - 1) : index] = texCoords.at(texCoordIndex);
|
if (texCoordIndex >= 0) {
|
||||||
}
|
mesh.texCoords[index < 0 ? (-index - 1) : index] = texCoords.at(texCoordIndex);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// convert the polygons to quads and triangles
|
// convert the polygons to quads and triangles
|
||||||
for (const int* beginIndex = polygonIndices.constData(), *end = beginIndex + polygonIndices.size();
|
for (const int* beginIndex = polygonIndices.constData(), *end = beginIndex + polygonIndices.size();
|
||||||
|
@ -441,29 +518,25 @@ FBXGeometry extractFBXGeometry(const FBXNode& node) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// the name is followed by a null and some type info
|
|
||||||
QByteArray name = object.properties.at(1).toByteArray();
|
|
||||||
static QHash<QByteArray, int> blendshapeMap = createBlendshapeMap();
|
|
||||||
extracted.index = blendshapeMap.value(name.left(name.indexOf('\0')));
|
|
||||||
|
|
||||||
blendshapes.append(extracted);
|
blendshapes.append(extracted);
|
||||||
}
|
}
|
||||||
} else if (object.name == "Model") {
|
} else if (object.name == "Model") {
|
||||||
QByteArray name = object.properties.at(1).toByteArray();
|
QByteArray name = object.properties.at(1).toByteArray();
|
||||||
if (name.startsWith("jointEyeLeft") || name.startsWith("EyeL") || name.startsWith("joint_Leye")) {
|
name = name.left(name.indexOf('\0'));
|
||||||
|
if (name == jointEyeLeftName || name == "EyeL" || name == "joint_Leye") {
|
||||||
jointEyeLeftID = object.properties.at(0).value<qint64>();
|
jointEyeLeftID = object.properties.at(0).value<qint64>();
|
||||||
|
|
||||||
} else if (name.startsWith("jointEyeRight") || name.startsWith("EyeR") || name.startsWith("joint_Reye")) {
|
} else if (name == jointEyeRightName || name == "EyeR" || name == "joint_Reye") {
|
||||||
jointEyeRightID = object.properties.at(0).value<qint64>();
|
jointEyeRightID = object.properties.at(0).value<qint64>();
|
||||||
|
|
||||||
} else if (name.startsWith("jointNeck") || name.startsWith("NeckRot") || name.startsWith("joint_neck")) {
|
} else if (name == jointNeckName || name == "NeckRot" || name == "joint_neck") {
|
||||||
jointNeckID = object.properties.at(0).value<qint64>();
|
jointNeckID = object.properties.at(0).value<qint64>();
|
||||||
}
|
}
|
||||||
glm::vec3 translation;
|
glm::vec3 translation;
|
||||||
glm::vec3 preRotation, rotation, postRotation;
|
glm::vec3 preRotation, rotation, postRotation;
|
||||||
glm::vec3 scale = glm::vec3(1.0f, 1.0f, 1.0f);
|
glm::vec3 scale = glm::vec3(1.0f, 1.0f, 1.0f);
|
||||||
glm::vec3 scalePivot, rotationPivot;
|
glm::vec3 scalePivot, rotationPivot;
|
||||||
Transform transform = { true };
|
Transform transform = { name, true };
|
||||||
foreach (const FBXNode& subobject, object.children) {
|
foreach (const FBXNode& subobject, object.children) {
|
||||||
if (subobject.name == "Properties70") {
|
if (subobject.name == "Properties70") {
|
||||||
foreach (const FBXNode& property, subobject.children) {
|
foreach (const FBXNode& property, subobject.children) {
|
||||||
|
@ -522,17 +595,55 @@ FBXGeometry extractFBXGeometry(const FBXNode& node) {
|
||||||
} else if (object.name == "Texture") {
|
} else if (object.name == "Texture") {
|
||||||
foreach (const FBXNode& subobject, object.children) {
|
foreach (const FBXNode& subobject, object.children) {
|
||||||
if (subobject.name == "RelativeFilename") {
|
if (subobject.name == "RelativeFilename") {
|
||||||
textureFilenames.insert(object.properties.at(0).value<qint64>(),
|
// trim off any path information
|
||||||
subobject.properties.at(0).toByteArray());
|
QByteArray filename = subobject.properties.at(0).toByteArray();
|
||||||
|
filename = filename.mid(qMax(filename.lastIndexOf('\\'), filename.lastIndexOf('/')) + 1);
|
||||||
|
textureFilenames.insert(object.properties.at(0).value<qint64>(), filename);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (object.name == "Deformer" && object.properties.at(2) == "Cluster") {
|
} else if (object.name == "Material") {
|
||||||
|
Material material = { glm::vec3(1.0f, 1.0f, 1.0f), glm::vec3(1.0f, 1.0f, 1.0f), 96.0f };
|
||||||
foreach (const FBXNode& subobject, object.children) {
|
foreach (const FBXNode& subobject, object.children) {
|
||||||
if (subobject.name == "TransformLink") {
|
if (subobject.name == "Properties70") {
|
||||||
QVector<double> values = subobject.properties.at(0).value<QVector<double> >();
|
foreach (const FBXNode& property, subobject.children) {
|
||||||
transformLinkMatrices.insert(object.properties.at(0).value<qint64>(), createMat4(values));
|
if (property.name == "P") {
|
||||||
|
if (property.properties.at(0) == "DiffuseColor") {
|
||||||
|
material.diffuse = glm::vec3(property.properties.at(4).value<double>(),
|
||||||
|
property.properties.at(5).value<double>(),
|
||||||
|
property.properties.at(6).value<double>());
|
||||||
|
|
||||||
|
} else if (property.properties.at(0) == "SpecularColor") {
|
||||||
|
material.specular = glm::vec3(property.properties.at(4).value<double>(),
|
||||||
|
property.properties.at(5).value<double>(),
|
||||||
|
property.properties.at(6).value<double>());
|
||||||
|
|
||||||
|
} else if (property.properties.at(0) == "Shininess") {
|
||||||
|
material.shininess = property.properties.at(4).value<double>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
materials.insert(object.properties.at(0).value<qint64>(), material);
|
||||||
|
|
||||||
|
} else if (object.name == "Deformer") {
|
||||||
|
if (object.properties.at(2) == "Cluster") {
|
||||||
|
foreach (const FBXNode& subobject, object.children) {
|
||||||
|
if (subobject.name == "TransformLink") {
|
||||||
|
QVector<double> values = subobject.properties.at(0).value<QVector<double> >();
|
||||||
|
transformLinkMatrices.insert(object.properties.at(0).value<qint64>(), createMat4(values));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (object.properties.at(2) == "BlendShapeChannel") {
|
||||||
|
QByteArray name = object.properties.at(1).toByteArray();
|
||||||
|
name = name.left(name.indexOf('\0'));
|
||||||
|
if (!blendshapeIndices.contains(name)) {
|
||||||
|
// try everything after the dot
|
||||||
|
name = name.mid(name.lastIndexOf('.') + 1);
|
||||||
|
}
|
||||||
|
blendshapeChannelIndices.insert(object.properties.at(0).value<qint64>(),
|
||||||
|
blendshapeIndices.value(name));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (child.name == "Connections") {
|
} else if (child.name == "Connections") {
|
||||||
|
@ -558,25 +669,41 @@ FBXGeometry extractFBXGeometry(const FBXNode& node) {
|
||||||
// assign the blendshapes to their corresponding meshes
|
// assign the blendshapes to their corresponding meshes
|
||||||
foreach (const ExtractedBlendshape& extracted, blendshapes) {
|
foreach (const ExtractedBlendshape& extracted, blendshapes) {
|
||||||
qint64 blendshapeChannelID = parentMap.value(extracted.id);
|
qint64 blendshapeChannelID = parentMap.value(extracted.id);
|
||||||
|
QPair<int, float> index = blendshapeChannelIndices.value(blendshapeChannelID);
|
||||||
qint64 blendshapeID = parentMap.value(blendshapeChannelID);
|
qint64 blendshapeID = parentMap.value(blendshapeChannelID);
|
||||||
qint64 meshID = parentMap.value(blendshapeID);
|
qint64 meshID = parentMap.value(blendshapeID);
|
||||||
FBXMesh& mesh = meshes[meshID];
|
FBXMesh& mesh = meshes[meshID];
|
||||||
mesh.blendshapes.resize(max(mesh.blendshapes.size(), extracted.index + 1));
|
mesh.blendshapes.resize(max(mesh.blendshapes.size(), index.first + 1));
|
||||||
mesh.blendshapes[extracted.index] = extracted.blendshape;
|
mesh.blendshapes[index.first] = extracted.blendshape;
|
||||||
}
|
}
|
||||||
|
|
||||||
// as a temporary hack, put the mesh with the most blendshapes on top; assume it to be the face
|
// get offset transform from mapping
|
||||||
|
float offsetScale = mapping.value("scale", 1.0f).toFloat();
|
||||||
|
glm::mat4 offset = glm::translate(mapping.value("tx").toFloat(), mapping.value("ty").toFloat(),
|
||||||
|
mapping.value("tz").toFloat()) * glm::mat4_cast(glm::quat(glm::radians(glm::vec3(mapping.value("rx").toFloat(),
|
||||||
|
mapping.value("ry").toFloat(), mapping.value("rz").toFloat())))) *
|
||||||
|
glm::scale(offsetScale, offsetScale, offsetScale);
|
||||||
|
|
||||||
FBXGeometry geometry;
|
FBXGeometry geometry;
|
||||||
int mostBlendshapes = 0;
|
QVariantHash springs = mapping.value("spring").toHash();
|
||||||
|
QVariant defaultSpring = springs.value("default");
|
||||||
for (QHash<qint64, FBXMesh>::iterator it = meshes.begin(); it != meshes.end(); it++) {
|
for (QHash<qint64, FBXMesh>::iterator it = meshes.begin(); it != meshes.end(); it++) {
|
||||||
FBXMesh& mesh = it.value();
|
FBXMesh& mesh = it.value();
|
||||||
|
|
||||||
// accumulate local transforms
|
// accumulate local transforms
|
||||||
qint64 modelID = parentMap.value(it.key());
|
qint64 modelID = parentMap.value(it.key());
|
||||||
|
mesh.springiness = springs.value(localTransforms.value(modelID).name, defaultSpring).toFloat();
|
||||||
glm::mat4 modelTransform = getGlobalTransform(parentMap, localTransforms, modelID);
|
glm::mat4 modelTransform = getGlobalTransform(parentMap, localTransforms, modelID);
|
||||||
|
|
||||||
// look for textures
|
// look for textures, material properties
|
||||||
foreach (qint64 childID, childMap.values(modelID)) {
|
foreach (qint64 childID, childMap.values(modelID)) {
|
||||||
|
if (!materials.contains(childID)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
Material material = materials.value(childID);
|
||||||
|
mesh.diffuseColor = material.diffuse;
|
||||||
|
mesh.specularColor = material.specular;
|
||||||
|
mesh.shininess = material.shininess;
|
||||||
qint64 diffuseTextureID = diffuseTextures.value(childID);
|
qint64 diffuseTextureID = diffuseTextures.value(childID);
|
||||||
if (diffuseTextureID != 0) {
|
if (diffuseTextureID != 0) {
|
||||||
mesh.diffuseFilename = textureFilenames.value(diffuseTextureID);
|
mesh.diffuseFilename = textureFilenames.value(diffuseTextureID);
|
||||||
|
@ -601,40 +728,72 @@ FBXGeometry extractFBXGeometry(const FBXNode& node) {
|
||||||
|
|
||||||
// see http://stackoverflow.com/questions/13566608/loading-skinning-information-from-fbx for a discussion
|
// see http://stackoverflow.com/questions/13566608/loading-skinning-information-from-fbx for a discussion
|
||||||
// of skinning information in FBX
|
// of skinning information in FBX
|
||||||
glm::mat4 jointTransform = getGlobalTransform(parentMap, localTransforms, jointID);
|
glm::mat4 jointTransform = offset * getGlobalTransform(parentMap, localTransforms, jointID);
|
||||||
mesh.transform = jointTransform * glm::inverse(transformLinkMatrices.value(clusterID)) * modelTransform;
|
mesh.transform = jointTransform * glm::inverse(transformLinkMatrices.value(clusterID)) * modelTransform;
|
||||||
|
|
||||||
// extract translation component for pivot
|
// extract translation component for pivot
|
||||||
glm::mat4 jointTransformScaled = getGlobalTransform(parentMap, localTransforms, jointID, true);
|
glm::mat4 jointTransformScaled = offset * getGlobalTransform(parentMap, localTransforms, jointID, true);
|
||||||
mesh.pivot = glm::vec3(jointTransformScaled[3][0], jointTransformScaled[3][1], jointTransformScaled[3][2]);
|
mesh.pivot = glm::vec3(jointTransformScaled[3][0], jointTransformScaled[3][1], jointTransformScaled[3][2]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mesh.blendshapes.size() > mostBlendshapes) {
|
// extract spring edges, connections if springy
|
||||||
geometry.meshes.prepend(mesh);
|
if (mesh.springiness > 0.0f) {
|
||||||
mostBlendshapes = mesh.blendshapes.size();
|
QSet<QPair<int, int> > edges;
|
||||||
|
|
||||||
} else {
|
mesh.vertexConnections.resize(mesh.vertices.size());
|
||||||
geometry.meshes.append(mesh);
|
for (int i = 0; i < mesh.quadIndices.size(); i += 4) {
|
||||||
|
int index0 = mesh.quadIndices.at(i);
|
||||||
|
int index1 = mesh.quadIndices.at(i + 1);
|
||||||
|
int index2 = mesh.quadIndices.at(i + 2);
|
||||||
|
int index3 = mesh.quadIndices.at(i + 3);
|
||||||
|
|
||||||
|
edges.insert(QPair<int, int>(qMin(index0, index1), qMax(index0, index1)));
|
||||||
|
edges.insert(QPair<int, int>(qMin(index1, index2), qMax(index1, index2)));
|
||||||
|
edges.insert(QPair<int, int>(qMin(index2, index3), qMax(index2, index3)));
|
||||||
|
edges.insert(QPair<int, int>(qMin(index3, index0), qMax(index3, index0)));
|
||||||
|
|
||||||
|
mesh.vertexConnections[index0].append(QPair<int, int>(index3, index1));
|
||||||
|
mesh.vertexConnections[index1].append(QPair<int, int>(index0, index2));
|
||||||
|
mesh.vertexConnections[index2].append(QPair<int, int>(index1, index3));
|
||||||
|
mesh.vertexConnections[index3].append(QPair<int, int>(index2, index0));
|
||||||
|
}
|
||||||
|
for (int i = 0; i < mesh.triangleIndices.size(); i += 3) {
|
||||||
|
int index0 = mesh.triangleIndices.at(i);
|
||||||
|
int index1 = mesh.triangleIndices.at(i + 1);
|
||||||
|
int index2 = mesh.triangleIndices.at(i + 2);
|
||||||
|
|
||||||
|
edges.insert(QPair<int, int>(qMin(index0, index1), qMax(index0, index1)));
|
||||||
|
edges.insert(QPair<int, int>(qMin(index1, index2), qMax(index1, index2)));
|
||||||
|
edges.insert(QPair<int, int>(qMin(index2, index0), qMax(index2, index0)));
|
||||||
|
|
||||||
|
mesh.vertexConnections[index0].append(QPair<int, int>(index2, index1));
|
||||||
|
mesh.vertexConnections[index1].append(QPair<int, int>(index0, index2));
|
||||||
|
mesh.vertexConnections[index2].append(QPair<int, int>(index1, index0));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (QSet<QPair<int, int> >::const_iterator edge = edges.constBegin(); edge != edges.constEnd(); edge++) {
|
||||||
|
mesh.springEdges.append(*edge);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
geometry.meshes.append(mesh);
|
||||||
}
|
}
|
||||||
|
|
||||||
// extract translation component for neck pivot
|
// extract translation component for neck pivot
|
||||||
glm::mat4 neckTransform = getGlobalTransform(parentMap, localTransforms, jointNeckID, true);
|
glm::mat4 neckTransform = offset * getGlobalTransform(parentMap, localTransforms, jointNeckID, true);
|
||||||
geometry.neckPivot = glm::vec3(neckTransform[3][0], neckTransform[3][1], neckTransform[3][2]);
|
geometry.neckPivot = glm::vec3(neckTransform[3][0], neckTransform[3][1], neckTransform[3][2]);
|
||||||
|
|
||||||
return geometry;
|
return geometry;
|
||||||
}
|
}
|
||||||
|
|
||||||
void printNode(const FBXNode& node, int indent) {
|
FBXGeometry readFBX(const QByteArray& model, const QByteArray& mapping) {
|
||||||
QByteArray spaces(indent, ' ');
|
QBuffer modelBuffer(const_cast<QByteArray*>(&model));
|
||||||
qDebug("%s%s: ", spaces.data(), node.name.data());
|
modelBuffer.open(QIODevice::ReadOnly);
|
||||||
foreach (const QVariant& property, node.properties) {
|
|
||||||
qDebug() << property;
|
QBuffer mappingBuffer(const_cast<QByteArray*>(&mapping));
|
||||||
}
|
mappingBuffer.open(QIODevice::ReadOnly);
|
||||||
qDebug() << "\n";
|
|
||||||
foreach (const FBXNode& child, node.children) {
|
return extractFBXGeometry(parseFBX(&modelBuffer), parseMapping(&mappingBuffer));
|
||||||
printNode(child, indent + 1);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -9,13 +9,12 @@
|
||||||
#ifndef __interface__FBXReader__
|
#ifndef __interface__FBXReader__
|
||||||
#define __interface__FBXReader__
|
#define __interface__FBXReader__
|
||||||
|
|
||||||
|
#include <QVarLengthArray>
|
||||||
#include <QVariant>
|
#include <QVariant>
|
||||||
#include <QVector>
|
#include <QVector>
|
||||||
|
|
||||||
#include <glm/glm.hpp>
|
#include <glm/glm.hpp>
|
||||||
|
|
||||||
class QIODevice;
|
|
||||||
|
|
||||||
class FBXNode;
|
class FBXNode;
|
||||||
|
|
||||||
typedef QList<FBXNode> FBXNodeList;
|
typedef QList<FBXNode> FBXNodeList;
|
||||||
|
@ -53,10 +52,18 @@ public:
|
||||||
|
|
||||||
bool isEye;
|
bool isEye;
|
||||||
|
|
||||||
|
glm::vec3 diffuseColor;
|
||||||
|
glm::vec3 specularColor;
|
||||||
|
float shininess;
|
||||||
|
|
||||||
QByteArray diffuseFilename;
|
QByteArray diffuseFilename;
|
||||||
QByteArray normalFilename;
|
QByteArray normalFilename;
|
||||||
|
|
||||||
QVector<FBXBlendshape> blendshapes;
|
QVector<FBXBlendshape> blendshapes;
|
||||||
|
|
||||||
|
float springiness;
|
||||||
|
QVector<QPair<int, int> > springEdges;
|
||||||
|
QVector<QVarLengthArray<QPair<int, int>, 4> > vertexConnections;
|
||||||
};
|
};
|
||||||
|
|
||||||
/// A set of meshes extracted from an FBX document.
|
/// A set of meshes extracted from an FBX document.
|
||||||
|
@ -68,17 +75,8 @@ public:
|
||||||
glm::vec3 neckPivot;
|
glm::vec3 neckPivot;
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Parses the input from the supplied data as an FBX file.
|
/// Reads FBX geometry from the supplied model and mapping data.
|
||||||
/// \exception QString if an error occurs in parsing
|
/// \exception QString if an error occurs in parsing
|
||||||
FBXNode parseFBX(const QByteArray& data);
|
FBXGeometry readFBX(const QByteArray& model, const QByteArray& mapping);
|
||||||
|
|
||||||
/// Parses the input from the supplied device as an FBX file.
|
|
||||||
/// \exception QString if an error occurs in parsing
|
|
||||||
FBXNode parseFBX(QIODevice* device);
|
|
||||||
|
|
||||||
/// Extracts the geometry from a parsed FBX node.
|
|
||||||
FBXGeometry extractFBXGeometry(const FBXNode& node);
|
|
||||||
|
|
||||||
void printNode(const FBXNode& node, int indent = 0);
|
|
||||||
|
|
||||||
#endif /* defined(__interface__FBXReader__) */
|
#endif /* defined(__interface__FBXReader__) */
|
||||||
|
|
|
@ -250,21 +250,37 @@ QSharedPointer<NetworkGeometry> GeometryCache::getGeometry(const QUrl& url) {
|
||||||
return geometry;
|
return geometry;
|
||||||
}
|
}
|
||||||
|
|
||||||
NetworkGeometry::NetworkGeometry(const QUrl& url) : _reply(NULL) {
|
NetworkGeometry::NetworkGeometry(const QUrl& url) :
|
||||||
|
_modelReply(NULL),
|
||||||
|
_mappingReply(NULL)
|
||||||
|
{
|
||||||
if (!url.isValid()) {
|
if (!url.isValid()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
QNetworkRequest request(url);
|
QNetworkRequest modelRequest(url);
|
||||||
request.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferCache);
|
modelRequest.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferCache);
|
||||||
_reply = Application::getInstance()->getNetworkAccessManager()->get(request);
|
_modelReply = Application::getInstance()->getNetworkAccessManager()->get(modelRequest);
|
||||||
|
|
||||||
connect(_reply, SIGNAL(downloadProgress(qint64,qint64)), SLOT(handleDownloadProgress(qint64,qint64)));
|
connect(_modelReply, SIGNAL(downloadProgress(qint64,qint64)), SLOT(maybeReadModelWithMapping()));
|
||||||
connect(_reply, SIGNAL(error(QNetworkReply::NetworkError)), SLOT(handleReplyError()));
|
connect(_modelReply, SIGNAL(error(QNetworkReply::NetworkError)), SLOT(handleModelReplyError()));
|
||||||
|
|
||||||
|
QUrl mappingURL = url;
|
||||||
|
QString path = url.path();
|
||||||
|
mappingURL.setPath(path.left(path.lastIndexOf('.')) + ".fst");
|
||||||
|
QNetworkRequest mappingRequest(mappingURL);
|
||||||
|
mappingRequest.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferCache);
|
||||||
|
_mappingReply = Application::getInstance()->getNetworkAccessManager()->get(mappingRequest);
|
||||||
|
|
||||||
|
connect(_mappingReply, SIGNAL(downloadProgress(qint64,qint64)), SLOT(maybeReadModelWithMapping()));
|
||||||
|
connect(_mappingReply, SIGNAL(error(QNetworkReply::NetworkError)), SLOT(handleMappingReplyError()));
|
||||||
}
|
}
|
||||||
|
|
||||||
NetworkGeometry::~NetworkGeometry() {
|
NetworkGeometry::~NetworkGeometry() {
|
||||||
if (_reply != NULL) {
|
if (_modelReply != NULL) {
|
||||||
delete _reply;
|
delete _modelReply;
|
||||||
|
}
|
||||||
|
if (_mappingReply != NULL) {
|
||||||
|
delete _mappingReply;
|
||||||
}
|
}
|
||||||
foreach (const NetworkMesh& mesh, _meshes) {
|
foreach (const NetworkMesh& mesh, _meshes) {
|
||||||
glDeleteBuffers(1, &mesh.indexBufferID);
|
glDeleteBuffers(1, &mesh.indexBufferID);
|
||||||
|
@ -272,19 +288,60 @@ NetworkGeometry::~NetworkGeometry() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void NetworkGeometry::handleDownloadProgress(qint64 bytesReceived, qint64 bytesTotal) {
|
glm::vec4 NetworkGeometry::computeAverageColor() const {
|
||||||
if (bytesReceived < bytesTotal && !_reply->isFinished()) {
|
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";
|
||||||
|
|
||||||
|
_modelReply->disconnect(this);
|
||||||
|
_modelReply->deleteLater();
|
||||||
|
_modelReply = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void NetworkGeometry::handleMappingReplyError() {
|
||||||
|
_mappingReply->disconnect(this);
|
||||||
|
_mappingReply->deleteLater();
|
||||||
|
_mappingReply = NULL;
|
||||||
|
|
||||||
|
maybeReadModelWithMapping();
|
||||||
|
}
|
||||||
|
|
||||||
|
void NetworkGeometry::maybeReadModelWithMapping() {
|
||||||
|
if (_modelReply == NULL || !_modelReply->isFinished() || (_mappingReply != NULL && !_mappingReply->isFinished())) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
QUrl url = _reply->url();
|
QUrl url = _modelReply->url();
|
||||||
QByteArray entirety = _reply->readAll();
|
QByteArray model = _modelReply->readAll();
|
||||||
_reply->disconnect(this);
|
_modelReply->disconnect(this);
|
||||||
_reply->deleteLater();
|
_modelReply->deleteLater();
|
||||||
_reply = NULL;
|
_modelReply = NULL;
|
||||||
|
|
||||||
|
QByteArray mapping;
|
||||||
|
if (_mappingReply != NULL) {
|
||||||
|
mapping = _mappingReply->readAll();
|
||||||
|
_mappingReply->disconnect(this);
|
||||||
|
_mappingReply->deleteLater();
|
||||||
|
_mappingReply = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
_geometry = extractFBXGeometry(parseFBX(entirety));
|
_geometry = readFBX(model, mapping);
|
||||||
|
|
||||||
} catch (const QString& error) {
|
} catch (const QString& error) {
|
||||||
qDebug() << "Error reading " << url << ": " << error << "\n";
|
qDebug() << "Error reading " << url << ": " << error << "\n";
|
||||||
|
@ -306,7 +363,7 @@ void NetworkGeometry::handleDownloadProgress(qint64 bytesReceived, qint64 bytesT
|
||||||
glGenBuffers(1, &networkMesh.vertexBufferID);
|
glGenBuffers(1, &networkMesh.vertexBufferID);
|
||||||
glBindBuffer(GL_ARRAY_BUFFER, networkMesh.vertexBufferID);
|
glBindBuffer(GL_ARRAY_BUFFER, networkMesh.vertexBufferID);
|
||||||
|
|
||||||
if (mesh.blendshapes.isEmpty()) {
|
if (mesh.blendshapes.isEmpty() && mesh.springiness == 0.0f) {
|
||||||
glBufferData(GL_ARRAY_BUFFER, (mesh.vertices.size() + mesh.normals.size()) * sizeof(glm::vec3) +
|
glBufferData(GL_ARRAY_BUFFER, (mesh.vertices.size() + mesh.normals.size()) * sizeof(glm::vec3) +
|
||||||
mesh.texCoords.size() * sizeof(glm::vec2), NULL, GL_STATIC_DRAW);
|
mesh.texCoords.size() * sizeof(glm::vec2), NULL, GL_STATIC_DRAW);
|
||||||
glBufferSubData(GL_ARRAY_BUFFER, 0, mesh.vertices.size() * sizeof(glm::vec3), mesh.vertices.constData());
|
glBufferSubData(GL_ARRAY_BUFFER, 0, mesh.vertices.size() * sizeof(glm::vec3), mesh.vertices.constData());
|
||||||
|
@ -323,10 +380,7 @@ void NetworkGeometry::handleDownloadProgress(qint64 bytesReceived, qint64 bytesT
|
||||||
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||||
|
|
||||||
QString basePath = url.path();
|
QString basePath = url.path();
|
||||||
int idx = basePath.lastIndexOf('/');
|
basePath = basePath.left(basePath.lastIndexOf('/') + 1);
|
||||||
if (idx != -1) {
|
|
||||||
basePath = basePath.left(idx);
|
|
||||||
}
|
|
||||||
if (!mesh.diffuseFilename.isEmpty()) {
|
if (!mesh.diffuseFilename.isEmpty()) {
|
||||||
url.setPath(basePath + mesh.diffuseFilename);
|
url.setPath(basePath + mesh.diffuseFilename);
|
||||||
networkMesh.diffuseTexture = Application::getInstance()->getTextureCache()->getTexture(url, mesh.isEye);
|
networkMesh.diffuseTexture = Application::getInstance()->getTextureCache()->getTexture(url, mesh.isEye);
|
||||||
|
@ -338,11 +392,3 @@ void NetworkGeometry::handleDownloadProgress(qint64 bytesReceived, qint64 bytesT
|
||||||
_meshes.append(networkMesh);
|
_meshes.append(networkMesh);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void NetworkGeometry::handleReplyError() {
|
|
||||||
qDebug() << _reply->errorString() << "\n";
|
|
||||||
|
|
||||||
_reply->disconnect(this);
|
|
||||||
_reply->deleteLater();
|
|
||||||
_reply = NULL;
|
|
||||||
}
|
|
||||||
|
|
|
@ -62,14 +62,19 @@ public:
|
||||||
const FBXGeometry& getFBXGeometry() const { return _geometry; }
|
const FBXGeometry& getFBXGeometry() const { return _geometry; }
|
||||||
const QVector<NetworkMesh>& getMeshes() const { return _meshes; }
|
const QVector<NetworkMesh>& getMeshes() const { return _meshes; }
|
||||||
|
|
||||||
|
/// Returns the average color of all meshes in the geometry.
|
||||||
|
glm::vec4 computeAverageColor() const;
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
|
|
||||||
void handleDownloadProgress(qint64 bytesReceived, qint64 bytesTotal);
|
void handleModelReplyError();
|
||||||
void handleReplyError();
|
void handleMappingReplyError();
|
||||||
|
void maybeReadModelWithMapping();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
QNetworkReply* _reply;
|
QNetworkReply* _modelReply;
|
||||||
|
QNetworkReply* _mappingReply;
|
||||||
|
|
||||||
FBXGeometry _geometry;
|
FBXGeometry _geometry;
|
||||||
QVector<NetworkMesh> _meshes;
|
QVector<NetworkMesh> _meshes;
|
||||||
|
|
|
@ -173,7 +173,7 @@ Texture::~Texture() {
|
||||||
glDeleteTextures(1, &_id);
|
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()) {
|
if (!url.isValid()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -206,6 +206,21 @@ void NetworkTexture::handleDownloadProgress(qint64 bytesReceived, qint64 bytesTo
|
||||||
_reply = NULL;
|
_reply = NULL;
|
||||||
|
|
||||||
QImage image = QImage::fromData(entirety).convertToFormat(QImage::Format_ARGB32);
|
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);
|
imageLoaded(image);
|
||||||
glBindTexture(GL_TEXTURE_2D, getID());
|
glBindTexture(GL_TEXTURE_2D, getID());
|
||||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, image.width(), image.height(), 1,
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, image.width(), image.height(), 1,
|
||||||
|
|
|
@ -99,6 +99,9 @@ public:
|
||||||
NetworkTexture(const QUrl& url);
|
NetworkTexture(const QUrl& url);
|
||||||
~NetworkTexture();
|
~NetworkTexture();
|
||||||
|
|
||||||
|
/// Returns the average color over the entire texture.
|
||||||
|
const glm::vec4& getAverageColor() const { return _averageColor; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
virtual void imageLoaded(const QImage& image);
|
virtual void imageLoaded(const QImage& image);
|
||||||
|
@ -111,6 +114,7 @@ private slots:
|
||||||
private:
|
private:
|
||||||
|
|
||||||
QNetworkReply* _reply;
|
QNetworkReply* _reply;
|
||||||
|
glm::vec4 _averageColor;
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Caches derived, dilated textures.
|
/// Caches derived, dilated textures.
|
||||||
|
|
|
@ -4,9 +4,7 @@
|
||||||
//
|
//
|
||||||
// Created by Philip Rosedale on 8/23/12.
|
// Created by Philip Rosedale on 8/23/12.
|
||||||
// Copyright (c) 2012 High Fidelity, Inc. All rights reserved.
|
// Copyright (c) 2012 High Fidelity, Inc. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
// Simulation happens in positive cube with edge of size WORLD_SIZE
|
|
||||||
|
|
||||||
#ifndef __interface__world__
|
#ifndef __interface__world__
|
||||||
#define __interface__world__
|
#define __interface__world__
|
||||||
|
|
|
@ -23,6 +23,7 @@ static const float fingerVectorRadix = 4; // bits of precision when converting f
|
||||||
|
|
||||||
AvatarData::AvatarData(Node* owningNode) :
|
AvatarData::AvatarData(Node* owningNode) :
|
||||||
NodeData(owningNode),
|
NodeData(owningNode),
|
||||||
|
_uuid(),
|
||||||
_handPosition(0,0,0),
|
_handPosition(0,0,0),
|
||||||
_bodyYaw(-90.0),
|
_bodyYaw(-90.0),
|
||||||
_bodyPitch(0.0),
|
_bodyPitch(0.0),
|
||||||
|
@ -116,6 +117,11 @@ int AvatarData::getBroadcastData(unsigned char* destinationBuffer) {
|
||||||
_handData = new HandData(this);
|
_handData = new HandData(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UUID
|
||||||
|
QByteArray uuidByteArray = _uuid.toRfc4122();
|
||||||
|
memcpy(destinationBuffer, uuidByteArray.constData(), uuidByteArray.size());
|
||||||
|
destinationBuffer += uuidByteArray.size();
|
||||||
|
|
||||||
// Body world position
|
// Body world position
|
||||||
memcpy(destinationBuffer, &_position, sizeof(float) * 3);
|
memcpy(destinationBuffer, &_position, sizeof(float) * 3);
|
||||||
destinationBuffer += sizeof(float) * 3;
|
destinationBuffer += sizeof(float) * 3;
|
||||||
|
@ -249,6 +255,11 @@ int AvatarData::parseData(unsigned char* sourceBuffer, int numBytes) {
|
||||||
// push past the node ID
|
// push past the node ID
|
||||||
sourceBuffer += sizeof(uint16_t);
|
sourceBuffer += sizeof(uint16_t);
|
||||||
|
|
||||||
|
// UUID
|
||||||
|
const int NUM_BYTES_RFC4122_UUID = 16;
|
||||||
|
_uuid = QUuid::fromRfc4122(QByteArray((char*) sourceBuffer, NUM_BYTES_RFC4122_UUID));
|
||||||
|
sourceBuffer += NUM_BYTES_RFC4122_UUID;
|
||||||
|
|
||||||
// Body world position
|
// Body world position
|
||||||
memcpy(&_position, sourceBuffer, sizeof(float) * 3);
|
memcpy(&_position, sourceBuffer, sizeof(float) * 3);
|
||||||
sourceBuffer += sizeof(float) * 3;
|
sourceBuffer += sizeof(float) * 3;
|
||||||
|
@ -259,7 +270,7 @@ int AvatarData::parseData(unsigned char* sourceBuffer, int numBytes) {
|
||||||
sourceBuffer += unpackFloatAngleFromTwoByte((uint16_t*) sourceBuffer, &_bodyRoll);
|
sourceBuffer += unpackFloatAngleFromTwoByte((uint16_t*) sourceBuffer, &_bodyRoll);
|
||||||
|
|
||||||
// Body scale
|
// Body scale
|
||||||
sourceBuffer += unpackFloatRatioFromTwoByte( sourceBuffer, _newScale);
|
sourceBuffer += unpackFloatRatioFromTwoByte(sourceBuffer, _newScale);
|
||||||
|
|
||||||
// Follow mode info
|
// Follow mode info
|
||||||
memcpy(&_leaderID, sourceBuffer, sizeof(uint16_t));
|
memcpy(&_leaderID, sourceBuffer, sizeof(uint16_t));
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
#include <glm/gtc/quaternion.hpp>
|
#include <glm/gtc/quaternion.hpp>
|
||||||
|
|
||||||
#include <QtCore/QObject>
|
#include <QtCore/QObject>
|
||||||
|
#include <QtCore/QUuid>
|
||||||
#include <QtCore/QVariantMap>
|
#include <QtCore/QVariantMap>
|
||||||
|
|
||||||
#include <NodeData.h>
|
#include <NodeData.h>
|
||||||
|
@ -72,6 +73,9 @@ public:
|
||||||
int getBroadcastData(unsigned char* destinationBuffer);
|
int getBroadcastData(unsigned char* destinationBuffer);
|
||||||
int parseData(unsigned char* sourceBuffer, int numBytes);
|
int parseData(unsigned char* sourceBuffer, int numBytes);
|
||||||
|
|
||||||
|
QUuid& getUUID() { return _uuid; }
|
||||||
|
void setUUID(const QUuid& uuid) { _uuid = uuid; }
|
||||||
|
|
||||||
// Body Rotation
|
// Body Rotation
|
||||||
float getBodyYaw() const { return _bodyYaw; }
|
float getBodyYaw() const { return _bodyYaw; }
|
||||||
void setBodyYaw(float bodyYaw) { _bodyYaw = bodyYaw; }
|
void setBodyYaw(float bodyYaw) { _bodyYaw = bodyYaw; }
|
||||||
|
@ -79,7 +83,6 @@ public:
|
||||||
void setBodyPitch(float bodyPitch) { _bodyPitch = bodyPitch; }
|
void setBodyPitch(float bodyPitch) { _bodyPitch = bodyPitch; }
|
||||||
float getBodyRoll() const { return _bodyRoll; }
|
float getBodyRoll() const { return _bodyRoll; }
|
||||||
void setBodyRoll(float bodyRoll) { _bodyRoll = bodyRoll; }
|
void setBodyRoll(float bodyRoll) { _bodyRoll = bodyRoll; }
|
||||||
|
|
||||||
|
|
||||||
// Hand State
|
// Hand State
|
||||||
void setHandState(char s) { _handState = s; }
|
void setHandState(char s) { _handState = s; }
|
||||||
|
@ -133,6 +136,8 @@ public slots:
|
||||||
void setWantOcclusionCulling(bool wantOcclusionCulling) { _wantOcclusionCulling = wantOcclusionCulling; }
|
void setWantOcclusionCulling(bool wantOcclusionCulling) { _wantOcclusionCulling = wantOcclusionCulling; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
QUuid _uuid;
|
||||||
|
|
||||||
glm::vec3 _position;
|
glm::vec3 _position;
|
||||||
glm::vec3 _handPosition;
|
glm::vec3 _handPosition;
|
||||||
|
|
||||||
|
|
|
@ -139,10 +139,6 @@ void Assignment::setPayload(const uchar* payload, int numBytes) {
|
||||||
memcpy(_payload, payload, _numPayloadBytes);
|
memcpy(_payload, payload, _numPayloadBytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
QString Assignment::getUUIDStringWithoutCurlyBraces() const {
|
|
||||||
return _uuid.toString().mid(1, _uuid.toString().length() - 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
int Assignment::packToBuffer(unsigned char* buffer) {
|
int Assignment::packToBuffer(unsigned char* buffer) {
|
||||||
int numPackedBytes = 0;
|
int numPackedBytes = 0;
|
||||||
|
|
||||||
|
|
|
@ -60,7 +60,6 @@ public:
|
||||||
|
|
||||||
void setUUID(const QUuid& uuid) { _uuid = uuid; }
|
void setUUID(const QUuid& uuid) { _uuid = uuid; }
|
||||||
const QUuid& getUUID() const { return _uuid; }
|
const QUuid& getUUID() const { return _uuid; }
|
||||||
QString getUUIDStringWithoutCurlyBraces() const;
|
|
||||||
void resetUUID() { _uuid = QUuid::createUuid(); }
|
void resetUUID() { _uuid = QUuid::createUuid(); }
|
||||||
|
|
||||||
Assignment::Command getCommand() const { return _command; }
|
Assignment::Command getCommand() const { return _command; }
|
||||||
|
|
|
@ -61,7 +61,11 @@ Node::Node(sockaddr* publicSocket, sockaddr* localSocket, char type, uint16_t no
|
||||||
Node::~Node() {
|
Node::~Node() {
|
||||||
delete _publicSocket;
|
delete _publicSocket;
|
||||||
delete _localSocket;
|
delete _localSocket;
|
||||||
delete _linkedData;
|
|
||||||
|
if (_linkedData) {
|
||||||
|
_linkedData->deleteLater();
|
||||||
|
}
|
||||||
|
|
||||||
delete _bytesReceivedMovingAverage;
|
delete _bytesReceivedMovingAverage;
|
||||||
|
|
||||||
pthread_mutex_destroy(&_mutex);
|
pthread_mutex_destroy(&_mutex);
|
||||||
|
|
|
@ -14,4 +14,6 @@ NodeData::NodeData(Node* owningNode) :
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
NodeData::~NodeData() {}
|
NodeData::~NodeData() {
|
||||||
|
|
||||||
|
}
|
|
@ -88,28 +88,35 @@ NodeList::~NodeList() {
|
||||||
|
|
||||||
void NodeList::setDomainHostname(const QString& domainHostname) {
|
void NodeList::setDomainHostname(const QString& domainHostname) {
|
||||||
|
|
||||||
int colonIndex = domainHostname.indexOf(':');
|
if (domainHostname != _domainHostname) {
|
||||||
|
int colonIndex = domainHostname.indexOf(':');
|
||||||
if (colonIndex > 0) {
|
|
||||||
// the user has included a custom DS port with the hostname
|
|
||||||
|
|
||||||
// the new hostname is everything up to the colon
|
if (colonIndex > 0) {
|
||||||
_domainHostname = domainHostname.left(colonIndex);
|
// the user has included a custom DS port with the hostname
|
||||||
|
|
||||||
|
// the new hostname is everything up to the colon
|
||||||
|
_domainHostname = domainHostname.left(colonIndex);
|
||||||
|
|
||||||
|
// grab the port by reading the string after the colon
|
||||||
|
_domainPort = atoi(domainHostname.mid(colonIndex + 1, domainHostname.size()).toLocal8Bit().constData());
|
||||||
|
|
||||||
|
qDebug() << "Updated hostname to" << _domainHostname << "and port to" << _domainPort << "\n";
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// no port included with the hostname, simply set the member variable and reset the domain server port to default
|
||||||
|
_domainHostname = domainHostname;
|
||||||
|
_domainPort = DEFAULT_DOMAIN_SERVER_PORT;
|
||||||
|
}
|
||||||
|
|
||||||
// grab the port by reading the string after the colon
|
// clear the NodeList so nodes from this domain are killed
|
||||||
_domainPort = atoi(domainHostname.mid(colonIndex + 1, domainHostname.size()).toLocal8Bit().constData());
|
clear();
|
||||||
|
|
||||||
qDebug() << "Updated hostname to" << _domainHostname << "and port to" << _domainPort << "\n";
|
// reset our _domainIP to the null address so that a lookup happens on next check in
|
||||||
|
_domainIP.clear();
|
||||||
} else {
|
notifyDomainChanged();
|
||||||
// no port included with the hostname, simply set the member variable and reset the domain server port to default
|
|
||||||
_domainHostname = domainHostname;
|
|
||||||
_domainPort = DEFAULT_DOMAIN_SERVER_PORT;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// reset our _domainIP to the null address so that a lookup happens on next check in
|
|
||||||
_domainIP.clear();
|
|
||||||
notifyDomainChanged();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void NodeList::timePingReply(sockaddr *nodeAddress, unsigned char *packetData) {
|
void NodeList::timePingReply(sockaddr *nodeAddress, unsigned char *packetData) {
|
||||||
|
@ -428,6 +435,7 @@ void NodeList::sendAssignment(Assignment& assignment) {
|
||||||
}
|
}
|
||||||
|
|
||||||
Node* NodeList::addOrUpdateNode(sockaddr* publicSocket, sockaddr* localSocket, char nodeType, uint16_t nodeId) {
|
Node* NodeList::addOrUpdateNode(sockaddr* publicSocket, sockaddr* localSocket, char nodeType, uint16_t nodeId) {
|
||||||
|
|
||||||
NodeList::iterator node = end();
|
NodeList::iterator node = end();
|
||||||
|
|
||||||
if (publicSocket) {
|
if (publicSocket) {
|
||||||
|
@ -439,7 +447,7 @@ Node* NodeList::addOrUpdateNode(sockaddr* publicSocket, sockaddr* localSocket, c
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (node == end()) {
|
if (node == end()) {
|
||||||
// we didn't have this node, so add them
|
// we didn't have this node, so add them
|
||||||
Node* newNode = new Node(publicSocket, localSocket, nodeType, nodeId);
|
Node* newNode = new Node(publicSocket, localSocket, nodeType, nodeId);
|
||||||
|
|
||||||
|
@ -532,15 +540,16 @@ Node* NodeList::soloNodeOfType(char nodeType) {
|
||||||
|
|
||||||
void* removeSilentNodes(void *args) {
|
void* removeSilentNodes(void *args) {
|
||||||
NodeList* nodeList = (NodeList*) args;
|
NodeList* nodeList = (NodeList*) args;
|
||||||
uint64_t checkTimeUSecs;
|
uint64_t checkTimeUsecs = usecTimestampNow();
|
||||||
int sleepTime;
|
int sleepTime;
|
||||||
|
|
||||||
while (!silentNodeThreadStopFlag) {
|
while (!silentNodeThreadStopFlag) {
|
||||||
checkTimeUSecs = usecTimestampNow();
|
|
||||||
|
|
||||||
for(NodeList::iterator node = nodeList->begin(); node != nodeList->end(); ++node) {
|
for(NodeList::iterator node = nodeList->begin(); node != nodeList->end(); ++node) {
|
||||||
|
|
||||||
if ((checkTimeUSecs - node->getLastHeardMicrostamp()) > NODE_SILENCE_THRESHOLD_USECS) {
|
node->lock();
|
||||||
|
|
||||||
|
if ((usecTimestampNow() - node->getLastHeardMicrostamp()) > NODE_SILENCE_THRESHOLD_USECS) {
|
||||||
|
|
||||||
qDebug() << "Killed " << *node << "\n";
|
qDebug() << "Killed " << *node << "\n";
|
||||||
|
|
||||||
|
@ -548,9 +557,11 @@ void* removeSilentNodes(void *args) {
|
||||||
|
|
||||||
node->setAlive(false);
|
node->setAlive(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
node->unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
sleepTime = NODE_SILENCE_THRESHOLD_USECS - (usecTimestampNow() - checkTimeUSecs);
|
sleepTime = NODE_SILENCE_THRESHOLD_USECS - (usecTimestampNow() - checkTimeUsecs);
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
Sleep( static_cast<int>(1000.0f*sleepTime) );
|
Sleep( static_cast<int>(1000.0f*sleepTime) );
|
||||||
#else
|
#else
|
||||||
|
|
|
@ -30,7 +30,7 @@ const int NODES_PER_BUCKET = 100;
|
||||||
|
|
||||||
const int MAX_PACKET_SIZE = 1500;
|
const int MAX_PACKET_SIZE = 1500;
|
||||||
|
|
||||||
const int NODE_SILENCE_THRESHOLD_USECS = 2 * 1000000;
|
const uint64_t NODE_SILENCE_THRESHOLD_USECS = 2 * 1000000;
|
||||||
const int DOMAIN_SERVER_CHECK_IN_USECS = 1 * 1000000;
|
const int DOMAIN_SERVER_CHECK_IN_USECS = 1 * 1000000;
|
||||||
|
|
||||||
extern const char SOLO_NODE_TYPES[2];
|
extern const char SOLO_NODE_TYPES[2];
|
||||||
|
|
|
@ -20,7 +20,7 @@ PACKET_VERSION versionForPacketType(PACKET_TYPE type) {
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
case PACKET_TYPE_HEAD_DATA:
|
case PACKET_TYPE_HEAD_DATA:
|
||||||
return 8;
|
return 9;
|
||||||
|
|
||||||
case PACKET_TYPE_AVATAR_URLS:
|
case PACKET_TYPE_AVATAR_URLS:
|
||||||
return 1;
|
return 1;
|
||||||
|
|
|
@ -41,6 +41,10 @@ const PACKET_TYPE PACKET_TYPE_DEPLOY_ASSIGNMENT = 'd';
|
||||||
const PACKET_TYPE PACKET_TYPE_VOXEL_STATS = '#';
|
const PACKET_TYPE PACKET_TYPE_VOXEL_STATS = '#';
|
||||||
const PACKET_TYPE PACKET_TYPE_VOXEL_JURISDICTION = 'J';
|
const PACKET_TYPE PACKET_TYPE_VOXEL_JURISDICTION = 'J';
|
||||||
const PACKET_TYPE PACKET_TYPE_VOXEL_JURISDICTION_REQUEST = 'j';
|
const PACKET_TYPE PACKET_TYPE_VOXEL_JURISDICTION_REQUEST = 'j';
|
||||||
|
const PACKET_TYPE PACKET_TYPE_DATA_SERVER_PUT = 'p';
|
||||||
|
const PACKET_TYPE PACKET_TYPE_DATA_SERVER_GET = 'g';
|
||||||
|
const PACKET_TYPE PACKET_TYPE_DATA_SERVER_SEND = 'u';
|
||||||
|
const PACKET_TYPE PACKET_TYPE_DATA_SERVER_CONFIRM = 'c';
|
||||||
|
|
||||||
typedef char PACKET_VERSION;
|
typedef char PACKET_VERSION;
|
||||||
|
|
||||||
|
|
|
@ -82,17 +82,11 @@ bool PacketSender::process() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool keepGoing = _packets.size() > 0;
|
int packetsLeft = _packets.size();
|
||||||
|
bool keepGoing = packetsLeft > 0;
|
||||||
while (keepGoing) {
|
while (keepGoing) {
|
||||||
|
uint64_t SEND_INTERVAL_USECS = (_packetsPerSecond == 0) ? USECS_PER_SECOND : (USECS_PER_SECOND / _packetsPerSecond);
|
||||||
// in threaded mode, we go till we're empty
|
|
||||||
if (isThreaded()) {
|
|
||||||
keepGoing = _packets.size() > 0;
|
|
||||||
} else {
|
|
||||||
// in non-threaded mode, we send as many packets as we need per expected call to process()
|
|
||||||
keepGoing = (packetsThisCall < packetsPerCall) && (_packets.size() > 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
NetworkPacket& packet = _packets.front();
|
NetworkPacket& packet = _packets.front();
|
||||||
|
|
||||||
// send the packet through the NodeList...
|
// send the packet through the NodeList...
|
||||||
|
@ -100,7 +94,7 @@ bool PacketSender::process() {
|
||||||
|
|
||||||
nodeSocket->send(&packet.getAddress(), packet.getData(), packet.getLength());
|
nodeSocket->send(&packet.getAddress(), packet.getData(), packet.getLength());
|
||||||
packetsThisCall++;
|
packetsThisCall++;
|
||||||
|
|
||||||
if (_notify) {
|
if (_notify) {
|
||||||
_notify->packetSentNotification(packet.getLength());
|
_notify->packetSentNotification(packet.getLength());
|
||||||
}
|
}
|
||||||
|
@ -109,16 +103,32 @@ bool PacketSender::process() {
|
||||||
_packets.erase(_packets.begin());
|
_packets.erase(_packets.begin());
|
||||||
unlock();
|
unlock();
|
||||||
|
|
||||||
// dynamically sleep until we need to fire off the next set of voxels we only sleep in threaded mode
|
packetsLeft = _packets.size();
|
||||||
|
|
||||||
|
// in threaded mode, we go till we're empty
|
||||||
if (isThreaded()) {
|
if (isThreaded()) {
|
||||||
uint64_t elapsed = now - _lastSendTime;
|
keepGoing = packetsLeft > 0;
|
||||||
int usecToSleep = std::max(SEND_INTERVAL_USECS, SEND_INTERVAL_USECS - elapsed);
|
|
||||||
|
// dynamically sleep until we need to fire off the next set of voxels we only sleep in threaded mode
|
||||||
// we only sleep in non-threaded mode
|
if (keepGoing) {
|
||||||
if (usecToSleep > 0) {
|
now = usecTimestampNow();
|
||||||
usleep(usecToSleep);
|
uint64_t elapsed = now - _lastSendTime;
|
||||||
|
int usecToSleep = SEND_INTERVAL_USECS - elapsed;
|
||||||
|
|
||||||
|
// we only sleep in non-threaded mode
|
||||||
|
if (usecToSleep > 0) {
|
||||||
|
if (usecToSleep > SEND_INTERVAL_USECS) {
|
||||||
|
usecToSleep = SEND_INTERVAL_USECS;
|
||||||
|
}
|
||||||
|
usleep(usecToSleep);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// in non-threaded mode, we send as many packets as we need per expected call to process()
|
||||||
|
keepGoing = (packetsThisCall < packetsPerCall) && (packetsLeft > 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
_lastSendTime = now;
|
_lastSendTime = now;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -36,7 +36,7 @@ public:
|
||||||
/// \thread any thread, typically the application thread
|
/// \thread any thread, typically the application thread
|
||||||
void queuePacketForSending(sockaddr& address, unsigned char* packetData, ssize_t packetLength);
|
void queuePacketForSending(sockaddr& address, unsigned char* packetData, ssize_t packetLength);
|
||||||
|
|
||||||
void setPacketsPerSecond(int packetsPerSecond) { _packetsPerSecond = std::min(MINIMUM_PACKETS_PER_SECOND, packetsPerSecond); }
|
void setPacketsPerSecond(int packetsPerSecond) { _packetsPerSecond = std::max(MINIMUM_PACKETS_PER_SECOND, packetsPerSecond); }
|
||||||
int getPacketsPerSecond() const { return _packetsPerSecond; }
|
int getPacketsPerSecond() const { return _packetsPerSecond; }
|
||||||
|
|
||||||
void setPacketSenderNotify(PacketSenderNotify* notify) { _notify = notify; }
|
void setPacketSenderNotify(PacketSenderNotify* notify) { _notify = notify; }
|
||||||
|
|
|
@ -103,6 +103,7 @@ int PerfStat::DumpStats(char** array) {
|
||||||
return lineCount;
|
return lineCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool PerformanceWarning::_suppressShortTimings = false;
|
||||||
|
|
||||||
// Destructor handles recording all of our stats
|
// Destructor handles recording all of our stats
|
||||||
PerformanceWarning::~PerformanceWarning() {
|
PerformanceWarning::~PerformanceWarning() {
|
||||||
|
@ -111,9 +112,17 @@ PerformanceWarning::~PerformanceWarning() {
|
||||||
if ((_alwaysDisplay || _renderWarningsOn) && elapsedmsec > 1) {
|
if ((_alwaysDisplay || _renderWarningsOn) && elapsedmsec > 1) {
|
||||||
if (elapsedmsec > 1000) {
|
if (elapsedmsec > 1000) {
|
||||||
double elapsedsec = (end - _start) / 1000000.0;
|
double elapsedsec = (end - _start) / 1000000.0;
|
||||||
qDebug("%s%s took %lf seconds\n", (_alwaysDisplay ? "" : "WARNING!"), _message, elapsedsec);
|
qDebug("%s took %lf seconds %s\n", _message, elapsedsec, (_alwaysDisplay ? "" : "WARNING!") );
|
||||||
} else {
|
} else {
|
||||||
qDebug("%s%s took %lf milliseconds\n", (_alwaysDisplay ? "" : "WARNING!"), _message, elapsedmsec);
|
if (_suppressShortTimings) {
|
||||||
|
if (elapsedmsec > 10) {
|
||||||
|
qDebug("%s took %lf milliseconds %s\n", _message, elapsedmsec,
|
||||||
|
(_alwaysDisplay || (elapsedmsec < 10) ? "" : "WARNING!"));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
qDebug("%s took %lf milliseconds %s\n", _message, elapsedmsec,
|
||||||
|
(_alwaysDisplay || (elapsedmsec < 10) ? "" : "WARNING!"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else if (_alwaysDisplay) {
|
} else if (_alwaysDisplay) {
|
||||||
qDebug("%s took %lf milliseconds\n", _message, elapsedmsec);
|
qDebug("%s took %lf milliseconds\n", _message, elapsedmsec);
|
||||||
|
|
|
@ -89,6 +89,7 @@ private:
|
||||||
const char* _message;
|
const char* _message;
|
||||||
bool _renderWarningsOn;
|
bool _renderWarningsOn;
|
||||||
bool _alwaysDisplay;
|
bool _alwaysDisplay;
|
||||||
|
static bool _suppressShortTimings;
|
||||||
public:
|
public:
|
||||||
PerformanceWarning(bool renderWarnings, const char* message, bool alwaysDisplay = false) :
|
PerformanceWarning(bool renderWarnings, const char* message, bool alwaysDisplay = false) :
|
||||||
_start(usecTimestampNow()),
|
_start(usecTimestampNow()),
|
||||||
|
@ -97,6 +98,8 @@ public:
|
||||||
_alwaysDisplay(alwaysDisplay) { }
|
_alwaysDisplay(alwaysDisplay) { }
|
||||||
|
|
||||||
~PerformanceWarning();
|
~PerformanceWarning();
|
||||||
|
|
||||||
|
static void setSuppressShortTimings(bool suppressShortTimings) { _suppressShortTimings = suppressShortTimings; }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -43,7 +43,7 @@ bool socketMatch(const sockaddr* first, const sockaddr* second) {
|
||||||
const sockaddr_in *secondIn = (const sockaddr_in *) second;
|
const sockaddr_in *secondIn = (const sockaddr_in *) second;
|
||||||
|
|
||||||
return firstIn->sin_addr.s_addr == secondIn->sin_addr.s_addr
|
return firstIn->sin_addr.s_addr == secondIn->sin_addr.s_addr
|
||||||
&& firstIn->sin_port == secondIn->sin_port;
|
&& firstIn->sin_port == secondIn->sin_port;
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -254,7 +254,7 @@ bool UDPSocket::receive(sockaddr* recvAddress, void* receivedData, ssize_t* rece
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
int addressSize = sizeof(*recvAddress);
|
int addressSize = sizeof(*recvAddress);
|
||||||
#else
|
#else
|
||||||
socklen_t addressSize = sizeof(&recvAddress);
|
socklen_t addressSize = sizeof(*recvAddress);
|
||||||
#endif
|
#endif
|
||||||
*receivedBytes = recvfrom(handle, static_cast<char*>(receivedData), MAX_BUFFER_LENGTH_BYTES,
|
*receivedBytes = recvfrom(handle, static_cast<char*>(receivedData), MAX_BUFFER_LENGTH_BYTES,
|
||||||
0, recvAddress, &addressSize);
|
0, recvAddress, &addressSize);
|
||||||
|
|
14
libraries/shared/src/UUID.cpp
Normal file
14
libraries/shared/src/UUID.cpp
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
//
|
||||||
|
// UUID.cpp
|
||||||
|
// hifi
|
||||||
|
//
|
||||||
|
// Created by Stephen Birarda on 10/7/13.
|
||||||
|
// Copyright (c) 2013 HighFidelity, Inc. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "UUID.h"
|
||||||
|
|
||||||
|
QString uuidStringWithoutCurlyBraces(const QUuid& uuid) {
|
||||||
|
QString uuidStringNoBraces = uuid.toString().mid(1, uuid.toString().length() - 2);
|
||||||
|
return uuidStringNoBraces;
|
||||||
|
}
|
16
libraries/shared/src/UUID.h
Normal file
16
libraries/shared/src/UUID.h
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
//
|
||||||
|
// UUID.h
|
||||||
|
// hifi
|
||||||
|
//
|
||||||
|
// Created by Stephen Birarda on 10/7/13.
|
||||||
|
// Copyright (c) 2013 HighFidelity, Inc. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef __hifi__UUID__
|
||||||
|
#define __hifi__UUID__
|
||||||
|
|
||||||
|
#include <QtCore/QUuid>
|
||||||
|
|
||||||
|
QString uuidStringWithoutCurlyBraces(const QUuid& uuid);
|
||||||
|
|
||||||
|
#endif /* defined(__hifi__UUID__) */
|
|
@ -74,7 +74,10 @@ bool VoxelNodeData::updateCurrentViewFrustum() {
|
||||||
newestViewFrustum.setOrientation(getCameraOrientation());
|
newestViewFrustum.setOrientation(getCameraOrientation());
|
||||||
|
|
||||||
// Also make sure it's got the correct lens details from the camera
|
// Also make sure it's got the correct lens details from the camera
|
||||||
newestViewFrustum.setFieldOfView(getCameraFov());
|
float originalFOV = getCameraFov();
|
||||||
|
float wideFOV = originalFOV + VIEW_FRUSTUM_FOV_OVERSEND;
|
||||||
|
|
||||||
|
newestViewFrustum.setFieldOfView(wideFOV); // hack
|
||||||
newestViewFrustum.setAspectRatio(getCameraAspectRatio());
|
newestViewFrustum.setAspectRatio(getCameraAspectRatio());
|
||||||
newestViewFrustum.setNearClip(getCameraNearClip());
|
newestViewFrustum.setNearClip(getCameraNearClip());
|
||||||
newestViewFrustum.setFarClip(getCameraFarClip());
|
newestViewFrustum.setFarClip(getCameraFarClip());
|
||||||
|
|
|
@ -295,19 +295,6 @@ void VoxelServer::run() {
|
||||||
}
|
}
|
||||||
qDebug("packetsPerSecond=%s PACKETS_PER_CLIENT_PER_INTERVAL=%d\n", packetsPerSecond, _packetsPerClientPerInterval);
|
qDebug("packetsPerSecond=%s PACKETS_PER_CLIENT_PER_INTERVAL=%d\n", packetsPerSecond, _packetsPerClientPerInterval);
|
||||||
}
|
}
|
||||||
|
|
||||||
// for now, initialize the environments with fixed values
|
|
||||||
_environmentData[1].setID(1);
|
|
||||||
_environmentData[1].setGravity(1.0f);
|
|
||||||
_environmentData[1].setAtmosphereCenter(glm::vec3(0.5, 0.5, (0.25 - 0.06125)) * (float)TREE_SCALE);
|
|
||||||
_environmentData[1].setAtmosphereInnerRadius(0.030625f * TREE_SCALE);
|
|
||||||
_environmentData[1].setAtmosphereOuterRadius(0.030625f * TREE_SCALE * 1.05f);
|
|
||||||
_environmentData[2].setID(2);
|
|
||||||
_environmentData[2].setGravity(1.0f);
|
|
||||||
_environmentData[2].setAtmosphereCenter(glm::vec3(0.5f, 0.5f, 0.5f) * (float)TREE_SCALE);
|
|
||||||
_environmentData[2].setAtmosphereInnerRadius(0.1875f * TREE_SCALE);
|
|
||||||
_environmentData[2].setAtmosphereOuterRadius(0.1875f * TREE_SCALE * 1.05f);
|
|
||||||
_environmentData[2].setScatteringWavelengths(glm::vec3(0.475f, 0.570f, 0.650f)); // swaps red and blue
|
|
||||||
|
|
||||||
sockaddr senderAddress;
|
sockaddr senderAddress;
|
||||||
|
|
||||||
|
|
|
@ -371,7 +371,7 @@ bool ViewFrustum::matches(const ViewFrustum& compareTo, bool debug) const {
|
||||||
|
|
||||||
void ViewFrustum::computePickRay(float x, float y, glm::vec3& origin, glm::vec3& direction) const {
|
void ViewFrustum::computePickRay(float x, float y, glm::vec3& origin, glm::vec3& direction) const {
|
||||||
origin = _nearTopLeft + x*(_nearTopRight - _nearTopLeft) + y*(_nearBottomLeft - _nearTopLeft);
|
origin = _nearTopLeft + x*(_nearTopRight - _nearTopLeft) + y*(_nearBottomLeft - _nearTopLeft);
|
||||||
direction = glm::normalize(origin - _position);
|
direction = glm::normalize(origin - (_position + _orientation * _eyeOffsetPosition));
|
||||||
}
|
}
|
||||||
|
|
||||||
void ViewFrustum::computeOffAxisFrustum(float& left, float& right, float& bottom, float& top, float& near, float& far,
|
void ViewFrustum::computeOffAxisFrustum(float& left, float& right, float& bottom, float& top, float& near, float& far,
|
||||||
|
@ -401,6 +401,10 @@ void ViewFrustum::computeOffAxisFrustum(float& left, float& right, float& bottom
|
||||||
far = max(far, -corners[i].z);
|
far = max(far, -corners[i].z);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// make sure the near clip isn't too small to be valid
|
||||||
|
const float MIN_NEAR = 0.01f;
|
||||||
|
near = max(MIN_NEAR, near);
|
||||||
|
|
||||||
// get the near/far normal and use it to find the clip planes
|
// get the near/far normal and use it to find the clip planes
|
||||||
glm::vec4 normal = eyeMatrix * glm::vec4(0.0f, 0.0f, 1.0f, 0.0f);
|
glm::vec4 normal = eyeMatrix * glm::vec4(0.0f, 0.0f, 1.0f, 0.0f);
|
||||||
nearClipPlane = glm::vec4(-normal.x, -normal.y, -normal.z, glm::dot(normal, corners[0]));
|
nearClipPlane = glm::vec4(-normal.x, -normal.y, -normal.z, glm::dot(normal, corners[0]));
|
||||||
|
|
|
@ -52,4 +52,6 @@ const float VIEW_CULLING_RATE_IN_MILLISECONDS = 1000.0f; // once a second is fin
|
||||||
|
|
||||||
const uint64_t CLIENT_TO_SERVER_VOXEL_SEND_INTERVAL_USECS = 1000 * 5; // 1 packet every 50 milliseconds
|
const uint64_t CLIENT_TO_SERVER_VOXEL_SEND_INTERVAL_USECS = 1000 * 5; // 1 packet every 50 milliseconds
|
||||||
|
|
||||||
|
const float VIEW_FRUSTUM_FOV_OVERSEND = 60.0f;
|
||||||
|
|
||||||
#endif
|
#endif
|
|
@ -60,11 +60,9 @@ void VoxelEditPacketSender::sendVoxelEditMessage(PACKET_TYPE type, VoxelDetail&
|
||||||
|
|
||||||
// This encodes the voxel edit message into a buffer...
|
// This encodes the voxel edit message into a buffer...
|
||||||
if (createVoxelEditMessage(type, 0, 1, &detail, bufferOut, sizeOut)){
|
if (createVoxelEditMessage(type, 0, 1, &detail, bufferOut, sizeOut)){
|
||||||
|
|
||||||
// If we don't have voxel jurisdictions, then we will simply queue up these packets and wait till we have
|
// If we don't have voxel jurisdictions, then we will simply queue up these packets and wait till we have
|
||||||
// jurisdictions for processing
|
// jurisdictions for processing
|
||||||
if (!voxelServersExist()) {
|
if (!voxelServersExist()) {
|
||||||
|
|
||||||
// If we're asked to save messages while waiting for voxel servers to arrive, then do so...
|
// If we're asked to save messages while waiting for voxel servers to arrive, then do so...
|
||||||
if (_maxPendingMessages > 0) {
|
if (_maxPendingMessages > 0) {
|
||||||
EditPacketBuffer* packet = new EditPacketBuffer(type, bufferOut, sizeOut);
|
EditPacketBuffer* packet = new EditPacketBuffer(type, bufferOut, sizeOut);
|
||||||
|
@ -79,7 +77,7 @@ void VoxelEditPacketSender::sendVoxelEditMessage(PACKET_TYPE type, VoxelDetail&
|
||||||
}
|
}
|
||||||
return; // bail early
|
return; // bail early
|
||||||
} else {
|
} else {
|
||||||
queuePacketToNode(UNKNOWN_NODE_ID, bufferOut, sizeOut); // sends to all servers... not ideal!
|
queuePacketToNodes(bufferOut, sizeOut);
|
||||||
}
|
}
|
||||||
|
|
||||||
// either way, clean up the created buffer
|
// either way, clean up the created buffer
|
||||||
|
@ -100,14 +98,14 @@ bool VoxelEditPacketSender::voxelServersExist() const {
|
||||||
|
|
||||||
// This method is called when the edit packet layer has determined that it has a fully formed packet destined for
|
// This method is called when the edit packet layer has determined that it has a fully formed packet destined for
|
||||||
// a known nodeID. However, we also want to handle the case where the
|
// a known nodeID. However, we also want to handle the case where the
|
||||||
void VoxelEditPacketSender::queuePacketToNode(uint16_t nodeID, unsigned char* bufferOut, ssize_t sizeOut) {
|
void VoxelEditPacketSender::queuePacketToNode(uint16_t nodeID, unsigned char* buffer, ssize_t length) {
|
||||||
NodeList* nodeList = NodeList::getInstance();
|
NodeList* nodeList = NodeList::getInstance();
|
||||||
for (NodeList::iterator node = nodeList->begin(); node != nodeList->end(); node++) {
|
for (NodeList::iterator node = nodeList->begin(); node != nodeList->end(); node++) {
|
||||||
// only send to the NodeTypes that are NODE_TYPE_VOXEL_SERVER
|
// only send to the NodeTypes that are NODE_TYPE_VOXEL_SERVER
|
||||||
if (node->getActiveSocket() != NULL && node->getType() == NODE_TYPE_VOXEL_SERVER &&
|
if (node->getActiveSocket() != NULL && node->getType() == NODE_TYPE_VOXEL_SERVER &&
|
||||||
((node->getNodeID() == nodeID) || (nodeID == (uint16_t)UNKNOWN_NODE_ID)) ) {
|
((node->getNodeID() == nodeID) || (nodeID == (uint16_t)UNKNOWN_NODE_ID)) ) {
|
||||||
sockaddr* nodeAddress = node->getActiveSocket();
|
sockaddr* nodeAddress = node->getActiveSocket();
|
||||||
queuePacketForSending(*nodeAddress, bufferOut, sizeOut);
|
queuePacketForSending(*nodeAddress, buffer, length);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -133,7 +131,7 @@ void VoxelEditPacketSender::processPreServerExistsPackets() {
|
||||||
// First send out all the single message packets...
|
// First send out all the single message packets...
|
||||||
while (!_preServerSingleMessagePackets.empty()) {
|
while (!_preServerSingleMessagePackets.empty()) {
|
||||||
EditPacketBuffer* packet = _preServerSingleMessagePackets.front();
|
EditPacketBuffer* packet = _preServerSingleMessagePackets.front();
|
||||||
queuePacketToNode(UNKNOWN_NODE_ID, &packet->_currentBuffer[0], packet->_currentSize);
|
queuePacketToNodes(&packet->_currentBuffer[0], packet->_currentSize);
|
||||||
delete packet;
|
delete packet;
|
||||||
_preServerSingleMessagePackets.erase(_preServerSingleMessagePackets.begin());
|
_preServerSingleMessagePackets.erase(_preServerSingleMessagePackets.begin());
|
||||||
}
|
}
|
||||||
|
@ -154,12 +152,15 @@ void VoxelEditPacketSender::processPreServerExistsPackets() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void VoxelEditPacketSender::queueVoxelEditMessageToNodes(unsigned char* codeColorBuffer, ssize_t length) {
|
void VoxelEditPacketSender::queuePacketToNodes(unsigned char* buffer, ssize_t length) {
|
||||||
if (!_shouldSend) {
|
if (!_shouldSend) {
|
||||||
return; // bail early
|
return; // bail early
|
||||||
}
|
}
|
||||||
|
|
||||||
assert(voxelServersExist()); // we must have jurisdictions to be here!!
|
assert(voxelServersExist()); // we must have jurisdictions to be here!!
|
||||||
|
|
||||||
|
int headerBytes = numBytesForPacketHeader(buffer) + sizeof(short);
|
||||||
|
unsigned char* octCode = buffer + headerBytes; // skip the packet header to get to the octcode
|
||||||
|
|
||||||
// We want to filter out edit messages for voxel servers based on the server's Jurisdiction
|
// We want to filter out edit messages for voxel servers based on the server's Jurisdiction
|
||||||
// But we can't really do that with a packed message, since each edit message could be destined
|
// But we can't really do that with a packed message, since each edit message could be destined
|
||||||
|
@ -171,20 +172,19 @@ void VoxelEditPacketSender::queueVoxelEditMessageToNodes(unsigned char* codeColo
|
||||||
if (node->getActiveSocket() != NULL && node->getType() == NODE_TYPE_VOXEL_SERVER) {
|
if (node->getActiveSocket() != NULL && node->getType() == NODE_TYPE_VOXEL_SERVER) {
|
||||||
uint16_t nodeID = node->getNodeID();
|
uint16_t nodeID = node->getNodeID();
|
||||||
bool isMyJurisdiction = true;
|
bool isMyJurisdiction = true;
|
||||||
|
|
||||||
// we need to get the jurisdiction for this
|
// we need to get the jurisdiction for this
|
||||||
// here we need to get the "pending packet" for this server
|
// here we need to get the "pending packet" for this server
|
||||||
const JurisdictionMap& map = (*_voxelServerJurisdictions)[nodeID];
|
const JurisdictionMap& map = (*_voxelServerJurisdictions)[nodeID];
|
||||||
isMyJurisdiction = (map.isMyJurisdiction(codeColorBuffer, CHECK_NODE_ONLY) == JurisdictionMap::WITHIN);
|
isMyJurisdiction = (map.isMyJurisdiction(octCode, CHECK_NODE_ONLY) == JurisdictionMap::WITHIN);
|
||||||
|
|
||||||
if (isMyJurisdiction) {
|
if (isMyJurisdiction) {
|
||||||
queuePacketToNode(nodeID, codeColorBuffer, length);
|
queuePacketToNode(nodeID, buffer, length);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// NOTE: codeColorBuffer - is JUST the octcode/color and does not contain the packet header!
|
||||||
void VoxelEditPacketSender::queueVoxelEditMessage(PACKET_TYPE type, unsigned char* codeColorBuffer, ssize_t length) {
|
void VoxelEditPacketSender::queueVoxelEditMessage(PACKET_TYPE type, unsigned char* codeColorBuffer, ssize_t length) {
|
||||||
if (!_shouldSend) {
|
if (!_shouldSend) {
|
||||||
return; // bail early
|
return; // bail early
|
||||||
|
|
|
@ -86,8 +86,8 @@ public:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool _shouldSend;
|
bool _shouldSend;
|
||||||
void queuePacketToNode(uint16_t nodeID, unsigned char* bufferOut, ssize_t sizeOut);
|
void queuePacketToNode(uint16_t nodeID, unsigned char* buffer, ssize_t length);
|
||||||
void queueVoxelEditMessageToNodes(unsigned char* codeColorBuffer, ssize_t length);
|
void queuePacketToNodes(unsigned char* buffer, ssize_t length);
|
||||||
void initializePacket(EditPacketBuffer& packetBuffer, PACKET_TYPE type);
|
void initializePacket(EditPacketBuffer& packetBuffer, PACKET_TYPE type);
|
||||||
void releaseQueuedPacket(EditPacketBuffer& packetBuffer); // releases specific queued packet
|
void releaseQueuedPacket(EditPacketBuffer& packetBuffer); // releases specific queued packet
|
||||||
bool voxelServersExist() const;
|
bool voxelServersExist() const;
|
||||||
|
|
|
@ -79,6 +79,7 @@ void VoxelTree::recurseTreeWithOperation(RecurseVoxelTreeOperation operation, vo
|
||||||
|
|
||||||
// Recurses voxel node with an operation function
|
// Recurses voxel node with an operation function
|
||||||
void VoxelTree::recurseNodeWithOperation(VoxelNode* node, RecurseVoxelTreeOperation operation, void* extraData) {
|
void VoxelTree::recurseNodeWithOperation(VoxelNode* node, RecurseVoxelTreeOperation operation, void* extraData) {
|
||||||
|
|
||||||
if (operation(node, extraData)) {
|
if (operation(node, extraData)) {
|
||||||
for (int i = 0; i < NUMBER_OF_CHILDREN; i++) {
|
for (int i = 0; i < NUMBER_OF_CHILDREN; i++) {
|
||||||
VoxelNode* child = node->getChildAtIndex(i);
|
VoxelNode* child = node->getChildAtIndex(i);
|
||||||
|
@ -221,6 +222,13 @@ int VoxelTree::readNodeData(VoxelNode* destinationNode, unsigned char* nodeData,
|
||||||
nodeWasDirty = childNodeAt->isDirty();
|
nodeWasDirty = childNodeAt->isDirty();
|
||||||
childNodeAt->setColor(newColor);
|
childNodeAt->setColor(newColor);
|
||||||
childNodeAt->setSourceID(args.sourceID);
|
childNodeAt->setSourceID(args.sourceID);
|
||||||
|
|
||||||
|
// if we had a local version of the node already, it's possible that we have it in the VBO but
|
||||||
|
// with the same color data, so this won't count as a change. To address this we check the following
|
||||||
|
if (!childNodeAt->isDirty() && !childNodeAt->isKnownBufferIndex() && childNodeAt->getShouldRender()) {
|
||||||
|
childNodeAt->setDirtyBit(); // force dirty!
|
||||||
|
}
|
||||||
|
|
||||||
nodeIsDirty = childNodeAt->isDirty();
|
nodeIsDirty = childNodeAt->isDirty();
|
||||||
}
|
}
|
||||||
if (nodeIsDirty) {
|
if (nodeIsDirty) {
|
||||||
|
|
|
@ -155,6 +155,7 @@ public:
|
||||||
creationMode mode, bool destructive = false, bool debug = false);
|
creationMode mode, bool destructive = false, bool debug = false);
|
||||||
|
|
||||||
void recurseTreeWithOperation(RecurseVoxelTreeOperation operation, void* extraData=NULL);
|
void recurseTreeWithOperation(RecurseVoxelTreeOperation operation, void* extraData=NULL);
|
||||||
|
|
||||||
void recurseTreeWithOperationDistanceSorted(RecurseVoxelTreeOperation operation,
|
void recurseTreeWithOperationDistanceSorted(RecurseVoxelTreeOperation operation,
|
||||||
const glm::vec3& point, void* extraData=NULL);
|
const glm::vec3& point, void* extraData=NULL);
|
||||||
|
|
||||||
|
@ -190,6 +191,7 @@ public:
|
||||||
bool getShouldReaverage() const { return _shouldReaverage; }
|
bool getShouldReaverage() const { return _shouldReaverage; }
|
||||||
|
|
||||||
void recurseNodeWithOperation(VoxelNode* node, RecurseVoxelTreeOperation operation, void* extraData);
|
void recurseNodeWithOperation(VoxelNode* node, RecurseVoxelTreeOperation operation, void* extraData);
|
||||||
|
|
||||||
void recurseNodeWithOperationDistanceSorted(VoxelNode* node, RecurseVoxelTreeOperation operation,
|
void recurseNodeWithOperationDistanceSorted(VoxelNode* node, RecurseVoxelTreeOperation operation,
|
||||||
const glm::vec3& point, void* extraData);
|
const glm::vec3& point, void* extraData);
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue