mirror of
https://github.com/HifiExperiments/overte.git
synced 2025-08-05 01:16:00 +02:00
Merge remote-tracking branch 'upstream/master' into particles
This commit is contained in:
commit
c348c5ec35
67 changed files with 3393 additions and 889 deletions
|
@ -10,14 +10,16 @@
|
|||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <cstdio>
|
||||
#include <OctalCode.h>
|
||||
#include <iostream>
|
||||
|
||||
#include <EnvironmentData.h>
|
||||
#include <NodeList.h>
|
||||
#include <NodeTypes.h>
|
||||
#include <EnvironmentData.h>
|
||||
#include <VoxelTree.h>
|
||||
#include <SharedUtil.h>
|
||||
#include <OctalCode.h>
|
||||
#include <PacketHeaders.h>
|
||||
#include <SceneUtils.h>
|
||||
#include <SharedUtil.h>
|
||||
#include <VoxelTree.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include "Syssocket.h"
|
||||
|
@ -46,11 +48,11 @@ bool wantLocalDomain = false;
|
|||
unsigned long packetsSent = 0;
|
||||
unsigned long bytesSent = 0;
|
||||
|
||||
static void sendVoxelEditMessage(PACKET_HEADER header, VoxelDetail& detail) {
|
||||
static void sendVoxelEditMessage(PACKET_TYPE type, VoxelDetail& detail) {
|
||||
unsigned char* bufferOut;
|
||||
int sizeOut;
|
||||
|
||||
if (createVoxelEditMessage(header, 0, 1, &detail, bufferOut, sizeOut)){
|
||||
if (createVoxelEditMessage(type, 0, 1, &detail, bufferOut, sizeOut)){
|
||||
|
||||
::packetsSent++;
|
||||
::bytesSent += sizeOut;
|
||||
|
@ -159,7 +161,7 @@ static void renderMovingBug() {
|
|||
}
|
||||
|
||||
// send the "erase message" first...
|
||||
PACKET_HEADER message = PACKET_HEADER_ERASE_VOXEL;
|
||||
PACKET_TYPE message = PACKET_TYPE_ERASE_VOXEL;
|
||||
if (createVoxelEditMessage(message, 0, VOXELS_PER_BUG, (VoxelDetail*)&details, bufferOut, sizeOut)){
|
||||
|
||||
::packetsSent++;
|
||||
|
@ -229,7 +231,7 @@ static void renderMovingBug() {
|
|||
}
|
||||
|
||||
// send the "create message" ...
|
||||
message = PACKET_HEADER_SET_VOXEL_DESTRUCTIVE;
|
||||
message = PACKET_TYPE_SET_VOXEL_DESTRUCTIVE;
|
||||
if (createVoxelEditMessage(message, 0, VOXELS_PER_BUG, (VoxelDetail*)&details, bufferOut, sizeOut)){
|
||||
|
||||
::packetsSent++;
|
||||
|
@ -274,7 +276,7 @@ static void sendVoxelBlinkMessage() {
|
|||
detail.green = 0 * ::intensity;
|
||||
detail.blue = 0 * ::intensity;
|
||||
|
||||
PACKET_HEADER message = PACKET_HEADER_SET_VOXEL_DESTRUCTIVE;
|
||||
PACKET_TYPE message = PACKET_TYPE_SET_VOXEL_DESTRUCTIVE;
|
||||
|
||||
sendVoxelEditMessage(message, detail);
|
||||
}
|
||||
|
@ -291,7 +293,7 @@ unsigned char onColor[3] = { 0, 255, 255 };
|
|||
const float STRING_OF_LIGHTS_SIZE = 0.125f / TREE_SCALE; // approximately 1/8th meter
|
||||
|
||||
static void sendBlinkingStringOfLights() {
|
||||
PACKET_HEADER message = PACKET_HEADER_SET_VOXEL_DESTRUCTIVE; // we're a bully!
|
||||
PACKET_TYPE message = PACKET_TYPE_SET_VOXEL_DESTRUCTIVE; // we're a bully!
|
||||
float lightScale = STRING_OF_LIGHTS_SIZE;
|
||||
static VoxelDetail details[LIGHTS_PER_SEGMENT];
|
||||
unsigned char* bufferOut;
|
||||
|
@ -421,7 +423,7 @@ const int PACKETS_PER_DANCE_FLOOR = DANCE_FLOOR_VOXELS_PER_PACKET / (DANCE_FLOOR
|
|||
int danceFloorColors[DANCE_FLOOR_WIDTH][DANCE_FLOOR_LENGTH];
|
||||
|
||||
void sendDanceFloor() {
|
||||
PACKET_HEADER message = PACKET_HEADER_SET_VOXEL_DESTRUCTIVE; // we're a bully!
|
||||
PACKET_TYPE message = PACKET_TYPE_SET_VOXEL_DESTRUCTIVE; // we're a bully!
|
||||
float lightScale = DANCE_FLOOR_LIGHT_SIZE;
|
||||
static VoxelDetail details[DANCE_FLOOR_VOXELS_PER_PACKET];
|
||||
unsigned char* bufferOut;
|
||||
|
@ -548,7 +550,7 @@ bool billboardMessage[BILLBOARD_HEIGHT][BILLBOARD_WIDTH] = {
|
|||
};
|
||||
|
||||
static void sendBillboard() {
|
||||
PACKET_HEADER message = PACKET_HEADER_SET_VOXEL_DESTRUCTIVE; // we're a bully!
|
||||
PACKET_TYPE message = PACKET_TYPE_SET_VOXEL_DESTRUCTIVE; // we're a bully!
|
||||
float lightScale = BILLBOARD_LIGHT_SIZE;
|
||||
static VoxelDetail details[VOXELS_PER_PACKET];
|
||||
unsigned char* bufferOut;
|
||||
|
@ -643,14 +645,14 @@ void* animateVoxels(void* args) {
|
|||
sendDanceFloor();
|
||||
}
|
||||
|
||||
long long end = usecTimestampNow();
|
||||
long long elapsedSeconds = (end - ::start) / 1000000;
|
||||
uint64_t end = usecTimestampNow();
|
||||
uint64_t elapsedSeconds = (end - ::start) / 1000000;
|
||||
if (::shouldShowPacketsPerSecond) {
|
||||
printf("packetsSent=%ld, bytesSent=%ld pps=%f bps=%f\n",packetsSent,bytesSent,
|
||||
(float)(packetsSent/elapsedSeconds),(float)(bytesSent/elapsedSeconds));
|
||||
}
|
||||
// dynamically sleep until we need to fire off the next set of voxels
|
||||
long long usecToSleep = ANIMATE_VOXELS_INTERVAL_USECS - (usecTimestampNow() - usecTimestamp(&lastSendTime));
|
||||
uint64_t usecToSleep = ANIMATE_VOXELS_INTERVAL_USECS - (usecTimestampNow() - usecTimestamp(&lastSendTime));
|
||||
|
||||
if (usecToSleep > 0) {
|
||||
usleep(usecToSleep);
|
||||
|
@ -724,7 +726,8 @@ int main(int argc, const char * argv[])
|
|||
}
|
||||
|
||||
// Nodes sending messages to us...
|
||||
if (nodeList->getNodeSocket()->receive(&nodePublicAddress, packetData, &receivedBytes)) {
|
||||
if (nodeList->getNodeSocket()->receive(&nodePublicAddress, packetData, &receivedBytes) &&
|
||||
packetVersionMatch(packetData)) {
|
||||
NodeList::getInstance()->processNodeData(&nodePublicAddress, packetData, receivedBytes);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,6 +24,6 @@ AvatarAudioRingBuffer::~AvatarAudioRingBuffer() {
|
|||
}
|
||||
|
||||
int AvatarAudioRingBuffer::parseData(unsigned char* sourceBuffer, int numBytes) {
|
||||
_shouldLoopbackForNode = (sourceBuffer[0] == PACKET_HEADER_MICROPHONE_AUDIO_WITH_ECHO);
|
||||
_shouldLoopbackForNode = (sourceBuffer[0] == PACKET_TYPE_MICROPHONE_AUDIO_WITH_ECHO);
|
||||
return PositionalAudioRingBuffer::parseData(sourceBuffer, numBytes);
|
||||
}
|
|
@ -21,7 +21,7 @@ InjectedAudioRingBuffer::InjectedAudioRingBuffer() :
|
|||
}
|
||||
|
||||
int InjectedAudioRingBuffer::parseData(unsigned char* sourceBuffer, int numBytes) {
|
||||
unsigned char* currentBuffer = sourceBuffer + sizeof(PACKET_HEADER_INJECT_AUDIO);
|
||||
unsigned char* currentBuffer = sourceBuffer + numBytesForPacketHeader(sourceBuffer);
|
||||
|
||||
// pull stream identifier from the packet
|
||||
memcpy(&_streamIdentifier, currentBuffer, sizeof(_streamIdentifier));
|
||||
|
|
|
@ -22,7 +22,7 @@ PositionalAudioRingBuffer::PositionalAudioRingBuffer() :
|
|||
}
|
||||
|
||||
int PositionalAudioRingBuffer::parseData(unsigned char* sourceBuffer, int numBytes) {
|
||||
unsigned char* currentBuffer = sourceBuffer + sizeof(PACKET_HEADER);
|
||||
unsigned char* currentBuffer = sourceBuffer + numBytesForPacketHeader(sourceBuffer);
|
||||
currentBuffer += parsePositionalData(currentBuffer, numBytes - (currentBuffer - sourceBuffer));
|
||||
currentBuffer += parseAudioSamples(currentBuffer, numBytes - (currentBuffer - sourceBuffer));
|
||||
|
||||
|
|
|
@ -6,60 +6,54 @@
|
|||
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <limits>
|
||||
#include <math.h>
|
||||
#include <string.h>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
#include <fstream>
|
||||
#include <limits>
|
||||
#include <signal.h>
|
||||
|
||||
#include <glm/gtx/norm.hpp>
|
||||
#include <glm/gtx/vector_angle.hpp>
|
||||
#include <NodeList.h>
|
||||
#include <Node.h>
|
||||
#include <NodeTypes.h>
|
||||
#include <SharedUtil.h>
|
||||
#include <StdDev.h>
|
||||
#include <Logstash.h>
|
||||
|
||||
#include "InjectedAudioRingBuffer.h"
|
||||
#include "AvatarAudioRingBuffer.h"
|
||||
#include <AudioRingBuffer.h>
|
||||
#include "PacketHeaders.h"
|
||||
#include <string.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include "Syssocket.h"
|
||||
#include "Systime.h"
|
||||
#include <math.h>
|
||||
#else
|
||||
#include <arpa/inet.h>
|
||||
#include <netinet/in.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#endif //_WIN32
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
#include <glm/gtx/norm.hpp>
|
||||
#include <glm/gtx/vector_angle.hpp>
|
||||
|
||||
#include <Logstash.h>
|
||||
#include <NodeList.h>
|
||||
#include <Node.h>
|
||||
#include <NodeTypes.h>
|
||||
#include <PacketHeaders.h>
|
||||
#include <SharedUtil.h>
|
||||
#include <StdDev.h>
|
||||
|
||||
#include <AudioRingBuffer.h>
|
||||
|
||||
#include "AvatarAudioRingBuffer.h"
|
||||
#include "InjectedAudioRingBuffer.h"
|
||||
|
||||
const unsigned short MIXER_LISTEN_PORT = 55443;
|
||||
|
||||
const short JITTER_BUFFER_MSECS = 12;
|
||||
const short JITTER_BUFFER_SAMPLES = JITTER_BUFFER_MSECS * (SAMPLE_RATE / 1000.0);
|
||||
|
||||
const long long BUFFER_SEND_INTERVAL_USECS = floorf((BUFFER_LENGTH_SAMPLES_PER_CHANNEL / SAMPLE_RATE) * 1000000);
|
||||
const unsigned int BUFFER_SEND_INTERVAL_USECS = floorf((BUFFER_LENGTH_SAMPLES_PER_CHANNEL / SAMPLE_RATE) * 1000000);
|
||||
|
||||
const long MAX_SAMPLE_VALUE = std::numeric_limits<int16_t>::max();
|
||||
const long MIN_SAMPLE_VALUE = std::numeric_limits<int16_t>::min();
|
||||
|
||||
void plateauAdditionOfSamples(int16_t &mixSample, int16_t sampleToAdd) {
|
||||
long sumSample = sampleToAdd + mixSample;
|
||||
|
||||
long normalizedSample = std::min(MAX_SAMPLE_VALUE, sumSample);
|
||||
normalizedSample = std::max(MIN_SAMPLE_VALUE, sumSample);
|
||||
|
||||
mixSample = normalizedSample;
|
||||
}
|
||||
const int MAX_SAMPLE_VALUE = std::numeric_limits<int16_t>::max();
|
||||
const int MIN_SAMPLE_VALUE = std::numeric_limits<int16_t>::min();
|
||||
|
||||
void attachNewBufferToNode(Node *newNode) {
|
||||
if (!newNode->getLinkedData()) {
|
||||
|
@ -103,8 +97,9 @@ int main(int argc, const char* argv[]) {
|
|||
int nextFrame = 0;
|
||||
timeval startTime;
|
||||
|
||||
unsigned char clientPacket[BUFFER_LENGTH_BYTES_STEREO + sizeof(PACKET_HEADER_MIXED_AUDIO)];
|
||||
clientPacket[0] = PACKET_HEADER_MIXED_AUDIO;
|
||||
int numBytesPacketHeader = numBytesForPacketHeader((unsigned char*) &PACKET_TYPE_MIXED_AUDIO);
|
||||
unsigned char clientPacket[BUFFER_LENGTH_BYTES_STEREO + numBytesPacketHeader];
|
||||
populateTypeAndVersion(clientPacket, PACKET_TYPE_MIXED_AUDIO);
|
||||
|
||||
int16_t clientSamples[BUFFER_LENGTH_SAMPLES_PER_CHANNEL * 2] = {};
|
||||
|
||||
|
@ -304,16 +299,23 @@ int main(int argc, const char* argv[]) {
|
|||
// pull the earlier sample for the delayed channel
|
||||
int earlierSample = delaySamplePointer[s] * attenuationCoefficient * weakChannelAmplitudeRatio;
|
||||
|
||||
plateauAdditionOfSamples(delayedChannel[s], earlierSample);
|
||||
delayedChannel[s] = glm::clamp(delayedChannel[s] + earlierSample,
|
||||
MIN_SAMPLE_VALUE,
|
||||
MAX_SAMPLE_VALUE);
|
||||
}
|
||||
|
||||
int16_t currentSample = stkFrameBuffer[s] * attenuationCoefficient;
|
||||
|
||||
plateauAdditionOfSamples(goodChannel[s], currentSample);
|
||||
goodChannel[s] = glm::clamp(goodChannel[s] + currentSample,
|
||||
MIN_SAMPLE_VALUE,
|
||||
MAX_SAMPLE_VALUE);
|
||||
|
||||
if (s + numSamplesDelay < BUFFER_LENGTH_SAMPLES_PER_CHANNEL) {
|
||||
plateauAdditionOfSamples(delayedChannel[s + numSamplesDelay],
|
||||
currentSample * weakChannelAmplitudeRatio);
|
||||
int sumSample = delayedChannel[s + numSamplesDelay]
|
||||
+ (currentSample * weakChannelAmplitudeRatio);
|
||||
delayedChannel[s + numSamplesDelay] = glm::clamp(sumSample,
|
||||
MIN_SAMPLE_VALUE,
|
||||
MAX_SAMPLE_VALUE);
|
||||
}
|
||||
|
||||
if (s >= BUFFER_LENGTH_SAMPLES_PER_CHANNEL - PHASE_DELAY_AT_90) {
|
||||
|
@ -325,7 +327,7 @@ int main(int argc, const char* argv[]) {
|
|||
}
|
||||
}
|
||||
|
||||
memcpy(clientPacket + sizeof(PACKET_HEADER_MIXED_AUDIO), clientSamples, sizeof(clientSamples));
|
||||
memcpy(clientPacket + numBytesPacketHeader, clientSamples, sizeof(clientSamples));
|
||||
nodeList->getNodeSocket()->send(node->getPublicSocket(), clientPacket, sizeof(clientPacket));
|
||||
}
|
||||
}
|
||||
|
@ -345,13 +347,14 @@ int main(int argc, const char* argv[]) {
|
|||
}
|
||||
|
||||
// pull any new audio data from nodes off of the network stack
|
||||
while (nodeList->getNodeSocket()->receive(nodeAddress, packetData, &receivedBytes)) {
|
||||
if (packetData[0] == PACKET_HEADER_MICROPHONE_AUDIO_NO_ECHO ||
|
||||
packetData[0] == PACKET_HEADER_MICROPHONE_AUDIO_WITH_ECHO) {
|
||||
while (nodeList->getNodeSocket()->receive(nodeAddress, packetData, &receivedBytes) &&
|
||||
packetVersionMatch(packetData)) {
|
||||
if (packetData[0] == PACKET_TYPE_MICROPHONE_AUDIO_NO_ECHO ||
|
||||
packetData[0] == PACKET_TYPE_MICROPHONE_AUDIO_WITH_ECHO) {
|
||||
Node* avatarNode = nodeList->addOrUpdateNode(nodeAddress,
|
||||
nodeAddress,
|
||||
NODE_TYPE_AGENT,
|
||||
nodeList->getLastNodeID());
|
||||
nodeAddress,
|
||||
NODE_TYPE_AGENT,
|
||||
nodeList->getLastNodeID());
|
||||
|
||||
if (avatarNode->getNodeID() == nodeList->getLastNodeID()) {
|
||||
nodeList->increaseNodeID();
|
||||
|
@ -363,7 +366,7 @@ int main(int argc, const char* argv[]) {
|
|||
// kill off this node - temporary solution to mixer crash on mac sleep
|
||||
avatarNode->setAlive(false);
|
||||
}
|
||||
} else if (packetData[0] == PACKET_HEADER_INJECT_AUDIO) {
|
||||
} else if (packetData[0] == PACKET_TYPE_INJECT_AUDIO) {
|
||||
Node* matchingInjector = NULL;
|
||||
|
||||
for (NodeList::iterator node = nodeList->begin(); node != nodeList->end(); node++) {
|
||||
|
@ -382,16 +385,16 @@ int main(int argc, const char* argv[]) {
|
|||
|
||||
if (!matchingInjector) {
|
||||
matchingInjector = nodeList->addOrUpdateNode(NULL,
|
||||
NULL,
|
||||
NODE_TYPE_AUDIO_INJECTOR,
|
||||
nodeList->getLastNodeID());
|
||||
NULL,
|
||||
NODE_TYPE_AUDIO_INJECTOR,
|
||||
nodeList->getLastNodeID());
|
||||
nodeList->increaseNodeID();
|
||||
|
||||
}
|
||||
|
||||
// give the new audio data to the matching injector node
|
||||
nodeList->updateNodeWithData(matchingInjector, packetData, receivedBytes);
|
||||
} else if (packetData[0] == PACKET_HEADER_PING) {
|
||||
} else if (packetData[0] == PACKET_TYPE_PING) {
|
||||
|
||||
// If the packet is a ping, let processNodeData handle it.
|
||||
nodeList->processNodeData(nodeAddress, packetData, receivedBytes);
|
||||
|
@ -412,7 +415,7 @@ int main(int argc, const char* argv[]) {
|
|||
numStatCollections++;
|
||||
}
|
||||
|
||||
long long usecToSleep = usecTimestamp(&startTime) + (++nextFrame * BUFFER_SEND_INTERVAL_USECS) - usecTimestampNow();
|
||||
int usecToSleep = usecTimestamp(&startTime) + (++nextFrame * BUFFER_SEND_INTERVAL_USECS) - usecTimestampNow();
|
||||
|
||||
if (usecToSleep > 0) {
|
||||
usleep(usecToSleep);
|
||||
|
|
|
@ -73,7 +73,7 @@ int main(int argc, const char* argv[]) {
|
|||
ssize_t receivedBytes = 0;
|
||||
|
||||
unsigned char *broadcastPacket = new unsigned char[MAX_PACKET_SIZE];
|
||||
*broadcastPacket = PACKET_HEADER_BULK_AVATAR_DATA;
|
||||
int numHeaderBytes = populateTypeAndVersion(broadcastPacket, PACKET_TYPE_BULK_AVATAR_DATA);
|
||||
|
||||
unsigned char* currentBufferPosition = NULL;
|
||||
|
||||
|
@ -92,9 +92,10 @@ int main(int argc, const char* argv[]) {
|
|||
NodeList::getInstance()->sendDomainServerCheckIn();
|
||||
}
|
||||
|
||||
if (nodeList->getNodeSocket()->receive(nodeAddress, packetData, &receivedBytes)) {
|
||||
if (nodeList->getNodeSocket()->receive(nodeAddress, packetData, &receivedBytes) &&
|
||||
packetVersionMatch(packetData)) {
|
||||
switch (packetData[0]) {
|
||||
case PACKET_HEADER_HEAD_DATA:
|
||||
case PACKET_TYPE_HEAD_DATA:
|
||||
// grab the node ID from the packet
|
||||
unpackNodeId(packetData + 1, &nodeID);
|
||||
|
||||
|
@ -103,8 +104,8 @@ int main(int argc, const char* argv[]) {
|
|||
|
||||
// parse positional data from an node
|
||||
nodeList->updateNodeWithData(avatarNode, packetData, receivedBytes);
|
||||
case PACKET_HEADER_INJECT_AUDIO:
|
||||
currentBufferPosition = broadcastPacket + 1;
|
||||
case PACKET_TYPE_INJECT_AUDIO:
|
||||
currentBufferPosition = broadcastPacket + numHeaderBytes;
|
||||
|
||||
// send back a packet with other active node data to this node
|
||||
for (NodeList::iterator node = nodeList->begin(); node != nodeList->end(); node++) {
|
||||
|
@ -116,9 +117,9 @@ int main(int argc, const char* argv[]) {
|
|||
nodeList->getNodeSocket()->send(nodeAddress, broadcastPacket, currentBufferPosition - broadcastPacket);
|
||||
|
||||
break;
|
||||
case PACKET_HEADER_AVATAR_VOXEL_URL:
|
||||
case PACKET_TYPE_AVATAR_VOXEL_URL:
|
||||
// grab the node ID from the packet
|
||||
unpackNodeId(packetData + 1, &nodeID);
|
||||
unpackNodeId(packetData + numBytesForPacketHeader(packetData), &nodeID);
|
||||
|
||||
// let everyone else know about the update
|
||||
for (NodeList::iterator node = nodeList->begin(); node != nodeList->end(); node++) {
|
||||
|
@ -127,7 +128,7 @@ int main(int argc, const char* argv[]) {
|
|||
}
|
||||
}
|
||||
break;
|
||||
case PACKET_HEADER_DOMAIN:
|
||||
case PACKET_TYPE_DOMAIN:
|
||||
// ignore the DS packet, for now nodes are added only when they communicate directly with us
|
||||
break;
|
||||
default:
|
||||
|
|
|
@ -70,7 +70,7 @@ int main(int argc, const char * argv[])
|
|||
char nodeType = '\0';
|
||||
|
||||
unsigned char broadcastPacket[MAX_PACKET_SIZE];
|
||||
broadcastPacket[0] = PACKET_HEADER_DOMAIN;
|
||||
int numHeaderBytes = populateTypeAndVersion(broadcastPacket, PACKET_TYPE_DOMAIN);
|
||||
|
||||
unsigned char* currentBufferPos;
|
||||
unsigned char* startPointer;
|
||||
|
@ -86,11 +86,15 @@ int main(int argc, const char * argv[])
|
|||
|
||||
while (true) {
|
||||
if (nodeList->getNodeSocket()->receive((sockaddr *)&nodePublicAddress, packetData, &receivedBytes) &&
|
||||
(packetData[0] == PACKET_HEADER_DOMAIN_REPORT_FOR_DUTY || packetData[0] == PACKET_HEADER_DOMAIN_LIST_REQUEST)) {
|
||||
(packetData[0] == PACKET_TYPE_DOMAIN_REPORT_FOR_DUTY || packetData[0] == PACKET_TYPE_DOMAIN_LIST_REQUEST) &&
|
||||
packetVersionMatch(packetData)) {
|
||||
// this is an RFD or domain list request packet, and there is a version match
|
||||
std::map<char, Node *> newestSoloNodes;
|
||||
|
||||
nodeType = packetData[1];
|
||||
int numBytesSocket = unpackSocket(packetData + sizeof(PACKET_HEADER) + sizeof(NODE_TYPE),
|
||||
int numBytesSenderHeader = numBytesForPacketHeader(packetData);
|
||||
|
||||
nodeType = *(packetData + numBytesSenderHeader);
|
||||
int numBytesSocket = unpackSocket(packetData + numBytesSenderHeader + sizeof(NODE_TYPE),
|
||||
(sockaddr*) &nodeLocalAddress);
|
||||
|
||||
sockaddr* destinationSocket = (sockaddr*) &nodePublicAddress;
|
||||
|
@ -108,18 +112,18 @@ int main(int argc, const char * argv[])
|
|||
}
|
||||
|
||||
Node* newNode = nodeList->addOrUpdateNode((sockaddr*) &nodePublicAddress,
|
||||
(sockaddr*) &nodeLocalAddress,
|
||||
nodeType,
|
||||
nodeList->getLastNodeID());
|
||||
(sockaddr*) &nodeLocalAddress,
|
||||
nodeType,
|
||||
nodeList->getLastNodeID());
|
||||
|
||||
if (newNode->getNodeID() == nodeList->getLastNodeID()) {
|
||||
nodeList->increaseNodeID();
|
||||
}
|
||||
|
||||
currentBufferPos = broadcastPacket + sizeof(PACKET_HEADER);
|
||||
currentBufferPos = broadcastPacket + numHeaderBytes;
|
||||
startPointer = currentBufferPos;
|
||||
|
||||
unsigned char* nodeTypesOfInterest = packetData + sizeof(PACKET_HEADER) + sizeof(NODE_TYPE)
|
||||
unsigned char* nodeTypesOfInterest = packetData + numBytesSenderHeader + sizeof(NODE_TYPE)
|
||||
+ numBytesSocket + sizeof(unsigned char);
|
||||
int numInterestTypes = *(nodeTypesOfInterest - 1);
|
||||
|
||||
|
@ -159,10 +163,10 @@ int main(int argc, const char * argv[])
|
|||
}
|
||||
|
||||
// update last receive to now
|
||||
long long timeNow = usecTimestampNow();
|
||||
uint64_t timeNow = usecTimestampNow();
|
||||
newNode->setLastHeardMicrostamp(timeNow);
|
||||
|
||||
if (packetData[0] == PACKET_HEADER_DOMAIN_REPORT_FOR_DUTY
|
||||
if (packetData[0] == PACKET_TYPE_DOMAIN_REPORT_FOR_DUTY
|
||||
&& memchr(SOLO_NODE_TYPES, nodeType, sizeof(SOLO_NODE_TYPES))) {
|
||||
newNode->setWakeMicrostamp(timeNow);
|
||||
}
|
||||
|
@ -172,8 +176,8 @@ int main(int argc, const char * argv[])
|
|||
|
||||
// send the constructed list back to this node
|
||||
nodeList->getNodeSocket()->send(destinationSocket,
|
||||
broadcastPacket,
|
||||
(currentBufferPos - startPointer) + 1);
|
||||
broadcastPacket,
|
||||
(currentBufferPos - startPointer) + numHeaderBytes);
|
||||
}
|
||||
|
||||
if (Logstash::shouldSendStats()) {
|
||||
|
|
|
@ -49,9 +49,10 @@ void *receiveNodeData(void *args) {
|
|||
NodeList* nodeList = NodeList::getInstance();
|
||||
|
||||
while (!::stopReceiveNodeDataThread) {
|
||||
if (nodeList->getNodeSocket()->receive(&senderAddress, incomingPacket, &bytesReceived)) {
|
||||
if (nodeList->getNodeSocket()->receive(&senderAddress, incomingPacket, &bytesReceived) &&
|
||||
packetVersionMatch(incomingPacket)) {
|
||||
switch (incomingPacket[0]) {
|
||||
case PACKET_HEADER_BULK_AVATAR_DATA:
|
||||
case PACKET_TYPE_BULK_AVATAR_DATA:
|
||||
// this is the positional data for other nodes
|
||||
// pass that off to the nodeList processBulkNodeData method
|
||||
nodeList->processBulkNodeData(&senderAddress, incomingPacket, bytesReceived);
|
||||
|
@ -85,9 +86,6 @@ int main(int argc, const char* argv[]) {
|
|||
// start the node list thread that will kill off nodes when they stop talking
|
||||
nodeList->startSilentNodeRemovalThread();
|
||||
|
||||
// start the ping thread that hole punches to create an active connection to other nodes
|
||||
nodeList->startPingUnknownNodesThread();
|
||||
|
||||
pthread_t receiveNodeDataThread;
|
||||
pthread_create(&receiveNodeDataThread, NULL, receiveNodeData, NULL);
|
||||
|
||||
|
@ -125,10 +123,10 @@ int main(int argc, const char* argv[]) {
|
|||
nodeList->linkedDataCreateCallback = createAvatarDataForNode;
|
||||
|
||||
unsigned char broadcastPacket[MAX_PACKET_SIZE];
|
||||
broadcastPacket[0] = PACKET_HEADER_HEAD_DATA;
|
||||
int numHeaderBytes = populateTypeAndVersion(broadcastPacket, PACKET_TYPE_HEAD_DATA);
|
||||
|
||||
timeval thisSend;
|
||||
long long numMicrosecondsSleep = 0;
|
||||
int numMicrosecondsSleep = 0;
|
||||
|
||||
int handStateTimer = 0;
|
||||
|
||||
|
@ -153,7 +151,7 @@ int main(int argc, const char* argv[]) {
|
|||
|
||||
// make sure we actually have an avatar mixer with an active socket
|
||||
if (nodeList->getOwnerID() != UNKNOWN_NODE_ID && avatarMixer && avatarMixer->getActiveSocket() != NULL) {
|
||||
unsigned char* packetPosition = broadcastPacket + sizeof(PACKET_HEADER);
|
||||
unsigned char* packetPosition = broadcastPacket + numHeaderBytes;
|
||||
packetPosition += packNodeId(packetPosition, nodeList->getOwnerID());
|
||||
|
||||
// use the getBroadcastData method in the AvatarData class to populate the broadcastPacket buffer
|
||||
|
@ -210,6 +208,5 @@ int main(int argc, const char* argv[]) {
|
|||
pthread_join(receiveNodeDataThread, NULL);
|
||||
|
||||
// stop the node list's threads
|
||||
nodeList->stopPingUnknownNodesThread();
|
||||
nodeList->stopSilentNodeRemovalThread();
|
||||
}
|
||||
|
|
|
@ -144,7 +144,8 @@ int main(int argc, char* argv[]) {
|
|||
nodeList->linkedDataCreateCallback = createAvatarDataForNode;
|
||||
|
||||
timeval lastSend = {};
|
||||
unsigned char broadcastPacket = PACKET_HEADER_INJECT_AUDIO;
|
||||
int numBytesPacketHeader = numBytesForPacketHeader((unsigned char*) &PACKET_TYPE_INJECT_AUDIO);
|
||||
unsigned char* broadcastPacket = new unsigned char[numBytesPacketHeader];
|
||||
|
||||
timeval lastDomainServerCheckIn = {};
|
||||
|
||||
|
@ -168,10 +169,10 @@ int main(int argc, char* argv[]) {
|
|||
NodeList::getInstance()->sendDomainServerCheckIn();
|
||||
}
|
||||
|
||||
while (nodeList->getNodeSocket()->receive(&senderAddress, incomingPacket, &bytesReceived)) {
|
||||
while (nodeList->getNodeSocket()->receive(&senderAddress, incomingPacket, &bytesReceived) &&
|
||||
packetVersionMatch(incomingPacket)) {
|
||||
switch (incomingPacket[0]) {
|
||||
case PACKET_HEADER_BULK_AVATAR_DATA:
|
||||
// this is the positional data for other nodes
|
||||
case PACKET_TYPE_BULK_AVATAR_DATA: // this is the positional data for other nodes
|
||||
// pass that off to the nodeList processBulkNodeData method
|
||||
nodeList->processBulkNodeData(&senderAddress, incomingPacket, bytesReceived);
|
||||
break;
|
||||
|
@ -211,8 +212,8 @@ int main(int argc, char* argv[]) {
|
|||
|
||||
// use the UDPSocket instance attached to our node list to ask avatar mixer for a list of avatars
|
||||
nodeList->getNodeSocket()->send(avatarMixer->getActiveSocket(),
|
||||
&broadcastPacket,
|
||||
sizeof(broadcastPacket));
|
||||
broadcastPacket,
|
||||
numBytesPacketHeader);
|
||||
}
|
||||
} else {
|
||||
if (!injector.isInjectingAudio() && (::shouldLoopAudio || !::hasInjectedAudioOnce)) {
|
||||
|
|
|
@ -99,6 +99,10 @@ find_package(OpenNI)
|
|||
find_package(UVCCameraControl)
|
||||
find_package(ZLIB)
|
||||
|
||||
# link the stk library
|
||||
set(STK_ROOT_DIR ${ROOT_DIR}/externals/stk)
|
||||
find_package(STK REQUIRED)
|
||||
|
||||
# let the source know that we have OpenNI/NITE for Kinect
|
||||
if (OPENNI_FOUND)
|
||||
add_definitions(-DHAVE_OPENNI)
|
||||
|
@ -122,6 +126,7 @@ include_directories(
|
|||
${LEAP_INCLUDE_DIRS}
|
||||
${MOTIONDRIVER_INCLUDE_DIRS}
|
||||
${OPENCV_INCLUDE_DIRS}
|
||||
${STK_INCLUDE_DIRS}
|
||||
)
|
||||
|
||||
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -isystem ${OPENCV_INCLUDE_DIRS}")
|
||||
|
@ -131,6 +136,7 @@ target_link_libraries(
|
|||
${MOTIONDRIVER_LIBRARIES}
|
||||
${OPENCV_LIBRARIES}
|
||||
${ZLIB_LIBRARIES}
|
||||
${STK_LIBRARIES}
|
||||
fervor
|
||||
)
|
||||
|
||||
|
|
|
@ -185,6 +185,8 @@ Application::Application(int& argc, char** argv, timeval &startup_time) :
|
|||
_touchAvgX(0.0f),
|
||||
_touchAvgY(0.0f),
|
||||
_isTouchPressed(false),
|
||||
_yawFromTouch(0.0f),
|
||||
_pitchFromTouch(0.0f),
|
||||
_mousePressed(false),
|
||||
_mouseVoxelScale(1.0f / 1024.0f),
|
||||
_justEditedVoxel(false),
|
||||
|
@ -253,7 +255,6 @@ Application::Application(int& argc, char** argv, timeval &startup_time) :
|
|||
|
||||
// start the nodeList threads
|
||||
NodeList::getInstance()->startSilentNodeRemovalThread();
|
||||
NodeList::getInstance()->startPingUnknownNodesThread();
|
||||
|
||||
_window->setCentralWidget(_glWidget);
|
||||
|
||||
|
@ -907,14 +908,15 @@ void Application::wheelEvent(QWheelEvent* event) {
|
|||
|
||||
void Application::sendPingPackets() {
|
||||
|
||||
char nodeTypesOfInterest[] = {NODE_TYPE_VOXEL_SERVER, NODE_TYPE_AUDIO_MIXER, NODE_TYPE_AVATAR_MIXER};
|
||||
long long currentTime = usecTimestampNow();
|
||||
unsigned char pingPacket[1 + sizeof(currentTime)];
|
||||
pingPacket[0] = PACKET_HEADER_PING;
|
||||
const char nodesToPing[] = {NODE_TYPE_VOXEL_SERVER, NODE_TYPE_AUDIO_MIXER, NODE_TYPE_AVATAR_MIXER};
|
||||
|
||||
uint64_t currentTime = usecTimestampNow();
|
||||
unsigned char pingPacket[numBytesForPacketHeader((unsigned char*) &PACKET_TYPE_PING) + sizeof(currentTime)];
|
||||
int numHeaderBytes = populateTypeAndVersion(pingPacket, PACKET_TYPE_PING);
|
||||
|
||||
memcpy(&pingPacket[1], ¤tTime, sizeof(currentTime));
|
||||
getInstance()->controlledBroadcastToNodes(pingPacket, 1 + sizeof(currentTime),
|
||||
nodeTypesOfInterest, sizeof(nodeTypesOfInterest));
|
||||
memcpy(pingPacket + numHeaderBytes, ¤tTime, sizeof(currentTime));
|
||||
getInstance()->controlledBroadcastToNodes(pingPacket, sizeof(pingPacket),
|
||||
nodesToPing, sizeof(nodesToPing));
|
||||
}
|
||||
|
||||
// Every second, check the frame rates and other stuff
|
||||
|
@ -971,6 +973,9 @@ void Application::idle() {
|
|||
gettimeofday(&check, NULL);
|
||||
|
||||
// Only run simulation code if more than IDLE_SIMULATE_MSECS have passed since last time we ran
|
||||
sendPostedEvents(NULL, QEvent::TouchBegin);
|
||||
sendPostedEvents(NULL, QEvent::TouchUpdate);
|
||||
sendPostedEvents(NULL, QEvent::TouchEnd);
|
||||
|
||||
double timeSinceLastUpdate = diffclock(&_lastTimeUpdated, &check);
|
||||
if (timeSinceLastUpdate > IDLE_SIMULATE_MSECS) {
|
||||
|
@ -980,9 +985,6 @@ void Application::idle() {
|
|||
// This is necessary because id the idle() call takes longer than the
|
||||
// interval between idle() calls, the event loop never gets to run,
|
||||
// and touch events get delayed.
|
||||
sendPostedEvents(NULL, QEvent::TouchBegin);
|
||||
sendPostedEvents(NULL, QEvent::TouchUpdate);
|
||||
sendPostedEvents(NULL, QEvent::TouchEnd);
|
||||
|
||||
const float BIGGEST_DELTA_TIME_SECS = 0.25f;
|
||||
update(glm::clamp((float)timeSinceLastUpdate / 1000.f, 0.f, BIGGEST_DELTA_TIME_SECS));
|
||||
|
@ -1022,7 +1024,11 @@ void Application::sendAvatarVoxelURLMessage(const QUrl& url) {
|
|||
return; // we don't yet know who we are
|
||||
}
|
||||
QByteArray message;
|
||||
message.append(PACKET_HEADER_AVATAR_VOXEL_URL);
|
||||
|
||||
char packetHeader[MAX_PACKET_HEADER_BYTES];
|
||||
int numBytesPacketHeader = populateTypeAndVersion((unsigned char*) packetHeader, PACKET_TYPE_AVATAR_VOXEL_URL);
|
||||
|
||||
message.append(packetHeader, numBytesPacketHeader);
|
||||
message.append((const char*)&ownerID, sizeof(ownerID));
|
||||
message.append(url.toEncoded());
|
||||
|
||||
|
@ -1031,8 +1037,9 @@ void Application::sendAvatarVoxelURLMessage(const QUrl& url) {
|
|||
|
||||
void Application::processAvatarVoxelURLMessage(unsigned char *packetData, size_t dataBytes) {
|
||||
// skip the header
|
||||
packetData++;
|
||||
dataBytes--;
|
||||
int numBytesPacketHeader = numBytesForPacketHeader(packetData);
|
||||
packetData += numBytesPacketHeader;
|
||||
dataBytes -= numBytesPacketHeader;
|
||||
|
||||
// read the node id
|
||||
uint16_t nodeID = *(uint16_t*)packetData;
|
||||
|
@ -1221,6 +1228,10 @@ void Application::doFalseColorizeOccluded() {
|
|||
_voxels.falseColorizeOccluded();
|
||||
}
|
||||
|
||||
void Application::doFalseColorizeOccludedV2() {
|
||||
_voxels.falseColorizeOccludedV2();
|
||||
}
|
||||
|
||||
void Application::doTrueVoxelColors() {
|
||||
_voxels.trueColorize();
|
||||
}
|
||||
|
@ -1229,20 +1240,20 @@ void Application::doTreeStats() {
|
|||
_voxels.collectStatsForTreesAndVBOs();
|
||||
}
|
||||
|
||||
void Application::setWantsLowResMoving(bool wantsLowResMoving) {
|
||||
_myAvatar.setWantLowResMoving(wantsLowResMoving);
|
||||
}
|
||||
|
||||
void Application::setWantsMonochrome(bool wantsMonochrome) {
|
||||
_myAvatar.setWantColor(!wantsMonochrome);
|
||||
}
|
||||
|
||||
void Application::setWantsResIn(bool wantsResIn) {
|
||||
_myAvatar.setWantResIn(wantsResIn);
|
||||
void Application::disableDeltaSending(bool disableDeltaSending) {
|
||||
_myAvatar.setWantDelta(!disableDeltaSending);
|
||||
}
|
||||
|
||||
void Application::setWantsDelta(bool wantsDelta) {
|
||||
_myAvatar.setWantDelta(wantsDelta);
|
||||
}
|
||||
|
||||
void Application::setWantsOcclusionCulling(bool wantsOcclusionCulling) {
|
||||
_myAvatar.setWantOcclusionCulling(wantsOcclusionCulling);
|
||||
void Application::disableOcclusionCulling(bool disableOcclusionCulling) {
|
||||
_myAvatar.setWantOcclusionCulling(!disableOcclusionCulling);
|
||||
}
|
||||
|
||||
void Application::updateVoxelModeActions() {
|
||||
|
@ -1254,11 +1265,11 @@ void Application::updateVoxelModeActions() {
|
|||
}
|
||||
}
|
||||
|
||||
void Application::sendVoxelEditMessage(PACKET_HEADER header, VoxelDetail& detail) {
|
||||
void Application::sendVoxelEditMessage(PACKET_TYPE type, VoxelDetail& detail) {
|
||||
unsigned char* bufferOut;
|
||||
int sizeOut;
|
||||
|
||||
if (createVoxelEditMessage(header, 0, 1, &detail, bufferOut, sizeOut)){
|
||||
if (createVoxelEditMessage(type, 0, 1, &detail, bufferOut, sizeOut)){
|
||||
Application::controlledBroadcastToNodes(bufferOut, sizeOut, & NODE_TYPE_VOXEL_SERVER, 1);
|
||||
delete[] bufferOut;
|
||||
}
|
||||
|
@ -1345,7 +1356,8 @@ 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 (args->bufferInUse + codeAndColorLength > MAXIMUM_EDIT_VOXEL_MESSAGE_SIZE) {
|
||||
controlledBroadcastToNodes(args->messageBuffer, args->bufferInUse, & NODE_TYPE_VOXEL_SERVER, 1);
|
||||
args->bufferInUse = sizeof(PACKET_HEADER_SET_VOXEL_DESTRUCTIVE) + sizeof(unsigned short int); // reset
|
||||
args->bufferInUse = numBytesForPacketHeader((unsigned char*) &PACKET_TYPE_SET_VOXEL_DESTRUCTIVE)
|
||||
+ sizeof(unsigned short int); // reset
|
||||
}
|
||||
|
||||
// copy this node's code color details into our buffer.
|
||||
|
@ -1412,10 +1424,12 @@ void Application::importVoxels() {
|
|||
// the server as an set voxel message, this will also rebase the voxels to the new location
|
||||
unsigned char* calculatedOctCode = NULL;
|
||||
SendVoxelsOperationArgs args;
|
||||
args.messageBuffer[0] = PACKET_HEADER_SET_VOXEL_DESTRUCTIVE;
|
||||
unsigned short int* sequenceAt = (unsigned short int*)&args.messageBuffer[sizeof(PACKET_HEADER_SET_VOXEL_DESTRUCTIVE)];
|
||||
|
||||
int numBytesPacketHeader = populateTypeAndVersion(args.messageBuffer, PACKET_TYPE_SET_VOXEL_DESTRUCTIVE);
|
||||
|
||||
unsigned short int* sequenceAt = (unsigned short int*)&args.messageBuffer[numBytesPacketHeader];
|
||||
*sequenceAt = 0;
|
||||
args.bufferInUse = sizeof(PACKET_HEADER_SET_VOXEL_DESTRUCTIVE) + sizeof(unsigned short int); // set to command + sequence
|
||||
args.bufferInUse = numBytesPacketHeader + sizeof(unsigned short int); // set to command + sequence
|
||||
|
||||
// we only need the selected voxel to get the newBaseOctCode, which we can actually calculate from the
|
||||
// voxel size/position details.
|
||||
|
@ -1428,7 +1442,7 @@ void Application::importVoxels() {
|
|||
importVoxels.recurseTreeWithOperation(sendVoxelsOperation, &args);
|
||||
|
||||
// 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 > (numBytesPacketHeader + sizeof(unsigned short int))) {
|
||||
controlledBroadcastToNodes(args.messageBuffer, args.bufferInUse, & NODE_TYPE_VOXEL_SERVER, 1);
|
||||
}
|
||||
|
||||
|
@ -1463,10 +1477,12 @@ void Application::pasteVoxels() {
|
|||
// Recurse the clipboard tree, where everything is root relative, and send all the colored voxels to
|
||||
// the server as an set voxel message, this will also rebase the voxels to the new location
|
||||
SendVoxelsOperationArgs args;
|
||||
args.messageBuffer[0] = PACKET_HEADER_SET_VOXEL_DESTRUCTIVE;
|
||||
unsigned short int* sequenceAt = (unsigned short int*)&args.messageBuffer[sizeof(PACKET_HEADER_SET_VOXEL_DESTRUCTIVE)];
|
||||
|
||||
int numBytesPacketHeader = populateTypeAndVersion(args.messageBuffer, PACKET_TYPE_SET_VOXEL_DESTRUCTIVE);
|
||||
|
||||
unsigned short int* sequenceAt = (unsigned short int*)&args.messageBuffer[numBytesPacketHeader];
|
||||
*sequenceAt = 0;
|
||||
args.bufferInUse = sizeof(PACKET_HEADER_SET_VOXEL_DESTRUCTIVE) + sizeof(unsigned short int); // set to command + sequence
|
||||
args.bufferInUse = numBytesPacketHeader + sizeof(unsigned short int); // set to command + sequence
|
||||
|
||||
// we only need the selected voxel to get the newBaseOctCode, which we can actually calculate from the
|
||||
// voxel size/position details. If we don't have an actual selectedNode then use the mouseVoxel to create a
|
||||
|
@ -1480,7 +1496,7 @@ void Application::pasteVoxels() {
|
|||
_clipboardTree.recurseTreeWithOperation(sendVoxelsOperation, &args);
|
||||
|
||||
// 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 > (numBytesPacketHeader + sizeof(unsigned short int))) {
|
||||
controlledBroadcastToNodes(args.messageBuffer, args.bufferInUse, & NODE_TYPE_VOXEL_SERVER, 1);
|
||||
}
|
||||
|
||||
|
@ -1624,13 +1640,22 @@ void Application::initMenu() {
|
|||
renderDebugMenu->addAction("FALSE Color Voxels by Distance", this, SLOT(doFalseColorizeByDistance()));
|
||||
renderDebugMenu->addAction("FALSE Color Voxel Out of View", this, SLOT(doFalseColorizeInView()));
|
||||
renderDebugMenu->addAction("FALSE Color Occluded Voxels", this, SLOT(doFalseColorizeOccluded()), Qt::CTRL | Qt::Key_O);
|
||||
renderDebugMenu->addAction("FALSE Color Occluded V2 Voxels", this, SLOT(doFalseColorizeOccludedV2()), Qt::CTRL | Qt::Key_P);
|
||||
renderDebugMenu->addAction("Show TRUE Colors", this, SLOT(doTrueVoxelColors()), Qt::CTRL | Qt::Key_T);
|
||||
|
||||
debugMenu->addAction("Wants Res-In", this, SLOT(setWantsResIn(bool)))->setCheckable(true);
|
||||
debugMenu->addAction("Wants Monochrome", this, SLOT(setWantsMonochrome(bool)))->setCheckable(true);
|
||||
debugMenu->addAction("Wants View Delta Sending", this, SLOT(setWantsDelta(bool)))->setCheckable(true);
|
||||
(_shouldLowPassFilter = debugMenu->addAction("Test: LowPass filter"))->setCheckable(true);
|
||||
debugMenu->addAction("Wants Occlusion Culling", this, SLOT(setWantsOcclusionCulling(bool)))->setCheckable(true);
|
||||
|
||||
debugMenu->addAction("Wants Monochrome", this, SLOT(setWantsMonochrome(bool)))->setCheckable(true);
|
||||
debugMenu->addAction("Use Lower Resolution While Moving", this, SLOT(setWantsLowResMoving(bool)))->setCheckable(true);
|
||||
debugMenu->addAction("Disable Delta Sending", this, SLOT(disableDeltaSending(bool)))->setCheckable(true);
|
||||
debugMenu->addAction("Disable Occlusion Culling", this, SLOT(disableOcclusionCulling(bool)),
|
||||
Qt::SHIFT | Qt::Key_C)->setCheckable(true);
|
||||
|
||||
(_renderCoverageMap = debugMenu->addAction("Render Coverage Map"))->setCheckable(true);
|
||||
_renderCoverageMap->setShortcut(Qt::SHIFT | Qt::CTRL | Qt::Key_O);
|
||||
(_renderCoverageMapV2 = debugMenu->addAction("Render Coverage Map V2"))->setCheckable(true);
|
||||
_renderCoverageMapV2->setShortcut(Qt::SHIFT | Qt::CTRL | Qt::Key_P);
|
||||
|
||||
|
||||
QMenu* settingsMenu = menuBar->addMenu("Settings");
|
||||
(_settingsAutosave = settingsMenu->addAction("Autosave"))->setCheckable(true);
|
||||
|
@ -1691,8 +1716,6 @@ void Application::init() {
|
|||
_headMouseX = _mouseX = _glWidget->width() / 2;
|
||||
_headMouseY = _mouseY = _glWidget->height() / 2;
|
||||
|
||||
_stars.readInput(STAR_FILE, STAR_CACHE_FILE, 0);
|
||||
|
||||
_myAvatar.init();
|
||||
_myAvatar.setPosition(START_LOCATION);
|
||||
_myCamera.setMode(CAMERA_MODE_FIRST_PERSON);
|
||||
|
@ -1718,7 +1741,6 @@ void Application::init() {
|
|||
|
||||
printLog("Loaded settings.\n");
|
||||
|
||||
|
||||
sendAvatarVoxelURLMessage(_myAvatar.getVoxels()->getVoxelURL());
|
||||
|
||||
_palette.init(_glWidget->width(), _glWidget->height());
|
||||
|
@ -1732,6 +1754,22 @@ void Application::init() {
|
|||
|
||||
const float MAX_AVATAR_EDIT_VELOCITY = 1.0f;
|
||||
const float MAX_VOXEL_EDIT_DISTANCE = 20.0f;
|
||||
const float HEAD_SPHERE_RADIUS = 0.07;
|
||||
|
||||
bool Application::isLookingAtOtherAvatar(glm::vec3& mouseRayOrigin, glm::vec3& mouseRayDirection, glm::vec3& eyePosition) {
|
||||
NodeList* nodeList = NodeList::getInstance();
|
||||
for (NodeList::iterator node = nodeList->begin(); node != nodeList->end(); node++) {
|
||||
if (node->getLinkedData() != NULL && node->getType() == NODE_TYPE_AGENT) {
|
||||
Avatar* avatar = (Avatar *) node->getLinkedData();
|
||||
glm::vec3 headPosition = avatar->getHead().getPosition();
|
||||
if (rayIntersectsSphere(mouseRayOrigin, mouseRayDirection, headPosition, HEAD_SPHERE_RADIUS)) {
|
||||
eyePosition = avatar->getHead().getEyeLevelPosition();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void Application::update(float deltaTime) {
|
||||
// Use Transmitter Hand to move hand if connected, else use mouse
|
||||
|
@ -1759,8 +1797,15 @@ void Application::update(float deltaTime) {
|
|||
_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);
|
||||
glm::vec3 eyePosition;
|
||||
if (isLookingAtOtherAvatar(mouseRayOrigin, mouseRayDirection, eyePosition)) {
|
||||
// If the mouse is over another avatar's head...
|
||||
glm::vec3 myLookAtFromMouse(eyePosition);
|
||||
_myAvatar.getHead().setLookAtPosition(myLookAtFromMouse);
|
||||
} else {
|
||||
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 = 0.0f;
|
||||
|
@ -1861,12 +1906,9 @@ void Application::update(float deltaTime) {
|
|||
if (_isTouchPressed) {
|
||||
float TOUCH_YAW_SCALE = -50.0f;
|
||||
float TOUCH_PITCH_SCALE = -50.0f;
|
||||
_myAvatar.getHead().addYaw((_touchAvgX - _lastTouchAvgX)
|
||||
* TOUCH_YAW_SCALE
|
||||
* deltaTime);
|
||||
_myAvatar.getHead().addPitch((_touchAvgY - _lastTouchAvgY)
|
||||
* TOUCH_PITCH_SCALE
|
||||
* deltaTime);
|
||||
_yawFromTouch += ((_touchAvgX - _lastTouchAvgX) * TOUCH_YAW_SCALE * deltaTime);
|
||||
_pitchFromTouch += ((_touchAvgY - _lastTouchAvgY) * TOUCH_PITCH_SCALE * deltaTime);
|
||||
|
||||
_lastTouchAvgX = _touchAvgX;
|
||||
_lastTouchAvgY = _touchAvgY;
|
||||
}
|
||||
|
@ -1974,11 +2016,20 @@ void Application::update(float deltaTime) {
|
|||
|
||||
void Application::updateAvatar(float deltaTime) {
|
||||
|
||||
// When head is rotated via touch/mouse look, slowly turn body to follow
|
||||
const float BODY_FOLLOW_HEAD_RATE = 0.5f;
|
||||
// update body yaw by body yaw delta
|
||||
_myAvatar.setOrientation(_myAvatar.getOrientation()
|
||||
* glm::quat(glm::vec3(0, _yawFromTouch * deltaTime * BODY_FOLLOW_HEAD_RATE, 0) * deltaTime));
|
||||
_yawFromTouch -= _yawFromTouch * deltaTime * BODY_FOLLOW_HEAD_RATE;
|
||||
|
||||
// Update my avatar's state from gyros and/or webcam
|
||||
_myAvatar.updateFromGyrosAndOrWebcam(_gyroLook->isChecked(),
|
||||
glm::vec3(_headCameraPitchYawScale,
|
||||
_headCameraPitchYawScale,
|
||||
_headCameraPitchYawScale));
|
||||
_headCameraPitchYawScale),
|
||||
_yawFromTouch,
|
||||
_pitchFromTouch);
|
||||
|
||||
if (_serialHeadSensor.isActive()) {
|
||||
|
||||
|
@ -2000,14 +2051,26 @@ void Application::updateAvatar(float deltaTime) {
|
|||
_headMouseY = max(_headMouseY, 0);
|
||||
_headMouseY = min(_headMouseY, _glWidget->height());
|
||||
|
||||
const float MIDPOINT_OF_SCREEN = 0.5;
|
||||
|
||||
// Set lookAtPosition if an avatar is at the center of the screen
|
||||
glm::vec3 screenCenterRayOrigin, screenCenterRayDirection;
|
||||
_viewFrustum.computePickRay(MIDPOINT_OF_SCREEN, MIDPOINT_OF_SCREEN, screenCenterRayOrigin, screenCenterRayDirection);
|
||||
|
||||
glm::vec3 eyePosition;
|
||||
if (isLookingAtOtherAvatar(screenCenterRayOrigin, screenCenterRayDirection, eyePosition)) {
|
||||
glm::vec3 myLookAtFromMouse(eyePosition);
|
||||
_myAvatar.getHead().setLookAtPosition(myLookAtFromMouse);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (OculusManager::isConnected()) {
|
||||
float yaw, pitch, roll;
|
||||
OculusManager::getEulerAngles(yaw, pitch, roll);
|
||||
|
||||
_myAvatar.getHead().setYaw(yaw);
|
||||
_myAvatar.getHead().setPitch(pitch);
|
||||
_myAvatar.getHead().setYaw(yaw + _yawFromTouch);
|
||||
_myAvatar.getHead().setPitch(pitch + _pitchFromTouch);
|
||||
_myAvatar.getHead().setRoll(roll);
|
||||
}
|
||||
|
||||
|
@ -2036,7 +2099,8 @@ void Application::updateAvatar(float deltaTime) {
|
|||
unsigned char broadcastString[200];
|
||||
unsigned char* endOfBroadcastStringWrite = broadcastString;
|
||||
|
||||
*(endOfBroadcastStringWrite++) = PACKET_HEADER_HEAD_DATA;
|
||||
endOfBroadcastStringWrite += populateTypeAndVersion(endOfBroadcastStringWrite, PACKET_TYPE_HEAD_DATA);
|
||||
|
||||
endOfBroadcastStringWrite += packNodeId(endOfBroadcastStringWrite, nodeList->getOwnerID());
|
||||
|
||||
endOfBroadcastStringWrite += _myAvatar.getBroadcastData(endOfBroadcastStringWrite);
|
||||
|
@ -2066,8 +2130,8 @@ void Application::updateAvatar(float deltaTime) {
|
|||
_paintingVoxel.y >= 0.0 && _paintingVoxel.y <= 1.0 &&
|
||||
_paintingVoxel.z >= 0.0 && _paintingVoxel.z <= 1.0) {
|
||||
|
||||
PACKET_HEADER message = (_destructiveAddVoxel->isChecked() ?
|
||||
PACKET_HEADER_SET_VOXEL_DESTRUCTIVE : PACKET_HEADER_SET_VOXEL);
|
||||
PACKET_TYPE message = (_destructiveAddVoxel->isChecked() ?
|
||||
PACKET_TYPE_SET_VOXEL_DESTRUCTIVE : PACKET_TYPE_SET_VOXEL);
|
||||
sendVoxelEditMessage(message, _paintingVoxel);
|
||||
}
|
||||
}
|
||||
|
@ -2296,6 +2360,9 @@ void Application::displaySide(Camera& whichCamera) {
|
|||
glMateriali(GL_FRONT, GL_SHININESS, 96);
|
||||
|
||||
if (_renderStarsOn->isChecked()) {
|
||||
if (!_stars.getFileLoaded()) {
|
||||
_stars.readInput(STAR_FILE, STAR_CACHE_FILE, 0);
|
||||
}
|
||||
// should be the first rendering pass - w/o depth buffer / lighting
|
||||
|
||||
// compute starfield alpha based on distance from atmosphere
|
||||
|
@ -2398,6 +2465,7 @@ void Application::displaySide(Camera& whichCamera) {
|
|||
|
||||
// brad's frustum for debugging
|
||||
if (_frustumOn->isChecked()) renderViewFrustum(_viewFrustum);
|
||||
|
||||
}
|
||||
|
||||
void Application::displayOverlay() {
|
||||
|
@ -2446,6 +2514,9 @@ void Application::displayOverlay() {
|
|||
|
||||
if (_renderStatsOn->isChecked()) { displayStats(); }
|
||||
|
||||
// testing rendering coverage map
|
||||
if (_renderCoverageMapV2->isChecked()) { renderCoverageMapV2(); }
|
||||
if (_renderCoverageMap->isChecked()) { renderCoverageMap(); }
|
||||
if (_bandwidthDisplayOn->isChecked()) { _bandwidthMeter.render(_glWidget->width(), _glWidget->height()); }
|
||||
|
||||
if (_logOn->isChecked()) { LogDisplay::instance.render(_glWidget->width(), _glWidget->height()); }
|
||||
|
@ -2458,7 +2529,7 @@ void Application::displayOverlay() {
|
|||
// Show on-screen msec timer
|
||||
if (_renderFrameTimerOn->isChecked()) {
|
||||
char frameTimer[10];
|
||||
long long mSecsNow = floor(usecTimestampNow() / 1000.0 + 0.5);
|
||||
uint64_t mSecsNow = floor(usecTimestampNow() / 1000.0 + 0.5);
|
||||
sprintf(frameTimer, "%d\n", (int)(mSecsNow % 1000));
|
||||
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);
|
||||
|
@ -2631,8 +2702,8 @@ void Application::renderThrustAtVoxel(const glm::vec3& thrust) {
|
|||
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) {
|
||||
|
@ -2647,6 +2718,149 @@ void Application::renderLineToTouchedVoxel() {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
glm::vec2 Application::getScaledScreenPoint(glm::vec2 projectedPoint) {
|
||||
float horizontalScale = _glWidget->width() / 2.0f;
|
||||
float verticalScale = _glWidget->height() / 2.0f;
|
||||
|
||||
// -1,-1 is 0,windowHeight
|
||||
// 1,1 is windowWidth,0
|
||||
|
||||
// -1,1 1,1
|
||||
// +-----------------------+
|
||||
// | | |
|
||||
// | | |
|
||||
// | -1,0 | |
|
||||
// |-----------+-----------|
|
||||
// | 0,0 |
|
||||
// | | |
|
||||
// | | |
|
||||
// | | |
|
||||
// +-----------------------+
|
||||
// -1,-1 1,-1
|
||||
|
||||
glm::vec2 screenPoint((projectedPoint.x + 1.0) * horizontalScale,
|
||||
((projectedPoint.y + 1.0) * -verticalScale) + _glWidget->height());
|
||||
|
||||
return screenPoint;
|
||||
}
|
||||
|
||||
// render the coverage map on screen
|
||||
void Application::renderCoverageMapV2() {
|
||||
|
||||
//printLog("renderCoverageMap()\n");
|
||||
|
||||
glDisable(GL_LIGHTING);
|
||||
glLineWidth(2.0);
|
||||
glBegin(GL_LINES);
|
||||
glColor3f(0,1,1);
|
||||
|
||||
renderCoverageMapsV2Recursively(&_voxels.myCoverageMapV2);
|
||||
|
||||
glEnd();
|
||||
glEnable(GL_LIGHTING);
|
||||
}
|
||||
|
||||
void Application::renderCoverageMapsV2Recursively(CoverageMapV2* map) {
|
||||
// render ourselves...
|
||||
if (map->isCovered()) {
|
||||
BoundingBox box = map->getBoundingBox();
|
||||
|
||||
glm::vec2 firstPoint = getScaledScreenPoint(box.getVertex(0));
|
||||
glm::vec2 lastPoint(firstPoint);
|
||||
|
||||
for (int i = 1; i < box.getVertexCount(); i++) {
|
||||
glm::vec2 thisPoint = getScaledScreenPoint(box.getVertex(i));
|
||||
|
||||
glVertex2f(lastPoint.x, lastPoint.y);
|
||||
glVertex2f(thisPoint.x, thisPoint.y);
|
||||
lastPoint = thisPoint;
|
||||
}
|
||||
|
||||
glVertex2f(lastPoint.x, lastPoint.y);
|
||||
glVertex2f(firstPoint.x, firstPoint.y);
|
||||
} else {
|
||||
// iterate our children and call render on them.
|
||||
for (int i = 0; i < CoverageMapV2::NUMBER_OF_CHILDREN; i++) {
|
||||
CoverageMapV2* childMap = map->getChild(i);
|
||||
if (childMap) {
|
||||
renderCoverageMapsV2Recursively(childMap);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// render the coverage map on screen
|
||||
void Application::renderCoverageMap() {
|
||||
|
||||
//printLog("renderCoverageMap()\n");
|
||||
|
||||
glDisable(GL_LIGHTING);
|
||||
glLineWidth(2.0);
|
||||
glBegin(GL_LINES);
|
||||
glColor3f(0,0,1);
|
||||
|
||||
renderCoverageMapsRecursively(&_voxels.myCoverageMap);
|
||||
|
||||
glEnd();
|
||||
glEnable(GL_LIGHTING);
|
||||
}
|
||||
|
||||
void Application::renderCoverageMapsRecursively(CoverageMap* map) {
|
||||
for (int i = 0; i < map->getPolygonCount(); i++) {
|
||||
|
||||
VoxelProjectedPolygon* polygon = map->getPolygon(i);
|
||||
|
||||
if (polygon->getProjectionType() == (PROJECTION_RIGHT | PROJECTION_NEAR | PROJECTION_BOTTOM)) {
|
||||
glColor3f(.5,0,0); // dark red
|
||||
} else if (polygon->getProjectionType() == (PROJECTION_NEAR | PROJECTION_RIGHT)) {
|
||||
glColor3f(.5,.5,0); // dark yellow
|
||||
} else if (polygon->getProjectionType() == (PROJECTION_NEAR | PROJECTION_LEFT)) {
|
||||
glColor3f(.5,.5,.5); // gray
|
||||
} else if (polygon->getProjectionType() == (PROJECTION_NEAR | PROJECTION_LEFT | PROJECTION_BOTTOM)) {
|
||||
glColor3f(.5,0,.5); // dark magenta
|
||||
} else if (polygon->getProjectionType() == (PROJECTION_NEAR | PROJECTION_BOTTOM)) {
|
||||
glColor3f(.75,0,0); // red
|
||||
} else if (polygon->getProjectionType() == (PROJECTION_NEAR | PROJECTION_TOP)) {
|
||||
glColor3f(1,0,1); // magenta
|
||||
} else if (polygon->getProjectionType() == (PROJECTION_NEAR | PROJECTION_LEFT | PROJECTION_TOP)) {
|
||||
glColor3f(0,0,1); // Blue
|
||||
} else if (polygon->getProjectionType() == (PROJECTION_NEAR | PROJECTION_RIGHT | PROJECTION_TOP)) {
|
||||
glColor3f(0,1,0); // green
|
||||
} else if (polygon->getProjectionType() == (PROJECTION_NEAR)) {
|
||||
glColor3f(1,1,0); // yellow
|
||||
} else if (polygon->getProjectionType() == (PROJECTION_FAR | PROJECTION_RIGHT | PROJECTION_BOTTOM)) {
|
||||
glColor3f(0,.5,.5); // dark cyan
|
||||
} else {
|
||||
glColor3f(1,0,0);
|
||||
}
|
||||
|
||||
glm::vec2 firstPoint = getScaledScreenPoint(polygon->getVertex(0));
|
||||
glm::vec2 lastPoint(firstPoint);
|
||||
|
||||
for (int i = 1; i < polygon->getVertexCount(); i++) {
|
||||
glm::vec2 thisPoint = getScaledScreenPoint(polygon->getVertex(i));
|
||||
|
||||
glVertex2f(lastPoint.x, lastPoint.y);
|
||||
glVertex2f(thisPoint.x, thisPoint.y);
|
||||
lastPoint = thisPoint;
|
||||
}
|
||||
|
||||
glVertex2f(lastPoint.x, lastPoint.y);
|
||||
glVertex2f(firstPoint.x, firstPoint.y);
|
||||
}
|
||||
|
||||
// iterate our children and call render on them.
|
||||
for (int i = 0; i < CoverageMapV2::NUMBER_OF_CHILDREN; i++) {
|
||||
CoverageMap* childMap = map->getChild(i);
|
||||
if (childMap) {
|
||||
renderCoverageMapsRecursively(childMap);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////
|
||||
// renderViewFrustum()
|
||||
//
|
||||
|
@ -2803,8 +3017,8 @@ void Application::shiftPaintingColor() {
|
|||
void Application::maybeEditVoxelUnderCursor() {
|
||||
if (_addVoxelMode->isChecked() || _colorVoxelMode->isChecked()) {
|
||||
if (_mouseVoxel.s != 0) {
|
||||
PACKET_HEADER message = (_destructiveAddVoxel->isChecked() ?
|
||||
PACKET_HEADER_SET_VOXEL_DESTRUCTIVE : PACKET_HEADER_SET_VOXEL);
|
||||
PACKET_TYPE message = (_destructiveAddVoxel->isChecked() ?
|
||||
PACKET_TYPE_SET_VOXEL_DESTRUCTIVE : PACKET_TYPE_SET_VOXEL);
|
||||
sendVoxelEditMessage(message, _mouseVoxel);
|
||||
|
||||
// create the voxel locally so it appears immediately
|
||||
|
@ -2877,7 +3091,7 @@ void Application::maybeEditVoxelUnderCursor() {
|
|||
void Application::deleteVoxelUnderCursor() {
|
||||
if (_mouseVoxel.s != 0) {
|
||||
// sending delete to the server is sufficient, server will send new version so we see updates soon enough
|
||||
sendVoxelEditMessage(PACKET_HEADER_ERASE_VOXEL, _mouseVoxel);
|
||||
sendVoxelEditMessage(PACKET_TYPE_ERASE_VOXEL, _mouseVoxel);
|
||||
AudioInjector* voxelInjector = AudioInjectionManager::injectorWithCapacity(5000);
|
||||
voxelInjector->setPosition(glm::vec3(_mouseVoxel.x, _mouseVoxel.y, _mouseVoxel.z));
|
||||
// voxelInjector->setBearing(0); //straight down the z axis
|
||||
|
@ -2987,36 +3201,39 @@ void* Application::networkReceive(void* args) {
|
|||
app->_packetCount++;
|
||||
app->_bytesCount += bytesReceived;
|
||||
|
||||
switch (app->_incomingPacket[0]) {
|
||||
case PACKET_HEADER_TRANSMITTER_DATA_V2:
|
||||
// V2 = IOS transmitter app
|
||||
app->_myTransmitter.processIncomingData(app->_incomingPacket, bytesReceived);
|
||||
|
||||
break;
|
||||
case PACKET_HEADER_MIXED_AUDIO:
|
||||
app->_audio.addReceivedAudioToBuffer(app->_incomingPacket, bytesReceived);
|
||||
break;
|
||||
case PACKET_HEADER_VOXEL_DATA:
|
||||
case PACKET_HEADER_VOXEL_DATA_MONOCHROME:
|
||||
case PACKET_HEADER_Z_COMMAND:
|
||||
case PACKET_HEADER_ERASE_VOXEL:
|
||||
app->_voxels.parseData(app->_incomingPacket, bytesReceived);
|
||||
break;
|
||||
case PACKET_HEADER_ENVIRONMENT_DATA:
|
||||
app->_environment.parseData(&senderAddress, app->_incomingPacket, bytesReceived);
|
||||
break;
|
||||
case PACKET_HEADER_BULK_AVATAR_DATA:
|
||||
NodeList::getInstance()->processBulkNodeData(&senderAddress,
|
||||
app->_incomingPacket,
|
||||
bytesReceived);
|
||||
getInstance()->_bandwidthMeter.inputStream(BandwidthMeter::AVATARS).updateValue(bytesReceived);
|
||||
break;
|
||||
case PACKET_HEADER_AVATAR_VOXEL_URL:
|
||||
processAvatarVoxelURLMessage(app->_incomingPacket, bytesReceived);
|
||||
break;
|
||||
default:
|
||||
NodeList::getInstance()->processNodeData(&senderAddress, app->_incomingPacket, bytesReceived);
|
||||
break;
|
||||
if (packetVersionMatch(app->_incomingPacket)) {
|
||||
// only process this packet if we have a match on the packet version
|
||||
switch (app->_incomingPacket[0]) {
|
||||
case PACKET_TYPE_TRANSMITTER_DATA_V2:
|
||||
// V2 = IOS transmitter app
|
||||
app->_myTransmitter.processIncomingData(app->_incomingPacket, bytesReceived);
|
||||
|
||||
break;
|
||||
case PACKET_TYPE_MIXED_AUDIO:
|
||||
app->_audio.addReceivedAudioToBuffer(app->_incomingPacket, bytesReceived);
|
||||
break;
|
||||
case PACKET_TYPE_VOXEL_DATA:
|
||||
case PACKET_TYPE_VOXEL_DATA_MONOCHROME:
|
||||
case PACKET_TYPE_Z_COMMAND:
|
||||
case PACKET_TYPE_ERASE_VOXEL:
|
||||
app->_voxels.parseData(app->_incomingPacket, bytesReceived);
|
||||
break;
|
||||
case PACKET_TYPE_ENVIRONMENT_DATA:
|
||||
app->_environment.parseData(&senderAddress, app->_incomingPacket, bytesReceived);
|
||||
break;
|
||||
case PACKET_TYPE_BULK_AVATAR_DATA:
|
||||
NodeList::getInstance()->processBulkNodeData(&senderAddress,
|
||||
app->_incomingPacket,
|
||||
bytesReceived);
|
||||
getInstance()->_bandwidthMeter.inputStream(BandwidthMeter::AVATARS).updateValue(bytesReceived);
|
||||
break;
|
||||
case PACKET_TYPE_AVATAR_VOXEL_URL:
|
||||
processAvatarVoxelURLMessage(app->_incomingPacket, bytesReceived);
|
||||
break;
|
||||
default:
|
||||
NodeList::getInstance()->processNodeData(&senderAddress, app->_incomingPacket, bytesReceived);
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else if (!app->_enableNetworkThread) {
|
||||
break;
|
||||
|
|
|
@ -132,13 +132,14 @@ private slots:
|
|||
void doFalseRandomizeEveryOtherVoxelColors();
|
||||
void doFalseColorizeByDistance();
|
||||
void doFalseColorizeOccluded();
|
||||
void doFalseColorizeOccludedV2();
|
||||
void doFalseColorizeInView();
|
||||
void doTrueVoxelColors();
|
||||
void doTreeStats();
|
||||
void setWantsMonochrome(bool wantsMonochrome);
|
||||
void setWantsResIn(bool wantsResIn);
|
||||
void setWantsDelta(bool wantsDelta);
|
||||
void setWantsOcclusionCulling(bool wantsOcclusionCulling);
|
||||
void setWantsLowResMoving(bool wantsLowResMoving);
|
||||
void disableDeltaSending(bool disableDeltaSending);
|
||||
void disableOcclusionCulling(bool disableOcclusionCulling);
|
||||
void updateVoxelModeActions();
|
||||
void decreaseVoxelSize();
|
||||
void increaseVoxelSize();
|
||||
|
@ -154,6 +155,14 @@ private slots:
|
|||
void copyVoxels();
|
||||
void pasteVoxels();
|
||||
void runTests();
|
||||
|
||||
void renderCoverageMap();
|
||||
void renderCoverageMapsRecursively(CoverageMap* map);
|
||||
|
||||
void renderCoverageMapV2();
|
||||
void renderCoverageMapsV2Recursively(CoverageMapV2* map);
|
||||
|
||||
glm::vec2 getScaledScreenPoint(glm::vec2 projectedPoint);
|
||||
void goHome();
|
||||
|
||||
private:
|
||||
|
@ -163,7 +172,7 @@ private:
|
|||
|
||||
static void sendVoxelServerAddScene();
|
||||
static bool sendVoxelsOperation(VoxelNode* node, void* extraData);
|
||||
static void sendVoxelEditMessage(PACKET_HEADER header, VoxelDetail& detail);
|
||||
static void sendVoxelEditMessage(PACKET_TYPE type, VoxelDetail& detail);
|
||||
static void sendAvatarVoxelURLMessage(const QUrl& url);
|
||||
static void processAvatarVoxelURLMessage(unsigned char *packetData, size_t dataBytes);
|
||||
static void sendPingPackets();
|
||||
|
@ -174,6 +183,7 @@ private:
|
|||
void init();
|
||||
|
||||
void update(float deltaTime);
|
||||
bool isLookingAtOtherAvatar(glm::vec3& mouseRayOrigin, glm::vec3& mouseRayDirection, glm::vec3& eyePosition);
|
||||
void updateAvatar(float deltaTime);
|
||||
void loadViewFrustum(Camera& camera, ViewFrustum& viewFrustum);
|
||||
|
||||
|
@ -247,6 +257,9 @@ private:
|
|||
QAction* _fullScreenMode; // whether we are in full screen mode
|
||||
QAction* _frustumRenderModeAction;
|
||||
QAction* _settingsAutosave; // Whether settings are saved automatically
|
||||
|
||||
QAction* _renderCoverageMapV2;
|
||||
QAction* _renderCoverageMap;
|
||||
|
||||
BandwidthMeter _bandwidthMeter;
|
||||
BandwidthDialog* _bandwidthDialog;
|
||||
|
@ -320,6 +333,8 @@ private:
|
|||
float _touchDragStartedAvgX;
|
||||
float _touchDragStartedAvgY;
|
||||
bool _isTouchPressed; // true if multitouch has been pressed (clear when finished)
|
||||
float _yawFromTouch;
|
||||
float _pitchFromTouch;
|
||||
|
||||
VoxelDetail _mouseVoxelDragging;
|
||||
glm::vec3 _voxelThrust;
|
||||
|
|
|
@ -99,16 +99,18 @@ inline void Audio::performIO(int16_t* inputLeft, int16_t* outputLeft, int16_t* o
|
|||
glm::vec3 headPosition = interfaceAvatar->getHeadJointPosition();
|
||||
glm::quat headOrientation = interfaceAvatar->getHead().getOrientation();
|
||||
|
||||
int leadingBytes = sizeof(PACKET_HEADER_MICROPHONE_AUDIO_NO_ECHO) + sizeof(headPosition) + sizeof(headOrientation);
|
||||
int numBytesPacketHeader = numBytesForPacketHeader((unsigned char*) &PACKET_TYPE_MICROPHONE_AUDIO_NO_ECHO);
|
||||
int leadingBytes = numBytesPacketHeader + sizeof(headPosition) + sizeof(headOrientation);
|
||||
|
||||
// we need the amount of bytes in the buffer + 1 for type
|
||||
// + 12 for 3 floats for position + float for bearing + 1 attenuation byte
|
||||
unsigned char dataPacket[BUFFER_LENGTH_BYTES_PER_CHANNEL + leadingBytes];
|
||||
|
||||
dataPacket[0] = (Application::getInstance()->shouldEchoAudio())
|
||||
? PACKET_HEADER_MICROPHONE_AUDIO_WITH_ECHO
|
||||
: PACKET_HEADER_MICROPHONE_AUDIO_NO_ECHO;
|
||||
unsigned char *currentPacketPtr = dataPacket + 1;
|
||||
PACKET_TYPE packetType = (Application::getInstance()->shouldEchoAudio())
|
||||
? PACKET_TYPE_MICROPHONE_AUDIO_WITH_ECHO
|
||||
: PACKET_TYPE_MICROPHONE_AUDIO_NO_ECHO;
|
||||
|
||||
unsigned char* currentPacketPtr = dataPacket + populateTypeAndVersion(dataPacket, packetType);
|
||||
|
||||
// memcpy the three float positions
|
||||
memcpy(currentPacketPtr, &headPosition, sizeof(headPosition));
|
||||
|
@ -446,10 +448,10 @@ void Audio::addReceivedAudioToBuffer(unsigned char* receivedData, int receivedBy
|
|||
|
||||
//printf("Got audio packet %d\n", _packetsReceivedThisPlayback);
|
||||
|
||||
_ringBuffer.parseData((unsigned char*) receivedData, PACKET_LENGTH_BYTES + sizeof(PACKET_HEADER));
|
||||
_ringBuffer.parseData((unsigned char*) receivedData, receivedBytes);
|
||||
|
||||
Application::getInstance()->getBandwidthMeter()->inputStream(BandwidthMeter::AUDIO)
|
||||
.updateValue(PACKET_LENGTH_BYTES + sizeof(PACKET_HEADER));
|
||||
.updateValue(PACKET_LENGTH_BYTES + sizeof(PACKET_TYPE));
|
||||
|
||||
_lastReceiveTime = currentReceiveTime;
|
||||
}
|
||||
|
|
|
@ -16,12 +16,14 @@
|
|||
#include "Hand.h"
|
||||
#include "Head.h"
|
||||
#include "Log.h"
|
||||
#include "Physics.h"
|
||||
#include "ui/TextRenderer.h"
|
||||
#include <NodeList.h>
|
||||
#include <NodeTypes.h>
|
||||
#include <PacketHeaders.h>
|
||||
#include <OculusManager.h>
|
||||
|
||||
|
||||
using namespace std;
|
||||
|
||||
const bool BALLS_ON = false;
|
||||
|
@ -285,7 +287,10 @@ void Avatar::reset() {
|
|||
}
|
||||
|
||||
// Update avatar head rotation with sensor data
|
||||
void Avatar::updateFromGyrosAndOrWebcam(bool gyroLook, const glm::vec3& amplifyAngle) {
|
||||
void Avatar::updateFromGyrosAndOrWebcam(bool gyroLook,
|
||||
const glm::vec3& amplifyAngle,
|
||||
float yawFromTouch,
|
||||
float pitchFromTouch) {
|
||||
SerialInterface* gyros = Application::getInstance()->getSerialHeadSensor();
|
||||
Webcam* webcam = Application::getInstance()->getWebcam();
|
||||
glm::vec3 estimatedPosition, estimatedRotation;
|
||||
|
@ -296,6 +301,8 @@ void Avatar::updateFromGyrosAndOrWebcam(bool gyroLook, const glm::vec3& amplifyA
|
|||
estimatedRotation = webcam->getEstimatedRotation();
|
||||
|
||||
} else {
|
||||
_head.setPitch(pitchFromTouch);
|
||||
_head.setYaw(yawFromTouch);
|
||||
return;
|
||||
}
|
||||
if (webcam->isActive()) {
|
||||
|
@ -316,8 +323,8 @@ void Avatar::updateFromGyrosAndOrWebcam(bool gyroLook, const glm::vec3& amplifyA
|
|||
}
|
||||
}
|
||||
}
|
||||
_head.setPitch(estimatedRotation.x * amplifyAngle.x);
|
||||
_head.setYaw(estimatedRotation.y * amplifyAngle.y);
|
||||
_head.setPitch(estimatedRotation.x * amplifyAngle.x + pitchFromTouch);
|
||||
_head.setYaw(estimatedRotation.y * amplifyAngle.y + yawFromTouch);
|
||||
_head.setRoll(estimatedRotation.z * amplifyAngle.z);
|
||||
_head.setCameraFollowsHead(gyroLook);
|
||||
|
||||
|
@ -356,16 +363,16 @@ void Avatar::updateThrust(float deltaTime, Transmitter * transmitter) {
|
|||
//
|
||||
// Gather thrust information from keyboard and sensors to apply to avatar motion
|
||||
//
|
||||
glm::quat orientation = getOrientation();
|
||||
glm::quat orientation = getHead().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_MAG_DOWN = 300.f;
|
||||
const float THRUST_MAG_FWD = 500.f;
|
||||
const float THRUST_MAG_BACK = 300.f;
|
||||
const float THRUST_MAG_LATERAL = 250.f;
|
||||
const float THRUST_JUMP = 120.f;
|
||||
|
||||
// Add Thrusts from keyboard
|
||||
|
@ -420,7 +427,7 @@ void Avatar::simulate(float deltaTime, Transmitter* transmitter) {
|
|||
glm::quat orientation = getOrientation();
|
||||
glm::vec3 front = orientation * IDENTITY_FRONT;
|
||||
glm::vec3 right = orientation * IDENTITY_RIGHT;
|
||||
|
||||
|
||||
// Update movement timers
|
||||
if (isMyAvatar()) {
|
||||
_elapsedTimeSinceCollision += deltaTime;
|
||||
|
@ -443,9 +450,6 @@ void Avatar::simulate(float deltaTime, Transmitter* transmitter) {
|
|||
glm::vec3 oldVelocity = getVelocity();
|
||||
|
||||
if (isMyAvatar()) {
|
||||
// update position by velocity
|
||||
_position += _velocity * deltaTime;
|
||||
|
||||
// calculate speed
|
||||
_speed = glm::length(_velocity);
|
||||
}
|
||||
|
@ -480,7 +484,7 @@ void Avatar::simulate(float deltaTime, Transmitter* transmitter) {
|
|||
enableHandMovement &= (it->jointID != AVATAR_JOINT_RIGHT_WRIST);
|
||||
}
|
||||
|
||||
// update avatar skeleton
|
||||
// update avatar skeleton
|
||||
_skeleton.update(deltaTime, getOrientation(), _position);
|
||||
|
||||
//determine the lengths of the body springs now that we have updated the skeleton at least once
|
||||
|
@ -501,51 +505,49 @@ void Avatar::simulate(float deltaTime, Transmitter* transmitter) {
|
|||
_ballSpringsInitialized = true;
|
||||
}
|
||||
|
||||
|
||||
// if this is not my avatar, then hand position comes from transmitted data
|
||||
if (!isMyAvatar()) {
|
||||
_skeleton.joint[ AVATAR_JOINT_RIGHT_FINGERTIPS ].position = _handPosition;
|
||||
}
|
||||
|
||||
//detect and respond to collisions with other avatars...
|
||||
if (isMyAvatar()) {
|
||||
updateAvatarCollisions(deltaTime);
|
||||
}
|
||||
|
||||
|
||||
//update the movement of the hand and process handshaking with other avatars...
|
||||
updateHandMovementAndTouching(deltaTime, enableHandMovement);
|
||||
_avatarTouch.simulate(deltaTime);
|
||||
|
||||
// apply gravity and collision with the ground/floor
|
||||
if (isMyAvatar() && USING_AVATAR_GRAVITY) {
|
||||
_velocity += _gravity * (GRAVITY_EARTH * deltaTime);
|
||||
}
|
||||
if (isMyAvatar()) {
|
||||
|
||||
// apply gravity
|
||||
if (USING_AVATAR_GRAVITY) {
|
||||
// For gravity, always move the avatar by the amount driven by gravity, so that the collision
|
||||
// routines will detect it and collide every frame when pulled by gravity to a surface
|
||||
//
|
||||
_velocity += _gravity * (GRAVITY_EARTH * deltaTime);
|
||||
_position += _gravity * (GRAVITY_EARTH * deltaTime) * deltaTime;
|
||||
}
|
||||
|
||||
updateCollisionWithEnvironment();
|
||||
updateCollisionWithVoxels();
|
||||
updateAvatarCollisions(deltaTime);
|
||||
}
|
||||
|
||||
// update body balls
|
||||
updateBodyBalls(deltaTime);
|
||||
|
||||
|
||||
// test for avatar collision response with the big sphere
|
||||
if (usingBigSphereCollisionTest) {
|
||||
updateCollisionWithSphere(_TEST_bigSpherePosition, _TEST_bigSphereRadius, deltaTime);
|
||||
}
|
||||
|
||||
// collision response with voxels
|
||||
if (isMyAvatar()) {
|
||||
updateCollisionWithVoxels();
|
||||
}
|
||||
|
||||
|
||||
if (isMyAvatar()) {
|
||||
|
||||
// add thrust to velocity
|
||||
_velocity += _thrust * deltaTime;
|
||||
|
||||
|
||||
// update body yaw by body yaw delta
|
||||
orientation = orientation * glm::quat(glm::radians(
|
||||
glm::vec3(_bodyPitchDelta, _bodyYawDelta, _bodyRollDelta) * deltaTime));
|
||||
|
||||
// decay body rotation momentum
|
||||
float bodySpinMomentum = 1.0 - BODY_SPIN_FRICTION * deltaTime;
|
||||
if (bodySpinMomentum < 0.0f) { bodySpinMomentum = 0.0f; }
|
||||
|
@ -553,22 +555,14 @@ void Avatar::simulate(float deltaTime, Transmitter* transmitter) {
|
|||
_bodyYawDelta *= bodySpinMomentum;
|
||||
_bodyRollDelta *= bodySpinMomentum;
|
||||
|
||||
// Decay velocity. If velocity is really low, increase decay to simulate static friction
|
||||
const float VELOCITY_DECAY_UNDER_THRUST = 0.2;
|
||||
const float VELOCITY_FAST_DECAY = 0.6;
|
||||
const float VELOCITY_SLOW_DECAY = 3.0;
|
||||
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;
|
||||
|
||||
const float MAX_STATIC_FRICTION_VELOCITY = 0.5f;
|
||||
const float STATIC_FRICTION_STRENGTH = 20.f;
|
||||
applyStaticFriction(deltaTime, _velocity, MAX_STATIC_FRICTION_VELOCITY, STATIC_FRICTION_STRENGTH);
|
||||
|
||||
const float LINEAR_DAMPING_STRENGTH = 3.0f;
|
||||
const float SQUARED_DAMPING_STRENGTH = 0.2f;
|
||||
applyDamping(deltaTime, _velocity, LINEAR_DAMPING_STRENGTH, SQUARED_DAMPING_STRENGTH);
|
||||
|
||||
//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_ROLL_WHILE_TURNING = 0.2;
|
||||
|
@ -659,6 +653,9 @@ void Avatar::simulate(float deltaTime, Transmitter* transmitter) {
|
|||
_mode = AVATAR_MODE_INTERACTING;
|
||||
}
|
||||
|
||||
// update position by velocity, and subtract the change added earlier for gravity
|
||||
_position += _velocity * deltaTime;
|
||||
|
||||
// Zero thrust out now that we've added it to velocity in this frame
|
||||
_thrust = glm::vec3(0, 0, 0);
|
||||
|
||||
|
|
|
@ -87,7 +87,10 @@ public:
|
|||
void reset();
|
||||
void simulate(float deltaTime, Transmitter* transmitter);
|
||||
void updateThrust(float deltaTime, Transmitter * transmitter);
|
||||
void updateFromGyrosAndOrWebcam(bool gyroLook, const glm::vec3& amplifyAngles);
|
||||
void updateFromGyrosAndOrWebcam(bool gyroLook,
|
||||
const glm::vec3& amplifyAngle,
|
||||
float yawFromTouch,
|
||||
float pitchFromTouch);
|
||||
void addBodyYaw(float y) {_bodyYaw += y;};
|
||||
void render(bool lookingInMirror, bool renderAvatarBalls);
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#include <QtDebug>
|
||||
|
||||
#include <GeometryUtil.h>
|
||||
#include <PacketHeaders.h>
|
||||
#include <SharedUtil.h>
|
||||
|
||||
#include "Camera.h"
|
||||
|
@ -138,8 +139,10 @@ bool Environment::findCapsulePenetration(const glm::vec3& start, const glm::vec3
|
|||
int Environment::parseData(sockaddr *senderAddress, unsigned char* sourceBuffer, int numBytes) {
|
||||
// push past the packet header
|
||||
unsigned char* start = sourceBuffer;
|
||||
sourceBuffer++;
|
||||
numBytes--;
|
||||
|
||||
int numBytesPacketHeader = numBytesForPacketHeader(sourceBuffer);
|
||||
sourceBuffer += numBytesPacketHeader;
|
||||
numBytes -= numBytesPacketHeader;
|
||||
|
||||
// get the lock for the duration of the call
|
||||
QMutexLocker locker(&_mutex);
|
||||
|
|
|
@ -54,7 +54,7 @@ void Hand::calculateGeometry() {
|
|||
_position = head.getPosition() + head.getOrientation() * offset;
|
||||
_orientation = head.getOrientation();
|
||||
|
||||
int numLeapBalls = _fingerTips.size() + _fingerRoots.size();
|
||||
int numLeapBalls = _fingerTips.size();
|
||||
_leapBalls.resize(numLeapBalls);
|
||||
|
||||
for (int i = 0; i < _fingerTips.size(); ++i) {
|
||||
|
|
|
@ -54,6 +54,7 @@ Head::Head(Avatar* owningAvatar) :
|
|||
_rotation(0.0f, 0.0f, 0.0f),
|
||||
_leftEyePosition(0.0f, 0.0f, 0.0f),
|
||||
_rightEyePosition(0.0f, 0.0f, 0.0f),
|
||||
_eyeLevelPosition(0.0f, 0.0f, 0.0f),
|
||||
_leftEyeBrowPosition(0.0f, 0.0f, 0.0f),
|
||||
_rightEyeBrowPosition(0.0f, 0.0f, 0.0f),
|
||||
_leftEarPosition(0.0f, 0.0f, 0.0f),
|
||||
|
@ -268,6 +269,8 @@ void Head::calculateGeometry() {
|
|||
+ up * _scale * EYE_UP_OFFSET
|
||||
+ front * _scale * EYE_FRONT_OFFSET;
|
||||
|
||||
_eyeLevelPosition = _position + up * _scale * EYE_UP_OFFSET;
|
||||
|
||||
//calculate the eyebrow positions
|
||||
_leftEyeBrowPosition = _leftEyePosition;
|
||||
_rightEyeBrowPosition = _rightEyePosition;
|
||||
|
|
|
@ -53,7 +53,8 @@ public:
|
|||
glm::quat getOrientation() const;
|
||||
glm::quat getCameraOrientation () const;
|
||||
|
||||
glm::vec3 getPosition() const { return _position; }
|
||||
glm::vec3 getPosition() const { return _position; }
|
||||
const glm::vec3& getEyeLevelPosition() const { return _eyeLevelPosition; }
|
||||
glm::vec3 getRightDirection() const { return getOrientation() * IDENTITY_RIGHT; }
|
||||
glm::vec3 getUpDirection () const { return getOrientation() * IDENTITY_UP; }
|
||||
glm::vec3 getFrontDirection() const { return getOrientation() * IDENTITY_FRONT; }
|
||||
|
@ -88,7 +89,8 @@ private:
|
|||
glm::vec3 _position;
|
||||
glm::vec3 _rotation;
|
||||
glm::vec3 _leftEyePosition;
|
||||
glm::vec3 _rightEyePosition;
|
||||
glm::vec3 _rightEyePosition;
|
||||
glm::vec3 _eyeLevelPosition;
|
||||
glm::vec3 _leftEyeBrowPosition;
|
||||
glm::vec3 _rightEyeBrowPosition;
|
||||
glm::vec3 _leftEarPosition;
|
||||
|
|
40
interface/src/Physics.cpp
Normal file
40
interface/src/Physics.cpp
Normal file
|
@ -0,0 +1,40 @@
|
|||
//
|
||||
// Physics.cpp
|
||||
// hifi
|
||||
//
|
||||
// Created by Philip on July 11, 2013
|
||||
//
|
||||
// Routines to help with doing virtual world physics
|
||||
//
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
#include <SharedUtil.h>
|
||||
|
||||
#include "Util.h"
|
||||
#include "world.h"
|
||||
#include "Physics.h"
|
||||
|
||||
//
|
||||
// Applies static friction: maxVelocity is the largest velocity for which there
|
||||
// there is friction, and strength is the amount of friction force applied to reduce
|
||||
// velocity.
|
||||
//
|
||||
void applyStaticFriction(float deltaTime, glm::vec3& velocity, float maxVelocity, float strength) {
|
||||
float v = glm::length(velocity);
|
||||
if (v < maxVelocity) {
|
||||
velocity *= glm::clamp((1.0f - deltaTime * strength * (1.f - v / maxVelocity)), 0.0f, 1.0f);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Applies velocity damping, with a strength value for linear and squared velocity damping
|
||||
//
|
||||
|
||||
void applyDamping(float deltaTime, glm::vec3& velocity, float linearStrength, float squaredStrength) {
|
||||
if (squaredStrength == 0.f) {
|
||||
velocity *= glm::clamp(1.f - deltaTime * linearStrength, 0.f, 1.f);
|
||||
} else {
|
||||
velocity *= glm::clamp(1.f - deltaTime * (linearStrength + glm::length(velocity) * squaredStrength), 0.f, 1.f);
|
||||
}
|
||||
}
|
||||
|
15
interface/src/Physics.h
Normal file
15
interface/src/Physics.h
Normal file
|
@ -0,0 +1,15 @@
|
|||
//
|
||||
// Balls.h
|
||||
// hifi
|
||||
//
|
||||
// Created by Philip on 4/25/13.
|
||||
//
|
||||
//
|
||||
|
||||
#ifndef hifi_Physics_h
|
||||
#define hifi_Physics_h
|
||||
|
||||
void applyStaticFriction(float deltaTime, glm::vec3& velocity, float maxVelocity, float strength);
|
||||
void applyDamping(float deltaTime, glm::vec3& velocity, float linearStrength, float squaredStrength);
|
||||
|
||||
#endif
|
|
@ -31,7 +31,7 @@ const short NO_READ_MAXIMUM_MSECS = 3000;
|
|||
const int GRAVITY_SAMPLES = 60; // Use the first few samples to baseline values
|
||||
const int NORTH_SAMPLES = 30;
|
||||
const int ACCELERATION_SENSOR_FUSION_SAMPLES = 20;
|
||||
const int COMPASS_SENSOR_FUSION_SAMPLES = 200;
|
||||
const int COMPASS_SENSOR_FUSION_SAMPLES = 100;
|
||||
const int LONG_TERM_RATE_SAMPLES = 1000;
|
||||
|
||||
const bool USING_INVENSENSE_MPU9150 = 1;
|
||||
|
@ -383,7 +383,7 @@ void SerialInterface::resetSerial() {
|
|||
glm::vec3 SerialInterface::recenterCompass(const glm::vec3& compass) {
|
||||
// compensate for "hard iron" distortion by subtracting the midpoint on each axis; see
|
||||
// http://www.sensorsmag.com/sensors/motion-velocity-displacement/compensating-tilt-hard-iron-and-soft-iron-effects-6475
|
||||
return compass - (_compassMinima + _compassMaxima) * 0.5f;
|
||||
return (compass - (_compassMinima + _compassMaxima) * 0.5f) / (_compassMaxima - _compassMinima);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -33,8 +33,8 @@ public:
|
|||
_estimatedVelocity(0, 0, 0),
|
||||
_lastAcceleration(0, 0, 0),
|
||||
_lastRotationRates(0, 0, 0),
|
||||
_compassMinima(-235, -132, -184), // experimentally derived initial values follow
|
||||
_compassMaxima(83, 155, 120),
|
||||
_compassMinima(-211, -132, -186),
|
||||
_compassMaxima(89, 95, 98),
|
||||
_angularVelocityToLinearAccel(
|
||||
0.003f, -0.001f, -0.006f,
|
||||
-0.005f, -0.001f, -0.006f,
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
#undef __interface__Starfield_impl__
|
||||
|
||||
Stars::Stars() :
|
||||
_controller(0l) {
|
||||
_controller(0l), _fileLoaded(false) {
|
||||
_controller = new starfield::Controller;
|
||||
}
|
||||
|
||||
|
@ -23,7 +23,8 @@ Stars::~Stars() {
|
|||
}
|
||||
|
||||
bool Stars::readInput(const char* url, const char* cacheFile, unsigned limit) {
|
||||
return _controller->readInput(url, cacheFile, limit);
|
||||
_fileLoaded = _controller->readInput(url, cacheFile, limit);
|
||||
return _fileLoaded;
|
||||
}
|
||||
|
||||
bool Stars::setResolution(unsigned k) {
|
||||
|
|
|
@ -65,6 +65,7 @@ class Stars {
|
|||
float changeLOD(float factor,
|
||||
float overalloc = 0.25, float realloc = 0.15);
|
||||
|
||||
bool getFileLoaded() const { return _fileLoaded; };
|
||||
private:
|
||||
// don't copy/assign
|
||||
Stars(Stars const&); // = delete;
|
||||
|
@ -73,6 +74,8 @@ class Stars {
|
|||
// variables
|
||||
|
||||
starfield::Controller* _controller;
|
||||
|
||||
bool _fileLoaded;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -3,15 +3,20 @@
|
|||
// hifi
|
||||
//
|
||||
// Created by Philip Rosedale on 5/20/13.
|
||||
//
|
||||
// Copyright (c) 2013 HighFidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#include "Transmitter.h"
|
||||
#include "InterfaceConfig.h"
|
||||
#include "Util.h"
|
||||
#include <cstring>
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
#include <PacketHeaders.h>
|
||||
|
||||
#include "InterfaceConfig.h"
|
||||
#include "Log.h"
|
||||
#include "Transmitter.h"
|
||||
#include "Util.h"
|
||||
|
||||
|
||||
const float DELTA_TIME = 1.f / 60.f;
|
||||
const float DECAY_RATE = 0.15f;
|
||||
|
@ -45,7 +50,9 @@ void Transmitter::resetLevels() {
|
|||
}
|
||||
|
||||
void Transmitter::processIncomingData(unsigned char* packetData, int numBytes) {
|
||||
const int PACKET_HEADER_SIZE = 1; // Packet's first byte is 'T'
|
||||
// Packet's first byte is 'T'
|
||||
int numBytesPacketHeader = numBytesForPacketHeader(packetData);
|
||||
|
||||
const int ROTATION_MARKER_SIZE = 1; // 'R' = Rotation (clockwise about x,y,z)
|
||||
const int ACCELERATION_MARKER_SIZE = 1; // 'A' = Acceleration (x,y,z)
|
||||
if (!_lastReceivedPacket) {
|
||||
|
@ -53,10 +60,10 @@ void Transmitter::processIncomingData(unsigned char* packetData, int numBytes) {
|
|||
}
|
||||
gettimeofday(_lastReceivedPacket, NULL);
|
||||
|
||||
if (numBytes == PACKET_HEADER_SIZE + ROTATION_MARKER_SIZE + ACCELERATION_MARKER_SIZE
|
||||
if (numBytes == numBytesPacketHeader + ROTATION_MARKER_SIZE + ACCELERATION_MARKER_SIZE
|
||||
+ sizeof(_lastRotationRate) + sizeof(_lastAcceleration)
|
||||
+ sizeof(_touchState.x) + sizeof(_touchState.y) + sizeof(_touchState.state)) {
|
||||
unsigned char* packetDataPosition = &packetData[PACKET_HEADER_SIZE + ROTATION_MARKER_SIZE];
|
||||
unsigned char* packetDataPosition = packetData + numBytesPacketHeader + ROTATION_MARKER_SIZE;
|
||||
memcpy(&_lastRotationRate, packetDataPosition, sizeof(_lastRotationRate));
|
||||
packetDataPosition += sizeof(_lastRotationRate) + ACCELERATION_MARKER_SIZE;
|
||||
memcpy(&_lastAcceleration, packetDataPosition, sizeof(_lastAcceleration));
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
// hifi
|
||||
//
|
||||
// Created by Philip Rosedale on 5/20/13.
|
||||
//
|
||||
// Copyright (c) 2013 HighFidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef __hifi__Transmitter__
|
||||
|
|
|
@ -499,6 +499,20 @@ void runTimingTests() {
|
|||
gettimeofday(&endTime, NULL);
|
||||
elapsedMsecs = diffclock(&startTime, &endTime);
|
||||
printLog("powf(f, 0.5) usecs: %f\n", 1000.0f * elapsedMsecs / (float) numTests);
|
||||
|
||||
// Vector Math
|
||||
float distance;
|
||||
glm::vec3 pointA(randVector()), pointB(randVector());
|
||||
gettimeofday(&startTime, NULL);
|
||||
for (int i = 1; i < numTests; i++) {
|
||||
//glm::vec3 temp = pointA - pointB;
|
||||
//float distanceSquared = glm::dot(temp, temp);
|
||||
distance = glm::distance(pointA, pointB);
|
||||
}
|
||||
gettimeofday(&endTime, NULL);
|
||||
elapsedMsecs = diffclock(&startTime, &endTime);
|
||||
printLog("vector math usecs: %f [%f msecs total for %d tests]\n",
|
||||
1000.0f * elapsedMsecs / (float) numTests, elapsedMsecs, numTests);
|
||||
|
||||
// Vec3 test
|
||||
glm::vec3 vecA(randVector()), vecB(randVector());
|
||||
|
@ -523,3 +537,13 @@ float loadSetting(QSettings* settings, const char* name, float defaultValue) {
|
|||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
bool rayIntersectsSphere(glm::vec3& rayStarting, glm::vec3& rayNormalizedDirection, glm::vec3& sphereCenter, double sphereRadius) {
|
||||
glm::vec3 vecFromRayToSphereCenter = sphereCenter - rayStarting;
|
||||
double projection = glm::dot(vecFromRayToSphereCenter, rayNormalizedDirection);
|
||||
double shortestDistance = sqrt(glm::dot(vecFromRayToSphereCenter, vecFromRayToSphereCenter) - projection * projection);
|
||||
if (shortestDistance <= sphereRadius) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -71,4 +71,6 @@ void runTimingTests();
|
|||
|
||||
float loadSetting(QSettings* settings, const char* name, float defaultValue);
|
||||
|
||||
bool rayIntersectsSphere(glm::vec3& rayStarting, glm::vec3& rayNormalizedDirection, glm::vec3& sphereCenter, double sphereRadius);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
#include "Log.h"
|
||||
#include "VoxelConstants.h"
|
||||
#include "CoverageMap.h"
|
||||
#include "CoverageMapV2.h"
|
||||
#include "InterfaceConfig.h"
|
||||
#include "renderer/ProgramObject.h"
|
||||
|
||||
|
@ -112,32 +113,31 @@ float VoxelSystem::getVoxelsBytesReadPerSecondAverage() {
|
|||
int VoxelSystem::parseData(unsigned char* sourceBuffer, int numBytes) {
|
||||
|
||||
unsigned char command = *sourceBuffer;
|
||||
unsigned char *voxelData = sourceBuffer + 1;
|
||||
int numBytesPacketHeader = numBytesForPacketHeader(sourceBuffer);
|
||||
unsigned char* voxelData = sourceBuffer + numBytesPacketHeader;
|
||||
|
||||
pthread_mutex_lock(&_treeLock);
|
||||
|
||||
switch(command) {
|
||||
case PACKET_HEADER_VOXEL_DATA:
|
||||
{
|
||||
case PACKET_TYPE_VOXEL_DATA: {
|
||||
PerformanceWarning warn(_renderWarningsOn, "readBitstreamToTree()");
|
||||
// ask the VoxelTree to read the bitstream into the tree
|
||||
_tree->readBitstreamToTree(voxelData, numBytes - 1, WANT_COLOR, WANT_EXISTS_BITS);
|
||||
_tree->readBitstreamToTree(voxelData, numBytes - numBytesPacketHeader, WANT_COLOR, WANT_EXISTS_BITS);
|
||||
}
|
||||
break;
|
||||
case PACKET_HEADER_VOXEL_DATA_MONOCHROME:
|
||||
{
|
||||
break;
|
||||
case PACKET_TYPE_VOXEL_DATA_MONOCHROME: {
|
||||
PerformanceWarning warn(_renderWarningsOn, "readBitstreamToTree()");
|
||||
// ask the VoxelTree to read the MONOCHROME bitstream into the tree
|
||||
_tree->readBitstreamToTree(voxelData, numBytes - 1, NO_COLOR, WANT_EXISTS_BITS);
|
||||
_tree->readBitstreamToTree(voxelData, numBytes - numBytesPacketHeader, NO_COLOR, WANT_EXISTS_BITS);
|
||||
}
|
||||
break;
|
||||
case PACKET_HEADER_Z_COMMAND:
|
||||
break;
|
||||
case PACKET_TYPE_Z_COMMAND:
|
||||
|
||||
// the Z command is a special command that allows the sender to send high level semantic
|
||||
// requests, like erase all, or add sphere scene, different receivers may handle these
|
||||
// messages differently
|
||||
char* packetData = (char *)sourceBuffer;
|
||||
char* command = &packetData[1]; // start of the command
|
||||
char* command = &packetData[numBytesPacketHeader]; // start of the command
|
||||
int commandLength = strlen(command); // commands are null terminated strings
|
||||
int totalLength = 1+commandLength+1;
|
||||
|
||||
|
@ -168,17 +168,17 @@ int VoxelSystem::parseData(unsigned char* sourceBuffer, int numBytes) {
|
|||
|
||||
void VoxelSystem::setupNewVoxelsForDrawing() {
|
||||
PerformanceWarning warn(_renderWarningsOn, "setupNewVoxelsForDrawing()"); // would like to include _voxelsInArrays, _voxelsUpdated
|
||||
long long start = usecTimestampNow();
|
||||
long long sinceLastTime = (start - _setupNewVoxelsForDrawingLastFinished) / 1000;
|
||||
uint64_t start = usecTimestampNow();
|
||||
uint64_t sinceLastTime = (start - _setupNewVoxelsForDrawingLastFinished) / 1000;
|
||||
|
||||
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((float) _setupNewVoxelsForDrawingLastElapsed, SIXTY_FPS_IN_MILLISECONDS)) {
|
||||
return; // bail early, it hasn't been long enough since the last time we ran
|
||||
}
|
||||
|
||||
long long sinceLastViewCulling = (start - _lastViewCulling) / 1000;
|
||||
uint64_t 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 ((sinceLastViewCulling >= std::max(_lastViewCullingElapsed, VIEW_CULLING_RATE_IN_MILLISECONDS))
|
||||
if ((sinceLastViewCulling >= std::max((float) _lastViewCullingElapsed, VIEW_CULLING_RATE_IN_MILLISECONDS))
|
||||
&& !isViewChanging() && hasViewChanged()) {
|
||||
_lastViewCulling = start;
|
||||
|
||||
|
@ -192,7 +192,7 @@ void VoxelSystem::setupNewVoxelsForDrawing() {
|
|||
// VBO reubuilding. Possibly we should do this only if our actual VBO usage crosses some lower boundary.
|
||||
cleanupRemovedVoxels();
|
||||
|
||||
long long endViewCulling = usecTimestampNow();
|
||||
uint64_t endViewCulling = usecTimestampNow();
|
||||
_lastViewCullingElapsed = (endViewCulling - start) / 1000;
|
||||
}
|
||||
|
||||
|
@ -229,8 +229,8 @@ void VoxelSystem::setupNewVoxelsForDrawing() {
|
|||
|
||||
pthread_mutex_unlock(&_bufferWriteLock);
|
||||
|
||||
long long end = usecTimestampNow();
|
||||
long long elapsedmsec = (end - start) / 1000;
|
||||
uint64_t end = usecTimestampNow();
|
||||
int elapsedmsec = (end - start) / 1000;
|
||||
_setupNewVoxelsForDrawingLastFinished = end;
|
||||
_setupNewVoxelsForDrawingLastElapsed = elapsedmsec;
|
||||
}
|
||||
|
@ -891,13 +891,11 @@ bool VoxelSystem::removeOutOfViewOperation(VoxelNode* node, void* extraData) {
|
|||
bool VoxelSystem::isViewChanging() {
|
||||
bool result = false; // assume the best
|
||||
|
||||
/** TEMPORARY HACK ******
|
||||
// If our viewFrustum has changed since our _lastKnowViewFrustum
|
||||
if (_viewFrustum && !_lastKnowViewFrustum.matches(_viewFrustum)) {
|
||||
if (!_lastKnowViewFrustum.matches(Application::getInstance()->getViewFrustum())) {
|
||||
result = true;
|
||||
_lastKnowViewFrustum = *_viewFrustum; // save last known
|
||||
_lastKnowViewFrustum = *Application::getInstance()->getViewFrustum(); // save last known
|
||||
}
|
||||
**/
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -1089,13 +1087,13 @@ void VoxelSystem::collectStatsForTreesAndVBOs() {
|
|||
args.expectedMax = _voxelsInWriteArrays;
|
||||
_tree->recurseTreeWithOperation(collectStatsForTreesAndVBOsOperation,&args);
|
||||
|
||||
printLog("_voxelsDirty=%s _voxelsInWriteArrays=%ld minDirty=%ld maxDirty=%ld \n", debug::valueOf(_voxelsDirty),
|
||||
_voxelsInWriteArrays, minDirty, maxDirty);
|
||||
|
||||
printLog("stats: total %ld, leaves %ld, dirty %ld, colored %ld, shouldRender %ld, inVBO %ld\n",
|
||||
printLog("Local Voxel Tree Statistics:\n total nodes %ld \n leaves %ld \n dirty %ld \n colored %ld \n shouldRender %ld \n",
|
||||
args.totalNodes, args.leafNodes, args.dirtyNodes, args.coloredNodes, args.shouldRenderNodes);
|
||||
|
||||
printLog("inVBO %ld, nodesInVBOOverExpectedMax %ld, duplicateVBOIndex %ld, nodesInVBONotShouldRender %ld\n",
|
||||
printLog(" _voxelsDirty=%s \n _voxelsInWriteArrays=%ld \n minDirty=%ld \n maxDirty=%ld \n", debug::valueOf(_voxelsDirty),
|
||||
_voxelsInWriteArrays, minDirty, maxDirty);
|
||||
|
||||
printLog(" inVBO %ld \n nodesInVBOOverExpectedMax %ld \n duplicateVBOIndex %ld \n nodesInVBONotShouldRender %ld \n",
|
||||
args.nodesInVBO, args.nodesInVBOOverExpectedMax, args.duplicateVBOIndex, args.nodesInVBONotShouldRender);
|
||||
|
||||
glBufferIndex minInVBO = GLBUFFER_INDEX_UNKNOWN;
|
||||
|
@ -1108,7 +1106,7 @@ void VoxelSystem::collectStatsForTreesAndVBOs() {
|
|||
}
|
||||
}
|
||||
|
||||
printLog("minInVBO=%ld maxInVBO=%ld _voxelsInWriteArrays=%ld _voxelsInReadArrays=%ld\n",
|
||||
printLog(" minInVBO=%ld \n maxInVBO=%ld \n _voxelsInWriteArrays=%ld \n _voxelsInReadArrays=%ld \n",
|
||||
minInVBO, maxInVBO, _voxelsInWriteArrays, _voxelsInReadArrays);
|
||||
|
||||
}
|
||||
|
@ -1162,6 +1160,7 @@ void VoxelSystem::copyFromTreeIntoSubTree(VoxelTree* sourceTree, VoxelNode* dest
|
|||
struct FalseColorizeOccludedArgs {
|
||||
ViewFrustum* viewFrustum;
|
||||
CoverageMap* map;
|
||||
CoverageMapV2* mapV2;
|
||||
VoxelTree* tree;
|
||||
long totalVoxels;
|
||||
long coloredVoxels;
|
||||
|
@ -1181,9 +1180,11 @@ struct FalseColorizeSubTreeOperationArgs {
|
|||
};
|
||||
|
||||
bool VoxelSystem::falseColorizeSubTreeOperation(VoxelNode* node, void* extraData) {
|
||||
FalseColorizeSubTreeOperationArgs* args = (FalseColorizeSubTreeOperationArgs*) extraData;
|
||||
node->setFalseColor(args->color[0], args->color[1], args->color[2]);
|
||||
args->voxelsTouched++;
|
||||
if (node->getShouldRender()) {
|
||||
FalseColorizeSubTreeOperationArgs* args = (FalseColorizeSubTreeOperationArgs*) extraData;
|
||||
node->setFalseColor(args->color[0], args->color[1], args->color[2]);
|
||||
args->voxelsTouched++;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -1263,12 +1264,143 @@ bool VoxelSystem::falseColorizeOccludedOperation(VoxelNode* node, void* extraDat
|
|||
}
|
||||
return true; // keep going!
|
||||
}
|
||||
|
||||
void VoxelSystem::falseColorizeOccluded() {
|
||||
PerformanceWarning warn(true, "falseColorizeOccluded()",true);
|
||||
CoverageMap map;
|
||||
myCoverageMap.erase();
|
||||
|
||||
FalseColorizeOccludedArgs args;
|
||||
args.viewFrustum = Application::getInstance()->getViewFrustum();
|
||||
args.map = ↦
|
||||
args.map = &myCoverageMap;
|
||||
args.totalVoxels = 0;
|
||||
args.coloredVoxels = 0;
|
||||
args.occludedVoxels = 0;
|
||||
args.notOccludedVoxels = 0;
|
||||
args.outOfView = 0;
|
||||
args.subtreeVoxelsSkipped = 0;
|
||||
args.nonLeaves = 0;
|
||||
args.stagedForDeletion = 0;
|
||||
args.nonLeavesOutOfView = 0;
|
||||
args.nonLeavesOccluded = 0;
|
||||
args.tree = _tree;
|
||||
|
||||
VoxelProjectedPolygon::pointInside_calls = 0;
|
||||
VoxelProjectedPolygon::occludes_calls = 0;
|
||||
VoxelProjectedPolygon::intersects_calls = 0;
|
||||
|
||||
glm::vec3 position = args.viewFrustum->getPosition() * (1.0f/TREE_SCALE);
|
||||
|
||||
_tree->recurseTreeWithOperationDistanceSorted(falseColorizeOccludedOperation, position, (void*)&args);
|
||||
|
||||
printLog("falseColorizeOccluded()\n position=(%f,%f)\n total=%ld\n colored=%ld\n occluded=%ld\n notOccluded=%ld\n outOfView=%ld\n subtreeVoxelsSkipped=%ld\n stagedForDeletion=%ld\n nonLeaves=%ld\n nonLeavesOutOfView=%ld\n nonLeavesOccluded=%ld\n pointInside_calls=%ld\n occludes_calls=%ld\n intersects_calls=%ld\n",
|
||||
position.x, position.y,
|
||||
args.totalVoxels, args.coloredVoxels, args.occludedVoxels,
|
||||
args.notOccludedVoxels, args.outOfView, args.subtreeVoxelsSkipped,
|
||||
args.stagedForDeletion,
|
||||
args.nonLeaves, args.nonLeavesOutOfView, args.nonLeavesOccluded,
|
||||
VoxelProjectedPolygon::pointInside_calls,
|
||||
VoxelProjectedPolygon::occludes_calls,
|
||||
VoxelProjectedPolygon::intersects_calls
|
||||
);
|
||||
|
||||
|
||||
//myCoverageMap.erase();
|
||||
|
||||
setupNewVoxelsForDrawing();
|
||||
}
|
||||
|
||||
bool VoxelSystem::falseColorizeOccludedV2Operation(VoxelNode* node, void* extraData) {
|
||||
|
||||
FalseColorizeOccludedArgs* args = (FalseColorizeOccludedArgs*) extraData;
|
||||
args->totalVoxels++;
|
||||
|
||||
// if this node is staged for deletion, then just return
|
||||
if (node->isStagedForDeletion()) {
|
||||
args->stagedForDeletion++;
|
||||
return true;
|
||||
}
|
||||
|
||||
// If we are a parent, let's see if we're completely occluded.
|
||||
if (!node->isLeaf()) {
|
||||
args->nonLeaves++;
|
||||
|
||||
AABox voxelBox = node->getAABox();
|
||||
voxelBox.scale(TREE_SCALE);
|
||||
VoxelProjectedPolygon* voxelPolygon = new VoxelProjectedPolygon(args->viewFrustum->getProjectedPolygon(voxelBox));
|
||||
|
||||
// If we're not all in view, then ignore it, and just return. But keep searching...
|
||||
if (!voxelPolygon->getAllInView()) {
|
||||
args->nonLeavesOutOfView++;
|
||||
delete voxelPolygon;
|
||||
return true;
|
||||
}
|
||||
|
||||
CoverageMapV2StorageResult result = args->mapV2->checkMap(voxelPolygon, false);
|
||||
if (result == V2_OCCLUDED) {
|
||||
args->nonLeavesOccluded++;
|
||||
delete voxelPolygon;
|
||||
|
||||
FalseColorizeSubTreeOperationArgs subArgs;
|
||||
subArgs.color[0] = 0;
|
||||
subArgs.color[1] = 255;
|
||||
subArgs.color[2] = 0;
|
||||
subArgs.voxelsTouched = 0;
|
||||
|
||||
args->tree->recurseNodeWithOperation(node, falseColorizeSubTreeOperation, &subArgs );
|
||||
|
||||
args->subtreeVoxelsSkipped += (subArgs.voxelsTouched - 1);
|
||||
args->totalVoxels += (subArgs.voxelsTouched - 1);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
delete voxelPolygon;
|
||||
return true; // keep looking...
|
||||
}
|
||||
|
||||
if (node->isLeaf() && node->isColored() && node->getShouldRender()) {
|
||||
args->coloredVoxels++;
|
||||
|
||||
AABox voxelBox = node->getAABox();
|
||||
voxelBox.scale(TREE_SCALE);
|
||||
VoxelProjectedPolygon* voxelPolygon = new VoxelProjectedPolygon(args->viewFrustum->getProjectedPolygon(voxelBox));
|
||||
|
||||
// If we're not all in view, then ignore it, and just return. But keep searching...
|
||||
if (!voxelPolygon->getAllInView()) {
|
||||
args->outOfView++;
|
||||
delete voxelPolygon;
|
||||
return true;
|
||||
}
|
||||
|
||||
CoverageMapV2StorageResult result = args->mapV2->checkMap(voxelPolygon, true);
|
||||
if (result == V2_OCCLUDED) {
|
||||
node->setFalseColor(255, 0, 0);
|
||||
args->occludedVoxels++;
|
||||
} else if (result == V2_STORED) {
|
||||
args->notOccludedVoxels++;
|
||||
//printLog("***** falseColorizeOccludedOperation() NODE is STORED *****\n");
|
||||
} else if (result == V2_DOESNT_FIT) {
|
||||
//printLog("***** falseColorizeOccludedOperation() NODE DOESNT_FIT???? *****\n");
|
||||
}
|
||||
delete voxelPolygon; // V2 maps don't store polygons, so we're always in charge of freeing
|
||||
}
|
||||
return true; // keep going!
|
||||
}
|
||||
|
||||
|
||||
void VoxelSystem::falseColorizeOccludedV2() {
|
||||
PerformanceWarning warn(true, "falseColorizeOccludedV2()",true);
|
||||
myCoverageMapV2.erase();
|
||||
|
||||
CoverageMapV2::wantDebugging = true;
|
||||
|
||||
VoxelProjectedPolygon::pointInside_calls = 0;
|
||||
VoxelProjectedPolygon::occludes_calls = 0;
|
||||
VoxelProjectedPolygon::intersects_calls = 0;
|
||||
|
||||
FalseColorizeOccludedArgs args;
|
||||
args.viewFrustum = Application::getInstance()->getViewFrustum();
|
||||
args.mapV2 = &myCoverageMapV2;
|
||||
args.totalVoxels = 0;
|
||||
args.coloredVoxels = 0;
|
||||
args.occludedVoxels = 0;
|
||||
|
@ -1283,15 +1415,22 @@ void VoxelSystem::falseColorizeOccluded() {
|
|||
|
||||
glm::vec3 position = args.viewFrustum->getPosition() * (1.0f/TREE_SCALE);
|
||||
|
||||
_tree->recurseTreeWithOperationDistanceSorted(falseColorizeOccludedOperation, position, (void*)&args);
|
||||
_tree->recurseTreeWithOperationDistanceSorted(falseColorizeOccludedV2Operation, position, (void*)&args);
|
||||
|
||||
printLog("falseColorizeOccluded()\n total=%ld\n colored=%ld\n occluded=%ld\n notOccluded=%ld\n outOfView=%ld\n subtreeVoxelsSkipped=%ld\n stagedForDeletion=%ld\n nonLeaves=%ld\n nonLeavesOutOfView=%ld\n nonLeavesOccluded=%ld\n",
|
||||
printLog("falseColorizeOccludedV2()\n position=(%f,%f)\n total=%ld\n colored=%ld\n occluded=%ld\n notOccluded=%ld\n outOfView=%ld\n subtreeVoxelsSkipped=%ld\n stagedForDeletion=%ld\n nonLeaves=%ld\n nonLeavesOutOfView=%ld\n nonLeavesOccluded=%ld\n pointInside_calls=%ld\n occludes_calls=%ld\n intersects_calls=%ld\n",
|
||||
position.x, position.y,
|
||||
args.totalVoxels, args.coloredVoxels, args.occludedVoxels,
|
||||
args.notOccludedVoxels, args.outOfView, args.subtreeVoxelsSkipped,
|
||||
args.stagedForDeletion,
|
||||
args.nonLeaves, args.nonLeavesOutOfView, args.nonLeavesOccluded);
|
||||
args.nonLeaves, args.nonLeavesOutOfView, args.nonLeavesOccluded,
|
||||
VoxelProjectedPolygon::pointInside_calls,
|
||||
VoxelProjectedPolygon::occludes_calls,
|
||||
VoxelProjectedPolygon::intersects_calls
|
||||
);
|
||||
//myCoverageMapV2.erase();
|
||||
|
||||
|
||||
setupNewVoxelsForDrawing();
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -11,11 +11,15 @@
|
|||
|
||||
#include "InterfaceConfig.h"
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
#include <SharedUtil.h>
|
||||
#include <UDPSocket.h>
|
||||
|
||||
#include <CoverageMapV2.h>
|
||||
#include <NodeData.h>
|
||||
#include <VoxelTree.h>
|
||||
#include <ViewFrustum.h>
|
||||
#include <VoxelTree.h>
|
||||
|
||||
#include "Camera.h"
|
||||
#include "Util.h"
|
||||
#include "world.h"
|
||||
|
@ -57,6 +61,7 @@ public:
|
|||
void falseColorizeDistanceFromView(ViewFrustum* viewFrustum);
|
||||
void falseColorizeRandomEveryOther();
|
||||
void falseColorizeOccluded();
|
||||
void falseColorizeOccludedV2();
|
||||
|
||||
void killLocalVoxels();
|
||||
void setRenderPipelineWarnings(bool on) { _renderWarningsOn = on; };
|
||||
|
@ -84,6 +89,9 @@ public:
|
|||
|
||||
void copySubTreeIntoNewTree(VoxelNode* startNode, VoxelTree* destinationTree, bool rebaseToRoot);
|
||||
void copyFromTreeIntoSubTree(VoxelTree* sourceTree, VoxelNode* destinationNode);
|
||||
|
||||
CoverageMapV2 myCoverageMapV2;
|
||||
CoverageMap myCoverageMap;
|
||||
|
||||
protected:
|
||||
float _treeScale;
|
||||
|
@ -123,6 +131,8 @@ private:
|
|||
static bool collectStatsForTreesAndVBOsOperation(VoxelNode* node, void* extraData);
|
||||
static bool falseColorizeOccludedOperation(VoxelNode* node, void* extraData);
|
||||
static bool falseColorizeSubTreeOperation(VoxelNode* node, void* extraData);
|
||||
static bool falseColorizeOccludedV2Operation(VoxelNode* node, void* extraData);
|
||||
|
||||
|
||||
int updateNodeInArraysAsFullVBO(VoxelNode* node);
|
||||
int updateNodeInArraysAsPartialVBO(VoxelNode* node);
|
||||
|
@ -150,10 +160,10 @@ private:
|
|||
bool _writeRenderFullVBO;
|
||||
bool _readRenderFullVBO;
|
||||
|
||||
double _setupNewVoxelsForDrawingLastElapsed;
|
||||
double _setupNewVoxelsForDrawingLastFinished;
|
||||
double _lastViewCulling;
|
||||
double _lastViewCullingElapsed;
|
||||
int _setupNewVoxelsForDrawingLastElapsed;
|
||||
uint64_t _setupNewVoxelsForDrawingLastFinished;
|
||||
uint64_t _lastViewCulling;
|
||||
int _lastViewCullingElapsed;
|
||||
|
||||
GLuint _vboVerticesID;
|
||||
GLuint _vboNormalsID;
|
||||
|
|
|
@ -50,7 +50,7 @@ void Webcam::setEnabled(bool enabled) {
|
|||
QMetaObject::invokeMethod(_grabber, "grabFrame");
|
||||
|
||||
} else {
|
||||
_grabberThread.quit();
|
||||
QMetaObject::invokeMethod(_grabber, "shutdown");
|
||||
_active = false;
|
||||
}
|
||||
}
|
||||
|
@ -191,8 +191,8 @@ void Webcam::setFrame(const Mat& frame, int format, const Mat& depth, const Rota
|
|||
|
||||
const int MAX_FPS = 60;
|
||||
const int MIN_FRAME_DELAY = 1000000 / MAX_FPS;
|
||||
long long now = usecTimestampNow();
|
||||
long long remaining = MIN_FRAME_DELAY;
|
||||
uint64_t now = usecTimestampNow();
|
||||
int remaining = MIN_FRAME_DELAY;
|
||||
if (_startTimestamp == 0) {
|
||||
_startTimestamp = now;
|
||||
} else {
|
||||
|
@ -267,8 +267,8 @@ FrameGrabber::FrameGrabber() : _initialized(false), _capture(0), _searchWindow(0
|
|||
}
|
||||
|
||||
FrameGrabber::~FrameGrabber() {
|
||||
if (_capture != 0) {
|
||||
cvReleaseCapture(&_capture);
|
||||
if (_initialized) {
|
||||
shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -366,6 +366,16 @@ void FrameGrabber::reset() {
|
|||
#endif
|
||||
}
|
||||
|
||||
void FrameGrabber::shutdown() {
|
||||
if (_capture != 0) {
|
||||
cvReleaseCapture(&_capture);
|
||||
_capture = 0;
|
||||
}
|
||||
_initialized = false;
|
||||
|
||||
thread()->quit();
|
||||
}
|
||||
|
||||
void FrameGrabber::grabFrame() {
|
||||
if (!(_initialized || init())) {
|
||||
return;
|
||||
|
@ -475,7 +485,7 @@ bool FrameGrabber::init() {
|
|||
|
||||
// load our face cascade
|
||||
switchToResourcesParentIfRequired();
|
||||
if (!_faceCascade.load("resources/haarcascades/haarcascade_frontalface_alt.xml")) {
|
||||
if (_faceCascade.empty() && !_faceCascade.load("resources/haarcascades/haarcascade_frontalface_alt.xml")) {
|
||||
printLog("Failed to load Haar cascade for face tracking.\n");
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -73,10 +73,10 @@ private:
|
|||
cv::RotatedRect _initialFaceRect;
|
||||
JointVector _joints;
|
||||
|
||||
long long _startTimestamp;
|
||||
uint64_t _startTimestamp;
|
||||
int _frameCount;
|
||||
|
||||
long long _lastFrameTimestamp;
|
||||
uint64_t _lastFrameTimestamp;
|
||||
|
||||
glm::vec3 _estimatedPosition;
|
||||
glm::vec3 _estimatedRotation;
|
||||
|
@ -94,6 +94,7 @@ public:
|
|||
public slots:
|
||||
|
||||
void reset();
|
||||
void shutdown();
|
||||
void grabFrame();
|
||||
|
||||
private:
|
||||
|
|
|
@ -70,7 +70,7 @@ void AudioInjector::injectAudio(UDPSocket* injectorSocket, sockaddr* destination
|
|||
timeval startTime;
|
||||
|
||||
// calculate the number of bytes required for additional data
|
||||
int leadingBytes = sizeof(PACKET_HEADER)
|
||||
int leadingBytes = numBytesForPacketHeader((unsigned char*) &PACKET_TYPE_INJECT_AUDIO)
|
||||
+ sizeof(_streamIdentifier)
|
||||
+ sizeof(_position)
|
||||
+ sizeof(_orientation)
|
||||
|
@ -79,8 +79,7 @@ void AudioInjector::injectAudio(UDPSocket* injectorSocket, sockaddr* destination
|
|||
|
||||
unsigned char dataPacket[(BUFFER_LENGTH_SAMPLES_PER_CHANNEL * sizeof(int16_t)) + leadingBytes];
|
||||
|
||||
dataPacket[0] = PACKET_HEADER_INJECT_AUDIO;
|
||||
unsigned char *currentPacketPtr = dataPacket + sizeof(PACKET_HEADER_INJECT_AUDIO);
|
||||
unsigned char* currentPacketPtr = dataPacket + populateTypeAndVersion(dataPacket, PACKET_TYPE_INJECT_AUDIO);
|
||||
|
||||
// copy the identifier for this injector
|
||||
memcpy(currentPacketPtr, &_streamIdentifier, sizeof(_streamIdentifier));
|
||||
|
@ -115,7 +114,7 @@ void AudioInjector::injectAudio(UDPSocket* injectorSocket, sockaddr* destination
|
|||
|
||||
injectorSocket->send(destinationSocket, dataPacket, sizeof(dataPacket));
|
||||
|
||||
long long usecToSleep = usecTimestamp(&startTime) + (++nextFrame * INJECT_INTERVAL_USECS) - usecTimestampNow();
|
||||
int usecToSleep = usecTimestamp(&startTime) + (++nextFrame * INJECT_INTERVAL_USECS) - usecTimestampNow();
|
||||
if (usecToSleep > 0) {
|
||||
usleep(usecToSleep);
|
||||
}
|
||||
|
|
|
@ -21,7 +21,7 @@ const int STREAM_IDENTIFIER_NUM_BYTES = 8;
|
|||
|
||||
const int MAX_INJECTOR_VOLUME = 0xFF;
|
||||
|
||||
const long long INJECT_INTERVAL_USECS = floorf((BUFFER_LENGTH_SAMPLES_PER_CHANNEL / SAMPLE_RATE) * 1000000);
|
||||
const int INJECT_INTERVAL_USECS = floorf((BUFFER_LENGTH_SAMPLES_PER_CHANNEL / SAMPLE_RATE) * 1000000);
|
||||
|
||||
class AudioInjector {
|
||||
public:
|
||||
|
|
|
@ -34,7 +34,8 @@ void AudioRingBuffer::reset() {
|
|||
}
|
||||
|
||||
int AudioRingBuffer::parseData(unsigned char* sourceBuffer, int numBytes) {
|
||||
return parseAudioSamples(sourceBuffer + sizeof(PACKET_HEADER_MIXED_AUDIO), numBytes - sizeof(PACKET_HEADER_MIXED_AUDIO));
|
||||
int numBytesPacketHeader = numBytesForPacketHeader(sourceBuffer);
|
||||
return parseAudioSamples(sourceBuffer + numBytesPacketHeader, numBytes - numBytesPacketHeader);
|
||||
}
|
||||
|
||||
int AudioRingBuffer::parseAudioSamples(unsigned char* sourceBuffer, int numBytes) {
|
||||
|
|
|
@ -32,10 +32,10 @@ AvatarData::AvatarData(Node* owningNode) :
|
|||
_cameraNearClip(0.0f),
|
||||
_cameraFarClip(0.0f),
|
||||
_keyState(NO_KEY_DOWN),
|
||||
_wantResIn(false),
|
||||
_wantColor(true),
|
||||
_wantDelta(false),
|
||||
_wantOcclusionCulling(false),
|
||||
_wantDelta(true),
|
||||
_wantLowResMoving(false),
|
||||
_wantOcclusionCulling(true),
|
||||
_headData(NULL),
|
||||
_handData(NULL)
|
||||
{
|
||||
|
@ -113,9 +113,9 @@ int AvatarData::getBroadcastData(unsigned char* destinationBuffer) {
|
|||
|
||||
// bitMask of less than byte wide items
|
||||
unsigned char bitItems = 0;
|
||||
if (_wantResIn) { setAtBit(bitItems, WANT_RESIN_AT_BIT); }
|
||||
if (_wantColor) { setAtBit(bitItems, WANT_COLOR_AT_BIT); }
|
||||
if (_wantDelta) { setAtBit(bitItems, WANT_DELTA_AT_BIT); }
|
||||
if (_wantLowResMoving) { setAtBit(bitItems, WANT_LOW_RES_MOVING_BIT); }
|
||||
if (_wantColor) { setAtBit(bitItems, WANT_COLOR_AT_BIT); }
|
||||
if (_wantDelta) { setAtBit(bitItems, WANT_DELTA_AT_BIT); }
|
||||
if (_wantOcclusionCulling) { setAtBit(bitItems, WANT_OCCLUSION_CULLING_BIT); }
|
||||
|
||||
// key state
|
||||
|
@ -137,6 +137,11 @@ int AvatarData::getBroadcastData(unsigned char* destinationBuffer) {
|
|||
if (numFingerVectors > 255)
|
||||
numFingerVectors = 0; // safety. We shouldn't ever get over 255, so consider that invalid.
|
||||
|
||||
/////////////////////////////////
|
||||
// Temporarily disable Leap finger sending, as it's causing a crash whenever someone's got a Leap connected
|
||||
numFingerVectors = 0;
|
||||
/////////////////////////////////
|
||||
|
||||
*destinationBuffer++ = (unsigned char)numFingerVectors;
|
||||
|
||||
if (numFingerVectors > 0) {
|
||||
|
@ -176,12 +181,13 @@ int AvatarData::parseData(unsigned char* sourceBuffer, int numBytes) {
|
|||
}
|
||||
|
||||
// increment to push past the packet header
|
||||
sourceBuffer += sizeof(PACKET_HEADER_HEAD_DATA);
|
||||
int numBytesPacketHeader = numBytesForPacketHeader(sourceBuffer);
|
||||
sourceBuffer += numBytesPacketHeader;
|
||||
|
||||
unsigned char* startPosition = sourceBuffer;
|
||||
|
||||
// push past the node ID
|
||||
sourceBuffer += + sizeof(uint16_t);
|
||||
sourceBuffer += sizeof(uint16_t);
|
||||
|
||||
// Body world position
|
||||
memcpy(&_position, sourceBuffer, sizeof(float) * 3);
|
||||
|
@ -240,9 +246,9 @@ int AvatarData::parseData(unsigned char* sourceBuffer, int numBytes) {
|
|||
// voxel sending features...
|
||||
unsigned char bitItems = 0;
|
||||
bitItems = (unsigned char)*sourceBuffer++;
|
||||
_wantResIn = oneAtBit(bitItems, WANT_RESIN_AT_BIT);
|
||||
_wantColor = oneAtBit(bitItems, WANT_COLOR_AT_BIT);
|
||||
_wantDelta = oneAtBit(bitItems, WANT_DELTA_AT_BIT);
|
||||
_wantLowResMoving = oneAtBit(bitItems, WANT_LOW_RES_MOVING_BIT);
|
||||
_wantColor = oneAtBit(bitItems, WANT_COLOR_AT_BIT);
|
||||
_wantDelta = oneAtBit(bitItems, WANT_DELTA_AT_BIT);
|
||||
_wantOcclusionCulling = oneAtBit(bitItems, WANT_OCCLUSION_CULLING_BIT);
|
||||
|
||||
// key state, stored as a semi-nibble in the bitItems
|
||||
|
@ -254,8 +260,8 @@ int AvatarData::parseData(unsigned char* sourceBuffer, int numBytes) {
|
|||
// leap hand data
|
||||
if (sourceBuffer - startPosition < numBytes) // safety check
|
||||
{
|
||||
std::vector<glm::vec3> fingerTips = _handData->getFingerTips();
|
||||
std::vector<glm::vec3> fingerRoots = _handData->getFingerRoots();
|
||||
std::vector<glm::vec3> fingerTips;
|
||||
std::vector<glm::vec3> fingerRoots;
|
||||
unsigned int numFingerVectors = *sourceBuffer++;
|
||||
unsigned int numFingerTips = numFingerVectors / 2;
|
||||
unsigned int numFingerRoots = numFingerVectors - numFingerTips;
|
||||
|
@ -266,6 +272,11 @@ int AvatarData::parseData(unsigned char* sourceBuffer, int numBytes) {
|
|||
sourceBuffer += unpackFloatScalarFromSignedTwoByteFixed((int16_t*) sourceBuffer, &(fingerTips[i].y), 4);
|
||||
sourceBuffer += unpackFloatScalarFromSignedTwoByteFixed((int16_t*) sourceBuffer, &(fingerTips[i].z), 4);
|
||||
}
|
||||
for (size_t i = 0; i < numFingerRoots; ++i) {
|
||||
sourceBuffer += unpackFloatScalarFromSignedTwoByteFixed((int16_t*) sourceBuffer, &(fingerRoots[i].x), 4);
|
||||
sourceBuffer += unpackFloatScalarFromSignedTwoByteFixed((int16_t*) sourceBuffer, &(fingerRoots[i].y), 4);
|
||||
sourceBuffer += unpackFloatScalarFromSignedTwoByteFixed((int16_t*) sourceBuffer, &(fingerRoots[i].z), 4);
|
||||
}
|
||||
_handData->setFingerTips(fingerTips);
|
||||
_handData->setFingerRoots(fingerRoots);
|
||||
}
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
#include "HeadData.h"
|
||||
#include "HandData.h"
|
||||
|
||||
const int WANT_RESIN_AT_BIT = 0;
|
||||
const int WANT_LOW_RES_MOVING_BIT = 0;
|
||||
const int WANT_COLOR_AT_BIT = 1;
|
||||
const int WANT_DELTA_AT_BIT = 2;
|
||||
const int KEY_STATE_START_BIT = 3; // 4th and 5th bits
|
||||
|
@ -91,13 +91,14 @@ public:
|
|||
const std::string& chatMessage () const { return _chatMessage; }
|
||||
|
||||
// related to Voxel Sending strategies
|
||||
bool getWantResIn() const { return _wantResIn; }
|
||||
bool getWantColor() const { return _wantColor; }
|
||||
bool getWantDelta() const { return _wantDelta; }
|
||||
bool getWantColor() const { return _wantColor; }
|
||||
bool getWantDelta() const { return _wantDelta; }
|
||||
bool getWantLowResMoving() const { return _wantLowResMoving; }
|
||||
bool getWantOcclusionCulling() const { return _wantOcclusionCulling; }
|
||||
void setWantResIn(bool wantResIn) { _wantResIn = wantResIn; }
|
||||
void setWantColor(bool wantColor) { _wantColor = wantColor; }
|
||||
void setWantDelta(bool wantDelta) { _wantDelta = wantDelta; }
|
||||
|
||||
void setWantColor(bool wantColor) { _wantColor = wantColor; }
|
||||
void setWantDelta(bool wantDelta) { _wantDelta = wantDelta; }
|
||||
void setWantLowResMoving(bool wantLowResMoving) { _wantLowResMoving = wantLowResMoving; }
|
||||
void setWantOcclusionCulling(bool wantOcclusionCulling) { _wantOcclusionCulling = wantOcclusionCulling; }
|
||||
|
||||
void setHeadData(HeadData* headData) { _headData = headData; }
|
||||
|
@ -130,9 +131,9 @@ protected:
|
|||
std::string _chatMessage;
|
||||
|
||||
// voxel server sending items
|
||||
bool _wantResIn;
|
||||
bool _wantColor;
|
||||
bool _wantDelta;
|
||||
bool _wantLowResMoving;
|
||||
bool _wantOcclusionCulling;
|
||||
|
||||
std::vector<JointData> _joints;
|
||||
|
|
|
@ -37,11 +37,11 @@ public:
|
|||
uint16_t getNodeID() const { return _nodeID; }
|
||||
void setNodeID(uint16_t nodeID) { _nodeID = nodeID;}
|
||||
|
||||
long long getWakeMicrostamp() const { return _wakeMicrostamp; }
|
||||
void setWakeMicrostamp(long long wakeMicrostamp) { _wakeMicrostamp = wakeMicrostamp; }
|
||||
uint64_t getWakeMicrostamp() const { return _wakeMicrostamp; }
|
||||
void setWakeMicrostamp(uint64_t wakeMicrostamp) { _wakeMicrostamp = wakeMicrostamp; }
|
||||
|
||||
long long getLastHeardMicrostamp() const { return _lastHeardMicrostamp; }
|
||||
void setLastHeardMicrostamp(long long lastHeardMicrostamp) { _lastHeardMicrostamp = lastHeardMicrostamp; }
|
||||
uint64_t getLastHeardMicrostamp() const { return _lastHeardMicrostamp; }
|
||||
void setLastHeardMicrostamp(uint64_t lastHeardMicrostamp) { _lastHeardMicrostamp = lastHeardMicrostamp; }
|
||||
|
||||
sockaddr* getPublicSocket() const { return _publicSocket; }
|
||||
void setPublicSocket(sockaddr* publicSocket) { _publicSocket = publicSocket; }
|
||||
|
@ -74,8 +74,8 @@ private:
|
|||
|
||||
char _type;
|
||||
uint16_t _nodeID;
|
||||
long long _wakeMicrostamp;
|
||||
long long _lastHeardMicrostamp;
|
||||
uint64_t _wakeMicrostamp;
|
||||
uint64_t _lastHeardMicrostamp;
|
||||
sockaddr* _publicSocket;
|
||||
sockaddr* _localSocket;
|
||||
sockaddr* _activeSocket;
|
||||
|
|
|
@ -72,7 +72,6 @@ NodeList::~NodeList() {
|
|||
|
||||
// stop the spawned threads, if they were started
|
||||
stopSilentNodeRemovalThread();
|
||||
stopPingUnknownNodesThread();
|
||||
|
||||
pthread_mutex_destroy(&mutex);
|
||||
}
|
||||
|
@ -81,27 +80,29 @@ void NodeList::timePingReply(sockaddr *nodeAddress, unsigned char *packetData) {
|
|||
for(NodeList::iterator node = begin(); node != end(); node++) {
|
||||
if (socketMatch(node->getPublicSocket(), nodeAddress) ||
|
||||
socketMatch(node->getLocalSocket(), nodeAddress)) {
|
||||
int pingTime = usecTimestampNow() - *(long long *)(packetData + 1);
|
||||
|
||||
int pingTime = usecTimestampNow() - *(uint64_t*)(packetData + numBytesForPacketHeader(packetData));
|
||||
|
||||
node->setPingMs(pingTime / 1000);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void NodeList::processNodeData(sockaddr *senderAddress, unsigned char *packetData, size_t dataBytes) {
|
||||
switch (((char *)packetData)[0]) {
|
||||
case PACKET_HEADER_DOMAIN: {
|
||||
void NodeList::processNodeData(sockaddr* senderAddress, unsigned char* packetData, size_t dataBytes) {
|
||||
switch (packetData[0]) {
|
||||
case PACKET_TYPE_DOMAIN: {
|
||||
processDomainServerList(packetData, dataBytes);
|
||||
break;
|
||||
}
|
||||
case PACKET_HEADER_PING: {
|
||||
case PACKET_TYPE_PING: {
|
||||
char pingPacket[dataBytes];
|
||||
memcpy(pingPacket, packetData, dataBytes);
|
||||
pingPacket[0] = PACKET_HEADER_PING_REPLY;
|
||||
populateTypeAndVersion((unsigned char*) pingPacket, PACKET_TYPE_PING_REPLY);
|
||||
_nodeSocket.send(senderAddress, pingPacket, dataBytes);
|
||||
break;
|
||||
}
|
||||
case PACKET_HEADER_PING_REPLY: {
|
||||
case PACKET_TYPE_PING_REPLY: {
|
||||
timePingReply(senderAddress, packetData);
|
||||
break;
|
||||
}
|
||||
|
@ -118,18 +119,23 @@ void NodeList::processBulkNodeData(sockaddr *senderAddress, unsigned char *packe
|
|||
bulkSendNode->setLastHeardMicrostamp(usecTimestampNow());
|
||||
bulkSendNode->recordBytesReceived(numTotalBytes);
|
||||
}
|
||||
|
||||
|
||||
int numBytesPacketHeader = numBytesForPacketHeader(packetData);
|
||||
|
||||
unsigned char *startPosition = packetData;
|
||||
unsigned char *currentPosition = startPosition + 1;
|
||||
unsigned char *currentPosition = startPosition + numBytesPacketHeader;
|
||||
unsigned char packetHolder[numTotalBytes];
|
||||
|
||||
packetHolder[0] = PACKET_HEADER_HEAD_DATA;
|
||||
// we've already verified packet version for the bulk packet, so all head data in the packet is also up to date
|
||||
populateTypeAndVersion(packetHolder, PACKET_TYPE_HEAD_DATA);
|
||||
|
||||
uint16_t nodeID = -1;
|
||||
|
||||
while ((currentPosition - startPosition) < numTotalBytes) {
|
||||
unpackNodeId(currentPosition, &nodeID);
|
||||
memcpy(packetHolder + 1, currentPosition, numTotalBytes - (currentPosition - startPosition));
|
||||
memcpy(packetHolder + numBytesPacketHeader,
|
||||
currentPosition,
|
||||
numTotalBytes - (currentPosition - startPosition));
|
||||
|
||||
Node* matchingNode = nodeWithID(nodeID);
|
||||
|
||||
|
@ -139,8 +145,8 @@ void NodeList::processBulkNodeData(sockaddr *senderAddress, unsigned char *packe
|
|||
}
|
||||
|
||||
currentPosition += updateNodeWithData(matchingNode,
|
||||
packetHolder,
|
||||
numTotalBytes - (currentPosition - startPosition));
|
||||
packetHolder,
|
||||
numTotalBytes - (currentPosition - startPosition));
|
||||
}
|
||||
|
||||
unlock();
|
||||
|
@ -213,6 +219,7 @@ void NodeList::setNodeTypesOfInterest(const char* nodeTypesOfInterest, int numNo
|
|||
|
||||
void NodeList::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;
|
||||
|
@ -229,26 +236,32 @@ void NodeList::sendDomainServerCheckIn() {
|
|||
printedDomainServerIP = true;
|
||||
}
|
||||
|
||||
// construct the DS check in packet if we need to
|
||||
static unsigned char* checkInPacket = NULL;
|
||||
static int checkInPacketSize;
|
||||
|
||||
static int checkInPacketSize = 0;
|
||||
|
||||
// construct the DS check in packet if we need to
|
||||
if (!checkInPacket) {
|
||||
int numBytesNodesOfInterest = _nodeTypesOfInterest ? strlen((char*) _nodeTypesOfInterest) : 0;
|
||||
|
||||
const int IP_ADDRESS_BYTES = 4;
|
||||
|
||||
// check in packet has header, node type, port, IP, node types of interest, null termination
|
||||
int numPacketBytes = sizeof(PACKET_HEADER) + sizeof(NODE_TYPE) + sizeof(uint16_t) + (sizeof(char) * 4) +
|
||||
numBytesNodesOfInterest + sizeof(unsigned char);
|
||||
int numPacketBytes = sizeof(PACKET_TYPE) + sizeof(PACKET_VERSION) + sizeof(NODE_TYPE) + sizeof(uint16_t) +
|
||||
IP_ADDRESS_BYTES + numBytesNodesOfInterest + sizeof(unsigned char);
|
||||
|
||||
checkInPacket = new unsigned char[numPacketBytes];
|
||||
unsigned char* packetPosition = checkInPacket;
|
||||
|
||||
*(packetPosition++) = (memchr(SOLO_NODE_TYPES, _ownerType, sizeof(SOLO_NODE_TYPES)))
|
||||
? PACKET_HEADER_DOMAIN_REPORT_FOR_DUTY
|
||||
: PACKET_HEADER_DOMAIN_LIST_REQUEST;
|
||||
PACKET_TYPE nodePacketType = (memchr(SOLO_NODE_TYPES, _ownerType, sizeof(SOLO_NODE_TYPES)))
|
||||
? PACKET_TYPE_DOMAIN_REPORT_FOR_DUTY
|
||||
: PACKET_TYPE_DOMAIN_LIST_REQUEST;
|
||||
|
||||
int numHeaderBytes = populateTypeAndVersion(packetPosition, nodePacketType);
|
||||
packetPosition += numHeaderBytes;
|
||||
|
||||
*(packetPosition++) = _ownerType;
|
||||
|
||||
packetPosition += packSocket(checkInPacket + sizeof(PACKET_HEADER) + sizeof(NODE_TYPE),
|
||||
packetPosition += packSocket(checkInPacket + numHeaderBytes + sizeof(NODE_TYPE),
|
||||
getLocalAddress(),
|
||||
htons(_nodeSocket.getListeningPort()));
|
||||
|
||||
|
@ -269,7 +282,7 @@ void NodeList::sendDomainServerCheckIn() {
|
|||
_nodeSocket.send(DOMAIN_IP, DOMAINSERVER_PORT, checkInPacket, checkInPacketSize);
|
||||
}
|
||||
|
||||
int NodeList::processDomainServerList(unsigned char *packetData, size_t dataBytes) {
|
||||
int NodeList::processDomainServerList(unsigned char* packetData, size_t dataBytes) {
|
||||
int readNodes = 0;
|
||||
|
||||
char nodeType;
|
||||
|
@ -281,16 +294,16 @@ int NodeList::processDomainServerList(unsigned char *packetData, size_t dataByte
|
|||
sockaddr_in nodeLocalSocket;
|
||||
nodeLocalSocket.sin_family = AF_INET;
|
||||
|
||||
unsigned char *readPtr = packetData + 1;
|
||||
unsigned char *startPtr = packetData;
|
||||
unsigned char* readPtr = packetData + numBytesForPacketHeader(packetData);
|
||||
unsigned char* startPtr = packetData;
|
||||
|
||||
while((readPtr - startPtr) < dataBytes - sizeof(uint16_t)) {
|
||||
nodeType = *readPtr++;
|
||||
readPtr += unpackNodeId(readPtr, (uint16_t *)&nodeId);
|
||||
readPtr += unpackSocket(readPtr, (sockaddr *)&nodePublicSocket);
|
||||
readPtr += unpackSocket(readPtr, (sockaddr *)&nodeLocalSocket);
|
||||
readPtr += unpackNodeId(readPtr, (uint16_t*) &nodeId);
|
||||
readPtr += unpackSocket(readPtr, (sockaddr*) &nodePublicSocket);
|
||||
readPtr += unpackSocket(readPtr, (sockaddr*) &nodeLocalSocket);
|
||||
|
||||
addOrUpdateNode((sockaddr *)&nodePublicSocket, (sockaddr *)&nodeLocalSocket, nodeType, nodeId);
|
||||
addOrUpdateNode((sockaddr*) &nodePublicSocket, (sockaddr*) &nodeLocalSocket, nodeType, nodeId);
|
||||
}
|
||||
|
||||
// read out our ID from the packet
|
||||
|
@ -401,49 +414,10 @@ Node* NodeList::soloNodeOfType(char nodeType) {
|
|||
return NULL;
|
||||
}
|
||||
|
||||
void *pingUnknownNodes(void *args) {
|
||||
|
||||
NodeList* nodeList = (NodeList*) args;
|
||||
const int PING_INTERVAL_USECS = 1 * 1000000;
|
||||
|
||||
timeval lastSend;
|
||||
|
||||
while (!pingUnknownNodeThreadStopFlag) {
|
||||
gettimeofday(&lastSend, NULL);
|
||||
|
||||
for(NodeList::iterator node = nodeList->begin();
|
||||
node != nodeList->end();
|
||||
node++) {
|
||||
if (!node->getActiveSocket() && node->getPublicSocket() && node->getLocalSocket()) {
|
||||
// ping both of the sockets for the node so we can figure out
|
||||
// which socket we can use
|
||||
nodeList->getNodeSocket()->send(node->getPublicSocket(), &PACKET_HEADER_PING, 1);
|
||||
nodeList->getNodeSocket()->send(node->getLocalSocket(), &PACKET_HEADER_PING, 1);
|
||||
}
|
||||
}
|
||||
|
||||
long long usecToSleep = PING_INTERVAL_USECS - (usecTimestampNow() - usecTimestamp(&lastSend));
|
||||
|
||||
if (usecToSleep > 0) {
|
||||
usleep(usecToSleep);
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void NodeList::startPingUnknownNodesThread() {
|
||||
pthread_create(&pingUnknownNodesThread, NULL, pingUnknownNodes, (void *)this);
|
||||
}
|
||||
|
||||
void NodeList::stopPingUnknownNodesThread() {
|
||||
pingUnknownNodeThreadStopFlag = true;
|
||||
pthread_join(pingUnknownNodesThread, NULL);
|
||||
}
|
||||
|
||||
void *removeSilentNodes(void *args) {
|
||||
NodeList* nodeList = (NodeList*) args;
|
||||
long long checkTimeUSecs, sleepTime;
|
||||
uint64_t checkTimeUSecs;
|
||||
int sleepTime;
|
||||
|
||||
while (!silentNodeThreadStopFlag) {
|
||||
checkTimeUSecs = usecTimestampNow();
|
||||
|
|
|
@ -89,8 +89,6 @@ public:
|
|||
|
||||
void startSilentNodeRemovalThread();
|
||||
void stopSilentNodeRemovalThread();
|
||||
void startPingUnknownNodesThread();
|
||||
void stopPingUnknownNodesThread();
|
||||
|
||||
friend class NodeListIterator;
|
||||
private:
|
||||
|
@ -113,7 +111,6 @@ private:
|
|||
uint16_t _lastNodeID;
|
||||
pthread_t removeSilentNodesThread;
|
||||
pthread_t checkInWithDomainServerThread;
|
||||
pthread_t pingUnknownNodesThread;
|
||||
pthread_mutex_t mutex;
|
||||
|
||||
void handlePingReply(sockaddr *nodeAddress);
|
||||
|
|
62
libraries/shared/src/PacketHeaders.cpp
Normal file
62
libraries/shared/src/PacketHeaders.cpp
Normal file
|
@ -0,0 +1,62 @@
|
|||
//
|
||||
// PacketHeaders.cpp
|
||||
// hifi
|
||||
//
|
||||
// Created by Stephen Birarda on 6/28/13.
|
||||
// Copyright (c) 2013 HighFidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include "PacketHeaders.h"
|
||||
|
||||
PACKET_VERSION versionForPacketType(PACKET_TYPE type) {
|
||||
switch (type) {
|
||||
default:
|
||||
return 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool packetVersionMatch(unsigned char* packetHeader) {
|
||||
// currently this just checks if the version in the packet matches our return from versionForPacketType
|
||||
// may need to be expanded in the future for types and versions that take > than 1 byte
|
||||
if (packetHeader[1] == versionForPacketType(packetHeader[0])) {
|
||||
return true;
|
||||
} else {
|
||||
printf("There is a packet version mismatch for packet with header %c\n", packetHeader[0]);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
int populateTypeAndVersion(unsigned char* destinationHeader, PACKET_TYPE type) {
|
||||
destinationHeader[0] = type;
|
||||
destinationHeader[1] = versionForPacketType(type);
|
||||
|
||||
// return the number of bytes written for pointer pushing
|
||||
return 2;
|
||||
}
|
||||
|
||||
int numBytesForPacketType(const unsigned char* packetType) {
|
||||
if (packetType[0] == 255) {
|
||||
return 1 + numBytesForPacketType(packetType + 1);
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
int numBytesForPacketVersion(const unsigned char* packetVersion) {
|
||||
if (packetVersion[0] == 255) {
|
||||
return 1 + numBytesForPacketVersion(packetVersion + 1);
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
int numBytesForPacketHeader(unsigned char* packetHeader) {
|
||||
// int numBytesType = numBytesForPacketType(packetHeader);
|
||||
// return numBytesType + numBytesForPacketVersion(packetHeader + numBytesType);
|
||||
|
||||
// currently this need not be dynamic - there are 2 bytes for each packet header
|
||||
return 2;
|
||||
}
|
|
@ -12,28 +12,38 @@
|
|||
#ifndef hifi_PacketHeaders_h
|
||||
#define hifi_PacketHeaders_h
|
||||
|
||||
typedef char PACKET_HEADER;
|
||||
const PACKET_HEADER PACKET_HEADER_DOMAIN = 'D';
|
||||
const PACKET_HEADER PACKET_HEADER_PING = 'P';
|
||||
const PACKET_HEADER PACKET_HEADER_PING_REPLY = 'R';
|
||||
const PACKET_HEADER PACKET_HEADER_HEAD_DATA = 'H';
|
||||
const PACKET_HEADER PACKET_HEADER_Z_COMMAND = 'Z';
|
||||
const PACKET_HEADER PACKET_HEADER_INJECT_AUDIO = 'I';
|
||||
const PACKET_HEADER PACKET_HEADER_MIXED_AUDIO = 'A';
|
||||
const PACKET_HEADER PACKET_HEADER_MICROPHONE_AUDIO_NO_ECHO = 'M';
|
||||
const PACKET_HEADER PACKET_HEADER_MICROPHONE_AUDIO_WITH_ECHO = 'm';
|
||||
const PACKET_HEADER PACKET_HEADER_SET_VOXEL = 'S';
|
||||
const PACKET_HEADER PACKET_HEADER_SET_VOXEL_DESTRUCTIVE = 'O';
|
||||
const PACKET_HEADER PACKET_HEADER_ERASE_VOXEL = 'E';
|
||||
const PACKET_HEADER PACKET_HEADER_VOXEL_DATA = 'V';
|
||||
const PACKET_HEADER PACKET_HEADER_VOXEL_DATA_MONOCHROME = 'v';
|
||||
const PACKET_HEADER PACKET_HEADER_BULK_AVATAR_DATA = 'X';
|
||||
const PACKET_HEADER PACKET_HEADER_AVATAR_VOXEL_URL = 'U';
|
||||
const PACKET_HEADER PACKET_HEADER_TRANSMITTER_DATA_V2 = 'T';
|
||||
const PACKET_HEADER PACKET_HEADER_ENVIRONMENT_DATA = 'e';
|
||||
const PACKET_HEADER PACKET_HEADER_DOMAIN_LIST_REQUEST = 'L';
|
||||
const PACKET_HEADER PACKET_HEADER_DOMAIN_REPORT_FOR_DUTY = 'C';
|
||||
typedef char PACKET_TYPE;
|
||||
const PACKET_TYPE PACKET_TYPE_DOMAIN = 'D';
|
||||
const PACKET_TYPE PACKET_TYPE_PING = 'P';
|
||||
const PACKET_TYPE PACKET_TYPE_PING_REPLY = 'R';
|
||||
const PACKET_TYPE PACKET_TYPE_HEAD_DATA = 'H';
|
||||
const PACKET_TYPE PACKET_TYPE_Z_COMMAND = 'Z';
|
||||
const PACKET_TYPE PACKET_TYPE_INJECT_AUDIO = 'I';
|
||||
const PACKET_TYPE PACKET_TYPE_MIXED_AUDIO = 'A';
|
||||
const PACKET_TYPE PACKET_TYPE_MICROPHONE_AUDIO_NO_ECHO = 'M';
|
||||
const PACKET_TYPE PACKET_TYPE_MICROPHONE_AUDIO_WITH_ECHO = 'm';
|
||||
const PACKET_TYPE PACKET_TYPE_SET_VOXEL = 'S';
|
||||
const PACKET_TYPE PACKET_TYPE_SET_VOXEL_DESTRUCTIVE = 'O';
|
||||
const PACKET_TYPE PACKET_TYPE_ERASE_VOXEL = 'E';
|
||||
const PACKET_TYPE PACKET_TYPE_VOXEL_DATA = 'V';
|
||||
const PACKET_TYPE PACKET_TYPE_VOXEL_DATA_MONOCHROME = 'v';
|
||||
const PACKET_TYPE PACKET_TYPE_BULK_AVATAR_DATA = 'X';
|
||||
const PACKET_TYPE PACKET_TYPE_AVATAR_VOXEL_URL = 'U';
|
||||
const PACKET_TYPE PACKET_TYPE_TRANSMITTER_DATA_V2 = 'T';
|
||||
const PACKET_TYPE PACKET_TYPE_ENVIRONMENT_DATA = 'e';
|
||||
const PACKET_TYPE PACKET_TYPE_DOMAIN_LIST_REQUEST = 'L';
|
||||
const PACKET_TYPE PACKET_TYPE_DOMAIN_REPORT_FOR_DUTY = 'C';
|
||||
|
||||
typedef char PACKET_VERSION;
|
||||
|
||||
PACKET_VERSION versionForPacketType(PACKET_TYPE type);
|
||||
|
||||
bool packetVersionMatch(unsigned char* packetHeader);
|
||||
|
||||
int populateTypeAndVersion(unsigned char* destinationHeader, PACKET_TYPE type);
|
||||
int numBytesForPacketHeader(unsigned char* packetHeader);
|
||||
|
||||
const int MAX_PACKET_HEADER_BYTES = sizeof(PACKET_TYPE) + sizeof(PACKET_VERSION);
|
||||
|
||||
// These are supported Z-Command
|
||||
#define ERASE_ALL_COMMAND "erase all"
|
||||
|
|
|
@ -105,17 +105,17 @@ int PerfStat::DumpStats(char** array) {
|
|||
|
||||
// Destructor handles recording all of our stats
|
||||
PerformanceWarning::~PerformanceWarning() {
|
||||
long long end = usecTimestampNow();
|
||||
uint64_t end = usecTimestampNow();
|
||||
double elapsedmsec = (end - _start) / 1000.0;
|
||||
if ((_alwaysDisplay || _renderWarningsOn) && elapsedmsec > 1) {
|
||||
if (elapsedmsec > 1000) {
|
||||
double elapsedsec = (end - _start) / 1000000.0;
|
||||
printLog("WARNING! %s took %lf seconds\n", _message, elapsedsec);
|
||||
printLog("%s%s took %lf seconds\n", (_alwaysDisplay ? "" : "WARNING!"), _message, elapsedsec);
|
||||
} else {
|
||||
printLog("WARNING! %s took %lf milliseconds\n", _message, elapsedmsec);
|
||||
printLog("%s%s took %lf milliseconds\n", (_alwaysDisplay ? "" : "WARNING!"), _message, elapsedmsec);
|
||||
}
|
||||
} else if (_alwaysDisplay) {
|
||||
printLog("WARNING! %s took %lf milliseconds\n", _message, elapsedmsec);
|
||||
printLog("%s took %lf milliseconds\n", _message, elapsedmsec);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -85,7 +85,7 @@ typedef std::map<std::string,PerfStatHistory,std::less<std::string> >::iterator
|
|||
|
||||
class PerformanceWarning {
|
||||
private:
|
||||
long long _start;
|
||||
uint64_t _start;
|
||||
const char* _message;
|
||||
bool _renderWarningsOn;
|
||||
bool _alwaysDisplay;
|
||||
|
|
|
@ -11,22 +11,25 @@
|
|||
#include <cstring>
|
||||
#include <cctype>
|
||||
#include <time.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include "Syssocket.h"
|
||||
#endif
|
||||
#include "Log.h"
|
||||
#include "SharedUtil.h"
|
||||
#include "OctalCode.h"
|
||||
|
||||
#ifdef __APPLE__
|
||||
#include <CoreFoundation/CoreFoundation.h>
|
||||
#endif
|
||||
|
||||
long long usecTimestamp(timeval *time) {
|
||||
#include "Log.h"
|
||||
#include "OctalCode.h"
|
||||
#include "PacketHeaders.h"
|
||||
#include "SharedUtil.h"
|
||||
|
||||
uint64_t usecTimestamp(timeval *time) {
|
||||
return (time->tv_sec * 1000000 + time->tv_usec);
|
||||
}
|
||||
|
||||
long long usecTimestampNow() {
|
||||
uint64_t usecTimestampNow() {
|
||||
timeval now;
|
||||
gettimeofday(&now, NULL);
|
||||
return (now.tv_sec * 1000000 + now.tv_usec);
|
||||
|
@ -209,13 +212,14 @@ bool createVoxelEditMessage(unsigned char command, short int sequence,
|
|||
int messageSize = MAXIMUM_EDIT_VOXEL_MESSAGE_SIZE; // just a guess for now
|
||||
int actualMessageSize = 3;
|
||||
unsigned char* messageBuffer = new unsigned char[messageSize];
|
||||
unsigned short int* sequenceAt = (unsigned short int*)&messageBuffer[1];
|
||||
|
||||
messageBuffer[0]=command;
|
||||
*sequenceAt=sequence;
|
||||
unsigned char* copyAt = &messageBuffer[3];
|
||||
int numBytesPacketHeader = populateTypeAndVersion(messageBuffer, command);
|
||||
unsigned short int* sequenceAt = (unsigned short int*) &messageBuffer[numBytesPacketHeader];
|
||||
|
||||
*sequenceAt = sequence;
|
||||
unsigned char* copyAt = &messageBuffer[numBytesPacketHeader + sizeof(sequence)];
|
||||
|
||||
for (int i=0;i<voxelCount && success;i++) {
|
||||
for (int i = 0; i < voxelCount && success; i++) {
|
||||
// get the coded voxel
|
||||
unsigned char* voxelData = pointToVoxel(voxelDetails[i].x,voxelDetails[i].y,voxelDetails[i].z,
|
||||
voxelDetails[i].s,voxelDetails[i].red,voxelDetails[i].green,voxelDetails[i].blue);
|
||||
|
@ -224,12 +228,12 @@ bool createVoxelEditMessage(unsigned char command, short int sequence,
|
|||
|
||||
// make sure we have room to copy this voxel
|
||||
if (actualMessageSize+lengthOfVoxelData > MAXIMUM_EDIT_VOXEL_MESSAGE_SIZE) {
|
||||
success=false;
|
||||
success = false;
|
||||
} else {
|
||||
// add it to our message
|
||||
memcpy(copyAt,voxelData,lengthOfVoxelData);
|
||||
copyAt+=lengthOfVoxelData;
|
||||
actualMessageSize+=lengthOfVoxelData;
|
||||
memcpy(copyAt, voxelData, lengthOfVoxelData);
|
||||
copyAt += lengthOfVoxelData;
|
||||
actualMessageSize += lengthOfVoxelData;
|
||||
}
|
||||
// cleanup
|
||||
delete[] voxelData;
|
||||
|
@ -238,9 +242,10 @@ bool createVoxelEditMessage(unsigned char command, short int sequence,
|
|||
if (success) {
|
||||
// finally, copy the result to the output
|
||||
bufferOut = new unsigned char[actualMessageSize];
|
||||
sizeOut=actualMessageSize;
|
||||
memcpy(bufferOut,messageBuffer,actualMessageSize);
|
||||
sizeOut = actualMessageSize;
|
||||
memcpy(bufferOut, messageBuffer, actualMessageSize);
|
||||
}
|
||||
|
||||
delete[] messageBuffer; // clean up our temporary buffer
|
||||
return success;
|
||||
}
|
||||
|
@ -440,3 +445,25 @@ int insertIntoSortedArrays(void* value, float key, int originalIndex,
|
|||
return -1; // error case
|
||||
}
|
||||
|
||||
int removeFromSortedArrays(void* value, void** valueArray, float* keyArray, int* originalIndexArray,
|
||||
int currentCount, int maxCount) {
|
||||
|
||||
int i = 0;
|
||||
if (currentCount > 0) {
|
||||
while (i < currentCount && value != valueArray[i]) {
|
||||
i++;
|
||||
}
|
||||
|
||||
if (value == valueArray[i] && i < currentCount) {
|
||||
// i is the location of the item we were looking for
|
||||
// shift array elements to the left
|
||||
memmove(&valueArray[i], &valueArray[i + 1], sizeof(void*) * ((currentCount-1) - i));
|
||||
memmove(&keyArray[i], &keyArray[i + 1], sizeof(float) * ((currentCount-1) - i));
|
||||
if (originalIndexArray) {
|
||||
memmove(&originalIndexArray[i], &originalIndexArray[i + 1], sizeof(int) * ((currentCount-1) - i));
|
||||
}
|
||||
return currentCount-1;
|
||||
}
|
||||
}
|
||||
return -1; // error case
|
||||
}
|
||||
|
|
|
@ -36,8 +36,8 @@ static const float DECIMETER = 0.1f;
|
|||
static const float CENTIMETER = 0.01f;
|
||||
static const float MILLIIMETER = 0.001f;
|
||||
|
||||
long long usecTimestamp(timeval *time);
|
||||
long long usecTimestampNow();
|
||||
uint64_t usecTimestamp(timeval *time);
|
||||
uint64_t usecTimestampNow();
|
||||
|
||||
float randFloat();
|
||||
int randIntInRange (int min, int max);
|
||||
|
@ -88,6 +88,11 @@ int insertIntoSortedArrays(void* value, float key, int originalIndex,
|
|||
void** valueArray, float* keyArray, int* originalIndexArray,
|
||||
int currentCount, int maxCount);
|
||||
|
||||
int removeFromSortedArrays(void* value, void** valueArray, float* keyArray, int* originalIndexArray,
|
||||
int currentCount, int maxCount);
|
||||
|
||||
|
||||
|
||||
// Helper Class for debugging
|
||||
class debug {
|
||||
public:
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
#ifndef __hifi__Stats__
|
||||
#define __hifi__Stats__
|
||||
|
||||
#include <iostream>
|
||||
#include <stdint.h>
|
||||
|
||||
class SimpleMovingAverage {
|
||||
public:
|
||||
|
@ -26,7 +26,7 @@ public:
|
|||
float getAverageSampleValuePerSecond();
|
||||
private:
|
||||
int _numSamples;
|
||||
long long _lastEventTimestamp;
|
||||
uint64_t _lastEventTimestamp;
|
||||
float _average;
|
||||
float _eventDeltaAverage;
|
||||
|
||||
|
|
|
@ -13,9 +13,12 @@
|
|||
|
||||
int CoverageMap::_mapCount = 0;
|
||||
int CoverageMap::_checkMapRootCalls = 0;
|
||||
int CoverageMap::_notAllInView = 0;
|
||||
bool CoverageMap::wantDebugging = false;
|
||||
|
||||
const BoundingBox CoverageMap::ROOT_BOUNDING_BOX = BoundingBox(glm::vec2(-2.f,-2.f), glm::vec2(4.f,4.f));
|
||||
const int MAX_POLYGONS_PER_REGION = 50;
|
||||
|
||||
const BoundingBox CoverageMap::ROOT_BOUNDING_BOX = BoundingBox(glm::vec2(-1.f,-1.f), glm::vec2(2.f,2.f));
|
||||
|
||||
// Coverage Map's polygon coordinates are from -1 to 1 in the following mapping to screen space.
|
||||
//
|
||||
|
@ -64,6 +67,22 @@ CoverageMap::~CoverageMap() {
|
|||
erase();
|
||||
};
|
||||
|
||||
void CoverageMap::printStats() {
|
||||
printLog("CoverageMap::printStats()...\n");
|
||||
printLog("MINIMUM_POLYGON_AREA_TO_STORE=%f\n",MINIMUM_POLYGON_AREA_TO_STORE);
|
||||
printLog("_mapCount=%d\n",_mapCount);
|
||||
printLog("_checkMapRootCalls=%d\n",_checkMapRootCalls);
|
||||
printLog("_notAllInView=%d\n",_notAllInView);
|
||||
printLog("_maxPolygonsUsed=%d\n",CoverageRegion::_maxPolygonsUsed);
|
||||
printLog("_totalPolygons=%d\n",CoverageRegion::_totalPolygons);
|
||||
printLog("_occlusionTests=%d\n",CoverageRegion::_occlusionTests);
|
||||
printLog("_regionSkips=%d\n",CoverageRegion::_regionSkips);
|
||||
printLog("_tooSmallSkips=%d\n",CoverageRegion::_tooSmallSkips);
|
||||
printLog("_regionFullSkips=%d\n",CoverageRegion::_regionFullSkips);
|
||||
printLog("_outOfOrderPolygon=%d\n",CoverageRegion::_outOfOrderPolygon);
|
||||
printLog("_clippedPolygons=%d\n",CoverageRegion::_clippedPolygons);
|
||||
}
|
||||
|
||||
void CoverageMap::erase() {
|
||||
// tell our regions to erase()
|
||||
_topHalf.erase();
|
||||
|
@ -81,19 +100,19 @@ void CoverageMap::erase() {
|
|||
|
||||
if (_isRoot && wantDebugging) {
|
||||
printLog("CoverageMap last to be deleted...\n");
|
||||
printLog("MINIMUM_POLYGON_AREA_TO_STORE=%f\n",MINIMUM_POLYGON_AREA_TO_STORE);
|
||||
printLog("_mapCount=%d\n",_mapCount);
|
||||
printLog("_checkMapRootCalls=%d\n",_checkMapRootCalls);
|
||||
printLog("_maxPolygonsUsed=%d\n",CoverageRegion::_maxPolygonsUsed);
|
||||
printLog("_totalPolygons=%d\n",CoverageRegion::_totalPolygons);
|
||||
printLog("_occlusionTests=%d\n",CoverageRegion::_occlusionTests);
|
||||
printLog("_outOfOrderPolygon=%d\n",CoverageRegion::_outOfOrderPolygon);
|
||||
printStats();
|
||||
|
||||
CoverageRegion::_maxPolygonsUsed = 0;
|
||||
CoverageRegion::_totalPolygons = 0;
|
||||
CoverageRegion::_occlusionTests = 0;
|
||||
CoverageRegion::_regionSkips = 0;
|
||||
CoverageRegion::_tooSmallSkips = 0;
|
||||
CoverageRegion::_regionFullSkips = 0;
|
||||
CoverageRegion::_outOfOrderPolygon = 0;
|
||||
CoverageRegion::_clippedPolygons = 0;
|
||||
_mapCount = 0;
|
||||
_checkMapRootCalls = 0;
|
||||
_notAllInView = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -121,16 +140,60 @@ BoundingBox CoverageMap::getChildBoundingBox(int childIndex) {
|
|||
return result;
|
||||
}
|
||||
|
||||
int CoverageMap::getPolygonCount() const {
|
||||
return (_topHalf.getPolygonCount() +
|
||||
_bottomHalf.getPolygonCount() +
|
||||
_leftHalf.getPolygonCount() +
|
||||
_rightHalf.getPolygonCount() +
|
||||
_remainder.getPolygonCount());
|
||||
}
|
||||
|
||||
VoxelProjectedPolygon* CoverageMap::getPolygon(int index) const {
|
||||
int base = 0;
|
||||
if ((index - base) < _topHalf.getPolygonCount()) {
|
||||
return _topHalf.getPolygon((index - base));
|
||||
}
|
||||
base += _topHalf.getPolygonCount();
|
||||
|
||||
if ((index - base) < _bottomHalf.getPolygonCount()) {
|
||||
return _bottomHalf.getPolygon((index - base));
|
||||
}
|
||||
base += _bottomHalf.getPolygonCount();
|
||||
|
||||
if ((index - base) < _leftHalf.getPolygonCount()) {
|
||||
return _leftHalf.getPolygon((index - base));
|
||||
}
|
||||
base += _leftHalf.getPolygonCount();
|
||||
|
||||
if ((index - base) < _rightHalf.getPolygonCount()) {
|
||||
return _rightHalf.getPolygon((index - base));
|
||||
}
|
||||
base += _rightHalf.getPolygonCount();
|
||||
|
||||
if ((index - base) < _remainder.getPolygonCount()) {
|
||||
return _remainder.getPolygon((index - base));
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// possible results = STORED/NOT_STORED, OCCLUDED, DOESNT_FIT
|
||||
CoverageMapStorageResult CoverageMap::checkMap(VoxelProjectedPolygon* polygon, bool storeIt) {
|
||||
|
||||
if (_isRoot) {
|
||||
_checkMapRootCalls++;
|
||||
|
||||
//printLog("CoverageMap::checkMap()... storeIt=%s\n", debug::valueOf(storeIt));
|
||||
//polygon->printDebugDetails();
|
||||
|
||||
}
|
||||
|
||||
// short circuit: we don't handle polygons that aren't all in view, so, if the polygon in question is
|
||||
// not in view, then we just discard it with a DOESNT_FIT, this saves us time checking values later.
|
||||
if (!polygon->getAllInView()) {
|
||||
_notAllInView++;
|
||||
//printLog("CoverageMap2::checkMap()... V2_OCCLUDED\n");
|
||||
return DOESNT_FIT;
|
||||
}
|
||||
|
||||
|
@ -142,22 +205,25 @@ CoverageMapStorageResult CoverageMap::checkMap(VoxelProjectedPolygon* polygon, b
|
|||
bool fitsInAHalf = false;
|
||||
|
||||
// Check each half of the box independently
|
||||
if (_topHalf.contains(polygonBox)) {
|
||||
result = _topHalf.checkRegion(polygon, polygonBox, storeIt);
|
||||
storeIn = &_topHalf;
|
||||
fitsInAHalf = true;
|
||||
} else if (_bottomHalf.contains(polygonBox)) {
|
||||
result = _bottomHalf.checkRegion(polygon, polygonBox, storeIt);
|
||||
storeIn = &_bottomHalf;
|
||||
fitsInAHalf = true;
|
||||
} else if (_leftHalf.contains(polygonBox)) {
|
||||
result = _leftHalf.checkRegion(polygon, polygonBox, storeIt);
|
||||
storeIn = &_leftHalf;
|
||||
fitsInAHalf = true;
|
||||
} else if (_rightHalf.contains(polygonBox)) {
|
||||
result = _rightHalf.checkRegion(polygon, polygonBox, storeIt);
|
||||
storeIn = &_rightHalf;
|
||||
fitsInAHalf = true;
|
||||
const bool useRegions = true; // for now we will continue to use regions
|
||||
if (useRegions) {
|
||||
if (_topHalf.contains(polygonBox)) {
|
||||
result = _topHalf.checkRegion(polygon, polygonBox, storeIt);
|
||||
storeIn = &_topHalf;
|
||||
fitsInAHalf = true;
|
||||
} else if (_bottomHalf.contains(polygonBox)) {
|
||||
result = _bottomHalf.checkRegion(polygon, polygonBox, storeIt);
|
||||
storeIn = &_bottomHalf;
|
||||
fitsInAHalf = true;
|
||||
} else if (_leftHalf.contains(polygonBox)) {
|
||||
result = _leftHalf.checkRegion(polygon, polygonBox, storeIt);
|
||||
storeIn = &_leftHalf;
|
||||
fitsInAHalf = true;
|
||||
} else if (_rightHalf.contains(polygonBox)) {
|
||||
result = _rightHalf.checkRegion(polygon, polygonBox, storeIt);
|
||||
storeIn = &_rightHalf;
|
||||
fitsInAHalf = true;
|
||||
}
|
||||
}
|
||||
|
||||
// if we got this far, there are one of two possibilities, either a polygon doesn't fit
|
||||
|
@ -171,36 +237,77 @@ CoverageMapStorageResult CoverageMap::checkMap(VoxelProjectedPolygon* polygon, b
|
|||
// It's possible that this first set of checks might have resulted in an out of order polygon
|
||||
// in which case we just return..
|
||||
if (result == STORED || result == OCCLUDED) {
|
||||
|
||||
/*
|
||||
if (result == STORED)
|
||||
printLog("CoverageMap2::checkMap()... STORED\n");
|
||||
else
|
||||
printLog("CoverageMap2::checkMap()... OCCLUDED\n");
|
||||
*/
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// if we made it here, then it means the polygon being stored is not occluded
|
||||
// at this level of the quad tree, so we can continue to insert it into the map.
|
||||
// First we check to see if it fits in any of our sub maps
|
||||
for (int i = 0; i < NUMBER_OF_CHILDREN; i++) {
|
||||
BoundingBox childMapBoundingBox = getChildBoundingBox(i);
|
||||
if (childMapBoundingBox.contains(polygon->getBoundingBox())) {
|
||||
// if no child map exists yet, then create it
|
||||
if (!_childMaps[i]) {
|
||||
_childMaps[i] = new CoverageMap(childMapBoundingBox, NOT_ROOT, _managePolygons);
|
||||
const bool useChildMaps = true; // for now we will continue to use child maps
|
||||
if (useChildMaps) {
|
||||
for (int i = 0; i < NUMBER_OF_CHILDREN; i++) {
|
||||
BoundingBox childMapBoundingBox = getChildBoundingBox(i);
|
||||
if (childMapBoundingBox.contains(polygon->getBoundingBox())) {
|
||||
// if no child map exists yet, then create it
|
||||
if (!_childMaps[i]) {
|
||||
_childMaps[i] = new CoverageMap(childMapBoundingBox, NOT_ROOT, _managePolygons);
|
||||
}
|
||||
result = _childMaps[i]->checkMap(polygon, storeIt);
|
||||
|
||||
/*
|
||||
switch (result) {
|
||||
case STORED:
|
||||
printLog("checkMap() = STORED\n");
|
||||
break;
|
||||
case NOT_STORED:
|
||||
printLog("checkMap() = NOT_STORED\n");
|
||||
break;
|
||||
case OCCLUDED:
|
||||
printLog("checkMap() = OCCLUDED\n");
|
||||
break;
|
||||
default:
|
||||
printLog("checkMap() = ????? \n");
|
||||
break;
|
||||
}
|
||||
*/
|
||||
|
||||
return result;
|
||||
}
|
||||
return _childMaps[i]->checkMap(polygon, storeIt);
|
||||
}
|
||||
}
|
||||
// if we got this far, then the polygon is in our bounding box, but doesn't fit in
|
||||
// any of our child bounding boxes, so we should add it here.
|
||||
if (storeIt) {
|
||||
if (polygon->getBoundingBox().area() > CoverageMap::MINIMUM_POLYGON_AREA_TO_STORE) {
|
||||
//printLog("storing polygon of area: %f\n",polygon->getBoundingBox().area());
|
||||
storeIn->storeInArray(polygon);
|
||||
return STORED;
|
||||
//printLog("storing polygon of area: %f\n",polygon->getBoundingBox().area());
|
||||
if (storeIn->getPolygonCount() < MAX_POLYGONS_PER_REGION) {
|
||||
storeIn->storeInArray(polygon);
|
||||
//printLog("CoverageMap2::checkMap()... STORED\n");
|
||||
return STORED;
|
||||
} else {
|
||||
CoverageRegion::_regionFullSkips++;
|
||||
//printLog("CoverageMap2::checkMap()... NOT_STORED\n");
|
||||
return NOT_STORED;
|
||||
}
|
||||
} else {
|
||||
CoverageRegion::_tooSmallSkips++;
|
||||
//printLog("CoverageMap2::checkMap()... NOT_STORED\n");
|
||||
return NOT_STORED;
|
||||
}
|
||||
} else {
|
||||
//printLog("CoverageMap2::checkMap()... NOT_STORED\n");
|
||||
return NOT_STORED;
|
||||
}
|
||||
}
|
||||
//printLog("CoverageMap2::checkMap()... DOESNT_FIT\n");
|
||||
return DOESNT_FIT;
|
||||
}
|
||||
|
||||
|
@ -223,12 +330,13 @@ void CoverageRegion::init() {
|
|||
_polygonArraySize = 0;
|
||||
_polygons = NULL;
|
||||
_polygonDistances = NULL;
|
||||
_polygonSizes = NULL;
|
||||
}
|
||||
|
||||
|
||||
void CoverageRegion::erase() {
|
||||
|
||||
/*
|
||||
/**
|
||||
if (_polygonCount) {
|
||||
printLog("CoverageRegion::erase()...\n");
|
||||
printLog("_polygonCount=%d\n",_polygonCount);
|
||||
|
@ -238,7 +346,7 @@ void CoverageRegion::erase() {
|
|||
// _polygons[i]->getBoundingBox().printDebugDetails();
|
||||
//}
|
||||
}
|
||||
*/
|
||||
**/
|
||||
// If we're in charge of managing the polygons, then clean them up first
|
||||
if (_managePolygons) {
|
||||
for (int i = 0; i < _polygonCount; i++) {
|
||||
|
@ -258,11 +366,16 @@ void CoverageRegion::erase() {
|
|||
delete[] _polygonDistances;
|
||||
_polygonDistances = NULL;
|
||||
}
|
||||
if (_polygonSizes) {
|
||||
delete[] _polygonSizes;
|
||||
_polygonSizes = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void CoverageRegion::growPolygonArray() {
|
||||
VoxelProjectedPolygon** newPolygons = new VoxelProjectedPolygon*[_polygonArraySize + DEFAULT_GROW_SIZE];
|
||||
float* newDistances = new float[_polygonArraySize + DEFAULT_GROW_SIZE];
|
||||
float* newDistances = new float[_polygonArraySize + DEFAULT_GROW_SIZE];
|
||||
float* newSizes = new float[_polygonArraySize + DEFAULT_GROW_SIZE];
|
||||
|
||||
|
||||
if (_polygons) {
|
||||
|
@ -270,9 +383,12 @@ void CoverageRegion::growPolygonArray() {
|
|||
delete[] _polygons;
|
||||
memcpy(newDistances, _polygonDistances, sizeof(float) * _polygonCount);
|
||||
delete[] _polygonDistances;
|
||||
memcpy(newSizes, _polygonSizes, sizeof(float) * _polygonCount);
|
||||
delete[] _polygonSizes;
|
||||
}
|
||||
_polygons = newPolygons;
|
||||
_polygons = newPolygons;
|
||||
_polygonDistances = newDistances;
|
||||
_polygonSizes = newSizes;
|
||||
_polygonArraySize = _polygonArraySize + DEFAULT_GROW_SIZE;
|
||||
//printLog("CoverageMap::growPolygonArray() _polygonArraySize=%d...\n",_polygonArraySize);
|
||||
}
|
||||
|
@ -297,26 +413,87 @@ const char* CoverageRegion::getRegionName() const {
|
|||
int CoverageRegion::_maxPolygonsUsed = 0;
|
||||
int CoverageRegion::_totalPolygons = 0;
|
||||
int CoverageRegion::_occlusionTests = 0;
|
||||
int CoverageRegion::_regionSkips = 0;
|
||||
int CoverageRegion::_tooSmallSkips = 0;
|
||||
int CoverageRegion::_regionFullSkips = 0;
|
||||
int CoverageRegion::_outOfOrderPolygon = 0;
|
||||
int CoverageRegion::_clippedPolygons = 0;
|
||||
|
||||
|
||||
bool CoverageRegion::mergeItemsInArray(VoxelProjectedPolygon* seed, bool seedInArray) {
|
||||
for (int i = 0; i < _polygonCount; i++) {
|
||||
VoxelProjectedPolygon* otherPolygon = _polygons[i];
|
||||
if (otherPolygon->canMerge(*seed)) {
|
||||
otherPolygon->merge(*seed);
|
||||
|
||||
if (seedInArray) {
|
||||
const int IGNORED = NULL;
|
||||
// remove this otherOtherPolygon for our polygon array
|
||||
_polygonCount = removeFromSortedArrays((void*)seed,
|
||||
(void**)_polygons, _polygonDistances, IGNORED,
|
||||
_polygonCount, _polygonArraySize);
|
||||
_totalPolygons--;
|
||||
}
|
||||
|
||||
//printLog("_polygonCount=%d\n",_polygonCount);
|
||||
|
||||
// clean up
|
||||
if (_managePolygons) {
|
||||
delete seed;
|
||||
}
|
||||
|
||||
// Now run again using our newly merged polygon as the seed
|
||||
mergeItemsInArray(otherPolygon, true);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// just handles storage in the array, doesn't test for occlusion or
|
||||
// determining if this is the correct map to store in!
|
||||
void CoverageRegion::storeInArray(VoxelProjectedPolygon* polygon) {
|
||||
|
||||
_currentCoveredBounds.explandToInclude(polygon->getBoundingBox());
|
||||
|
||||
|
||||
// Before we actually store this polygon in the array, check to see if this polygon can be merged to any of the existing
|
||||
// polygons already in our array.
|
||||
if (mergeItemsInArray(polygon, false)) {
|
||||
return; // exit early
|
||||
}
|
||||
|
||||
// only after we attempt to merge!
|
||||
_totalPolygons++;
|
||||
|
||||
if (_polygonArraySize < _polygonCount + 1) {
|
||||
growPolygonArray();
|
||||
}
|
||||
|
||||
// This old code assumes that polygons will always be added in z-buffer order, but that doesn't seem to
|
||||
// be a good assumption. So instead, we will need to sort this by distance. Use a binary search to find the
|
||||
// insertion point in this array, and shift the array accordingly
|
||||
// As an experiment we're going to see if we get an improvement by storing the polygons in coverage area sorted order
|
||||
// this means the bigger polygons are earlier in the array. We should have a higher probability of being occluded earlier
|
||||
// in the list. We still check to see if the polygon is "in front" of the target polygon before we test occlusion. Since
|
||||
// sometimes things come out of order.
|
||||
const bool SORT_BY_SIZE = false;
|
||||
const int IGNORED = NULL;
|
||||
_polygonCount = insertIntoSortedArrays((void*)polygon, polygon->getDistance(), IGNORED,
|
||||
(void**)_polygons, _polygonDistances, IGNORED,
|
||||
_polygonCount, _polygonArraySize);
|
||||
|
||||
if (SORT_BY_SIZE) {
|
||||
// This old code assumes that polygons will always be added in z-buffer order, but that doesn't seem to
|
||||
// be a good assumption. So instead, we will need to sort this by distance. Use a binary search to find the
|
||||
// insertion point in this array, and shift the array accordingly
|
||||
float area = polygon->getBoundingBox().area();
|
||||
float reverseArea = 4.0f - area;
|
||||
//printLog("store by size area=%f reverse area=%f\n", area, reverseArea);
|
||||
_polygonCount = insertIntoSortedArrays((void*)polygon, reverseArea, IGNORED,
|
||||
(void**)_polygons, _polygonSizes, IGNORED,
|
||||
_polygonCount, _polygonArraySize);
|
||||
} else {
|
||||
const int IGNORED = NULL;
|
||||
_polygonCount = insertIntoSortedArrays((void*)polygon, polygon->getDistance(), IGNORED,
|
||||
(void**)_polygons, _polygonDistances, IGNORED,
|
||||
_polygonCount, _polygonArraySize);
|
||||
}
|
||||
|
||||
// Debugging and Optimization Tuning code.
|
||||
if (_polygonCount > _maxPolygonsUsed) {
|
||||
_maxPolygonsUsed = _polygonCount;
|
||||
|
@ -335,37 +512,47 @@ CoverageMapStorageResult CoverageRegion::checkRegion(VoxelProjectedPolygon* poly
|
|||
|
||||
if (_isRoot || _myBoundingBox.contains(polygonBox)) {
|
||||
result = NOT_STORED; // if we got here, then we DO fit...
|
||||
|
||||
// only actually check the polygons if this polygon is in the covered bounds for this region
|
||||
if (!_currentCoveredBounds.contains(polygonBox)) {
|
||||
_regionSkips += _polygonCount;
|
||||
} else {
|
||||
// check to make sure this polygon isn't occluded by something at this level
|
||||
for (int i = 0; i < _polygonCount; i++) {
|
||||
VoxelProjectedPolygon* polygonAtThisLevel = _polygons[i];
|
||||
|
||||
// check to make sure this polygon isn't occluded by something at this level
|
||||
for (int i = 0; i < _polygonCount; i++) {
|
||||
VoxelProjectedPolygon* polygonAtThisLevel = _polygons[i];
|
||||
// Check to make sure that the polygon in question is "behind" the polygon in the list
|
||||
// otherwise, we don't need to test it's occlusion (although, it means we've potentially
|
||||
// added an item previously that may be occluded??? Is that possible? Maybe not, because two
|
||||
// voxels can't have the exact same outline. So one occludes the other, they can't both occlude
|
||||
// each other.
|
||||
|
||||
|
||||
_occlusionTests++;
|
||||
if (polygonAtThisLevel->occludes(*polygon)) {
|
||||
// if the polygonAtThisLevel is actually behind the one we're inserting, then we don't
|
||||
// want to report our inserted one as occluded, but we do want to add our inserted one.
|
||||
if (polygonAtThisLevel->getDistance() >= polygon->getDistance()) {
|
||||
_outOfOrderPolygon++;
|
||||
if (storeIt) {
|
||||
if (polygon->getBoundingBox().area() > CoverageMap::MINIMUM_POLYGON_AREA_TO_STORE) {
|
||||
//printLog("storing polygon of area: %f\n",polygon->getBoundingBox().area());
|
||||
storeInArray(polygon);
|
||||
return STORED;
|
||||
// Check to make sure that the polygon in question is "behind" the polygon in the list
|
||||
// otherwise, we don't need to test it's occlusion (although, it means we've potentially
|
||||
// added an item previously that may be occluded??? Is that possible? Maybe not, because two
|
||||
// voxels can't have the exact same outline. So one occludes the other, they can't both occlude
|
||||
// each other.
|
||||
|
||||
_occlusionTests++;
|
||||
if (polygonAtThisLevel->occludes(*polygon)) {
|
||||
// if the polygonAtThisLevel is actually behind the one we're inserting, then we don't
|
||||
// want to report our inserted one as occluded, but we do want to add our inserted one.
|
||||
if (polygonAtThisLevel->getDistance() >= polygon->getDistance()) {
|
||||
_outOfOrderPolygon++;
|
||||
if (storeIt) {
|
||||
if (polygon->getBoundingBox().area() > CoverageMap::MINIMUM_POLYGON_AREA_TO_STORE) {
|
||||
if (getPolygonCount() < MAX_POLYGONS_PER_REGION) {
|
||||
storeInArray(polygon);
|
||||
return STORED;
|
||||
} else {
|
||||
CoverageRegion::_regionFullSkips++;
|
||||
return NOT_STORED;
|
||||
}
|
||||
} else {
|
||||
_tooSmallSkips++;
|
||||
return NOT_STORED;
|
||||
}
|
||||
} else {
|
||||
return NOT_STORED;
|
||||
}
|
||||
} else {
|
||||
return NOT_STORED;
|
||||
}
|
||||
// this polygon is occluded by a closer polygon, so don't store it, and let the caller know
|
||||
return OCCLUDED;
|
||||
}
|
||||
// this polygon is occluded by a closer polygon, so don't store it, and let the caller know
|
||||
return OCCLUDED;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,24 +31,38 @@ public:
|
|||
static int _maxPolygonsUsed;
|
||||
static int _totalPolygons;
|
||||
static int _occlusionTests;
|
||||
static int _regionSkips;
|
||||
static int _tooSmallSkips;
|
||||
static int _regionFullSkips;
|
||||
static int _outOfOrderPolygon;
|
||||
static int _clippedPolygons;
|
||||
|
||||
|
||||
const char* getRegionName() const;
|
||||
|
||||
int getPolygonCount() const { return _polygonCount; };
|
||||
VoxelProjectedPolygon* getPolygon(int index) const { return _polygons[index]; };
|
||||
|
||||
private:
|
||||
void init();
|
||||
|
||||
bool _isRoot; // is this map the root, if so, it never returns DOESNT_FIT
|
||||
BoundingBox _myBoundingBox;
|
||||
BoundingBox _currentCoveredBounds; // area in this region currently covered by some polygon
|
||||
bool _managePolygons; // will the coverage map delete the polygons on destruct
|
||||
RegionName _regionName;
|
||||
int _polygonCount; // how many polygons at this level
|
||||
int _polygonArraySize; // how much room is there to store polygons at this level
|
||||
VoxelProjectedPolygon** _polygons;
|
||||
|
||||
// we will use one or the other of these depending on settings in the code.
|
||||
float* _polygonDistances;
|
||||
float* _polygonSizes;
|
||||
void growPolygonArray();
|
||||
static const int DEFAULT_GROW_SIZE = 100;
|
||||
|
||||
bool mergeItemsInArray(VoxelProjectedPolygon* seed, bool seedInArray);
|
||||
|
||||
};
|
||||
|
||||
class CoverageMap {
|
||||
|
@ -68,9 +82,14 @@ public:
|
|||
BoundingBox getChildBoundingBox(int childIndex);
|
||||
|
||||
void erase(); // erase the coverage map
|
||||
void printStats();
|
||||
|
||||
static bool wantDebugging;
|
||||
|
||||
int getPolygonCount() const;
|
||||
VoxelProjectedPolygon* getPolygon(int index) const;
|
||||
CoverageMap* getChild(int childIndex) const { return _childMaps[childIndex]; };
|
||||
|
||||
private:
|
||||
void init();
|
||||
|
||||
|
@ -89,6 +108,7 @@ private:
|
|||
|
||||
static int _mapCount;
|
||||
static int _checkMapRootCalls;
|
||||
static int _notAllInView;
|
||||
};
|
||||
|
||||
|
||||
|
|
246
libraries/voxels/src/CoverageMapV2.cpp
Normal file
246
libraries/voxels/src/CoverageMapV2.cpp
Normal file
|
@ -0,0 +1,246 @@
|
|||
//
|
||||
// CoverageMapV2.cpp -
|
||||
// hifi
|
||||
//
|
||||
// Added by Brad Hefta-Gaub on 06/11/13.
|
||||
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "CoverageMapV2.h"
|
||||
#include <SharedUtil.h>
|
||||
#include <cstring>
|
||||
#include "Log.h"
|
||||
|
||||
int CoverageMapV2::_mapCount = 0;
|
||||
int CoverageMapV2::_checkMapRootCalls = 0;
|
||||
int CoverageMapV2::_notAllInView = 0;
|
||||
bool CoverageMapV2::wantDebugging = false;
|
||||
|
||||
const BoundingBox CoverageMapV2::ROOT_BOUNDING_BOX = BoundingBox(glm::vec2(-1.f,-1.f), glm::vec2(2.f,2.f));
|
||||
|
||||
// Coverage Map's polygon coordinates are from -1 to 1 in the following mapping to screen space.
|
||||
//
|
||||
// (0,0) (windowWidth, 0)
|
||||
// -1,1 1,1
|
||||
// +-----------------------+
|
||||
// | | |
|
||||
// | | |
|
||||
// | -1,0 | |
|
||||
// |-----------+-----------|
|
||||
// | 0,0 |
|
||||
// | | |
|
||||
// | | |
|
||||
// | | |
|
||||
// +-----------------------+
|
||||
// -1,-1 1,-1
|
||||
// (0,windowHeight) (windowWidth,windowHeight)
|
||||
//
|
||||
|
||||
// Choosing a minimum sized polygon. Since we know a typical window is approximately 1500 pixels wide
|
||||
// then a pixel on our screen will be ~ 2.0/1500 or 0.0013 "units" wide, similarly pixels are typically
|
||||
// about that tall as well. If we say that polygons should be at least 10x10 pixels to be considered "big enough"
|
||||
// then we can calculate a reasonable polygon area
|
||||
const int TYPICAL_SCREEN_WIDTH_IN_PIXELS = 1500;
|
||||
const int MINIMUM_POLYGON_AREA_SIDE_IN_PIXELS = 10;
|
||||
const float TYPICAL_SCREEN_PIXEL_WIDTH = (2.0f / TYPICAL_SCREEN_WIDTH_IN_PIXELS);
|
||||
const float CoverageMapV2::MINIMUM_POLYGON_AREA_TO_STORE = (TYPICAL_SCREEN_PIXEL_WIDTH * MINIMUM_POLYGON_AREA_SIDE_IN_PIXELS) *
|
||||
(TYPICAL_SCREEN_PIXEL_WIDTH * MINIMUM_POLYGON_AREA_SIDE_IN_PIXELS);
|
||||
const float CoverageMapV2::NOT_COVERED = FLT_MAX;
|
||||
const float CoverageMapV2::MINIMUM_OCCLUSION_CHECK_AREA = MINIMUM_POLYGON_AREA_TO_STORE/10.0f; // one quarter the size of poly
|
||||
|
||||
|
||||
CoverageMapV2::CoverageMapV2(BoundingBox boundingBox, bool isRoot, bool isCovered, float coverageDistance) :
|
||||
_isRoot(isRoot),
|
||||
_myBoundingBox(boundingBox),
|
||||
_isCovered(isCovered),
|
||||
_coveredDistance(coverageDistance)
|
||||
{
|
||||
_mapCount++;
|
||||
init();
|
||||
//printLog("CoverageMapV2 created... _mapCount=%d\n",_mapCount);
|
||||
};
|
||||
|
||||
CoverageMapV2::~CoverageMapV2() {
|
||||
erase();
|
||||
};
|
||||
|
||||
void CoverageMapV2::erase() {
|
||||
|
||||
for (int i = 0; i < NUMBER_OF_CHILDREN; i++) {
|
||||
if (_childMaps[i]) {
|
||||
delete _childMaps[i];
|
||||
_childMaps[i] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (_isRoot && wantDebugging) {
|
||||
printLog("CoverageMapV2 last to be deleted...\n");
|
||||
printLog("MINIMUM_POLYGON_AREA_TO_STORE=%f\n",MINIMUM_POLYGON_AREA_TO_STORE);
|
||||
printLog("_mapCount=%d\n",_mapCount);
|
||||
printLog("_checkMapRootCalls=%d\n",_checkMapRootCalls);
|
||||
printLog("_notAllInView=%d\n",_notAllInView);
|
||||
_mapCount = 0;
|
||||
_checkMapRootCalls = 0;
|
||||
_notAllInView = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void CoverageMapV2::init() {
|
||||
memset(_childMaps,0,sizeof(_childMaps));
|
||||
}
|
||||
|
||||
// 0 = bottom, left
|
||||
// 1 = bottom, right
|
||||
// 2 = top, left
|
||||
// 3 = top, right
|
||||
BoundingBox CoverageMapV2::getChildBoundingBox(int childIndex) {
|
||||
const int RIGHT_BIT = 1;
|
||||
const int TOP_BIT = 2;
|
||||
// initialize to our corner, and half our size
|
||||
BoundingBox result(_myBoundingBox.corner,_myBoundingBox.size/2.0f);
|
||||
// if our "right" bit is set, then add size.x to the corner
|
||||
if ((childIndex & RIGHT_BIT) == RIGHT_BIT) {
|
||||
result.corner.x += result.size.x;
|
||||
}
|
||||
// if our "top" bit is set, then add size.y to the corner
|
||||
if ((childIndex & TOP_BIT) == TOP_BIT) {
|
||||
result.corner.y += result.size.y;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// possible results = STORED/NOT_STORED, OCCLUDED, DOESNT_FIT
|
||||
CoverageMapV2StorageResult CoverageMapV2::checkMap(const VoxelProjectedPolygon* polygon, bool storeIt) {
|
||||
assert(_isRoot); // you can only call this on the root map!!!
|
||||
_checkMapRootCalls++;
|
||||
|
||||
// short circuit: if we're the root node (only case we're here), and we're covered, and this polygon is deeper than our
|
||||
// covered depth, then this polygon is occluded!
|
||||
if (_isCovered && _coveredDistance < polygon->getDistance()) {
|
||||
return V2_OCCLUDED;
|
||||
}
|
||||
|
||||
// short circuit: we don't handle polygons that aren't all in view, so, if the polygon in question is
|
||||
// not in view, then we just discard it with a DOESNT_FIT, this saves us time checking values later.
|
||||
if (!polygon->getAllInView()) {
|
||||
_notAllInView++;
|
||||
return V2_DOESNT_FIT;
|
||||
}
|
||||
|
||||
// Here's where we recursively check the polygon against the coverage map. We need to maintain two pieces of state.
|
||||
// The first state is: have we seen at least one "fully occluded" map items. If we haven't then we don't track the covered
|
||||
// state of the polygon.
|
||||
// The second piece of state is: Are all of our "fully occluded" map items "covered". If even one of these occluded map
|
||||
// items is not covered, then our polygon is not covered.
|
||||
bool seenOccludedMapNodes = false;
|
||||
bool allOccludedMapNodesCovered = false;
|
||||
|
||||
recurseMap(polygon, storeIt, seenOccludedMapNodes, allOccludedMapNodesCovered);
|
||||
|
||||
// Ok, no matter how we were called, if all our occluded map nodes are covered, then we know this polygon
|
||||
// is occluded, otherwise, we will report back to the caller about whether or not we stored the polygon
|
||||
if (allOccludedMapNodesCovered) {
|
||||
return V2_OCCLUDED;
|
||||
}
|
||||
if (storeIt) {
|
||||
return V2_STORED; // otherwise report that we STORED it
|
||||
}
|
||||
return V2_NOT_STORED; // unless we weren't asked to store it, then we didn't
|
||||
}
|
||||
|
||||
void CoverageMapV2::recurseMap(const VoxelProjectedPolygon* polygon, bool storeIt,
|
||||
bool& seenOccludedMapNodes, bool& allOccludedMapNodesCovered) {
|
||||
|
||||
// if we are really small, then we act like we don't intersect, this allows us to stop
|
||||
// recusing as we get to the smalles edge of the polygon
|
||||
if (_myBoundingBox.area() < MINIMUM_OCCLUSION_CHECK_AREA) {
|
||||
return; // stop recursion, we're done!
|
||||
}
|
||||
|
||||
// Determine if this map node intersects the polygon and/or is fully covered by the polygon
|
||||
// There are a couple special cases: If we're the root, we are assumed to intersect with all
|
||||
// polygons. Also, any map node that is fully occluded also intersects.
|
||||
bool nodeIsCoveredByPolygon = polygon->occludes(_myBoundingBox);
|
||||
bool nodeIsIntersectedByPolygon = nodeIsCoveredByPolygon || _isRoot || polygon->intersects(_myBoundingBox);
|
||||
|
||||
// If we don't intersect, then we can just return, we're done recursing
|
||||
if (!nodeIsIntersectedByPolygon) {
|
||||
return; // stop recursion, we're done!
|
||||
}
|
||||
|
||||
// At this point, we know our node intersects with the polygon. If this node is covered, then we want to treat it
|
||||
// as if the node was fully covered, because this allows us to short circuit further recursion...
|
||||
if (_isCovered && _coveredDistance < polygon->getDistance()) {
|
||||
nodeIsCoveredByPolygon = true; // fake it till you make it
|
||||
}
|
||||
|
||||
// If this node in the map is fully covered by our polygon, then we don't need to recurse any further, but
|
||||
// we do need to do some bookkeeping.
|
||||
if (nodeIsCoveredByPolygon) {
|
||||
// If this is the very first fully covered node we've seen, then we're initialize our allOccludedMapNodesCovered
|
||||
// to be our current covered state. This has the following effect: if this node isn't already covered, then by
|
||||
// definition, we know that at least one node for this polygon isn't covered, and therefore we aren't fully covered.
|
||||
if (!seenOccludedMapNodes) {
|
||||
allOccludedMapNodesCovered = (_isCovered && _coveredDistance < polygon->getDistance());
|
||||
// We need to mark that we've seen at least one node of our polygon! ;)
|
||||
seenOccludedMapNodes = true;
|
||||
} else {
|
||||
// If this is our second or later node of our polygon, then we need to track our allOccludedMapNodesCovered state
|
||||
allOccludedMapNodesCovered = allOccludedMapNodesCovered &&
|
||||
(_isCovered && _coveredDistance < polygon->getDistance());
|
||||
}
|
||||
|
||||
// if we're in store mode then we want to record that this node is covered.
|
||||
if (storeIt) {
|
||||
_isCovered = true;
|
||||
// store the minimum distance of our previous known distance, or our current polygon's distance. This is because
|
||||
// we know that we're at least covered at this distance, but if we had previously identified that we're covered
|
||||
// at a shallower distance, then we want to maintain that distance
|
||||
_coveredDistance = std::min(polygon->getDistance(), _coveredDistance);
|
||||
|
||||
// Note: this might be a good chance to delete child maps, but we're not going to do that at this point because
|
||||
// we're trying to maintain the known distances in the lower portion of the tree.
|
||||
}
|
||||
|
||||
// and since this node of the quad map is covered, we can safely stop recursion. because we know all smaller map
|
||||
// nodes will also be covered.
|
||||
return;
|
||||
}
|
||||
|
||||
// If we got here, then it means we know that this node is not fully covered by the polygon, but it does intersect
|
||||
// with the polygon.
|
||||
|
||||
// Another case is that we aren't yet marked as covered, and so we should recurse and process smaller quad tree nodes.
|
||||
// Note: we use this to determine if we can collapse the child quad trees and mark this node as covered
|
||||
bool allChildrenOccluded = true;
|
||||
float maxChildCoveredDepth = NOT_COVERED;
|
||||
for (int i = 0; i < NUMBER_OF_CHILDREN; i++) {
|
||||
BoundingBox childMapBoundingBox = getChildBoundingBox(i);
|
||||
// if no child map exists yet, then create it
|
||||
if (!_childMaps[i]) {
|
||||
// children get created with the coverage state of their parent.
|
||||
_childMaps[i] = new CoverageMapV2(childMapBoundingBox, NOT_ROOT, _isCovered, _coveredDistance);
|
||||
}
|
||||
|
||||
_childMaps[i]->recurseMap(polygon, storeIt, seenOccludedMapNodes, allOccludedMapNodesCovered);
|
||||
|
||||
// if so far, all of our children are covered, then record our furthest coverage distance
|
||||
if (allChildrenOccluded && _childMaps[i]->_isCovered) {
|
||||
maxChildCoveredDepth = std::max(maxChildCoveredDepth, _childMaps[i]->_coveredDistance);
|
||||
} else {
|
||||
// otherwise, at least one of our children is not covered, so not all are covered
|
||||
allChildrenOccluded = false;
|
||||
}
|
||||
}
|
||||
// if all the children are covered, this makes our quad tree "shallower" because it records that
|
||||
// entire quad is covered, it uses the "furthest" z-order so that if a shalower polygon comes through
|
||||
// we won't assume its occluded
|
||||
if (allChildrenOccluded && storeIt) {
|
||||
_isCovered = true;
|
||||
_coveredDistance = maxChildCoveredDepth;
|
||||
}
|
||||
|
||||
// normal exit case... return...
|
||||
}
|
69
libraries/voxels/src/CoverageMapV2.h
Normal file
69
libraries/voxels/src/CoverageMapV2.h
Normal file
|
@ -0,0 +1,69 @@
|
|||
//
|
||||
// CoverageMapV2.h - 2D CoverageMapV2 Quad tree for storage of VoxelProjectedPolygons
|
||||
// hifi
|
||||
//
|
||||
// Added by Brad Hefta-Gaub on 06/11/13.
|
||||
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef _COVERAGE_MAP_V2_
|
||||
#define _COVERAGE_MAP_V2_
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
#include "VoxelProjectedPolygon.h"
|
||||
|
||||
typedef enum {
|
||||
V2_DOESNT_FIT, V2_STORED, V2_NOT_STORED,
|
||||
V2_INTERSECT, V2_NO_INTERSECT,
|
||||
V2_OCCLUDED, V2_NOT_OCCLUDED
|
||||
} CoverageMapV2StorageResult;
|
||||
|
||||
class CoverageMapV2 {
|
||||
|
||||
public:
|
||||
static const int NUMBER_OF_CHILDREN = 4;
|
||||
static const bool NOT_ROOT = false;
|
||||
static const bool IS_ROOT = true;
|
||||
static const BoundingBox ROOT_BOUNDING_BOX;
|
||||
static const float MINIMUM_POLYGON_AREA_TO_STORE;
|
||||
static const float NOT_COVERED;
|
||||
static const float MINIMUM_OCCLUSION_CHECK_AREA;
|
||||
static bool wantDebugging;
|
||||
|
||||
CoverageMapV2(BoundingBox boundingBox = ROOT_BOUNDING_BOX, bool isRoot = IS_ROOT,
|
||||
bool isCovered = false, float coverageDistance = NOT_COVERED);
|
||||
~CoverageMapV2();
|
||||
|
||||
CoverageMapV2StorageResult checkMap(const VoxelProjectedPolygon* polygon, bool storeIt = true);
|
||||
|
||||
BoundingBox getChildBoundingBox(int childIndex);
|
||||
const BoundingBox& getBoundingBox() const { return _myBoundingBox; };
|
||||
CoverageMapV2* getChild(int childIndex) const { return _childMaps[childIndex]; };
|
||||
bool isCovered() const { return _isCovered; };
|
||||
|
||||
void erase(); // erase the coverage map
|
||||
|
||||
void render();
|
||||
|
||||
|
||||
private:
|
||||
void recurseMap(const VoxelProjectedPolygon* polygon, bool storeIt,
|
||||
bool& seenOccludedMapNodes, bool& allOccludedMapNodesCovered);
|
||||
|
||||
void init();
|
||||
|
||||
bool _isRoot;
|
||||
BoundingBox _myBoundingBox;
|
||||
CoverageMapV2* _childMaps[NUMBER_OF_CHILDREN];
|
||||
|
||||
bool _isCovered;
|
||||
float _coveredDistance;
|
||||
|
||||
static int _mapCount;
|
||||
static int _checkMapRootCalls;
|
||||
static int _notAllInView;
|
||||
};
|
||||
|
||||
|
||||
#endif // _COVERAGE_MAP_V2_
|
|
@ -5,6 +5,9 @@
|
|||
// Created by Andrzej Kapolka on 5/21/13.
|
||||
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#include <Log.h>
|
||||
#include <SharedUtil.h>
|
||||
|
||||
#include "GeometryUtil.h"
|
||||
|
@ -117,6 +120,7 @@ glm::vec3 addPenetrations(const glm::vec3& currentPenetration, const glm::vec3&
|
|||
}
|
||||
|
||||
// Do line segments (r1p1.x, r1p1.y)--(r1p2.x, r1p2.y) and (r2p1.x, r2p1.y)--(r2p2.x, r2p2.y) intersect?
|
||||
// from: http://ptspts.blogspot.com/2010/06/how-to-determine-if-two-line-segments.html
|
||||
bool doLineSegmentsIntersect(glm::vec2 r1p1, glm::vec2 r1p2, glm::vec2 r2p1, glm::vec2 r2p2) {
|
||||
int d1 = computeDirection(r2p1.x, r2p1.y, r2p2.x, r2p2.y, r1p1.x, r1p1.y);
|
||||
int d2 = computeDirection(r2p1.x, r2p1.y, r2p2.x, r2p2.y, r1p2.x, r1p2.y);
|
||||
|
@ -140,3 +144,193 @@ int computeDirection(float xi, float yi, float xj, float yj, float xk, float yk)
|
|||
float b = (xj - xi) * (yk - yi);
|
||||
return a < b ? -1 : a > b ? 1 : 0;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Polygon Clipping routines inspired by, pseudo code found here: http://www.cs.rit.edu/~icss571/clipTrans/PolyClipBack.html
|
||||
//
|
||||
// Coverage Map's polygon coordinates are from -1 to 1 in the following mapping to screen space.
|
||||
//
|
||||
// (0,0) (windowWidth, 0)
|
||||
// -1,1 1,1
|
||||
// +-----------------------+
|
||||
// | | |
|
||||
// | | |
|
||||
// | -1,0 | |
|
||||
// |-----------+-----------|
|
||||
// | 0,0 |
|
||||
// | | |
|
||||
// | | |
|
||||
// | | |
|
||||
// +-----------------------+
|
||||
// -1,-1 1,-1
|
||||
// (0,windowHeight) (windowWidth,windowHeight)
|
||||
//
|
||||
|
||||
const float PolygonClip::TOP_OF_CLIPPING_WINDOW = 1.0f;
|
||||
const float PolygonClip::BOTTOM_OF_CLIPPING_WINDOW = -1.0f;
|
||||
const float PolygonClip::LEFT_OF_CLIPPING_WINDOW = -1.0f;
|
||||
const float PolygonClip::RIGHT_OF_CLIPPING_WINDOW = 1.0f;
|
||||
|
||||
const glm::vec2 PolygonClip::TOP_LEFT_CLIPPING_WINDOW ( LEFT_OF_CLIPPING_WINDOW , TOP_OF_CLIPPING_WINDOW );
|
||||
const glm::vec2 PolygonClip::TOP_RIGHT_CLIPPING_WINDOW ( RIGHT_OF_CLIPPING_WINDOW, TOP_OF_CLIPPING_WINDOW );
|
||||
const glm::vec2 PolygonClip::BOTTOM_LEFT_CLIPPING_WINDOW ( LEFT_OF_CLIPPING_WINDOW , BOTTOM_OF_CLIPPING_WINDOW );
|
||||
const glm::vec2 PolygonClip::BOTTOM_RIGHT_CLIPPING_WINDOW ( RIGHT_OF_CLIPPING_WINDOW, BOTTOM_OF_CLIPPING_WINDOW );
|
||||
|
||||
void PolygonClip::clipToScreen(const glm::vec2* inputVertexArray, int inLength, glm::vec2*& outputVertexArray, int& outLength) {
|
||||
int tempLengthA = inLength;
|
||||
int tempLengthB;
|
||||
int maxLength = inLength * 2;
|
||||
glm::vec2* tempVertexArrayA = new glm::vec2[maxLength];
|
||||
glm::vec2* tempVertexArrayB = new glm::vec2[maxLength];
|
||||
|
||||
// set up our temporary arrays
|
||||
memcpy(tempVertexArrayA, inputVertexArray, sizeof(glm::vec2) * inLength);
|
||||
|
||||
// Left edge
|
||||
LineSegment2 edge;
|
||||
edge[0] = TOP_LEFT_CLIPPING_WINDOW;
|
||||
edge[1] = BOTTOM_LEFT_CLIPPING_WINDOW;
|
||||
// clip the array from tempVertexArrayA and copy end result to tempVertexArrayB
|
||||
sutherlandHodgmanPolygonClip(tempVertexArrayA, tempVertexArrayB, tempLengthA, tempLengthB, edge);
|
||||
// clean the array from tempVertexArrayA and copy cleaned result to tempVertexArrayA
|
||||
copyCleanArray(tempLengthA, tempVertexArrayA, tempLengthB, tempVertexArrayB);
|
||||
|
||||
// Bottom Edge
|
||||
edge[0] = BOTTOM_LEFT_CLIPPING_WINDOW;
|
||||
edge[1] = BOTTOM_RIGHT_CLIPPING_WINDOW;
|
||||
// clip the array from tempVertexArrayA and copy end result to tempVertexArrayB
|
||||
sutherlandHodgmanPolygonClip(tempVertexArrayA, tempVertexArrayB, tempLengthA, tempLengthB, edge);
|
||||
// clean the array from tempVertexArrayA and copy cleaned result to tempVertexArrayA
|
||||
copyCleanArray(tempLengthA, tempVertexArrayA, tempLengthB, tempVertexArrayB);
|
||||
|
||||
// Right Edge
|
||||
edge[0] = BOTTOM_RIGHT_CLIPPING_WINDOW;
|
||||
edge[1] = TOP_RIGHT_CLIPPING_WINDOW;
|
||||
// clip the array from tempVertexArrayA and copy end result to tempVertexArrayB
|
||||
sutherlandHodgmanPolygonClip(tempVertexArrayA, tempVertexArrayB, tempLengthA, tempLengthB, edge);
|
||||
// clean the array from tempVertexArrayA and copy cleaned result to tempVertexArrayA
|
||||
copyCleanArray(tempLengthA, tempVertexArrayA, tempLengthB, tempVertexArrayB);
|
||||
|
||||
// Top Edge
|
||||
edge[0] = TOP_RIGHT_CLIPPING_WINDOW;
|
||||
edge[1] = TOP_LEFT_CLIPPING_WINDOW;
|
||||
// clip the array from tempVertexArrayA and copy end result to tempVertexArrayB
|
||||
sutherlandHodgmanPolygonClip(tempVertexArrayA, tempVertexArrayB, tempLengthA, tempLengthB, edge);
|
||||
// clean the array from tempVertexArrayA and copy cleaned result to tempVertexArrayA
|
||||
copyCleanArray(tempLengthA, tempVertexArrayA, tempLengthB, tempVertexArrayB);
|
||||
|
||||
// copy final output to outputVertexArray
|
||||
outputVertexArray = tempVertexArrayA;
|
||||
outLength = tempLengthA;
|
||||
|
||||
// cleanup our unused temporary buffer...
|
||||
delete[] tempVertexArrayB;
|
||||
|
||||
// Note: we don't delete tempVertexArrayA, because that's the caller's responsibility
|
||||
}
|
||||
|
||||
void PolygonClip::sutherlandHodgmanPolygonClip(glm::vec2* inVertexArray, glm::vec2* outVertexArray,
|
||||
int inLength, int& outLength, const LineSegment2& clipBoundary) {
|
||||
glm::vec2 start, end; // Start, end point of current polygon edge
|
||||
glm::vec2 intersection; // Intersection point with a clip boundary
|
||||
|
||||
outLength = 0;
|
||||
start = inVertexArray[inLength - 1]; // Start with the last vertex in inVertexArray
|
||||
for (int j = 0; j < inLength; j++) {
|
||||
end = inVertexArray[j]; // Now start and end correspond to the vertices
|
||||
|
||||
// Cases 1 and 4 - the endpoint is inside the boundary
|
||||
if (pointInsideBoundary(end,clipBoundary)) {
|
||||
// Case 1 - Both inside
|
||||
if (pointInsideBoundary(start, clipBoundary)) {
|
||||
appendPoint(end, outLength, outVertexArray);
|
||||
} else { // Case 4 - end is inside, but start is outside
|
||||
segmentIntersectsBoundary(start, end, clipBoundary, intersection);
|
||||
appendPoint(intersection, outLength, outVertexArray);
|
||||
appendPoint(end, outLength, outVertexArray);
|
||||
}
|
||||
} else { // Cases 2 and 3 - end is outside
|
||||
if (pointInsideBoundary(start, clipBoundary)) {
|
||||
// Cases 2 - start is inside, end is outside
|
||||
segmentIntersectsBoundary(start, end, clipBoundary, intersection);
|
||||
appendPoint(intersection, outLength, outVertexArray);
|
||||
} else {
|
||||
// Case 3 - both are outside, No action
|
||||
}
|
||||
}
|
||||
start = end; // Advance to next pair of vertices
|
||||
}
|
||||
}
|
||||
|
||||
bool PolygonClip::pointInsideBoundary(const glm::vec2& testVertex, const LineSegment2& clipBoundary) {
|
||||
// bottom edge
|
||||
if (clipBoundary[1].x > clipBoundary[0].x) {
|
||||
if (testVertex.y >= clipBoundary[0].y) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// top edge
|
||||
if (clipBoundary[1].x < clipBoundary[0].x) {
|
||||
if (testVertex.y <= clipBoundary[0].y) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// right edge
|
||||
if (clipBoundary[1].y > clipBoundary[0].y) {
|
||||
if (testVertex.x <= clipBoundary[1].x) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// left edge
|
||||
if (clipBoundary[1].y < clipBoundary[0].y) {
|
||||
if (testVertex.x >= clipBoundary[1].x) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void PolygonClip::segmentIntersectsBoundary(const glm::vec2& first, const glm::vec2& second,
|
||||
const LineSegment2& clipBoundary, glm::vec2& intersection) {
|
||||
// horizontal
|
||||
if (clipBoundary[0].y==clipBoundary[1].y) {
|
||||
intersection.y = clipBoundary[0].y;
|
||||
intersection.x = first.x + (clipBoundary[0].y - first.y) * (second.x - first.x) / (second.y - first.y);
|
||||
} else { // Vertical
|
||||
intersection.x = clipBoundary[0].x;
|
||||
intersection.y = first.y + (clipBoundary[0].x - first.x) * (second.y - first.y) / (second.x - first.x);
|
||||
}
|
||||
}
|
||||
|
||||
void PolygonClip::appendPoint(glm::vec2 newVertex, int& outLength, glm::vec2* outVertexArray) {
|
||||
outVertexArray[outLength].x = newVertex.x;
|
||||
outVertexArray[outLength].y = newVertex.y;
|
||||
outLength++;
|
||||
}
|
||||
|
||||
// The copyCleanArray() function sets the resulting polygon of the previous step up to be the input polygon for next step of the
|
||||
// clipping algorithm. As the Sutherland-Hodgman algorithm is a polygon clipping algorithm, it does not handle line
|
||||
// clipping very well. The modification so that lines may be clipped as well as polygons is included in this function.
|
||||
// when completed vertexArrayA will be ready for output and/or next step of clipping
|
||||
void PolygonClip::copyCleanArray(int& lengthA, glm::vec2* vertexArrayA, int& lengthB, glm::vec2* vertexArrayB) {
|
||||
// Fix lines: they will come back with a length of 3, from an original of length of 2
|
||||
if ((lengthA == 2) && (lengthB == 3)) {
|
||||
// The first vertex should be copied as is.
|
||||
vertexArrayA[0] = vertexArrayB[0];
|
||||
// If the first two vertices of the "B" array are same, then collapse them down to be the 2nd vertex
|
||||
if (vertexArrayB[0].x == vertexArrayB[1].x) {
|
||||
vertexArrayA[1] = vertexArrayB[2];
|
||||
} else {
|
||||
// Otherwise the first vertex should be the same as third vertex
|
||||
vertexArrayA[1] = vertexArrayB[1];
|
||||
}
|
||||
lengthA=2;
|
||||
} else {
|
||||
// for all other polygons, then just copy the vertexArrayB to vertextArrayA for next step
|
||||
lengthA = lengthB;
|
||||
for (int i = 0; i < lengthB; i++) {
|
||||
vertexArrayA[i] = vertexArrayB[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -43,4 +43,39 @@ bool doLineSegmentsIntersect(glm::vec2 r1p1, glm::vec2 r1p2, glm::vec2 r2p1, glm
|
|||
bool isOnSegment(float xi, float yi, float xj, float yj, float xk, float yk);
|
||||
int computeDirection(float xi, float yi, float xj, float yj, float xk, float yk);
|
||||
|
||||
|
||||
typedef glm::vec2 LineSegment2[2];
|
||||
|
||||
// Polygon Clipping routines inspired by, pseudo code found here: http://www.cs.rit.edu/~icss571/clipTrans/PolyClipBack.html
|
||||
class PolygonClip {
|
||||
|
||||
public:
|
||||
static void clipToScreen(const glm::vec2* inputVertexArray, int length, glm::vec2*& outputVertexArray, int& outLength);
|
||||
|
||||
static const float TOP_OF_CLIPPING_WINDOW;
|
||||
static const float BOTTOM_OF_CLIPPING_WINDOW;
|
||||
static const float LEFT_OF_CLIPPING_WINDOW;
|
||||
static const float RIGHT_OF_CLIPPING_WINDOW;
|
||||
|
||||
static const glm::vec2 TOP_LEFT_CLIPPING_WINDOW;
|
||||
static const glm::vec2 TOP_RIGHT_CLIPPING_WINDOW;
|
||||
static const glm::vec2 BOTTOM_LEFT_CLIPPING_WINDOW;
|
||||
static const glm::vec2 BOTTOM_RIGHT_CLIPPING_WINDOW;
|
||||
|
||||
private:
|
||||
|
||||
static void sutherlandHodgmanPolygonClip(glm::vec2* inVertexArray, glm::vec2* outVertexArray,
|
||||
int inLength, int& outLength, const LineSegment2& clipBoundary);
|
||||
|
||||
static bool pointInsideBoundary(const glm::vec2& testVertex, const LineSegment2& clipBoundary);
|
||||
|
||||
static void segmentIntersectsBoundary(const glm::vec2& first, const glm::vec2& second,
|
||||
const LineSegment2& clipBoundary, glm::vec2& intersection);
|
||||
|
||||
static void appendPoint(glm::vec2 newVertex, int& outLength, glm::vec2* outVertexArray);
|
||||
|
||||
static void copyCleanArray(int& lengthA, glm::vec2* vertexArrayA, int& lengthB, glm::vec2* vertexArrayB);
|
||||
};
|
||||
|
||||
|
||||
#endif /* defined(__interface__GeometryUtil__) */
|
||||
|
|
|
@ -3,12 +3,15 @@
|
|||
// hifi
|
||||
//
|
||||
// Created by Brad Hefta-Gaub on 5/7/2013.
|
||||
//
|
||||
// Copyright (c) 2013 HighFidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#include "SceneUtils.h"
|
||||
#include <algorithm>
|
||||
|
||||
#include <glm/gtc/noise.hpp>
|
||||
|
||||
#include "SceneUtils.h"
|
||||
|
||||
void addCornersAndAxisLines(VoxelTree* tree) {
|
||||
// We want our corner voxels to be about 1/2 meter high, and our TREE_SCALE is in meters, so...
|
||||
float voxelSize = 0.5f / TREE_SCALE;
|
||||
|
|
|
@ -3,12 +3,15 @@
|
|||
// hifi
|
||||
//
|
||||
// Created by Tomáš Horáček on 6/25/13.
|
||||
//
|
||||
// Copyright (c) 2013 HighFidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#include "SquarePixelMap.h"
|
||||
#include <string.h>
|
||||
#include <algorithm>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "SquarePixelMap.h"
|
||||
|
||||
#define CHILD_COORD_X_IS_1 0x1
|
||||
#define CHILD_COORD_Y_IS_1 0x2
|
||||
|
|
|
@ -115,8 +115,8 @@ TagCompound::TagCompound(std::stringstream &ss) :
|
|||
_width(0),
|
||||
_length(0),
|
||||
_height(0),
|
||||
_blocksId(NULL),
|
||||
_blocksData(NULL)
|
||||
_blocksData(NULL),
|
||||
_blocksId(NULL)
|
||||
{
|
||||
int tagId;
|
||||
|
||||
|
|
|
@ -12,11 +12,15 @@
|
|||
|
||||
#include <glm/gtx/transform.hpp>
|
||||
|
||||
#include "ViewFrustum.h"
|
||||
#include "VoxelConstants.h"
|
||||
#include "SharedUtil.h"
|
||||
#include "Log.h"
|
||||
|
||||
#include "CoverageMap.h"
|
||||
#include "GeometryUtil.h"
|
||||
#include "ViewFrustum.h"
|
||||
#include "VoxelConstants.h"
|
||||
|
||||
|
||||
using namespace std;
|
||||
|
||||
ViewFrustum::ViewFrustum() :
|
||||
|
@ -262,6 +266,7 @@ ViewFrustum::location ViewFrustum::sphereInFrustum(const glm::vec3& center, floa
|
|||
|
||||
|
||||
ViewFrustum::location ViewFrustum::boxInFrustum(const AABox& box) const {
|
||||
|
||||
ViewFrustum::location regularResult = INSIDE;
|
||||
ViewFrustum::location keyholeResult = OUTSIDE;
|
||||
|
||||
|
@ -274,11 +279,11 @@ ViewFrustum::location ViewFrustum::boxInFrustum(const AABox& box) const {
|
|||
}
|
||||
|
||||
for(int i=0; i < 6; i++) {
|
||||
glm::vec3 normal = _planes[i].getNormal();
|
||||
glm::vec3 boxVertexP = box.getVertexP(normal);
|
||||
const glm::vec3& normal = _planes[i].getNormal();
|
||||
const glm::vec3& boxVertexP = box.getVertexP(normal);
|
||||
float planeToBoxVertexPDistance = _planes[i].distance(boxVertexP);
|
||||
|
||||
glm::vec3 boxVertexN = box.getVertexN(normal);
|
||||
const glm::vec3& boxVertexN = box.getVertexN(normal);
|
||||
float planeToBoxVertexNDistance = _planes[i].distance(boxVertexN);
|
||||
|
||||
if (planeToBoxVertexPDistance < 0) {
|
||||
|
@ -451,16 +456,22 @@ glm::vec2 ViewFrustum::projectPoint(glm::vec3 point, bool& pointInView) const {
|
|||
|
||||
const int MAX_POSSIBLE_COMBINATIONS = 43;
|
||||
|
||||
const int hullVertexLookup[MAX_POSSIBLE_COMBINATIONS][MAX_SHADOW_VERTEX_COUNT+1] = {
|
||||
const int hullVertexLookup[MAX_POSSIBLE_COMBINATIONS][MAX_PROJECTED_POLYGON_VERTEX_COUNT+1] = {
|
||||
// Number of vertices in shadow polygon for the visible faces, then a list of the index of each vertice from the AABox
|
||||
|
||||
//0
|
||||
{0}, // inside
|
||||
{4, BOTTOM_RIGHT_NEAR, BOTTOM_RIGHT_FAR, TOP_RIGHT_FAR, TOP_RIGHT_NEAR}, // right
|
||||
{4, BOTTOM_LEFT_NEAR, BOTTOM_LEFT_FAR, TOP_LEFT_FAR, TOP_LEFT_NEAR}, // left
|
||||
{4, BOTTOM_LEFT_FAR, BOTTOM_LEFT_NEAR, TOP_LEFT_NEAR, TOP_LEFT_FAR }, // left
|
||||
{0}, // n/a
|
||||
{4, BOTTOM_RIGHT_NEAR, BOTTOM_RIGHT_FAR, BOTTOM_LEFT_FAR, BOTTOM_LEFT_NEAR}, // bottom
|
||||
{6, BOTTOM_RIGHT_NEAR, TOP_RIGHT_NEAR, TOP_RIGHT_FAR, BOTTOM_RIGHT_FAR, BOTTOM_LEFT_FAR, BOTTOM_LEFT_NEAR},//bottom, right
|
||||
{6, BOTTOM_RIGHT_NEAR, BOTTOM_RIGHT_FAR, BOTTOM_LEFT_FAR, TOP_LEFT_FAR, TOP_LEFT_NEAR, BOTTOM_LEFT_NEAR},//bottom, left
|
||||
|
||||
//4
|
||||
{4, BOTTOM_RIGHT_NEAR, BOTTOM_LEFT_NEAR, BOTTOM_LEFT_FAR, BOTTOM_RIGHT_FAR}, // bottom
|
||||
//5
|
||||
{6, BOTTOM_RIGHT_NEAR, BOTTOM_LEFT_NEAR, BOTTOM_LEFT_FAR, BOTTOM_RIGHT_FAR, TOP_RIGHT_FAR, TOP_RIGHT_NEAR },//bottom, right
|
||||
{6, BOTTOM_RIGHT_NEAR, BOTTOM_LEFT_NEAR, TOP_LEFT_NEAR, TOP_LEFT_FAR, BOTTOM_LEFT_FAR, BOTTOM_RIGHT_FAR, },//bottom, left
|
||||
{0}, // n/a
|
||||
//8
|
||||
{4, TOP_RIGHT_NEAR, TOP_RIGHT_FAR, TOP_LEFT_FAR, TOP_LEFT_NEAR}, // top
|
||||
{6, TOP_RIGHT_NEAR, BOTTOM_RIGHT_NEAR, BOTTOM_RIGHT_FAR, TOP_RIGHT_FAR, TOP_LEFT_FAR, TOP_LEFT_NEAR}, // top, right
|
||||
{6, TOP_RIGHT_NEAR, TOP_RIGHT_FAR, TOP_LEFT_FAR, BOTTOM_LEFT_FAR, BOTTOM_LEFT_NEAR, TOP_LEFT_NEAR}, // top, left
|
||||
|
@ -469,32 +480,52 @@ const int hullVertexLookup[MAX_POSSIBLE_COMBINATIONS][MAX_SHADOW_VERTEX_COUNT+1]
|
|||
{0}, // n/a
|
||||
{0}, // n/a
|
||||
{0}, // n/a
|
||||
{4, BOTTOM_RIGHT_NEAR, TOP_RIGHT_NEAR, TOP_LEFT_NEAR, BOTTOM_LEFT_NEAR}, // front or near
|
||||
{6, BOTTOM_RIGHT_NEAR, BOTTOM_RIGHT_FAR, TOP_RIGHT_FAR, TOP_RIGHT_NEAR, TOP_LEFT_NEAR, BOTTOM_LEFT_NEAR}, // front, right
|
||||
{6, BOTTOM_RIGHT_NEAR, TOP_RIGHT_NEAR, TOP_LEFT_NEAR, TOP_LEFT_FAR, BOTTOM_LEFT_FAR, BOTTOM_LEFT_NEAR}, // front, left
|
||||
//16
|
||||
{4, BOTTOM_LEFT_NEAR, BOTTOM_RIGHT_NEAR, TOP_RIGHT_NEAR, TOP_LEFT_NEAR }, // front or near
|
||||
|
||||
{6, BOTTOM_LEFT_NEAR, BOTTOM_RIGHT_NEAR, BOTTOM_RIGHT_FAR, TOP_RIGHT_FAR, TOP_RIGHT_NEAR, TOP_LEFT_NEAR }, // front, right
|
||||
{6, BOTTOM_LEFT_FAR, BOTTOM_LEFT_NEAR, BOTTOM_RIGHT_NEAR, TOP_RIGHT_NEAR, TOP_LEFT_NEAR, TOP_LEFT_FAR, }, // front, left
|
||||
{0}, // n/a
|
||||
{6, BOTTOM_RIGHT_NEAR, BOTTOM_RIGHT_FAR, BOTTOM_LEFT_FAR, BOTTOM_LEFT_NEAR, TOP_LEFT_NEAR, TOP_RIGHT_NEAR}, // front,bottom
|
||||
{6, BOTTOM_LEFT_NEAR, TOP_LEFT_NEAR, TOP_RIGHT_NEAR, TOP_RIGHT_FAR, BOTTOM_RIGHT_FAR, BOTTOM_LEFT_FAR}, //front,bottom,right
|
||||
{6, BOTTOM_RIGHT_NEAR, BOTTOM_RIGHT_FAR, BOTTOM_LEFT_FAR, TOP_LEFT_FAR, TOP_LEFT_NEAR, TOP_RIGHT_NEAR}, //front,bottom,left
|
||||
//20
|
||||
{6, BOTTOM_LEFT_NEAR, BOTTOM_LEFT_FAR, BOTTOM_RIGHT_FAR, BOTTOM_RIGHT_NEAR, TOP_RIGHT_NEAR, TOP_LEFT_NEAR }, // front,bottom
|
||||
|
||||
//21
|
||||
{6, BOTTOM_LEFT_NEAR, BOTTOM_LEFT_FAR, BOTTOM_RIGHT_FAR, TOP_RIGHT_FAR, TOP_RIGHT_NEAR, TOP_LEFT_NEAR }, //front,bottom,right
|
||||
//22
|
||||
{6, BOTTOM_LEFT_FAR, BOTTOM_RIGHT_FAR, BOTTOM_RIGHT_NEAR, TOP_RIGHT_NEAR, TOP_LEFT_NEAR, TOP_LEFT_FAR }, //front,bottom,left
|
||||
{0}, // n/a
|
||||
{6, BOTTOM_RIGHT_NEAR, TOP_RIGHT_NEAR, TOP_RIGHT_FAR, TOP_LEFT_FAR, TOP_LEFT_NEAR, BOTTOM_LEFT_NEAR}, // front, top
|
||||
{6, BOTTOM_RIGHT_NEAR, BOTTOM_RIGHT_FAR, TOP_RIGHT_FAR, TOP_LEFT_FAR, TOP_LEFT_NEAR, BOTTOM_LEFT_NEAR}, // front, top, right
|
||||
{6, BOTTOM_RIGHT_NEAR, TOP_RIGHT_NEAR, TOP_RIGHT_FAR, TOP_LEFT_FAR, BOTTOM_LEFT_FAR, BOTTOM_LEFT_NEAR}, // front, top, left
|
||||
|
||||
{6, BOTTOM_LEFT_NEAR, BOTTOM_RIGHT_NEAR, TOP_RIGHT_NEAR, TOP_RIGHT_FAR, TOP_LEFT_FAR, TOP_LEFT_NEAR}, // front, top
|
||||
|
||||
{6, BOTTOM_LEFT_NEAR, BOTTOM_RIGHT_NEAR, BOTTOM_RIGHT_FAR, TOP_RIGHT_FAR, TOP_LEFT_FAR, TOP_LEFT_NEAR }, // front, top, right
|
||||
|
||||
{6, BOTTOM_LEFT_FAR, BOTTOM_LEFT_NEAR, BOTTOM_RIGHT_NEAR, TOP_RIGHT_NEAR, TOP_RIGHT_FAR, TOP_LEFT_FAR }, // front, top, left
|
||||
{0}, // n/a
|
||||
{0}, // n/a
|
||||
{0}, // n/a
|
||||
{0}, // n/a
|
||||
{0}, // n/a
|
||||
{4, BOTTOM_RIGHT_FAR, TOP_RIGHT_FAR, TOP_LEFT_FAR, BOTTOM_LEFT_FAR}, // back
|
||||
{6, BOTTOM_RIGHT_NEAR, TOP_RIGHT_NEAR, TOP_RIGHT_FAR, TOP_LEFT_FAR, BOTTOM_LEFT_FAR, BOTTOM_RIGHT_FAR}, // back, right
|
||||
{6, BOTTOM_RIGHT_FAR, TOP_RIGHT_FAR, TOP_LEFT_FAR, TOP_LEFT_NEAR, BOTTOM_LEFT_NEAR, BOTTOM_LEFT_FAR}, // back, left
|
||||
//32
|
||||
{4, BOTTOM_RIGHT_FAR, BOTTOM_LEFT_FAR, TOP_LEFT_FAR, TOP_RIGHT_FAR }, // back
|
||||
{6, BOTTOM_RIGHT_NEAR, BOTTOM_RIGHT_FAR, BOTTOM_LEFT_FAR, TOP_LEFT_FAR, TOP_RIGHT_FAR, TOP_RIGHT_NEAR}, // back, right
|
||||
//34
|
||||
{6, BOTTOM_RIGHT_FAR, BOTTOM_LEFT_FAR, BOTTOM_LEFT_NEAR, TOP_LEFT_NEAR, TOP_LEFT_FAR, TOP_RIGHT_FAR }, // back, left
|
||||
|
||||
|
||||
{0}, // n/a
|
||||
{6, BOTTOM_RIGHT_NEAR, BOTTOM_RIGHT_FAR, TOP_RIGHT_FAR, TOP_LEFT_FAR, BOTTOM_LEFT_FAR, BOTTOM_LEFT_NEAR}, // back, bottom
|
||||
{6, BOTTOM_RIGHT_NEAR, TOP_RIGHT_NEAR, TOP_RIGHT_FAR, TOP_LEFT_FAR, BOTTOM_LEFT_FAR, BOTTOM_LEFT_NEAR},//back, bottom, right
|
||||
{6, BOTTOM_RIGHT_NEAR, BOTTOM_RIGHT_FAR, TOP_RIGHT_FAR, TOP_LEFT_FAR, TOP_LEFT_NEAR, BOTTOM_LEFT_NEAR},//back, bottom, left
|
||||
//36
|
||||
{6, BOTTOM_RIGHT_NEAR, BOTTOM_LEFT_NEAR, BOTTOM_LEFT_FAR, TOP_LEFT_FAR, TOP_RIGHT_FAR, BOTTOM_RIGHT_FAR}, // back, bottom
|
||||
{6, BOTTOM_RIGHT_NEAR, BOTTOM_LEFT_NEAR, BOTTOM_LEFT_FAR, TOP_LEFT_FAR, TOP_RIGHT_FAR, TOP_RIGHT_NEAR},//back, bottom, right
|
||||
|
||||
// 38
|
||||
{6, BOTTOM_RIGHT_NEAR, BOTTOM_LEFT_NEAR, TOP_LEFT_NEAR, TOP_LEFT_FAR, TOP_RIGHT_FAR, BOTTOM_RIGHT_FAR },//back, bottom, left
|
||||
{0}, // n/a
|
||||
{6, BOTTOM_RIGHT_FAR, TOP_RIGHT_FAR, TOP_RIGHT_NEAR, TOP_LEFT_NEAR, TOP_LEFT_FAR, BOTTOM_LEFT_FAR}, // back, top
|
||||
|
||||
// 40
|
||||
{6, BOTTOM_RIGHT_FAR, BOTTOM_LEFT_FAR, TOP_LEFT_FAR, TOP_LEFT_NEAR, TOP_RIGHT_NEAR, TOP_RIGHT_FAR}, // back, top
|
||||
|
||||
{6, BOTTOM_RIGHT_NEAR, BOTTOM_RIGHT_FAR, BOTTOM_LEFT_FAR, TOP_LEFT_FAR, TOP_LEFT_NEAR, TOP_RIGHT_NEAR}, // back, top, right
|
||||
//42
|
||||
{6, TOP_RIGHT_NEAR, TOP_RIGHT_FAR, BOTTOM_RIGHT_FAR, BOTTOM_LEFT_FAR, BOTTOM_LEFT_NEAR, TOP_LEFT_NEAR}, // back, top, left
|
||||
};
|
||||
|
||||
|
@ -508,9 +539,11 @@ VoxelProjectedPolygon ViewFrustum::getProjectedPolygon(const AABox& box) const {
|
|||
+ ((_position.z < bottomNearRight.z) << 4) // 16 = front/near | the 6 defining
|
||||
+ ((_position.z > topFarLeft.z ) << 5); // 32 = back/far | planes
|
||||
|
||||
int vertexCount = hullVertexLookup[lookUp][0]; //look up number of vertices
|
||||
//printLog(">>>>>>>>> ViewFrustum::getProjectedPolygon() lookup=%d\n",lookUp);
|
||||
|
||||
VoxelProjectedPolygon shadow(vertexCount);
|
||||
int vertexCount = hullVertexLookup[lookUp][0]; //look up number of vertices
|
||||
|
||||
VoxelProjectedPolygon projectedPolygon(vertexCount);
|
||||
|
||||
bool pointInView = true;
|
||||
bool allPointsInView = false; // assume the best, but wait till we know we have a vertex
|
||||
|
@ -523,13 +556,40 @@ VoxelProjectedPolygon ViewFrustum::getProjectedPolygon(const AABox& box) const {
|
|||
glm::vec2 projectedPoint = projectPoint(point, pointInView);
|
||||
allPointsInView = allPointsInView && pointInView;
|
||||
anyPointsInView = anyPointsInView || pointInView;
|
||||
shadow.setVertex(i, projectedPoint);
|
||||
projectedPolygon.setVertex(i, projectedPoint);
|
||||
}
|
||||
|
||||
/***
|
||||
// Now that we've got the polygon, if it extends beyond the clipping window, then let's clip it
|
||||
// NOTE: This clipping does not improve our overall performance. It basically causes more polygons to
|
||||
// end up in the same quad/half and so the polygon lists get longer, and that's more calls to polygon.occludes()
|
||||
if ( (projectedPolygon.getMaxX() > PolygonClip::RIGHT_OF_CLIPPING_WINDOW ) ||
|
||||
(projectedPolygon.getMaxY() > PolygonClip::TOP_OF_CLIPPING_WINDOW ) ||
|
||||
(projectedPolygon.getMaxX() < PolygonClip::LEFT_OF_CLIPPING_WINDOW ) ||
|
||||
(projectedPolygon.getMaxY() < PolygonClip::BOTTOM_OF_CLIPPING_WINDOW) ) {
|
||||
|
||||
CoverageRegion::_clippedPolygons++;
|
||||
|
||||
glm::vec2* clippedVertices;
|
||||
int clippedVertexCount;
|
||||
PolygonClip::clipToScreen(projectedPolygon.getVertices(), vertexCount, clippedVertices, clippedVertexCount);
|
||||
|
||||
// Now reset the vertices of our projectedPolygon object
|
||||
projectedPolygon.setVertexCount(clippedVertexCount);
|
||||
for(int i = 0; i < clippedVertexCount; i++) {
|
||||
projectedPolygon.setVertex(i, clippedVertices[i]);
|
||||
}
|
||||
delete[] clippedVertices;
|
||||
|
||||
lookUp += PROJECTION_CLIPPED;
|
||||
}
|
||||
***/
|
||||
}
|
||||
// set the distance from our camera position, to the closest vertex
|
||||
float distance = glm::distance(getPosition(), box.getCenter());
|
||||
shadow.setDistance(distance);
|
||||
shadow.setAnyInView(anyPointsInView);
|
||||
shadow.setAllInView(allPointsInView);
|
||||
return shadow;
|
||||
projectedPolygon.setDistance(distance);
|
||||
projectedPolygon.setAnyInView(anyPointsInView);
|
||||
projectedPolygon.setAllInView(allPointsInView);
|
||||
projectedPolygon.setProjectionType(lookUp); // remember the projection type
|
||||
return projectedPolygon;
|
||||
}
|
||||
|
|
|
@ -34,7 +34,7 @@ const int COLOR_VALUES_PER_VOXEL = NUMBER_OF_COLORS * VERTICES_PER_VOXEL;
|
|||
typedef unsigned long int glBufferIndex;
|
||||
const glBufferIndex GLBUFFER_INDEX_UNKNOWN = ULONG_MAX;
|
||||
|
||||
const double SIXTY_FPS_IN_MILLISECONDS = 1000.0/60;
|
||||
const double VIEW_CULLING_RATE_IN_MILLISECONDS = 1000.0; // once a second is fine
|
||||
const float SIXTY_FPS_IN_MILLISECONDS = 1000.0f / 60.0f;
|
||||
const float VIEW_CULLING_RATE_IN_MILLISECONDS = 1000.0f; // once a second is fine
|
||||
|
||||
#endif
|
||||
|
|
|
@ -29,7 +29,7 @@ private:
|
|||
#endif
|
||||
glBufferIndex _glBufferIndex;
|
||||
bool _isDirty;
|
||||
long long _lastChanged;
|
||||
uint64_t _lastChanged;
|
||||
bool _shouldRender;
|
||||
bool _isStagedForDeletion;
|
||||
AABox _box;
|
||||
|
@ -62,7 +62,7 @@ public:
|
|||
const glm::vec3& getCenter() const { return _box.getCenter(); };
|
||||
const glm::vec3& getCorner() const { return _box.getCorner(); };
|
||||
float getScale() const { return _box.getSize().x; /* voxelScale = (1 / powf(2, *node->getOctalCode())); */ };
|
||||
int getLevel() const { return *_octalCode + 1; /* one based or zero based? */ };
|
||||
int getLevel() const { return *_octalCode + 1; /* one based or zero based? this doesn't correctly handle 2 byte case */ };
|
||||
|
||||
float getEnclosingRadius() const;
|
||||
|
||||
|
@ -80,7 +80,7 @@ public:
|
|||
void printDebugDetails(const char* label) const;
|
||||
bool isDirty() const { return _isDirty; };
|
||||
void clearDirtyBit() { _isDirty = false; };
|
||||
bool hasChangedSince(long long time) const { return (_lastChanged > time); };
|
||||
bool hasChangedSince(uint64_t time) const { return (_lastChanged > time); };
|
||||
void markWithChangedTime() { _lastChanged = usecTimestampNow(); };
|
||||
void handleSubtreeChanged(VoxelTree* myTree);
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -10,29 +10,58 @@
|
|||
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
const int MAX_SHADOW_VERTEX_COUNT = 6;
|
||||
|
||||
typedef glm::vec2 ShadowVertices[MAX_SHADOW_VERTEX_COUNT];
|
||||
// there's a max of 6 vertices of a project polygon, and a max of twice that when clipped to the screen
|
||||
const int MAX_PROJECTED_POLYGON_VERTEX_COUNT = 6;
|
||||
const int MAX_CLIPPED_PROJECTED_POLYGON_VERTEX_COUNT = MAX_PROJECTED_POLYGON_VERTEX_COUNT * 2;
|
||||
typedef glm::vec2 ProjectedVertices[MAX_CLIPPED_PROJECTED_POLYGON_VERTEX_COUNT];
|
||||
|
||||
class BoundingBox {
|
||||
public:
|
||||
BoundingBox(glm::vec2 corner, glm::vec2 size) : corner(corner), size(size) {};
|
||||
enum { BOTTOM_LEFT, BOTTOM_RIGHT, TOP_RIGHT, TOP_LEFT, VERTEX_COUNT };
|
||||
|
||||
BoundingBox(glm::vec2 corner, glm::vec2 size) : corner(corner), size(size), _set(true) {};
|
||||
BoundingBox() : _set(false) {};
|
||||
glm::vec2 corner;
|
||||
glm::vec2 size;
|
||||
bool contains(const BoundingBox& box) const;
|
||||
bool contains(const glm::vec2& point) const;
|
||||
bool pointInside(const glm::vec2& point) const { return contains(point); };
|
||||
|
||||
void explandToInclude(const BoundingBox& box);
|
||||
|
||||
float area() const { return size.x * size.y; };
|
||||
|
||||
int getVertexCount() const { return VERTEX_COUNT; };
|
||||
glm::vec2 getVertex(int vertexNumber) const;
|
||||
|
||||
BoundingBox topHalf() const;
|
||||
BoundingBox bottomHalf() const;
|
||||
BoundingBox leftHalf() const;
|
||||
BoundingBox rightHalf() const;
|
||||
|
||||
float getMaxX() const { return corner.x + size.x; }
|
||||
float getMaxY() const { return corner.y + size.y; }
|
||||
float getMinX() const { return corner.x; }
|
||||
float getMinY() const { return corner.y; }
|
||||
|
||||
void printDebugDetails(const char* label=NULL) const;
|
||||
private:
|
||||
bool _set;
|
||||
};
|
||||
|
||||
const int PROJECTION_RIGHT = 1;
|
||||
const int PROJECTION_LEFT = 2;
|
||||
const int PROJECTION_BOTTOM = 4;
|
||||
const int PROJECTION_TOP = 8;
|
||||
const int PROJECTION_NEAR = 16;
|
||||
const int PROJECTION_FAR = 32;
|
||||
const int PROJECTION_CLIPPED = 64;
|
||||
|
||||
class VoxelProjectedPolygon {
|
||||
|
||||
public:
|
||||
VoxelProjectedPolygon(const BoundingBox& box);
|
||||
|
||||
VoxelProjectedPolygon(int vertexCount = 0) :
|
||||
_vertexCount(vertexCount),
|
||||
_maxX(-FLT_MAX), _maxY(-FLT_MAX), _minX(FLT_MAX), _minY(FLT_MAX),
|
||||
|
@ -40,22 +69,33 @@ public:
|
|||
{ };
|
||||
|
||||
~VoxelProjectedPolygon() { };
|
||||
const ShadowVertices& getVerices() const { return _vertices; };
|
||||
const ProjectedVertices& getVertices() const { return _vertices; };
|
||||
const glm::vec2& getVertex(int i) const { return _vertices[i]; };
|
||||
void setVertex(int vertex, const glm::vec2& point);
|
||||
int getVertexCount() const { return _vertexCount; };
|
||||
void setVertexCount(int vertexCount) { _vertexCount = vertexCount; };
|
||||
|
||||
float getDistance() const { return _distance; }
|
||||
void setDistance(float distance) { _distance = distance; }
|
||||
int getVertexCount() const { return _vertexCount; };
|
||||
void setVertexCount(int vertexCount) { _vertexCount = vertexCount; };
|
||||
float getDistance() const { return _distance; }
|
||||
void setDistance(float distance) { _distance = distance; }
|
||||
bool getAnyInView() const { return _anyInView; };
|
||||
void setAnyInView(bool anyInView) { _anyInView = anyInView; };
|
||||
bool getAllInView() const { return _allInView; };
|
||||
void setAllInView(bool allInView) { _allInView = allInView; };
|
||||
void setProjectionType(unsigned char type) { _projectionType = type; };
|
||||
unsigned char getProjectionType() const { return _projectionType; };
|
||||
|
||||
bool getAnyInView() const { return _anyInView; };
|
||||
void setAnyInView(bool anyInView) { _anyInView = anyInView; };
|
||||
bool getAllInView() const { return _allInView; };
|
||||
void setAllInView(bool allInView) { _allInView = allInView; };
|
||||
|
||||
bool pointInside(const glm::vec2& point, bool* matchesVertex = NULL) const;
|
||||
bool occludes(const VoxelProjectedPolygon& occludee, bool checkAllInView = false) const;
|
||||
bool pointInside(const glm::vec2& point) const;
|
||||
bool occludes(const BoundingBox& occludee) const;
|
||||
bool intersects(const VoxelProjectedPolygon& testee) const;
|
||||
bool intersects(const BoundingBox& box) const;
|
||||
bool matches(const VoxelProjectedPolygon& testee) const;
|
||||
bool matches(const BoundingBox& testee) const;
|
||||
bool intersectsOnAxes(const VoxelProjectedPolygon& testee) const;
|
||||
|
||||
bool canMerge(const VoxelProjectedPolygon& that) const;
|
||||
void merge(const VoxelProjectedPolygon& that); // replaces vertices of this with new merged version
|
||||
|
||||
float getMaxX() const { return _maxX; }
|
||||
float getMaxY() const { return _maxY; }
|
||||
|
@ -67,10 +107,14 @@ public:
|
|||
};
|
||||
|
||||
void printDebugDetails() const;
|
||||
|
||||
static long pointInside_calls;
|
||||
static long occludes_calls;
|
||||
static long intersects_calls;
|
||||
|
||||
private:
|
||||
int _vertexCount;
|
||||
ShadowVertices _vertices;
|
||||
ProjectedVertices _vertices;
|
||||
float _maxX;
|
||||
float _maxY;
|
||||
float _minX;
|
||||
|
@ -78,6 +122,7 @@ private:
|
|||
float _distance;
|
||||
bool _anyInView; // if any points are in view
|
||||
bool _allInView; // if all points are in view
|
||||
unsigned char _projectionType;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -29,12 +29,16 @@
|
|||
|
||||
#include <glm/gtc/noise.hpp>
|
||||
|
||||
|
||||
int boundaryDistanceForRenderLevel(unsigned int renderLevel) {
|
||||
float voxelSizeScale = 50000.0f;
|
||||
float boundaryDistanceForRenderLevel(unsigned int renderLevel) {
|
||||
const float voxelSizeScale = 50000.0f;
|
||||
return voxelSizeScale / powf(2, renderLevel);
|
||||
}
|
||||
|
||||
float boundaryDistanceSquaredForRenderLevel(unsigned int renderLevel) {
|
||||
const float voxelSizeScale = (50000.0f/TREE_SCALE) * (50000.0f/TREE_SCALE);
|
||||
return voxelSizeScale / powf(2, (2 * renderLevel));
|
||||
}
|
||||
|
||||
VoxelTree::VoxelTree(bool shouldReaverage) :
|
||||
voxelsCreated(0),
|
||||
voxelsColored(0),
|
||||
|
@ -55,6 +59,61 @@ VoxelTree::~VoxelTree() {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
void VoxelTree::recurseTreeWithOperationDistanceSortedTimed(PointerStack* stackOfNodes, long allowedTime,
|
||||
RecurseVoxelTreeOperation operation,
|
||||
const glm::vec3& point, void* extraData) {
|
||||
|
||||
long long start = usecTimestampNow();
|
||||
|
||||
// start case, stack empty, so start with root...
|
||||
if (stackOfNodes->empty()) {
|
||||
stackOfNodes->push(rootNode);
|
||||
}
|
||||
while (!stackOfNodes->empty()) {
|
||||
VoxelNode* node = (VoxelNode*)stackOfNodes->top();
|
||||
stackOfNodes->pop();
|
||||
|
||||
if (operation(node, extraData)) {
|
||||
|
||||
//sortChildren... CLOSEST to FURTHEST
|
||||
// determine the distance sorted order of our children
|
||||
VoxelNode* sortedChildren[NUMBER_OF_CHILDREN];
|
||||
float distancesToChildren[NUMBER_OF_CHILDREN];
|
||||
int indexOfChildren[NUMBER_OF_CHILDREN]; // not really needed
|
||||
int currentCount = 0;
|
||||
|
||||
for (int i = 0; i < NUMBER_OF_CHILDREN; i++) {
|
||||
VoxelNode* childNode = node->getChildAtIndex(i);
|
||||
if (childNode) {
|
||||
// chance to optimize, doesn't need to be actual distance!! Could be distance squared
|
||||
float distanceSquared = childNode->distanceSquareToPoint(point);
|
||||
currentCount = insertIntoSortedArrays((void*)childNode, distanceSquared, i,
|
||||
(void**)&sortedChildren, (float*)&distancesToChildren,
|
||||
(int*)&indexOfChildren, currentCount, NUMBER_OF_CHILDREN);
|
||||
}
|
||||
}
|
||||
|
||||
//iterate sorted children FURTHEST to CLOSEST
|
||||
for (int i = currentCount-1; i >= 0; i--) {
|
||||
VoxelNode* child = sortedChildren[i];
|
||||
stackOfNodes->push(child);
|
||||
}
|
||||
}
|
||||
|
||||
// at this point, we can check to see if we should bail for timing reasons
|
||||
// because if we bail at this point, then reenter the while, we will basically
|
||||
// be back to processing the stack from same place we left off, and all can proceed normally
|
||||
long long now = usecTimestampNow();
|
||||
long elapsedTime = now - start;
|
||||
|
||||
if (elapsedTime > allowedTime) {
|
||||
return; // caller responsible for calling us again to finish the job!
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Recurses voxel tree calling the RecurseVoxelTreeOperation function for each node.
|
||||
// stops recursion if operation function returns false.
|
||||
void VoxelTree::recurseTreeWithOperation(RecurseVoxelTreeOperation operation, void* extraData) {
|
||||
|
@ -62,7 +121,7 @@ void VoxelTree::recurseTreeWithOperation(RecurseVoxelTreeOperation operation, vo
|
|||
}
|
||||
|
||||
// Recurses voxel node with an operation function
|
||||
void VoxelTree::recurseNodeWithOperation(VoxelNode* node,RecurseVoxelTreeOperation operation, void* extraData) {
|
||||
void VoxelTree::recurseNodeWithOperation(VoxelNode* node, RecurseVoxelTreeOperation operation, void* extraData) {
|
||||
if (operation(node, extraData)) {
|
||||
for (int i = 0; i < NUMBER_OF_CHILDREN; i++) {
|
||||
VoxelNode* child = node->getChildAtIndex(i);
|
||||
|
@ -77,15 +136,15 @@ void VoxelTree::recurseNodeWithOperation(VoxelNode* node,RecurseVoxelTreeOperati
|
|||
// stops recursion if operation function returns false.
|
||||
void VoxelTree::recurseTreeWithOperationDistanceSorted(RecurseVoxelTreeOperation operation,
|
||||
const glm::vec3& point, void* extraData) {
|
||||
|
||||
recurseNodeWithOperationDistanceSorted(rootNode, operation, point, extraData);
|
||||
}
|
||||
|
||||
// Recurses voxel node with an operation function
|
||||
void VoxelTree::recurseNodeWithOperationDistanceSorted(VoxelNode* node, RecurseVoxelTreeOperation operation,
|
||||
void VoxelTree::recurseNodeWithOperationDistanceSorted(VoxelNode* node, RecurseVoxelTreeOperation operation,
|
||||
const glm::vec3& point, void* extraData) {
|
||||
if (operation(node, extraData)) {
|
||||
// determine the distance sorted order of our children
|
||||
|
||||
VoxelNode* sortedChildren[NUMBER_OF_CHILDREN];
|
||||
float distancesToChildren[NUMBER_OF_CHILDREN];
|
||||
int indexOfChildren[NUMBER_OF_CHILDREN]; // not really needed
|
||||
|
@ -537,7 +596,7 @@ void VoxelTree::readCodeColorBufferToTreeRecursion(VoxelNode* node, void* extraD
|
|||
|
||||
void VoxelTree::processRemoveVoxelBitstream(unsigned char * bitstream, int bufferSizeBytes) {
|
||||
//unsigned short int itemNumber = (*((unsigned short int*)&bitstream[sizeof(PACKET_HEADER)]));
|
||||
int atByte = sizeof(short int) + sizeof(PACKET_HEADER);
|
||||
int atByte = sizeof(short int) + numBytesForPacketHeader(bitstream);
|
||||
unsigned char* voxelCode = (unsigned char*)&bitstream[atByte];
|
||||
while (atByte < bufferSizeBytes) {
|
||||
int codeLength = numberOfThreeBitSectionsInCode(voxelCode);
|
||||
|
@ -1099,6 +1158,8 @@ int VoxelTree::encodeTreeBitstreamRecursion(VoxelNode* node, unsigned char* outp
|
|||
|
||||
// Keep track of how deep we've encoded.
|
||||
currentEncodeLevel++;
|
||||
|
||||
params.maxLevelReached = std::max(currentEncodeLevel,params.maxLevelReached);
|
||||
|
||||
// If we've reached our max Search Level, then stop searching.
|
||||
if (currentEncodeLevel >= params.maxEncodeLevel) {
|
||||
|
@ -1108,7 +1169,7 @@ int VoxelTree::encodeTreeBitstreamRecursion(VoxelNode* node, unsigned char* outp
|
|||
// caller can pass NULL as viewFrustum if they want everything
|
||||
if (params.viewFrustum) {
|
||||
float distance = node->distanceToCamera(*params.viewFrustum);
|
||||
float boundaryDistance = boundaryDistanceForRenderLevel(*node->getOctalCode() + 1);
|
||||
float boundaryDistance = boundaryDistanceForRenderLevel(node->getLevel() + params.boundaryLevelAdjust);
|
||||
|
||||
// If we're too far away for our render level, then just return
|
||||
if (distance >= boundaryDistance) {
|
||||
|
@ -1121,7 +1182,26 @@ int VoxelTree::encodeTreeBitstreamRecursion(VoxelNode* node, unsigned char* outp
|
|||
if (!node->isInView(*params.viewFrustum)) {
|
||||
return bytesAtThisLevel;
|
||||
}
|
||||
|
||||
// Ok, we are in view, but if we're in delta mode, then we also want to make sure we weren't already in view
|
||||
// because we don't send nodes from the previously know in view frustum.
|
||||
bool wasInView = false;
|
||||
|
||||
if (params.deltaViewFrustum && params.lastViewFrustum) {
|
||||
ViewFrustum::location location = node->inFrustum(*params.lastViewFrustum);
|
||||
|
||||
// If we're a leaf, then either intersect or inside is considered "formerly in view"
|
||||
if (node->isLeaf()) {
|
||||
wasInView = location != ViewFrustum::OUTSIDE;
|
||||
} else {
|
||||
wasInView = location == ViewFrustum::INSIDE;
|
||||
}
|
||||
}
|
||||
|
||||
// If we were in view, then bail out early!
|
||||
if (wasInView) {
|
||||
return bytesAtThisLevel;
|
||||
}
|
||||
|
||||
// If the user also asked for occlusion culling, check if this node is occluded, but only if it's not a leaf.
|
||||
// leaf occlusion is handled down below when we check child nodes
|
||||
|
@ -1139,16 +1219,9 @@ int VoxelTree::encodeTreeBitstreamRecursion(VoxelNode* node, unsigned char* outp
|
|||
CoverageMapStorageResult result = params.map->checkMap(voxelPolygon, false);
|
||||
delete voxelPolygon; // cleanup
|
||||
if (result == OCCLUDED) {
|
||||
//node->printDebugDetails("upper section, non-Leaf is occluded!! node=");
|
||||
//args->nonLeavesOccluded++;
|
||||
|
||||
//args->subtreeVoxelsSkipped += (subArgs.voxelsTouched - 1);
|
||||
//args->totalVoxels += (subArgs.voxelsTouched - 1);
|
||||
|
||||
return bytesAtThisLevel;
|
||||
}
|
||||
} else {
|
||||
//node->printDebugDetails("upper section, shadow Not in view node=");
|
||||
// If this shadow wasn't "all in view" then we ignored it for occlusion culling, but
|
||||
// we do need to clean up memory and proceed as normal...
|
||||
delete voxelPolygon;
|
||||
|
@ -1224,7 +1297,8 @@ int VoxelTree::encodeTreeBitstreamRecursion(VoxelNode* node, unsigned char* outp
|
|||
if (childIsInView) {
|
||||
// Before we determine consider this further, let's see if it's in our LOD scope...
|
||||
float distance = distancesToChildren[i]; // params.viewFrustum ? childNode->distanceToCamera(*params.viewFrustum) : 0;
|
||||
float boundaryDistance = params.viewFrustum ? boundaryDistanceForRenderLevel(*childNode->getOctalCode() + 1) : 1;
|
||||
float boundaryDistance = !params.viewFrustum ? 1 :
|
||||
boundaryDistanceForRenderLevel(childNode->getLevel() + params.boundaryLevelAdjust);
|
||||
|
||||
if (distance < boundaryDistance) {
|
||||
inViewCount++;
|
||||
|
@ -1270,44 +1344,40 @@ int VoxelTree::encodeTreeBitstreamRecursion(VoxelNode* node, unsigned char* outp
|
|||
} // wants occlusion culling & isLeaf()
|
||||
|
||||
|
||||
bool childWasInView = (childNode && params.deltaViewFrustum &&
|
||||
|
||||
(params.lastViewFrustum && ViewFrustum::INSIDE == childNode->inFrustum(*params.lastViewFrustum)));
|
||||
|
||||
// There are two types of nodes for which we want to send colors:
|
||||
// 1) Leaves - obviously
|
||||
// 2) Non-leaves who's children would be visible and beyond our LOD.
|
||||
// NOTE: This code works, but it's pretty expensive, because we're calculating distances for all the grand
|
||||
// children, which we'll end up doing again later in the next level of recursion. We need to optimize this
|
||||
// in the future.
|
||||
// 2) Non-leaves who's children would be visible but are beyond our LOD.
|
||||
bool isLeafOrLOD = childNode->isLeaf();
|
||||
if (params.viewFrustum && childNode->isColored() && !childNode->isLeaf()) {
|
||||
int grandChildrenInView = 0;
|
||||
int grandChildrenInLOD = 0;
|
||||
for (int grandChildIndex = 0; grandChildIndex < NUMBER_OF_CHILDREN; grandChildIndex++) {
|
||||
VoxelNode* grandChild = childNode->getChildAtIndex(grandChildIndex);
|
||||
|
||||
if (grandChild && grandChild->isColored() && grandChild->isInView(*params.viewFrustum)) {
|
||||
grandChildrenInView++;
|
||||
|
||||
float grandChildDistance = grandChild->distanceToCamera(*params.viewFrustum);
|
||||
float grandChildBoundaryDistance = boundaryDistanceForRenderLevel(grandChild->getLevel() + 1);
|
||||
if (grandChildDistance < grandChildBoundaryDistance) {
|
||||
grandChildrenInLOD++;
|
||||
}
|
||||
}
|
||||
}
|
||||
// if any of our grandchildren ARE in view, then we don't want to include our color. If none are, then
|
||||
// we do want to include our color
|
||||
if (grandChildrenInView > 0 && grandChildrenInLOD==0) {
|
||||
isLeafOrLOD = true;
|
||||
}
|
||||
int childLevel = childNode->getLevel();
|
||||
float childBoundary = boundaryDistanceForRenderLevel(childLevel + params.boundaryLevelAdjust);
|
||||
float grandChildBoundary = boundaryDistanceForRenderLevel(childLevel + 1 + params.boundaryLevelAdjust);
|
||||
isLeafOrLOD = ((distance <= childBoundary) && !(distance <= grandChildBoundary));
|
||||
}
|
||||
|
||||
// track children with actual color, only if the child wasn't previously in view!
|
||||
if (childNode && isLeafOrLOD && childNode->isColored() && !childWasInView && !childIsOccluded) {
|
||||
childrenColoredBits += (1 << (7 - originalIndex));
|
||||
inViewWithColorCount++;
|
||||
if (childNode && isLeafOrLOD && childNode->isColored() && !childIsOccluded) {
|
||||
bool childWasInView = false;
|
||||
|
||||
if (childNode && params.deltaViewFrustum && params.lastViewFrustum) {
|
||||
ViewFrustum::location location = childNode->inFrustum(*params.lastViewFrustum);
|
||||
|
||||
// If we're a leaf, then either intersect or inside is considered "formerly in view"
|
||||
if (childNode->isLeaf()) {
|
||||
childWasInView = location != ViewFrustum::OUTSIDE;
|
||||
} else {
|
||||
childWasInView = location == ViewFrustum::INSIDE;
|
||||
}
|
||||
}
|
||||
|
||||
// If our child wasn't in view (or we're ignoring wasInView) then we add it to our sending items
|
||||
if (!childWasInView) {
|
||||
childrenColoredBits += (1 << (7 - originalIndex));
|
||||
inViewWithColorCount++;
|
||||
} else {
|
||||
// otherwise just track stats of the items we discarded
|
||||
params.childWasInViewDiscarded++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
#include "VoxelNode.h"
|
||||
#include "VoxelNodeBag.h"
|
||||
#include "CoverageMap.h"
|
||||
#include "PointerStack.h"
|
||||
|
||||
// Callback function, for recuseTreeWithOperation
|
||||
typedef bool (*RecurseVoxelTreeOperation)(VoxelNode* node, void* extraData);
|
||||
|
@ -32,10 +33,13 @@ typedef enum {GRADIENT, RANDOM, NATURAL} creationMode;
|
|||
#define WANT_OCCLUSION_CULLING true
|
||||
#define IGNORE_COVERAGE_MAP NULL
|
||||
#define DONT_CHOP 0
|
||||
#define NO_BOUNDARY_ADJUST 0
|
||||
#define LOW_RES_MOVING_ADJUST 1
|
||||
|
||||
class EncodeBitstreamParams {
|
||||
public:
|
||||
int maxEncodeLevel;
|
||||
int maxLevelReached;
|
||||
const ViewFrustum* viewFrustum;
|
||||
bool includeColor;
|
||||
bool includeExistsBits;
|
||||
|
@ -43,28 +47,34 @@ public:
|
|||
bool deltaViewFrustum;
|
||||
const ViewFrustum* lastViewFrustum;
|
||||
bool wantOcclusionCulling;
|
||||
long childWasInViewDiscarded;
|
||||
int boundaryLevelAdjust;
|
||||
|
||||
CoverageMap* map;
|
||||
|
||||
EncodeBitstreamParams(
|
||||
int maxEncodeLevel = INT_MAX,
|
||||
const ViewFrustum* viewFrustum = IGNORE_VIEW_FRUSTUM,
|
||||
const ViewFrustum* viewFrustum = IGNORE_VIEW_FRUSTUM,
|
||||
bool includeColor = WANT_COLOR,
|
||||
bool includeExistsBits = WANT_EXISTS_BITS,
|
||||
int chopLevels = 0,
|
||||
bool deltaViewFrustum = false,
|
||||
const ViewFrustum* lastViewFrustum = IGNORE_VIEW_FRUSTUM,
|
||||
bool wantOcclusionCulling= NO_OCCLUSION_CULLING,
|
||||
CoverageMap* map = IGNORE_COVERAGE_MAP) :
|
||||
|
||||
maxEncodeLevel (maxEncodeLevel),
|
||||
viewFrustum (viewFrustum),
|
||||
includeColor (includeColor),
|
||||
includeExistsBits (includeExistsBits),
|
||||
chopLevels (chopLevels),
|
||||
deltaViewFrustum (deltaViewFrustum),
|
||||
lastViewFrustum (lastViewFrustum),
|
||||
wantOcclusionCulling(wantOcclusionCulling),
|
||||
map (map)
|
||||
CoverageMap* map = IGNORE_COVERAGE_MAP,
|
||||
int boundaryLevelAdjust = NO_BOUNDARY_ADJUST) :
|
||||
maxEncodeLevel (maxEncodeLevel),
|
||||
maxLevelReached (0),
|
||||
viewFrustum (viewFrustum),
|
||||
includeColor (includeColor),
|
||||
includeExistsBits (includeExistsBits),
|
||||
chopLevels (chopLevels),
|
||||
deltaViewFrustum (deltaViewFrustum),
|
||||
lastViewFrustum (lastViewFrustum),
|
||||
wantOcclusionCulling (wantOcclusionCulling),
|
||||
childWasInViewDiscarded (0),
|
||||
boundaryLevelAdjust (boundaryLevelAdjust),
|
||||
map (map)
|
||||
{}
|
||||
};
|
||||
|
||||
|
@ -148,6 +158,11 @@ public:
|
|||
void recurseNodeWithOperation(VoxelNode* node, RecurseVoxelTreeOperation operation, void* extraData);
|
||||
void recurseNodeWithOperationDistanceSorted(VoxelNode* node, RecurseVoxelTreeOperation operation,
|
||||
const glm::vec3& point, void* extraData);
|
||||
|
||||
|
||||
void recurseTreeWithOperationDistanceSortedTimed(PointerStack* stackOfNodes, long allowedTime,
|
||||
RecurseVoxelTreeOperation operation,
|
||||
const glm::vec3& point, void* extraData);
|
||||
|
||||
private:
|
||||
void deleteVoxelCodeFromTreeRecursion(VoxelNode* node, void* extraData);
|
||||
|
@ -172,6 +187,7 @@ private:
|
|||
bool _shouldReaverage;
|
||||
};
|
||||
|
||||
int boundaryDistanceForRenderLevel(unsigned int renderLevel);
|
||||
float boundaryDistanceForRenderLevel(unsigned int renderLevel);
|
||||
float boundaryDistanceSquaredForRenderLevel(unsigned int renderLevel);
|
||||
|
||||
#endif /* defined(__hifi__VoxelTree__) */
|
||||
|
|
|
@ -17,7 +17,9 @@ VoxelNodeData::VoxelNodeData(Node* owningNode) :
|
|||
_voxelPacketAvailableBytes(MAX_VOXEL_PACKET_SIZE),
|
||||
_maxSearchLevel(1),
|
||||
_maxLevelReachedInLastSearch(1),
|
||||
_lastTimeBagEmpty(0)
|
||||
_lastTimeBagEmpty(0),
|
||||
_viewFrustumChanging(false),
|
||||
_currentPacketIsColor(true)
|
||||
{
|
||||
_voxelPacket = new unsigned char[MAX_VOXEL_PACKET_SIZE];
|
||||
_voxelPacketAt = _voxelPacket;
|
||||
|
@ -27,9 +29,13 @@ VoxelNodeData::VoxelNodeData(Node* owningNode) :
|
|||
|
||||
|
||||
void VoxelNodeData::resetVoxelPacket() {
|
||||
_voxelPacket[0] = getWantColor() ? PACKET_HEADER_VOXEL_DATA : PACKET_HEADER_VOXEL_DATA_MONOCHROME;
|
||||
_voxelPacketAt = &_voxelPacket[1];
|
||||
_voxelPacketAvailableBytes = MAX_VOXEL_PACKET_SIZE - 1;
|
||||
// If we're moving, and the client asked for low res, then we force monochrome, otherwise, use
|
||||
// the clients requested color state.
|
||||
_currentPacketIsColor = (getWantLowResMoving() && _viewFrustumChanging) ? false : getWantColor();
|
||||
PACKET_TYPE voxelPacketType = _currentPacketIsColor ? PACKET_TYPE_VOXEL_DATA : PACKET_TYPE_VOXEL_DATA_MONOCHROME;
|
||||
int numBytesPacketHeader = populateTypeAndVersion(_voxelPacket, voxelPacketType);
|
||||
_voxelPacketAt = _voxelPacket + numBytesPacketHeader;
|
||||
_voxelPacketAvailableBytes = MAX_VOXEL_PACKET_SIZE - numBytesPacketHeader;
|
||||
_voxelPacketWaiting = false;
|
||||
}
|
||||
|
||||
|
@ -63,6 +69,7 @@ bool VoxelNodeData::updateCurrentViewFrustum() {
|
|||
_currentViewFrustum.calculate();
|
||||
currentViewFrustumChanged = true;
|
||||
}
|
||||
_viewFrustumChanging = currentViewFrustumChanged;
|
||||
return currentViewFrustumChanged;
|
||||
}
|
||||
|
||||
|
|
|
@ -50,10 +50,10 @@ public:
|
|||
bool getViewSent() const { return _viewSent; };
|
||||
void setViewSent(bool viewSent) { _viewSent = viewSent; }
|
||||
|
||||
long long getLastTimeBagEmpty() const { return _lastTimeBagEmpty; };
|
||||
void setLastTimeBagEmpty(long long lastTimeBagEmpty) { _lastTimeBagEmpty = lastTimeBagEmpty; };
|
||||
|
||||
uint64_t getLastTimeBagEmpty() const { return _lastTimeBagEmpty; };
|
||||
void setLastTimeBagEmpty(uint64_t lastTimeBagEmpty) { _lastTimeBagEmpty = lastTimeBagEmpty; };
|
||||
|
||||
bool getCurrentPacketIsColor() const { return _currentPacketIsColor; };
|
||||
private:
|
||||
VoxelNodeData(const VoxelNodeData &);
|
||||
VoxelNodeData& operator= (const VoxelNodeData&);
|
||||
|
@ -67,8 +67,9 @@ private:
|
|||
int _maxLevelReachedInLastSearch;
|
||||
ViewFrustum _currentViewFrustum;
|
||||
ViewFrustum _lastKnownViewFrustum;
|
||||
long long _lastTimeBagEmpty;
|
||||
|
||||
uint64_t _lastTimeBagEmpty;
|
||||
bool _viewFrustumChanging;
|
||||
bool _currentPacketIsColor;
|
||||
};
|
||||
|
||||
#endif /* defined(__hifi__VoxelNodeData__) */
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
// main.cpp
|
||||
// Voxel Server
|
||||
//
|
||||
// Created by Stephen Birara on 03/06/13.
|
||||
// Created by Stephen Birarda on 03/06/13.
|
||||
// Copyright (c) 2012 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
|
@ -32,7 +32,7 @@
|
|||
|
||||
const char* LOCAL_VOXELS_PERSIST_FILE = "resources/voxels.svo";
|
||||
const char* VOXELS_PERSIST_FILE = "/etc/highfidelity/voxel-server/resources/voxels.svo";
|
||||
const long long VOXEL_PERSIST_INTERVAL = 1000 * 30; // every 30 seconds
|
||||
const int VOXEL_PERSIST_INTERVAL = 1000 * 30; // every 30 seconds
|
||||
|
||||
const int VOXEL_LISTEN_PORT = 40106;
|
||||
|
||||
|
@ -110,132 +110,6 @@ void eraseVoxelTreeAndCleanupNodeVisitData() {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
// Version of voxel distributor that sends each LOD level at a time
|
||||
void resInVoxelDistributor(NodeList* nodeList,
|
||||
NodeList::iterator& node,
|
||||
VoxelNodeData* nodeData) {
|
||||
ViewFrustum viewFrustum = nodeData->getCurrentViewFrustum();
|
||||
bool searchReset = false;
|
||||
int searchLoops = 0;
|
||||
int searchLevelWas = nodeData->getMaxSearchLevel();
|
||||
long long start = usecTimestampNow();
|
||||
while (!searchReset && nodeData->nodeBag.isEmpty()) {
|
||||
searchLoops++;
|
||||
|
||||
searchLevelWas = nodeData->getMaxSearchLevel();
|
||||
int maxLevelReached = serverTree.searchForColoredNodes(nodeData->getMaxSearchLevel(), serverTree.rootNode,
|
||||
viewFrustum, nodeData->nodeBag);
|
||||
nodeData->setMaxLevelReached(maxLevelReached);
|
||||
|
||||
// If nothing got added, then we bump our levels.
|
||||
if (nodeData->nodeBag.isEmpty()) {
|
||||
if (nodeData->getMaxLevelReached() < nodeData->getMaxSearchLevel()) {
|
||||
nodeData->resetMaxSearchLevel();
|
||||
searchReset = true;
|
||||
} else {
|
||||
nodeData->incrementMaxSearchLevel();
|
||||
}
|
||||
}
|
||||
}
|
||||
long long end = usecTimestampNow();
|
||||
int elapsedmsec = (end - start)/1000;
|
||||
if (elapsedmsec > 100) {
|
||||
if (elapsedmsec > 1000) {
|
||||
int elapsedsec = (end - start)/1000000;
|
||||
printf("WARNING! searchForColoredNodes() took %d seconds to identify %d nodes at level %d in %d loops\n",
|
||||
elapsedsec, nodeData->nodeBag.count(), searchLevelWas, searchLoops);
|
||||
} else {
|
||||
printf("WARNING! searchForColoredNodes() took %d milliseconds to identify %d nodes at level %d in %d loops\n",
|
||||
elapsedmsec, nodeData->nodeBag.count(), searchLevelWas, searchLoops);
|
||||
}
|
||||
} else if (::debugVoxelSending) {
|
||||
printf("searchForColoredNodes() took %d milliseconds to identify %d nodes at level %d in %d loops\n",
|
||||
elapsedmsec, nodeData->nodeBag.count(), searchLevelWas, searchLoops);
|
||||
}
|
||||
|
||||
|
||||
// If we have something in our nodeBag, then turn them into packets and send them out...
|
||||
if (!nodeData->nodeBag.isEmpty()) {
|
||||
static unsigned char tempOutputBuffer[MAX_VOXEL_PACKET_SIZE - 1]; // save on allocs by making this static
|
||||
int bytesWritten = 0;
|
||||
int packetsSentThisInterval = 0;
|
||||
int truePacketsSent = 0;
|
||||
int trueBytesSent = 0;
|
||||
long long start = usecTimestampNow();
|
||||
|
||||
bool shouldSendEnvironments = shouldDo(ENVIRONMENT_SEND_INTERVAL_USECS, VOXEL_SEND_INTERVAL_USECS);
|
||||
while (packetsSentThisInterval < PACKETS_PER_CLIENT_PER_INTERVAL - (shouldSendEnvironments ? 1 : 0)) {
|
||||
if (!nodeData->nodeBag.isEmpty()) {
|
||||
VoxelNode* subTree = nodeData->nodeBag.extract();
|
||||
|
||||
EncodeBitstreamParams params(nodeData->getMaxSearchLevel(), &viewFrustum,
|
||||
nodeData->getWantColor(), WANT_EXISTS_BITS);
|
||||
|
||||
bytesWritten = serverTree.encodeTreeBitstream(subTree, &tempOutputBuffer[0], MAX_VOXEL_PACKET_SIZE - 1,
|
||||
nodeData->nodeBag, params);
|
||||
|
||||
if (nodeData->getAvailable() >= bytesWritten) {
|
||||
nodeData->writeToPacket(&tempOutputBuffer[0], bytesWritten);
|
||||
} else {
|
||||
nodeList->getNodeSocket()->send(node->getActiveSocket(),
|
||||
nodeData->getPacket(), nodeData->getPacketLength());
|
||||
trueBytesSent += nodeData->getPacketLength();
|
||||
truePacketsSent++;
|
||||
packetsSentThisInterval++;
|
||||
nodeData->resetVoxelPacket();
|
||||
nodeData->writeToPacket(&tempOutputBuffer[0], bytesWritten);
|
||||
}
|
||||
} else {
|
||||
if (nodeData->isPacketWaiting()) {
|
||||
nodeList->getNodeSocket()->send(node->getActiveSocket(),
|
||||
nodeData->getPacket(), nodeData->getPacketLength());
|
||||
trueBytesSent += nodeData->getPacketLength();
|
||||
truePacketsSent++;
|
||||
nodeData->resetVoxelPacket();
|
||||
|
||||
}
|
||||
packetsSentThisInterval = PACKETS_PER_CLIENT_PER_INTERVAL; // done for now, no nodes left
|
||||
}
|
||||
}
|
||||
// send the environment packets
|
||||
if (shouldSendEnvironments) {
|
||||
int envPacketLength = 1;
|
||||
*tempOutputBuffer = PACKET_HEADER_ENVIRONMENT_DATA;
|
||||
for (int i = 0; i < sizeof(environmentData) / sizeof(environmentData[0]); i++) {
|
||||
envPacketLength += environmentData[i].getBroadcastData(tempOutputBuffer + envPacketLength);
|
||||
}
|
||||
nodeList->getNodeSocket()->send(node->getActiveSocket(), tempOutputBuffer, envPacketLength);
|
||||
trueBytesSent += envPacketLength;
|
||||
truePacketsSent++;
|
||||
}
|
||||
long long end = usecTimestampNow();
|
||||
int elapsedmsec = (end - start)/1000;
|
||||
if (elapsedmsec > 100) {
|
||||
if (elapsedmsec > 1000) {
|
||||
int elapsedsec = (end - start)/1000000;
|
||||
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, nodeData->nodeBag.count());
|
||||
} else {
|
||||
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, nodeData->nodeBag.count());
|
||||
}
|
||||
} else if (::debugVoxelSending) {
|
||||
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, nodeData->nodeBag.count());
|
||||
}
|
||||
|
||||
// if during this last pass, we emptied our bag, then we want to move to the next level.
|
||||
if (nodeData->nodeBag.isEmpty()) {
|
||||
if (nodeData->getMaxLevelReached() < nodeData->getMaxSearchLevel()) {
|
||||
nodeData->resetMaxSearchLevel();
|
||||
} else {
|
||||
nodeData->incrementMaxSearchLevel();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pthread_mutex_t treeLock;
|
||||
|
||||
// Version of voxel distributor that sends the deepest LOD level at once
|
||||
|
@ -248,10 +122,48 @@ void deepestLevelVoxelDistributor(NodeList* nodeList,
|
|||
pthread_mutex_lock(&::treeLock);
|
||||
|
||||
int maxLevelReached = 0;
|
||||
long long start = usecTimestampNow();
|
||||
uint64_t start = usecTimestampNow();
|
||||
int truePacketsSent = 0;
|
||||
int trueBytesSent = 0;
|
||||
|
||||
// FOR NOW... node tells us if it wants to receive only view frustum deltas
|
||||
bool wantDelta = nodeData->getWantDelta();
|
||||
bool wantDelta = viewFrustumChanged && nodeData->getWantDelta();
|
||||
|
||||
// If our packet already has content in it, then we must use the color choice of the waiting packet.
|
||||
// If we're starting a fresh packet, then...
|
||||
// If we're moving, and the client asked for low res, then we force monochrome, otherwise, use
|
||||
// the clients requested color state.
|
||||
bool wantColor = ((nodeData->getWantLowResMoving() && viewFrustumChanged) ? false : nodeData->getWantColor());
|
||||
|
||||
// If we have a packet waiting, and our desired want color, doesn't match the current waiting packets color
|
||||
// then let's just send that waiting packet.
|
||||
if (wantColor != nodeData->getCurrentPacketIsColor()) {
|
||||
|
||||
if (nodeData->isPacketWaiting()) {
|
||||
if (::debugVoxelSending) {
|
||||
printf("wantColor=%s --- SENDING PARTIAL PACKET! nodeData->getCurrentPacketIsColor()=%s\n",
|
||||
debug::valueOf(wantColor), debug::valueOf(nodeData->getCurrentPacketIsColor()));
|
||||
}
|
||||
nodeList->getNodeSocket()->send(node->getActiveSocket(),
|
||||
nodeData->getPacket(), nodeData->getPacketLength());
|
||||
trueBytesSent += nodeData->getPacketLength();
|
||||
truePacketsSent++;
|
||||
nodeData->resetVoxelPacket();
|
||||
} else {
|
||||
if (::debugVoxelSending) {
|
||||
printf("wantColor=%s --- FIXING HEADER! nodeData->getCurrentPacketIsColor()=%s\n",
|
||||
debug::valueOf(wantColor), debug::valueOf(nodeData->getCurrentPacketIsColor()));
|
||||
}
|
||||
nodeData->resetVoxelPacket();
|
||||
}
|
||||
}
|
||||
|
||||
if (::debugVoxelSending) {
|
||||
printf("wantColor=%s getCurrentPacketIsColor()=%s, viewFrustumChanged=%s, getWantLowResMoving()=%s\n",
|
||||
debug::valueOf(wantColor), debug::valueOf(nodeData->getCurrentPacketIsColor()),
|
||||
debug::valueOf(viewFrustumChanged), debug::valueOf(nodeData->getWantLowResMoving()));
|
||||
}
|
||||
|
||||
const ViewFrustum* lastViewFrustum = wantDelta ? &nodeData->getLastKnownViewFrustum() : NULL;
|
||||
|
||||
if (::debugVoxelSending) {
|
||||
|
@ -267,18 +179,19 @@ void deepestLevelVoxelDistributor(NodeList* nodeList,
|
|||
if (::debugVoxelSending) {
|
||||
printf("(viewFrustumChanged=%s || nodeData->nodeBag.isEmpty() =%s)...\n",
|
||||
debug::valueOf(viewFrustumChanged), debug::valueOf(nodeData->nodeBag.isEmpty()));
|
||||
long long now = usecTimestampNow();
|
||||
uint64_t now = usecTimestampNow();
|
||||
if (nodeData->getLastTimeBagEmpty() > 0) {
|
||||
float elapsedSceneSend = (now - nodeData->getLastTimeBagEmpty()) / 1000000.0f;
|
||||
|
||||
if (viewFrustumChanged) {
|
||||
printf("viewFrustumChanged resetting after elapsed time to send scene = %f seconds", elapsedSceneSend);
|
||||
} else {
|
||||
printf("elapsed time to send scene = %f seconds", elapsedSceneSend);
|
||||
}
|
||||
printf(" [occlusionCulling: %s]\n", debug::valueOf(nodeData->getWantOcclusionCulling()));
|
||||
printf(" [occlusionCulling:%s, wantDelta:%s, wantColor:%s ]\n",
|
||||
debug::valueOf(nodeData->getWantOcclusionCulling()), debug::valueOf(wantDelta),
|
||||
debug::valueOf(wantColor));
|
||||
}
|
||||
nodeData->setLastTimeBagEmpty(now);
|
||||
nodeData->setLastTimeBagEmpty(now); // huh? why is this inside debug? probably not what we want
|
||||
}
|
||||
|
||||
// if our view has changed, we need to reset these things...
|
||||
|
@ -304,9 +217,8 @@ void deepestLevelVoxelDistributor(NodeList* nodeList,
|
|||
} else {
|
||||
nodeData->nodeBag.insert(serverTree.rootNode);
|
||||
}
|
||||
|
||||
}
|
||||
long long end = usecTimestampNow();
|
||||
uint64_t end = usecTimestampNow();
|
||||
int elapsedmsec = (end - start)/1000;
|
||||
if (elapsedmsec > 100) {
|
||||
if (elapsedmsec > 1000) {
|
||||
|
@ -327,14 +239,12 @@ void deepestLevelVoxelDistributor(NodeList* nodeList,
|
|||
static unsigned char tempOutputBuffer[MAX_VOXEL_PACKET_SIZE - 1]; // save on allocs by making this static
|
||||
int bytesWritten = 0;
|
||||
int packetsSentThisInterval = 0;
|
||||
int truePacketsSent = 0;
|
||||
int trueBytesSent = 0;
|
||||
long long start = usecTimestampNow();
|
||||
uint64_t start = usecTimestampNow();
|
||||
|
||||
bool shouldSendEnvironments = shouldDo(ENVIRONMENT_SEND_INTERVAL_USECS, VOXEL_SEND_INTERVAL_USECS);
|
||||
while (packetsSentThisInterval < PACKETS_PER_CLIENT_PER_INTERVAL - (shouldSendEnvironments ? 1 : 0)) {
|
||||
// Check to see if we're taking too long, and if so bail early...
|
||||
long long now = usecTimestampNow();
|
||||
uint64_t now = usecTimestampNow();
|
||||
long elapsedUsec = (now - start);
|
||||
long elapsedUsecPerPacket = (truePacketsSent == 0) ? 0 : (elapsedUsec / truePacketsSent);
|
||||
long usecRemaining = (VOXEL_SEND_INTERVAL_USECS - elapsedUsec);
|
||||
|
@ -350,22 +260,27 @@ void deepestLevelVoxelDistributor(NodeList* nodeList,
|
|||
|
||||
if (!nodeData->nodeBag.isEmpty()) {
|
||||
VoxelNode* subTree = nodeData->nodeBag.extract();
|
||||
|
||||
bool wantOcclusionCulling = nodeData->getWantOcclusionCulling();
|
||||
CoverageMap* coverageMap = wantOcclusionCulling ? &nodeData->map : IGNORE_COVERAGE_MAP;
|
||||
int boundaryLevelAdjust = viewFrustumChanged && nodeData->getWantLowResMoving()
|
||||
? LOW_RES_MOVING_ADJUST : NO_BOUNDARY_ADJUST;
|
||||
|
||||
EncodeBitstreamParams params(INT_MAX, &nodeData->getCurrentViewFrustum(), nodeData->getWantColor(),
|
||||
EncodeBitstreamParams params(INT_MAX, &nodeData->getCurrentViewFrustum(), wantColor,
|
||||
WANT_EXISTS_BITS, DONT_CHOP, wantDelta, lastViewFrustum,
|
||||
wantOcclusionCulling, coverageMap);
|
||||
wantOcclusionCulling, coverageMap, boundaryLevelAdjust);
|
||||
|
||||
bytesWritten = serverTree.encodeTreeBitstream(subTree, &tempOutputBuffer[0], MAX_VOXEL_PACKET_SIZE - 1,
|
||||
nodeData->nodeBag, params);
|
||||
|
||||
if (::debugVoxelSending && wantDelta) {
|
||||
printf("encodeTreeBitstream() childWasInViewDiscarded=%ld\n", params.childWasInViewDiscarded);
|
||||
}
|
||||
|
||||
if (nodeData->getAvailable() >= bytesWritten) {
|
||||
nodeData->writeToPacket(&tempOutputBuffer[0], bytesWritten);
|
||||
} else {
|
||||
nodeList->getNodeSocket()->send(node->getActiveSocket(),
|
||||
nodeData->getPacket(), nodeData->getPacketLength());
|
||||
nodeData->getPacket(), nodeData->getPacketLength());
|
||||
trueBytesSent += nodeData->getPacketLength();
|
||||
truePacketsSent++;
|
||||
packetsSentThisInterval++;
|
||||
|
@ -375,28 +290,29 @@ void deepestLevelVoxelDistributor(NodeList* nodeList,
|
|||
} else {
|
||||
if (nodeData->isPacketWaiting()) {
|
||||
nodeList->getNodeSocket()->send(node->getActiveSocket(),
|
||||
nodeData->getPacket(), nodeData->getPacketLength());
|
||||
nodeData->getPacket(), nodeData->getPacketLength());
|
||||
trueBytesSent += nodeData->getPacketLength();
|
||||
truePacketsSent++;
|
||||
nodeData->resetVoxelPacket();
|
||||
|
||||
}
|
||||
packetsSentThisInterval = PACKETS_PER_CLIENT_PER_INTERVAL; // done for now, no nodes left
|
||||
}
|
||||
}
|
||||
// send the environment packet
|
||||
if (shouldSendEnvironments) {
|
||||
int envPacketLength = 1;
|
||||
*tempOutputBuffer = PACKET_HEADER_ENVIRONMENT_DATA;
|
||||
for (int i = 0; i < sizeof(environmentData) / sizeof(environmentData[0]); i++) {
|
||||
int numBytesPacketHeader = populateTypeAndVersion(tempOutputBuffer, PACKET_TYPE_ENVIRONMENT_DATA);
|
||||
int envPacketLength = numBytesPacketHeader;
|
||||
|
||||
for (int i = 0; i < sizeof(environmentData) / sizeof(EnvironmentData); i++) {
|
||||
envPacketLength += environmentData[i].getBroadcastData(tempOutputBuffer + envPacketLength);
|
||||
}
|
||||
|
||||
nodeList->getNodeSocket()->send(node->getActiveSocket(), tempOutputBuffer, envPacketLength);
|
||||
trueBytesSent += envPacketLength;
|
||||
truePacketsSent++;
|
||||
}
|
||||
|
||||
long long end = usecTimestampNow();
|
||||
uint64_t end = usecTimestampNow();
|
||||
int elapsedmsec = (end - start)/1000;
|
||||
if (elapsedmsec > 100) {
|
||||
if (elapsedmsec > 1000) {
|
||||
|
@ -417,6 +333,9 @@ void deepestLevelVoxelDistributor(NodeList* nodeList,
|
|||
if (nodeData->nodeBag.isEmpty()) {
|
||||
nodeData->updateLastKnownViewFrustum();
|
||||
nodeData->setViewSent(true);
|
||||
if (::debugVoxelSending) {
|
||||
nodeData->map.printStats();
|
||||
}
|
||||
nodeData->map.erase(); // It would be nice if we could save this, and only reset it when the view frustum changes
|
||||
}
|
||||
|
||||
|
@ -425,10 +344,10 @@ void deepestLevelVoxelDistributor(NodeList* nodeList,
|
|||
pthread_mutex_unlock(&::treeLock);
|
||||
}
|
||||
|
||||
long long lastPersistVoxels = 0;
|
||||
uint64_t lastPersistVoxels = 0;
|
||||
void persistVoxelsWhenDirty() {
|
||||
long long now = usecTimestampNow();
|
||||
long long sinceLastTime = (now - ::lastPersistVoxels) / 1000;
|
||||
uint64_t now = usecTimestampNow();
|
||||
int sinceLastTime = (now - ::lastPersistVoxels) / 1000;
|
||||
|
||||
// check the dirty bit and persist here...
|
||||
if (::wantVoxelPersist && ::serverTree.isDirty() && sinceLastTime > VOXEL_PERSIST_INTERVAL) {
|
||||
|
@ -463,17 +382,12 @@ void *distributeVoxelsToListeners(void *args) {
|
|||
if (::debugVoxelSending) {
|
||||
printf("nodeData->updateCurrentViewFrustum() changed=%s\n", debug::valueOf(viewFrustumChanged));
|
||||
}
|
||||
|
||||
if (nodeData->getWantResIn()) {
|
||||
resInVoxelDistributor(nodeList, node, nodeData);
|
||||
} else {
|
||||
deepestLevelVoxelDistributor(nodeList, node, nodeData, viewFrustumChanged);
|
||||
}
|
||||
deepestLevelVoxelDistributor(nodeList, node, nodeData, viewFrustumChanged);
|
||||
}
|
||||
}
|
||||
|
||||
// dynamically sleep until we need to fire off the next set of voxels
|
||||
long long usecToSleep = VOXEL_SEND_INTERVAL_USECS - (usecTimestampNow() - usecTimestamp(&lastSendTime));
|
||||
int usecToSleep = VOXEL_SEND_INTERVAL_USECS - (usecTimestampNow() - usecTimestamp(&lastSendTime));
|
||||
|
||||
if (usecToSleep > 0) {
|
||||
usleep(usecToSleep);
|
||||
|
@ -636,19 +550,24 @@ int main(int argc, const char * argv[]) {
|
|||
// check to see if we need to persist our voxel state
|
||||
persistVoxelsWhenDirty();
|
||||
|
||||
if (nodeList->getNodeSocket()->receive(&nodePublicAddress, packetData, &receivedBytes)) {
|
||||
if (packetData[0] == PACKET_HEADER_SET_VOXEL || packetData[0] == PACKET_HEADER_SET_VOXEL_DESTRUCTIVE) {
|
||||
bool destructive = (packetData[0] == PACKET_HEADER_SET_VOXEL_DESTRUCTIVE);
|
||||
if (nodeList->getNodeSocket()->receive(&nodePublicAddress, packetData, &receivedBytes) &&
|
||||
packetVersionMatch(packetData)) {
|
||||
|
||||
int numBytesPacketHeader = numBytesForPacketHeader(packetData);
|
||||
|
||||
if (packetData[0] == PACKET_TYPE_SET_VOXEL || packetData[0] == PACKET_TYPE_SET_VOXEL_DESTRUCTIVE) {
|
||||
bool destructive = (packetData[0] == PACKET_TYPE_SET_VOXEL_DESTRUCTIVE);
|
||||
PerformanceWarning warn(::shouldShowAnimationDebug,
|
||||
destructive ? "PACKET_HEADER_SET_VOXEL_DESTRUCTIVE" : "PACKET_HEADER_SET_VOXEL",
|
||||
destructive ? "PACKET_TYPE_SET_VOXEL_DESTRUCTIVE" : "PACKET_TYPE_SET_VOXEL",
|
||||
::shouldShowAnimationDebug);
|
||||
unsigned short int itemNumber = (*((unsigned short int*)&packetData[1]));
|
||||
|
||||
unsigned short int itemNumber = (*((unsigned short int*)(packetData + numBytesPacketHeader)));
|
||||
if (::shouldShowAnimationDebug) {
|
||||
printf("got %s - command from client receivedBytes=%ld itemNumber=%d\n",
|
||||
destructive ? "PACKET_HEADER_SET_VOXEL_DESTRUCTIVE" : "PACKET_HEADER_SET_VOXEL",
|
||||
destructive ? "PACKET_TYPE_SET_VOXEL_DESTRUCTIVE" : "PACKET_TYPE_SET_VOXEL",
|
||||
receivedBytes,itemNumber);
|
||||
}
|
||||
int atByte = sizeof(PACKET_HEADER) + sizeof(itemNumber);
|
||||
int atByte = numBytesPacketHeader + sizeof(itemNumber);
|
||||
unsigned char* voxelData = (unsigned char*)&packetData[atByte];
|
||||
while (atByte < receivedBytes) {
|
||||
unsigned char octets = (unsigned char)*voxelData;
|
||||
|
@ -690,21 +609,20 @@ int main(int argc, const char * argv[]) {
|
|||
voxelData += voxelDataSize;
|
||||
atByte += voxelDataSize;
|
||||
}
|
||||
}
|
||||
if (packetData[0] == PACKET_HEADER_ERASE_VOXEL) {
|
||||
} else if (packetData[0] == PACKET_TYPE_ERASE_VOXEL) {
|
||||
|
||||
// Send these bits off to the VoxelTree class to process them
|
||||
pthread_mutex_lock(&::treeLock);
|
||||
serverTree.processRemoveVoxelBitstream((unsigned char*)packetData, receivedBytes);
|
||||
pthread_mutex_unlock(&::treeLock);
|
||||
}
|
||||
if (packetData[0] == PACKET_HEADER_Z_COMMAND) {
|
||||
} else if (packetData[0] == PACKET_TYPE_Z_COMMAND) {
|
||||
|
||||
// the Z command is a special command that allows the sender to send the voxel server high level semantic
|
||||
// requests, like erase all, or add sphere scene
|
||||
char* command = (char*) &packetData[1]; // start of the command
|
||||
|
||||
char* command = (char*) &packetData[numBytesPacketHeader]; // start of the command
|
||||
int commandLength = strlen(command); // commands are null terminated strings
|
||||
int totalLength = sizeof(PACKET_HEADER_Z_COMMAND) + commandLength + 1; // 1 for null termination
|
||||
int totalLength = numBytesPacketHeader + commandLength + 1; // 1 for null termination
|
||||
printf("got Z message len(%ld)= %s\n", receivedBytes, command);
|
||||
bool rebroadcast = true; // by default rebroadcast
|
||||
|
||||
|
@ -730,21 +648,20 @@ int main(int argc, const char * argv[]) {
|
|||
printf("rebroadcasting Z message to connected nodes... nodeList.broadcastToNodes()\n");
|
||||
nodeList->broadcastToNodes(packetData, receivedBytes, &NODE_TYPE_AGENT, 1);
|
||||
}
|
||||
}
|
||||
// If we got a PACKET_HEADER_HEAD_DATA, then we're talking to an NODE_TYPE_AVATAR, and we
|
||||
// need to make sure we have it in our nodeList.
|
||||
if (packetData[0] == PACKET_HEADER_HEAD_DATA) {
|
||||
} else if (packetData[0] == PACKET_TYPE_HEAD_DATA) {
|
||||
// If we got a PACKET_TYPE_HEAD_DATA, then we're talking to an NODE_TYPE_AVATAR, and we
|
||||
// need to make sure we have it in our nodeList.
|
||||
|
||||
uint16_t nodeID = 0;
|
||||
unpackNodeId(packetData + sizeof(PACKET_HEADER_HEAD_DATA), &nodeID);
|
||||
unpackNodeId(packetData + numBytesPacketHeader, &nodeID);
|
||||
Node* node = nodeList->addOrUpdateNode(&nodePublicAddress,
|
||||
&nodePublicAddress,
|
||||
NODE_TYPE_AGENT,
|
||||
nodeID);
|
||||
&nodePublicAddress,
|
||||
NODE_TYPE_AGENT,
|
||||
nodeID);
|
||||
|
||||
nodeList->updateNodeWithData(node, packetData, receivedBytes);
|
||||
}
|
||||
// If the packet is a ping, let processNodeData handle it.
|
||||
if (packetData[0] == PACKET_HEADER_PING) {
|
||||
} else if (packetData[0] == PACKET_TYPE_PING) {
|
||||
// If the packet is a ping, let processNodeData handle it.
|
||||
nodeList->processNodeData(&nodePublicAddress, packetData, receivedBytes);
|
||||
}
|
||||
}
|
||||
|
@ -755,3 +672,5 @@ int main(int argc, const char * argv[]) {
|
|||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
|
Loading…
Reference in a new issue