Merge branch 'master' of https://github.com/worklist/hifi into localmode

This commit is contained in:
ZappoMan 2013-06-14 09:21:02 -07:00
commit 985b8d66d2
40 changed files with 919 additions and 619 deletions

View file

@ -59,7 +59,7 @@ static void sendVoxelEditMessage(PACKET_HEADER header, VoxelDetail& detail) {
printf("sending packet of size=%d\n",sizeOut); printf("sending packet of size=%d\n",sizeOut);
} }
AgentList::getInstance()->broadcastToAgents(bufferOut, sizeOut, &AGENT_TYPE_VOXEL, 1); AgentList::getInstance()->broadcastToAgents(bufferOut, sizeOut, &AGENT_TYPE_VOXEL_SERVER, 1);
delete[] bufferOut; delete[] bufferOut;
} }
} }
@ -168,7 +168,7 @@ static void renderMovingBug() {
if (::shouldShowPacketsPerSecond) { if (::shouldShowPacketsPerSecond) {
printf("sending packet of size=%d\n", sizeOut); printf("sending packet of size=%d\n", sizeOut);
} }
AgentList::getInstance()->broadcastToAgents(bufferOut, sizeOut, &AGENT_TYPE_VOXEL, 1); AgentList::getInstance()->broadcastToAgents(bufferOut, sizeOut, &AGENT_TYPE_VOXEL_SERVER, 1);
delete[] bufferOut; delete[] bufferOut;
} }
@ -238,13 +238,12 @@ static void renderMovingBug() {
if (::shouldShowPacketsPerSecond) { if (::shouldShowPacketsPerSecond) {
printf("sending packet of size=%d\n", sizeOut); printf("sending packet of size=%d\n", sizeOut);
} }
AgentList::getInstance()->broadcastToAgents(bufferOut, sizeOut, &AGENT_TYPE_VOXEL, 1); AgentList::getInstance()->broadcastToAgents(bufferOut, sizeOut, &AGENT_TYPE_VOXEL_SERVER, 1);
delete[] bufferOut; delete[] bufferOut;
} }
} }
float intensity = 0.5f; float intensity = 0.5f;
float intensityIncrement = 0.1f; float intensityIncrement = 0.1f;
const float MAX_INTENSITY = 1.0f; const float MAX_INTENSITY = 1.0f;
@ -345,7 +344,7 @@ static void sendBlinkingStringOfLights() {
if (::shouldShowPacketsPerSecond) { if (::shouldShowPacketsPerSecond) {
printf("sending packet of size=%d\n",sizeOut); printf("sending packet of size=%d\n",sizeOut);
} }
AgentList::getInstance()->broadcastToAgents(bufferOut, sizeOut, &AGENT_TYPE_VOXEL, 1); AgentList::getInstance()->broadcastToAgents(bufferOut, sizeOut, &AGENT_TYPE_VOXEL_SERVER, 1);
delete[] bufferOut; delete[] bufferOut;
} }
@ -387,7 +386,7 @@ static void sendBlinkingStringOfLights() {
if (::shouldShowPacketsPerSecond) { if (::shouldShowPacketsPerSecond) {
printf("sending packet of size=%d\n",sizeOut); printf("sending packet of size=%d\n",sizeOut);
} }
AgentList::getInstance()->broadcastToAgents(bufferOut, sizeOut, &AGENT_TYPE_VOXEL, 1); AgentList::getInstance()->broadcastToAgents(bufferOut, sizeOut, &AGENT_TYPE_VOXEL_SERVER, 1);
delete[] bufferOut; delete[] bufferOut;
} }
} }
@ -510,7 +509,7 @@ void sendDanceFloor() {
if (::shouldShowPacketsPerSecond) { if (::shouldShowPacketsPerSecond) {
printf("sending packet of size=%d\n", sizeOut); printf("sending packet of size=%d\n", sizeOut);
} }
AgentList::getInstance()->broadcastToAgents(bufferOut, sizeOut, &AGENT_TYPE_VOXEL, 1); AgentList::getInstance()->broadcastToAgents(bufferOut, sizeOut, &AGENT_TYPE_VOXEL_SERVER, 1);
delete[] bufferOut; delete[] bufferOut;
} }
} }
@ -607,7 +606,7 @@ static void sendBillboard() {
if (::shouldShowPacketsPerSecond) { if (::shouldShowPacketsPerSecond) {
printf("sending packet of size=%d\n", sizeOut); printf("sending packet of size=%d\n", sizeOut);
} }
AgentList::getInstance()->broadcastToAgents(bufferOut, sizeOut, &AGENT_TYPE_VOXEL, 1); AgentList::getInstance()->broadcastToAgents(bufferOut, sizeOut, &AGENT_TYPE_VOXEL_SERVER, 1);
delete[] bufferOut; delete[] bufferOut;
} }
} }
@ -644,14 +643,14 @@ void* animateVoxels(void* args) {
sendDanceFloor(); sendDanceFloor();
} }
double end = usecTimestampNow(); long long end = usecTimestampNow();
double elapsedSeconds = (end - ::start) / 1000000.0; long long elapsedSeconds = (end - ::start) / 1000000;
if (::shouldShowPacketsPerSecond) { if (::shouldShowPacketsPerSecond) {
printf("packetsSent=%ld, bytesSent=%ld pps=%f bps=%f\n",packetsSent,bytesSent, printf("packetsSent=%ld, bytesSent=%ld pps=%f bps=%f\n",packetsSent,bytesSent,
(float)(packetsSent/elapsedSeconds),(float)(bytesSent/elapsedSeconds)); (float)(packetsSent/elapsedSeconds),(float)(bytesSent/elapsedSeconds));
} }
// dynamically sleep until we need to fire off the next set of voxels // dynamically sleep until we need to fire off the next set of voxels
double usecToSleep = ANIMATE_VOXELS_INTERVAL_USECS - (usecTimestampNow() - usecTimestamp(&lastSendTime)); long long usecToSleep = ANIMATE_VOXELS_INTERVAL_USECS - (usecTimestampNow() - usecTimestamp(&lastSendTime));
if (usecToSleep > 0) { if (usecToSleep > 0) {
usleep(usecToSleep); usleep(usecToSleep);
@ -702,7 +701,6 @@ int main(int argc, const char * argv[])
agentList->linkedDataCreateCallback = NULL; // do we need a callback? agentList->linkedDataCreateCallback = NULL; // do we need a callback?
agentList->startSilentAgentRemovalThread(); agentList->startSilentAgentRemovalThread();
agentList->startDomainServerCheckInThread();
srand((unsigned)time(0)); srand((unsigned)time(0));
@ -713,16 +711,21 @@ int main(int argc, const char * argv[])
unsigned char* packetData = new unsigned char[MAX_PACKET_SIZE]; unsigned char* packetData = new unsigned char[MAX_PACKET_SIZE];
ssize_t receivedBytes; ssize_t receivedBytes;
timeval lastDomainServerCheckIn = {};
AgentList::getInstance()->setAgentTypesOfInterest(&AGENT_TYPE_VOXEL_SERVER, 1);
// loop to send to agents requesting data // loop to send to agents requesting data
while (true) { while (true) {
// Agents sending messages to us... // 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);
AgentList::getInstance()->sendDomainServerCheckIn();
}
// Agents sending messages to us...
if (agentList->getAgentSocket()->receive(&agentPublicAddress, packetData, &receivedBytes)) { if (agentList->getAgentSocket()->receive(&agentPublicAddress, packetData, &receivedBytes)) {
switch (packetData[0]) { AgentList::getInstance()->processAgentData(&agentPublicAddress, packetData, receivedBytes);
default: {
AgentList::getInstance()->processAgentData(&agentPublicAddress, packetData, receivedBytes);
} break;
}
} }
} }

View file

@ -95,7 +95,6 @@ int main(int argc, const char* argv[]) {
agentList->linkedDataCreateCallback = attachNewBufferToAgent; agentList->linkedDataCreateCallback = attachNewBufferToAgent;
agentList->startSilentAgentRemovalThread(); agentList->startSilentAgentRemovalThread();
agentList->startDomainServerCheckInThread();
unsigned char* packetData = new unsigned char[MAX_PACKET_SIZE]; unsigned char* packetData = new unsigned char[MAX_PACKET_SIZE];
@ -114,7 +113,16 @@ int main(int argc, const char* argv[]) {
gettimeofday(&startTime, NULL); gettimeofday(&startTime, NULL);
timeval lastDomainServerCheckIn = {};
while (true) { 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);
AgentList::getInstance()->sendDomainServerCheckIn();
}
for (AgentList::iterator agent = agentList->begin(); agent != agentList->end(); agent++) { for (AgentList::iterator agent = agentList->begin(); agent != agentList->end(); agent++) {
PositionalAudioRingBuffer* positionalRingBuffer = (PositionalAudioRingBuffer*) agent->getLinkedData(); PositionalAudioRingBuffer* positionalRingBuffer = (PositionalAudioRingBuffer*) agent->getLinkedData();
@ -325,7 +333,7 @@ int main(int argc, const char* argv[]) {
} }
} }
double usecToSleep = usecTimestamp(&startTime) + (++nextFrame * BUFFER_SEND_INTERVAL_USECS) - usecTimestampNow(); long long usecToSleep = usecTimestamp(&startTime) + (++nextFrame * BUFFER_SEND_INTERVAL_USECS) - usecTimestampNow();
if (usecToSleep > 0) { if (usecToSleep > 0) {
usleep(usecToSleep); usleep(usecToSleep);

View file

@ -66,7 +66,6 @@ int main(int argc, const char* argv[]) {
agentList->linkedDataCreateCallback = attachAvatarDataToAgent; agentList->linkedDataCreateCallback = attachAvatarDataToAgent;
agentList->startDomainServerCheckInThread();
agentList->startSilentAgentRemovalThread(); agentList->startSilentAgentRemovalThread();
sockaddr *agentAddress = new sockaddr; sockaddr *agentAddress = new sockaddr;
@ -80,8 +79,19 @@ int main(int argc, const char* argv[]) {
uint16_t agentID = 0; uint16_t agentID = 0;
Agent* avatarAgent = NULL; Agent* avatarAgent = NULL;
timeval lastDomainServerCheckIn = {};
// we only need to hear back about avatar agents from the DS
AgentList::getInstance()->setAgentTypesOfInterest(&AGENT_TYPE_AVATAR, 1);
while (true) { 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);
AgentList::getInstance()->sendDomainServerCheckIn();
}
if (agentList->getAgentSocket()->receive(agentAddress, packetData, &receivedBytes)) { if (agentList->getAgentSocket()->receive(agentAddress, packetData, &receivedBytes)) {
switch (packetData[0]) { switch (packetData[0]) {
case PACKET_HEADER_HEAD_DATA: case PACKET_HEADER_HEAD_DATA:
@ -129,7 +139,6 @@ int main(int argc, const char* argv[]) {
} }
agentList->stopSilentAgentRemovalThread(); agentList->stopSilentAgentRemovalThread();
agentList->stopDomainServerCheckInThread();
return 0; return 0;
} }

View file

@ -94,15 +94,14 @@ int main(int argc, const char * argv[])
agentList->startSilentAgentRemovalThread(); agentList->startSilentAgentRemovalThread();
uint16_t packetAgentID = 0;
while (true) { while (true) {
if (agentList->getAgentSocket()->receive((sockaddr *)&agentPublicAddress, packetData, &receivedBytes) && if (agentList->getAgentSocket()->receive((sockaddr *)&agentPublicAddress, packetData, &receivedBytes) &&
(packetData[0] == PACKET_HEADER_DOMAIN_RFD || packetData[0] == PACKET_HEADER_DOMAIN_LIST_REQUEST)) { (packetData[0] == PACKET_HEADER_DOMAIN_REPORT_FOR_DUTY || packetData[0] == PACKET_HEADER_DOMAIN_LIST_REQUEST)) {
std::map<char, Agent *> newestSoloAgents; std::map<char, Agent *> newestSoloAgents;
agentType = packetData[1]; agentType = packetData[1];
unpackSocket(packetData + 2, (sockaddr*) &agentLocalAddress); int numBytesSocket = unpackSocket(packetData + sizeof(PACKET_HEADER) + sizeof(AGENT_TYPE),
(sockaddr*) &agentLocalAddress);
// check the agent public address // check the agent public address
// if it matches our local address we're on the same box // if it matches our local address we're on the same box
@ -124,51 +123,59 @@ int main(int argc, const char * argv[])
agentList->increaseAgentID(); agentList->increaseAgentID();
} }
currentBufferPos = broadcastPacket + 1; currentBufferPos = broadcastPacket + sizeof(PACKET_HEADER);
startPointer = currentBufferPos; startPointer = currentBufferPos;
for (AgentList::iterator agent = agentList->begin(); agent != agentList->end(); agent++) { unsigned char* agentTypesOfInterest = packetData + sizeof(PACKET_HEADER) + sizeof(AGENT_TYPE)
if (!agent->matches((sockaddr*) &agentPublicAddress, (sockaddr*) &agentLocalAddress, agentType)) { + numBytesSocket + sizeof(unsigned char);
if (memchr(SOLO_AGENT_TYPES, agent->getType(), sizeof(SOLO_AGENT_TYPES)) == NULL) { int numInterestTypes = *(agentTypesOfInterest - 1);
// this is an agent of which there can be multiple, just add them to the packet
// don't send avatar agents to other avatars, that will come from avatar mixer if (numInterestTypes > 0) {
if (agentType != AGENT_TYPE_AVATAR || agent->getType() != AGENT_TYPE_AVATAR) { // if the agent has sent no types of interest, assume they want nothing but their own ID back
currentBufferPos = addAgentToBroadcastPacket(currentBufferPos, &(*agent)); for (AgentList::iterator agent = agentList->begin(); agent != agentList->end(); agent++) {
} if (!agent->matches((sockaddr*) &agentPublicAddress, (sockaddr*) &agentLocalAddress, agentType) &&
memchr(agentTypesOfInterest, agent->getType(), numInterestTypes)) {
// this is not the agent themselves
// and this is an agent of a type in the passed agent types of interest
// or the agent did not pass us any specific types they are interested in
if (memchr(SOLO_AGENT_TYPES, agent->getType(), sizeof(SOLO_AGENT_TYPES)) == NULL) {
// this is an agent of which there can be multiple, just add them to the packet
// don't send avatar agents to other avatars, that will come from avatar mixer
if (agentType != AGENT_TYPE_AVATAR || agent->getType() != AGENT_TYPE_AVATAR) {
currentBufferPos = addAgentToBroadcastPacket(currentBufferPos, &(*agent));
}
} else { } else {
// solo agent, we need to only send newest // solo agent, we need to only send newest
if (newestSoloAgents[agent->getType()] == NULL || if (newestSoloAgents[agent->getType()] == NULL ||
newestSoloAgents[agent->getType()]->getWakeMicrostamp() < agent->getWakeMicrostamp()) { newestSoloAgents[agent->getType()]->getWakeMicrostamp() < agent->getWakeMicrostamp()) {
// we have to set the newer solo agent to add it to the broadcast later // we have to set the newer solo agent to add it to the broadcast later
newestSoloAgents[agent->getType()] = &(*agent); newestSoloAgents[agent->getType()] = &(*agent);
}
} }
} }
} else { }
double timeNow = usecTimestampNow();
for (std::map<char, Agent *>::iterator soloAgent = newestSoloAgents.begin();
// this is the agent, just update last receive to now soloAgent != newestSoloAgents.end();
agent->setLastHeardMicrostamp(timeNow); soloAgent++) {
// this is the newest alive solo agent, add them to the packet
// grab the ID for this agent so we can send it back with the packet currentBufferPos = addAgentToBroadcastPacket(currentBufferPos, soloAgent->second);
packetAgentID = agent->getAgentID();
if (packetData[0] == PACKET_HEADER_DOMAIN_RFD
&& memchr(SOLO_AGENT_TYPES, agentType, sizeof(SOLO_AGENT_TYPES))) {
agent->setWakeMicrostamp(timeNow);
}
} }
} }
// update last receive to now
long long timeNow = usecTimestampNow();
newAgent->setLastHeardMicrostamp(timeNow);
for (std::map<char, Agent *>::iterator soloAgent = newestSoloAgents.begin(); if (packetData[0] == PACKET_HEADER_DOMAIN_REPORT_FOR_DUTY
soloAgent != newestSoloAgents.end(); && memchr(SOLO_AGENT_TYPES, agentType, sizeof(SOLO_AGENT_TYPES))) {
soloAgent++) { newAgent->setWakeMicrostamp(timeNow);
// this is the newest alive solo agent, add them to the packet
currentBufferPos = addAgentToBroadcastPacket(currentBufferPos, soloAgent->second);
} }
// add the agent ID to the end of the pointer // add the agent ID to the end of the pointer
currentBufferPos += packAgentId(currentBufferPos, packetAgentID); currentBufferPos += packAgentId(currentBufferPos, newAgent->getAgentID());
// send the constructed list back to this agent // send the constructed list back to this agent
agentList->getAgentSocket()->send((sockaddr*) &agentPublicAddress, agentList->getAgentSocket()->send((sockaddr*) &agentPublicAddress,

View file

@ -82,9 +82,6 @@ int main(int argc, const char* argv[]) {
// create an AgentList instance to handle communication with other agents // create an AgentList instance to handle communication with other agents
AgentList* agentList = AgentList::createInstance(AGENT_TYPE_AVATAR, EVE_AGENT_LISTEN_PORT); AgentList* agentList = AgentList::createInstance(AGENT_TYPE_AVATAR, EVE_AGENT_LISTEN_PORT);
// start telling the domain server that we are alive
agentList->startDomainServerCheckInThread();
// start the agent list thread that will kill off agents when they stop talking // start the agent list thread that will kill off agents when they stop talking
agentList->startSilentAgentRemovalThread(); agentList->startSilentAgentRemovalThread();
@ -131,11 +128,23 @@ int main(int argc, const char* argv[]) {
broadcastPacket[0] = PACKET_HEADER_HEAD_DATA; broadcastPacket[0] = PACKET_HEADER_HEAD_DATA;
timeval thisSend; timeval thisSend;
double numMicrosecondsSleep = 0; long long numMicrosecondsSleep = 0;
int handStateTimer = 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_AGENT_TYPES_OF_INTEREST[] = {AGENT_TYPE_AVATAR_MIXER, AGENT_TYPE_AUDIO_MIXER};
AgentList::getInstance()->setAgentTypesOfInterest(EVE_AGENT_TYPES_OF_INTEREST, sizeof(EVE_AGENT_TYPES_OF_INTEREST));
while (true) { 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);
AgentList::getInstance()->sendDomainServerCheckIn();
}
// update the thisSend timeval to the current time // update the thisSend timeval to the current time
gettimeofday(&thisSend, NULL); gettimeofday(&thisSend, NULL);
@ -201,7 +210,6 @@ int main(int argc, const char* argv[]) {
pthread_join(receiveAgentDataThread, NULL); pthread_join(receiveAgentDataThread, NULL);
// stop the agent list's threads // stop the agent list's threads
agentList->stopDomainServerCheckInThread();
agentList->stopPingUnknownAgentsThread(); agentList->stopPingUnknownAgentsThread();
agentList->stopSilentAgentRemovalThread(); agentList->stopSilentAgentRemovalThread();
} }

View file

@ -167,9 +167,6 @@ int main(int argc, char* argv[]) {
pthread_t receiveAgentDataThread; pthread_t receiveAgentDataThread;
pthread_create(&receiveAgentDataThread, NULL, receiveAgentData, NULL); pthread_create(&receiveAgentDataThread, NULL, receiveAgentData, NULL);
// start telling the domain server that we are alive
agentList->startDomainServerCheckInThread();
// start the agent list thread that will kill off agents when they stop talking // start the agent list thread that will kill off agents when they stop talking
agentList->startSilentAgentRemovalThread(); agentList->startSilentAgentRemovalThread();
@ -190,9 +187,22 @@ int main(int argc, char* argv[]) {
unsigned char broadcastPacket = PACKET_HEADER_INJECT_AUDIO; unsigned char broadcastPacket = PACKET_HEADER_INJECT_AUDIO;
timeval thisSend; timeval thisSend;
double numMicrosecondsSleep = 0; long long numMicrosecondsSleep = 0;
timeval lastDomainServerCheckIn = {};
// the audio injector needs to know about the avatar mixer and the audio mixer
const char INJECTOR_AGENTS_OF_INTEREST[] = {AGENT_TYPE_AVATAR_MIXER, AGENT_TYPE_AUDIO_MIXER};
AgentList::getInstance()->setAgentTypesOfInterest(INJECTOR_AGENTS_OF_INTEREST, sizeof(INJECTOR_AGENTS_OF_INTEREST));
while (true) { 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);
AgentList::getInstance()->sendDomainServerCheckIn();
}
if (::triggerDistance) { if (::triggerDistance) {
// update the thisSend timeval to the current time // update the thisSend timeval to the current time
@ -260,7 +270,6 @@ int main(int argc, char* argv[]) {
pthread_join(receiveAgentDataThread, NULL); pthread_join(receiveAgentDataThread, NULL);
// stop the agent list's threads // stop the agent list's threads
agentList->stopDomainServerCheckInThread();
agentList->stopSilentAgentRemovalThread(); agentList->stopSilentAgentRemovalThread();
} }
} }

Binary file not shown.

Before

Width:  |  Height:  |  Size: 144 KiB

After

Width:  |  Height:  |  Size: 152 KiB

View file

@ -0,0 +1,21 @@
#version 120
//
// iris.frag
// fragment shader
//
// Created by Andrzej Kapolka on 6/13/13.
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
//
// the iris texture
uniform sampler2D texture;
// the interpolated normal
varying vec4 normal;
void main(void) {
float specular = max(0.0, dot(normalize(gl_LightSource[0].position + vec4(0.0, 0.0, 1.0, 0.0)), normalize(normal)));
gl_FragColor = vec4(gl_Color.rgb * texture2D(texture, gl_TexCoord[0].st).rgb +
pow(specular, gl_FrontMaterial.shininess) * gl_FrontLightProduct[0].specular.rgb, 1.0);
}

View file

@ -0,0 +1,20 @@
#version 120
//
// iris.vert
// vertex shader
//
// Created by Andrzej Kapolka on 6/13/13.
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
//
// the interpolated normal
varying vec4 normal;
void main(void) {
normal = normalize(gl_ModelViewMatrix * vec4(gl_Normal, 0.0));
gl_FrontColor = gl_Color * (gl_LightModel.ambient + gl_LightSource[0].ambient +
gl_LightSource[0].diffuse * max(0.0, dot(normal, gl_LightSource[0].position)));
gl_TexCoord[0] = gl_MultiTexCoord0;
gl_Position = ftransform();
}

View file

@ -62,8 +62,6 @@
using namespace std; using namespace std;
const bool TESTING_AVATAR_TOUCH = false;
// Starfield information // Starfield information
static char STAR_FILE[] = "https://s3-us-west-1.amazonaws.com/highfidelity/stars.txt"; static char STAR_FILE[] = "https://s3-us-west-1.amazonaws.com/highfidelity/stars.txt";
static char STAR_CACHE_FILE[] = "cachedStars.txt"; static char STAR_CACHE_FILE[] = "cachedStars.txt";
@ -144,7 +142,6 @@ Application::Application(int& argc, char** argv, timeval &startup_time) :
_viewFrustumOffsetDistance(25.0), _viewFrustumOffsetDistance(25.0),
_viewFrustumOffsetUp(0.0), _viewFrustumOffsetUp(0.0),
_audioScope(256, 200, true), _audioScope(256, 200, true),
_manualFirstPerson(false),
_mouseX(0), _mouseX(0),
_mouseY(0), _mouseY(0),
_mousePressed(false), _mousePressed(false),
@ -170,7 +167,7 @@ Application::Application(int& argc, char** argv, timeval &startup_time) :
_window->setWindowTitle("Interface"); _window->setWindowTitle("Interface");
printLog("Interface Startup:\n"); printLog("Interface Startup:\n");
unsigned int listenPort = AGENT_SOCKET_LISTEN_PORT; unsigned int listenPort = 0; // bind to an ephemeral port by default
const char** constArgv = const_cast<const char**>(argv); const char** constArgv = const_cast<const char**>(argv);
const char* portStr = getCmdOption(argc, constArgv, "--listenPort"); const char* portStr = getCmdOption(argc, constArgv, "--listenPort");
if (portStr) { if (portStr) {
@ -207,10 +204,13 @@ Application::Application(int& argc, char** argv, timeval &startup_time) :
WSADATA WsaData; WSADATA WsaData;
int wsaresult = WSAStartup(MAKEWORD(2,2), &WsaData); int wsaresult = WSAStartup(MAKEWORD(2,2), &WsaData);
#endif #endif
// tell the AgentList instance who to tell the domain server we care about
const char agentTypesOfInterest[] = {AGENT_TYPE_AUDIO_MIXER, AGENT_TYPE_AVATAR_MIXER, AGENT_TYPE_VOXEL_SERVER};
AgentList::getInstance()->setAgentTypesOfInterest(agentTypesOfInterest, sizeof(agentTypesOfInterest));
// start the agentList threads // start the agentList threads
AgentList::getInstance()->startSilentAgentRemovalThread(); AgentList::getInstance()->startSilentAgentRemovalThread();
AgentList::getInstance()->startDomainServerCheckInThread();
AgentList::getInstance()->startPingUnknownAgentsThread(); AgentList::getInstance()->startPingUnknownAgentsThread();
_window->setCentralWidget(_glWidget); _window->setCentralWidget(_glWidget);
@ -303,9 +303,11 @@ void Application::paintGL() {
glEnable(GL_LINE_SMOOTH); glEnable(GL_LINE_SMOOTH);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
float headCameraScale = _serialHeadSensor.active ? _headCameraPitchYawScale : 1.0f;
if (_myCamera.getMode() == CAMERA_MODE_MIRROR) { if (_myCamera.getMode() == CAMERA_MODE_MIRROR) {
_myCamera.setTightness (100.0f); _myCamera.setTightness (100.0f);
_myCamera.setTargetPosition(_myAvatar.getBallPosition(AVATAR_JOINT_HEAD_BASE)); _myCamera.setTargetPosition(_myAvatar.getUprightHeadPosition());
_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()) {
@ -317,12 +319,12 @@ 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.getBallPosition(AVATAR_JOINT_HEAD_BASE)); _myCamera.setTargetPosition(_myAvatar.getUprightHeadPosition());
_myCamera.setTargetRotation(_myAvatar.getHead().getCameraOrientation(_headCameraPitchYawScale)); _myCamera.setTargetRotation(_myAvatar.getHead().getCameraOrientation(headCameraScale));
} else if (_myCamera.getMode() == CAMERA_MODE_THIRD_PERSON) { } else if (_myCamera.getMode() == CAMERA_MODE_THIRD_PERSON) {
_myCamera.setTargetPosition(_myAvatar.getHeadJointPosition()); _myCamera.setTargetPosition(_myAvatar.getUprightHeadPosition());
_myCamera.setTargetRotation(_myAvatar.getHead().getCameraOrientation(_headCameraPitchYawScale)); _myCamera.setTargetRotation(_myAvatar.getHead().getCameraOrientation(headCameraScale));
} }
// Update camera position // Update camera position
@ -424,7 +426,7 @@ static void sendVoxelServerAddScene() {
char message[100]; char message[100];
sprintf(message,"%c%s",'Z',"add scene"); sprintf(message,"%c%s",'Z',"add scene");
int messageSize = strlen(message) + 1; int messageSize = strlen(message) + 1;
AgentList::getInstance()->broadcastToAgents((unsigned char*)message, messageSize, &AGENT_TYPE_VOXEL, 1); AgentList::getInstance()->broadcastToAgents((unsigned char*)message, messageSize, &AGENT_TYPE_VOXEL_SERVER, 1);
} }
void Application::keyPressEvent(QKeyEvent* event) { void Application::keyPressEvent(QKeyEvent* event) {
@ -508,6 +510,9 @@ void Application::keyPressEvent(QKeyEvent* event) {
break; break;
case Qt::Key_E: case Qt::Key_E:
if (!_myAvatar.getDriveKeys(UP)) {
_myAvatar.jump();
}
_myAvatar.setDriveKeys(UP, 1); _myAvatar.setDriveKeys(UP, 1);
break; break;
@ -718,6 +723,9 @@ void Application::mousePressEvent(QMouseEvent* event) {
if (event->button() == Qt::LeftButton) { if (event->button() == Qt::LeftButton) {
_mouseX = event->x(); _mouseX = event->x();
_mouseY = event->y(); _mouseY = event->y();
_mouseDragStartedX = _mouseX;
_mouseDragStartedY = _mouseY;
_mouseVoxelDragging = _mouseVoxel;
_mousePressed = true; _mousePressed = true;
maybeEditVoxelUnderCursor(); maybeEditVoxelUnderCursor();
@ -767,6 +775,9 @@ void Application::timer() {
if (!_serialHeadSensor.active) { if (!_serialHeadSensor.active) {
_serialHeadSensor.pair(); _serialHeadSensor.pair();
} }
// ask the agent list to check in with the domain server
AgentList::getInstance()->sendDomainServerCheckIn();
} }
static glm::vec3 getFaceVector(BoxFace face) { static glm::vec3 getFaceVector(BoxFace face) {
@ -821,6 +832,7 @@ void Application::terminate() {
static void sendAvatarVoxelURLMessage(const QUrl& url) { static void sendAvatarVoxelURLMessage(const QUrl& url) {
uint16_t ownerID = AgentList::getInstance()->getOwnerID(); uint16_t ownerID = AgentList::getInstance()->getOwnerID();
if (ownerID == UNKNOWN_AGENT_ID) { if (ownerID == UNKNOWN_AGENT_ID) {
return; // we don't yet know who we are return; // we don't yet know who we are
} }
@ -874,6 +886,10 @@ void Application::editPreferences() {
headCameraPitchYawScale->setValue(_headCameraPitchYawScale); headCameraPitchYawScale->setValue(_headCameraPitchYawScale);
form->addRow("Head Camera Pitch/Yaw Scale:", headCameraPitchYawScale); form->addRow("Head Camera Pitch/Yaw Scale:", headCameraPitchYawScale);
QDoubleSpinBox* leanScale = new QDoubleSpinBox();
leanScale->setValue(_myAvatar.getLeanScale());
form->addRow("Lean Scale:", leanScale);
QDialogButtonBox* buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); QDialogButtonBox* buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
dialog.connect(buttons, SIGNAL(accepted()), SLOT(accept())); dialog.connect(buttons, SIGNAL(accepted()), SLOT(accept()));
dialog.connect(buttons, SIGNAL(rejected()), SLOT(reject())); dialog.connect(buttons, SIGNAL(rejected()), SLOT(reject()));
@ -887,6 +903,7 @@ void Application::editPreferences() {
sendAvatarVoxelURLMessage(url); sendAvatarVoxelURLMessage(url);
_headCameraPitchYawScale = headCameraPitchYawScale->value(); _headCameraPitchYawScale = headCameraPitchYawScale->value();
_myAvatar.setLeanScale(leanScale->value());
} }
void Application::pair() { void Application::pair() {
@ -897,6 +914,8 @@ void Application::setHead(bool head) {
if (head) { if (head) {
_myCamera.setMode(CAMERA_MODE_MIRROR); _myCamera.setMode(CAMERA_MODE_MIRROR);
_myCamera.setModeShiftRate(100.0f); _myCamera.setModeShiftRate(100.0f);
_manualFirstPerson->setChecked(false);
} else { } else {
_myCamera.setMode(CAMERA_MODE_THIRD_PERSON); _myCamera.setMode(CAMERA_MODE_THIRD_PERSON);
_myCamera.setModeShiftRate(1.0f); _myCamera.setModeShiftRate(1.0f);
@ -914,7 +933,9 @@ void Application::setFullscreen(bool fullscreen) {
} }
void Application::setRenderFirstPerson(bool firstPerson) { void Application::setRenderFirstPerson(bool firstPerson) {
_manualFirstPerson = firstPerson; if (firstPerson && _lookingInMirror->isChecked()) {
_lookingInMirror->trigger();
}
} }
void Application::setFrustumOffset(bool frustumOffset) { void Application::setFrustumOffset(bool frustumOffset) {
@ -992,11 +1013,17 @@ static void sendVoxelEditMessage(PACKET_HEADER header, VoxelDetail& detail) {
int sizeOut; int sizeOut;
if (createVoxelEditMessage(header, 0, 1, &detail, bufferOut, sizeOut)){ if (createVoxelEditMessage(header, 0, 1, &detail, bufferOut, sizeOut)){
AgentList::getInstance()->broadcastToAgents(bufferOut, sizeOut, &AGENT_TYPE_VOXEL, 1); AgentList::getInstance()->broadcastToAgents(bufferOut, sizeOut, &AGENT_TYPE_VOXEL_SERVER, 1);
delete[] bufferOut; delete[] bufferOut;
} }
} }
const glm::vec3 Application::getMouseVoxelWorldCoordinates(const VoxelDetail _mouseVoxel) {
return glm::vec3((_mouseVoxel.x + _mouseVoxel.s / 2.f) * TREE_SCALE,
(_mouseVoxel.y + _mouseVoxel.s / 2.f) * TREE_SCALE,
(_mouseVoxel.z + _mouseVoxel.s / 2.f) * TREE_SCALE);
}
void Application::decreaseVoxelSize() { void Application::decreaseVoxelSize() {
_mouseVoxelScale /= 2; _mouseVoxelScale /= 2;
} }
@ -1061,7 +1088,7 @@ bool Application::sendVoxelsOperation(VoxelNode* node, void* extraData) {
// if we have room don't have room in the buffer, then send the previously generated message first // if we have room don't have room in the buffer, then send the previously generated message first
if (args->bufferInUse + codeAndColorLength > MAXIMUM_EDIT_VOXEL_MESSAGE_SIZE) { if (args->bufferInUse + codeAndColorLength > MAXIMUM_EDIT_VOXEL_MESSAGE_SIZE) {
AgentList::getInstance()->broadcastToAgents(args->messageBuffer, args->bufferInUse, &AGENT_TYPE_VOXEL, 1); AgentList::getInstance()->broadcastToAgents(args->messageBuffer, args->bufferInUse, &AGENT_TYPE_VOXEL_SERVER, 1);
args->bufferInUse = sizeof(PACKET_HEADER_SET_VOXEL_DESTRUCTIVE) + sizeof(unsigned short int); // reset args->bufferInUse = sizeof(PACKET_HEADER_SET_VOXEL_DESTRUCTIVE) + sizeof(unsigned short int); // reset
} }
@ -1125,7 +1152,7 @@ void Application::importVoxels() {
// If we have voxels left in the packet, then send the packet // If we have voxels left in the packet, then send the packet
if (args.bufferInUse > (sizeof(PACKET_HEADER_SET_VOXEL_DESTRUCTIVE) + sizeof(unsigned short int))) { if (args.bufferInUse > (sizeof(PACKET_HEADER_SET_VOXEL_DESTRUCTIVE) + sizeof(unsigned short int))) {
AgentList::getInstance()->broadcastToAgents(args.messageBuffer, args.bufferInUse, &AGENT_TYPE_VOXEL, 1); AgentList::getInstance()->broadcastToAgents(args.messageBuffer, args.bufferInUse, &AGENT_TYPE_VOXEL_SERVER, 1);
} }
if (calculatedOctCode) { if (calculatedOctCode) {
@ -1177,7 +1204,7 @@ void Application::pasteVoxels() {
// If we have voxels left in the packet, then send the packet // If we have voxels left in the packet, then send the packet
if (args.bufferInUse > (sizeof(PACKET_HEADER_SET_VOXEL_DESTRUCTIVE) + sizeof(unsigned short int))) { if (args.bufferInUse > (sizeof(PACKET_HEADER_SET_VOXEL_DESTRUCTIVE) + sizeof(unsigned short int))) {
AgentList::getInstance()->broadcastToAgents(args.messageBuffer, args.bufferInUse, &AGENT_TYPE_VOXEL, 1); AgentList::getInstance()->broadcastToAgents(args.messageBuffer, args.bufferInUse, &AGENT_TYPE_VOXEL_SERVER, 1);
} }
if (calculatedOctCode) { if (calculatedOctCode) {
@ -1206,7 +1233,7 @@ void Application::initMenu() {
(_gyroLook = optionsMenu->addAction("Gyro Look"))->setCheckable(true); (_gyroLook = optionsMenu->addAction("Gyro Look"))->setCheckable(true);
_gyroLook->setChecked(false); _gyroLook->setChecked(false);
(_mouseLook = optionsMenu->addAction("Mouse Look"))->setCheckable(true); (_mouseLook = optionsMenu->addAction("Mouse Look"))->setCheckable(true);
_mouseLook->setChecked(false); _mouseLook->setChecked(true);
(_showHeadMouse = optionsMenu->addAction("Head Mouse"))->setCheckable(true); (_showHeadMouse = optionsMenu->addAction("Head Mouse"))->setCheckable(true);
_showHeadMouse->setChecked(false); _showHeadMouse->setChecked(false);
(_transmitterDrives = optionsMenu->addAction("Transmitter Drive"))->setCheckable(true); (_transmitterDrives = optionsMenu->addAction("Transmitter Drive"))->setCheckable(true);
@ -1230,7 +1257,6 @@ void Application::initMenu() {
_renderAtmosphereOn->setShortcut(Qt::SHIFT | Qt::Key_A); _renderAtmosphereOn->setShortcut(Qt::SHIFT | Qt::Key_A);
(_renderGroundPlaneOn = renderMenu->addAction("Ground Plane"))->setCheckable(true); (_renderGroundPlaneOn = renderMenu->addAction("Ground Plane"))->setCheckable(true);
_renderGroundPlaneOn->setChecked(true); _renderGroundPlaneOn->setChecked(true);
_renderGroundPlaneOn->setShortcut(Qt::SHIFT | Qt::Key_G);
(_renderAvatarsOn = renderMenu->addAction("Avatars"))->setCheckable(true); (_renderAvatarsOn = renderMenu->addAction("Avatars"))->setCheckable(true);
_renderAvatarsOn->setChecked(true); _renderAvatarsOn->setChecked(true);
(_renderAvatarBalls = renderMenu->addAction("Avatar as Balls"))->setCheckable(true); (_renderAvatarBalls = renderMenu->addAction("Avatar as Balls"))->setCheckable(true);
@ -1239,8 +1265,8 @@ void Application::initMenu() {
_renderFrameTimerOn->setChecked(false); _renderFrameTimerOn->setChecked(false);
(_renderLookatOn = renderMenu->addAction("Lookat Vectors"))->setCheckable(true); (_renderLookatOn = renderMenu->addAction("Lookat Vectors"))->setCheckable(true);
_renderLookatOn->setChecked(false); _renderLookatOn->setChecked(false);
(_manualFirstPerson = renderMenu->addAction(
renderMenu->addAction("First Person", this, SLOT(setRenderFirstPerson(bool)), Qt::Key_P)->setCheckable(true); "First Person", this, SLOT(setRenderFirstPerson(bool)), Qt::Key_P))->setCheckable(true);
QMenu* toolsMenu = menuBar->addMenu("Tools"); QMenu* toolsMenu = menuBar->addMenu("Tools");
(_renderStatsOn = toolsMenu->addAction("Stats"))->setCheckable(true); (_renderStatsOn = toolsMenu->addAction("Stats"))->setCheckable(true);
@ -1425,7 +1451,31 @@ void Application::update(float deltaTime) {
// tell my avatar the posiion and direction of the ray projected ino the world based on the mouse position // tell my avatar the posiion and direction of the ray projected ino the world based on the mouse position
_myAvatar.setMouseRay(mouseRayOrigin, mouseRayDirection); _myAvatar.setMouseRay(mouseRayOrigin, mouseRayDirection);
// Set where I am looking based on my mouse ray (so that other people can see)
glm::vec3 myLookAtFromMouse(mouseRayOrigin + mouseRayDirection);
_myAvatar.getHead().setLookAtPosition(myLookAtFromMouse);
// If we are dragging on a voxel, add thrust according to the amount the mouse is dragging
const float VOXEL_GRAB_THRUST = 5.0f;
if (_mousePressed && (_mouseVoxel.s != 0)) {
glm::vec2 mouseDrag(_mouseX - _mouseDragStartedX, _mouseY - _mouseDragStartedY);
glm::quat orientation = _myAvatar.getOrientation();
glm::vec3 front = orientation * IDENTITY_FRONT;
glm::vec3 up = orientation * IDENTITY_UP;
glm::vec3 towardVoxel = getMouseVoxelWorldCoordinates(_mouseVoxelDragging)
- _myAvatar.getCameraPosition();
towardVoxel = front * glm::length(towardVoxel);
glm::vec3 lateralToVoxel = glm::cross(up, glm::normalize(towardVoxel)) * glm::length(towardVoxel);
_voxelThrust = glm::vec3(0, 0, 0);
_voxelThrust += towardVoxel * VOXEL_GRAB_THRUST * deltaTime * mouseDrag.y;
_voxelThrust += lateralToVoxel * VOXEL_GRAB_THRUST * deltaTime * mouseDrag.x;
// Add thrust from voxel grabbing to the avatar
_myAvatar.addThrust(_voxelThrust);
}
_mouseVoxel.s = 0.0f; _mouseVoxel.s = 0.0f;
if (checkedVoxelModeAction() != 0 && if (checkedVoxelModeAction() != 0 &&
(fabs(_myAvatar.getVelocity().x) + (fabs(_myAvatar.getVelocity().x) +
@ -1554,30 +1604,27 @@ void Application::update(float deltaTime) {
_myAvatar.simulate(deltaTime, NULL); _myAvatar.simulate(deltaTime, NULL);
} }
if (TESTING_AVATAR_TOUCH) {
if (_myCamera.getMode() != CAMERA_MODE_THIRD_PERSON) {
_myCamera.setMode(CAMERA_MODE_THIRD_PERSON);
_myCamera.setModeShiftRate(1.0f);
}
} else {
if (_myCamera.getMode() != CAMERA_MODE_MIRROR && !OculusManager::isConnected()) { if (_myCamera.getMode() != CAMERA_MODE_MIRROR && !OculusManager::isConnected()) {
if (_manualFirstPerson) { if (_manualFirstPerson->isChecked()) {
if (_myCamera.getMode() != CAMERA_MODE_FIRST_PERSON ) { if (_myCamera.getMode() != CAMERA_MODE_FIRST_PERSON ) {
_myCamera.setMode(CAMERA_MODE_FIRST_PERSON); _myCamera.setMode(CAMERA_MODE_FIRST_PERSON);
_myCamera.setModeShiftRate(1.0f); _myCamera.setModeShiftRate(1.0f);
} }
} else { } else {
if (_myAvatar.getIsNearInteractingOther()) { const float THIRD_PERSON_SHIFT_VELOCITY = 2.0f;
if (_myCamera.getMode() != CAMERA_MODE_FIRST_PERSON) { const float TIME_BEFORE_SHIFT_INTO_FIRST_PERSON = 0.75f;
_myCamera.setMode(CAMERA_MODE_FIRST_PERSON); const float TIME_BEFORE_SHIFT_INTO_THIRD_PERSON = 0.1f;
_myCamera.setModeShiftRate(1.0f);
} if ((_myAvatar.getElapsedTimeStopped() > TIME_BEFORE_SHIFT_INTO_FIRST_PERSON)
} else { && (_myCamera.getMode() != CAMERA_MODE_FIRST_PERSON)) {
if (_myCamera.getMode() != CAMERA_MODE_THIRD_PERSON) { _myCamera.setMode(CAMERA_MODE_FIRST_PERSON);
_myCamera.setMode(CAMERA_MODE_THIRD_PERSON); _myCamera.setModeShiftRate(1.0f);
_myCamera.setModeShiftRate(1.0f);
}
} }
if ((_myAvatar.getSpeed() > THIRD_PERSON_SHIFT_VELOCITY)
&& (_myAvatar.getElapsedTimeMoving() > TIME_BEFORE_SHIFT_INTO_THIRD_PERSON)
&& (_myCamera.getMode() != CAMERA_MODE_THIRD_PERSON)) {
_myCamera.setMode(CAMERA_MODE_THIRD_PERSON);
_myCamera.setModeShiftRate(1000.0f);
} }
} }
} }
@ -1664,7 +1711,7 @@ void Application::updateAvatar(float deltaTime) {
endOfBroadcastStringWrite += _myAvatar.getBroadcastData(endOfBroadcastStringWrite); endOfBroadcastStringWrite += _myAvatar.getBroadcastData(endOfBroadcastStringWrite);
const char broadcastReceivers[2] = {AGENT_TYPE_VOXEL, AGENT_TYPE_AVATAR_MIXER}; const char broadcastReceivers[2] = {AGENT_TYPE_VOXEL_SERVER, AGENT_TYPE_AVATAR_MIXER};
AgentList::getInstance()->broadcastToAgents(broadcastString, endOfBroadcastStringWrite - broadcastString, broadcastReceivers, sizeof(broadcastReceivers)); AgentList::getInstance()->broadcastToAgents(broadcastString, endOfBroadcastStringWrite - broadcastString, broadcastReceivers, sizeof(broadcastReceivers));
// once in a while, send my voxel url // once in a while, send my voxel url
@ -1945,6 +1992,10 @@ void Application::displaySide(Camera& whichCamera) {
glEnable(GL_LIGHTING); glEnable(GL_LIGHTING);
glEnable(GL_DEPTH_TEST); glEnable(GL_DEPTH_TEST);
// Enable to show line from me to the voxel I am touching
//renderLineToTouchedVoxel();
//renderThrustAtVoxel(_voxelThrust);
// draw a red sphere // draw a red sphere
float sphereRadius = 0.25f; float sphereRadius = 0.25f;
glColor3f(1,0,0); glColor3f(1,0,0);
@ -1993,11 +2044,15 @@ void Application::displaySide(Camera& whichCamera) {
avatar->init(); avatar->init();
} }
avatar->render(false, _renderAvatarBalls->isChecked()); avatar->render(false, _renderAvatarBalls->isChecked());
avatar->setDisplayingLookatVectors(_renderLookatOn->isChecked());
} }
} }
agentList->unlock(); agentList->unlock();
// Render my own Avatar // Render my own Avatar
if (_myCamera.getMode() == CAMERA_MODE_MIRROR) {
_myAvatar.getHead().setLookAtPosition(_myCamera.getPosition());
}
_myAvatar.render(_lookingInMirror->isChecked(), _renderAvatarBalls->isChecked()); _myAvatar.render(_lookingInMirror->isChecked(), _renderAvatarBalls->isChecked());
_myAvatar.setDisplayingLookatVectors(_renderLookatOn->isChecked()); _myAvatar.setDisplayingLookatVectors(_renderLookatOn->isChecked());
} }
@ -2063,9 +2118,8 @@ void Application::displayOverlay() {
// Show on-screen msec timer // Show on-screen msec timer
if (_renderFrameTimerOn->isChecked()) { if (_renderFrameTimerOn->isChecked()) {
char frameTimer[10]; char frameTimer[10];
double mSecsNow = floor(usecTimestampNow() / 1000.0 + 0.5); long long mSecsNow = floor(usecTimestampNow() / 1000.0 + 0.5);
mSecsNow = mSecsNow - floor(mSecsNow / 1000.0) * 1000.0; sprintf(frameTimer, "%d\n", (int)(mSecsNow % 1000));
sprintf(frameTimer, "%3.0f\n", mSecsNow);
drawtext(_glWidget->width() - 100, _glWidget->height() - 20, 0.30, 0, 1.0, 0, frameTimer, 0, 0, 0); drawtext(_glWidget->width() - 100, _glWidget->height() - 20, 0.30, 0, 1.0, 0, frameTimer, 0, 0, 0);
drawtext(_glWidget->width() - 102, _glWidget->height() - 22, 0.30, 0, 1.0, 0, frameTimer, 1, 1, 1); drawtext(_glWidget->width() - 102, _glWidget->height() - 22, 0.30, 0, 1.0, 0, frameTimer, 1, 1, 1);
} }
@ -2161,6 +2215,32 @@ void Application::displayStats() {
} }
} }
void Application::renderThrustAtVoxel(const glm::vec3& thrust) {
if (_mousePressed) {
glColor3f(1, 0, 0);
glLineWidth(2.0f);
glBegin(GL_LINES);
glm::vec3 voxelTouched = getMouseVoxelWorldCoordinates(_mouseVoxelDragging);
glVertex3f(voxelTouched.x, voxelTouched.y, voxelTouched.z);
glVertex3f(voxelTouched.x + thrust.x, voxelTouched.y + thrust.y, voxelTouched.z + thrust.z);
glEnd();
}
}
void Application::renderLineToTouchedVoxel() {
// Draw a teal line to the voxel I am currently dragging on
if (_mousePressed) {
glColor3f(0, 1, 1);
glLineWidth(2.0f);
glBegin(GL_LINES);
glm::vec3 voxelTouched = getMouseVoxelWorldCoordinates(_mouseVoxelDragging);
glVertex3f(voxelTouched.x, voxelTouched.y, voxelTouched.z);
glm::vec3 headPosition = _myAvatar.getHeadJointPosition();
glVertex3fv(&headPosition.x);
glEnd();
}
}
///////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////
// renderViewFrustum() // renderViewFrustum()
// //
@ -2437,6 +2517,8 @@ void Application::resetSensors() {
QCursor::setPos(_headMouseX, _headMouseY); QCursor::setPos(_headMouseX, _headMouseY);
_myAvatar.reset(); _myAvatar.reset();
_myTransmitter.resetLevels(); _myTransmitter.resetLevels();
_myAvatar.setVelocity(glm::vec3(0,0,0));
_myAvatar.setThrust(glm::vec3(0,0,0));
} }
static void setShortcutsEnabled(QWidget* widget, bool enabled) { static void setShortcutsEnabled(QWidget* widget, bool enabled) {

View file

@ -67,6 +67,8 @@ public:
void wheelEvent(QWheelEvent* event); void wheelEvent(QWheelEvent* event);
const glm::vec3 getMouseVoxelWorldCoordinates(const VoxelDetail _mouseVoxel);
Avatar* getAvatar() { return &_myAvatar; } Avatar* getAvatar() { return &_myAvatar; }
Camera* getCamera() { return &_myCamera; } Camera* getCamera() { return &_myCamera; }
ViewFrustum* getViewFrustum() { return &_viewFrustum; } ViewFrustum* getViewFrustum() { return &_viewFrustum; }
@ -93,6 +95,9 @@ private slots:
void setRenderFirstPerson(bool firstPerson); void setRenderFirstPerson(bool firstPerson);
void renderThrustAtVoxel(const glm::vec3& thrust);
void renderLineToTouchedVoxel();
void setFrustumOffset(bool frustumOffset); void setFrustumOffset(bool frustumOffset);
void cycleFrustumRenderMode(); void cycleFrustumRenderMode();
@ -141,7 +146,7 @@ private:
void displayStats(); void displayStats();
void renderViewFrustum(ViewFrustum& viewFrustum); void renderViewFrustum(ViewFrustum& viewFrustum);
void setupPaintingVoxel(); void setupPaintingVoxel();
void shiftPaintingColor(); void shiftPaintingColor();
void maybeEditVoxelUnderCursor(); void maybeEditVoxelUnderCursor();
@ -186,6 +191,7 @@ private:
QAction* _renderStatsOn; // Whether to show onscreen text overlay with stats QAction* _renderStatsOn; // Whether to show onscreen text overlay with stats
QAction* _renderFrameTimerOn; // Whether to show onscreen text overlay with stats QAction* _renderFrameTimerOn; // Whether to show onscreen text overlay with stats
QAction* _renderLookatOn; // Whether to show lookat vectors from avatar eyes if looking at something QAction* _renderLookatOn; // Whether to show lookat vectors from avatar eyes if looking at something
QAction* _manualFirstPerson; // Whether to force first-person mode
QAction* _logOn; // Whether to show on-screen log QAction* _logOn; // Whether to show on-screen log
QActionGroup* _voxelModeActions; // The group of voxel edit mode actions QActionGroup* _voxelModeActions; // The group of voxel edit mode actions
QAction* _addVoxelMode; // Whether add voxel mode is enabled QAction* _addVoxelMode; // Whether add voxel mode is enabled
@ -249,14 +255,18 @@ private:
Environment _environment; Environment _environment;
int _headMouseX, _headMouseY; int _headMouseX, _headMouseY;
bool _manualFirstPerson;
float _headCameraPitchYawScale; float _headCameraPitchYawScale;
HandControl _handControl; HandControl _handControl;
int _mouseX; int _mouseX;
int _mouseY; int _mouseY;
int _mouseDragStartedX;
int _mouseDragStartedY;
VoxelDetail _mouseVoxelDragging;
glm::vec3 _voxelThrust;
bool _mousePressed; // true if mouse has been pressed (clear when finished) bool _mousePressed; // true if mouse has been pressed (clear when finished)
VoxelDetail _mouseVoxel; // details of the voxel under the mouse cursor VoxelDetail _mouseVoxel; // details of the voxel under the mouse cursor
float _mouseVoxelScale; // the scale for adding/removing voxels float _mouseVoxelScale; // the scale for adding/removing voxels

View file

@ -74,12 +74,13 @@ Avatar::Avatar(Agent* owningAgent) :
_bodyRollDelta(0.0f), _bodyRollDelta(0.0f),
_movedHandOffset(0.0f, 0.0f, 0.0f), _movedHandOffset(0.0f, 0.0f, 0.0f),
_mode(AVATAR_MODE_STANDING), _mode(AVATAR_MODE_STANDING),
_cameraPosition(0.0f, 0.0f, 0.0f),
_handHoldingPosition(0.0f, 0.0f, 0.0f), _handHoldingPosition(0.0f, 0.0f, 0.0f),
_velocity(0.0f, 0.0f, 0.0f), _velocity(0.0f, 0.0f, 0.0f),
_thrust(0.0f, 0.0f, 0.0f), _thrust(0.0f, 0.0f, 0.0f),
_shouldJump(false),
_speed(0.0f), _speed(0.0f),
_maxArmLength(0.0f), _maxArmLength(0.0f),
_leanScale(0.5f),
_pelvisStandingHeight(0.0f), _pelvisStandingHeight(0.0f),
_pelvisFloatingHeight(0.0f), _pelvisFloatingHeight(0.0f),
_distanceToNearestAvatar(std::numeric_limits<float>::max()), _distanceToNearestAvatar(std::numeric_limits<float>::max()),
@ -89,6 +90,9 @@ Avatar::Avatar(Agent* owningAgent) :
_mouseRayDirection(0.0f, 0.0f, 0.0f), _mouseRayDirection(0.0f, 0.0f, 0.0f),
_interactingOther(NULL), _interactingOther(NULL),
_isMouseTurningRight(false), _isMouseTurningRight(false),
_elapsedTimeMoving(0.0f),
_elapsedTimeStopped(0.0f),
_elapsedTimeSinceCollision(0.0f),
_voxels(this) _voxels(this)
{ {
// give the pointer to our head to inherited _headData variable from AvatarData // give the pointer to our head to inherited _headData variable from AvatarData
@ -103,9 +107,11 @@ Avatar::Avatar(Agent* owningAgent) :
initializeBodyBalls(); initializeBodyBalls();
_height = _skeleton.getHeight() + _bodyBall[ BODY_BALL_LEFT_HEEL ].radius + _bodyBall[ BODY_BALL_HEAD_BASE ].radius; _height = _skeleton.getHeight() + _bodyBall[ BODY_BALL_LEFT_HEEL ].radius + _bodyBall[ BODY_BALL_HEAD_BASE ].radius;
_maxArmLength = _skeleton.getArmLength(); _maxArmLength = _skeleton.getArmLength();
_pelvisStandingHeight = _skeleton.getPelvisStandingHeight() + _bodyBall[ BODY_BALL_LEFT_HEEL ].radius; _pelvisStandingHeight = _skeleton.getPelvisStandingHeight() + _bodyBall[ BODY_BALL_LEFT_HEEL ].radius;
_pelvisFloatingHeight = _skeleton.getPelvisFloatingHeight() + _bodyBall[ BODY_BALL_LEFT_HEEL ].radius; _pelvisFloatingHeight = _skeleton.getPelvisFloatingHeight() + _bodyBall[ BODY_BALL_LEFT_HEEL ].radius;
_pelvisToHeadLength = _skeleton.getPelvisToHeadLength();
_avatarTouch.setReachableRadius(PERIPERSONAL_RADIUS); _avatarTouch.setReachableRadius(PERIPERSONAL_RADIUS);
@ -265,6 +271,7 @@ Avatar::~Avatar() {
} }
void Avatar::init() { void Avatar::init() {
_head.init();
_voxels.init(); _voxels.init();
_initialized = true; _initialized = true;
} }
@ -284,19 +291,12 @@ void Avatar::updateHeadFromGyros(float deltaTime, SerialInterface* serialInterfa
_head.setYaw(estimatedRotation.y * AMPLIFY_YAW); _head.setYaw(estimatedRotation.y * AMPLIFY_YAW);
_head.setRoll(estimatedRotation.z * AMPLIFY_ROLL); _head.setRoll(estimatedRotation.z * AMPLIFY_ROLL);
// Update head lean distance based on accelerometer data // Update torso lean distance based on accelerometer data
glm::vec3 headRotationRates(_head.getPitch(), _head.getYaw(), _head.getRoll()); glm::vec3 estimatedPosition = serialInterface->getEstimatedPosition() * _leanScale;
const float TORSO_LENGTH = 0.5f;
glm::vec3 leaning = (serialInterface->getLastAcceleration() - serialInterface->getGravity()) const float MAX_LEAN = 45.0f;
* LEAN_SENSITIVITY _head.setLeanSideways(glm::clamp(glm::degrees(atanf(-estimatedPosition.x / TORSO_LENGTH)), -MAX_LEAN, MAX_LEAN));
* (1.f - fminf(glm::length(headRotationRates), HEAD_RATE_MAX) / HEAD_RATE_MAX); _head.setLeanForward(glm::clamp(glm::degrees(atanf(estimatedPosition.z / TORSO_LENGTH)), -MAX_LEAN, MAX_LEAN));
leaning.y = 0.f;
if (glm::length(leaning) < LEAN_MAX) {
_head.setLeanForward(_head.getLeanForward() * (1.f - LEAN_AVERAGING * deltaTime) +
(LEAN_AVERAGING * deltaTime) * leaning.z * LEAN_SENSITIVITY);
_head.setLeanSideways(_head.getLeanSideways() * (1.f - LEAN_AVERAGING * deltaTime) +
(LEAN_AVERAGING * deltaTime) * leaning.x * LEAN_SENSITIVITY);
}
} }
float Avatar::getAbsoluteHeadYaw() const { float Avatar::getAbsoluteHeadYaw() const {
@ -315,45 +315,141 @@ glm::quat Avatar::getWorldAlignedOrientation () const {
return computeRotationFromBodyToWorldUp() * getOrientation(); return computeRotationFromBodyToWorldUp() * getOrientation();
} }
glm::vec3 Avatar::getUprightHeadPosition() const {
return _position + getWorldAlignedOrientation() * glm::vec3(0.0f, _pelvisToHeadLength, 0.0f);
}
void Avatar::updateFromMouse(int mouseX, int mouseY, int screenWidth, int screenHeight) { void Avatar::updateFromMouse(int mouseX, int mouseY, int screenWidth, int screenHeight) {
// Update head yaw and pitch based on mouse input // Update head yaw and pitch based on mouse input
const float MOUSE_MOVE_RADIUS = 0.3f; const float MOUSE_ROTATE_SPEED = 0.01f;
const float MOUSE_ROTATE_SPEED = 4.0f; const float MOUSE_PITCH_SPEED = 0.02f;
const float MOUSE_PITCH_SPEED = 2.0f;
const int TITLE_BAR_HEIGHT = 46; const int TITLE_BAR_HEIGHT = 46;
float mouseLocationX = (float)mouseX / (float)screenWidth - 0.5f;
float mouseLocationY = (float)mouseY / (float)screenHeight - 0.5f;
if ((mouseX > 1) && (mouseX < screenWidth) && (mouseY > TITLE_BAR_HEIGHT) && (mouseY < screenHeight)) { if ((mouseX > 1) && (mouseX < screenWidth) && (mouseY > TITLE_BAR_HEIGHT) && (mouseY < screenHeight)) {
// //
// Mouse must be inside screen (not at edge) and not on title bar for movement to happen // Mouse must be inside screen (not at edge) and not on title bar for movement to happen
// //
if (mouseLocationX > MOUSE_MOVE_RADIUS) {
_head.addYaw(-(mouseLocationX - MOUSE_MOVE_RADIUS) / (0.5f - MOUSE_MOVE_RADIUS) * MOUSE_ROTATE_SPEED);
} else if (mouseLocationX < -MOUSE_MOVE_RADIUS) {
_head.addYaw(-(mouseLocationX + MOUSE_MOVE_RADIUS) / (0.5f - MOUSE_MOVE_RADIUS) * MOUSE_ROTATE_SPEED);
}
if (mouseLocationY > MOUSE_MOVE_RADIUS) { int pixelMoveThreshold = screenWidth / 6;
_head.addPitch(-(mouseLocationY - MOUSE_MOVE_RADIUS) / (0.5f - MOUSE_MOVE_RADIUS) * MOUSE_PITCH_SPEED); glm::vec2 mouseVector(mouseX - (screenWidth / 2), mouseY - (screenHeight / 2));
} else if (mouseLocationY < -MOUSE_MOVE_RADIUS) { if (glm::length(mouseVector) > pixelMoveThreshold) {
_head.addPitch(-(mouseLocationY + MOUSE_MOVE_RADIUS) / (0.5f - MOUSE_MOVE_RADIUS) * MOUSE_PITCH_SPEED); mouseVector -= glm::normalize(mouseVector) * (float) pixelMoveThreshold;
_head.addYaw(-mouseVector.x * MOUSE_ROTATE_SPEED);
_head.addPitch(-mouseVector.y * MOUSE_PITCH_SPEED);
} }
} }
return;
} }
void Avatar::updateThrust(float deltaTime, Transmitter * transmitter) {
//
// Gather thrust information from keyboard and sensors to apply to avatar motion
//
glm::quat orientation = getOrientation();
glm::vec3 front = orientation * IDENTITY_FRONT;
glm::vec3 right = orientation * IDENTITY_RIGHT;
glm::vec3 up = orientation * IDENTITY_UP;
const float THRUST_MAG_UP = 800.0f;
const float THRUST_MAG_DOWN = 200.f;
const float THRUST_MAG_FWD = 300.f;
const float THRUST_MAG_BACK = 150.f;
const float THRUST_MAG_LATERAL = 200.f;
const float THRUST_JUMP = 120.f;
// Add Thrusts from keyboard
if (_driveKeys[FWD ]) {_thrust += THRUST_MAG_FWD * deltaTime * front;}
if (_driveKeys[BACK ]) {_thrust -= THRUST_MAG_BACK * deltaTime * front;}
if (_driveKeys[RIGHT ]) {_thrust += THRUST_MAG_LATERAL * deltaTime * right;}
if (_driveKeys[LEFT ]) {_thrust -= THRUST_MAG_LATERAL * deltaTime * right;}
if (_driveKeys[UP ]) {_thrust += THRUST_MAG_UP * deltaTime * up;}
if (_driveKeys[DOWN ]) {_thrust -= THRUST_MAG_DOWN * deltaTime * up;}
if (_driveKeys[ROT_RIGHT]) {_bodyYawDelta -= YAW_MAG * deltaTime;}
if (_driveKeys[ROT_LEFT ]) {_bodyYawDelta += YAW_MAG * deltaTime;}
// Add one time jumping force if requested
if (_shouldJump) {
_thrust += THRUST_JUMP * up;
_shouldJump = false;
}
// Add thrusts from Transmitter
if (transmitter) {
transmitter->checkForLostTransmitter();
glm::vec3 rotation = transmitter->getEstimatedRotation();
const float TRANSMITTER_MIN_RATE = 1.f;
const float TRANSMITTER_MIN_YAW_RATE = 4.f;
const float TRANSMITTER_LATERAL_FORCE_SCALE = 5.f;
const float TRANSMITTER_FWD_FORCE_SCALE = 25.f;
const float TRANSMITTER_UP_FORCE_SCALE = 100.f;
const float TRANSMITTER_YAW_SCALE = 10.0f;
const float TRANSMITTER_LIFT_SCALE = 3.f;
const float TOUCH_POSITION_RANGE_HALF = 32767.f;
if (fabs(rotation.z) > TRANSMITTER_MIN_RATE) {
_thrust += rotation.z * TRANSMITTER_LATERAL_FORCE_SCALE * deltaTime * right;
}
if (fabs(rotation.x) > TRANSMITTER_MIN_RATE) {
_thrust += -rotation.x * TRANSMITTER_FWD_FORCE_SCALE * deltaTime * front;
}
if (fabs(rotation.y) > TRANSMITTER_MIN_YAW_RATE) {
_bodyYawDelta += rotation.y * TRANSMITTER_YAW_SCALE * deltaTime;
}
if (transmitter->getTouchState()->state == 'D') {
_thrust += TRANSMITTER_UP_FORCE_SCALE *
(float)(transmitter->getTouchState()->y - TOUCH_POSITION_RANGE_HALF) / TOUCH_POSITION_RANGE_HALF *
TRANSMITTER_LIFT_SCALE *
deltaTime *
up;
}
}
}
void Avatar::simulate(float deltaTime, Transmitter* transmitter) { void Avatar::simulate(float deltaTime, Transmitter* transmitter) {
//figure out if the mouse cursor is over any body spheres... glm::quat orientation = getOrientation();
checkForMouseRayTouching(); glm::vec3 front = orientation * IDENTITY_FRONT;
glm::vec3 right = orientation * IDENTITY_RIGHT;
// Update movement timers
if (!_owningAgent) {
_elapsedTimeSinceCollision += deltaTime;
const float VELOCITY_MOVEMENT_TIMER_THRESHOLD = 0.2f;
if (glm::length(_velocity) < VELOCITY_MOVEMENT_TIMER_THRESHOLD) {
_elapsedTimeMoving = 0.f;
_elapsedTimeStopped += deltaTime;
} else {
_elapsedTimeStopped = 0.f;
_elapsedTimeMoving += deltaTime;
}
}
// Collect thrust forces from keyboard and devices
if (!_owningAgent) {
updateThrust(deltaTime, transmitter);
}
// copy velocity so we can use it later for acceleration // copy velocity so we can use it later for acceleration
glm::vec3 oldVelocity = getVelocity(); glm::vec3 oldVelocity = getVelocity();
if (!_owningAgent) {
// update position by velocity
_position += _velocity * deltaTime;
// calculate speed
_speed = glm::length(_velocity);
}
//figure out if the mouse cursor is over any body spheres...
if (!_owningAgent) {
checkForMouseRayTouching();
}
// update balls // update balls
if (_balls) { _balls->simulate(deltaTime); } if (_balls) { _balls->simulate(deltaTime); }
// update torso rotation based on head lean
_skeleton.joint[AVATAR_JOINT_TORSO].rotation = glm::quat(glm::radians(glm::vec3(
_head.getLeanForward(), 0.0f, _head.getLeanSideways())));
// update avatar skeleton // update avatar skeleton
_skeleton.update(deltaTime, getOrientation(), _position); _skeleton.update(deltaTime, getOrientation(), _position);
@ -388,12 +484,13 @@ void Avatar::simulate(float deltaTime, Transmitter* transmitter) {
//update the movement of the hand and process handshaking with other avatars... //update the movement of the hand and process handshaking with other avatars...
updateHandMovementAndTouching(deltaTime); updateHandMovementAndTouching(deltaTime);
_avatarTouch.simulate(deltaTime); _avatarTouch.simulate(deltaTime);
// apply gravity and collision with the ground/floor // apply gravity and collision with the ground/floor
if (!_owningAgent && USING_AVATAR_GRAVITY) { if (!_owningAgent && USING_AVATAR_GRAVITY) {
_velocity += _gravity * (GRAVITY_EARTH * deltaTime); _velocity += _gravity * (GRAVITY_EARTH * deltaTime);
}
if (!_owningAgent) {
updateCollisionWithEnvironment(); updateCollisionWithEnvironment();
} }
@ -410,57 +507,11 @@ void Avatar::simulate(float deltaTime, Transmitter* transmitter) {
updateCollisionWithVoxels(); updateCollisionWithVoxels();
} }
glm::quat orientation = getOrientation();
glm::vec3 front = orientation * IDENTITY_FRONT;
glm::vec3 right = orientation * IDENTITY_RIGHT;
glm::vec3 up = orientation * IDENTITY_UP;
// driving the avatar around should only apply if this is my avatar (as opposed to an avatar being driven remotely)
const float THRUST_MAG = 600.0f;
if (!_owningAgent) { if (!_owningAgent) {
_thrust = glm::vec3(0.0f, 0.0f, 0.0f); // add thrust to velocity
_velocity += _thrust * deltaTime;
// Add Thrusts from keyboard
if (_driveKeys[FWD ]) {_thrust += THRUST_MAG * deltaTime * front;}
if (_driveKeys[BACK ]) {_thrust -= THRUST_MAG * deltaTime * front;}
if (_driveKeys[RIGHT ]) {_thrust += THRUST_MAG * deltaTime * right;}
if (_driveKeys[LEFT ]) {_thrust -= THRUST_MAG * deltaTime * right;}
if (_driveKeys[UP ]) {_thrust += THRUST_MAG * deltaTime * up;}
if (_driveKeys[DOWN ]) {_thrust -= THRUST_MAG * deltaTime * up;}
if (_driveKeys[ROT_RIGHT]) {_bodyYawDelta -= YAW_MAG * deltaTime;}
if (_driveKeys[ROT_LEFT ]) {_bodyYawDelta += YAW_MAG * deltaTime;}
// Add thrusts from Transmitter
if (transmitter) {
transmitter->checkForLostTransmitter();
glm::vec3 rotation = transmitter->getEstimatedRotation();
const float TRANSMITTER_MIN_RATE = 1.f;
const float TRANSMITTER_MIN_YAW_RATE = 4.f;
const float TRANSMITTER_LATERAL_FORCE_SCALE = 25.f;
const float TRANSMITTER_FWD_FORCE_SCALE = 100.f;
const float TRANSMITTER_YAW_SCALE = 10.0f;
const float TRANSMITTER_LIFT_SCALE = 3.f;
const float TOUCH_POSITION_RANGE_HALF = 32767.f;
if (fabs(rotation.z) > TRANSMITTER_MIN_RATE) {
_thrust += rotation.z * TRANSMITTER_LATERAL_FORCE_SCALE * deltaTime * right;
}
if (fabs(rotation.x) > TRANSMITTER_MIN_RATE) {
_thrust += -rotation.x * TRANSMITTER_FWD_FORCE_SCALE * deltaTime * front;
}
if (fabs(rotation.y) > TRANSMITTER_MIN_YAW_RATE) {
_bodyYawDelta += rotation.y * TRANSMITTER_YAW_SCALE * deltaTime;
}
if (transmitter->getTouchState()->state == 'D') {
_thrust += THRUST_MAG *
(float)(transmitter->getTouchState()->y - TOUCH_POSITION_RANGE_HALF) / TOUCH_POSITION_RANGE_HALF *
TRANSMITTER_LIFT_SCALE *
deltaTime *
up;
}
}
// update body yaw by body yaw delta // update body yaw by body yaw delta
orientation = orientation * glm::quat(glm::radians( orientation = orientation * glm::quat(glm::radians(
glm::vec3(_bodyPitchDelta, _bodyYawDelta, _bodyRollDelta) * deltaTime)); glm::vec3(_bodyPitchDelta, _bodyYawDelta, _bodyRollDelta) * deltaTime));
@ -472,12 +523,22 @@ void Avatar::simulate(float deltaTime, Transmitter* transmitter) {
_bodyYawDelta *= bodySpinMomentum; _bodyYawDelta *= bodySpinMomentum;
_bodyRollDelta *= bodySpinMomentum; _bodyRollDelta *= bodySpinMomentum;
// add thrust to velocity // Decay velocity. If velocity is really low, increase decay to simulate static friction
_velocity += _thrust * deltaTime; const float VELOCITY_DECAY_UNDER_THRUST = 0.2;
const float VELOCITY_FAST_DECAY = 0.6;
// calculate speed const float VELOCITY_SLOW_DECAY = 3.0;
_speed = glm::length(_velocity); const float VELOCITY_FAST_THRESHOLD = 2.0f;
float decayConstant, decay;
if (glm::length(_thrust) > 0.f) {
decayConstant = VELOCITY_DECAY_UNDER_THRUST;
} else if (glm::length(_velocity) > VELOCITY_FAST_THRESHOLD) {
decayConstant = VELOCITY_FAST_DECAY;
} else {
decayConstant = VELOCITY_SLOW_DECAY;
}
decay = glm::clamp(1.0f - decayConstant * deltaTime, 0.0f, 1.0f);
_velocity *= decay;
//pitch and roll the body as a function of forward speed and turning delta //pitch and roll the body as a function of forward speed and turning delta
const float BODY_PITCH_WHILE_WALKING = -20.0; const float BODY_PITCH_WHILE_WALKING = -20.0;
const float BODY_ROLL_WHILE_TURNING = 0.2; const float BODY_ROLL_WHILE_TURNING = 0.2;
@ -497,29 +558,6 @@ void Avatar::simulate(float deltaTime, Transmitter* transmitter) {
//the following will be used to make the avatar upright no matter what gravity is //the following will be used to make the avatar upright no matter what gravity is
setOrientation(computeRotationFromBodyToWorldUp(tiltDecay) * orientation); setOrientation(computeRotationFromBodyToWorldUp(tiltDecay) * orientation);
// update position by velocity
_position += _velocity * deltaTime;
// decay velocity
const float VELOCITY_DECAY = 0.9;
float decay = 1.0 - VELOCITY_DECAY * deltaTime;
if ( decay < 0.0 ) {
_velocity = glm::vec3( 0.0f, 0.0f, 0.0f );
} else {
_velocity *= decay;
}
// If another avatar is near, dampen velocity as a function of closeness
if (_distanceToNearestAvatar < PERIPERSONAL_RADIUS) {
float closeness = 1.0f - (_distanceToNearestAvatar / PERIPERSONAL_RADIUS);
float drag = 1.0f - closeness * AVATAR_BRAKING_STRENGTH * deltaTime;
if ( drag > 0.0f ) {
_velocity *= drag;
} else {
_velocity = glm::vec3( 0.0f, 0.0f, 0.0f );
}
}
// Compute instantaneous acceleration // Compute instantaneous acceleration
float forwardAcceleration = glm::length(glm::dot(getBodyFrontDirection(), getVelocity() - oldVelocity)) / deltaTime; float forwardAcceleration = glm::length(glm::dot(getBodyFrontDirection(), getVelocity() - oldVelocity)) / deltaTime;
const float ACCELERATION_PITCH_DECAY = 0.4f; const float ACCELERATION_PITCH_DECAY = 0.4f;
@ -578,15 +616,6 @@ void Avatar::simulate(float deltaTime, Transmitter* transmitter) {
} }
} }
// set head lookat position
if (!_owningAgent) {
if (_interactingOther) {
_head.setLookAtPosition(_interactingOther->caclulateAverageEyePosition());
} else {
_head.setLookAtPosition(glm::vec3(0.0f, 0.0f, 0.0f)); // 0,0,0 represents NOT looking at anything
}
}
_head.setBodyRotation (glm::vec3(_bodyPitch, _bodyYaw, _bodyRoll)); _head.setBodyRotation (glm::vec3(_bodyPitch, _bodyYaw, _bodyRoll));
_head.setPosition(_bodyBall[ BODY_BALL_HEAD_BASE ].position); _head.setPosition(_bodyBall[ BODY_BALL_HEAD_BASE ].position);
_head.setScale (_bodyBall[ BODY_BALL_HEAD_BASE ].radius); _head.setScale (_bodyBall[ BODY_BALL_HEAD_BASE ].radius);
@ -599,6 +628,10 @@ void Avatar::simulate(float deltaTime, Transmitter* transmitter) {
} else { } else {
_mode = AVATAR_MODE_INTERACTING; _mode = AVATAR_MODE_INTERACTING;
} }
// Zero thrust out now that we've added it to velocity in this frame
_thrust = glm::vec3(0, 0, 0);
} }
void Avatar::checkForMouseRayTouching() { void Avatar::checkForMouseRayTouching() {
@ -779,41 +812,52 @@ void Avatar::updateCollisionWithSphere(glm::vec3 position, float radius, float d
} }
void Avatar::updateCollisionWithEnvironment() { void Avatar::updateCollisionWithEnvironment() {
glm::vec3 up = getBodyUpDirection(); glm::vec3 up = getBodyUpDirection();
float radius = _height * 0.125f; float radius = _height * 0.125f;
const float ENVIRONMENT_SURFACE_ELASTICITY = 1.0f;
const float ENVIRONMENT_SURFACE_DAMPING = 0.01;
glm::vec3 penetration; glm::vec3 penetration;
if (Application::getInstance()->getEnvironment()->findCapsulePenetration( if (Application::getInstance()->getEnvironment()->findCapsulePenetration(
_position - up * (_pelvisFloatingHeight - radius), _position - up * (_pelvisFloatingHeight - radius),
_position + up * (_height - _pelvisFloatingHeight - radius), radius, penetration)) { _position + up * (_height - _pelvisFloatingHeight - radius), radius, penetration)) {
applyCollisionWithScene(penetration); applyHardCollision(penetration, ENVIRONMENT_SURFACE_ELASTICITY, ENVIRONMENT_SURFACE_DAMPING);
} }
} }
void Avatar::updateCollisionWithVoxels() { void Avatar::updateCollisionWithVoxels() {
float radius = _height * 0.125f; float radius = _height * 0.125f;
const float VOXEL_ELASTICITY = 1.4f;
const float VOXEL_DAMPING = 0.0;
glm::vec3 penetration; glm::vec3 penetration;
if (Application::getInstance()->getVoxels()->findCapsulePenetration( if (Application::getInstance()->getVoxels()->findCapsulePenetration(
_position - glm::vec3(0.0f, _pelvisFloatingHeight - radius, 0.0f), _position - glm::vec3(0.0f, _pelvisFloatingHeight - radius, 0.0f),
_position + glm::vec3(0.0f, _height - _pelvisFloatingHeight - radius, 0.0f), radius, penetration)) { _position + glm::vec3(0.0f, _height - _pelvisFloatingHeight - radius, 0.0f), radius, penetration)) {
applyCollisionWithScene(penetration); applyHardCollision(penetration, VOXEL_ELASTICITY, VOXEL_DAMPING);
} }
} }
void Avatar::applyCollisionWithScene(const glm::vec3& penetration) { void Avatar::applyHardCollision(const glm::vec3& penetration, float elasticity, float damping) {
//
// Update the avatar in response to a hard collision. Position will be reset exactly
// to outside the colliding surface. Velocity will be modified according to elasticity.
//
// if elasticity = 1.0, collision is inelastic.
// if elasticity > 1.0, collision is elastic.
//
_position -= penetration; _position -= penetration;
static float STATIC_FRICTION_VELOCITY = 0.15f; static float HALTING_VELOCITY = 0.2f;
static float STATIC_FRICTION_DAMPING = 0.0f;
static float KINETIC_FRICTION_DAMPING = 0.95f;
// cancel out the velocity component in the direction of penetration // cancel out the velocity component in the direction of penetration
float penetrationLength = glm::length(penetration); float penetrationLength = glm::length(penetration);
if (penetrationLength > EPSILON) { if (penetrationLength > EPSILON) {
_elapsedTimeSinceCollision = 0.0f;
glm::vec3 direction = penetration / penetrationLength; glm::vec3 direction = penetration / penetrationLength;
_velocity -= glm::dot(_velocity, direction) * direction; _velocity -= glm::dot(_velocity, direction) * direction * elasticity;
_velocity *= KINETIC_FRICTION_DAMPING; _velocity *= glm::clamp(1.f - damping, 0.0f, 1.0f);
// If velocity is quite low, apply static friction that takes away energy if ((glm::length(_velocity) < HALTING_VELOCITY) && (glm::length(_thrust) == 0.f)) {
if (glm::length(_velocity) < STATIC_FRICTION_VELOCITY) { // If moving really slowly after a collision, and not applying forces, stop altogether
_velocity *= STATIC_FRICTION_DAMPING; _velocity *= 0.f;
} }
} }
} }
@ -836,7 +880,6 @@ void Avatar::updateAvatarCollisions(float deltaTime) {
// apply forces from collision // apply forces from collision
applyCollisionWithOtherAvatar(otherAvatar, deltaTime); applyCollisionWithOtherAvatar(otherAvatar, deltaTime);
} }
// test other avatar hand position for proximity // test other avatar hand position for proximity
glm::vec3 v(_skeleton.joint[ AVATAR_JOINT_RIGHT_SHOULDER ].position); glm::vec3 v(_skeleton.joint[ AVATAR_JOINT_RIGHT_SHOULDER ].position);
v -= otherAvatar->getPosition(); v -= otherAvatar->getPosition();
@ -911,9 +954,7 @@ void Avatar::setGravity(glm::vec3 gravity) {
} }
void Avatar::render(bool lookingInMirror, bool renderAvatarBalls) { void Avatar::render(bool lookingInMirror, bool renderAvatarBalls) {
_cameraPosition = Application::getInstance()->getCamera()->getPosition();
if (!_owningAgent && usingBigSphereCollisionTest) { if (!_owningAgent && usingBigSphereCollisionTest) {
// show TEST big sphere // show TEST big sphere
glColor4f(0.5f, 0.6f, 0.8f, 0.7); glColor4f(0.5f, 0.6f, 0.8f, 0.7);
@ -932,7 +973,7 @@ void Avatar::render(bool lookingInMirror, bool renderAvatarBalls) {
// if this is my avatar, then render my interactions with the other avatar // if this is my avatar, then render my interactions with the other avatar
if (!_owningAgent) { if (!_owningAgent) {
_avatarTouch.render(getCameraPosition()); _avatarTouch.render(Application::getInstance()->getCamera()->getPosition());
} }
// Render the balls // Render the balls
@ -1130,32 +1171,27 @@ glm::quat Avatar::computeRotationFromBodyToWorldUp(float proportion) const {
return glm::angleAxis(angle * proportion, axis); return glm::angleAxis(angle * proportion, axis);
} }
float Avatar::getBallRenderAlpha(int ball, bool lookingInMirror) const {
const float RENDER_OPAQUE_OUTSIDE = 1.25f; // render opaque if greater than this distance
const float DO_NOT_RENDER_INSIDE = 0.75f; // do not render if less than this distance
float distanceToCamera = glm::length(Application::getInstance()->getCamera()->getPosition() - _bodyBall[ball].position);
return (lookingInMirror || _owningAgent) ? 1.0f : glm::clamp(
(distanceToCamera - DO_NOT_RENDER_INSIDE) / (RENDER_OPAQUE_OUTSIDE - DO_NOT_RENDER_INSIDE), 0.f, 1.f);
}
void Avatar::renderBody(bool lookingInMirror, bool renderAvatarBalls) { void Avatar::renderBody(bool lookingInMirror, bool renderAvatarBalls) {
const float RENDER_OPAQUE_BEYOND = 1.0f; // Meters beyond which body is shown opaque
const float RENDER_TRANSLUCENT_BEYOND = 0.5f;
// Render the body as balls and cones // Render the body as balls and cones
if (renderAvatarBalls || !_voxels.getVoxelURL().isValid()) { if (renderAvatarBalls || !_voxels.getVoxelURL().isValid()) {
for (int b = 0; b < NUM_AVATAR_BODY_BALLS; b++) { for (int b = 0; b < NUM_AVATAR_BODY_BALLS; b++) {
float distanceToCamera = glm::length(_cameraPosition - _bodyBall[b].position); float alpha = getBallRenderAlpha(b, lookingInMirror);
float alpha = lookingInMirror ? 1.0f : glm::clamp((distanceToCamera - RENDER_TRANSLUCENT_BEYOND) /
(RENDER_OPAQUE_BEYOND - RENDER_TRANSLUCENT_BEYOND), 0.f, 1.f);
if (lookingInMirror || _owningAgent) {
alpha = 1.0f;
}
// Always render other people, and render myself when beyond threshold distance // Always render other people, and render myself when beyond threshold distance
if (b == BODY_BALL_HEAD_BASE) { // the head is rendered as a special if (b == BODY_BALL_HEAD_BASE) { // the head is rendered as a special
if (lookingInMirror || _owningAgent || distanceToCamera > RENDER_OPAQUE_BEYOND * 0.5) { if (alpha > 0.0f) {
_head.render(lookingInMirror, _cameraPosition, alpha); _head.render(lookingInMirror, alpha);
} }
} else if (_owningAgent || distanceToCamera > RENDER_TRANSLUCENT_BEYOND } else if (alpha > 0.0f) {
|| b == BODY_BALL_RIGHT_ELBOW
|| b == BODY_BALL_RIGHT_WRIST
|| b == BODY_BALL_RIGHT_FINGERTIPS ) {
// Render the body ball sphere // Render the body ball sphere
if (_owningAgent || b == BODY_BALL_RIGHT_ELBOW if (_owningAgent || b == BODY_BALL_RIGHT_ELBOW
|| b == BODY_BALL_RIGHT_WRIST || b == BODY_BALL_RIGHT_WRIST
@ -1207,7 +1243,10 @@ void Avatar::renderBody(bool lookingInMirror, bool renderAvatarBalls) {
} }
} else { } else {
// Render the body's voxels // Render the body's voxels
_voxels.render(false); float alpha = getBallRenderAlpha(BODY_BALL_HEAD_BASE, lookingInMirror);
if (alpha > 0.0f) {
_voxels.render(false);
}
} }
} }
@ -1225,6 +1264,8 @@ void Avatar::loadData(QSettings* settings) {
_voxels.setVoxelURL(settings->value("voxelURL").toUrl()); _voxels.setVoxelURL(settings->value("voxelURL").toUrl());
_leanScale = loadSetting(settings, "leanScale", 0.5f);
settings->endGroup(); settings->endGroup();
} }
@ -1246,6 +1287,8 @@ void Avatar::saveData(QSettings* set) {
set->setValue("voxelURL", _voxels.getVoxelURL()); set->setValue("voxelURL", _voxels.getVoxelURL());
set->setValue("leanScale", _leanScale);
set->endGroup(); set->endGroup();
} }

View file

@ -85,6 +85,7 @@ public:
void init(); void init();
void reset(); void reset();
void simulate(float deltaTime, Transmitter* transmitter); void simulate(float deltaTime, Transmitter* transmitter);
void updateThrust(float deltaTime, Transmitter * transmitter);
void updateHeadFromGyros(float frametime, SerialInterface * serialInterface); void updateHeadFromGyros(float frametime, SerialInterface * serialInterface);
void updateFromMouse(int mouseX, int mouseY, int screenWidth, int screenHeight); void updateFromMouse(int mouseX, int mouseY, int screenWidth, int screenHeight);
void addBodyYaw(float y) {_bodyYaw += y;}; void addBodyYaw(float y) {_bodyYaw += y;};
@ -96,6 +97,8 @@ public:
void setMovedHandOffset (glm::vec3 movedHandOffset ) { _movedHandOffset = movedHandOffset;} void setMovedHandOffset (glm::vec3 movedHandOffset ) { _movedHandOffset = movedHandOffset;}
void setThrust (glm::vec3 newThrust ) { _thrust = newThrust; }; void setThrust (glm::vec3 newThrust ) { _thrust = newThrust; };
void setDisplayingLookatVectors(bool displayingLookatVectors) { _head.setRenderLookatVectors(displayingLookatVectors);} void setDisplayingLookatVectors(bool displayingLookatVectors) { _head.setRenderLookatVectors(displayingLookatVectors);}
void setVelocity (const glm::vec3 velocity ) { _velocity = velocity; };
void setLeanScale (float scale ) { _leanScale = scale;}
void setGravity (glm::vec3 gravity); void setGravity (glm::vec3 gravity);
void setMouseRay (const glm::vec3 &origin, const glm::vec3 &direction); void setMouseRay (const glm::vec3 &origin, const glm::vec3 &direction);
void setOrientation (const glm::quat& orientation); void setOrientation (const glm::quat& orientation);
@ -115,17 +118,24 @@ public:
float getSpeed () const { return _speed;} float getSpeed () const { return _speed;}
float getHeight () const { return _height;} float getHeight () const { return _height;}
AvatarMode getMode () const { return _mode;} AvatarMode getMode () const { return _mode;}
float getLeanScale () const { return _leanScale;}
float getElapsedTimeStopped () const { return _elapsedTimeStopped;}
float getElapsedTimeMoving () const { return _elapsedTimeMoving;}
float getElapsedTimeSinceCollision() const { return _elapsedTimeSinceCollision;}
float getAbsoluteHeadYaw () const; float getAbsoluteHeadYaw () const;
float getAbsoluteHeadPitch () const; float getAbsoluteHeadPitch () const;
Head& getHead () {return _head; } Head& getHead () {return _head; }
glm::quat getOrientation () const; glm::quat getOrientation () const;
glm::quat getWorldAlignedOrientation() const; glm::quat getWorldAlignedOrientation() const;
glm::vec3 getUprightHeadPosition() const;
AvatarVoxelSystem* getVoxels() { return &_voxels; } AvatarVoxelSystem* getVoxels() { return &_voxels; }
// 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; };
bool getDriveKeys(int key) { return _driveKeys[key]; }; bool getDriveKeys(int key) { return _driveKeys[key]; };
void jump() { _shouldJump = true; };
// Set/Get update the thrust that will move the avatar around // Set/Get update the thrust that will move the avatar around
void addThrust(glm::vec3 newThrust) { _thrust += newThrust; }; void addThrust(glm::vec3 newThrust) { _thrust += newThrust; };
@ -175,32 +185,37 @@ private:
glm::vec3 _movedHandOffset; glm::vec3 _movedHandOffset;
AvatarBall _bodyBall[ NUM_AVATAR_BODY_BALLS ]; AvatarBall _bodyBall[ NUM_AVATAR_BODY_BALLS ];
AvatarMode _mode; AvatarMode _mode;
glm::vec3 _cameraPosition;
glm::vec3 _handHoldingPosition; glm::vec3 _handHoldingPosition;
glm::vec3 _velocity; glm::vec3 _velocity;
glm::vec3 _thrust; glm::vec3 _thrust;
bool _shouldJump;
float _speed; float _speed;
float _maxArmLength; float _maxArmLength;
glm::quat _righting; float _leanScale;
int _driveKeys[MAX_DRIVE_KEYS]; int _driveKeys[MAX_DRIVE_KEYS];
float _pelvisStandingHeight; float _pelvisStandingHeight;
float _pelvisFloatingHeight; float _pelvisFloatingHeight;
float _pelvisToHeadLength;
float _height; float _height;
Balls* _balls; Balls* _balls;
AvatarTouch _avatarTouch; AvatarTouch _avatarTouch;
float _distanceToNearestAvatar; // How close is the nearest avatar? float _distanceToNearestAvatar; // How close is the nearest avatar?
glm::vec3 _gravity; glm::vec3 _gravity;
glm::vec3 _worldUpDirection; glm::vec3 _worldUpDirection;
glm::vec3 _mouseRayOrigin; glm::vec3 _mouseRayOrigin;
glm::vec3 _mouseRayDirection; glm::vec3 _mouseRayDirection;
Avatar* _interactingOther; Avatar* _interactingOther;
bool _isMouseTurningRight; bool _isMouseTurningRight;
float _elapsedTimeMoving; // Timers to drive camera transitions when moving
float _elapsedTimeStopped;
float _elapsedTimeSinceCollision;
AvatarVoxelSystem _voxels; AvatarVoxelSystem _voxels;
// private methods... // private methods...
glm::vec3 caclulateAverageEyePosition() { return _head.caclulateAverageEyePosition(); } // 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)
glm::quat computeRotationFromBodyToWorldUp(float proportion = 1.0f) const; glm::quat computeRotationFromBodyToWorldUp(float proportion = 1.0f) const;
float getBallRenderAlpha(int ball, bool lookingInMirror) const;
void renderBody(bool lookingInMirror, bool renderAvatarBalls); void renderBody(bool lookingInMirror, bool renderAvatarBalls);
void initializeBodyBalls(); void initializeBodyBalls();
void resetBodyBalls(); void resetBodyBalls();
@ -213,7 +228,7 @@ private:
void updateCollisionWithSphere( glm::vec3 position, float radius, float deltaTime ); void updateCollisionWithSphere( glm::vec3 position, float radius, float deltaTime );
void updateCollisionWithEnvironment(); void updateCollisionWithEnvironment();
void updateCollisionWithVoxels(); void updateCollisionWithVoxels();
void applyCollisionWithScene(const glm::vec3& penetration); void applyHardCollision(const glm::vec3& penetration, float elasticity, float damping);
void applyCollisionWithOtherAvatar( Avatar * other, float deltaTime ); void applyCollisionWithOtherAvatar( Avatar * other, float deltaTime );
void checkForMouseRayTouching(); void checkForMouseRayTouching();
void renderJointConnectingCone(glm::vec3 position1, glm::vec3 position2, float radius1, float radius2); void renderJointConnectingCone(glm::vec3 position1, glm::vec3 position2, float radius1, float radius2);

View file

@ -17,7 +17,7 @@
#include "renderer/ProgramObject.h" #include "renderer/ProgramObject.h"
const float AVATAR_TREE_SCALE = 1.0f; const float AVATAR_TREE_SCALE = 1.0f;
const int MAX_VOXELS_PER_AVATAR = 2000; const int MAX_VOXELS_PER_AVATAR = 10000;
const int BONE_ELEMENTS_PER_VOXEL = BONE_ELEMENTS_PER_VERTEX * VERTICES_PER_VOXEL; const int BONE_ELEMENTS_PER_VOXEL = BONE_ELEMENTS_PER_VERTEX * VERTICES_PER_VOXEL;
AvatarVoxelSystem::AvatarVoxelSystem(Avatar* avatar) : AvatarVoxelSystem::AvatarVoxelSystem(Avatar* avatar) :

View file

@ -5,12 +5,16 @@
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved. // Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
#include <glm/gtx/quaternion.hpp> #include <glm/gtx/quaternion.hpp>
#include <QImage>
#include <AgentList.h>
#include "Application.h"
#include "Avatar.h" #include "Avatar.h"
#include "Head.h" #include "Head.h"
#include "Util.h" #include "Util.h"
#include <vector> #include "renderer/ProgramObject.h"
#include <lodepng.h>
#include <AgentList.h>
using namespace std; using namespace std;
@ -35,9 +39,8 @@ const float IRIS_RADIUS = 0.007;
const float IRIS_PROTRUSION = 0.0145f; const float IRIS_PROTRUSION = 0.0145f;
const char IRIS_TEXTURE_FILENAME[] = "resources/images/iris.png"; const char IRIS_TEXTURE_FILENAME[] = "resources/images/iris.png";
unsigned int IRIS_TEXTURE_WIDTH = 768; ProgramObject* Head::_irisProgram = 0;
unsigned int IRIS_TEXTURE_HEIGHT = 498; GLuint Head::_irisTextureID;
vector<unsigned char> irisTexture;
Head::Head(Avatar* owningAvatar) : Head::Head(Avatar* owningAvatar) :
HeadData((AvatarData*)owningAvatar), HeadData((AvatarData*)owningAvatar),
@ -56,7 +59,6 @@ Head::Head(Avatar* owningAvatar) :
_mouthPosition(0.0f, 0.0f, 0.0f), _mouthPosition(0.0f, 0.0f, 0.0f),
_scale(1.0f), _scale(1.0f),
_browAudioLift(0.0f), _browAudioLift(0.0f),
_lookingAtSomething(false),
_gravity(0.0f, -1.0f, 0.0f), _gravity(0.0f, -1.0f, 0.0f),
_lastLoudness(0.0f), _lastLoudness(0.0f),
_averageLoudness(0.0f), _averageLoudness(0.0f),
@ -66,13 +68,35 @@ Head::Head(Avatar* owningAvatar) :
_lookingInMirror(false), _lookingInMirror(false),
_renderLookatVectors(false), _renderLookatVectors(false),
_mohawkTriangleFan(NULL), _mohawkTriangleFan(NULL),
_mohawkColors(NULL) _mohawkColors(NULL),
_saccade(0.0f, 0.0f, 0.0f),
_saccadeTarget(0.0f, 0.0f, 0.0f)
{ {
if (USING_PHYSICAL_MOHAWK) { if (USING_PHYSICAL_MOHAWK) {
resetHairPhysics(); resetHairPhysics();
} }
} }
void Head::init() {
if (_irisProgram == 0) {
switchToResourcesParentIfRequired();
_irisProgram = new ProgramObject();
_irisProgram->addShaderFromSourceFile(QGLShader::Vertex, "resources/shaders/iris.vert");
_irisProgram->addShaderFromSourceFile(QGLShader::Fragment, "resources/shaders/iris.frag");
_irisProgram->link();
_irisProgram->setUniformValue("texture", 0);
QImage image = QImage(IRIS_TEXTURE_FILENAME).convertToFormat(QImage::Format_RGB888);
glGenTextures(1, &_irisTextureID);
glBindTexture(GL_TEXTURE_2D, _irisTextureID);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, image.width(), image.height(), 0, GL_RGB, GL_UNSIGNED_BYTE, image.constBits());
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glBindTexture(GL_TEXTURE_2D, 0);
}
}
void Head::reset() { void Head::reset() {
_yaw = _pitch = _roll = 0.0f; _yaw = _pitch = _roll = 0.0f;
_leanForward = _leanSideways = 0.0f; _leanForward = _leanSideways = 0.0f;
@ -101,32 +125,19 @@ void Head::resetHairPhysics() {
void Head::simulate(float deltaTime, bool isMine) { void Head::simulate(float deltaTime, bool isMine) {
const float HEAD_MOTION_DECAY = 0.00;
/* // Update eye saccades
// Decay head back to center if turned on const float AVERAGE_MICROSACCADE_INTERVAL = 0.50f;
if (isMine && _returnHeadToCenter) { const float AVERAGE_SACCADE_INTERVAL = 4.0f;
const float MICROSACCADE_MAGNITUDE = 0.002f;
const float SACCADE_MAGNITUDE = 0.04;
// Decay rotation back toward center if (randFloat() < deltaTime / AVERAGE_MICROSACCADE_INTERVAL) {
_pitch *= (1.0f - HEAD_MOTION_DECAY * _returnSpringScale * deltaTime); _saccadeTarget = MICROSACCADE_MAGNITUDE * randVector();
_yaw *= (1.0f - HEAD_MOTION_DECAY * _returnSpringScale * deltaTime); } else if (randFloat() < deltaTime / AVERAGE_SACCADE_INTERVAL) {
_roll *= (1.0f - HEAD_MOTION_DECAY * _returnSpringScale * deltaTime); _saccadeTarget = SACCADE_MAGNITUDE * randVector();
} }
_saccade += (_saccadeTarget - _saccade) * 0.50f;
// For invensense gyro, decay only slightly when near center (until we add fusion)
if (isMine) {
const float RETURN_RANGE = 15.0;
const float RETURN_STRENGTH = 0.5;
if (fabs(_pitch) < RETURN_RANGE) { _pitch *= (1.0f - RETURN_STRENGTH * deltaTime); }
if (fabs(_yaw ) < RETURN_RANGE) { _yaw *= (1.0f - RETURN_STRENGTH * deltaTime); }
if (fabs(_roll ) < RETURN_RANGE) { _roll *= (1.0f - RETURN_STRENGTH * deltaTime); }
}
*/
// decay lean
_leanForward *= (1.f - HEAD_MOTION_DECAY * 30 * deltaTime);
_leanSideways *= (1.f - HEAD_MOTION_DECAY * 30 * deltaTime);
// Update audio trailing average for rendering facial animations // Update audio trailing average for rendering facial animations
const float AUDIO_AVERAGING_SECS = 0.05; const float AUDIO_AVERAGING_SECS = 0.05;
@ -147,30 +158,13 @@ void Head::simulate(float deltaTime, bool isMine) {
_browAudioLift *= 0.7f; _browAudioLift *= 0.7f;
// based on the nature of the lookat position, determine if the eyes can look / are looking at it. // based on the nature of the lookat position, determine if the eyes can look / are looking at it.
determineIfLookingAtSomething();
if (USING_PHYSICAL_MOHAWK) { if (USING_PHYSICAL_MOHAWK) {
updateHairPhysics(deltaTime); updateHairPhysics(deltaTime);
} }
} }
void Head::determineIfLookingAtSomething() {
if ( fabs(_lookAtPosition.x + _lookAtPosition.y + _lookAtPosition.z) == 0.0 ) { // a lookatPosition of 0,0,0 signifies NOT looking
_lookingAtSomething = false;
} else {
glm::vec3 targetLookatAxis = glm::normalize(_lookAtPosition - caclulateAverageEyePosition());
float dot = glm::dot(targetLookatAxis, getFrontDirection());
if (dot < MINIMUM_EYE_ROTATION_DOT) { // too far off from center for the eyes to rotate
_lookingAtSomething = false;
} else {
_lookingAtSomething = true;
}
}
}
void Head::calculateGeometry() { void Head::calculateGeometry() {
//generate orientation directions //generate orientation directions
glm::quat orientation = getOrientation(); glm::quat orientation = getOrientation();
@ -202,7 +196,7 @@ void Head::calculateGeometry() {
} }
void Head::render(bool lookingInMirror, glm::vec3 cameraPosition, float alpha) { void Head::render(bool lookingInMirror, float alpha) {
_renderAlpha = alpha; _renderAlpha = alpha;
_lookingInMirror = lookingInMirror; _lookingInMirror = lookingInMirror;
@ -212,14 +206,14 @@ void Head::render(bool lookingInMirror, glm::vec3 cameraPosition, float alpha) {
glEnable(GL_DEPTH_TEST); glEnable(GL_DEPTH_TEST);
glEnable(GL_RESCALE_NORMAL); glEnable(GL_RESCALE_NORMAL);
renderMohawk(cameraPosition); renderMohawk();
renderHeadSphere(); renderHeadSphere();
renderEyeBalls(); renderEyeBalls();
renderEars(); renderEars();
renderMouth(); renderMouth();
renderEyeBrows(); renderEyeBrows();
if (_renderLookatVectors && _lookingAtSomething) { if (_renderLookatVectors) {
renderLookatVectors(_leftEyePosition, _rightEyePosition, _lookAtPosition); renderLookatVectors(_leftEyePosition, _rightEyePosition, _lookAtPosition);
} }
} }
@ -256,7 +250,7 @@ void Head::createMohawk() {
} }
} }
void Head::renderMohawk(glm::vec3 cameraPosition) { void Head::renderMohawk() {
if (!_mohawkTriangleFan) { if (!_mohawkTriangleFan) {
createMohawk(); createMohawk();
@ -267,7 +261,7 @@ void Head::renderMohawk(glm::vec3 cameraPosition) {
glm::vec3 baseAxis = _hairTuft[t].midPosition - _hairTuft[t].basePosition; glm::vec3 baseAxis = _hairTuft[t].midPosition - _hairTuft[t].basePosition;
glm::vec3 midAxis = _hairTuft[t].endPosition - _hairTuft[t].midPosition; glm::vec3 midAxis = _hairTuft[t].endPosition - _hairTuft[t].midPosition;
glm::vec3 viewVector = _hairTuft[t].basePosition - cameraPosition; glm::vec3 viewVector = _hairTuft[t].basePosition - Application::getInstance()->getCamera()->getPosition();
glm::vec3 basePerpendicular = glm::normalize(glm::cross(baseAxis, viewVector)); glm::vec3 basePerpendicular = glm::normalize(glm::cross(baseAxis, viewVector));
glm::vec3 midPerpendicular = glm::normalize(glm::cross(midAxis, viewVector)); glm::vec3 midPerpendicular = glm::normalize(glm::cross(midAxis, viewVector));
@ -468,22 +462,11 @@ void Head::renderEyeBrows() {
void Head::renderEyeBalls() { void Head::renderEyeBalls() {
if (::irisTexture.size() == 0) { // setup the texture to be used on each iris
switchToResourcesParentIfRequired();
unsigned error = lodepng::decode(::irisTexture, IRIS_TEXTURE_WIDTH, IRIS_TEXTURE_HEIGHT, IRIS_TEXTURE_FILENAME);
if (error != 0) {
printLog("error %u: %s\n", error, lodepng_error_text(error));
}
}
// setup the texutre to be used on each iris
GLUquadric* irisQuadric = gluNewQuadric(); GLUquadric* irisQuadric = gluNewQuadric();
gluQuadricTexture(irisQuadric, GL_TRUE); gluQuadricTexture(irisQuadric, GL_TRUE);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
gluQuadricOrientation(irisQuadric, GLU_OUTSIDE); gluQuadricOrientation(irisQuadric, GLU_OUTSIDE);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, IRIS_TEXTURE_WIDTH, IRIS_TEXTURE_HEIGHT,
0, GL_RGBA, GL_UNSIGNED_BYTE, &::irisTexture[0]);
// render white ball of left eyeball // render white ball of left eyeball
glPushMatrix(); glPushMatrix();
@ -492,45 +475,6 @@ void Head::renderEyeBalls() {
gluSphere(irisQuadric, EYEBALL_RADIUS, 30, 30); gluSphere(irisQuadric, EYEBALL_RADIUS, 30, 30);
glPopMatrix(); glPopMatrix();
glm::vec3 front = getFrontDirection();
// render left iris
glPushMatrix(); {
glTranslatef(_leftEyePosition.x, _leftEyePosition.y, _leftEyePosition.z); //translate to eyeball position
glPushMatrix();
if (_lookingAtSomething) {
//rotate the eyeball to aim towards the lookat position
glm::vec3 targetLookatAxis = glm::normalize(_lookAtPosition - _leftEyePosition); // the lookat direction
glm::vec3 rotationAxis = glm::cross(targetLookatAxis, IDENTITY_UP);
float angle = 180.0f - angleBetween(targetLookatAxis, IDENTITY_UP);
glRotatef(angle, rotationAxis.x, rotationAxis.y, rotationAxis.z);
glRotatef(180.0, 0.0f, 1.0f, 0.0f); //adjust roll to correct after previous rotations
} else {
//rotate the eyeball to aim straight ahead
glm::vec3 rotationAxisToHeadFront = glm::cross(front, IDENTITY_UP);
float angleToHeadFront = 180.0f - angleBetween(front, IDENTITY_UP);
glRotatef(angleToHeadFront, rotationAxisToHeadFront.x, rotationAxisToHeadFront.y, rotationAxisToHeadFront.z);
//set the amount of roll (for correction after previous rotations)
float rollRotation = angleBetween(front, IDENTITY_FRONT);
float dot = glm::dot(front, -IDENTITY_RIGHT);
if ( dot < 0.0f ) { rollRotation = -rollRotation; }
glRotatef(rollRotation, 0.0f, 1.0f, 0.0f); //roll the iris or correct roll about the lookat vector
}
glTranslatef( 0.0f, -IRIS_PROTRUSION, 0.0f);//push the iris out a bit (otherwise - inside of eyeball!)
glScalef( 1.0f, 0.5f, 1.0f); // flatten the iris
glEnable(GL_TEXTURE_2D);
gluSphere(irisQuadric, IRIS_RADIUS, 15, 15);
glDisable(GL_TEXTURE_2D);
glPopMatrix();
}
glPopMatrix();
//render white ball of right eyeball //render white ball of right eyeball
glPushMatrix(); glPushMatrix();
glColor3fv(EYEBALL_COLOR); glColor3fv(EYEBALL_COLOR);
@ -538,43 +482,50 @@ void Head::renderEyeBalls() {
gluSphere(irisQuadric, EYEBALL_RADIUS, 30, 30); gluSphere(irisQuadric, EYEBALL_RADIUS, 30, 30);
glPopMatrix(); glPopMatrix();
_irisProgram->bind();
glBindTexture(GL_TEXTURE_2D, _irisTextureID);
glEnable(GL_TEXTURE_2D);
glm::vec3 front = getFrontDirection();
// render left iris
glPushMatrix(); {
glTranslatef(_leftEyePosition.x, _leftEyePosition.y, _leftEyePosition.z); //translate to eyeball position
glPushMatrix();
//rotate the eyeball to aim towards the lookat position
glm::vec3 targetLookatAxis = glm::normalize(_lookAtPosition + _saccade - _leftEyePosition);
glm::vec3 rotationAxis = glm::cross(targetLookatAxis, IDENTITY_UP);
float angle = 180.0f - angleBetween(targetLookatAxis, IDENTITY_UP);
glRotatef(angle, rotationAxis.x, rotationAxis.y, rotationAxis.z);
glRotatef(180.0, 0.0f, 1.0f, 0.0f); //adjust roll to correct after previous rotations
glTranslatef( 0.0f, -IRIS_PROTRUSION, 0.0f);
glScalef( 1.0f, 0.5f, 1.0f); // flatten the iris
gluSphere(irisQuadric, IRIS_RADIUS, 15, 15);
glPopMatrix();
}
glPopMatrix();
// render right iris // render right iris
glPushMatrix(); { glPushMatrix(); {
glTranslatef(_rightEyePosition.x, _rightEyePosition.y, _rightEyePosition.z); //translate to eyeball position glTranslatef(_rightEyePosition.x, _rightEyePosition.y, _rightEyePosition.z); //translate to eyeball position
glPushMatrix(); glPushMatrix();
if (_lookingAtSomething) {
//rotate the eyeball to aim towards the lookat position
glm::vec3 targetLookatAxis = glm::normalize(_lookAtPosition - _rightEyePosition);
glm::vec3 rotationAxis = glm::cross(targetLookatAxis, IDENTITY_UP);
float angle = 180.0f - angleBetween(targetLookatAxis, IDENTITY_UP);
glRotatef(angle, rotationAxis.x, rotationAxis.y, rotationAxis.z);
glRotatef(180.0f, 0.0f, 1.0f, 0.0f); //adjust roll to correct after previous rotations
} else { //rotate the eyeball to aim towards the lookat position
glm::vec3 targetLookatAxis = glm::normalize(_lookAtPosition + _saccade - _rightEyePosition);
//rotate the eyeball to aim straight ahead glm::vec3 rotationAxis = glm::cross(targetLookatAxis, IDENTITY_UP);
glm::vec3 rotationAxisToHeadFront = glm::cross(front, IDENTITY_UP); float angle = 180.0f - angleBetween(targetLookatAxis, IDENTITY_UP);
float angleToHeadFront = 180.0f - angleBetween(front, IDENTITY_UP); glRotatef(angle, rotationAxis.x, rotationAxis.y, rotationAxis.z);
glRotatef(angleToHeadFront, rotationAxisToHeadFront.x, rotationAxisToHeadFront.y, rotationAxisToHeadFront.z); glRotatef(180.0f, 0.0f, 1.0f, 0.0f); //adjust roll to correct after previous rotations
glTranslatef( 0.0f, -IRIS_PROTRUSION, 0.0f);
//set the amount of roll (for correction after previous rotations) glScalef( 1.0f, 0.5f, 1.0f); // flatten the iris
float rollRotation = angleBetween(front, IDENTITY_FRONT);
float dot = glm::dot(front, -IDENTITY_RIGHT);
if ( dot < 0.0f ) { rollRotation = -rollRotation; }
glRotatef(rollRotation, 0.0f, 1.0f, 0.0f); //roll the iris or correct roll about the lookat vector
}
glTranslatef( 0.0f, -IRIS_PROTRUSION, 0.0f);//push the iris out a bit (otherwise - inside of eyeball!)
glScalef( 1.0f, 0.5f, 1.0f); // flatten the iris
glEnable(GL_TEXTURE_2D);
gluSphere(irisQuadric, IRIS_RADIUS, 15, 15); gluSphere(irisQuadric, IRIS_RADIUS, 15, 15);
glDisable(GL_TEXTURE_2D);
glPopMatrix(); glPopMatrix();
} }
_irisProgram->release();
glBindTexture(GL_TEXTURE_2D, 0);
glDisable(GL_TEXTURE_2D);
// delete the iris quadric now that we're done with it // delete the iris quadric now that we're done with it
gluDeleteQuadric(irisQuadric); gluDeleteQuadric(irisQuadric);
glPopMatrix(); glPopMatrix();

View file

@ -26,15 +26,17 @@ enum eyeContactTargets
const int NUM_HAIR_TUFTS = 4; const int NUM_HAIR_TUFTS = 4;
class Avatar; class Avatar;
class ProgramObject;
class Head : public HeadData { class Head : public HeadData {
public: public:
Head(Avatar* owningAvatar); Head(Avatar* owningAvatar);
void init();
void reset(); void reset();
void simulate(float deltaTime, bool isMine); void simulate(float deltaTime, bool isMine);
void render(bool lookingInMirror, glm::vec3 cameraPosition, float alpha); void render(bool lookingInMirror, float alpha);
void renderMohawk(glm::vec3 cameraPosition); void renderMohawk();
void setScale (float scale ) { _scale = scale; } void setScale (float scale ) { _scale = scale; }
void setPosition (glm::vec3 position ) { _position = position; } void setPosition (glm::vec3 position ) { _position = position; }
@ -55,7 +57,7 @@ public:
const bool getReturnToCenter() const { return _returnHeadToCenter; } // Do you want head to try to return to center (depends on interface detected) const bool getReturnToCenter() const { return _returnHeadToCenter; } // Do you want head to try to return to center (depends on interface detected)
float getAverageLoudness() {return _averageLoudness;}; float getAverageLoudness() {return _averageLoudness;};
glm::vec3 caclulateAverageEyePosition() { return _leftEyePosition + (_rightEyePosition - _leftEyePosition ) * ONE_HALF; } glm::vec3 calculateAverageEyePosition() { return _leftEyePosition + (_rightEyePosition - _leftEyePosition ) * ONE_HALF; }
float yawRate; float yawRate;
float noise; float noise;
@ -91,7 +93,6 @@ private:
glm::vec3 _mouthPosition; glm::vec3 _mouthPosition;
float _scale; float _scale;
float _browAudioLift; float _browAudioLift;
bool _lookingAtSomething;
glm::vec3 _gravity; glm::vec3 _gravity;
float _lastLoudness; float _lastLoudness;
float _averageLoudness; float _averageLoudness;
@ -103,6 +104,11 @@ private:
HairTuft _hairTuft[NUM_HAIR_TUFTS]; HairTuft _hairTuft[NUM_HAIR_TUFTS];
glm::vec3* _mohawkTriangleFan; glm::vec3* _mohawkTriangleFan;
glm::vec3* _mohawkColors; glm::vec3* _mohawkColors;
glm::vec3 _saccade;
glm::vec3 _saccadeTarget;
static ProgramObject* _irisProgram;
static GLuint _irisTextureID;
// private methods // private methods
void createMohawk(); void createMohawk();
@ -113,7 +119,6 @@ private:
void renderMouth(); void renderMouth();
void renderLookatVectors(glm::vec3 leftEyePosition, glm::vec3 rightEyePosition, glm::vec3 lookatPosition); void renderLookatVectors(glm::vec3 leftEyePosition, glm::vec3 rightEyePosition, glm::vec3 lookatPosition);
void calculateGeometry(); void calculateGeometry();
void determineIfLookingAtSomething();
void resetHairPhysics(); void resetHairPhysics();
void updateHairPhysics(float deltaTime); void updateHairPhysics(float deltaTime);
}; };

View file

@ -6,6 +6,7 @@
// //
#include "SerialInterface.h" #include "SerialInterface.h"
#include "SharedUtil.h"
#include "Util.h" #include "Util.h"
#include <glm/gtx/vector_angle.hpp> #include <glm/gtx/vector_angle.hpp>
#include <math.h> #include <math.h>
@ -155,11 +156,11 @@ void SerialInterface::renderLevels(int width, int height) {
// Acceleration rates // Acceleration rates
glColor4f(1, 1, 1, 1); glColor4f(1, 1, 1, 1);
glVertex2f(LEVEL_CORNER_X + LEVEL_CENTER, LEVEL_CORNER_Y + 42); glVertex2f(LEVEL_CORNER_X + LEVEL_CENTER, LEVEL_CORNER_Y + 42);
glVertex2f(LEVEL_CORNER_X + LEVEL_CENTER + (int)((_lastAcceleration.x - _gravity.x) *ACCEL_VIEW_SCALING), LEVEL_CORNER_Y + 42); glVertex2f(LEVEL_CORNER_X + LEVEL_CENTER + (int)(_estimatedAcceleration.x * ACCEL_VIEW_SCALING), LEVEL_CORNER_Y + 42);
glVertex2f(LEVEL_CORNER_X + LEVEL_CENTER, LEVEL_CORNER_Y + 57); glVertex2f(LEVEL_CORNER_X + LEVEL_CENTER, LEVEL_CORNER_Y + 57);
glVertex2f(LEVEL_CORNER_X + LEVEL_CENTER + (int)((_lastAcceleration.y - _gravity.y) *ACCEL_VIEW_SCALING), LEVEL_CORNER_Y + 57); glVertex2f(LEVEL_CORNER_X + LEVEL_CENTER + (int)(_estimatedAcceleration.y * ACCEL_VIEW_SCALING), LEVEL_CORNER_Y + 57);
glVertex2f(LEVEL_CORNER_X + LEVEL_CENTER, LEVEL_CORNER_Y + 72); glVertex2f(LEVEL_CORNER_X + LEVEL_CENTER, LEVEL_CORNER_Y + 72);
glVertex2f(LEVEL_CORNER_X + LEVEL_CENTER + (int)((_lastAcceleration.z - _gravity.z) * ACCEL_VIEW_SCALING), LEVEL_CORNER_Y + 72); glVertex2f(LEVEL_CORNER_X + LEVEL_CENTER + (int)(_estimatedAcceleration.z * ACCEL_VIEW_SCALING), LEVEL_CORNER_Y + 72);
// Estimated Position // Estimated Position
glColor4f(0, 1, 1, 1); glColor4f(0, 1, 1, 1);
@ -217,6 +218,7 @@ void SerialInterface::readData(float deltaTime) {
_lastAcceleration = glm::vec3(-accelXRate, -accelYRate, -accelZRate) * LSB_TO_METERS_PER_SECOND2; _lastAcceleration = glm::vec3(-accelXRate, -accelYRate, -accelZRate) * LSB_TO_METERS_PER_SECOND2;
int rollRate, yawRate, pitchRate; int rollRate, yawRate, pitchRate;
convertHexToInt(sensorBuffer + 22, rollRate); convertHexToInt(sensorBuffer + 22, rollRate);
@ -225,35 +227,87 @@ void SerialInterface::readData(float deltaTime) {
// Convert the integer rates to floats // Convert the integer rates to floats
const float LSB_TO_DEGREES_PER_SECOND = 1.f / 16.4f; // From MPU-9150 register map, 2000 deg/sec. const float LSB_TO_DEGREES_PER_SECOND = 1.f / 16.4f; // From MPU-9150 register map, 2000 deg/sec.
_lastRotationRates[0] = ((float) -pitchRate) * LSB_TO_DEGREES_PER_SECOND; glm::vec3 rotationRates;
_lastRotationRates[1] = ((float) -yawRate) * LSB_TO_DEGREES_PER_SECOND; rotationRates[0] = ((float) -pitchRate) * LSB_TO_DEGREES_PER_SECOND;
_lastRotationRates[2] = ((float) -rollRate) * LSB_TO_DEGREES_PER_SECOND; rotationRates[1] = ((float) -yawRate) * LSB_TO_DEGREES_PER_SECOND;
rotationRates[2] = ((float) -rollRate) * LSB_TO_DEGREES_PER_SECOND;
// update and subtract the long term average
_averageRotationRates = (1.f - 1.f/(float)LONG_TERM_RATE_SAMPLES) * _averageRotationRates +
1.f/(float)LONG_TERM_RATE_SAMPLES * rotationRates;
rotationRates -= _averageRotationRates;
// compute the angular acceleration
glm::vec3 angularAcceleration = (deltaTime < EPSILON) ? glm::vec3() : (rotationRates - _lastRotationRates) / deltaTime;
_lastRotationRates = rotationRates;
// Update raw rotation estimates // Update raw rotation estimates
glm::quat estimatedRotation = glm::quat(glm::radians(_estimatedRotation)) * glm::quat estimatedRotation = glm::quat(glm::radians(_estimatedRotation)) *
glm::quat(glm::radians(deltaTime * (_lastRotationRates - _averageRotationRates))); glm::quat(glm::radians(deltaTime * _lastRotationRates));
// Update acceleration estimate: first, subtract gravity as rotated into current frame
_estimatedAcceleration = (totalSamples < GRAVITY_SAMPLES) ? glm::vec3() :
_lastAcceleration - glm::inverse(estimatedRotation) * _gravity;
// update and subtract the long term average
_averageAcceleration = (1.f - 1.f/(float)LONG_TERM_RATE_SAMPLES) * _averageAcceleration +
1.f/(float)LONG_TERM_RATE_SAMPLES * _estimatedAcceleration;
_estimatedAcceleration -= _averageAcceleration;
// Consider updating our angular velocity/acceleration to linear acceleration mapping
if (glm::length(_estimatedAcceleration) > EPSILON &&
(glm::length(_lastRotationRates) > EPSILON || glm::length(angularAcceleration) > EPSILON)) {
// compute predicted linear acceleration, find error between actual and predicted
glm::vec3 predictedAcceleration = _angularVelocityToLinearAccel * _lastRotationRates +
_angularAccelToLinearAccel * angularAcceleration;
glm::vec3 error = _estimatedAcceleration - predictedAcceleration;
// the "error" is actually what we want: the linear acceleration minus rotational influences
_estimatedAcceleration = error;
// adjust according to error in each dimension, in proportion to input magnitudes
for (int i = 0; i < 3; i++) {
if (fabsf(error[i]) < EPSILON) {
continue;
}
const float LEARNING_RATE = 0.001f;
float rateSum = fabsf(_lastRotationRates.x) + fabsf(_lastRotationRates.y) + fabsf(_lastRotationRates.z);
if (rateSum > EPSILON) {
for (int j = 0; j < 3; j++) {
float proportion = LEARNING_RATE * fabsf(_lastRotationRates[j]) / rateSum;
if (proportion > EPSILON) {
_angularVelocityToLinearAccel[j][i] += error[i] * proportion / _lastRotationRates[j];
}
}
}
float accelSum = fabsf(angularAcceleration.x) + fabsf(angularAcceleration.y) + fabsf(angularAcceleration.z);
if (accelSum > EPSILON) {
for (int j = 0; j < 3; j++) {
float proportion = LEARNING_RATE * fabsf(angularAcceleration[j]) / accelSum;
if (proportion > EPSILON) {
_angularAccelToLinearAccel[j][i] += error[i] * proportion / angularAcceleration[j];
}
}
}
}
}
// rotate estimated acceleration into global rotation frame
_estimatedAcceleration = estimatedRotation * _estimatedAcceleration;
// Update estimated position and velocity // Update estimated position and velocity
float const DECAY_VELOCITY = 0.95f; float const DECAY_VELOCITY = 0.975f;
float const DECAY_POSITION = 0.95f; float const DECAY_POSITION = 0.975f;
_estimatedVelocity += deltaTime * (_lastAcceleration - _averageAcceleration); _estimatedVelocity += deltaTime * _estimatedAcceleration;
_estimatedPosition += deltaTime * _estimatedVelocity; _estimatedPosition += deltaTime * _estimatedVelocity;
_estimatedVelocity *= DECAY_VELOCITY; _estimatedVelocity *= DECAY_VELOCITY;
_estimatedPosition *= DECAY_POSITION; _estimatedPosition *= DECAY_POSITION;
// Accumulate a set of initial baseline readings for setting gravity // Accumulate a set of initial baseline readings for setting gravity
if (totalSamples == 0) { if (totalSamples == 0) {
_averageRotationRates = _lastRotationRates;
_averageAcceleration = _lastAcceleration;
_gravity = _lastAcceleration; _gravity = _lastAcceleration;
} }
else { else {
// Cumulate long term average to (hopefully) take DC bias out of rotation rates
_averageRotationRates = (1.f - 1.f / (float)LONG_TERM_RATE_SAMPLES) * _averageRotationRates
+ 1.f / (float)LONG_TERM_RATE_SAMPLES * _lastRotationRates;
_averageAcceleration = (1.f - 1.f / (float)LONG_TERM_RATE_SAMPLES) * _averageAcceleration
+ 1.f / (float)LONG_TERM_RATE_SAMPLES * _lastAcceleration;
if (totalSamples < GRAVITY_SAMPLES) { if (totalSamples < GRAVITY_SAMPLES) {
_gravity = (1.f - 1.f/(float)GRAVITY_SAMPLES) * _gravity + _gravity = (1.f - 1.f/(float)GRAVITY_SAMPLES) * _gravity +
1.f/(float)GRAVITY_SAMPLES * _lastAcceleration; 1.f/(float)GRAVITY_SAMPLES * _lastAcceleration;
@ -299,6 +353,7 @@ void SerialInterface::resetAverages() {
_estimatedRotation = glm::vec3(0, 0, 0); _estimatedRotation = glm::vec3(0, 0, 0);
_estimatedPosition = glm::vec3(0, 0, 0); _estimatedPosition = glm::vec3(0, 0, 0);
_estimatedVelocity = glm::vec3(0, 0, 0); _estimatedVelocity = glm::vec3(0, 0, 0);
_estimatedAcceleration = glm::vec3(0, 0, 0);
} }
void SerialInterface::resetSerial() { void SerialInterface::resetSerial() {

View file

@ -32,18 +32,27 @@ public:
_estimatedPosition(0, 0, 0), _estimatedPosition(0, 0, 0),
_estimatedVelocity(0, 0, 0), _estimatedVelocity(0, 0, 0),
_lastAcceleration(0, 0, 0), _lastAcceleration(0, 0, 0),
_lastRotationRates(0, 0, 0) _lastRotationRates(0, 0, 0),
_angularVelocityToLinearAccel( // experimentally derived initial values
0.003f, -0.001f, -0.006f,
-0.005f, -0.001f, -0.006f,
0.010f, 0.004f, 0.007f),
_angularAccelToLinearAccel( // experimentally derived initial values
0.0f, 0.0f, 0.002f,
0.0f, 0.0f, 0.001f,
-0.002f, -0.002f, 0.0f)
{} {}
void pair(); void pair();
void readData(float deltaTime); void readData(float deltaTime);
const float getLastPitchRate() const { return _lastRotationRates[0] - _averageRotationRates[0]; } const float getLastPitchRate() const { return _lastRotationRates[0]; }
const float getLastYawRate() const { return _lastRotationRates[1] - _averageRotationRates[1]; } const float getLastYawRate() const { return _lastRotationRates[1]; }
const float getLastRollRate() const { return _lastRotationRates[2] - _averageRotationRates[2]; } const float getLastRollRate() const { return _lastRotationRates[2]; }
const glm::vec3& getLastRotationRates() const { return _lastRotationRates; }; const glm::vec3& getLastRotationRates() const { return _lastRotationRates; };
const glm::vec3& getEstimatedRotation() const { return _estimatedRotation; }; const glm::vec3& getEstimatedRotation() const { return _estimatedRotation; };
const glm::vec3& getEstimatedPosition() const { return _estimatedPosition; }; const glm::vec3& getEstimatedPosition() const { return _estimatedPosition; };
const glm::vec3& getEstimatedVelocity() const { return _estimatedVelocity; }; const glm::vec3& getEstimatedVelocity() const { return _estimatedVelocity; };
const glm::vec3& getEstimatedAcceleration() const { return _estimatedAcceleration; };
const glm::vec3& getLastAcceleration() const { return _lastAcceleration; }; const glm::vec3& getLastAcceleration() const { return _lastAcceleration; };
const glm::vec3& getGravity() const { return _gravity; }; const glm::vec3& getGravity() const { return _gravity; };
@ -64,8 +73,12 @@ private:
glm::vec3 _estimatedRotation; glm::vec3 _estimatedRotation;
glm::vec3 _estimatedPosition; glm::vec3 _estimatedPosition;
glm::vec3 _estimatedVelocity; glm::vec3 _estimatedVelocity;
glm::vec3 _estimatedAcceleration;
glm::vec3 _lastAcceleration; glm::vec3 _lastAcceleration;
glm::vec3 _lastRotationRates; glm::vec3 _lastRotationRates;
glm::mat3 _angularVelocityToLinearAccel;
glm::mat3 _angularAccelToLinearAccel;
}; };
#endif #endif

View file

@ -132,15 +132,15 @@ void Skeleton::update(float deltaTime, const glm::quat& orientation, glm::vec3 p
for (int b = 0; b < NUM_AVATAR_JOINTS; b++) { for (int b = 0; b < NUM_AVATAR_JOINTS; b++) {
if (joint[b].parent == AVATAR_JOINT_NULL) { if (joint[b].parent == AVATAR_JOINT_NULL) {
joint[b].rotation = orientation; joint[b].absoluteRotation = orientation * joint[b].rotation;
joint[b].position = position; joint[b].position = position;
} }
else { else {
joint[b].rotation = joint[ joint[b].parent ].rotation; joint[b].absoluteRotation = joint[ joint[b].parent ].absoluteRotation * joint[b].rotation;
joint[b].position = joint[ joint[b].parent ].position; joint[b].position = joint[ joint[b].parent ].position;
} }
glm::vec3 rotatedJointVector = joint[b].rotation * joint[b].defaultPosePosition; glm::vec3 rotatedJointVector = joint[b].absoluteRotation * joint[b].defaultPosePosition;
joint[b].position += rotatedJointVector; joint[b].position += rotatedJointVector;
} }
} }
@ -174,6 +174,13 @@ float Skeleton::getPelvisFloatingHeight() {
FLOATING_HEIGHT; FLOATING_HEIGHT;
} }
float Skeleton::getPelvisToHeadLength() {
return
joint[ AVATAR_JOINT_TORSO ].length +
joint[ AVATAR_JOINT_CHEST ].length +
joint[ AVATAR_JOINT_NECK_BASE ].length +
joint[ AVATAR_JOINT_HEAD_BASE ].length;
}

View file

@ -56,6 +56,7 @@ public:
float getHeight(); float getHeight();
float getPelvisStandingHeight(); float getPelvisStandingHeight();
float getPelvisFloatingHeight(); float getPelvisFloatingHeight();
float getPelvisToHeadLength();
//glm::vec3 getJointVectorFromParent(AvatarJointID jointID) {return joint[jointID].position - joint[joint[jointID].parent].position; } //glm::vec3 getJointVectorFromParent(AvatarJointID jointID) {return joint[jointID].position - joint[joint[jointID].parent].position; }
struct AvatarJoint struct AvatarJoint
@ -68,6 +69,7 @@ public:
glm::quat absoluteBindPoseRotation; // the absolute rotation when the avatar is in the "T-pose" glm::quat absoluteBindPoseRotation; // the absolute rotation when the avatar is in the "T-pose"
float bindRadius; // the radius of the bone capsule that envelops the vertices to bind float bindRadius; // the radius of the bone capsule that envelops the vertices to bind
glm::quat rotation; // the parent-relative rotation (orientation) of the joint as a quaternion glm::quat rotation; // the parent-relative rotation (orientation) of the joint as a quaternion
glm::quat absoluteRotation; // the absolute rotation of the joint as a quaternion
float length; // the length of vector connecting the joint and its parent float length; // the length of vector connecting the joint and its parent
}; };

View file

@ -70,9 +70,7 @@ void Transmitter::processIncomingData(unsigned char* packetData, int numBytes) {
// Update estimated absolute position from rotation rates // Update estimated absolute position from rotation rates
_estimatedRotation += _lastRotationRate * DELTA_TIME; _estimatedRotation += _lastRotationRate * DELTA_TIME;
printf("The accel %f, %f, %f\n", _lastAcceleration.x, _lastAcceleration.y, _lastAcceleration.z);
// Sensor Fusion! Slowly adjust estimated rotation to be relative to gravity (average acceleration) // Sensor Fusion! Slowly adjust estimated rotation to be relative to gravity (average acceleration)
const float GRAVITY_FOLLOW_RATE = 1.f; const float GRAVITY_FOLLOW_RATE = 1.f;
float rollAngle = angleBetween(glm::vec3(_lastAcceleration.x, _lastAcceleration.y, 0.f), glm::vec3(0,-1,0)) * float rollAngle = angleBetween(glm::vec3(_lastAcceleration.x, _lastAcceleration.y, 0.f), glm::vec3(0,-1,0)) *

View file

@ -267,6 +267,11 @@ double diffclock(timeval *clock1,timeval *clock2)
return diffms; return diffms;
} }
// Return a random vector of average length 1
const glm::vec3 randVector() {
return glm::vec3(randFloat() - 0.5f, randFloat() - 0.5f, randFloat() - 0.5f) * 2.f;
}
static TextRenderer* textRenderer(int mono) { static TextRenderer* textRenderer(int mono) {
static TextRenderer* monoRenderer = new TextRenderer(MONO_FONT_FAMILY); static TextRenderer* monoRenderer = new TextRenderer(MONO_FONT_FAMILY);
static TextRenderer* proportionalRenderer = new TextRenderer(SANS_FONT_FAMILY, -1, -1, false, TextRenderer::SHADOW_EFFECT); static TextRenderer* proportionalRenderer = new TextRenderer(SANS_FONT_FAMILY, -1, -1, false, TextRenderer::SHADOW_EFFECT);
@ -498,9 +503,9 @@ void runTimingTests() {
} }
float loadSetting(QSettings* settings, const char* name, float defaultValue) { float loadSetting(QSettings* settings, const char* name, float defaultValue) {
float value = settings->value(name, 0.0f).toFloat(); float value = settings->value(name, defaultValue).toFloat();
if (isnan(value)) { if (isnan(value)) {
value = defaultValue; value = defaultValue;
} }
return value; return value;
} }

View file

@ -32,6 +32,8 @@ float azimuth_to(glm::vec3 head_pos, glm::vec3 source_pos);
float angle_to(glm::vec3 head_pos, glm::vec3 source_pos, float render_yaw, float head_yaw); float angle_to(glm::vec3 head_pos, glm::vec3 source_pos, float render_yaw, float head_yaw);
float randFloat(); float randFloat();
const glm::vec3 randVector();
void render_world_box(); void render_world_box();
int widthText(float scale, int mono, char const* string); int widthText(float scale, int mono, char const* string);
float widthChar(float scale, int mono, char ch); float widthChar(float scale, int mono, char ch);

View file

@ -165,15 +165,15 @@ int VoxelSystem::parseData(unsigned char* sourceBuffer, int numBytes) {
void VoxelSystem::setupNewVoxelsForDrawing() { void VoxelSystem::setupNewVoxelsForDrawing() {
PerformanceWarning warn(_renderWarningsOn, "setupNewVoxelsForDrawing()"); // would like to include _voxelsInArrays, _voxelsUpdated PerformanceWarning warn(_renderWarningsOn, "setupNewVoxelsForDrawing()"); // would like to include _voxelsInArrays, _voxelsUpdated
double start = usecTimestampNow(); long long start = usecTimestampNow();
double sinceLastTime = (start - _setupNewVoxelsForDrawingLastFinished) / 1000.0; long long sinceLastTime = (start - _setupNewVoxelsForDrawingLastFinished) / 1000;
bool iAmDebugging = false; // if you're debugging set this to true, so you won't get skipped for slow debugging bool iAmDebugging = false; // if you're debugging set this to true, so you won't get skipped for slow debugging
if (!iAmDebugging && sinceLastTime <= std::max(_setupNewVoxelsForDrawingLastElapsed, SIXTY_FPS_IN_MILLISECONDS)) { if (!iAmDebugging && sinceLastTime <= std::max(_setupNewVoxelsForDrawingLastElapsed, SIXTY_FPS_IN_MILLISECONDS)) {
return; // bail early, it hasn't been long enough since the last time we ran return; // bail early, it hasn't been long enough since the last time we ran
} }
double sinceLastViewCulling = (start - _lastViewCulling) / 1000.0; long long sinceLastViewCulling = (start - _lastViewCulling) / 1000;
// If the view frustum is no longer changing, but has changed, since last time, then remove nodes that are out of view // If the view frustum is no longer changing, but has changed, since last time, then remove nodes that are out of view
if ((sinceLastViewCulling >= std::max(_lastViewCullingElapsed, VIEW_CULLING_RATE_IN_MILLISECONDS)) if ((sinceLastViewCulling >= std::max(_lastViewCullingElapsed, VIEW_CULLING_RATE_IN_MILLISECONDS))
&& !isViewChanging() && hasViewChanged()) { && !isViewChanging() && hasViewChanged()) {
@ -189,8 +189,8 @@ void VoxelSystem::setupNewVoxelsForDrawing() {
// VBO reubuilding. Possibly we should do this only if our actual VBO usage crosses some lower boundary. // VBO reubuilding. Possibly we should do this only if our actual VBO usage crosses some lower boundary.
cleanupRemovedVoxels(); cleanupRemovedVoxels();
double endViewCulling = usecTimestampNow(); long long endViewCulling = usecTimestampNow();
_lastViewCullingElapsed = (endViewCulling - start) / 1000.0; _lastViewCullingElapsed = (endViewCulling - start) / 1000;
} }
bool didWriteFullVBO = _writeRenderFullVBO; bool didWriteFullVBO = _writeRenderFullVBO;
@ -226,8 +226,8 @@ void VoxelSystem::setupNewVoxelsForDrawing() {
pthread_mutex_unlock(&_bufferWriteLock); pthread_mutex_unlock(&_bufferWriteLock);
double end = usecTimestampNow(); long long end = usecTimestampNow();
double elapsedmsec = (end - start) / 1000.0; long long elapsedmsec = (end - start) / 1000;
_setupNewVoxelsForDrawingLastFinished = end; _setupNewVoxelsForDrawingLastFinished = end;
_setupNewVoxelsForDrawingLastElapsed = elapsedmsec; _setupNewVoxelsForDrawingLastElapsed = elapsedmsec;
} }

View file

@ -115,7 +115,7 @@ void AudioInjector::injectAudio(UDPSocket* injectorSocket, sockaddr* destination
injectorSocket->send(destinationSocket, dataPacket, sizeof(dataPacket)); injectorSocket->send(destinationSocket, dataPacket, sizeof(dataPacket));
double usecToSleep = usecTimestamp(&startTime) + (++nextFrame * INJECT_INTERVAL_USECS) - usecTimestampNow(); long long usecToSleep = usecTimestamp(&startTime) + (++nextFrame * INJECT_INTERVAL_USECS) - usecTimestampNow();
if (usecToSleep > 0) { if (usecToSleep > 0) {
usleep(usecToSleep); usleep(usecToSleep);
} }

View file

@ -6,6 +6,8 @@
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved. // Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
// //
#include "stdio.h"
#include <pthread.h> #include <pthread.h>
#include "Agent.h" #include "Agent.h"
#include "AgentTypes.h" #include "AgentTypes.h"
@ -62,7 +64,7 @@ Agent::~Agent() {
// Names of Agent Types // Names of Agent Types
const char* AGENT_TYPE_NAME_DOMAIN = "Domain"; const char* AGENT_TYPE_NAME_DOMAIN = "Domain";
const char* AGENT_TYPE_NAME_VOXEL = "Voxel Server"; const char* AGENT_TYPE_NAME_VOXEL_SERVER = "Voxel Server";
const char* AGENT_TYPE_NAME_INTERFACE = "Client Interface"; const char* AGENT_TYPE_NAME_INTERFACE = "Client Interface";
const char* AGENT_TYPE_NAME_AUDIO_MIXER = "Audio Mixer"; const char* AGENT_TYPE_NAME_AUDIO_MIXER = "Audio Mixer";
const char* AGENT_TYPE_NAME_AVATAR_MIXER = "Avatar Mixer"; const char* AGENT_TYPE_NAME_AVATAR_MIXER = "Avatar Mixer";
@ -74,8 +76,8 @@ const char* Agent::getTypeName() const {
switch (this->_type) { switch (this->_type) {
case AGENT_TYPE_DOMAIN: case AGENT_TYPE_DOMAIN:
return AGENT_TYPE_NAME_DOMAIN; return AGENT_TYPE_NAME_DOMAIN;
case AGENT_TYPE_VOXEL: case AGENT_TYPE_VOXEL_SERVER:
return AGENT_TYPE_NAME_VOXEL; return AGENT_TYPE_NAME_VOXEL_SERVER;
case AGENT_TYPE_AVATAR: case AGENT_TYPE_AVATAR:
return AGENT_TYPE_NAME_INTERFACE; return AGENT_TYPE_NAME_INTERFACE;
case AGENT_TYPE_AUDIO_MIXER: case AGENT_TYPE_AUDIO_MIXER:

View file

@ -37,11 +37,11 @@ public:
uint16_t getAgentID() const { return _agentID; } uint16_t getAgentID() const { return _agentID; }
void setAgentID(uint16_t agentID) { _agentID = agentID;} void setAgentID(uint16_t agentID) { _agentID = agentID;}
double getWakeMicrostamp() const { return _wakeMicrostamp; } long long getWakeMicrostamp() const { return _wakeMicrostamp; }
void setWakeMicrostamp(double wakeMicrostamp) { _wakeMicrostamp = wakeMicrostamp; } void setWakeMicrostamp(long long wakeMicrostamp) { _wakeMicrostamp = wakeMicrostamp; }
double getLastHeardMicrostamp() const { return _lastHeardMicrostamp; } long long getLastHeardMicrostamp() const { return _lastHeardMicrostamp; }
void setLastHeardMicrostamp(double lastHeardMicrostamp) { _lastHeardMicrostamp = lastHeardMicrostamp; } void setLastHeardMicrostamp(long long lastHeardMicrostamp) { _lastHeardMicrostamp = lastHeardMicrostamp; }
sockaddr* getPublicSocket() const { return _publicSocket; } sockaddr* getPublicSocket() const { return _publicSocket; }
void setPublicSocket(sockaddr* publicSocket) { _publicSocket = publicSocket; } void setPublicSocket(sockaddr* publicSocket) { _publicSocket = publicSocket; }
@ -71,8 +71,8 @@ private:
char _type; char _type;
uint16_t _agentID; uint16_t _agentID;
double _wakeMicrostamp; long long _wakeMicrostamp;
double _lastHeardMicrostamp; long long _lastHeardMicrostamp;
sockaddr* _publicSocket; sockaddr* _publicSocket;
sockaddr* _localSocket; sockaddr* _localSocket;
sockaddr* _activeSocket; sockaddr* _activeSocket;

View file

@ -26,7 +26,7 @@
const char SOLO_AGENT_TYPES[3] = { const char SOLO_AGENT_TYPES[3] = {
AGENT_TYPE_AVATAR_MIXER, AGENT_TYPE_AVATAR_MIXER,
AGENT_TYPE_AUDIO_MIXER, AGENT_TYPE_AUDIO_MIXER,
AGENT_TYPE_VOXEL AGENT_TYPE_VOXEL_SERVER
}; };
char DOMAIN_HOSTNAME[] = "highfidelity.below92.com"; char DOMAIN_HOSTNAME[] = "highfidelity.below92.com";
@ -34,7 +34,6 @@ char DOMAIN_IP[100] = ""; // IP Address will be re-set by lookup on startup
const int DOMAINSERVER_PORT = 40102; const int DOMAINSERVER_PORT = 40102;
bool silentAgentThreadStopFlag = false; bool silentAgentThreadStopFlag = false;
bool domainServerCheckinStopFlag = false;
bool pingUnknownAgentThreadStopFlag = false; bool pingUnknownAgentThreadStopFlag = false;
AgentList* AgentList::_sharedInstance = NULL; AgentList* AgentList::_sharedInstance = NULL;
@ -60,27 +59,24 @@ AgentList* AgentList::getInstance() {
AgentList::AgentList(char newOwnerType, unsigned int newSocketListenPort) : AgentList::AgentList(char newOwnerType, unsigned int newSocketListenPort) :
_agentBuckets(), _agentBuckets(),
_numAgents(0), _numAgents(0),
agentSocket(newSocketListenPort), _agentSocket(newSocketListenPort),
_ownerType(newOwnerType), _ownerType(newOwnerType),
socketListenPort(newSocketListenPort), _agentTypesOfInterest(NULL),
_ownerID(UNKNOWN_AGENT_ID), _ownerID(UNKNOWN_AGENT_ID),
_lastAgentID(0) { _lastAgentID(0) {
pthread_mutex_init(&mutex, 0); pthread_mutex_init(&mutex, 0);
} }
AgentList::~AgentList() { AgentList::~AgentList() {
delete _agentTypesOfInterest;
// stop the spawned threads, if they were started // stop the spawned threads, if they were started
stopSilentAgentRemovalThread(); stopSilentAgentRemovalThread();
stopDomainServerCheckInThread();
stopPingUnknownAgentsThread(); stopPingUnknownAgentsThread();
pthread_mutex_destroy(&mutex); pthread_mutex_destroy(&mutex);
} }
unsigned int AgentList::getSocketListenPort() {
return socketListenPort;
}
void AgentList::processAgentData(sockaddr *senderAddress, unsigned char *packetData, size_t dataBytes) { void AgentList::processAgentData(sockaddr *senderAddress, unsigned char *packetData, size_t dataBytes) {
switch (((char *)packetData)[0]) { switch (((char *)packetData)[0]) {
case PACKET_HEADER_DOMAIN: { case PACKET_HEADER_DOMAIN: {
@ -88,7 +84,7 @@ void AgentList::processAgentData(sockaddr *senderAddress, unsigned char *packetD
break; break;
} }
case PACKET_HEADER_PING: { case PACKET_HEADER_PING: {
agentSocket.send(senderAddress, &PACKET_HEADER_PING_REPLY, 1); _agentSocket.send(senderAddress, &PACKET_HEADER_PING_REPLY, 1);
break; break;
} }
case PACKET_HEADER_PING_REPLY: { case PACKET_HEADER_PING_REPLY: {
@ -181,6 +177,72 @@ Agent* AgentList::agentWithID(uint16_t agentID) {
return NULL; return NULL;
} }
void AgentList::setAgentTypesOfInterest(const char* agentTypesOfInterest, int numAgentTypesOfInterest) {
delete _agentTypesOfInterest;
_agentTypesOfInterest = new char[numAgentTypesOfInterest + sizeof(char)];
memcpy(_agentTypesOfInterest, agentTypesOfInterest, numAgentTypesOfInterest);
_agentTypesOfInterest[numAgentTypesOfInterest] = '\0';
}
void AgentList::sendDomainServerCheckIn() {
static bool printedDomainServerIP = false;
// Lookup the IP address of the domain server if we need to
if (atoi(DOMAIN_IP) == 0) {
struct hostent* pHostInfo;
if ((pHostInfo = gethostbyname(DOMAIN_HOSTNAME)) != NULL) {
sockaddr_in tempAddress;
memcpy(&tempAddress.sin_addr, pHostInfo->h_addr_list[0], pHostInfo->h_length);
strcpy(DOMAIN_IP, inet_ntoa(tempAddress.sin_addr));
printLog("Domain Server: %s \n", DOMAIN_HOSTNAME);
} else {
printLog("Failed domain server lookup\n");
}
} else if (!printedDomainServerIP) {
printLog("Domain Server IP: %s\n", DOMAIN_IP);
printedDomainServerIP = true;
}
// construct the DS check in packet if we need to
static unsigned char* checkInPacket = NULL;
static int checkInPacketSize;
if (!checkInPacket) {
int numBytesAgentsOfInterest = _agentTypesOfInterest ? strlen((char*) _agentTypesOfInterest) : 0;
// check in packet has header, agent type, port, IP, agent types of interest, null termination
int numPacketBytes = sizeof(PACKET_HEADER) + sizeof(AGENT_TYPE) + sizeof(uint16_t) + (sizeof(char) * 4) +
numBytesAgentsOfInterest + sizeof(unsigned char);
checkInPacket = new unsigned char[numPacketBytes];
unsigned char* packetPosition = checkInPacket;
*(packetPosition++) = (memchr(SOLO_AGENT_TYPES, _ownerType, sizeof(SOLO_AGENT_TYPES)))
? PACKET_HEADER_DOMAIN_REPORT_FOR_DUTY
: PACKET_HEADER_DOMAIN_LIST_REQUEST;
*(packetPosition++) = _ownerType;
packetPosition += packSocket(checkInPacket + sizeof(PACKET_HEADER) + sizeof(AGENT_TYPE),
getLocalAddress(),
htons(_agentSocket.getListeningPort()));
// add the number of bytes for agent types of interest
*(packetPosition++) = numBytesAgentsOfInterest;
// copy over the bytes for agent types of interest, if required
if (numBytesAgentsOfInterest > 0) {
memcpy(packetPosition,
_agentTypesOfInterest,
numBytesAgentsOfInterest);
packetPosition += numBytesAgentsOfInterest;
}
checkInPacketSize = packetPosition - checkInPacket;
}
_agentSocket.send(DOMAIN_IP, DOMAINSERVER_PORT, checkInPacket, checkInPacketSize);
}
int AgentList::processDomainServerList(unsigned char *packetData, size_t dataBytes) { int AgentList::processDomainServerList(unsigned char *packetData, size_t dataBytes) {
int readAgents = 0; int readAgents = 0;
@ -233,7 +295,7 @@ Agent* AgentList::addOrUpdateAgent(sockaddr* publicSocket, sockaddr* localSocket
newAgent->activatePublicSocket(); newAgent->activatePublicSocket();
} }
if (newAgent->getType() == AGENT_TYPE_VOXEL || if (newAgent->getType() == AGENT_TYPE_VOXEL_SERVER ||
newAgent->getType() == AGENT_TYPE_AVATAR_MIXER || newAgent->getType() == AGENT_TYPE_AVATAR_MIXER ||
newAgent->getType() == AGENT_TYPE_AUDIO_MIXER) { newAgent->getType() == AGENT_TYPE_AUDIO_MIXER) {
// this is currently the cheat we use to talk directly to our test servers on EC2 // this is currently the cheat we use to talk directly to our test servers on EC2
@ -247,9 +309,7 @@ Agent* AgentList::addOrUpdateAgent(sockaddr* publicSocket, sockaddr* localSocket
} else { } else {
if (agent->getType() == AGENT_TYPE_AUDIO_MIXER || if (agent->getType() == AGENT_TYPE_AUDIO_MIXER ||
agent->getType() == AGENT_TYPE_VOXEL || agent->getType() == AGENT_TYPE_VOXEL_SERVER) {
agent->getType() == AGENT_TYPE_ANIMATION_SERVER ||
agent->getType() == AGENT_TYPE_AUDIO_INJECTOR) {
// until the Audio class also uses our agentList, we need to update // until the Audio class also uses our agentList, we need to update
// the lastRecvTimeUsecs for the audio mixer so it doesn't get killed and re-added continously // the lastRecvTimeUsecs for the audio mixer so it doesn't get killed and re-added continously
agent->setLastHeardMicrostamp(usecTimestampNow()); agent->setLastHeardMicrostamp(usecTimestampNow());
@ -281,7 +341,7 @@ void AgentList::broadcastToAgents(unsigned char *broadcastData, size_t dataBytes
// only send to the AgentTypes we are asked to send to. // only send to the AgentTypes we are asked to send to.
if (agent->getActiveSocket() != NULL && memchr(agentTypes, agent->getType(), numAgentTypes)) { if (agent->getActiveSocket() != NULL && memchr(agentTypes, agent->getType(), numAgentTypes)) {
// we know which socket is good for this agent, send there // we know which socket is good for this agent, send there
agentSocket.send(agent->getActiveSocket(), broadcastData, dataBytes); _agentSocket.send(agent->getActiveSocket(), broadcastData, dataBytes);
} }
} }
} }
@ -333,7 +393,7 @@ void *pingUnknownAgents(void *args) {
} }
} }
double usecToSleep = PING_INTERVAL_USECS - (usecTimestampNow() - usecTimestamp(&lastSend)); long long usecToSleep = PING_INTERVAL_USECS - (usecTimestampNow() - usecTimestamp(&lastSend));
if (usecToSleep > 0) { if (usecToSleep > 0) {
usleep(usecToSleep); usleep(usecToSleep);
@ -354,7 +414,7 @@ void AgentList::stopPingUnknownAgentsThread() {
void *removeSilentAgents(void *args) { void *removeSilentAgents(void *args) {
AgentList* agentList = (AgentList*) args; AgentList* agentList = (AgentList*) args;
double checkTimeUSecs, sleepTime; long long checkTimeUSecs, sleepTime;
while (!silentAgentThreadStopFlag) { while (!silentAgentThreadStopFlag) {
checkTimeUSecs = usecTimestampNow(); checkTimeUSecs = usecTimestampNow();
@ -362,8 +422,8 @@ void *removeSilentAgents(void *args) {
for(AgentList::iterator agent = agentList->begin(); agent != agentList->end(); ++agent) { for(AgentList::iterator agent = agentList->begin(); agent != agentList->end(); ++agent) {
if ((checkTimeUSecs - agent->getLastHeardMicrostamp()) > AGENT_SILENCE_THRESHOLD_USECS if ((checkTimeUSecs - agent->getLastHeardMicrostamp()) > AGENT_SILENCE_THRESHOLD_USECS
&& agent->getType() != AGENT_TYPE_VOXEL) { && agent->getType() != AGENT_TYPE_VOXEL_SERVER) {
printLog("Killed "); printLog("Killed ");
Agent::printLog(*agent); Agent::printLog(*agent);
@ -392,62 +452,6 @@ void AgentList::stopSilentAgentRemovalThread() {
pthread_join(removeSilentAgentsThread, NULL); pthread_join(removeSilentAgentsThread, NULL);
} }
void *checkInWithDomainServer(void *args) {
const int DOMAIN_SERVER_CHECK_IN_USECS = 1 * 1000000;
// Lookup the IP address of the domain server if we need to
if (atoi(DOMAIN_IP) == 0) {
struct hostent* pHostInfo;
if ((pHostInfo = gethostbyname(DOMAIN_HOSTNAME)) != NULL) {
sockaddr_in tempAddress;
memcpy(&tempAddress.sin_addr, pHostInfo->h_addr_list[0], pHostInfo->h_length);
strcpy(DOMAIN_IP, inet_ntoa(tempAddress.sin_addr));
printLog("Domain Server: %s \n", DOMAIN_HOSTNAME);
} else {
printLog("Failed lookup domainserver\n");
}
} else printLog("Domain Server IP: %s\n", DOMAIN_IP);
AgentList* parentAgentList = (AgentList*) args;
timeval lastSend;
in_addr_t localAddress = getLocalAddress();
unsigned char packet[8];
packet[0] = PACKET_HEADER_DOMAIN_RFD;
packet[1] = parentAgentList->getOwnerType();
while (!domainServerCheckinStopFlag) {
gettimeofday(&lastSend, NULL);
packSocket(packet + 2, localAddress, htons(parentAgentList->getSocketListenPort()));
parentAgentList->getAgentSocket()->send(DOMAIN_IP, DOMAINSERVER_PORT, packet, sizeof(packet));
packet[0] = PACKET_HEADER_DOMAIN_LIST_REQUEST;
double usecToSleep = DOMAIN_SERVER_CHECK_IN_USECS - (usecTimestampNow() - usecTimestamp(&lastSend));
if (usecToSleep > 0) {
usleep(usecToSleep);
}
}
pthread_exit(0);
return NULL;
}
void AgentList::startDomainServerCheckInThread() {
pthread_create(&checkInWithDomainServerThread, NULL, checkInWithDomainServer, (void*) this);
}
void AgentList::stopDomainServerCheckInThread() {
domainServerCheckinStopFlag = true;
pthread_join(checkInWithDomainServerThread, NULL);
}
AgentList::iterator AgentList::begin() const { AgentList::iterator AgentList::begin() const {
Agent** agentBucket = NULL; Agent** agentBucket = NULL;

View file

@ -24,7 +24,10 @@ const int AGENTS_PER_BUCKET = 100;
const int MAX_PACKET_SIZE = 1500; const int MAX_PACKET_SIZE = 1500;
const unsigned int AGENT_SOCKET_LISTEN_PORT = 40103; const unsigned int AGENT_SOCKET_LISTEN_PORT = 40103;
const int AGENT_SILENCE_THRESHOLD_USECS = 2 * 1000000; const int AGENT_SILENCE_THRESHOLD_USECS = 2 * 1000000;
const int DOMAIN_SERVER_CHECK_IN_USECS = 1 * 1000000;
extern const char SOLO_AGENT_TYPES[3]; extern const char SOLO_AGENT_TYPES[3];
extern char DOMAIN_HOSTNAME[]; extern char DOMAIN_HOSTNAME[];
@ -45,15 +48,27 @@ public:
AgentListIterator begin() const; AgentListIterator begin() const;
AgentListIterator end() const; AgentListIterator end() const;
char getOwnerType() const { return _ownerType; }
uint16_t getLastAgentID() const { return _lastAgentID; }
void increaseAgentID() { ++_lastAgentID; }
uint16_t getOwnerID() const { return _ownerID; }
void setOwnerID(uint16_t ownerID) { _ownerID = ownerID; }
UDPSocket* getAgentSocket() { return &_agentSocket; }
unsigned int getSocketListenPort() const { return _agentSocket.getListeningPort(); };
void(*linkedDataCreateCallback)(Agent *); void(*linkedDataCreateCallback)(Agent *);
int size() { return _numAgents; } int size() { return _numAgents; }
UDPSocket* getAgentSocket() { return &agentSocket; }
void lock() { pthread_mutex_lock(&mutex); } void lock() { pthread_mutex_lock(&mutex); }
void unlock() { pthread_mutex_unlock(&mutex); } void unlock() { pthread_mutex_unlock(&mutex); }
void setAgentTypesOfInterest(const char* agentTypesOfInterest, int numAgentTypesOfInterest);
void sendDomainServerCheckIn();
int processDomainServerList(unsigned char *packetData, size_t dataBytes); int processDomainServerList(unsigned char *packetData, size_t dataBytes);
Agent* agentWithAddress(sockaddr *senderAddress); Agent* agentWithAddress(sockaddr *senderAddress);
@ -68,22 +83,11 @@ public:
int updateAgentWithData(Agent *agent, unsigned char *packetData, int dataBytes); int updateAgentWithData(Agent *agent, unsigned char *packetData, int dataBytes);
void broadcastToAgents(unsigned char *broadcastData, size_t dataBytes, const char* agentTypes, int numAgentTypes); void broadcastToAgents(unsigned char *broadcastData, size_t dataBytes, const char* agentTypes, int numAgentTypes);
unsigned int getSocketListenPort();
char getOwnerType() const { return _ownerType; }
uint16_t getLastAgentID() const { return _lastAgentID; }
void increaseAgentID() { ++_lastAgentID; }
uint16_t getOwnerID() const { return _ownerID; }
void setOwnerID(uint16_t ownerID) { _ownerID = ownerID; }
Agent* soloAgentOfType(char agentType); Agent* soloAgentOfType(char agentType);
void startSilentAgentRemovalThread(); void startSilentAgentRemovalThread();
void stopSilentAgentRemovalThread(); void stopSilentAgentRemovalThread();
void startDomainServerCheckInThread();
void stopDomainServerCheckInThread();
void startPingUnknownAgentsThread(); void startPingUnknownAgentsThread();
void stopPingUnknownAgentsThread(); void stopPingUnknownAgentsThread();
@ -100,9 +104,10 @@ private:
Agent** _agentBuckets[MAX_NUM_AGENTS / AGENTS_PER_BUCKET]; Agent** _agentBuckets[MAX_NUM_AGENTS / AGENTS_PER_BUCKET];
int _numAgents; int _numAgents;
UDPSocket agentSocket; UDPSocket _agentSocket;
char _ownerType; char _ownerType;
unsigned int socketListenPort; char* _agentTypesOfInterest;
unsigned int _socketListenPort;
uint16_t _ownerID; uint16_t _ownerID;
uint16_t _lastAgentID; uint16_t _lastAgentID;
pthread_t removeSilentAgentsThread; pthread_t removeSilentAgentsThread;

View file

@ -3,11 +3,10 @@
// hifi // hifi
// //
// Created by Brad Hefta-Gaub on 2013/04/09 // Created by Brad Hefta-Gaub on 2013/04/09
// // Copyright (c) 2013 HighFidelity, Inc. All rights reserved.
// //
// Single byte/character Agent Types used to identify various agents in the system. // Single byte/character Agent Types used to identify various agents in the system.
// For example, an agent whose is 'V' is always a voxel server. // For example, an agent whose is 'V' is always a voxel server.
//
#ifndef hifi_AgentTypes_h #ifndef hifi_AgentTypes_h
#define hifi_AgentTypes_h #define hifi_AgentTypes_h
@ -17,13 +16,13 @@
// If you don't then it will make things harder on your co-developers in debugging because the Agent // If you don't then it will make things harder on your co-developers in debugging because the Agent
// class won't know the name and will report it as "Unknown". // class won't know the name and will report it as "Unknown".
// Agent Type Codes typedef char AGENT_TYPE;
const char AGENT_TYPE_DOMAIN = 'D'; const AGENT_TYPE AGENT_TYPE_DOMAIN = 'D';
const char AGENT_TYPE_VOXEL = 'V'; const AGENT_TYPE AGENT_TYPE_VOXEL_SERVER = 'V';
const char AGENT_TYPE_AVATAR = 'I'; const AGENT_TYPE AGENT_TYPE_AVATAR = 'I';
const char AGENT_TYPE_AUDIO_MIXER = 'M'; const AGENT_TYPE AGENT_TYPE_AUDIO_MIXER = 'M';
const char AGENT_TYPE_AVATAR_MIXER = 'W'; const AGENT_TYPE AGENT_TYPE_AVATAR_MIXER = 'W';
const char AGENT_TYPE_AUDIO_INJECTOR = 'A'; const AGENT_TYPE AGENT_TYPE_AUDIO_INJECTOR = 'A';
const char AGENT_TYPE_ANIMATION_SERVER = 'a'; const AGENT_TYPE AGENT_TYPE_ANIMATION_SERVER = 'a';
#endif #endif

View file

@ -32,7 +32,7 @@ const PACKET_HEADER PACKET_HEADER_AVATAR_VOXEL_URL = 'U';
const PACKET_HEADER PACKET_HEADER_TRANSMITTER_DATA_V2 = 'T'; const PACKET_HEADER PACKET_HEADER_TRANSMITTER_DATA_V2 = 'T';
const PACKET_HEADER PACKET_HEADER_ENVIRONMENT_DATA = 'e'; const PACKET_HEADER PACKET_HEADER_ENVIRONMENT_DATA = 'e';
const PACKET_HEADER PACKET_HEADER_DOMAIN_LIST_REQUEST = 'L'; const PACKET_HEADER PACKET_HEADER_DOMAIN_LIST_REQUEST = 'L';
const PACKET_HEADER PACKET_HEADER_DOMAIN_RFD = 'C'; const PACKET_HEADER PACKET_HEADER_DOMAIN_REPORT_FOR_DUTY = 'C';
// These are supported Z-Command // These are supported Z-Command

View file

@ -104,7 +104,7 @@ int PerfStat::DumpStats(char** array) {
// Destructor handles recording all of our stats // Destructor handles recording all of our stats
PerformanceWarning::~PerformanceWarning() { PerformanceWarning::~PerformanceWarning() {
double end = usecTimestampNow(); long long end = usecTimestampNow();
double elapsedmsec = (end - _start) / 1000.0; double elapsedmsec = (end - _start) / 1000.0;
if ((_alwaysDisplay || _renderWarningsOn) && elapsedmsec > 1) { if ((_alwaysDisplay || _renderWarningsOn) && elapsedmsec > 1) {
if (elapsedmsec > 1000) { if (elapsedmsec > 1000) {

View file

@ -84,7 +84,7 @@ typedef std::map<std::string,PerfStatHistory,std::less<std::string> >::iterator
class PerformanceWarning { class PerformanceWarning {
private: private:
double _start; long long _start;
const char* _message; const char* _message;
bool _renderWarningsOn; bool _renderWarningsOn;
bool _alwaysDisplay; bool _alwaysDisplay;

View file

@ -22,14 +22,14 @@
#include <CoreFoundation/CoreFoundation.h> #include <CoreFoundation/CoreFoundation.h>
#endif #endif
double usecTimestamp(timeval *time) { long long usecTimestamp(timeval *time) {
return (time->tv_sec * 1000000.0 + time->tv_usec); return (time->tv_sec * 1000000 + time->tv_usec);
} }
double usecTimestampNow() { long long usecTimestampNow() {
timeval now; timeval now;
gettimeofday(&now, NULL); gettimeofday(&now, NULL);
return (now.tv_sec * 1000000.0 + now.tv_usec); return (now.tv_sec * 1000000 + now.tv_usec);
} }
float randFloat () { float randFloat () {
@ -102,12 +102,12 @@ void setAtBit(unsigned char& byte, int bitIndex) {
} }
int getSemiNibbleAt(unsigned char& byte, int bitIndex) { int getSemiNibbleAt(unsigned char& byte, int bitIndex) {
return (byte >> (7 - bitIndex) & 3); // semi-nibbles store 00, 01, 10, or 11 return (byte >> (6 - bitIndex) & 3); // semi-nibbles store 00, 01, 10, or 11
} }
void setSemiNibbleAt(unsigned char& byte, int bitIndex, int value) { void setSemiNibbleAt(unsigned char& byte, int bitIndex, int value) {
//assert(value <= 3 && value >= 0); //assert(value <= 3 && value >= 0);
byte += ((value & 3) << (7 - bitIndex)); // semi-nibbles store 00, 01, 10, or 11 byte += ((value & 3) << (6 - bitIndex)); // semi-nibbles store 00, 01, 10, or 11
} }

View file

@ -36,8 +36,8 @@ static const float DECIMETER = 0.1f;
static const float CENTIMETER = 0.01f; static const float CENTIMETER = 0.01f;
static const float MILLIIMETER = 0.001f; static const float MILLIIMETER = 0.001f;
double usecTimestamp(timeval *time); long long usecTimestamp(timeval *time);
double usecTimestampNow(); long long usecTimestampNow();
float randFloat(); float randFloat();
int randIntInRange (int min, int max); int randIntInRange (int min, int max);

View file

@ -25,7 +25,7 @@ public:
float getAverageSampleValuePerSecond(); float getAverageSampleValuePerSecond();
private: private:
int _numSamples; int _numSamples;
double _lastEventTimestamp; long long _lastEventTimestamp;
float _average; float _average;
float _eventDeltaAverage; float _eventDeltaAverage;

View file

@ -117,7 +117,7 @@ unsigned short loadBufferWithSocketInfo(char* addressBuffer, sockaddr* socket) {
} }
} }
UDPSocket::UDPSocket(int listeningPort) : blocking(true) { UDPSocket::UDPSocket(int listeningPort) : listeningPort(listeningPort), blocking(true) {
init(); init();
// create the socket // create the socket
handle = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); handle = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
@ -140,6 +140,13 @@ UDPSocket::UDPSocket(int listeningPort) : blocking(true) {
return; return;
} }
// if we requested an ephemeral port, get the actual port
if (listeningPort == 0) {
socklen_t addressLength = sizeof(sockaddr_in);
getsockname(handle, (sockaddr*) &bind_address, &addressLength);
listeningPort = ntohs(bind_address.sin_port);
}
// set timeout on socket recieve to 0.5 seconds // set timeout on socket recieve to 0.5 seconds
struct timeval tv; struct timeval tv;
tv.tv_sec = 0; tv.tv_sec = 0;

View file

@ -23,14 +23,16 @@ public:
UDPSocket(int listening_port); UDPSocket(int listening_port);
~UDPSocket(); ~UDPSocket();
bool init(); bool init();
int getListeningPort() const { return listeningPort; }
void setBlocking(bool blocking); void setBlocking(bool blocking);
bool isBlocking() { return blocking; } bool isBlocking() const { return blocking; }
int send(sockaddr* destAddress, const void* data, size_t byteLength) const; int send(sockaddr* destAddress, const void* data, size_t byteLength) const;
int send(char* destAddress, int destPort, const void* data, size_t byteLength) const; int send(char* destAddress, int destPort, const void* data, size_t byteLength) const;
bool receive(void* receivedData, ssize_t* receivedBytes) const; bool receive(void* receivedData, ssize_t* receivedBytes) const;
bool receive(sockaddr* recvAddress, void* receivedData, ssize_t* receivedBytes) const; bool receive(sockaddr* recvAddress, void* receivedData, ssize_t* receivedBytes) const;
private: private:
int handle; int handle;
int listeningPort;
bool blocking; bool blocking;
}; };

View file

@ -29,7 +29,7 @@ private:
#endif #endif
glBufferIndex _glBufferIndex; glBufferIndex _glBufferIndex;
bool _isDirty; bool _isDirty;
double _lastChanged; long long _lastChanged;
bool _shouldRender; bool _shouldRender;
bool _isStagedForDeletion; bool _isStagedForDeletion;
AABox _box; AABox _box;
@ -80,7 +80,7 @@ public:
void printDebugDetails(const char* label) const; void printDebugDetails(const char* label) const;
bool isDirty() const { return _isDirty; }; bool isDirty() const { return _isDirty; };
void clearDirtyBit() { _isDirty = false; }; void clearDirtyBit() { _isDirty = false; };
bool hasChangedSince(double time) const { return (_lastChanged > time); }; bool hasChangedSince(long long time) const { return (_lastChanged > time); };
void markWithChangedTime() { _lastChanged = usecTimestampNow(); }; void markWithChangedTime() { _lastChanged = usecTimestampNow(); };
void handleSubtreeChanged(VoxelTree* myTree); void handleSubtreeChanged(VoxelTree* myTree);
@ -103,8 +103,8 @@ public:
void setColor(const nodeColor& color); void setColor(const nodeColor& color);
const nodeColor& getTrueColor() const { return _trueColor; }; const nodeColor& getTrueColor() const { return _trueColor; };
const nodeColor& getColor() const { return _currentColor; }; const nodeColor& getColor() const { return _currentColor; };
void setDensity(const float density) { _density = density; }; void setDensity(float density) { _density = density; };
const float getDensity() const { return _density; }; float getDensity() const { return _density; };
#else #else
void setFalseColor(colorPart red, colorPart green, colorPart blue) { /* no op */ }; void setFalseColor(colorPart red, colorPart green, colorPart blue) { /* no op */ };
void setFalseColored(bool isFalseColored) { /* no op */ }; void setFalseColored(bool isFalseColored) { /* no op */ };

View file

@ -32,7 +32,7 @@
const char* LOCAL_VOXELS_PERSIST_FILE = "resources/voxels.svo"; const char* LOCAL_VOXELS_PERSIST_FILE = "resources/voxels.svo";
const char* VOXELS_PERSIST_FILE = "/etc/highfidelity/voxel-server/resources/voxels.svo"; const char* VOXELS_PERSIST_FILE = "/etc/highfidelity/voxel-server/resources/voxels.svo";
const double VOXEL_PERSIST_INTERVAL = 1000.0 * 30; // every 30 seconds const long long VOXEL_PERSIST_INTERVAL = 1000 * 30; // every 30 seconds
const int VOXEL_LISTEN_PORT = 40106; const int VOXEL_LISTEN_PORT = 40106;
@ -118,7 +118,7 @@ void resInVoxelDistributor(AgentList* agentList,
bool searchReset = false; bool searchReset = false;
int searchLoops = 0; int searchLoops = 0;
int searchLevelWas = agentData->getMaxSearchLevel(); int searchLevelWas = agentData->getMaxSearchLevel();
double start = usecTimestampNow(); long long start = usecTimestampNow();
while (!searchReset && agentData->nodeBag.isEmpty()) { while (!searchReset && agentData->nodeBag.isEmpty()) {
searchLoops++; searchLoops++;
@ -137,19 +137,19 @@ void resInVoxelDistributor(AgentList* agentList,
} }
} }
} }
double end = usecTimestampNow(); long long end = usecTimestampNow();
double elapsedmsec = (end - start)/1000.0; int elapsedmsec = (end - start)/1000;
if (elapsedmsec > 100) { if (elapsedmsec > 100) {
if (elapsedmsec > 1000) { if (elapsedmsec > 1000) {
double elapsedsec = (end - start)/1000000.0; int elapsedsec = (end - start)/1000000;
printf("WARNING! searchForColoredNodes() took %lf seconds to identify %d nodes at level %d in %d loops\n", printf("WARNING! searchForColoredNodes() took %d seconds to identify %d nodes at level %d in %d loops\n",
elapsedsec, agentData->nodeBag.count(), searchLevelWas, searchLoops); elapsedsec, agentData->nodeBag.count(), searchLevelWas, searchLoops);
} else { } else {
printf("WARNING! searchForColoredNodes() took %lf milliseconds to identify %d nodes at level %d in %d loops\n", printf("WARNING! searchForColoredNodes() took %d milliseconds to identify %d nodes at level %d in %d loops\n",
elapsedmsec, agentData->nodeBag.count(), searchLevelWas, searchLoops); elapsedmsec, agentData->nodeBag.count(), searchLevelWas, searchLoops);
} }
} else if (::debugVoxelSending) { } else if (::debugVoxelSending) {
printf("searchForColoredNodes() took %lf milliseconds to identify %d nodes at level %d in %d loops\n", printf("searchForColoredNodes() took %d milliseconds to identify %d nodes at level %d in %d loops\n",
elapsedmsec, agentData->nodeBag.count(), searchLevelWas, searchLoops); elapsedmsec, agentData->nodeBag.count(), searchLevelWas, searchLoops);
} }
@ -161,7 +161,7 @@ void resInVoxelDistributor(AgentList* agentList,
int packetsSentThisInterval = 0; int packetsSentThisInterval = 0;
int truePacketsSent = 0; int truePacketsSent = 0;
int trueBytesSent = 0; int trueBytesSent = 0;
double start = usecTimestampNow(); long long start = usecTimestampNow();
bool shouldSendEnvironments = shouldDo(ENVIRONMENT_SEND_INTERVAL_USECS, VOXEL_SEND_INTERVAL_USECS); bool shouldSendEnvironments = shouldDo(ENVIRONMENT_SEND_INTERVAL_USECS, VOXEL_SEND_INTERVAL_USECS);
while (packetsSentThisInterval < PACKETS_PER_CLIENT_PER_INTERVAL - (shouldSendEnvironments ? 1 : 0)) { while (packetsSentThisInterval < PACKETS_PER_CLIENT_PER_INTERVAL - (shouldSendEnvironments ? 1 : 0)) {
@ -206,19 +206,19 @@ void resInVoxelDistributor(AgentList* agentList,
trueBytesSent += envPacketLength; trueBytesSent += envPacketLength;
truePacketsSent++; truePacketsSent++;
} }
double end = usecTimestampNow(); long long end = usecTimestampNow();
double elapsedmsec = (end - start)/1000.0; int elapsedmsec = (end - start)/1000;
if (elapsedmsec > 100) { if (elapsedmsec > 100) {
if (elapsedmsec > 1000) { if (elapsedmsec > 1000) {
double elapsedsec = (end - start)/1000000.0; int elapsedsec = (end - start)/1000000;
printf("WARNING! packetLoop() took %lf seconds to generate %d bytes in %d packets at level %d, %d nodes still to send\n", printf("WARNING! packetLoop() took %d seconds to generate %d bytes in %d packets at level %d, %d nodes still to send\n",
elapsedsec, trueBytesSent, truePacketsSent, searchLevelWas, agentData->nodeBag.count()); elapsedsec, trueBytesSent, truePacketsSent, searchLevelWas, agentData->nodeBag.count());
} else { } else {
printf("WARNING! packetLoop() took %lf milliseconds to generate %d bytes in %d packets at level %d, %d nodes still to send\n", printf("WARNING! packetLoop() took %d milliseconds to generate %d bytes in %d packets at level %d, %d nodes still to send\n",
elapsedmsec, trueBytesSent, truePacketsSent, searchLevelWas, agentData->nodeBag.count()); elapsedmsec, trueBytesSent, truePacketsSent, searchLevelWas, agentData->nodeBag.count());
} }
} else if (::debugVoxelSending) { } else if (::debugVoxelSending) {
printf("packetLoop() took %lf milliseconds to generate %d bytes in %d packets at level %d, %d nodes still to send\n", printf("packetLoop() took %d milliseconds to generate %d bytes in %d packets at level %d, %d nodes still to send\n",
elapsedmsec, trueBytesSent, truePacketsSent, searchLevelWas, agentData->nodeBag.count()); elapsedmsec, trueBytesSent, truePacketsSent, searchLevelWas, agentData->nodeBag.count());
} }
@ -245,7 +245,7 @@ void deepestLevelVoxelDistributor(AgentList* agentList,
pthread_mutex_lock(&::treeLock); pthread_mutex_lock(&::treeLock);
int maxLevelReached = 0; int maxLevelReached = 0;
double start = usecTimestampNow(); long long start = usecTimestampNow();
// FOR NOW... agent tells us if it wants to receive only view frustum deltas // FOR NOW... agent tells us if it wants to receive only view frustum deltas
bool wantDelta = agentData->getWantDelta(); bool wantDelta = agentData->getWantDelta();
@ -281,19 +281,19 @@ void deepestLevelVoxelDistributor(AgentList* agentList,
} }
} }
double end = usecTimestampNow(); long long end = usecTimestampNow();
double elapsedmsec = (end - start)/1000.0; int elapsedmsec = (end - start)/1000;
if (elapsedmsec > 100) { if (elapsedmsec > 100) {
if (elapsedmsec > 1000) { if (elapsedmsec > 1000) {
double elapsedsec = (end - start)/1000000.0; int elapsedsec = (end - start)/1000000;
printf("WARNING! searchForColoredNodes() took %lf seconds to identify %d nodes at level %d\n", printf("WARNING! searchForColoredNodes() took %d seconds to identify %d nodes at level %d\n",
elapsedsec, agentData->nodeBag.count(), maxLevelReached); elapsedsec, agentData->nodeBag.count(), maxLevelReached);
} else { } else {
printf("WARNING! searchForColoredNodes() took %lf milliseconds to identify %d nodes at level %d\n", printf("WARNING! searchForColoredNodes() took %d milliseconds to identify %d nodes at level %d\n",
elapsedmsec, agentData->nodeBag.count(), maxLevelReached); elapsedmsec, agentData->nodeBag.count(), maxLevelReached);
} }
} else if (::debugVoxelSending) { } else if (::debugVoxelSending) {
printf("searchForColoredNodes() took %lf milliseconds to identify %d nodes at level %d\n", printf("searchForColoredNodes() took %d milliseconds to identify %d nodes at level %d\n",
elapsedmsec, agentData->nodeBag.count(), maxLevelReached); elapsedmsec, agentData->nodeBag.count(), maxLevelReached);
} }
@ -304,7 +304,7 @@ void deepestLevelVoxelDistributor(AgentList* agentList,
int packetsSentThisInterval = 0; int packetsSentThisInterval = 0;
int truePacketsSent = 0; int truePacketsSent = 0;
int trueBytesSent = 0; int trueBytesSent = 0;
double start = usecTimestampNow(); long long start = usecTimestampNow();
bool shouldSendEnvironments = shouldDo(ENVIRONMENT_SEND_INTERVAL_USECS, VOXEL_SEND_INTERVAL_USECS); bool shouldSendEnvironments = shouldDo(ENVIRONMENT_SEND_INTERVAL_USECS, VOXEL_SEND_INTERVAL_USECS);
while (packetsSentThisInterval < PACKETS_PER_CLIENT_PER_INTERVAL - (shouldSendEnvironments ? 1 : 0)) { while (packetsSentThisInterval < PACKETS_PER_CLIENT_PER_INTERVAL - (shouldSendEnvironments ? 1 : 0)) {
@ -351,19 +351,19 @@ void deepestLevelVoxelDistributor(AgentList* agentList,
truePacketsSent++; truePacketsSent++;
} }
double end = usecTimestampNow(); long long end = usecTimestampNow();
double elapsedmsec = (end - start)/1000.0; int elapsedmsec = (end - start)/1000;
if (elapsedmsec > 100) { if (elapsedmsec > 100) {
if (elapsedmsec > 1000) { if (elapsedmsec > 1000) {
double elapsedsec = (end - start)/1000000.0; int elapsedsec = (end - start)/1000000;
printf("WARNING! packetLoop() took %lf seconds to generate %d bytes in %d packets %d nodes still to send\n", printf("WARNING! packetLoop() took %d seconds to generate %d bytes in %d packets %d nodes still to send\n",
elapsedsec, trueBytesSent, truePacketsSent, agentData->nodeBag.count()); elapsedsec, trueBytesSent, truePacketsSent, agentData->nodeBag.count());
} else { } else {
printf("WARNING! packetLoop() took %lf milliseconds to generate %d bytes in %d packets, %d nodes still to send\n", printf("WARNING! packetLoop() took %d milliseconds to generate %d bytes in %d packets, %d nodes still to send\n",
elapsedmsec, trueBytesSent, truePacketsSent, agentData->nodeBag.count()); elapsedmsec, trueBytesSent, truePacketsSent, agentData->nodeBag.count());
} }
} else if (::debugVoxelSending) { } else if (::debugVoxelSending) {
printf("packetLoop() took %lf milliseconds to generate %d bytes in %d packets, %d nodes still to send\n", printf("packetLoop() took %d milliseconds to generate %d bytes in %d packets, %d nodes still to send\n",
elapsedmsec, trueBytesSent, truePacketsSent, agentData->nodeBag.count()); elapsedmsec, trueBytesSent, truePacketsSent, agentData->nodeBag.count());
} }
@ -380,10 +380,10 @@ void deepestLevelVoxelDistributor(AgentList* agentList,
pthread_mutex_unlock(&::treeLock); pthread_mutex_unlock(&::treeLock);
} }
double lastPersistVoxels = 0; long long lastPersistVoxels = 0;
void persistVoxelsWhenDirty() { void persistVoxelsWhenDirty() {
double now = usecTimestampNow(); long long now = usecTimestampNow();
double sinceLastTime = (now - ::lastPersistVoxels) / 1000.0; long long sinceLastTime = (now - ::lastPersistVoxels) / 1000;
// check the dirty bit and persist here... // check the dirty bit and persist here...
if (::wantVoxelPersist && ::serverTree.isDirty() && sinceLastTime > VOXEL_PERSIST_INTERVAL) { if (::wantVoxelPersist && ::serverTree.isDirty() && sinceLastTime > VOXEL_PERSIST_INTERVAL) {
@ -428,7 +428,7 @@ void *distributeVoxelsToListeners(void *args) {
} }
// dynamically sleep until we need to fire off the next set of voxels // dynamically sleep until we need to fire off the next set of voxels
double usecToSleep = VOXEL_SEND_INTERVAL_USECS - (usecTimestampNow() - usecTimestamp(&lastSendTime)); long long usecToSleep = VOXEL_SEND_INTERVAL_USECS - (usecTimestampNow() - usecTimestamp(&lastSendTime));
if (usecToSleep > 0) { if (usecToSleep > 0) {
usleep(usecToSleep); usleep(usecToSleep);
@ -450,7 +450,7 @@ int main(int argc, const char * argv[]) {
pthread_mutex_init(&::treeLock, NULL); pthread_mutex_init(&::treeLock, NULL);
AgentList* agentList = AgentList::createInstance(AGENT_TYPE_VOXEL, VOXEL_LISTEN_PORT); AgentList* agentList = AgentList::createInstance(AGENT_TYPE_VOXEL_SERVER, VOXEL_LISTEN_PORT);
setvbuf(stdout, NULL, _IOLBF, 0); setvbuf(stdout, NULL, _IOLBF, 0);
// Handle Local Domain testing with the --local command line // Handle Local Domain testing with the --local command line
@ -464,7 +464,6 @@ int main(int argc, const char * argv[]) {
agentList->linkedDataCreateCallback = &attachVoxelAgentDataToAgent; agentList->linkedDataCreateCallback = &attachVoxelAgentDataToAgent;
agentList->startSilentAgentRemovalThread(); agentList->startSilentAgentRemovalThread();
agentList->startDomainServerCheckInThread();
srand((unsigned)time(0)); srand((unsigned)time(0));
@ -576,10 +575,19 @@ int main(int argc, const char * argv[]) {
unsigned char *packetData = new unsigned char[MAX_PACKET_SIZE]; unsigned char *packetData = new unsigned char[MAX_PACKET_SIZE];
ssize_t receivedBytes; ssize_t receivedBytes;
timeval lastDomainServerCheckIn = {};
// loop to send to agents requesting data // loop to send to agents requesting data
while (true) {
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);
AgentList::getInstance()->sendDomainServerCheckIn();
}
// check to see if we need to persist our voxel state // check to see if we need to persist our voxel state
persistVoxelsWhenDirty(); persistVoxelsWhenDirty();