mirror of
https://github.com/overte-org/overte.git
synced 2025-08-09 21:43:03 +02:00
Merge branch 'master' of git://github.com/worklist/hifi into 19371
This commit is contained in:
commit
7342b7a774
29 changed files with 1679 additions and 594 deletions
|
@ -116,6 +116,8 @@ int main(int argc, const char* argv[]) {
|
||||||
float sumFrameTimePercentages = 0.0f;
|
float sumFrameTimePercentages = 0.0f;
|
||||||
int numStatCollections = 0;
|
int numStatCollections = 0;
|
||||||
|
|
||||||
|
stk::StkFrames stkFrameBuffer(BUFFER_LENGTH_SAMPLES_PER_CHANNEL, 1);
|
||||||
|
|
||||||
// if we'll be sending stats, call the Logstash::socket() method to make it load the logstash IP outside the loop
|
// if we'll be sending stats, call the Logstash::socket() method to make it load the logstash IP outside the loop
|
||||||
if (Logstash::shouldSendStats()) {
|
if (Logstash::shouldSendStats()) {
|
||||||
Logstash::socket();
|
Logstash::socket();
|
||||||
|
@ -161,6 +163,9 @@ int main(int argc, const char* argv[]) {
|
||||||
}
|
}
|
||||||
|
|
||||||
for (AgentList::iterator agent = agentList->begin(); agent != agentList->end(); agent++) {
|
for (AgentList::iterator agent = agentList->begin(); agent != agentList->end(); agent++) {
|
||||||
|
|
||||||
|
const int PHASE_DELAY_AT_90 = 20;
|
||||||
|
|
||||||
if (agent->getType() == AGENT_TYPE_AVATAR) {
|
if (agent->getType() == AGENT_TYPE_AVATAR) {
|
||||||
AvatarAudioRingBuffer* agentRingBuffer = (AvatarAudioRingBuffer*) agent->getLinkedData();
|
AvatarAudioRingBuffer* agentRingBuffer = (AvatarAudioRingBuffer*) agent->getLinkedData();
|
||||||
|
|
||||||
|
@ -248,7 +253,6 @@ int main(int argc, const char* argv[]) {
|
||||||
glm::normalize(rotatedSourcePosition),
|
glm::normalize(rotatedSourcePosition),
|
||||||
glm::vec3(0.0f, 1.0f, 0.0f));
|
glm::vec3(0.0f, 1.0f, 0.0f));
|
||||||
|
|
||||||
const int PHASE_DELAY_AT_90 = 20;
|
|
||||||
const float PHASE_AMPLITUDE_RATIO_AT_90 = 0.5;
|
const float PHASE_AMPLITUDE_RATIO_AT_90 = 0.5;
|
||||||
|
|
||||||
// figure out the number of samples of delay and the ratio of the amplitude
|
// figure out the number of samples of delay and the ratio of the amplitude
|
||||||
|
@ -279,6 +283,8 @@ int main(int argc, const char* argv[]) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int16_t* sourceBuffer = otherAgentBuffer->getNextOutput();
|
||||||
|
|
||||||
int16_t* goodChannel = (bearingRelativeAngleToSource > 0.0f)
|
int16_t* goodChannel = (bearingRelativeAngleToSource > 0.0f)
|
||||||
? clientSamples
|
? clientSamples
|
||||||
: clientSamples + BUFFER_LENGTH_SAMPLES_PER_CHANNEL;
|
: clientSamples + BUFFER_LENGTH_SAMPLES_PER_CHANNEL;
|
||||||
|
@ -290,22 +296,25 @@ int main(int argc, const char* argv[]) {
|
||||||
? otherAgentBuffer->getBuffer() + RING_BUFFER_LENGTH_SAMPLES - numSamplesDelay
|
? otherAgentBuffer->getBuffer() + RING_BUFFER_LENGTH_SAMPLES - numSamplesDelay
|
||||||
: otherAgentBuffer->getNextOutput() - numSamplesDelay;
|
: otherAgentBuffer->getNextOutput() - numSamplesDelay;
|
||||||
|
|
||||||
|
for (int s = 0; s < BUFFER_LENGTH_SAMPLES_PER_CHANNEL; s++) {
|
||||||
|
// load up the stkFrameBuffer with this source's samples
|
||||||
|
stkFrameBuffer[s] = (stk::StkFloat) sourceBuffer[s];
|
||||||
|
}
|
||||||
|
|
||||||
|
// perform the TwoPole effect on the stkFrameBuffer
|
||||||
|
if (otherAgentTwoPole) {
|
||||||
|
otherAgentTwoPole->tick(stkFrameBuffer);
|
||||||
|
}
|
||||||
|
|
||||||
for (int s = 0; s < BUFFER_LENGTH_SAMPLES_PER_CHANNEL; s++) {
|
for (int s = 0; s < BUFFER_LENGTH_SAMPLES_PER_CHANNEL; s++) {
|
||||||
if (s < numSamplesDelay) {
|
if (s < numSamplesDelay) {
|
||||||
// pull the earlier sample for the delayed channel
|
// pull the earlier sample for the delayed channel
|
||||||
int earlierSample = delaySamplePointer[s]
|
int earlierSample = delaySamplePointer[s] * attenuationCoefficient * weakChannelAmplitudeRatio;
|
||||||
* attenuationCoefficient
|
|
||||||
* weakChannelAmplitudeRatio;
|
|
||||||
|
|
||||||
plateauAdditionOfSamples(delayedChannel[s], earlierSample);
|
plateauAdditionOfSamples(delayedChannel[s], earlierSample);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (otherAgentTwoPole) {
|
int16_t currentSample = stkFrameBuffer[s] * attenuationCoefficient;
|
||||||
otherAgentBuffer->getNextOutput()[s] = otherAgentTwoPole->tick(otherAgentBuffer->getNextOutput()[s]);
|
|
||||||
}
|
|
||||||
|
|
||||||
int16_t currentSample = otherAgentBuffer->getNextOutput()[s] * attenuationCoefficient;
|
|
||||||
|
|
||||||
plateauAdditionOfSamples(goodChannel[s], currentSample);
|
plateauAdditionOfSamples(goodChannel[s], currentSample);
|
||||||
|
|
||||||
|
@ -313,6 +322,12 @@ int main(int argc, const char* argv[]) {
|
||||||
plateauAdditionOfSamples(delayedChannel[s + numSamplesDelay],
|
plateauAdditionOfSamples(delayedChannel[s + numSamplesDelay],
|
||||||
currentSample * weakChannelAmplitudeRatio);
|
currentSample * weakChannelAmplitudeRatio);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (s >= BUFFER_LENGTH_SAMPLES_PER_CHANNEL - PHASE_DELAY_AT_90) {
|
||||||
|
// this could be a delayed sample on the next pass
|
||||||
|
// so store the affected back in the ARB
|
||||||
|
otherAgentBuffer->getNextOutput()[s] = (int16_t) stkFrameBuffer[s];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -395,9 +410,7 @@ int main(int argc, const char* argv[]) {
|
||||||
float percentageOfMaxElapsed = ((float) (usecTimestamp(&endSendTime) - usecTimestamp(&beginSendTime))
|
float percentageOfMaxElapsed = ((float) (usecTimestamp(&endSendTime) - usecTimestamp(&beginSendTime))
|
||||||
/ BUFFER_SEND_INTERVAL_USECS) * 100.0f;
|
/ BUFFER_SEND_INTERVAL_USECS) * 100.0f;
|
||||||
|
|
||||||
if (percentageOfMaxElapsed > 0) {
|
sumFrameTimePercentages += percentageOfMaxElapsed;
|
||||||
sumFrameTimePercentages += percentageOfMaxElapsed;
|
|
||||||
}
|
|
||||||
|
|
||||||
numStatCollections++;
|
numStatCollections++;
|
||||||
}
|
}
|
||||||
|
|
|
@ -65,8 +65,8 @@ int main(int argc, const char * argv[])
|
||||||
// with the EC2 IP. Otherwise, we will replace the IP like we used to
|
// with the EC2 IP. Otherwise, we will replace the IP like we used to
|
||||||
// this allows developers to run a local domain without recompiling the
|
// this allows developers to run a local domain without recompiling the
|
||||||
// domain server
|
// domain server
|
||||||
bool useLocal = cmdOptionExists(argc, argv, "--local");
|
bool isLocalMode = cmdOptionExists(argc, argv, "--local");
|
||||||
if (useLocal) {
|
if (isLocalMode) {
|
||||||
printf("NOTE: Running in Local Mode!\n");
|
printf("NOTE: Running in Local Mode!\n");
|
||||||
} else {
|
} else {
|
||||||
printf("--------------------------------------------------\n");
|
printf("--------------------------------------------------\n");
|
||||||
|
@ -103,14 +103,17 @@ int main(int argc, const char * argv[])
|
||||||
int numBytesSocket = unpackSocket(packetData + sizeof(PACKET_HEADER) + sizeof(AGENT_TYPE),
|
int numBytesSocket = unpackSocket(packetData + sizeof(PACKET_HEADER) + sizeof(AGENT_TYPE),
|
||||||
(sockaddr*) &agentLocalAddress);
|
(sockaddr*) &agentLocalAddress);
|
||||||
|
|
||||||
|
sockaddr* destinationSocket = (sockaddr*) &agentPublicAddress;
|
||||||
|
|
||||||
// check the agent public address
|
// check the agent public address
|
||||||
// if it matches our local address we're on the same box
|
// if it matches our local address we're on the same box
|
||||||
// so hardcode the EC2 public address for now
|
// so hardcode the EC2 public address for now
|
||||||
if (agentPublicAddress.sin_addr.s_addr == serverLocalAddress) {
|
if (agentPublicAddress.sin_addr.s_addr == serverLocalAddress) {
|
||||||
// If we're not running "local" then we do replace the IP
|
// If we're not running "local" then we do replace the IP
|
||||||
// with the EC2 IP. Otherwise, we use our normal public IP
|
// with the EC2 IP. Otherwise, we use our normal public IP
|
||||||
if (!useLocal) {
|
if (!isLocalMode) {
|
||||||
agentPublicAddress.sin_addr.s_addr = 895283510; // local IP in this format...
|
agentPublicAddress.sin_addr.s_addr = 895283510; // local IP in this format...
|
||||||
|
destinationSocket = (sockaddr*) &agentLocalAddress;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -178,9 +181,9 @@ int main(int argc, const char * argv[])
|
||||||
currentBufferPos += packAgentId(currentBufferPos, newAgent->getAgentID());
|
currentBufferPos += packAgentId(currentBufferPos, newAgent->getAgentID());
|
||||||
|
|
||||||
// send the constructed list back to this agent
|
// send the constructed list back to this agent
|
||||||
agentList->getAgentSocket()->send((sockaddr*) &agentPublicAddress,
|
agentList->getAgentSocket()->send(destinationSocket,
|
||||||
broadcastPacket,
|
broadcastPacket,
|
||||||
(currentBufferPos - startPointer) + 1);
|
(currentBufferPos - startPointer) + 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -35,11 +35,12 @@ enum {
|
||||||
};
|
};
|
||||||
|
|
||||||
// Command line parameter defaults
|
// Command line parameter defaults
|
||||||
bool loopAudio = true;
|
bool shouldLoopAudio = true;
|
||||||
|
bool hasInjectedAudioOnce = false;
|
||||||
float sleepIntervalMin = 1.00;
|
float sleepIntervalMin = 1.00;
|
||||||
float sleepIntervalMax = 2.00;
|
float sleepIntervalMax = 2.00;
|
||||||
char *sourceAudioFile = NULL;
|
char *sourceAudioFile = NULL;
|
||||||
const char *allowedParameters = ":sb::t::c::a::f::d::r:";
|
const char *allowedParameters = ":sc::a::f::t::r:";
|
||||||
float floatArguments[4] = {0.0f, 0.0f, 0.0f, 0.0f};
|
float floatArguments[4] = {0.0f, 0.0f, 0.0f, 0.0f};
|
||||||
unsigned char volume = DEFAULT_INJECTOR_VOLUME;
|
unsigned char volume = DEFAULT_INJECTOR_VOLUME;
|
||||||
float triggerDistance = 0.0f;
|
float triggerDistance = 0.0f;
|
||||||
|
@ -47,13 +48,11 @@ float radius = 0.0f;
|
||||||
|
|
||||||
void usage(void) {
|
void usage(void) {
|
||||||
std::cout << "High Fidelity - Interface audio injector" << std::endl;
|
std::cout << "High Fidelity - Interface audio injector" << std::endl;
|
||||||
std::cout << " -s Random sleep mode. If not specified will default to constant loop." << std::endl;
|
std::cout << " -s Single play mode. If not specified will default to constant loop." << std::endl;
|
||||||
std::cout << " -b FLOAT Min. number of seconds to sleep. Only valid in random sleep mode. Default 1.0" << std::endl;
|
|
||||||
std::cout << " -t FLOAT Max. number of seconds to sleep. Only valid in random sleep mode. Default 2.0" << std::endl;
|
|
||||||
std::cout << " -c FLOAT,FLOAT,FLOAT,FLOAT X,Y,Z,YAW position in universe where audio will be originating from and direction. Defaults to 0,0,0,0" << std::endl;
|
std::cout << " -c FLOAT,FLOAT,FLOAT,FLOAT X,Y,Z,YAW position in universe where audio will be originating from and direction. Defaults to 0,0,0,0" << std::endl;
|
||||||
std::cout << " -a 0-255 Attenuation curve modifier, defaults to 255" << std::endl;
|
std::cout << " -a 0-255 Attenuation curve modifier, defaults to 255" << std::endl;
|
||||||
std::cout << " -f FILENAME Name of audio source file. Required - RAW format, 22050hz 16bit signed mono" << std::endl;
|
std::cout << " -f FILENAME Name of audio source file. Required - RAW format, 22050hz 16bit signed mono" << std::endl;
|
||||||
std::cout << " -d FLOAT Trigger distance for injection. If not specified will loop constantly" << std::endl;
|
std::cout << " -t FLOAT Trigger distance for injection. If not specified will loop constantly" << std::endl;
|
||||||
std::cout << " -r FLOAT Radius for spherical source. If not specified injected audio is point source" << std::endl;
|
std::cout << " -r FLOAT Radius for spherical source. If not specified injected audio is point source" << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -62,16 +61,8 @@ bool processParameters(int parameterCount, char* parameterData[]) {
|
||||||
while ((p = getopt(parameterCount, parameterData, allowedParameters)) != -1) {
|
while ((p = getopt(parameterCount, parameterData, allowedParameters)) != -1) {
|
||||||
switch (p) {
|
switch (p) {
|
||||||
case 's':
|
case 's':
|
||||||
::loopAudio = false;
|
::shouldLoopAudio = false;
|
||||||
std::cout << "[DEBUG] Random sleep mode enabled" << std::endl;
|
std::cout << "[DEBUG] Single play mode enabled" << std::endl;
|
||||||
break;
|
|
||||||
case 'b':
|
|
||||||
::sleepIntervalMin = atof(optarg);
|
|
||||||
std::cout << "[DEBUG] Min delay between plays " << sleepIntervalMin << "sec" << std::endl;
|
|
||||||
break;
|
|
||||||
case 't':
|
|
||||||
::sleepIntervalMax = atof(optarg);
|
|
||||||
std::cout << "[DEBUG] Max delay between plays " << sleepIntervalMax << "sec" << std::endl;
|
|
||||||
break;
|
break;
|
||||||
case 'f':
|
case 'f':
|
||||||
::sourceAudioFile = optarg;
|
::sourceAudioFile = optarg;
|
||||||
|
@ -97,7 +88,7 @@ bool processParameters(int parameterCount, char* parameterData[]) {
|
||||||
::volume = atoi(optarg);
|
::volume = atoi(optarg);
|
||||||
std::cout << "[DEBUG] Attenuation modifier: " << optarg << std::endl;
|
std::cout << "[DEBUG] Attenuation modifier: " << optarg << std::endl;
|
||||||
break;
|
break;
|
||||||
case 'd':
|
case 't':
|
||||||
::triggerDistance = atof(optarg);
|
::triggerDistance = atof(optarg);
|
||||||
std::cout << "[DEBUG] Trigger distance: " << optarg << std::endl;
|
std::cout << "[DEBUG] Trigger distance: " << optarg << std::endl;
|
||||||
break;
|
break;
|
||||||
|
@ -113,35 +104,6 @@ bool processParameters(int parameterCount, char* parameterData[]) {
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
bool stopReceiveAgentDataThread;
|
|
||||||
|
|
||||||
void *receiveAgentData(void *args) {
|
|
||||||
sockaddr senderAddress;
|
|
||||||
ssize_t bytesReceived;
|
|
||||||
unsigned char incomingPacket[MAX_PACKET_SIZE];
|
|
||||||
|
|
||||||
AgentList* agentList = AgentList::getInstance();
|
|
||||||
|
|
||||||
while (!::stopReceiveAgentDataThread) {
|
|
||||||
if (agentList->getAgentSocket()->receive(&senderAddress, incomingPacket, &bytesReceived)) {
|
|
||||||
switch (incomingPacket[0]) {
|
|
||||||
case PACKET_HEADER_BULK_AVATAR_DATA:
|
|
||||||
// this is the positional data for other agents
|
|
||||||
// pass that off to the agentList processBulkAgentData method
|
|
||||||
agentList->processBulkAgentData(&senderAddress, incomingPacket, bytesReceived);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
// have the agentList handle list of agents from DS, replies from other agents, etc.
|
|
||||||
agentList->processAgentData(&senderAddress, incomingPacket, bytesReceived);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pthread_exit(0);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
void createAvatarDataForAgent(Agent* agent) {
|
void createAvatarDataForAgent(Agent* agent) {
|
||||||
if (!agent->getLinkedData()) {
|
if (!agent->getLinkedData()) {
|
||||||
agent->setLinkedData(new AvatarData(agent));
|
agent->setLinkedData(new AvatarData(agent));
|
||||||
|
@ -164,9 +126,6 @@ int main(int argc, char* argv[]) {
|
||||||
// create an AgentList instance to handle communication with other agents
|
// create an AgentList instance to handle communication with other agents
|
||||||
AgentList* agentList = AgentList::createInstance(AGENT_TYPE_AUDIO_INJECTOR, AUDIO_UDP_SEND_PORT);
|
AgentList* agentList = AgentList::createInstance(AGENT_TYPE_AUDIO_INJECTOR, AUDIO_UDP_SEND_PORT);
|
||||||
|
|
||||||
pthread_t receiveAgentDataThread;
|
|
||||||
pthread_create(&receiveAgentDataThread, NULL, receiveAgentData, NULL);
|
|
||||||
|
|
||||||
// start the agent list thread that will kill off agents when they stop talking
|
// start the agent list thread that will kill off agents when they stop talking
|
||||||
agentList->startSilentAgentRemovalThread();
|
agentList->startSilentAgentRemovalThread();
|
||||||
|
|
||||||
|
@ -183,42 +142,47 @@ int main(int argc, char* argv[]) {
|
||||||
|
|
||||||
// register the callback for agent data creation
|
// register the callback for agent data creation
|
||||||
agentList->linkedDataCreateCallback = createAvatarDataForAgent;
|
agentList->linkedDataCreateCallback = createAvatarDataForAgent;
|
||||||
|
|
||||||
unsigned char broadcastPacket = PACKET_HEADER_INJECT_AUDIO;
|
timeval lastSend = {};
|
||||||
|
unsigned char broadcastPacket = PACKET_HEADER_INJECT_AUDIO;
|
||||||
timeval thisSend;
|
|
||||||
long long numMicrosecondsSleep = 0;
|
|
||||||
|
|
||||||
timeval lastDomainServerCheckIn = {};
|
timeval lastDomainServerCheckIn = {};
|
||||||
|
|
||||||
// the audio injector needs to know about the avatar mixer and the audio mixer
|
sockaddr senderAddress;
|
||||||
const char INJECTOR_AGENTS_OF_INTEREST[] = {AGENT_TYPE_AVATAR_MIXER, AGENT_TYPE_AUDIO_MIXER};
|
ssize_t bytesReceived;
|
||||||
AgentList::getInstance()->setAgentTypesOfInterest(INJECTOR_AGENTS_OF_INTEREST, sizeof(INJECTOR_AGENTS_OF_INTEREST));
|
unsigned char incomingPacket[MAX_PACKET_SIZE];
|
||||||
|
|
||||||
while (true) {
|
// the audio injector needs to know about the avatar mixer and the audio mixer
|
||||||
|
const char INJECTOR_AGENTS_OF_INTEREST[] = {AGENT_TYPE_AUDIO_MIXER, AGENT_TYPE_AVATAR_MIXER};
|
||||||
|
|
||||||
|
int bytesAgentsOfInterest = (::triggerDistance > 0)
|
||||||
|
? sizeof(INJECTOR_AGENTS_OF_INTEREST)
|
||||||
|
: sizeof(INJECTOR_AGENTS_OF_INTEREST) - 1;
|
||||||
|
|
||||||
|
AgentList::getInstance()->setAgentTypesOfInterest(INJECTOR_AGENTS_OF_INTEREST, bytesAgentsOfInterest);
|
||||||
|
|
||||||
|
while (true) {
|
||||||
// send a check in packet to the domain server if DOMAIN_SERVER_CHECK_IN_USECS has elapsed
|
// send a check in packet to the domain server if DOMAIN_SERVER_CHECK_IN_USECS has elapsed
|
||||||
if (usecTimestampNow() - usecTimestamp(&lastDomainServerCheckIn) >= DOMAIN_SERVER_CHECK_IN_USECS) {
|
if (usecTimestampNow() - usecTimestamp(&lastDomainServerCheckIn) >= DOMAIN_SERVER_CHECK_IN_USECS) {
|
||||||
gettimeofday(&lastDomainServerCheckIn, NULL);
|
gettimeofday(&lastDomainServerCheckIn, NULL);
|
||||||
AgentList::getInstance()->sendDomainServerCheckIn();
|
AgentList::getInstance()->sendDomainServerCheckIn();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (::triggerDistance) {
|
while (agentList->getAgentSocket()->receive(&senderAddress, incomingPacket, &bytesReceived)) {
|
||||||
|
switch (incomingPacket[0]) {
|
||||||
// update the thisSend timeval to the current time
|
case PACKET_HEADER_BULK_AVATAR_DATA:
|
||||||
gettimeofday(&thisSend, NULL);
|
// this is the positional data for other agents
|
||||||
|
// pass that off to the agentList processBulkAgentData method
|
||||||
// find the current avatar mixer
|
agentList->processBulkAgentData(&senderAddress, incomingPacket, bytesReceived);
|
||||||
Agent* avatarMixer = agentList->soloAgentOfType(AGENT_TYPE_AVATAR_MIXER);
|
break;
|
||||||
|
default:
|
||||||
// make sure we actually have an avatar mixer with an active socket
|
// have the agentList handle list of agents from DS, replies from other agents, etc.
|
||||||
if (avatarMixer && avatarMixer->getActiveSocket() != NULL) {
|
agentList->processAgentData(&senderAddress, incomingPacket, bytesReceived);
|
||||||
// use the UDPSocket instance attached to our agent list to ask avatar mixer for a list of avatars
|
break;
|
||||||
agentList->getAgentSocket()->send(avatarMixer->getActiveSocket(),
|
|
||||||
&broadcastPacket,
|
|
||||||
sizeof(broadcastPacket));
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (::triggerDistance) {
|
||||||
if (!injector.isInjectingAudio()) {
|
if (!injector.isInjectingAudio()) {
|
||||||
// enumerate the other agents to decide if one is close enough that we should inject
|
// enumerate the other agents to decide if one is close enough that we should inject
|
||||||
for (AgentList::iterator agent = agentList->begin(); agent != agentList->end(); agent++) {
|
for (AgentList::iterator agent = agentList->begin(); agent != agentList->end(); agent++) {
|
||||||
|
@ -226,49 +190,39 @@ int main(int argc, char* argv[]) {
|
||||||
|
|
||||||
if (avatarData) {
|
if (avatarData) {
|
||||||
glm::vec3 tempVector = injector.getPosition() - avatarData->getPosition();
|
glm::vec3 tempVector = injector.getPosition() - avatarData->getPosition();
|
||||||
float squareDistance = glm::dot(tempVector, tempVector);
|
|
||||||
|
|
||||||
if (squareDistance <= ::triggerDistance) {
|
if (glm::dot(tempVector, tempVector) <= ::triggerDistance) {
|
||||||
// look for an audio mixer in our agent list
|
// use the AudioInjectionManager to thread this injector
|
||||||
Agent* audioMixer = AgentList::getInstance()->soloAgentOfType(AGENT_TYPE_AUDIO_MIXER);
|
AudioInjectionManager::threadInjector(&injector);
|
||||||
|
|
||||||
if (audioMixer) {
|
|
||||||
// we have an active audio mixer we can send data to
|
|
||||||
AudioInjectionManager::threadInjector(&injector);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// sleep for the correct amount of time to have data send be consistently timed
|
// find the current avatar mixer
|
||||||
if ((numMicrosecondsSleep = (AVATAR_MIXER_DATA_SEND_INTERVAL_MSECS * 1000) -
|
Agent* avatarMixer = agentList->soloAgentOfType(AGENT_TYPE_AVATAR_MIXER);
|
||||||
(usecTimestampNow() - usecTimestamp(&thisSend))) > 0) {
|
|
||||||
usleep(numMicrosecondsSleep);
|
// make sure we actually have an avatar mixer with an active socket
|
||||||
|
if (avatarMixer && avatarMixer->getActiveSocket() != NULL
|
||||||
|
&& (usecTimestampNow() - usecTimestamp(&lastSend) > AVATAR_MIXER_DATA_SEND_INTERVAL_MSECS)) {
|
||||||
|
|
||||||
|
// update the lastSend timeval to the current time
|
||||||
|
gettimeofday(&lastSend, NULL);
|
||||||
|
|
||||||
|
// use the UDPSocket instance attached to our agent list to ask avatar mixer for a list of avatars
|
||||||
|
agentList->getAgentSocket()->send(avatarMixer->getActiveSocket(),
|
||||||
|
&broadcastPacket,
|
||||||
|
sizeof(broadcastPacket));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// look for an audio mixer in our agent list
|
if (!injector.isInjectingAudio() && (::shouldLoopAudio || !::hasInjectedAudioOnce)) {
|
||||||
Agent* audioMixer = AgentList::getInstance()->soloAgentOfType(AGENT_TYPE_AUDIO_MIXER);
|
// use the AudioInjectionManager to thread this injector
|
||||||
|
AudioInjectionManager::threadInjector(&injector);
|
||||||
if (audioMixer) {
|
::hasInjectedAudioOnce = true;
|
||||||
injector.injectAudio(agentList->getAgentSocket(), audioMixer->getActiveSocket());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
float delay = 0;
|
|
||||||
int usecDelay = 0;
|
|
||||||
|
|
||||||
if (!::loopAudio) {
|
|
||||||
delay = randFloatInRange(::sleepIntervalMin, ::sleepIntervalMax);
|
|
||||||
usecDelay = delay * 1000 * 1000;
|
|
||||||
usleep(usecDelay);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// stop the receive agent data thread
|
|
||||||
stopReceiveAgentDataThread = true;
|
|
||||||
pthread_join(receiveAgentDataThread, NULL);
|
|
||||||
|
|
||||||
// stop the agent list's threads
|
// stop the agent list's threads
|
||||||
agentList->stopSilentAgentRemovalThread();
|
agentList->stopSilentAgentRemovalThread();
|
||||||
}
|
}
|
||||||
|
|
|
@ -986,6 +986,10 @@ void Application::doFalseColorizeInView() {
|
||||||
_voxels.falseColorizeInView(&_viewFrustum);
|
_voxels.falseColorizeInView(&_viewFrustum);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Application::doFalseColorizeOccluded() {
|
||||||
|
_voxels.falseColorizeOccluded();
|
||||||
|
}
|
||||||
|
|
||||||
void Application::doTrueVoxelColors() {
|
void Application::doTrueVoxelColors() {
|
||||||
_voxels.trueColorize();
|
_voxels.trueColorize();
|
||||||
}
|
}
|
||||||
|
@ -1006,6 +1010,10 @@ void Application::setWantsDelta(bool wantsDelta) {
|
||||||
_myAvatar.setWantDelta(wantsDelta);
|
_myAvatar.setWantDelta(wantsDelta);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Application::setWantsOcclusionCulling(bool wantsOcclusionCulling) {
|
||||||
|
_myAvatar.setWantOcclusionCulling(wantsOcclusionCulling);
|
||||||
|
}
|
||||||
|
|
||||||
void Application::updateVoxelModeActions() {
|
void Application::updateVoxelModeActions() {
|
||||||
// only the sender can be checked
|
// only the sender can be checked
|
||||||
foreach (QAction* action, _voxelModeActions->actions()) {
|
foreach (QAction* action, _voxelModeActions->actions()) {
|
||||||
|
@ -1325,9 +1333,6 @@ void Application::initMenu() {
|
||||||
_frustumOn->setShortcut(Qt::SHIFT | Qt::Key_F);
|
_frustumOn->setShortcut(Qt::SHIFT | Qt::Key_F);
|
||||||
(_viewFrustumFromOffset = frustumMenu->addAction(
|
(_viewFrustumFromOffset = frustumMenu->addAction(
|
||||||
"Use Offset Camera", this, SLOT(setFrustumOffset(bool)), Qt::SHIFT | Qt::Key_O))->setCheckable(true);
|
"Use Offset Camera", this, SLOT(setFrustumOffset(bool)), Qt::SHIFT | Qt::Key_O))->setCheckable(true);
|
||||||
(_cameraFrustum = frustumMenu->addAction("Switch Camera"))->setCheckable(true);
|
|
||||||
_cameraFrustum->setChecked(true);
|
|
||||||
_cameraFrustum->setShortcut(Qt::SHIFT | Qt::Key_C);
|
|
||||||
_frustumRenderModeAction = frustumMenu->addAction(
|
_frustumRenderModeAction = frustumMenu->addAction(
|
||||||
"Render Mode", this, SLOT(cycleFrustumRenderMode()), Qt::SHIFT | Qt::Key_R);
|
"Render Mode", this, SLOT(cycleFrustumRenderMode()), Qt::SHIFT | Qt::Key_R);
|
||||||
updateFrustumRenderModeAction();
|
updateFrustumRenderModeAction();
|
||||||
|
@ -1343,11 +1348,13 @@ void Application::initMenu() {
|
||||||
renderDebugMenu->addAction("FALSE Color Voxel Every Other Randomly", this, SLOT(doFalseRandomizeEveryOtherVoxelColors()));
|
renderDebugMenu->addAction("FALSE Color Voxel Every Other Randomly", this, SLOT(doFalseRandomizeEveryOtherVoxelColors()));
|
||||||
renderDebugMenu->addAction("FALSE Color Voxels by Distance", this, SLOT(doFalseColorizeByDistance()));
|
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 Voxel Out of View", this, SLOT(doFalseColorizeInView()));
|
||||||
renderDebugMenu->addAction("Show TRUE Colors", this, SLOT(doTrueVoxelColors()));
|
renderDebugMenu->addAction("FALSE Color Occluded Voxels", this, SLOT(doFalseColorizeOccluded()), Qt::CTRL | Qt::Key_O);
|
||||||
|
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 Res-In", this, SLOT(setWantsResIn(bool)))->setCheckable(true);
|
||||||
debugMenu->addAction("Wants Monochrome", this, SLOT(setWantsMonochrome(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);
|
debugMenu->addAction("Wants View Delta Sending", this, SLOT(setWantsDelta(bool)))->setCheckable(true);
|
||||||
|
debugMenu->addAction("Wants Occlusion Culling", this, SLOT(setWantsOcclusionCulling(bool)))->setCheckable(true);
|
||||||
|
|
||||||
QMenu* settingsMenu = menuBar->addMenu("Settings");
|
QMenu* settingsMenu = menuBar->addMenu("Settings");
|
||||||
(_settingsAutosave = settingsMenu->addAction("Autosave"))->setCheckable(true);
|
(_settingsAutosave = settingsMenu->addAction("Autosave"))->setCheckable(true);
|
||||||
|
@ -1758,15 +1765,7 @@ void Application::updateAvatar(float deltaTime) {
|
||||||
//
|
//
|
||||||
void Application::loadViewFrustum(Camera& camera, ViewFrustum& viewFrustum) {
|
void Application::loadViewFrustum(Camera& camera, ViewFrustum& viewFrustum) {
|
||||||
// We will use these below, from either the camera or head vectors calculated above
|
// We will use these below, from either the camera or head vectors calculated above
|
||||||
glm::vec3 position;
|
glm::vec3 position(camera.getPosition());
|
||||||
|
|
||||||
// Camera or Head?
|
|
||||||
if (_cameraFrustum->isChecked()) {
|
|
||||||
position = camera.getPosition();
|
|
||||||
} else {
|
|
||||||
position = _myAvatar.getHeadJointPosition();
|
|
||||||
}
|
|
||||||
|
|
||||||
float fov = camera.getFieldOfView();
|
float fov = camera.getFieldOfView();
|
||||||
float nearClip = camera.getNearClip();
|
float nearClip = camera.getNearClip();
|
||||||
float farClip = camera.getFarClip();
|
float farClip = camera.getFarClip();
|
||||||
|
@ -1933,7 +1932,7 @@ void Application::displayOculus(Camera& whichCamera) {
|
||||||
|
|
||||||
glPopMatrix();
|
glPopMatrix();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Application::displaySide(Camera& whichCamera) {
|
void Application::displaySide(Camera& whichCamera) {
|
||||||
// transform by eye offset
|
// transform by eye offset
|
||||||
|
|
||||||
|
|
|
@ -107,12 +107,14 @@ private slots:
|
||||||
void doFalseRandomizeVoxelColors();
|
void doFalseRandomizeVoxelColors();
|
||||||
void doFalseRandomizeEveryOtherVoxelColors();
|
void doFalseRandomizeEveryOtherVoxelColors();
|
||||||
void doFalseColorizeByDistance();
|
void doFalseColorizeByDistance();
|
||||||
|
void doFalseColorizeOccluded();
|
||||||
void doFalseColorizeInView();
|
void doFalseColorizeInView();
|
||||||
void doTrueVoxelColors();
|
void doTrueVoxelColors();
|
||||||
void doTreeStats();
|
void doTreeStats();
|
||||||
void setWantsMonochrome(bool wantsMonochrome);
|
void setWantsMonochrome(bool wantsMonochrome);
|
||||||
void setWantsResIn(bool wantsResIn);
|
void setWantsResIn(bool wantsResIn);
|
||||||
void setWantsDelta(bool wantsDelta);
|
void setWantsDelta(bool wantsDelta);
|
||||||
|
void setWantsOcclusionCulling(bool wantsOcclusionCulling);
|
||||||
void updateVoxelModeActions();
|
void updateVoxelModeActions();
|
||||||
void decreaseVoxelSize();
|
void decreaseVoxelSize();
|
||||||
void increaseVoxelSize();
|
void increaseVoxelSize();
|
||||||
|
@ -144,7 +146,6 @@ private:
|
||||||
void displaySide(Camera& whichCamera);
|
void displaySide(Camera& whichCamera);
|
||||||
void displayOverlay();
|
void displayOverlay();
|
||||||
void displayStats();
|
void displayStats();
|
||||||
|
|
||||||
void renderViewFrustum(ViewFrustum& viewFrustum);
|
void renderViewFrustum(ViewFrustum& viewFrustum);
|
||||||
|
|
||||||
void setupPaintingVoxel();
|
void setupPaintingVoxel();
|
||||||
|
@ -203,7 +204,6 @@ private:
|
||||||
QAction* _destructiveAddVoxel; // when doing voxel editing do we want them to be destructive
|
QAction* _destructiveAddVoxel; // when doing voxel editing do we want them to be destructive
|
||||||
QAction* _frustumOn; // Whether or not to display the debug view frustum
|
QAction* _frustumOn; // Whether or not to display the debug view frustum
|
||||||
QAction* _viewFrustumFromOffset; // Whether or not to offset the view of the frustum
|
QAction* _viewFrustumFromOffset; // Whether or not to offset the view of the frustum
|
||||||
QAction* _cameraFrustum; // which frustum to look at
|
|
||||||
QAction* _fullScreenMode; // whether we are in full screen mode
|
QAction* _fullScreenMode; // whether we are in full screen mode
|
||||||
QAction* _frustumRenderModeAction;
|
QAction* _frustumRenderModeAction;
|
||||||
QAction* _settingsAutosave; // Whether settings are saved automatically
|
QAction* _settingsAutosave; // Whether settings are saved automatically
|
||||||
|
|
|
@ -22,6 +22,7 @@
|
||||||
#include "Application.h"
|
#include "Application.h"
|
||||||
#include "Log.h"
|
#include "Log.h"
|
||||||
#include "VoxelConstants.h"
|
#include "VoxelConstants.h"
|
||||||
|
#include "CoverageMap.h"
|
||||||
#include "InterfaceConfig.h"
|
#include "InterfaceConfig.h"
|
||||||
#include "renderer/ProgramObject.h"
|
#include "renderer/ProgramObject.h"
|
||||||
|
|
||||||
|
@ -1156,3 +1157,139 @@ void VoxelSystem::copyFromTreeIntoSubTree(VoxelTree* sourceTree, VoxelNode* dest
|
||||||
_tree->copyFromTreeIntoSubTree(sourceTree, destinationNode);
|
_tree->copyFromTreeIntoSubTree(sourceTree, destinationNode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct FalseColorizeOccludedArgs {
|
||||||
|
ViewFrustum* viewFrustum;
|
||||||
|
CoverageMap* map;
|
||||||
|
VoxelTree* tree;
|
||||||
|
long totalVoxels;
|
||||||
|
long coloredVoxels;
|
||||||
|
long occludedVoxels;
|
||||||
|
long notOccludedVoxels;
|
||||||
|
long outOfView;
|
||||||
|
long subtreeVoxelsSkipped;
|
||||||
|
long nonLeaves;
|
||||||
|
long nonLeavesOutOfView;
|
||||||
|
long nonLeavesOccluded;
|
||||||
|
long stagedForDeletion;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct FalseColorizeSubTreeOperationArgs {
|
||||||
|
unsigned char color[NUMBER_OF_COLORS];
|
||||||
|
long voxelsTouched;
|
||||||
|
};
|
||||||
|
|
||||||
|
bool VoxelSystem::falseColorizeSubTreeOperation(VoxelNode* node, void* extraData) {
|
||||||
|
FalseColorizeSubTreeOperationArgs* args = (FalseColorizeSubTreeOperationArgs*) extraData;
|
||||||
|
node->setFalseColor(args->color[0], args->color[1], args->color[2]);
|
||||||
|
args->voxelsTouched++;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool VoxelSystem::falseColorizeOccludedOperation(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* voxelShadow = new VoxelProjectedPolygon(args->viewFrustum->getProjectedShadow(voxelBox));
|
||||||
|
|
||||||
|
// If we're not all in view, then ignore it, and just return. But keep searching...
|
||||||
|
if (!voxelShadow->getAllInView()) {
|
||||||
|
args->nonLeavesOutOfView++;
|
||||||
|
delete voxelShadow;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
CoverageMap::StorageResult result = args->map->checkMap(voxelShadow, false);
|
||||||
|
if (result == CoverageMap::OCCLUDED) {
|
||||||
|
args->nonLeavesOccluded++;
|
||||||
|
delete voxelShadow;
|
||||||
|
|
||||||
|
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 voxelShadow;
|
||||||
|
return true; // keep looking...
|
||||||
|
}
|
||||||
|
|
||||||
|
if (node->isLeaf() && node->isColored() && node->getShouldRender()) {
|
||||||
|
args->coloredVoxels++;
|
||||||
|
|
||||||
|
AABox voxelBox = node->getAABox();
|
||||||
|
voxelBox.scale(TREE_SCALE);
|
||||||
|
VoxelProjectedPolygon* voxelShadow = new VoxelProjectedPolygon(args->viewFrustum->getProjectedShadow(voxelBox));
|
||||||
|
|
||||||
|
// If we're not all in view, then ignore it, and just return. But keep searching...
|
||||||
|
if (!voxelShadow->getAllInView()) {
|
||||||
|
args->outOfView++;
|
||||||
|
delete voxelShadow;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
CoverageMap::StorageResult result = args->map->checkMap(voxelShadow, true);
|
||||||
|
if (result == CoverageMap::OCCLUDED) {
|
||||||
|
node->setFalseColor(255, 0, 0);
|
||||||
|
args->occludedVoxels++;
|
||||||
|
} else if (result == CoverageMap::STORED) {
|
||||||
|
args->notOccludedVoxels++;
|
||||||
|
//printLog("***** falseColorizeOccludedOperation() NODE is STORED *****\n");
|
||||||
|
} else if (result == CoverageMap::DOESNT_FIT) {
|
||||||
|
//printLog("***** falseColorizeOccludedOperation() NODE DOESNT_FIT???? *****\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true; // keep going!
|
||||||
|
}
|
||||||
|
void VoxelSystem::falseColorizeOccluded() {
|
||||||
|
PerformanceWarning warn(true, "falseColorizeOccluded()",true);
|
||||||
|
CoverageMap map;
|
||||||
|
FalseColorizeOccludedArgs args;
|
||||||
|
args.viewFrustum = Application::getInstance()->getViewFrustum();
|
||||||
|
args.map = ↦
|
||||||
|
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;
|
||||||
|
|
||||||
|
glm::vec3 position = args.viewFrustum->getPosition() * (1.0f/TREE_SCALE);
|
||||||
|
|
||||||
|
_tree->recurseTreeWithOperationDistanceSorted(falseColorizeOccludedOperation, 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",
|
||||||
|
args.totalVoxels, args.coloredVoxels, args.occludedVoxels,
|
||||||
|
args.notOccludedVoxels, args.outOfView, args.subtreeVoxelsSkipped,
|
||||||
|
args.stagedForDeletion,
|
||||||
|
args.nonLeaves, args.nonLeavesOutOfView, args.nonLeavesOccluded);
|
||||||
|
|
||||||
|
|
||||||
|
setupNewVoxelsForDrawing();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -56,6 +56,7 @@ public:
|
||||||
void falseColorizeInView(ViewFrustum* viewFrustum);
|
void falseColorizeInView(ViewFrustum* viewFrustum);
|
||||||
void falseColorizeDistanceFromView(ViewFrustum* viewFrustum);
|
void falseColorizeDistanceFromView(ViewFrustum* viewFrustum);
|
||||||
void falseColorizeRandomEveryOther();
|
void falseColorizeRandomEveryOther();
|
||||||
|
void falseColorizeOccluded();
|
||||||
|
|
||||||
void killLocalVoxels();
|
void killLocalVoxels();
|
||||||
void setRenderPipelineWarnings(bool on) { _renderWarningsOn = on; };
|
void setRenderPipelineWarnings(bool on) { _renderWarningsOn = on; };
|
||||||
|
@ -120,6 +121,8 @@ private:
|
||||||
static bool removeOutOfViewOperation(VoxelNode* node, void* extraData);
|
static bool removeOutOfViewOperation(VoxelNode* node, void* extraData);
|
||||||
static bool falseColorizeRandomEveryOtherOperation(VoxelNode* node, void* extraData);
|
static bool falseColorizeRandomEveryOtherOperation(VoxelNode* node, void* extraData);
|
||||||
static bool collectStatsForTreesAndVBOsOperation(VoxelNode* node, void* extraData);
|
static bool collectStatsForTreesAndVBOsOperation(VoxelNode* node, void* extraData);
|
||||||
|
static bool falseColorizeOccludedOperation(VoxelNode* node, void* extraData);
|
||||||
|
static bool falseColorizeSubTreeOperation(VoxelNode* node, void* extraData);
|
||||||
|
|
||||||
int updateNodeInArraysAsFullVBO(VoxelNode* node);
|
int updateNodeInArraysAsFullVBO(VoxelNode* node);
|
||||||
int updateNodeInArraysAsPartialVBO(VoxelNode* node);
|
int updateNodeInArraysAsPartialVBO(VoxelNode* node);
|
||||||
|
|
122
jenkins/jobs.groovy
Normal file
122
jenkins/jobs.groovy
Normal file
|
@ -0,0 +1,122 @@
|
||||||
|
def hifiJob(String targetName, Boolean deploy) {
|
||||||
|
def JENKINS_URL = 'https://jenkins.below92.com/'
|
||||||
|
def GITHUB_HOOK_URL = 'https://github.com/worklist/hifi/'
|
||||||
|
def GIT_REPO_URL = 'git@github.com:worklist/hifi.git'
|
||||||
|
def HIPCHAT_ROOM = 'High Fidelity'
|
||||||
|
|
||||||
|
job {
|
||||||
|
name "hifi-${targetName}"
|
||||||
|
logRotator(7, -1, -1, -1)
|
||||||
|
|
||||||
|
scm {
|
||||||
|
git(GIT_REPO_URL, 'master') { node ->
|
||||||
|
node << includedRegions << "${targetName}/.*\nlibraries/.*"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
configure { project ->
|
||||||
|
project / 'properties' << {
|
||||||
|
'com.coravy.hudson.plugins.github.GithubProjectProperty' {
|
||||||
|
projectUrl GITHUB_HOOK_URL
|
||||||
|
}
|
||||||
|
|
||||||
|
'jenkins.plugins.hipchat.HipChatNotifier_-HipChatJobProperty' {
|
||||||
|
room HIPCHAT_ROOM
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
project / 'triggers' << 'com.cloudbees.jenkins.GitHubPushTrigger' {
|
||||||
|
spec ''
|
||||||
|
}
|
||||||
|
|
||||||
|
project / 'builders' << 'hudson.plugins.cmake.CmakeBuilder' {
|
||||||
|
sourceDir targetName
|
||||||
|
buildDir 'build'
|
||||||
|
installDir ''
|
||||||
|
buildType 'RelWithDebInfo'
|
||||||
|
generator 'Unix Makefiles'
|
||||||
|
makeCommand 'make'
|
||||||
|
installCommand 'make install'
|
||||||
|
preloadScript ''
|
||||||
|
cmakeArgs ''
|
||||||
|
projectCmakePath '/usr/bin/cmake'
|
||||||
|
cleanBuild 'false'
|
||||||
|
cleanInstallDir 'false'
|
||||||
|
builderImpl ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (deploy) {
|
||||||
|
publishers {
|
||||||
|
publishScp("${ARTIFACT_DESTINATION}") {
|
||||||
|
entry("**/build/${targetName}", "deploy/${targetName}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
configure { project ->
|
||||||
|
|
||||||
|
project / 'publishers' << {
|
||||||
|
if (deploy) {
|
||||||
|
'hudson.plugins.postbuildtask.PostbuildTask' {
|
||||||
|
'tasks' {
|
||||||
|
'hudson.plugins.postbuildtask.TaskProperties' {
|
||||||
|
logTexts {
|
||||||
|
'hudson.plugins.postbuildtask.LogProperties' {
|
||||||
|
logText '.'
|
||||||
|
operator 'AND'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
EscalateStatus true
|
||||||
|
RunIfJobSuccessful true
|
||||||
|
script "curl -d 'action=deploy&role=highfidelity-live&revision=${targetName}' https://${ARTIFACT_DESTINATION}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
'jenkins.plugins.hipchat.HipChatNotifier' {
|
||||||
|
jenkinsUrl JENKINS_URL
|
||||||
|
authToken "${HIPCHAT_AUTH_TOKEN}"
|
||||||
|
room HIPCHAT_ROOM
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def deployTargets = [
|
||||||
|
'animation-server',
|
||||||
|
'audio-mixer',
|
||||||
|
'avatar-mixer',
|
||||||
|
'domain-server',
|
||||||
|
'eve',
|
||||||
|
'pairing-server',
|
||||||
|
'space-server',
|
||||||
|
'voxel-server'
|
||||||
|
]
|
||||||
|
|
||||||
|
/* setup all of the deploys jobs that use the above template */
|
||||||
|
deployTargets.each {
|
||||||
|
hifiJob(it, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
/* setup the interface job, doesn't deploy */
|
||||||
|
hifiJob('interface', false)
|
||||||
|
|
||||||
|
/* setup the parametrized-build job for builds from jenkins */
|
||||||
|
parameterizedJob = hifiJob('$TARGET', true)
|
||||||
|
parameterizedJob.with {
|
||||||
|
name 'hifi-branch-deploy'
|
||||||
|
parameters {
|
||||||
|
stringParam('GITHUB_USER', '', "Specifies the name of the GitHub user that we're building from.")
|
||||||
|
stringParam('GIT_BRANCH', '', "Specifies the specific branch to build and deploy.")
|
||||||
|
stringParam('HOSTNAME', 'devel.highfidelity.io', "Specifies the hostname to deploy against.")
|
||||||
|
stringParam('TARGET', '', "What server to build specifically")
|
||||||
|
}
|
||||||
|
scm {
|
||||||
|
git('git@github.com:/$GITHUB_USER/hifi.git', '$GIT_BRANCH') { node ->
|
||||||
|
node / 'wipeOutWorkspace' << true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -35,6 +35,7 @@ AvatarData::AvatarData(Agent* owningAgent) :
|
||||||
_wantResIn(false),
|
_wantResIn(false),
|
||||||
_wantColor(true),
|
_wantColor(true),
|
||||||
_wantDelta(false),
|
_wantDelta(false),
|
||||||
|
_wantOcclusionCulling(false),
|
||||||
_headData(NULL)
|
_headData(NULL)
|
||||||
{
|
{
|
||||||
|
|
||||||
|
@ -106,9 +107,10 @@ int AvatarData::getBroadcastData(unsigned char* destinationBuffer) {
|
||||||
|
|
||||||
// bitMask of less than byte wide items
|
// bitMask of less than byte wide items
|
||||||
unsigned char bitItems = 0;
|
unsigned char bitItems = 0;
|
||||||
if (_wantResIn) { setAtBit(bitItems,WANT_RESIN_AT_BIT); }
|
if (_wantResIn) { setAtBit(bitItems, WANT_RESIN_AT_BIT); }
|
||||||
if (_wantColor) { setAtBit(bitItems,WANT_COLOR_AT_BIT); }
|
if (_wantColor) { setAtBit(bitItems, WANT_COLOR_AT_BIT); }
|
||||||
if (_wantDelta) { setAtBit(bitItems,WANT_DELTA_AT_BIT); }
|
if (_wantDelta) { setAtBit(bitItems, WANT_DELTA_AT_BIT); }
|
||||||
|
if (_wantOcclusionCulling) { setAtBit(bitItems, WANT_OCCLUSION_CULLING_BIT); }
|
||||||
|
|
||||||
// key state
|
// key state
|
||||||
setSemiNibbleAt(bitItems,KEY_STATE_START_BIT,_keyState);
|
setSemiNibbleAt(bitItems,KEY_STATE_START_BIT,_keyState);
|
||||||
|
@ -192,9 +194,10 @@ int AvatarData::parseData(unsigned char* sourceBuffer, int numBytes) {
|
||||||
// voxel sending features...
|
// voxel sending features...
|
||||||
unsigned char bitItems = 0;
|
unsigned char bitItems = 0;
|
||||||
bitItems = (unsigned char)*sourceBuffer++;
|
bitItems = (unsigned char)*sourceBuffer++;
|
||||||
_wantResIn = oneAtBit(bitItems,WANT_RESIN_AT_BIT);
|
_wantResIn = oneAtBit(bitItems, WANT_RESIN_AT_BIT);
|
||||||
_wantColor = oneAtBit(bitItems,WANT_COLOR_AT_BIT);
|
_wantColor = oneAtBit(bitItems, WANT_COLOR_AT_BIT);
|
||||||
_wantDelta = oneAtBit(bitItems,WANT_DELTA_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
|
// key state, stored as a semi-nibble in the bitItems
|
||||||
_keyState = (KeyState)getSemiNibbleAt(bitItems,KEY_STATE_START_BIT);
|
_keyState = (KeyState)getSemiNibbleAt(bitItems,KEY_STATE_START_BIT);
|
||||||
|
|
|
@ -23,6 +23,7 @@ const int WANT_COLOR_AT_BIT = 1;
|
||||||
const int WANT_DELTA_AT_BIT = 2;
|
const int WANT_DELTA_AT_BIT = 2;
|
||||||
const int KEY_STATE_START_BIT = 3; // 4th and 5th bits
|
const int KEY_STATE_START_BIT = 3; // 4th and 5th bits
|
||||||
const int HAND_STATE_START_BIT = 5; // 6th and 7th bits
|
const int HAND_STATE_START_BIT = 5; // 6th and 7th bits
|
||||||
|
const int WANT_OCCLUSION_CULLING_BIT = 7; // 8th bit
|
||||||
|
|
||||||
const float MAX_AUDIO_LOUDNESS = 1000.0; // close enough for mouth animation
|
const float MAX_AUDIO_LOUDNESS = 1000.0; // close enough for mouth animation
|
||||||
|
|
||||||
|
@ -89,9 +90,11 @@ public:
|
||||||
bool getWantResIn() const { return _wantResIn; }
|
bool getWantResIn() const { return _wantResIn; }
|
||||||
bool getWantColor() const { return _wantColor; }
|
bool getWantColor() const { return _wantColor; }
|
||||||
bool getWantDelta() const { return _wantDelta; }
|
bool getWantDelta() const { return _wantDelta; }
|
||||||
|
bool getWantOcclusionCulling() const { return _wantOcclusionCulling; }
|
||||||
void setWantResIn(bool wantResIn) { _wantResIn = wantResIn; }
|
void setWantResIn(bool wantResIn) { _wantResIn = wantResIn; }
|
||||||
void setWantColor(bool wantColor) { _wantColor = wantColor; }
|
void setWantColor(bool wantColor) { _wantColor = wantColor; }
|
||||||
void setWantDelta(bool wantDelta) { _wantDelta = wantDelta; }
|
void setWantDelta(bool wantDelta) { _wantDelta = wantDelta; }
|
||||||
|
void setWantOcclusionCulling(bool wantOcclusionCulling) { _wantOcclusionCulling = wantOcclusionCulling; }
|
||||||
|
|
||||||
void setHeadData(HeadData* headData) { _headData = headData; }
|
void setHeadData(HeadData* headData) { _headData = headData; }
|
||||||
|
|
||||||
|
@ -125,6 +128,7 @@ protected:
|
||||||
bool _wantResIn;
|
bool _wantResIn;
|
||||||
bool _wantColor;
|
bool _wantColor;
|
||||||
bool _wantDelta;
|
bool _wantDelta;
|
||||||
|
bool _wantOcclusionCulling;
|
||||||
|
|
||||||
HeadData* _headData;
|
HeadData* _headData;
|
||||||
private:
|
private:
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
// hifi
|
// hifi
|
||||||
//
|
//
|
||||||
// Created by Brad Hefta-Gaub on 3/29/13.
|
// Created by Brad Hefta-Gaub on 3/29/13.
|
||||||
|
// Copyright (c) 2013 HighFidelity, Inc. All rights reserved.
|
||||||
//
|
//
|
||||||
// Poor-man's performance stats collector class. Useful for collecting timing
|
// Poor-man's performance stats collector class. Useful for collecting timing
|
||||||
// details from various portions of the code.
|
// details from various portions of the code.
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
// hifi
|
// hifi
|
||||||
//
|
//
|
||||||
// Created by Brad Hefta-Gaub on 3/29/13.
|
// Created by Brad Hefta-Gaub on 3/29/13.
|
||||||
|
// Copyright (c) 2013 HighFidelity, Inc. All rights reserved.
|
||||||
//
|
//
|
||||||
// Poor-man's performance stats collector class. Useful for collecting timing
|
// Poor-man's performance stats collector class. Useful for collecting timing
|
||||||
// details from various portions of the code.
|
// details from various portions of the code.
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
// hifi
|
// hifi
|
||||||
//
|
//
|
||||||
// Created by Stephen Birarda on 2/22/13.
|
// Created by Stephen Birarda on 2/22/13.
|
||||||
//
|
// Copyright (c) 2013 HighFidelity, Inc. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
|
@ -404,11 +404,11 @@ void printVoxelCode(unsigned char* voxelCode) {
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
// Inserts the value and key into three arrays sorted by the key array, the first array is the value,
|
// Inserts the value and key into three arrays sorted by the key array, the first array is the value,
|
||||||
// the second array is a sorted key for the value, the third array is the index for the value in it original
|
// the second array is a sorted key for the value, the third array is the index for the value in it original
|
||||||
// non-sorted array
|
// non-sorted array
|
||||||
// returns -1 if size exceeded
|
// returns -1 if size exceeded
|
||||||
|
// originalIndexArray is optional
|
||||||
int insertIntoSortedArrays(void* value, float key, int originalIndex,
|
int insertIntoSortedArrays(void* value, float key, int originalIndex,
|
||||||
void** valueArray, float* keyArray, int* originalIndexArray,
|
void** valueArray, float* keyArray, int* originalIndexArray,
|
||||||
int currentCount, int maxCount) {
|
int currentCount, int maxCount) {
|
||||||
|
@ -422,15 +422,19 @@ int insertIntoSortedArrays(void* value, float key, int originalIndex,
|
||||||
// i is our desired location
|
// i is our desired location
|
||||||
// shift array elements to the right
|
// shift array elements to the right
|
||||||
if (i < currentCount && i+1 < maxCount) {
|
if (i < currentCount && i+1 < maxCount) {
|
||||||
memcpy(&valueArray[i + 1], &valueArray[i], sizeof(void*) * (currentCount - i));
|
memmove(&valueArray[i + 1], &valueArray[i], sizeof(void*) * (currentCount - i));
|
||||||
memcpy(&keyArray[i + 1], &keyArray[i], sizeof(float) * (currentCount - i));
|
memmove(&keyArray[i + 1], &keyArray[i], sizeof(float) * (currentCount - i));
|
||||||
memcpy(&originalIndexArray[i + 1], &originalIndexArray[i], sizeof(int) * (currentCount - i));
|
if (originalIndexArray) {
|
||||||
|
memmove(&originalIndexArray[i + 1], &originalIndexArray[i], sizeof(int) * (currentCount - i));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// place new element at i
|
// place new element at i
|
||||||
valueArray[i] = value;
|
valueArray[i] = value;
|
||||||
keyArray[i] = key;
|
keyArray[i] = key;
|
||||||
originalIndexArray[i] = originalIndex;
|
if (originalIndexArray) {
|
||||||
|
originalIndexArray[i] = originalIndex;
|
||||||
|
}
|
||||||
return currentCount + 1;
|
return currentCount + 1;
|
||||||
}
|
}
|
||||||
return -1; // error case
|
return -1; // error case
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
// hifi
|
// hifi
|
||||||
//
|
//
|
||||||
// Created by Stephen Birarda on 2/22/13.
|
// Created by Stephen Birarda on 2/22/13.
|
||||||
//
|
// Copyright (c) 2013 HighFidelity, Inc. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
#ifndef __hifi__SharedUtil__
|
#ifndef __hifi__SharedUtil__
|
||||||
|
|
|
@ -3,13 +3,13 @@
|
||||||
// hifi
|
// hifi
|
||||||
//
|
//
|
||||||
// Created by Philip Rosedale on 3/12/13.
|
// Created by Philip Rosedale on 3/12/13.
|
||||||
//
|
// Copyright (c) 2013 HighFidelity, Inc. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
#include "StdDev.h"
|
#include "StdDev.h"
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
|
||||||
const int MAX_STDEV_SAMPLES = 1000; // Don't add more than this number of samples.
|
const int MAX_STDEV_SAMPLES = 1000;
|
||||||
|
|
||||||
StDev::StDev() {
|
StDev::StDev() {
|
||||||
data = new float[MAX_STDEV_SAMPLES];
|
data = new float[MAX_STDEV_SAMPLES];
|
||||||
|
|
|
@ -1,332 +1,353 @@
|
||||||
//
|
//
|
||||||
// AABox.h - Axis Aligned Boxes
|
// AABox.h - Axis Aligned Boxes
|
||||||
// hifi
|
// hifi
|
||||||
//
|
//
|
||||||
// Added by Brad Hefta-Gaub on 04/11/13.
|
// Added by Brad Hefta-Gaub on 04/11/13.
|
||||||
// Originally from lighthouse3d. Modified to utilize glm::vec3 and clean up to our coding standards
|
// Originally from lighthouse3d. Modified to utilize glm::vec3 and clean up to our coding standards
|
||||||
//
|
//
|
||||||
// Simple axis aligned box class.
|
// Simple axis aligned box class.
|
||||||
//
|
//
|
||||||
|
|
||||||
#include "SharedUtil.h"
|
#include "SharedUtil.h"
|
||||||
|
|
||||||
#include "AABox.h"
|
#include "AABox.h"
|
||||||
#include "GeometryUtil.h"
|
#include "GeometryUtil.h"
|
||||||
|
|
||||||
|
|
||||||
void AABox::scale(float scale) {
|
void AABox::scale(float scale) {
|
||||||
_corner = _corner * scale;
|
_corner = _corner * scale;
|
||||||
_size = _size * scale;
|
_size = _size * scale;
|
||||||
_center = _center * scale;
|
_center = _center * scale;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void AABox::setBox(const glm::vec3& corner, const glm::vec3& size) {
|
glm::vec3 AABox::getVertex(BoxVertex vertex) const {
|
||||||
_corner = corner;
|
switch (vertex) {
|
||||||
_size = size;
|
case BOTTOM_LEFT_NEAR:
|
||||||
|
return _corner + glm::vec3(_size.x, 0, 0);
|
||||||
// In the event that the caller gave us negative sizes, fix things up to be reasonable
|
case BOTTOM_RIGHT_NEAR:
|
||||||
if (_size.x < 0.0) {
|
return _corner;
|
||||||
_size.x = -size.x;
|
case TOP_RIGHT_NEAR:
|
||||||
_corner.x -= _size.x;
|
return _corner + glm::vec3(0, _size.y, 0);
|
||||||
}
|
case TOP_LEFT_NEAR:
|
||||||
if (_size.y < 0.0) {
|
return _corner + glm::vec3(_size.x, _size.y, 0);
|
||||||
_size.y = -size.y;
|
case BOTTOM_LEFT_FAR:
|
||||||
_corner.y -= _size.y;
|
return _corner + glm::vec3(_size.x, 0, _size.z);
|
||||||
}
|
case BOTTOM_RIGHT_FAR:
|
||||||
if (_size.z < 0.0) {
|
return _corner + glm::vec3(0, 0, _size.z);
|
||||||
_size.z = -size.z;
|
case TOP_RIGHT_FAR:
|
||||||
_corner.z -= _size.z;
|
return _corner + glm::vec3(0, _size.y, _size.z);
|
||||||
}
|
case TOP_LEFT_FAR:
|
||||||
_center = _corner + (_size * 0.5f);
|
return _corner + _size;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
glm::vec3 AABox::getVertexP(const glm::vec3 &normal) const {
|
|
||||||
glm::vec3 res = _corner;
|
void AABox::setBox(const glm::vec3& corner, const glm::vec3& size) {
|
||||||
if (normal.x > 0)
|
_corner = corner;
|
||||||
res.x += _size.x;
|
_size = size;
|
||||||
|
|
||||||
if (normal.y > 0)
|
// In the event that the caller gave us negative sizes, fix things up to be reasonable
|
||||||
res.y += _size.y;
|
if (_size.x < 0.0) {
|
||||||
|
_size.x = -size.x;
|
||||||
if (normal.z > 0)
|
_corner.x -= _size.x;
|
||||||
res.z += _size.z;
|
}
|
||||||
|
if (_size.y < 0.0) {
|
||||||
return(res);
|
_size.y = -size.y;
|
||||||
}
|
_corner.y -= _size.y;
|
||||||
|
}
|
||||||
|
if (_size.z < 0.0) {
|
||||||
|
_size.z = -size.z;
|
||||||
glm::vec3 AABox::getVertexN(const glm::vec3 &normal) const {
|
_corner.z -= _size.z;
|
||||||
glm::vec3 res = _corner;
|
}
|
||||||
|
_center = _corner + (_size * 0.5f);
|
||||||
if (normal.x < 0)
|
}
|
||||||
res.x += _size.x;
|
|
||||||
|
glm::vec3 AABox::getVertexP(const glm::vec3& normal) const {
|
||||||
if (normal.y < 0)
|
glm::vec3 result = _corner;
|
||||||
res.y += _size.y;
|
if (normal.x > 0) {
|
||||||
|
result.x += _size.x;
|
||||||
if (normal.z < 0)
|
}
|
||||||
res.z += _size.z;
|
if (normal.y > 0) {
|
||||||
|
result.y += _size.y;
|
||||||
return(res);
|
}
|
||||||
}
|
if (normal.z > 0) {
|
||||||
|
result.z += _size.z;
|
||||||
// determines whether a value is within the extents
|
}
|
||||||
static bool isWithin(float value, float corner, float size) {
|
return result;
|
||||||
return value >= corner && value <= corner + size;
|
}
|
||||||
}
|
|
||||||
|
glm::vec3 AABox::getVertexN(const glm::vec3& normal) const {
|
||||||
bool AABox::contains(const glm::vec3& point) const {
|
glm::vec3 result = _corner;
|
||||||
return isWithin(point.x, _corner.x, _size.x) &&
|
|
||||||
isWithin(point.y, _corner.y, _size.y) &&
|
if (normal.x < 0) {
|
||||||
isWithin(point.z, _corner.z, _size.z);
|
result.x += _size.x;
|
||||||
}
|
}
|
||||||
|
|
||||||
// determines whether a value is within the expanded extents
|
if (normal.y < 0) {
|
||||||
static bool isWithinExpanded(float value, float corner, float size, float expansion) {
|
result.y += _size.y;
|
||||||
return value >= corner - expansion && value <= corner + size + expansion;
|
}
|
||||||
}
|
|
||||||
|
if (normal.z < 0) {
|
||||||
bool AABox::expandedContains(const glm::vec3& point, float expansion) const {
|
result.z += _size.z;
|
||||||
return isWithinExpanded(point.x, _corner.x, _size.x, expansion) &&
|
}
|
||||||
isWithinExpanded(point.y, _corner.y, _size.y, expansion) &&
|
|
||||||
isWithinExpanded(point.z, _corner.z, _size.z, expansion);
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
// finds the intersection between a ray and the facing plane on one axis
|
// determines whether a value is within the extents
|
||||||
static bool findIntersection(float origin, float direction, float corner, float size, float& distance) {
|
static bool isWithin(float value, float corner, float size) {
|
||||||
if (direction > EPSILON) {
|
return value >= corner && value <= corner + size;
|
||||||
distance = (corner - origin) / direction;
|
}
|
||||||
return true;
|
|
||||||
|
bool AABox::contains(const glm::vec3& point) const {
|
||||||
} else if (direction < -EPSILON) {
|
return isWithin(point.x, _corner.x, _size.x) &&
|
||||||
distance = (corner + size - origin) / direction;
|
isWithin(point.y, _corner.y, _size.y) &&
|
||||||
return true;
|
isWithin(point.z, _corner.z, _size.z);
|
||||||
}
|
}
|
||||||
return false;
|
|
||||||
}
|
// determines whether a value is within the expanded extents
|
||||||
|
static bool isWithinExpanded(float value, float corner, float size, float expansion) {
|
||||||
bool AABox::expandedIntersectsSegment(const glm::vec3& start, const glm::vec3& end, float expansion) const {
|
return value >= corner - expansion && value <= corner + size + expansion;
|
||||||
// handle the trivial cases where the expanded box contains the start or end
|
}
|
||||||
if (expandedContains(start, expansion) || expandedContains(end, expansion)) {
|
|
||||||
return true;
|
bool AABox::expandedContains(const glm::vec3& point, float expansion) const {
|
||||||
}
|
return isWithinExpanded(point.x, _corner.x, _size.x, expansion) &&
|
||||||
// check each axis
|
isWithinExpanded(point.y, _corner.y, _size.y, expansion) &&
|
||||||
glm::vec3 expandedCorner = _corner - glm::vec3(expansion, expansion, expansion);
|
isWithinExpanded(point.z, _corner.z, _size.z, expansion);
|
||||||
glm::vec3 expandedSize = _size + glm::vec3(expansion, expansion, expansion) * 2.0f;
|
}
|
||||||
glm::vec3 direction = end - start;
|
|
||||||
float axisDistance;
|
// finds the intersection between a ray and the facing plane on one axis
|
||||||
return (findIntersection(start.x, direction.x, expandedCorner.x, expandedSize.x, axisDistance) &&
|
static bool findIntersection(float origin, float direction, float corner, float size, float& distance) {
|
||||||
axisDistance >= 0.0f && axisDistance <= 1.0f &&
|
if (direction > EPSILON) {
|
||||||
isWithin(start.y + axisDistance*direction.y, expandedCorner.y, expandedSize.y) &&
|
distance = (corner - origin) / direction;
|
||||||
isWithin(start.z + axisDistance*direction.z, expandedCorner.z, expandedSize.z)) ||
|
return true;
|
||||||
(findIntersection(start.y, direction.y, expandedCorner.y, expandedSize.y, axisDistance) &&
|
} else if (direction < -EPSILON) {
|
||||||
axisDistance >= 0.0f && axisDistance <= 1.0f &&
|
distance = (corner + size - origin) / direction;
|
||||||
isWithin(start.x + axisDistance*direction.x, expandedCorner.x, expandedSize.x) &&
|
return true;
|
||||||
isWithin(start.z + axisDistance*direction.z, expandedCorner.z, expandedSize.z)) ||
|
}
|
||||||
(findIntersection(start.z, direction.z, expandedCorner.z, expandedSize.z, axisDistance) &&
|
return false;
|
||||||
axisDistance >= 0.0f && axisDistance <= 1.0f &&
|
}
|
||||||
isWithin(start.y + axisDistance*direction.y, expandedCorner.y, expandedSize.y) &&
|
|
||||||
isWithin(start.x + axisDistance*direction.x, expandedCorner.x, expandedSize.x));
|
bool AABox::expandedIntersectsSegment(const glm::vec3& start, const glm::vec3& end, float expansion) const {
|
||||||
}
|
// handle the trivial cases where the expanded box contains the start or end
|
||||||
|
if (expandedContains(start, expansion) || expandedContains(end, expansion)) {
|
||||||
bool AABox::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance, BoxFace& face) const {
|
return true;
|
||||||
// handle the trivial case where the box contains the origin
|
}
|
||||||
if (contains(origin)) {
|
// check each axis
|
||||||
distance = 0;
|
glm::vec3 expandedCorner = _corner - glm::vec3(expansion, expansion, expansion);
|
||||||
return true;
|
glm::vec3 expandedSize = _size + glm::vec3(expansion, expansion, expansion) * 2.0f;
|
||||||
}
|
glm::vec3 direction = end - start;
|
||||||
// check each axis
|
float axisDistance;
|
||||||
float axisDistance;
|
return (findIntersection(start.x, direction.x, expandedCorner.x, expandedSize.x, axisDistance) &&
|
||||||
if ((findIntersection(origin.x, direction.x, _corner.x, _size.x, axisDistance) && axisDistance >= 0 &&
|
axisDistance >= 0.0f && axisDistance <= 1.0f &&
|
||||||
isWithin(origin.y + axisDistance*direction.y, _corner.y, _size.y) &&
|
isWithin(start.y + axisDistance*direction.y, expandedCorner.y, expandedSize.y) &&
|
||||||
isWithin(origin.z + axisDistance*direction.z, _corner.z, _size.z))) {
|
isWithin(start.z + axisDistance*direction.z, expandedCorner.z, expandedSize.z)) ||
|
||||||
distance = axisDistance;
|
(findIntersection(start.y, direction.y, expandedCorner.y, expandedSize.y, axisDistance) &&
|
||||||
face = direction.x > 0 ? MIN_X_FACE : MAX_X_FACE;
|
axisDistance >= 0.0f && axisDistance <= 1.0f &&
|
||||||
return true;
|
isWithin(start.x + axisDistance*direction.x, expandedCorner.x, expandedSize.x) &&
|
||||||
}
|
isWithin(start.z + axisDistance*direction.z, expandedCorner.z, expandedSize.z)) ||
|
||||||
if ((findIntersection(origin.y, direction.y, _corner.y, _size.y, axisDistance) && axisDistance >= 0 &&
|
(findIntersection(start.z, direction.z, expandedCorner.z, expandedSize.z, axisDistance) &&
|
||||||
isWithin(origin.x + axisDistance*direction.x, _corner.x, _size.x) &&
|
axisDistance >= 0.0f && axisDistance <= 1.0f &&
|
||||||
isWithin(origin.z + axisDistance*direction.z, _corner.z, _size.z))) {
|
isWithin(start.y + axisDistance*direction.y, expandedCorner.y, expandedSize.y) &&
|
||||||
distance = axisDistance;
|
isWithin(start.x + axisDistance*direction.x, expandedCorner.x, expandedSize.x));
|
||||||
face = direction.y > 0 ? MIN_Y_FACE : MAX_Y_FACE;
|
}
|
||||||
return true;
|
|
||||||
}
|
bool AABox::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance, BoxFace& face) const {
|
||||||
if ((findIntersection(origin.z, direction.z, _corner.z, _size.z, axisDistance) && axisDistance >= 0 &&
|
// handle the trivial case where the box contains the origin
|
||||||
isWithin(origin.y + axisDistance*direction.y, _corner.y, _size.y) &&
|
if (contains(origin)) {
|
||||||
isWithin(origin.x + axisDistance*direction.x, _corner.x, _size.x))) {
|
distance = 0;
|
||||||
distance = axisDistance;
|
return true;
|
||||||
face = direction.z > 0 ? MIN_Z_FACE : MAX_Z_FACE;
|
}
|
||||||
return true;
|
// check each axis
|
||||||
}
|
float axisDistance;
|
||||||
return false;
|
if ((findIntersection(origin.x, direction.x, _corner.x, _size.x, axisDistance) && axisDistance >= 0 &&
|
||||||
}
|
isWithin(origin.y + axisDistance*direction.y, _corner.y, _size.y) &&
|
||||||
|
isWithin(origin.z + axisDistance*direction.z, _corner.z, _size.z))) {
|
||||||
bool AABox::findSpherePenetration(const glm::vec3& center, float radius, glm::vec3& penetration) const {
|
distance = axisDistance;
|
||||||
glm::vec4 center4 = glm::vec4(center, 1.0f);
|
face = direction.x > 0 ? MIN_X_FACE : MAX_X_FACE;
|
||||||
|
return true;
|
||||||
float minPenetrationLength = FLT_MAX;
|
}
|
||||||
for (int i = 0; i < FACE_COUNT; i++) {
|
if ((findIntersection(origin.y, direction.y, _corner.y, _size.y, axisDistance) && axisDistance >= 0 &&
|
||||||
glm::vec4 facePlane = getPlane((BoxFace)i);
|
isWithin(origin.x + axisDistance*direction.x, _corner.x, _size.x) &&
|
||||||
glm::vec3 vector = getClosestPointOnFace(center, (BoxFace)i) - center;
|
isWithin(origin.z + axisDistance*direction.z, _corner.z, _size.z))) {
|
||||||
if (glm::dot(center4, getPlane((BoxFace)i)) >= 0.0f) {
|
distance = axisDistance;
|
||||||
// outside this face, so use vector to closest point to determine penetration
|
face = direction.y > 0 ? MIN_Y_FACE : MAX_Y_FACE;
|
||||||
return ::findSpherePenetration(vector, glm::vec3(-facePlane), radius, penetration);
|
return true;
|
||||||
}
|
}
|
||||||
float vectorLength = glm::length(vector);
|
if ((findIntersection(origin.z, direction.z, _corner.z, _size.z, axisDistance) && axisDistance >= 0 &&
|
||||||
if (vectorLength < minPenetrationLength) {
|
isWithin(origin.y + axisDistance*direction.y, _corner.y, _size.y) &&
|
||||||
// remember the smallest penetration vector; if we're inside all faces, we'll use that
|
isWithin(origin.x + axisDistance*direction.x, _corner.x, _size.x))) {
|
||||||
penetration = (vectorLength < EPSILON) ? glm::vec3(-facePlane) * radius :
|
distance = axisDistance;
|
||||||
vector * ((vectorLength + radius) / -vectorLength);
|
face = direction.z > 0 ? MIN_Z_FACE : MAX_Z_FACE;
|
||||||
minPenetrationLength = vectorLength;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
return false;
|
||||||
|
}
|
||||||
return true;
|
|
||||||
}
|
bool AABox::findSpherePenetration(const glm::vec3& center, float radius, glm::vec3& penetration) const {
|
||||||
|
glm::vec4 center4 = glm::vec4(center, 1.0f);
|
||||||
bool AABox::findCapsulePenetration(const glm::vec3& start, const glm::vec3& end, float radius, glm::vec3& penetration) const {
|
|
||||||
glm::vec4 start4 = glm::vec4(start, 1.0f);
|
float minPenetrationLength = FLT_MAX;
|
||||||
glm::vec4 end4 = glm::vec4(end, 1.0f);
|
for (int i = 0; i < FACE_COUNT; i++) {
|
||||||
glm::vec4 startToEnd = glm::vec4(end - start, 0.0f);
|
glm::vec4 facePlane = getPlane((BoxFace)i);
|
||||||
|
glm::vec3 vector = getClosestPointOnFace(center, (BoxFace)i) - center;
|
||||||
float minPenetrationLength = FLT_MAX;
|
if (glm::dot(center4, getPlane((BoxFace)i)) >= 0.0f) {
|
||||||
for (int i = 0; i < FACE_COUNT; i++) {
|
// outside this face, so use vector to closest point to determine penetration
|
||||||
// find the vector from the segment to the closest point on the face (starting from deeper end)
|
return ::findSpherePenetration(vector, glm::vec3(-facePlane), radius, penetration);
|
||||||
glm::vec4 facePlane = getPlane((BoxFace)i);
|
}
|
||||||
glm::vec3 closest = (glm::dot(start4, facePlane) <= glm::dot(end4, facePlane)) ?
|
float vectorLength = glm::length(vector);
|
||||||
getClosestPointOnFace(start4, startToEnd, (BoxFace)i) : getClosestPointOnFace(end4, -startToEnd, (BoxFace)i);
|
if (vectorLength < minPenetrationLength) {
|
||||||
glm::vec3 vector = -computeVectorFromPointToSegment(closest, start, end);
|
// remember the smallest penetration vector; if we're inside all faces, we'll use that
|
||||||
if (glm::dot(vector, glm::vec3(facePlane)) < 0.0f) {
|
penetration = (vectorLength < EPSILON) ? glm::vec3(-facePlane) * radius :
|
||||||
// outside this face, so use vector to closest point to determine penetration
|
vector * ((vectorLength + radius) / -vectorLength);
|
||||||
return ::findSpherePenetration(vector, glm::vec3(-facePlane), radius, penetration);
|
minPenetrationLength = vectorLength;
|
||||||
}
|
}
|
||||||
float vectorLength = glm::length(vector);
|
}
|
||||||
if (vectorLength < minPenetrationLength) {
|
|
||||||
// remember the smallest penetration vector; if we're inside all faces, we'll use that
|
return true;
|
||||||
penetration = (vectorLength < EPSILON) ? glm::vec3(-facePlane) * radius :
|
}
|
||||||
vector * ((vectorLength + radius) / -vectorLength);
|
|
||||||
minPenetrationLength = vectorLength;
|
bool AABox::findCapsulePenetration(const glm::vec3& start, const glm::vec3& end, float radius, glm::vec3& penetration) const {
|
||||||
}
|
glm::vec4 start4 = glm::vec4(start, 1.0f);
|
||||||
}
|
glm::vec4 end4 = glm::vec4(end, 1.0f);
|
||||||
|
glm::vec4 startToEnd = glm::vec4(end - start, 0.0f);
|
||||||
return true;
|
|
||||||
}
|
float minPenetrationLength = FLT_MAX;
|
||||||
|
for (int i = 0; i < FACE_COUNT; i++) {
|
||||||
glm::vec3 AABox::getClosestPointOnFace(const glm::vec3& point, BoxFace face) const {
|
// find the vector from the segment to the closest point on the face (starting from deeper end)
|
||||||
switch (face) {
|
glm::vec4 facePlane = getPlane((BoxFace)i);
|
||||||
case MIN_X_FACE:
|
glm::vec3 closest = (glm::dot(start4, facePlane) <= glm::dot(end4, facePlane)) ?
|
||||||
return glm::clamp(point, glm::vec3(_corner.x, _corner.y, _corner.z),
|
getClosestPointOnFace(start4, startToEnd, (BoxFace)i) : getClosestPointOnFace(end4, -startToEnd, (BoxFace)i);
|
||||||
glm::vec3(_corner.x, _corner.y + _size.y, _corner.z + _size.z));
|
glm::vec3 vector = -computeVectorFromPointToSegment(closest, start, end);
|
||||||
|
if (glm::dot(vector, glm::vec3(facePlane)) < 0.0f) {
|
||||||
case MAX_X_FACE:
|
// outside this face, so use vector to closest point to determine penetration
|
||||||
return glm::clamp(point, glm::vec3(_corner.x + _size.x, _corner.y, _corner.z),
|
return ::findSpherePenetration(vector, glm::vec3(-facePlane), radius, penetration);
|
||||||
glm::vec3(_corner.x + _size.x, _corner.y + _size.y, _corner.z + _size.z));
|
}
|
||||||
|
float vectorLength = glm::length(vector);
|
||||||
case MIN_Y_FACE:
|
if (vectorLength < minPenetrationLength) {
|
||||||
return glm::clamp(point, glm::vec3(_corner.x, _corner.y, _corner.z),
|
// remember the smallest penetration vector; if we're inside all faces, we'll use that
|
||||||
glm::vec3(_corner.x + _size.x, _corner.y, _corner.z + _size.z));
|
penetration = (vectorLength < EPSILON) ? glm::vec3(-facePlane) * radius :
|
||||||
|
vector * ((vectorLength + radius) / -vectorLength);
|
||||||
case MAX_Y_FACE:
|
minPenetrationLength = vectorLength;
|
||||||
return glm::clamp(point, glm::vec3(_corner.x, _corner.y + _size.y, _corner.z),
|
}
|
||||||
glm::vec3(_corner.x + _size.x, _corner.y + _size.y, _corner.z + _size.z));
|
}
|
||||||
|
|
||||||
case MIN_Z_FACE:
|
return true;
|
||||||
return glm::clamp(point, glm::vec3(_corner.x, _corner.y, _corner.z),
|
}
|
||||||
glm::vec3(_corner.x + _size.x, _corner.y + _size.y, _corner.z));
|
|
||||||
|
glm::vec3 AABox::getClosestPointOnFace(const glm::vec3& point, BoxFace face) const {
|
||||||
case MAX_Z_FACE:
|
switch (face) {
|
||||||
return glm::clamp(point, glm::vec3(_corner.x, _corner.y, _corner.z + _size.z),
|
case MIN_X_FACE:
|
||||||
glm::vec3(_corner.x + _size.x, _corner.y + _size.y, _corner.z + _size.z));
|
return glm::clamp(point, glm::vec3(_corner.x, _corner.y, _corner.z),
|
||||||
}
|
glm::vec3(_corner.x, _corner.y + _size.y, _corner.z + _size.z));
|
||||||
}
|
|
||||||
|
case MAX_X_FACE:
|
||||||
glm::vec3 AABox::getClosestPointOnFace(const glm::vec4& origin, const glm::vec4& direction, BoxFace face) const {
|
return glm::clamp(point, glm::vec3(_corner.x + _size.x, _corner.y, _corner.z),
|
||||||
// check against the four planes that border the face
|
glm::vec3(_corner.x + _size.x, _corner.y + _size.y, _corner.z + _size.z));
|
||||||
BoxFace oppositeFace = getOppositeFace(face);
|
|
||||||
bool anyOutside = false;
|
case MIN_Y_FACE:
|
||||||
for (int i = 0; i < FACE_COUNT; i++) {
|
return glm::clamp(point, glm::vec3(_corner.x, _corner.y, _corner.z),
|
||||||
if (i == face || i == oppositeFace) {
|
glm::vec3(_corner.x + _size.x, _corner.y, _corner.z + _size.z));
|
||||||
continue;
|
|
||||||
}
|
case MAX_Y_FACE:
|
||||||
glm::vec4 iPlane = getPlane((BoxFace)i);
|
return glm::clamp(point, glm::vec3(_corner.x, _corner.y + _size.y, _corner.z),
|
||||||
float originDistance = glm::dot(origin, iPlane);
|
glm::vec3(_corner.x + _size.x, _corner.y + _size.y, _corner.z + _size.z));
|
||||||
if (originDistance < 0.0f) {
|
|
||||||
continue; // inside the border
|
case MIN_Z_FACE:
|
||||||
}
|
return glm::clamp(point, glm::vec3(_corner.x, _corner.y, _corner.z),
|
||||||
anyOutside = true;
|
glm::vec3(_corner.x + _size.x, _corner.y + _size.y, _corner.z));
|
||||||
float divisor = glm::dot(direction, iPlane);
|
|
||||||
if (fabs(divisor) < EPSILON) {
|
case MAX_Z_FACE:
|
||||||
continue; // segment is parallel to plane
|
return glm::clamp(point, glm::vec3(_corner.x, _corner.y, _corner.z + _size.z),
|
||||||
}
|
glm::vec3(_corner.x + _size.x, _corner.y + _size.y, _corner.z + _size.z));
|
||||||
// find intersection and see if it lies within face bounds
|
}
|
||||||
float directionalDistance = -originDistance / divisor;
|
}
|
||||||
glm::vec4 intersection = origin + direction * directionalDistance;
|
|
||||||
BoxFace iOppositeFace = getOppositeFace((BoxFace)i);
|
glm::vec3 AABox::getClosestPointOnFace(const glm::vec4& origin, const glm::vec4& direction, BoxFace face) const {
|
||||||
for (int j = 0; j < FACE_COUNT; j++) {
|
// check against the four planes that border the face
|
||||||
if (j == face || j == oppositeFace || j == i || j == iOppositeFace) {
|
BoxFace oppositeFace = getOppositeFace(face);
|
||||||
continue;
|
bool anyOutside = false;
|
||||||
}
|
for (int i = 0; i < FACE_COUNT; i++) {
|
||||||
if (glm::dot(intersection, getPlane((BoxFace)j)) > 0.0f) {
|
if (i == face || i == oppositeFace) {
|
||||||
goto outerContinue; // intersection is out of bounds
|
continue;
|
||||||
}
|
}
|
||||||
}
|
glm::vec4 iPlane = getPlane((BoxFace)i);
|
||||||
return getClosestPointOnFace(glm::vec3(intersection), face);
|
float originDistance = glm::dot(origin, iPlane);
|
||||||
|
if (originDistance < 0.0f) {
|
||||||
outerContinue: ;
|
continue; // inside the border
|
||||||
}
|
}
|
||||||
|
anyOutside = true;
|
||||||
// if we were outside any of the sides, we must check against the diagonals
|
float divisor = glm::dot(direction, iPlane);
|
||||||
if (anyOutside) {
|
if (fabs(divisor) < EPSILON) {
|
||||||
int faceAxis = face / 2;
|
continue; // segment is parallel to plane
|
||||||
int secondAxis = (faceAxis + 1) % 3;
|
}
|
||||||
int thirdAxis = (faceAxis + 2) % 3;
|
// find intersection and see if it lies within face bounds
|
||||||
|
float directionalDistance = -originDistance / divisor;
|
||||||
glm::vec4 secondAxisMinPlane = getPlane((BoxFace)(secondAxis * 2));
|
glm::vec4 intersection = origin + direction * directionalDistance;
|
||||||
glm::vec4 secondAxisMaxPlane = getPlane((BoxFace)(secondAxis * 2 + 1));
|
BoxFace iOppositeFace = getOppositeFace((BoxFace)i);
|
||||||
glm::vec4 thirdAxisMaxPlane = getPlane((BoxFace)(thirdAxis * 2 + 1));
|
for (int j = 0; j < FACE_COUNT; j++) {
|
||||||
|
if (j == face || j == oppositeFace || j == i || j == iOppositeFace) {
|
||||||
glm::vec4 offset = glm::vec4(0.0f, 0.0f, 0.0f,
|
continue;
|
||||||
glm::dot(glm::vec3(secondAxisMaxPlane + thirdAxisMaxPlane), _size) * 0.5f);
|
}
|
||||||
glm::vec4 diagonals[] = { secondAxisMinPlane + thirdAxisMaxPlane + offset,
|
if (glm::dot(intersection, getPlane((BoxFace)j)) > 0.0f) {
|
||||||
secondAxisMaxPlane + thirdAxisMaxPlane + offset };
|
goto outerContinue; // intersection is out of bounds
|
||||||
|
}
|
||||||
float minDistance = FLT_MAX;
|
}
|
||||||
for (int i = 0; i < sizeof(diagonals) / sizeof(diagonals[0]); i++) {
|
return getClosestPointOnFace(glm::vec3(intersection), face);
|
||||||
float divisor = glm::dot(direction, diagonals[i]);
|
|
||||||
if (fabs(divisor) < EPSILON) {
|
outerContinue: ;
|
||||||
continue; // segment is parallel to diagonal plane
|
}
|
||||||
}
|
|
||||||
minDistance = glm::min(-glm::dot(origin, diagonals[i]) / divisor, minDistance);
|
// if we were outside any of the sides, we must check against the diagonals
|
||||||
}
|
if (anyOutside) {
|
||||||
if (minDistance != FLT_MAX) {
|
int faceAxis = face / 2;
|
||||||
return getClosestPointOnFace(glm::vec3(origin + direction * minDistance), face);
|
int secondAxis = (faceAxis + 1) % 3;
|
||||||
}
|
int thirdAxis = (faceAxis + 2) % 3;
|
||||||
}
|
|
||||||
|
glm::vec4 secondAxisMinPlane = getPlane((BoxFace)(secondAxis * 2));
|
||||||
// last resort or all inside: clamp origin to face
|
glm::vec4 secondAxisMaxPlane = getPlane((BoxFace)(secondAxis * 2 + 1));
|
||||||
return getClosestPointOnFace(glm::vec3(origin), face);
|
glm::vec4 thirdAxisMaxPlane = getPlane((BoxFace)(thirdAxis * 2 + 1));
|
||||||
}
|
|
||||||
|
glm::vec4 offset = glm::vec4(0.0f, 0.0f, 0.0f,
|
||||||
glm::vec4 AABox::getPlane(BoxFace face) const {
|
glm::dot(glm::vec3(secondAxisMaxPlane + thirdAxisMaxPlane), _size) * 0.5f);
|
||||||
switch (face) {
|
glm::vec4 diagonals[] = { secondAxisMinPlane + thirdAxisMaxPlane + offset,
|
||||||
case MIN_X_FACE: return glm::vec4(-1.0f, 0.0f, 0.0f, _corner.x);
|
secondAxisMaxPlane + thirdAxisMaxPlane + offset };
|
||||||
case MAX_X_FACE: return glm::vec4(1.0f, 0.0f, 0.0f, -_corner.x - _size.x);
|
|
||||||
case MIN_Y_FACE: return glm::vec4(0.0f, -1.0f, 0.0f, _corner.y);
|
float minDistance = FLT_MAX;
|
||||||
case MAX_Y_FACE: return glm::vec4(0.0f, 1.0f, 0.0f, -_corner.y - _size.y);
|
for (int i = 0; i < sizeof(diagonals) / sizeof(diagonals[0]); i++) {
|
||||||
case MIN_Z_FACE: return glm::vec4(0.0f, 0.0f, -1.0f, _corner.z);
|
float divisor = glm::dot(direction, diagonals[i]);
|
||||||
case MAX_Z_FACE: return glm::vec4(0.0f, 0.0f, 1.0f, -_corner.z - _size.z);
|
if (fabs(divisor) < EPSILON) {
|
||||||
}
|
continue; // segment is parallel to diagonal plane
|
||||||
}
|
}
|
||||||
|
minDistance = glm::min(-glm::dot(origin, diagonals[i]) / divisor, minDistance);
|
||||||
BoxFace AABox::getOppositeFace(BoxFace face) {
|
}
|
||||||
switch (face) {
|
if (minDistance != FLT_MAX) {
|
||||||
case MIN_X_FACE: return MAX_X_FACE;
|
return getClosestPointOnFace(glm::vec3(origin + direction * minDistance), face);
|
||||||
case MAX_X_FACE: return MIN_X_FACE;
|
}
|
||||||
case MIN_Y_FACE: return MAX_Y_FACE;
|
}
|
||||||
case MAX_Y_FACE: return MIN_Y_FACE;
|
|
||||||
case MIN_Z_FACE: return MAX_Z_FACE;
|
// last resort or all inside: clamp origin to face
|
||||||
case MAX_Z_FACE: return MIN_Z_FACE;
|
return getClosestPointOnFace(glm::vec3(origin), face);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
glm::vec4 AABox::getPlane(BoxFace face) const {
|
||||||
|
switch (face) {
|
||||||
|
case MIN_X_FACE: return glm::vec4(-1.0f, 0.0f, 0.0f, _corner.x);
|
||||||
|
case MAX_X_FACE: return glm::vec4(1.0f, 0.0f, 0.0f, -_corner.x - _size.x);
|
||||||
|
case MIN_Y_FACE: return glm::vec4(0.0f, -1.0f, 0.0f, _corner.y);
|
||||||
|
case MAX_Y_FACE: return glm::vec4(0.0f, 1.0f, 0.0f, -_corner.y - _size.y);
|
||||||
|
case MIN_Z_FACE: return glm::vec4(0.0f, 0.0f, -1.0f, _corner.z);
|
||||||
|
case MAX_Z_FACE: return glm::vec4(0.0f, 0.0f, 1.0f, -_corner.z - _size.z);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
BoxFace AABox::getOppositeFace(BoxFace face) {
|
||||||
|
switch (face) {
|
||||||
|
case MIN_X_FACE: return MAX_X_FACE;
|
||||||
|
case MAX_X_FACE: return MIN_X_FACE;
|
||||||
|
case MIN_Y_FACE: return MAX_Y_FACE;
|
||||||
|
case MAX_Y_FACE: return MIN_Y_FACE;
|
||||||
|
case MIN_Z_FACE: return MAX_Z_FACE;
|
||||||
|
case MAX_Z_FACE: return MIN_Z_FACE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,72 +1,86 @@
|
||||||
//
|
//
|
||||||
// AABox.h - Axis Aligned Boxes
|
// AABox.h - Axis Aligned Boxes
|
||||||
// hifi
|
// hifi
|
||||||
//
|
//
|
||||||
// Added by Brad Hefta-Gaub on 04/11/13.
|
// Added by Brad Hefta-Gaub on 04/11/13.
|
||||||
// Originally from lighthouse3d. Modified to utilize glm::vec3 and clean up to our coding standards
|
// Originally from lighthouse3d. Modified to utilize glm::vec3 and clean up to our coding standards
|
||||||
//
|
//
|
||||||
// Simple axis aligned box class.
|
// Simple axis aligned box class.
|
||||||
//
|
//
|
||||||
|
|
||||||
#ifndef _AABOX_
|
#ifndef _AABOX_
|
||||||
#define _AABOX_
|
#define _AABOX_
|
||||||
|
|
||||||
#include <glm/glm.hpp>
|
#include <glm/glm.hpp>
|
||||||
|
|
||||||
enum BoxFace {
|
enum BoxFace {
|
||||||
MIN_X_FACE,
|
MIN_X_FACE,
|
||||||
MAX_X_FACE,
|
MAX_X_FACE,
|
||||||
MIN_Y_FACE,
|
MIN_Y_FACE,
|
||||||
MAX_Y_FACE,
|
MAX_Y_FACE,
|
||||||
MIN_Z_FACE,
|
MIN_Z_FACE,
|
||||||
MAX_Z_FACE
|
MAX_Z_FACE
|
||||||
};
|
};
|
||||||
|
|
||||||
const int FACE_COUNT = 6;
|
|
||||||
|
enum BoxVertex {
|
||||||
class AABox
|
BOTTOM_LEFT_NEAR = 0,
|
||||||
{
|
BOTTOM_RIGHT_NEAR = 1,
|
||||||
|
TOP_RIGHT_NEAR = 2,
|
||||||
public:
|
TOP_LEFT_NEAR = 3,
|
||||||
|
BOTTOM_LEFT_FAR = 4,
|
||||||
AABox(const glm::vec3& corner, float size) : _corner(corner), _size(size, size, size) { };
|
BOTTOM_RIGHT_FAR = 5,
|
||||||
AABox(const glm::vec3& corner, float x, float y, float z) : _corner(corner), _size(x, y, z) { };
|
TOP_RIGHT_FAR = 6,
|
||||||
AABox(const glm::vec3& corner, const glm::vec3& size) : _corner(corner), _size(size) { };
|
TOP_LEFT_FAR = 7
|
||||||
AABox() : _corner(0,0,0), _size(0,0,0) { }
|
};
|
||||||
~AABox() { }
|
|
||||||
|
const int FACE_COUNT = 6;
|
||||||
void setBox(const glm::vec3& corner, float x, float y, float z) { setBox(corner,glm::vec3(x,y,z)); };
|
|
||||||
void setBox(const glm::vec3& corner, const glm::vec3& size);
|
class AABox
|
||||||
|
{
|
||||||
// for use in frustum computations
|
|
||||||
glm::vec3 getVertexP(const glm::vec3& normal) const;
|
public:
|
||||||
glm::vec3 getVertexN(const glm::vec3& normal) const;
|
|
||||||
|
AABox(const glm::vec3& corner, float size) : _corner(corner), _size(size, size, size) { };
|
||||||
void scale(float scale);
|
AABox(const glm::vec3& corner, float x, float y, float z) : _corner(corner), _size(x, y, z) { };
|
||||||
|
AABox(const glm::vec3& corner, const glm::vec3& size) : _corner(corner), _size(size) { };
|
||||||
const glm::vec3& getCorner() const { return _corner; };
|
AABox() : _corner(0,0,0), _size(0,0,0) { }
|
||||||
const glm::vec3& getSize() const { return _size; };
|
~AABox() { }
|
||||||
const glm::vec3& getCenter() const { return _center; };
|
|
||||||
|
void setBox(const glm::vec3& corner, float x, float y, float z) { setBox(corner,glm::vec3(x,y,z)); };
|
||||||
bool contains(const glm::vec3& point) const;
|
void setBox(const glm::vec3& corner, const glm::vec3& size);
|
||||||
bool expandedContains(const glm::vec3& point, float expansion) const;
|
|
||||||
bool expandedIntersectsSegment(const glm::vec3& start, const glm::vec3& end, float expansion) const;
|
// for use in frustum computations
|
||||||
bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance, BoxFace& face) const;
|
glm::vec3 getVertexP(const glm::vec3& normal) const;
|
||||||
bool findSpherePenetration(const glm::vec3& center, float radius, glm::vec3& penetration) const;
|
glm::vec3 getVertexN(const glm::vec3& normal) const;
|
||||||
bool findCapsulePenetration(const glm::vec3& start, const glm::vec3& end, float radius, glm::vec3& penetration) const;
|
|
||||||
|
void scale(float scale);
|
||||||
private:
|
|
||||||
|
const glm::vec3& getCorner() const { return _corner; };
|
||||||
glm::vec3 getClosestPointOnFace(const glm::vec3& point, BoxFace face) const;
|
const glm::vec3& getSize() const { return _size; };
|
||||||
glm::vec3 getClosestPointOnFace(const glm::vec4& origin, const glm::vec4& direction, BoxFace face) const;
|
const glm::vec3& getCenter() const { return _center; };
|
||||||
glm::vec4 getPlane(BoxFace face) const;
|
|
||||||
|
glm::vec3 getVertex(BoxVertex vertex) const;
|
||||||
static BoxFace getOppositeFace(BoxFace face);
|
|
||||||
|
bool contains(const glm::vec3& point) const;
|
||||||
glm::vec3 _corner;
|
bool expandedContains(const glm::vec3& point, float expansion) const;
|
||||||
glm::vec3 _center;
|
bool expandedIntersectsSegment(const glm::vec3& start, const glm::vec3& end, float expansion) const;
|
||||||
glm::vec3 _size;
|
bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance, BoxFace& face) const;
|
||||||
};
|
bool findSpherePenetration(const glm::vec3& center, float radius, glm::vec3& penetration) const;
|
||||||
|
bool findCapsulePenetration(const glm::vec3& start, const glm::vec3& end, float radius, glm::vec3& penetration) const;
|
||||||
|
|
||||||
#endif
|
private:
|
||||||
|
|
||||||
|
glm::vec3 getClosestPointOnFace(const glm::vec3& point, BoxFace face) const;
|
||||||
|
glm::vec3 getClosestPointOnFace(const glm::vec4& origin, const glm::vec4& direction, BoxFace face) const;
|
||||||
|
glm::vec4 getPlane(BoxFace face) const;
|
||||||
|
|
||||||
|
static BoxFace getOppositeFace(BoxFace face);
|
||||||
|
|
||||||
|
glm::vec3 _corner;
|
||||||
|
glm::vec3 _center;
|
||||||
|
glm::vec3 _size;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
193
libraries/voxels/src/CoverageMap.cpp
Normal file
193
libraries/voxels/src/CoverageMap.cpp
Normal file
|
@ -0,0 +1,193 @@
|
||||||
|
//
|
||||||
|
// CoverageMap.cpp -
|
||||||
|
// hifi
|
||||||
|
//
|
||||||
|
// Added by Brad Hefta-Gaub on 06/11/13.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "CoverageMap.h"
|
||||||
|
#include <SharedUtil.h>
|
||||||
|
#include <cstring>
|
||||||
|
#include "Log.h"
|
||||||
|
|
||||||
|
int CoverageMap::_mapCount = 0;
|
||||||
|
const BoundingBox CoverageMap::ROOT_BOUNDING_BOX = BoundingBox(glm::vec2(-2.f,-2.f), glm::vec2(4.f,4.f));
|
||||||
|
|
||||||
|
CoverageMap::CoverageMap(BoundingBox boundingBox, bool isRoot, bool managePolygons) :
|
||||||
|
_isRoot(isRoot), _myBoundingBox(boundingBox), _managePolygons(managePolygons) {
|
||||||
|
_mapCount++;
|
||||||
|
init();
|
||||||
|
//printLog("CoverageMap created... _mapCount=%d\n",_mapCount);
|
||||||
|
};
|
||||||
|
|
||||||
|
CoverageMap::~CoverageMap() {
|
||||||
|
erase();
|
||||||
|
};
|
||||||
|
|
||||||
|
void CoverageMap::erase() {
|
||||||
|
// If we're in charge of managing the polygons, then clean them up first
|
||||||
|
if (_managePolygons) {
|
||||||
|
for (int i = 0; i < _polygonCount; i++) {
|
||||||
|
delete _polygons[i];
|
||||||
|
_polygons[i] = NULL; // do we need to do this?
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now, clean up our local storage
|
||||||
|
_polygonCount = 0;
|
||||||
|
_polygonArraySize = 0;
|
||||||
|
if (_polygons) {
|
||||||
|
delete[] _polygons;
|
||||||
|
_polygons = NULL;
|
||||||
|
}
|
||||||
|
if (_polygonDistances) {
|
||||||
|
delete[] _polygonDistances;
|
||||||
|
_polygonDistances = NULL;
|
||||||
|
}
|
||||||
|
for (int i = 0; i < NUMBER_OF_CHILDREN; i++) {
|
||||||
|
if (_childMaps[i]) {
|
||||||
|
delete _childMaps[i];
|
||||||
|
_childMaps[i] = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
if (_isRoot) {
|
||||||
|
printLog("CoverageMap last to be deleted...\n");
|
||||||
|
printLog("_mapCount=%d\n",_mapCount);
|
||||||
|
printLog("_maxPolygonsUsed=%d\n",_maxPolygonsUsed);
|
||||||
|
printLog("_totalPolygons=%d\n",_totalPolygons);
|
||||||
|
|
||||||
|
_maxPolygonsUsed = 0;
|
||||||
|
_totalPolygons = 0;
|
||||||
|
_mapCount = 0;
|
||||||
|
}
|
||||||
|
**/
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void CoverageMap::init() {
|
||||||
|
_polygonCount = 0;
|
||||||
|
_polygonArraySize = 0;
|
||||||
|
_polygons = NULL;
|
||||||
|
_polygonDistances = NULL;
|
||||||
|
memset(_childMaps,0,sizeof(_childMaps));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 0 = bottom, right
|
||||||
|
// 1 = bottom, left
|
||||||
|
// 2 = top, right
|
||||||
|
// 3 = top, left
|
||||||
|
BoundingBox CoverageMap::getChildBoundingBox(int childIndex) {
|
||||||
|
const int LEFT_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 "left" bit is set, then add size.x to the corner
|
||||||
|
if ((childIndex & LEFT_BIT) == LEFT_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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void CoverageMap::growPolygonArray() {
|
||||||
|
VoxelProjectedPolygon** newPolygons = new VoxelProjectedPolygon*[_polygonArraySize + DEFAULT_GROW_SIZE];
|
||||||
|
float* newDistances = new float[_polygonArraySize + DEFAULT_GROW_SIZE];
|
||||||
|
|
||||||
|
|
||||||
|
if (_polygons) {
|
||||||
|
memcpy(newPolygons, _polygons, sizeof(VoxelProjectedPolygon*) * _polygonCount);
|
||||||
|
delete[] _polygons;
|
||||||
|
memcpy(newDistances, _polygonDistances, sizeof(float) * _polygonCount);
|
||||||
|
delete[] _polygonDistances;
|
||||||
|
}
|
||||||
|
_polygons = newPolygons;
|
||||||
|
_polygonDistances = newDistances;
|
||||||
|
_polygonArraySize = _polygonArraySize + DEFAULT_GROW_SIZE;
|
||||||
|
//printLog("CoverageMap::growPolygonArray() _polygonArraySize=%d...\n",_polygonArraySize);
|
||||||
|
}
|
||||||
|
|
||||||
|
int CoverageMap::_maxPolygonsUsed = 0;
|
||||||
|
int CoverageMap::_totalPolygons = 0;
|
||||||
|
|
||||||
|
// just handles storage in the array, doesn't test for occlusion or
|
||||||
|
// determining if this is the correct map to store in!
|
||||||
|
void CoverageMap::storeInArray(VoxelProjectedPolygon* polygon) {
|
||||||
|
|
||||||
|
_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
|
||||||
|
const int IGNORED = NULL;
|
||||||
|
_polygonCount = insertIntoSortedArrays((void*)polygon, polygon->getDistance(), IGNORED,
|
||||||
|
(void**)_polygons, _polygonDistances, IGNORED,
|
||||||
|
_polygonCount, _polygonArraySize);
|
||||||
|
|
||||||
|
if (_polygonCount > _maxPolygonsUsed) {
|
||||||
|
_maxPolygonsUsed = _polygonCount;
|
||||||
|
//printLog("CoverageMap new _maxPolygonsUsed reached=%d\n",_maxPolygonsUsed);
|
||||||
|
//_myBoundingBox.printDebugDetails("map._myBoundingBox");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// possible results = STORED/NOT_STORED, OCCLUDED, DOESNT_FIT
|
||||||
|
CoverageMap::StorageResult CoverageMap::checkMap(VoxelProjectedPolygon* polygon, bool storeIt) {
|
||||||
|
if (_isRoot || _myBoundingBox.contains(polygon->getBoundingBox())) {
|
||||||
|
// 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.
|
||||||
|
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()) {
|
||||||
|
if (storeIt) {
|
||||||
|
storeInArray(polygon);
|
||||||
|
return 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 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);
|
||||||
|
}
|
||||||
|
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) {
|
||||||
|
storeInArray(polygon);
|
||||||
|
return STORED;
|
||||||
|
} else {
|
||||||
|
return NOT_STORED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return DOESNT_FIT;
|
||||||
|
}
|
54
libraries/voxels/src/CoverageMap.h
Normal file
54
libraries/voxels/src/CoverageMap.h
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
//
|
||||||
|
// CoverageMap.h - 2D CoverageMap Quad tree for storage of VoxelProjectedPolygons
|
||||||
|
// hifi
|
||||||
|
//
|
||||||
|
// Added by Brad Hefta-Gaub on 06/11/13.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef _COVERAGE_MAP_
|
||||||
|
#define _COVERAGE_MAP_
|
||||||
|
|
||||||
|
#include <glm/glm.hpp>
|
||||||
|
#include "VoxelProjectedPolygon.h"
|
||||||
|
|
||||||
|
class CoverageMap {
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
CoverageMap(BoundingBox boundingBox = ROOT_BOUNDING_BOX, bool isRoot = IS_ROOT, bool managePolygons = true);
|
||||||
|
~CoverageMap();
|
||||||
|
|
||||||
|
typedef enum {STORED, OCCLUDED, DOESNT_FIT, NOT_STORED} StorageResult;
|
||||||
|
StorageResult checkMap(VoxelProjectedPolygon* polygon, bool storeIt = true);
|
||||||
|
|
||||||
|
BoundingBox getChildBoundingBox(int childIndex);
|
||||||
|
|
||||||
|
void erase(); // erase the coverage map
|
||||||
|
|
||||||
|
private:
|
||||||
|
void init();
|
||||||
|
void growPolygonArray();
|
||||||
|
void storeInArray(VoxelProjectedPolygon* polygon);
|
||||||
|
|
||||||
|
bool _isRoot; // is this map the root, if so, it never returns DOESNT_FIT
|
||||||
|
BoundingBox _myBoundingBox;
|
||||||
|
bool _managePolygons; // will the coverage map delete the polygons on destruct
|
||||||
|
int _polygonCount; // how many polygons at this level
|
||||||
|
int _polygonArraySize; // how much room is there to store polygons at this level
|
||||||
|
VoxelProjectedPolygon** _polygons;
|
||||||
|
float* _polygonDistances;
|
||||||
|
CoverageMap* _childMaps[NUMBER_OF_CHILDREN];
|
||||||
|
|
||||||
|
static const int DEFAULT_GROW_SIZE = 100;
|
||||||
|
static int _mapCount;
|
||||||
|
static int _maxPolygonsUsed;
|
||||||
|
static int _totalPolygons;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif // _COVERAGE_MAP_
|
|
@ -115,3 +115,28 @@ glm::vec3 addPenetrations(const glm::vec3& currentPenetration, const glm::vec3&
|
||||||
return currentDirection * glm::max(directionalComponent, currentLength) +
|
return currentDirection * glm::max(directionalComponent, currentLength) +
|
||||||
newPenetration - (currentDirection * directionalComponent);
|
newPenetration - (currentDirection * directionalComponent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Do line segments (r1p1.x, r1p1.y)--(r1p2.x, r1p2.y) and (r2p1.x, r2p1.y)--(r2p2.x, r2p2.y) intersect?
|
||||||
|
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);
|
||||||
|
int d3 = computeDirection(r1p1.x, r1p1.y, r1p2.x, r1p2.y, r2p1.x, r2p1.y);
|
||||||
|
int d4 = computeDirection(r1p1.x, r1p1.y, r1p2.x, r1p2.y, r2p2.x, r2p2.y);
|
||||||
|
return (((d1 > 0 && d2 < 0) || (d1 < 0 && d2 > 0)) &&
|
||||||
|
((d3 > 0 && d4 < 0) || (d3 < 0 && d4 > 0))) ||
|
||||||
|
(d1 == 0 && isOnSegment(r2p1.x, r2p1.y, r2p2.x, r2p2.y, r1p1.x, r1p1.y)) ||
|
||||||
|
(d2 == 0 && isOnSegment(r2p1.x, r2p1.y, r2p2.x, r2p2.y, r1p2.x, r1p2.y)) ||
|
||||||
|
(d3 == 0 && isOnSegment(r1p1.x, r1p1.y, r1p2.x, r1p2.y, r2p1.x, r2p1.y)) ||
|
||||||
|
(d4 == 0 && isOnSegment(r1p1.x, r1p1.y, r1p2.x, r1p2.y, r2p2.x, r2p2.y));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isOnSegment(float xi, float yi, float xj, float yj, float xk, float yk) {
|
||||||
|
return (xi <= xk || xj <= xk) && (xk <= xi || xk <= xj) &&
|
||||||
|
(yi <= yk || yj <= yk) && (yk <= yi || yk <= yj);
|
||||||
|
}
|
||||||
|
|
||||||
|
int computeDirection(float xi, float yi, float xj, float yj, float xk, float yk) {
|
||||||
|
float a = (xk - xi) * (yj - yi);
|
||||||
|
float b = (xj - xi) * (yk - yi);
|
||||||
|
return a < b ? -1 : a > b ? 1 : 0;
|
||||||
|
}
|
||||||
|
|
|
@ -39,4 +39,8 @@ bool findCapsulePlanePenetration(const glm::vec3& penetratorStart, const glm::ve
|
||||||
|
|
||||||
glm::vec3 addPenetrations(const glm::vec3& currentPenetration, const glm::vec3& newPenetration);
|
glm::vec3 addPenetrations(const glm::vec3& currentPenetration, const glm::vec3& newPenetration);
|
||||||
|
|
||||||
|
bool doLineSegmentsIntersect(glm::vec2 r1p1, glm::vec2 r1p2, glm::vec2 r2p1, glm::vec2 r2p2);
|
||||||
|
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);
|
||||||
|
|
||||||
#endif /* defined(__interface__GeometryUtil__) */
|
#endif /* defined(__interface__GeometryUtil__) */
|
||||||
|
|
|
@ -425,3 +425,116 @@ void ViewFrustum::printDebugDetails() const {
|
||||||
_eyeOffsetOrientation.w );
|
_eyeOffsetOrientation.w );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
glm::vec2 ViewFrustum::projectPoint(glm::vec3 point, bool& pointInView) const {
|
||||||
|
|
||||||
|
// Projection matrix : Field of View, ratio, display range : near to far
|
||||||
|
glm::mat4 projection = glm::perspective(_fieldOfView, _aspectRatio, _nearClip, _farClip);
|
||||||
|
glm::vec3 lookAt = _position + _direction;
|
||||||
|
glm::mat4 view = glm::lookAt(_position, lookAt, _up);
|
||||||
|
// Our ModelViewProjection : multiplication of our 3 matrices (note: model is identity, so we can drop it)
|
||||||
|
glm::mat4 VP = projection * view; // Remember, matrix multiplication is the other way around
|
||||||
|
|
||||||
|
glm::vec4 pointVec4 = glm::vec4(point,1);
|
||||||
|
glm::vec4 projectedPointVec4 = VP * pointVec4;
|
||||||
|
pointInView = (projectedPointVec4.w > 0); // math! If the w result is negative then the point is behind the viewer
|
||||||
|
|
||||||
|
// what happens with w is 0???
|
||||||
|
float x = projectedPointVec4.x / projectedPointVec4.w;
|
||||||
|
float y = projectedPointVec4.y / projectedPointVec4.w;
|
||||||
|
glm::vec2 projectedPoint(x,y);
|
||||||
|
|
||||||
|
// if the point is out of view we also need to flip the signs of x and y
|
||||||
|
if (!pointInView) {
|
||||||
|
projectedPoint.x = -x;
|
||||||
|
projectedPoint.y = -y;
|
||||||
|
}
|
||||||
|
|
||||||
|
return projectedPoint;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const int MAX_POSSIBLE_COMBINATIONS = 43;
|
||||||
|
|
||||||
|
const int hullVertexLookup[MAX_POSSIBLE_COMBINATIONS][MAX_SHADOW_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}, // 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
|
||||||
|
{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
|
||||||
|
{0}, // n/a
|
||||||
|
{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
|
||||||
|
{0}, // n/a
|
||||||
|
{0}, // n/a
|
||||||
|
{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
|
||||||
|
{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
|
||||||
|
{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
|
||||||
|
{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
|
||||||
|
{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
|
||||||
|
{0}, // n/a
|
||||||
|
{6, BOTTOM_RIGHT_FAR, TOP_RIGHT_FAR, TOP_RIGHT_NEAR, TOP_LEFT_NEAR, TOP_LEFT_FAR, BOTTOM_LEFT_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
|
||||||
|
{6, TOP_RIGHT_NEAR, TOP_RIGHT_FAR, BOTTOM_RIGHT_FAR, BOTTOM_LEFT_FAR, BOTTOM_LEFT_NEAR, TOP_LEFT_NEAR}, // back, top, left
|
||||||
|
};
|
||||||
|
|
||||||
|
VoxelProjectedPolygon ViewFrustum::getProjectedShadow(const AABox& box) const {
|
||||||
|
glm::vec3 bottomNearRight = box.getCorner();
|
||||||
|
glm::vec3 topFarLeft = box.getCorner() + box.getSize();
|
||||||
|
int lookUp = ((_position.x < bottomNearRight.x) ) // 1 = right | compute 6-bit
|
||||||
|
+ ((_position.x > topFarLeft.x ) << 1) // 2 = left | code to
|
||||||
|
+ ((_position.y < bottomNearRight.y) << 2) // 4 = bottom | classify camera
|
||||||
|
+ ((_position.y > topFarLeft.y ) << 3) // 8 = top | with respect to
|
||||||
|
+ ((_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
|
||||||
|
|
||||||
|
VoxelProjectedPolygon shadow(vertexCount);
|
||||||
|
|
||||||
|
bool pointInView = true;
|
||||||
|
bool allPointsInView = false; // assume the best, but wait till we know we have a vertex
|
||||||
|
bool anyPointsInView = false; // assume the worst!
|
||||||
|
if (vertexCount) {
|
||||||
|
allPointsInView = true; // assume the best!
|
||||||
|
for(int i = 0; i < vertexCount; i++) {
|
||||||
|
int vertexNum = hullVertexLookup[lookUp][i+1];
|
||||||
|
glm::vec3 point = box.getVertex((BoxVertex)vertexNum);
|
||||||
|
glm::vec2 projectedPoint = projectPoint(point, pointInView);
|
||||||
|
allPointsInView = allPointsInView && pointInView;
|
||||||
|
anyPointsInView = anyPointsInView || pointInView;
|
||||||
|
shadow.setVertex(i, projectedPoint);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
#include <glm/gtc/quaternion.hpp>
|
#include <glm/gtc/quaternion.hpp>
|
||||||
#include "Plane.h"
|
#include "Plane.h"
|
||||||
#include "AABox.h"
|
#include "AABox.h"
|
||||||
|
#include "VoxelProjectedPolygon.h"
|
||||||
|
|
||||||
const float DEFAULT_KEYHOLE_RADIUS = 2.0f;
|
const float DEFAULT_KEYHOLE_RADIUS = 2.0f;
|
||||||
|
|
||||||
|
@ -87,6 +88,9 @@ public:
|
||||||
|
|
||||||
void printDebugDetails() const;
|
void printDebugDetails() const;
|
||||||
|
|
||||||
|
glm::vec2 projectPoint(glm::vec3 point, bool& pointInView) const;
|
||||||
|
VoxelProjectedPolygon getProjectedShadow(const AABox& box) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
// Used for keyhole calculations
|
// Used for keyhole calculations
|
||||||
|
|
126
libraries/voxels/src/VoxelProjectedPolygon.cpp
Normal file
126
libraries/voxels/src/VoxelProjectedPolygon.cpp
Normal file
|
@ -0,0 +1,126 @@
|
||||||
|
//
|
||||||
|
// VoxelProjectedPolygon.cpp - The projected shadow (on the 2D view plane) for a voxel
|
||||||
|
// hifi
|
||||||
|
//
|
||||||
|
// Added by Brad Hefta-Gaub on 06/11/13.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "VoxelProjectedPolygon.h"
|
||||||
|
#include "GeometryUtil.h"
|
||||||
|
#include "Log.h"
|
||||||
|
|
||||||
|
|
||||||
|
bool BoundingBox::contains(const BoundingBox& box) const {
|
||||||
|
return (
|
||||||
|
(box.corner.x >= corner.x) &&
|
||||||
|
(box.corner.y >= corner.y) &&
|
||||||
|
(box.corner.x + box.size.x <= corner.x + size.x) &&
|
||||||
|
(box.corner.y + box.size.y <= corner.y + size.y)
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
void BoundingBox::printDebugDetails(const char* label) const {
|
||||||
|
if (label) {
|
||||||
|
printLog(label);
|
||||||
|
} else {
|
||||||
|
printLog("BoundingBox");
|
||||||
|
}
|
||||||
|
printLog("\n corner=%f,%f size=%f,%f\n", corner.x, corner.y, size.x, size.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void VoxelProjectedPolygon::setVertex(int vertex, const glm::vec2& point) {
|
||||||
|
_vertices[vertex] = point;
|
||||||
|
|
||||||
|
// keep track of our bounding box
|
||||||
|
if (point.x > _maxX) {
|
||||||
|
_maxX = point.x;
|
||||||
|
}
|
||||||
|
if (point.y > _maxY) {
|
||||||
|
_maxY = point.y;
|
||||||
|
}
|
||||||
|
if (point.x < _minX) {
|
||||||
|
_minX = point.x;
|
||||||
|
}
|
||||||
|
if (point.y < _minY) {
|
||||||
|
_minY = point.y;
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
bool VoxelProjectedPolygon::occludes(const VoxelProjectedPolygon& occludee) const {
|
||||||
|
|
||||||
|
// if we are completely out of view, then we definitely don't occlude!
|
||||||
|
// if the occludee is completely out of view, then we also don't occlude it
|
||||||
|
//
|
||||||
|
// this is true, but unfortunately, we're not quite handling projects in the
|
||||||
|
// case when SOME points are in view and others are not. So, we will not consider
|
||||||
|
// occlusion for any shadows that are partially in view.
|
||||||
|
if (!getAllInView() || !occludee.getAllInView() ) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// first check the bounding boxes, the occludee must be fully within the boounding box of this shadow
|
||||||
|
if ((occludee.getMaxX() > getMaxX()) ||
|
||||||
|
(occludee.getMaxY() > getMaxY()) ||
|
||||||
|
(occludee.getMinX() < getMinX()) ||
|
||||||
|
(occludee.getMinY() < getMinY())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if we got this far, then check each vertex of the occludee, if all those points
|
||||||
|
// are inside our polygon, then the tested occludee is fully occluded
|
||||||
|
for(int i = 0; i < occludee.getVertexCount(); i++) {
|
||||||
|
if (!pointInside(occludee.getVertex(i))) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// if we got this far, then indeed the occludee is fully occluded by us
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool VoxelProjectedPolygon::pointInside(const glm::vec2& point) const {
|
||||||
|
// first check the bounding boxes, the point must be fully within the boounding box of this shadow
|
||||||
|
if ((point.x > getMaxX()) ||
|
||||||
|
(point.y > getMaxY()) ||
|
||||||
|
(point.x < getMinX()) ||
|
||||||
|
(point.y < getMinY())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
float e = (getMaxX() - getMinX()) / 100.0f; // some epsilon
|
||||||
|
|
||||||
|
// We need to have one ray that goes from a known outside position to the point in question. We'll pick a
|
||||||
|
// start point just outside of our min X
|
||||||
|
glm::vec2 r1p1(getMinX() - e, point.y);
|
||||||
|
glm::vec2 r1p2(point);
|
||||||
|
|
||||||
|
glm::vec2 r2p1(getVertex(getVertexCount()-1)); // start with last vertex to first vertex
|
||||||
|
glm::vec2 r2p2;
|
||||||
|
|
||||||
|
// Test the ray against all sides
|
||||||
|
int intersections = 0;
|
||||||
|
for (int i = 0; i < getVertexCount(); i++) {
|
||||||
|
r2p2 = getVertex(i);
|
||||||
|
if (doLineSegmentsIntersect(r1p1, r1p2, r2p1, r2p2)) {
|
||||||
|
intersections++;
|
||||||
|
}
|
||||||
|
r2p1 = r2p2; // set up for next side
|
||||||
|
}
|
||||||
|
|
||||||
|
// If odd number of intersections, we're inside
|
||||||
|
return ((intersections & 1) == 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void VoxelProjectedPolygon::printDebugDetails() const {
|
||||||
|
printf("VoxelProjectedPolygon...");
|
||||||
|
printf(" minX=%f maxX=%f minY=%f maxY=%f\n", getMinX(), getMaxX(), getMinY(), getMaxY());
|
||||||
|
printf(" vertex count=%d distance=%f\n", getVertexCount(), getDistance());
|
||||||
|
for (int i = 0; i < getVertexCount(); i++) {
|
||||||
|
glm::vec2 point = getVertex(i);
|
||||||
|
printf(" vertex[%d] = %f, %f \n", i, point.x, point.y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
78
libraries/voxels/src/VoxelProjectedPolygon.h
Normal file
78
libraries/voxels/src/VoxelProjectedPolygon.h
Normal file
|
@ -0,0 +1,78 @@
|
||||||
|
//
|
||||||
|
// VoxelProjectedPolygon.h - The projected shadow (on the 2D view plane) for a voxel
|
||||||
|
// hifi
|
||||||
|
//
|
||||||
|
// Added by Brad Hefta-Gaub on 06/11/13.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef _VOXEL_PROJECTED_SHADOW_
|
||||||
|
#define _VOXEL_PROJECTED_SHADOW_
|
||||||
|
|
||||||
|
#include <glm/glm.hpp>
|
||||||
|
|
||||||
|
const int MAX_SHADOW_VERTEX_COUNT = 6;
|
||||||
|
|
||||||
|
typedef glm::vec2 ShadowVertices[MAX_SHADOW_VERTEX_COUNT];
|
||||||
|
|
||||||
|
class BoundingBox {
|
||||||
|
public:
|
||||||
|
BoundingBox(glm::vec2 corner, glm::vec2 size) : corner(corner), size(size) {};
|
||||||
|
glm::vec2 corner;
|
||||||
|
glm::vec2 size;
|
||||||
|
bool contains(const BoundingBox& box) const;
|
||||||
|
|
||||||
|
void printDebugDetails(const char* label=NULL) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
class VoxelProjectedPolygon {
|
||||||
|
|
||||||
|
public:
|
||||||
|
VoxelProjectedPolygon(int vertexCount = 0) :
|
||||||
|
_vertexCount(vertexCount),
|
||||||
|
_maxX(-FLT_MAX), _maxY(-FLT_MAX), _minX(FLT_MAX), _minY(FLT_MAX),
|
||||||
|
_distance(0)
|
||||||
|
{ };
|
||||||
|
|
||||||
|
~VoxelProjectedPolygon() { };
|
||||||
|
const ShadowVertices& getVerices() 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; }
|
||||||
|
|
||||||
|
bool getAnyInView() const { return _anyInView; };
|
||||||
|
void setAnyInView(bool anyInView) { _anyInView = anyInView; };
|
||||||
|
bool getAllInView() const { return _allInView; };
|
||||||
|
void setAllInView(bool allInView) { _allInView = allInView; };
|
||||||
|
|
||||||
|
bool occludes(const VoxelProjectedPolygon& occludee) const;
|
||||||
|
bool pointInside(const glm::vec2& point) const;
|
||||||
|
|
||||||
|
float getMaxX() const { return _maxX; }
|
||||||
|
float getMaxY() const { return _maxY; }
|
||||||
|
float getMinX() const { return _minX; }
|
||||||
|
float getMinY() const { return _minY; }
|
||||||
|
|
||||||
|
BoundingBox getBoundingBox() const {
|
||||||
|
return BoundingBox(glm::vec2(_minX,_minY), glm::vec2(_maxX - _minX, _maxY - _minY));
|
||||||
|
};
|
||||||
|
|
||||||
|
void printDebugDetails() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
int _vertexCount;
|
||||||
|
ShadowVertices _vertices;
|
||||||
|
float _maxX;
|
||||||
|
float _maxY;
|
||||||
|
float _minX;
|
||||||
|
float _minY;
|
||||||
|
float _distance;
|
||||||
|
bool _anyInView; // if any points are in view
|
||||||
|
bool _allInView; // if all points are in view
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif // _VOXEL_PROJECTED_SHADOW_
|
|
@ -22,6 +22,7 @@
|
||||||
#include "ViewFrustum.h"
|
#include "ViewFrustum.h"
|
||||||
#include <fstream> // to load voxels from file
|
#include <fstream> // to load voxels from file
|
||||||
#include "VoxelConstants.h"
|
#include "VoxelConstants.h"
|
||||||
|
#include "CoverageMap.h"
|
||||||
|
|
||||||
#include <glm/gtc/noise.hpp>
|
#include <glm/gtc/noise.hpp>
|
||||||
|
|
||||||
|
@ -68,7 +69,51 @@ void VoxelTree::recurseNodeWithOperation(VoxelNode* node,RecurseVoxelTreeOperati
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
VoxelNode * VoxelTree::nodeForOctalCode(VoxelNode* ancestorNode, unsigned char* needleCode, VoxelNode** parentOfFoundNode) const {
|
// Recurses voxel tree calling the RecurseVoxelTreeOperation function for each node.
|
||||||
|
// 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,
|
||||||
|
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
|
||||||
|
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);
|
||||||
|
//printLog("recurseNodeWithOperationDistanceSorted() CHECKING child[%d] point=%f,%f center=%f,%f distance=%f...\n", i, point.x, point.y, center.x, center.y, distance);
|
||||||
|
//childNode->printDebugDetails("");
|
||||||
|
currentCount = insertIntoSortedArrays((void*)childNode, distanceSquared, i,
|
||||||
|
(void**)&sortedChildren, (float*)&distancesToChildren,
|
||||||
|
(int*)&indexOfChildren, currentCount, NUMBER_OF_CHILDREN);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < currentCount; i++) {
|
||||||
|
VoxelNode* childNode = sortedChildren[i];
|
||||||
|
if (childNode) {
|
||||||
|
//printLog("recurseNodeWithOperationDistanceSorted() PROCESSING child[%d] distance=%f...\n", i, distancesToChildren[i]);
|
||||||
|
//childNode->printDebugDetails("");
|
||||||
|
recurseNodeWithOperationDistanceSorted(childNode, operation, point, extraData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
VoxelNode* VoxelTree::nodeForOctalCode(VoxelNode* ancestorNode,
|
||||||
|
unsigned char* needleCode, VoxelNode** parentOfFoundNode) const {
|
||||||
// find the appropriate branch index based on this ancestorNode
|
// find the appropriate branch index based on this ancestorNode
|
||||||
if (*needleCode > 0) {
|
if (*needleCode > 0) {
|
||||||
int branchForNeedle = branchIndexWithDescendant(ancestorNode->getOctalCode(), needleCode);
|
int branchForNeedle = branchIndexWithDescendant(ancestorNode->getOctalCode(), needleCode);
|
||||||
|
@ -1012,7 +1057,7 @@ int VoxelTree::encodeTreeBitstream(VoxelNode* node, unsigned char* outputBuffer,
|
||||||
*outputBuffer = 0; // root
|
*outputBuffer = 0; // root
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
codeLength = bytesRequiredForCodeLength(*node->getOctalCode());
|
codeLength = bytesRequiredForCodeLength(numberOfThreeBitSectionsInCode(node->getOctalCode()));
|
||||||
memcpy(outputBuffer, node->getOctalCode(), codeLength);
|
memcpy(outputBuffer, node->getOctalCode(), codeLength);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1072,6 +1117,39 @@ int VoxelTree::encodeTreeBitstreamRecursion(VoxelNode* node, unsigned char* outp
|
||||||
if (!node->isInView(*params.viewFrustum)) {
|
if (!node->isInView(*params.viewFrustum)) {
|
||||||
return bytesAtThisLevel;
|
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
|
||||||
|
if (params.wantOcclusionCulling && !node->isLeaf()) {
|
||||||
|
//node->printDebugDetails("upper section, params.wantOcclusionCulling... node=");
|
||||||
|
AABox voxelBox = node->getAABox();
|
||||||
|
voxelBox.scale(TREE_SCALE);
|
||||||
|
VoxelProjectedPolygon* voxelShadow = new VoxelProjectedPolygon(params.viewFrustum->getProjectedShadow(voxelBox));
|
||||||
|
|
||||||
|
// In order to check occlusion culling, the shadow has to be "all in view" otherwise, we will ignore occlusion
|
||||||
|
// culling and proceed as normal
|
||||||
|
if (voxelShadow->getAllInView()) {
|
||||||
|
//node->printDebugDetails("upper section, voxelShadow->getAllInView() node=");
|
||||||
|
|
||||||
|
CoverageMap::StorageResult result = params.map->checkMap(voxelShadow, false);
|
||||||
|
delete voxelShadow; // cleanup
|
||||||
|
if (result == CoverageMap::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 voxelShadow;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool keepDiggingDeeper = true; // Assuming we're in view we have a great work ethic, we're always ready for more!
|
bool keepDiggingDeeper = true; // Assuming we're in view we have a great work ethic, we're always ready for more!
|
||||||
|
@ -1080,37 +1158,68 @@ int VoxelTree::encodeTreeBitstreamRecursion(VoxelNode* node, unsigned char* outp
|
||||||
// is 1 byte for child colors + 3*NUMBER_OF_CHILDREN bytes for the actual colors + 1 byte for child trees. There could be sub trees
|
// is 1 byte for child colors + 3*NUMBER_OF_CHILDREN bytes for the actual colors + 1 byte for child trees. There could be sub trees
|
||||||
// below this point, which might take many more bytes, but that's ok, because we can always mark our subtrees as
|
// below this point, which might take many more bytes, but that's ok, because we can always mark our subtrees as
|
||||||
// not existing and stop the packet at this point, then start up with a new packet for the remaining sub trees.
|
// not existing and stop the packet at this point, then start up with a new packet for the remaining sub trees.
|
||||||
const int CHILD_COLOR_MASK_BYTES = 1;
|
unsigned char childrenExistInTreeBits = 0;
|
||||||
|
unsigned char childrenExistInPacketBits = 0;
|
||||||
|
unsigned char childrenColoredBits = 0;
|
||||||
|
|
||||||
|
const int CHILD_COLOR_MASK_BYTES = sizeof(childrenColoredBits);
|
||||||
const int BYTES_PER_COLOR = 3;
|
const int BYTES_PER_COLOR = 3;
|
||||||
const int CHILD_TREE_EXISTS_BYTES = 1;
|
const int CHILD_TREE_EXISTS_BYTES = sizeof(childrenExistInTreeBits) + sizeof(childrenExistInPacketBits);
|
||||||
const int MAX_LEVEL_BYTES = CHILD_COLOR_MASK_BYTES + NUMBER_OF_CHILDREN * BYTES_PER_COLOR + CHILD_TREE_EXISTS_BYTES;
|
const int MAX_LEVEL_BYTES = CHILD_COLOR_MASK_BYTES + NUMBER_OF_CHILDREN * BYTES_PER_COLOR + CHILD_TREE_EXISTS_BYTES;
|
||||||
|
|
||||||
// Make our local buffer large enough to handle writing at this level in case we need to.
|
// Make our local buffer large enough to handle writing at this level in case we need to.
|
||||||
unsigned char thisLevelBuffer[MAX_LEVEL_BYTES];
|
unsigned char thisLevelBuffer[MAX_LEVEL_BYTES];
|
||||||
unsigned char* writeToThisLevelBuffer = &thisLevelBuffer[0];
|
unsigned char* writeToThisLevelBuffer = &thisLevelBuffer[0];
|
||||||
|
|
||||||
unsigned char childrenExistInTreeBits = 0;
|
|
||||||
unsigned char childrenExistInPacketBits = 0;
|
|
||||||
unsigned char childrenColoredBits = 0;
|
|
||||||
int inViewCount = 0;
|
int inViewCount = 0;
|
||||||
int inViewNotLeafCount = 0;
|
int inViewNotLeafCount = 0;
|
||||||
int inViewWithColorCount = 0;
|
int inViewWithColorCount = 0;
|
||||||
|
|
||||||
// for each child node, check to see if they exist, are colored, and in view, and if so
|
VoxelNode* sortedChildren[NUMBER_OF_CHILDREN];
|
||||||
// add them to our distance ordered array 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++) {
|
for (int i = 0; i < NUMBER_OF_CHILDREN; i++) {
|
||||||
VoxelNode* childNode = node->getChildAtIndex(i);
|
VoxelNode* childNode = node->getChildAtIndex(i);
|
||||||
|
|
||||||
// if the caller wants to include childExistsBits, then include them even if not in view
|
// if the caller wants to include childExistsBits, then include them even if not in view
|
||||||
if (params.includeExistsBits && childNode) {
|
if (params.includeExistsBits && childNode) {
|
||||||
childrenExistInTreeBits += (1 << (7 - i));
|
childrenExistInTreeBits += (1 << (7 - i));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (params.wantOcclusionCulling) {
|
||||||
|
if (childNode) {
|
||||||
|
// chance to optimize, doesn't need to be actual distance!! Could be distance squared
|
||||||
|
//float distanceSquared = childNode->distanceSquareToPoint(point);
|
||||||
|
//printLog("recurseNodeWithOperationDistanceSorted() CHECKING child[%d] point=%f,%f center=%f,%f distance=%f...\n", i, point.x, point.y, center.x, center.y, distance);
|
||||||
|
//childNode->printDebugDetails("");
|
||||||
|
|
||||||
|
float distance = params.viewFrustum ? childNode->distanceToCamera(*params.viewFrustum) : 0;
|
||||||
|
|
||||||
|
currentCount = insertIntoSortedArrays((void*)childNode, distance, i,
|
||||||
|
(void**)&sortedChildren, (float*)&distancesToChildren,
|
||||||
|
(int*)&indexOfChildren, currentCount, NUMBER_OF_CHILDREN);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
sortedChildren[i] = childNode;
|
||||||
|
indexOfChildren[i] = i;
|
||||||
|
distancesToChildren[i] = 0.0f;
|
||||||
|
currentCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// for each child node in Distance sorted order..., check to see if they exist, are colored, and in view, and if so
|
||||||
|
// add them to our distance ordered array of children
|
||||||
|
for (int i = 0; i < currentCount; i++) {
|
||||||
|
VoxelNode* childNode = sortedChildren[i];
|
||||||
|
int originalIndex = indexOfChildren[i];
|
||||||
|
|
||||||
bool childIsInView = (childNode && (!params.viewFrustum || childNode->isInView(*params.viewFrustum)));
|
bool childIsInView = (childNode && (!params.viewFrustum || childNode->isInView(*params.viewFrustum)));
|
||||||
|
|
||||||
if (childIsInView) {
|
if (childIsInView) {
|
||||||
// Before we determine consider this further, let's see if it's in our LOD scope...
|
// Before we determine consider this further, let's see if it's in our LOD scope...
|
||||||
float distance = params.viewFrustum ? childNode->distanceToCamera(*params.viewFrustum) : 0;
|
float distance = distancesToChildren[i]; // params.viewFrustum ? childNode->distanceToCamera(*params.viewFrustum) : 0;
|
||||||
float boundaryDistance = params.viewFrustum ? boundaryDistanceForRenderLevel(*childNode->getOctalCode() + 1) : 1;
|
float boundaryDistance = params.viewFrustum ? boundaryDistanceForRenderLevel(*childNode->getOctalCode() + 1) : 1;
|
||||||
|
|
||||||
if (distance < boundaryDistance) {
|
if (distance < boundaryDistance) {
|
||||||
|
@ -1120,16 +1229,48 @@ int VoxelTree::encodeTreeBitstreamRecursion(VoxelNode* node, unsigned char* outp
|
||||||
// we don't care about recursing deeper on them, and we don't consider their
|
// we don't care about recursing deeper on them, and we don't consider their
|
||||||
// subtree to exist
|
// subtree to exist
|
||||||
if (!(childNode && childNode->isLeaf())) {
|
if (!(childNode && childNode->isLeaf())) {
|
||||||
childrenExistInPacketBits += (1 << (7 - i));
|
childrenExistInPacketBits += (1 << (7 - originalIndex));
|
||||||
inViewNotLeafCount++;
|
inViewNotLeafCount++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool childIsOccluded = false; // assume it's not occluded
|
||||||
|
|
||||||
|
// If the user also asked for occlusion culling, check if this node is occluded
|
||||||
|
if (params.wantOcclusionCulling && childNode->isLeaf()) {
|
||||||
|
// Don't check occlusion here, just add them to our distance ordered array...
|
||||||
|
|
||||||
|
AABox voxelBox = childNode->getAABox();
|
||||||
|
voxelBox.scale(TREE_SCALE);
|
||||||
|
VoxelProjectedPolygon* voxelShadow = new VoxelProjectedPolygon(params.viewFrustum->getProjectedShadow(voxelBox));
|
||||||
|
|
||||||
|
// In order to check occlusion culling, the shadow has to be "all in view" otherwise, we will ignore occlusion
|
||||||
|
// culling and proceed as normal
|
||||||
|
if (voxelShadow->getAllInView()) {
|
||||||
|
CoverageMap::StorageResult result = params.map->checkMap(voxelShadow, true);
|
||||||
|
|
||||||
|
// In all cases where the shadow wasn't stored, we need to free our own memory.
|
||||||
|
// In the case where it is stored, the CoverageMap will free memory for us later.
|
||||||
|
if (result != CoverageMap::STORED) {
|
||||||
|
delete voxelShadow;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If while attempting to add this voxel's shadow, we determined it was occluded, then
|
||||||
|
// we don't need to process it further and we can exit early.
|
||||||
|
if (result == CoverageMap::OCCLUDED) {
|
||||||
|
childIsOccluded = true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
delete voxelShadow;
|
||||||
|
}
|
||||||
|
} // wants occlusion culling & isLeaf()
|
||||||
|
|
||||||
|
|
||||||
bool childWasInView = (childNode && params.deltaViewFrustum &&
|
bool childWasInView = (childNode && params.deltaViewFrustum &&
|
||||||
(params.lastViewFrustum && ViewFrustum::INSIDE == childNode->inFrustum(*params.lastViewFrustum)));
|
(params.lastViewFrustum && ViewFrustum::INSIDE == childNode->inFrustum(*params.lastViewFrustum)));
|
||||||
|
|
||||||
// track children with actual color, only if the child wasn't previously in view!
|
// track children with actual color, only if the child wasn't previously in view!
|
||||||
if (childNode && childNode->isColored() && !childWasInView) {
|
if (childNode && childNode->isColored() && !childWasInView && !childIsOccluded) {
|
||||||
childrenColoredBits += (1 << (7 - i));
|
childrenColoredBits += (1 << (7 - originalIndex));
|
||||||
inViewWithColorCount++;
|
inViewWithColorCount++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1189,15 +1330,35 @@ int VoxelTree::encodeTreeBitstreamRecursion(VoxelNode* node, unsigned char* outp
|
||||||
//
|
//
|
||||||
// we know the last thing we wrote to the outputBuffer was our childrenExistInPacketBits. Let's remember where that was!
|
// we know the last thing we wrote to the outputBuffer was our childrenExistInPacketBits. Let's remember where that was!
|
||||||
unsigned char* childExistsPlaceHolder = outputBuffer-sizeof(childrenExistInPacketBits);
|
unsigned char* childExistsPlaceHolder = outputBuffer-sizeof(childrenExistInPacketBits);
|
||||||
|
|
||||||
for (int i = 0; i < NUMBER_OF_CHILDREN; i++) {
|
|
||||||
|
|
||||||
if (oneAtBit(childrenExistInPacketBits, i)) {
|
// we are also going to recurse these child trees in "distance" sorted order, but we need to pack them in the
|
||||||
VoxelNode* childNode = node->getChildAtIndex(i);
|
// final packet in standard order. So what we're going to do is keep track of how big each subtree was in bytes,
|
||||||
|
// and then later reshuffle these sections of our output buffer back into normal order. This allows us to make
|
||||||
|
// a single recursive pass in distance sorted order, but retain standard order in our encoded packet
|
||||||
|
int recursiveSliceSizes[NUMBER_OF_CHILDREN];
|
||||||
|
unsigned char* recursiveSliceStarts[NUMBER_OF_CHILDREN];
|
||||||
|
unsigned char* firstRecursiveSlice = outputBuffer;
|
||||||
|
int allSlicesSize = 0;
|
||||||
|
|
||||||
|
// for each child node in Distance sorted order..., check to see if they exist, are colored, and in view, and if so
|
||||||
|
// add them to our distance ordered array of children
|
||||||
|
for (int indexByDistance = 0; indexByDistance < currentCount; indexByDistance++) {
|
||||||
|
VoxelNode* childNode = sortedChildren[indexByDistance];
|
||||||
|
int originalIndex = indexOfChildren[indexByDistance];
|
||||||
|
|
||||||
|
if (oneAtBit(childrenExistInPacketBits, originalIndex)) {
|
||||||
|
|
||||||
int thisLevel = currentEncodeLevel;
|
int thisLevel = currentEncodeLevel;
|
||||||
|
|
||||||
|
// remember this for reshuffling
|
||||||
|
recursiveSliceStarts[originalIndex] = outputBuffer;
|
||||||
|
|
||||||
int childTreeBytesOut = encodeTreeBitstreamRecursion(childNode, outputBuffer, availableBytes, bag,
|
int childTreeBytesOut = encodeTreeBitstreamRecursion(childNode, outputBuffer, availableBytes, bag,
|
||||||
params, thisLevel);
|
params, thisLevel);
|
||||||
|
|
||||||
|
// remember this for reshuffling
|
||||||
|
recursiveSliceSizes[originalIndex] = childTreeBytesOut;
|
||||||
|
allSlicesSize += childTreeBytesOut;
|
||||||
|
|
||||||
// if the child wrote 0 bytes, it means that nothing below exists or was in view, or we ran out of space,
|
// if the child wrote 0 bytes, it means that nothing below exists or was in view, or we ran out of space,
|
||||||
// basically, the children below don't contain any info.
|
// basically, the children below don't contain any info.
|
||||||
|
@ -1227,15 +1388,40 @@ int VoxelTree::encodeTreeBitstreamRecursion(VoxelNode* node, unsigned char* outp
|
||||||
// then we want to remove their bit from the childExistsPlaceHolder bitmask
|
// then we want to remove their bit from the childExistsPlaceHolder bitmask
|
||||||
if (childTreeBytesOut == 0) {
|
if (childTreeBytesOut == 0) {
|
||||||
// remove this child's bit...
|
// remove this child's bit...
|
||||||
childrenExistInPacketBits -= (1 << (7 - i));
|
childrenExistInPacketBits -= (1 << (7 - originalIndex));
|
||||||
// repair the child exists mask
|
// repair the child exists mask
|
||||||
*childExistsPlaceHolder = childrenExistInPacketBits;
|
*childExistsPlaceHolder = childrenExistInPacketBits;
|
||||||
// Note: no need to move the pointer, cause we already stored this
|
// Note: no need to move the pointer, cause we already stored this
|
||||||
} // end if (childTreeBytesOut == 0)
|
} // end if (childTreeBytesOut == 0)
|
||||||
} // end if (oneAtBit(childrenExistInPacketBits, i))
|
} // end if (oneAtBit(childrenExistInPacketBits, originalIndex))
|
||||||
} // end for
|
} // end for
|
||||||
} // end keepDiggingDeeper
|
|
||||||
|
|
||||||
|
// reshuffle here...
|
||||||
|
if (params.wantOcclusionCulling) {
|
||||||
|
unsigned char tempReshuffleBuffer[MAX_VOXEL_PACKET_SIZE];
|
||||||
|
|
||||||
|
unsigned char* tempBufferTo = &tempReshuffleBuffer[0]; // this is our temporary destination
|
||||||
|
|
||||||
|
// iterate through our childrenExistInPacketBits, these will be the sections of the packet that we copied subTree
|
||||||
|
// details into. Unfortunately, they're in distance sorted order, not original index order. we need to put them
|
||||||
|
// back into original distance order
|
||||||
|
for (int originalIndex = 0; originalIndex < NUMBER_OF_CHILDREN; originalIndex++) {
|
||||||
|
if (oneAtBit(childrenExistInPacketBits, originalIndex)) {
|
||||||
|
int thisSliceSize = recursiveSliceSizes[originalIndex];
|
||||||
|
unsigned char* thisSliceStarts = recursiveSliceStarts[originalIndex];
|
||||||
|
|
||||||
|
memcpy(tempBufferTo, thisSliceStarts, thisSliceSize);
|
||||||
|
tempBufferTo += thisSliceSize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// now that all slices are back in the correct order, copy them to the correct output buffer
|
||||||
|
memcpy(firstRecursiveSlice, &tempReshuffleBuffer[0], allSlicesSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
} // end keepDiggingDeeper
|
||||||
|
|
||||||
return bytesAtThisLevel;
|
return bytesAtThisLevel;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,20 +13,25 @@
|
||||||
#include "ViewFrustum.h"
|
#include "ViewFrustum.h"
|
||||||
#include "VoxelNode.h"
|
#include "VoxelNode.h"
|
||||||
#include "VoxelNodeBag.h"
|
#include "VoxelNodeBag.h"
|
||||||
|
#include "CoverageMap.h"
|
||||||
|
|
||||||
// Callback function, for recuseTreeWithOperation
|
// Callback function, for recuseTreeWithOperation
|
||||||
typedef bool (*RecurseVoxelTreeOperation)(VoxelNode* node, void* extraData);
|
typedef bool (*RecurseVoxelTreeOperation)(VoxelNode* node, void* extraData);
|
||||||
typedef enum {GRADIENT, RANDOM, NATURAL} creationMode;
|
typedef enum {GRADIENT, RANDOM, NATURAL} creationMode;
|
||||||
|
|
||||||
#define NO_EXISTS_BITS false
|
#define NO_EXISTS_BITS false
|
||||||
#define WANT_EXISTS_BITS true
|
#define WANT_EXISTS_BITS true
|
||||||
#define NO_COLOR false
|
#define NO_COLOR false
|
||||||
#define WANT_COLOR true
|
#define WANT_COLOR true
|
||||||
#define IGNORE_VIEW_FRUSTUM NULL
|
#define IGNORE_VIEW_FRUSTUM NULL
|
||||||
#define JUST_STAGE_DELETION true
|
#define JUST_STAGE_DELETION true
|
||||||
#define ACTUALLY_DELETE false
|
#define ACTUALLY_DELETE false
|
||||||
#define COLLAPSE_EMPTY_TREE true
|
#define COLLAPSE_EMPTY_TREE true
|
||||||
#define DONT_COLLAPSE false
|
#define DONT_COLLAPSE false
|
||||||
|
#define NO_OCCLUSION_CULLING false
|
||||||
|
#define WANT_OCCLUSION_CULLING true
|
||||||
|
#define IGNORE_COVERAGE_MAP NULL
|
||||||
|
#define DONT_CHOP 0
|
||||||
|
|
||||||
class EncodeBitstreamParams {
|
class EncodeBitstreamParams {
|
||||||
public:
|
public:
|
||||||
|
@ -37,6 +42,8 @@ public:
|
||||||
int chopLevels;
|
int chopLevels;
|
||||||
bool deltaViewFrustum;
|
bool deltaViewFrustum;
|
||||||
const ViewFrustum* lastViewFrustum;
|
const ViewFrustum* lastViewFrustum;
|
||||||
|
bool wantOcclusionCulling;
|
||||||
|
CoverageMap* map;
|
||||||
|
|
||||||
EncodeBitstreamParams(
|
EncodeBitstreamParams(
|
||||||
int maxEncodeLevel = INT_MAX,
|
int maxEncodeLevel = INT_MAX,
|
||||||
|
@ -45,7 +52,9 @@ public:
|
||||||
bool includeExistsBits = WANT_EXISTS_BITS,
|
bool includeExistsBits = WANT_EXISTS_BITS,
|
||||||
int chopLevels = 0,
|
int chopLevels = 0,
|
||||||
bool deltaViewFrustum = false,
|
bool deltaViewFrustum = false,
|
||||||
const ViewFrustum* lastViewFrustum = IGNORE_VIEW_FRUSTUM) :
|
const ViewFrustum* lastViewFrustum = IGNORE_VIEW_FRUSTUM,
|
||||||
|
bool wantOcclusionCulling= NO_OCCLUSION_CULLING,
|
||||||
|
CoverageMap* map = IGNORE_COVERAGE_MAP) :
|
||||||
|
|
||||||
maxEncodeLevel (maxEncodeLevel),
|
maxEncodeLevel (maxEncodeLevel),
|
||||||
viewFrustum (viewFrustum),
|
viewFrustum (viewFrustum),
|
||||||
|
@ -53,7 +62,9 @@ public:
|
||||||
includeExistsBits (includeExistsBits),
|
includeExistsBits (includeExistsBits),
|
||||||
chopLevels (chopLevels),
|
chopLevels (chopLevels),
|
||||||
deltaViewFrustum (deltaViewFrustum),
|
deltaViewFrustum (deltaViewFrustum),
|
||||||
lastViewFrustum (lastViewFrustum)
|
lastViewFrustum (lastViewFrustum),
|
||||||
|
wantOcclusionCulling(wantOcclusionCulling),
|
||||||
|
map (map)
|
||||||
{}
|
{}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -96,6 +107,8 @@ public:
|
||||||
creationMode mode, bool destructive = false, bool debug = false);
|
creationMode mode, bool destructive = false, bool debug = false);
|
||||||
|
|
||||||
void recurseTreeWithOperation(RecurseVoxelTreeOperation operation, void* extraData=NULL);
|
void recurseTreeWithOperation(RecurseVoxelTreeOperation operation, void* extraData=NULL);
|
||||||
|
void recurseTreeWithOperationDistanceSorted(RecurseVoxelTreeOperation operation,
|
||||||
|
const glm::vec3& point, void* extraData=NULL);
|
||||||
|
|
||||||
int encodeTreeBitstream(VoxelNode* node, unsigned char* outputBuffer, int availableBytes, VoxelNodeBag& bag,
|
int encodeTreeBitstream(VoxelNode* node, unsigned char* outputBuffer, int availableBytes, VoxelNodeBag& bag,
|
||||||
EncodeBitstreamParams& params) const;
|
EncodeBitstreamParams& params) const;
|
||||||
|
@ -127,6 +140,10 @@ public:
|
||||||
void copyFromTreeIntoSubTree(VoxelTree* sourceTree, VoxelNode* destinationNode);
|
void copyFromTreeIntoSubTree(VoxelTree* sourceTree, VoxelNode* destinationNode);
|
||||||
|
|
||||||
bool getShouldReaverage() const { return _shouldReaverage; }
|
bool getShouldReaverage() const { return _shouldReaverage; }
|
||||||
|
|
||||||
|
void recurseNodeWithOperation(VoxelNode* node, RecurseVoxelTreeOperation operation, void* extraData);
|
||||||
|
void recurseNodeWithOperationDistanceSorted(VoxelNode* node, RecurseVoxelTreeOperation operation,
|
||||||
|
const glm::vec3& point, void* extraData);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void deleteVoxelCodeFromTreeRecursion(VoxelNode* node, void* extraData);
|
void deleteVoxelCodeFromTreeRecursion(VoxelNode* node, void* extraData);
|
||||||
|
@ -141,7 +158,6 @@ private:
|
||||||
|
|
||||||
static bool countVoxelsOperation(VoxelNode* node, void* extraData);
|
static bool countVoxelsOperation(VoxelNode* node, void* extraData);
|
||||||
|
|
||||||
void recurseNodeWithOperation(VoxelNode* node, RecurseVoxelTreeOperation operation, void* extraData);
|
|
||||||
VoxelNode* nodeForOctalCode(VoxelNode* ancestorNode, unsigned char* needleCode, VoxelNode** parentOfFoundNode) const;
|
VoxelNode* nodeForOctalCode(VoxelNode* ancestorNode, unsigned char* needleCode, VoxelNode** parentOfFoundNode) const;
|
||||||
VoxelNode* createMissingNode(VoxelNode* lastParentNode, unsigned char* deepestCodeToCreate);
|
VoxelNode* createMissingNode(VoxelNode* lastParentNode, unsigned char* deepestCodeToCreate);
|
||||||
int readNodeData(VoxelNode *destinationNode, unsigned char* nodeData, int bufferSizeBytes,
|
int readNodeData(VoxelNode *destinationNode, unsigned char* nodeData, int bufferSizeBytes,
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
#include <AvatarData.h>
|
#include <AvatarData.h>
|
||||||
#include "VoxelNodeBag.h"
|
#include "VoxelNodeBag.h"
|
||||||
#include "VoxelConstants.h"
|
#include "VoxelConstants.h"
|
||||||
|
#include "CoverageMap.h"
|
||||||
|
|
||||||
class VoxelAgentData : public AvatarData {
|
class VoxelAgentData : public AvatarData {
|
||||||
public:
|
public:
|
||||||
|
@ -36,6 +37,7 @@ public:
|
||||||
void setMaxLevelReached(int maxLevelReached) { _maxLevelReachedInLastSearch = maxLevelReached; }
|
void setMaxLevelReached(int maxLevelReached) { _maxLevelReachedInLastSearch = maxLevelReached; }
|
||||||
|
|
||||||
VoxelNodeBag nodeBag;
|
VoxelNodeBag nodeBag;
|
||||||
|
CoverageMap map;
|
||||||
|
|
||||||
ViewFrustum& getCurrentViewFrustum() { return _currentViewFrustum; };
|
ViewFrustum& getCurrentViewFrustum() { return _currentViewFrustum; };
|
||||||
ViewFrustum& getLastKnownViewFrustum() { return _lastKnownViewFrustum; };
|
ViewFrustum& getLastKnownViewFrustum() { return _lastKnownViewFrustum; };
|
||||||
|
|
|
@ -312,9 +312,13 @@ void deepestLevelVoxelDistributor(AgentList* agentList,
|
||||||
while (packetsSentThisInterval < PACKETS_PER_CLIENT_PER_INTERVAL - (shouldSendEnvironments ? 1 : 0)) {
|
while (packetsSentThisInterval < PACKETS_PER_CLIENT_PER_INTERVAL - (shouldSendEnvironments ? 1 : 0)) {
|
||||||
if (!agentData->nodeBag.isEmpty()) {
|
if (!agentData->nodeBag.isEmpty()) {
|
||||||
VoxelNode* subTree = agentData->nodeBag.extract();
|
VoxelNode* subTree = agentData->nodeBag.extract();
|
||||||
|
|
||||||
|
bool wantOcclusionCulling = agentData->getWantOcclusionCulling();
|
||||||
|
CoverageMap* coverageMap = wantOcclusionCulling ? &agentData->map : IGNORE_COVERAGE_MAP;
|
||||||
|
|
||||||
EncodeBitstreamParams params(INT_MAX, &agentData->getCurrentViewFrustum(), agentData->getWantColor(),
|
EncodeBitstreamParams params(INT_MAX, &agentData->getCurrentViewFrustum(), agentData->getWantColor(),
|
||||||
WANT_EXISTS_BITS, wantDelta, lastViewFrustum);
|
WANT_EXISTS_BITS, DONT_CHOP, wantDelta, lastViewFrustum,
|
||||||
|
wantOcclusionCulling, coverageMap);
|
||||||
|
|
||||||
bytesWritten = serverTree.encodeTreeBitstream(subTree, &tempOutputBuffer[0], MAX_VOXEL_PACKET_SIZE - 1,
|
bytesWritten = serverTree.encodeTreeBitstream(subTree, &tempOutputBuffer[0], MAX_VOXEL_PACKET_SIZE - 1,
|
||||||
agentData->nodeBag, params);
|
agentData->nodeBag, params);
|
||||||
|
@ -375,6 +379,7 @@ void deepestLevelVoxelDistributor(AgentList* agentList,
|
||||||
if (agentData->nodeBag.isEmpty()) {
|
if (agentData->nodeBag.isEmpty()) {
|
||||||
agentData->updateLastKnownViewFrustum();
|
agentData->updateLastKnownViewFrustum();
|
||||||
agentData->setViewSent(true);
|
agentData->setViewSent(true);
|
||||||
|
agentData->map.erase();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue