mirror of
https://github.com/overte-org/overte.git
synced 2025-06-18 00:00:43 +02:00
Merge branch 'master' of https://github.com/worklist/hifi into occlusion_culling
This commit is contained in:
commit
c9e1c898c2
21 changed files with 453 additions and 218 deletions
|
@ -19,3 +19,9 @@ include_glm(${TARGET_NAME} ${ROOT_DIR})
|
||||||
include(${MACRO_DIR}/LinkHifiLibrary.cmake)
|
include(${MACRO_DIR}/LinkHifiLibrary.cmake)
|
||||||
link_hifi_library(shared ${TARGET_NAME} ${ROOT_DIR})
|
link_hifi_library(shared ${TARGET_NAME} ${ROOT_DIR})
|
||||||
link_hifi_library(audio ${TARGET_NAME} ${ROOT_DIR})
|
link_hifi_library(audio ${TARGET_NAME} ${ROOT_DIR})
|
||||||
|
|
||||||
|
# link the stk library
|
||||||
|
set(STK_ROOT_DIR ${ROOT_DIR}/externals/stk)
|
||||||
|
find_package(STK REQUIRED)
|
||||||
|
target_link_libraries(${TARGET_NAME} ${STK_LIBRARIES})
|
||||||
|
include_directories(${STK_INCLUDE_DIRS})
|
|
@ -11,10 +11,18 @@
|
||||||
#include "AvatarAudioRingBuffer.h"
|
#include "AvatarAudioRingBuffer.h"
|
||||||
|
|
||||||
AvatarAudioRingBuffer::AvatarAudioRingBuffer() :
|
AvatarAudioRingBuffer::AvatarAudioRingBuffer() :
|
||||||
|
_twoPoles(),
|
||||||
_shouldLoopbackForAgent(false) {
|
_shouldLoopbackForAgent(false) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
AvatarAudioRingBuffer::~AvatarAudioRingBuffer() {
|
||||||
|
// enumerate the freeVerbs map and delete the FreeVerb objects
|
||||||
|
for (TwoPoleAgentMap::iterator poleIterator = _twoPoles.begin(); poleIterator != _twoPoles.end(); poleIterator++) {
|
||||||
|
delete poleIterator->second;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int AvatarAudioRingBuffer::parseData(unsigned char* sourceBuffer, int numBytes) {
|
int AvatarAudioRingBuffer::parseData(unsigned char* sourceBuffer, int numBytes) {
|
||||||
_shouldLoopbackForAgent = (sourceBuffer[0] == PACKET_HEADER_MICROPHONE_AUDIO_WITH_ECHO);
|
_shouldLoopbackForAgent = (sourceBuffer[0] == PACKET_HEADER_MICROPHONE_AUDIO_WITH_ECHO);
|
||||||
return PositionalAudioRingBuffer::parseData(sourceBuffer, numBytes);
|
return PositionalAudioRingBuffer::parseData(sourceBuffer, numBytes);
|
||||||
|
|
|
@ -9,20 +9,29 @@
|
||||||
#ifndef __hifi__AvatarAudioRingBuffer__
|
#ifndef __hifi__AvatarAudioRingBuffer__
|
||||||
#define __hifi__AvatarAudioRingBuffer__
|
#define __hifi__AvatarAudioRingBuffer__
|
||||||
|
|
||||||
|
#include <Stk.h>
|
||||||
|
#include <TwoPole.h>
|
||||||
|
|
||||||
#include "PositionalAudioRingBuffer.h"
|
#include "PositionalAudioRingBuffer.h"
|
||||||
|
|
||||||
|
typedef std::map<uint16_t, stk::TwoPole*> TwoPoleAgentMap;
|
||||||
|
|
||||||
class AvatarAudioRingBuffer : public PositionalAudioRingBuffer {
|
class AvatarAudioRingBuffer : public PositionalAudioRingBuffer {
|
||||||
public:
|
public:
|
||||||
AvatarAudioRingBuffer();
|
AvatarAudioRingBuffer();
|
||||||
|
~AvatarAudioRingBuffer();
|
||||||
|
|
||||||
int parseData(unsigned char* sourceBuffer, int numBytes);
|
int parseData(unsigned char* sourceBuffer, int numBytes);
|
||||||
|
|
||||||
|
TwoPoleAgentMap& getTwoPoles() { return _twoPoles; }
|
||||||
|
|
||||||
bool shouldLoopbackForAgent() const { return _shouldLoopbackForAgent; }
|
bool shouldLoopbackForAgent() const { return _shouldLoopbackForAgent; }
|
||||||
private:
|
private:
|
||||||
// disallow copying of AvatarAudioRingBuffer objects
|
// disallow copying of AvatarAudioRingBuffer objects
|
||||||
AvatarAudioRingBuffer(const AvatarAudioRingBuffer&);
|
AvatarAudioRingBuffer(const AvatarAudioRingBuffer&);
|
||||||
AvatarAudioRingBuffer& operator= (const AvatarAudioRingBuffer&);
|
AvatarAudioRingBuffer& operator= (const AvatarAudioRingBuffer&);
|
||||||
|
|
||||||
|
TwoPoleAgentMap _twoPoles;
|
||||||
bool _shouldLoopbackForAgent;
|
bool _shouldLoopbackForAgent;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -24,6 +24,7 @@
|
||||||
#include <AgentTypes.h>
|
#include <AgentTypes.h>
|
||||||
#include <SharedUtil.h>
|
#include <SharedUtil.h>
|
||||||
#include <StdDev.h>
|
#include <StdDev.h>
|
||||||
|
#include <Logstash.h>
|
||||||
|
|
||||||
#include "InjectedAudioRingBuffer.h"
|
#include "InjectedAudioRingBuffer.h"
|
||||||
#include "AvatarAudioRingBuffer.h"
|
#include "AvatarAudioRingBuffer.h"
|
||||||
|
@ -46,7 +47,7 @@ const unsigned short MIXER_LISTEN_PORT = 55443;
|
||||||
const short JITTER_BUFFER_MSECS = 12;
|
const short JITTER_BUFFER_MSECS = 12;
|
||||||
const short JITTER_BUFFER_SAMPLES = JITTER_BUFFER_MSECS * (SAMPLE_RATE / 1000.0);
|
const short JITTER_BUFFER_SAMPLES = JITTER_BUFFER_MSECS * (SAMPLE_RATE / 1000.0);
|
||||||
|
|
||||||
const float BUFFER_SEND_INTERVAL_USECS = (BUFFER_LENGTH_SAMPLES_PER_CHANNEL / SAMPLE_RATE) * 1000000;
|
const long long BUFFER_SEND_INTERVAL_USECS = floorf((BUFFER_LENGTH_SAMPLES_PER_CHANNEL / SAMPLE_RATE) * 1000000);
|
||||||
|
|
||||||
const long MAX_SAMPLE_VALUE = std::numeric_limits<int16_t>::max();
|
const long MAX_SAMPLE_VALUE = std::numeric_limits<int16_t>::max();
|
||||||
const long MIN_SAMPLE_VALUE = std::numeric_limits<int16_t>::min();
|
const long MIN_SAMPLE_VALUE = std::numeric_limits<int16_t>::min();
|
||||||
|
@ -70,9 +71,20 @@ void attachNewBufferToAgent(Agent *newAgent) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool wantLocalDomain = false;
|
||||||
|
|
||||||
int main(int argc, const char* argv[]) {
|
int main(int argc, const char* argv[]) {
|
||||||
setvbuf(stdout, NULL, _IOLBF, 0);
|
setvbuf(stdout, NULL, _IOLBF, 0);
|
||||||
|
|
||||||
|
// Handle Local Domain testing with the --local command line
|
||||||
|
const char* local = "--local";
|
||||||
|
::wantLocalDomain = cmdOptionExists(argc, argv,local);
|
||||||
|
if (::wantLocalDomain) {
|
||||||
|
printf("Local Domain MODE!\n");
|
||||||
|
int ip = getLocalAddress();
|
||||||
|
sprintf(DOMAIN_IP,"%d.%d.%d.%d", (ip & 0xFF), ((ip >> 8) & 0xFF),((ip >> 16) & 0xFF), ((ip >> 24) & 0xFF));
|
||||||
|
}
|
||||||
|
|
||||||
AgentList* agentList = AgentList::createInstance(AGENT_TYPE_AUDIO_MIXER, MIXER_LISTEN_PORT);
|
AgentList* agentList = AgentList::createInstance(AGENT_TYPE_AUDIO_MIXER, MIXER_LISTEN_PORT);
|
||||||
|
|
||||||
ssize_t receivedBytes = 0;
|
ssize_t receivedBytes = 0;
|
||||||
|
@ -100,12 +112,42 @@ int main(int argc, const char* argv[]) {
|
||||||
|
|
||||||
timeval lastDomainServerCheckIn = {};
|
timeval lastDomainServerCheckIn = {};
|
||||||
|
|
||||||
|
timeval beginSendTime, endSendTime;
|
||||||
|
float sumFrameTimePercentages = 0.0f;
|
||||||
|
int numStatCollections = 0;
|
||||||
|
|
||||||
|
// if we'll be sending stats, call the Logstash::socket() method to make it load the logstash IP outside the loop
|
||||||
|
if (Logstash::shouldSendStats()) {
|
||||||
|
Logstash::socket();
|
||||||
|
}
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
|
if (Logstash::shouldSendStats()) {
|
||||||
|
gettimeofday(&beginSendTime, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
// 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 (Logstash::shouldSendStats() && numStatCollections > 0) {
|
||||||
|
// if we should be sending stats to Logstash send the appropriate average now
|
||||||
|
const char MIXER_LOGSTASH_METRIC_NAME[] = "audio-mixer-frame-time-usage";
|
||||||
|
|
||||||
|
// we're sending a floating point percentage with two mandatory numbers after decimal point
|
||||||
|
// that could be up to 6 bytes
|
||||||
|
const int MIXER_LOGSTASH_PACKET_BYTES = strlen(MIXER_LOGSTASH_METRIC_NAME) + 7;
|
||||||
|
char logstashPacket[MIXER_LOGSTASH_PACKET_BYTES];
|
||||||
|
|
||||||
|
float averageFrameTimePercentage = sumFrameTimePercentages / numStatCollections;
|
||||||
|
int packetBytes = sprintf(logstashPacket, "%s %.2f", MIXER_LOGSTASH_METRIC_NAME, averageFrameTimePercentage);
|
||||||
|
|
||||||
|
agentList->getAgentSocket()->send(Logstash::socket(), logstashPacket, packetBytes);
|
||||||
|
|
||||||
|
sumFrameTimePercentages = 0.0f;
|
||||||
|
numStatCollections = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (AgentList::iterator agent = agentList->begin(); agent != agentList->end(); agent++) {
|
for (AgentList::iterator agent = agentList->begin(); agent != agentList->end(); agent++) {
|
||||||
|
@ -136,6 +178,8 @@ int main(int argc, const char* argv[]) {
|
||||||
int numSamplesDelay = 0;
|
int numSamplesDelay = 0;
|
||||||
float weakChannelAmplitudeRatio = 1.0f;
|
float weakChannelAmplitudeRatio = 1.0f;
|
||||||
|
|
||||||
|
stk::TwoPole* otherAgentTwoPole = NULL;
|
||||||
|
|
||||||
if (otherAgent != agent) {
|
if (otherAgent != agent) {
|
||||||
|
|
||||||
glm::vec3 listenerPosition = agentRingBuffer->getPosition();
|
glm::vec3 listenerPosition = agentRingBuffer->getPosition();
|
||||||
|
@ -212,6 +256,26 @@ int main(int argc, const char* argv[]) {
|
||||||
float sinRatio = fabsf(sinf(glm::radians(bearingRelativeAngleToSource)));
|
float sinRatio = fabsf(sinf(glm::radians(bearingRelativeAngleToSource)));
|
||||||
numSamplesDelay = PHASE_DELAY_AT_90 * sinRatio;
|
numSamplesDelay = PHASE_DELAY_AT_90 * sinRatio;
|
||||||
weakChannelAmplitudeRatio = 1 - (PHASE_AMPLITUDE_RATIO_AT_90 * sinRatio);
|
weakChannelAmplitudeRatio = 1 - (PHASE_AMPLITUDE_RATIO_AT_90 * sinRatio);
|
||||||
|
|
||||||
|
// grab the TwoPole object for this source, add it if it doesn't exist
|
||||||
|
TwoPoleAgentMap& agentTwoPoles = agentRingBuffer->getTwoPoles();
|
||||||
|
TwoPoleAgentMap::iterator twoPoleIterator = agentTwoPoles.find(otherAgent->getAgentID());
|
||||||
|
|
||||||
|
if (twoPoleIterator == agentTwoPoles.end()) {
|
||||||
|
// setup the freeVerb effect for this source for this client
|
||||||
|
otherAgentTwoPole = agentTwoPoles[otherAgent->getAgentID()] = new stk::TwoPole;
|
||||||
|
} else {
|
||||||
|
otherAgentTwoPole = twoPoleIterator->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
// calculate the reasonance for this TwoPole based on angle to source
|
||||||
|
float TWO_POLE_CUT_OFF_FREQUENCY = 800.0f;
|
||||||
|
float TWO_POLE_MAX_FILTER_STRENGTH = 0.4f;
|
||||||
|
|
||||||
|
otherAgentTwoPole->setResonance(TWO_POLE_CUT_OFF_FREQUENCY,
|
||||||
|
TWO_POLE_MAX_FILTER_STRENGTH
|
||||||
|
* fabsf(bearingRelativeAngleToSource) / 180.0f,
|
||||||
|
true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -237,6 +301,10 @@ int main(int argc, const char* argv[]) {
|
||||||
plateauAdditionOfSamples(delayedChannel[s], earlierSample);
|
plateauAdditionOfSamples(delayedChannel[s], earlierSample);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (otherAgentTwoPole) {
|
||||||
|
otherAgentBuffer->getNextOutput()[s] = otherAgentTwoPole->tick(otherAgentBuffer->getNextOutput()[s]);
|
||||||
|
}
|
||||||
|
|
||||||
int16_t currentSample = otherAgentBuffer->getNextOutput()[s] * attenuationCoefficient;
|
int16_t currentSample = otherAgentBuffer->getNextOutput()[s] * attenuationCoefficient;
|
||||||
|
|
||||||
plateauAdditionOfSamples(goodChannel[s], currentSample);
|
plateauAdditionOfSamples(goodChannel[s], currentSample);
|
||||||
|
@ -318,6 +386,22 @@ int main(int argc, const char* argv[]) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (Logstash::shouldSendStats()) {
|
||||||
|
// send a packet to our logstash instance
|
||||||
|
|
||||||
|
// calculate the percentage value for time elapsed for this send (of the max allowable time)
|
||||||
|
gettimeofday(&endSendTime, NULL);
|
||||||
|
|
||||||
|
float percentageOfMaxElapsed = ((float) (usecTimestamp(&endSendTime) - usecTimestamp(&beginSendTime))
|
||||||
|
/ BUFFER_SEND_INTERVAL_USECS) * 100.0f;
|
||||||
|
|
||||||
|
if (percentageOfMaxElapsed > 0) {
|
||||||
|
sumFrameTimePercentages += percentageOfMaxElapsed;
|
||||||
|
}
|
||||||
|
|
||||||
|
numStatCollections++;
|
||||||
|
}
|
||||||
|
|
||||||
long long usecToSleep = usecTimestamp(&startTime) + (++nextFrame * BUFFER_SEND_INTERVAL_USECS) - usecTimestampNow();
|
long long usecToSleep = usecTimestamp(&startTime) + (++nextFrame * BUFFER_SEND_INTERVAL_USECS) - usecTimestampNow();
|
||||||
|
|
||||||
if (usecToSleep > 0) {
|
if (usecToSleep > 0) {
|
||||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 144 KiB After Width: | Height: | Size: 71 KiB |
24
interface/resources/shaders/iris.frag
Normal file
24
interface/resources/shaders/iris.frag
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
#version 120
|
||||||
|
|
||||||
|
//
|
||||||
|
// iris.frag
|
||||||
|
// fragment shader
|
||||||
|
//
|
||||||
|
// Created by Andrzej Kapolka on 6/13/13.
|
||||||
|
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
// the iris texture
|
||||||
|
uniform sampler2D texture;
|
||||||
|
|
||||||
|
// the interpolated normal
|
||||||
|
varying vec4 normal;
|
||||||
|
|
||||||
|
void main(void) {
|
||||||
|
// compute the specular component (sans exponent) based on the normal OpenGL lighting model
|
||||||
|
float specular = max(0.0, dot(normalize(gl_LightSource[0].position + vec4(0.0, 0.0, 1.0, 0.0)), normalize(normal)));
|
||||||
|
|
||||||
|
// modulate texture by diffuse color and add specular contribution
|
||||||
|
gl_FragColor = gl_Color * texture2D(texture, gl_TexCoord[0].st) +
|
||||||
|
pow(specular, gl_FrontMaterial.shininess) * gl_FrontLightProduct[0].specular;
|
||||||
|
}
|
36
interface/resources/shaders/iris.vert
Normal file
36
interface/resources/shaders/iris.vert
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
#version 120
|
||||||
|
|
||||||
|
//
|
||||||
|
// iris.vert
|
||||||
|
// vertex shader
|
||||||
|
//
|
||||||
|
// Created by Andrzej Kapolka on 6/13/13.
|
||||||
|
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
// the location of the eye in model space
|
||||||
|
uniform vec3 eyePosition;
|
||||||
|
|
||||||
|
// the interpolated normal
|
||||||
|
varying vec4 normal;
|
||||||
|
|
||||||
|
// the ratio of the indices of refraction
|
||||||
|
const float refractionEta = 0.75;
|
||||||
|
|
||||||
|
void main(void) {
|
||||||
|
|
||||||
|
// transform and store the normal for interpolation
|
||||||
|
normal = normalize(gl_ModelViewMatrix * vec4(gl_Normal, 0.0));
|
||||||
|
|
||||||
|
// compute standard diffuse lighting per-vertex
|
||||||
|
gl_FrontColor = vec4(gl_Color.rgb * (gl_LightModel.ambient.rgb + gl_LightSource[0].ambient.rgb +
|
||||||
|
gl_LightSource[0].diffuse.rgb * max(0.0, dot(normal, gl_LightSource[0].position))), gl_Color.a);
|
||||||
|
|
||||||
|
// compute the texture coordinate based on where refracted vector hits z=0 in model space
|
||||||
|
vec4 incidence = normalize(gl_Vertex - vec4(eyePosition, 1.0));
|
||||||
|
vec4 refracted = refract(incidence, normalize(vec4(gl_Normal, 0.0)), refractionEta);
|
||||||
|
gl_TexCoord[0] = (gl_Vertex - (gl_Vertex.z / refracted.z) * refracted) + vec4(0.5, 0.5, 0.0, 0.0);
|
||||||
|
|
||||||
|
// use standard pipeline transform
|
||||||
|
gl_Position = ftransform();
|
||||||
|
}
|
|
@ -305,6 +305,8 @@ void Application::paintGL() {
|
||||||
glEnable(GL_LINE_SMOOTH);
|
glEnable(GL_LINE_SMOOTH);
|
||||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||||
|
|
||||||
|
float headCameraScale = _serialHeadSensor.active ? _headCameraPitchYawScale : 1.0f;
|
||||||
|
|
||||||
if (_myCamera.getMode() == CAMERA_MODE_MIRROR) {
|
if (_myCamera.getMode() == CAMERA_MODE_MIRROR) {
|
||||||
_myCamera.setTightness (100.0f);
|
_myCamera.setTightness (100.0f);
|
||||||
_myCamera.setTargetPosition(_myAvatar.getUprightHeadPosition());
|
_myCamera.setTargetPosition(_myAvatar.getUprightHeadPosition());
|
||||||
|
@ -320,11 +322,11 @@ void Application::paintGL() {
|
||||||
} else if (_myCamera.getMode() == CAMERA_MODE_FIRST_PERSON) {
|
} else if (_myCamera.getMode() == CAMERA_MODE_FIRST_PERSON) {
|
||||||
_myCamera.setTightness(0.0f); // In first person, camera follows head exactly without delay
|
_myCamera.setTightness(0.0f); // In first person, camera follows head exactly without delay
|
||||||
_myCamera.setTargetPosition(_myAvatar.getUprightHeadPosition());
|
_myCamera.setTargetPosition(_myAvatar.getUprightHeadPosition());
|
||||||
_myCamera.setTargetRotation(_myAvatar.getHead().getCameraOrientation(_headCameraPitchYawScale));
|
_myCamera.setTargetRotation(_myAvatar.getHead().getCameraOrientation(headCameraScale));
|
||||||
|
|
||||||
} else if (_myCamera.getMode() == CAMERA_MODE_THIRD_PERSON) {
|
} else if (_myCamera.getMode() == CAMERA_MODE_THIRD_PERSON) {
|
||||||
_myCamera.setTargetPosition(_myAvatar.getUprightHeadPosition());
|
_myCamera.setTargetPosition(_myAvatar.getUprightHeadPosition());
|
||||||
_myCamera.setTargetRotation(_myAvatar.getHead().getCameraOrientation(_headCameraPitchYawScale));
|
_myCamera.setTargetRotation(_myAvatar.getHead().getCameraOrientation(headCameraScale));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update camera position
|
// Update camera position
|
||||||
|
@ -1455,6 +1457,10 @@ void Application::update(float deltaTime) {
|
||||||
// tell my avatar the posiion and direction of the ray projected ino the world based on the mouse position
|
// tell my avatar the posiion and direction of the ray projected ino the world based on the mouse position
|
||||||
_myAvatar.setMouseRay(mouseRayOrigin, mouseRayDirection);
|
_myAvatar.setMouseRay(mouseRayOrigin, mouseRayDirection);
|
||||||
|
|
||||||
|
// Set where I am looking based on my mouse ray (so that other people can see)
|
||||||
|
glm::vec3 myLookAtFromMouse(mouseRayOrigin + mouseRayDirection);
|
||||||
|
_myAvatar.getHead().setLookAtPosition(myLookAtFromMouse);
|
||||||
|
|
||||||
// If we are dragging on a voxel, add thrust according to the amount the mouse is dragging
|
// If we are dragging on a voxel, add thrust according to the amount the mouse is dragging
|
||||||
const float VOXEL_GRAB_THRUST = 5.0f;
|
const float VOXEL_GRAB_THRUST = 5.0f;
|
||||||
if (_mousePressed && (_mouseVoxel.s != 0)) {
|
if (_mousePressed && (_mouseVoxel.s != 0)) {
|
||||||
|
@ -2123,11 +2129,15 @@ void Application::displaySide(Camera& whichCamera) {
|
||||||
avatar->init();
|
avatar->init();
|
||||||
}
|
}
|
||||||
avatar->render(false, _renderAvatarBalls->isChecked());
|
avatar->render(false, _renderAvatarBalls->isChecked());
|
||||||
|
avatar->setDisplayingLookatVectors(_renderLookatOn->isChecked());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
agentList->unlock();
|
agentList->unlock();
|
||||||
|
|
||||||
// Render my own Avatar
|
// Render my own Avatar
|
||||||
|
if (_myCamera.getMode() == CAMERA_MODE_MIRROR) {
|
||||||
|
_myAvatar.getHead().setLookAtPosition(_myCamera.getPosition());
|
||||||
|
}
|
||||||
_myAvatar.render(_lookingInMirror->isChecked(), _renderAvatarBalls->isChecked());
|
_myAvatar.render(_lookingInMirror->isChecked(), _renderAvatarBalls->isChecked());
|
||||||
_myAvatar.setDisplayingLookatVectors(_renderLookatOn->isChecked());
|
_myAvatar.setDisplayingLookatVectors(_renderLookatOn->isChecked());
|
||||||
}
|
}
|
||||||
|
|
|
@ -271,6 +271,7 @@ Avatar::~Avatar() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Avatar::init() {
|
void Avatar::init() {
|
||||||
|
_head.init();
|
||||||
_voxels.init();
|
_voxels.init();
|
||||||
_initialized = true;
|
_initialized = true;
|
||||||
}
|
}
|
||||||
|
@ -615,15 +616,6 @@ void Avatar::simulate(float deltaTime, Transmitter* transmitter) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// set head lookat position
|
|
||||||
if (!_owningAgent) {
|
|
||||||
if (_interactingOther) {
|
|
||||||
_head.setLookAtPosition(_interactingOther->calculateAverageEyePosition());
|
|
||||||
} else {
|
|
||||||
_head.setLookAtPosition(glm::vec3(0.0f, 0.0f, 0.0f)); // 0,0,0 represents NOT looking at anything
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_head.setBodyRotation (glm::vec3(_bodyPitch, _bodyYaw, _bodyRoll));
|
_head.setBodyRotation (glm::vec3(_bodyPitch, _bodyYaw, _bodyRoll));
|
||||||
_head.setPosition(_bodyBall[ BODY_BALL_HEAD_BASE ].position);
|
_head.setPosition(_bodyBall[ BODY_BALL_HEAD_BASE ].position);
|
||||||
_head.setScale (_bodyBall[ BODY_BALL_HEAD_BASE ].radius);
|
_head.setScale (_bodyBall[ BODY_BALL_HEAD_BASE ].radius);
|
||||||
|
|
|
@ -5,13 +5,16 @@
|
||||||
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
|
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
|
||||||
|
|
||||||
#include <glm/gtx/quaternion.hpp>
|
#include <glm/gtx/quaternion.hpp>
|
||||||
|
|
||||||
|
#include <QImage>
|
||||||
|
|
||||||
|
#include <AgentList.h>
|
||||||
|
|
||||||
#include "Application.h"
|
#include "Application.h"
|
||||||
#include "Avatar.h"
|
#include "Avatar.h"
|
||||||
#include "Head.h"
|
#include "Head.h"
|
||||||
#include "Util.h"
|
#include "Util.h"
|
||||||
#include <vector>
|
#include "renderer/ProgramObject.h"
|
||||||
#include <lodepng.h>
|
|
||||||
#include <AgentList.h>
|
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
|
@ -36,9 +39,9 @@ const float IRIS_RADIUS = 0.007;
|
||||||
const float IRIS_PROTRUSION = 0.0145f;
|
const float IRIS_PROTRUSION = 0.0145f;
|
||||||
const char IRIS_TEXTURE_FILENAME[] = "resources/images/iris.png";
|
const char IRIS_TEXTURE_FILENAME[] = "resources/images/iris.png";
|
||||||
|
|
||||||
unsigned int IRIS_TEXTURE_WIDTH = 768;
|
ProgramObject* Head::_irisProgram = 0;
|
||||||
unsigned int IRIS_TEXTURE_HEIGHT = 498;
|
GLuint Head::_irisTextureID;
|
||||||
vector<unsigned char> irisTexture;
|
int Head::_eyePositionLocation;
|
||||||
|
|
||||||
Head::Head(Avatar* owningAvatar) :
|
Head::Head(Avatar* owningAvatar) :
|
||||||
HeadData((AvatarData*)owningAvatar),
|
HeadData((AvatarData*)owningAvatar),
|
||||||
|
@ -57,7 +60,6 @@ Head::Head(Avatar* owningAvatar) :
|
||||||
_mouthPosition(0.0f, 0.0f, 0.0f),
|
_mouthPosition(0.0f, 0.0f, 0.0f),
|
||||||
_scale(1.0f),
|
_scale(1.0f),
|
||||||
_browAudioLift(0.0f),
|
_browAudioLift(0.0f),
|
||||||
_lookingAtSomething(false),
|
|
||||||
_gravity(0.0f, -1.0f, 0.0f),
|
_gravity(0.0f, -1.0f, 0.0f),
|
||||||
_lastLoudness(0.0f),
|
_lastLoudness(0.0f),
|
||||||
_averageLoudness(0.0f),
|
_averageLoudness(0.0f),
|
||||||
|
@ -67,13 +69,38 @@ Head::Head(Avatar* owningAvatar) :
|
||||||
_lookingInMirror(false),
|
_lookingInMirror(false),
|
||||||
_renderLookatVectors(false),
|
_renderLookatVectors(false),
|
||||||
_mohawkTriangleFan(NULL),
|
_mohawkTriangleFan(NULL),
|
||||||
_mohawkColors(NULL)
|
_mohawkColors(NULL),
|
||||||
|
_saccade(0.0f, 0.0f, 0.0f),
|
||||||
|
_saccadeTarget(0.0f, 0.0f, 0.0f)
|
||||||
{
|
{
|
||||||
if (USING_PHYSICAL_MOHAWK) {
|
if (USING_PHYSICAL_MOHAWK) {
|
||||||
resetHairPhysics();
|
resetHairPhysics();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Head::init() {
|
||||||
|
if (_irisProgram == 0) {
|
||||||
|
switchToResourcesParentIfRequired();
|
||||||
|
_irisProgram = new ProgramObject();
|
||||||
|
_irisProgram->addShaderFromSourceFile(QGLShader::Vertex, "resources/shaders/iris.vert");
|
||||||
|
_irisProgram->addShaderFromSourceFile(QGLShader::Fragment, "resources/shaders/iris.frag");
|
||||||
|
_irisProgram->link();
|
||||||
|
|
||||||
|
_irisProgram->setUniformValue("texture", 0);
|
||||||
|
_eyePositionLocation = _irisProgram->uniformLocation("eyePosition");
|
||||||
|
|
||||||
|
QImage image = QImage(IRIS_TEXTURE_FILENAME).convertToFormat(QImage::Format_ARGB32);
|
||||||
|
|
||||||
|
glGenTextures(1, &_irisTextureID);
|
||||||
|
glBindTexture(GL_TEXTURE_2D, _irisTextureID);
|
||||||
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, image.width(), image.height(), 1, GL_BGRA, GL_UNSIGNED_BYTE, image.constBits());
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
|
||||||
|
glBindTexture(GL_TEXTURE_2D, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Head::reset() {
|
void Head::reset() {
|
||||||
_yaw = _pitch = _roll = 0.0f;
|
_yaw = _pitch = _roll = 0.0f;
|
||||||
_leanForward = _leanSideways = 0.0f;
|
_leanForward = _leanSideways = 0.0f;
|
||||||
|
@ -103,31 +130,18 @@ void Head::resetHairPhysics() {
|
||||||
|
|
||||||
void Head::simulate(float deltaTime, bool isMine) {
|
void Head::simulate(float deltaTime, bool isMine) {
|
||||||
|
|
||||||
const float HEAD_MOTION_DECAY = 0.00;
|
// Update eye saccades
|
||||||
|
const float AVERAGE_MICROSACCADE_INTERVAL = 0.50f;
|
||||||
|
const float AVERAGE_SACCADE_INTERVAL = 4.0f;
|
||||||
|
const float MICROSACCADE_MAGNITUDE = 0.002f;
|
||||||
|
const float SACCADE_MAGNITUDE = 0.04;
|
||||||
|
|
||||||
/*
|
if (randFloat() < deltaTime / AVERAGE_MICROSACCADE_INTERVAL) {
|
||||||
// Decay head back to center if turned on
|
_saccadeTarget = MICROSACCADE_MAGNITUDE * randVector();
|
||||||
if (isMine && _returnHeadToCenter) {
|
} else if (randFloat() < deltaTime / AVERAGE_SACCADE_INTERVAL) {
|
||||||
|
_saccadeTarget = SACCADE_MAGNITUDE * randVector();
|
||||||
// Decay rotation back toward center
|
|
||||||
_pitch *= (1.0f - HEAD_MOTION_DECAY * _returnSpringScale * deltaTime);
|
|
||||||
_yaw *= (1.0f - HEAD_MOTION_DECAY * _returnSpringScale * deltaTime);
|
|
||||||
_roll *= (1.0f - HEAD_MOTION_DECAY * _returnSpringScale * deltaTime);
|
|
||||||
}
|
}
|
||||||
|
_saccade += (_saccadeTarget - _saccade) * 0.50f;
|
||||||
// For invensense gyro, decay only slightly when near center (until we add fusion)
|
|
||||||
if (isMine) {
|
|
||||||
const float RETURN_RANGE = 15.0;
|
|
||||||
const float RETURN_STRENGTH = 0.5;
|
|
||||||
if (fabs(_pitch) < RETURN_RANGE) { _pitch *= (1.0f - RETURN_STRENGTH * deltaTime); }
|
|
||||||
if (fabs(_yaw ) < RETURN_RANGE) { _yaw *= (1.0f - RETURN_STRENGTH * deltaTime); }
|
|
||||||
if (fabs(_roll ) < RETURN_RANGE) { _roll *= (1.0f - RETURN_STRENGTH * deltaTime); }
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
// decay lean
|
|
||||||
_leanForward *= (1.f - HEAD_MOTION_DECAY * 30 * deltaTime);
|
|
||||||
_leanSideways *= (1.f - HEAD_MOTION_DECAY * 30 * deltaTime);
|
|
||||||
|
|
||||||
// Update audio trailing average for rendering facial animations
|
// Update audio trailing average for rendering facial animations
|
||||||
const float AUDIO_AVERAGING_SECS = 0.05;
|
const float AUDIO_AVERAGING_SECS = 0.05;
|
||||||
|
@ -149,29 +163,12 @@ void Head::simulate(float deltaTime, bool isMine) {
|
||||||
_browAudioLift *= 0.7f;
|
_browAudioLift *= 0.7f;
|
||||||
|
|
||||||
// based on the nature of the lookat position, determine if the eyes can look / are looking at it.
|
// based on the nature of the lookat position, determine if the eyes can look / are looking at it.
|
||||||
determineIfLookingAtSomething();
|
|
||||||
|
|
||||||
if (USING_PHYSICAL_MOHAWK) {
|
if (USING_PHYSICAL_MOHAWK) {
|
||||||
updateHairPhysics(deltaTime);
|
updateHairPhysics(deltaTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Head::determineIfLookingAtSomething() {
|
|
||||||
|
|
||||||
if ( fabs(_lookAtPosition.x + _lookAtPosition.y + _lookAtPosition.z) == 0.0 ) { // a lookatPosition of 0,0,0 signifies NOT looking
|
|
||||||
_lookingAtSomething = false;
|
|
||||||
} else {
|
|
||||||
glm::vec3 targetLookatAxis = glm::normalize(_lookAtPosition - calculateAverageEyePosition());
|
|
||||||
float dot = glm::dot(targetLookatAxis, getFrontDirection());
|
|
||||||
if (dot < MINIMUM_EYE_ROTATION_DOT) { // too far off from center for the eyes to rotate
|
|
||||||
_lookingAtSomething = false;
|
|
||||||
} else {
|
|
||||||
_lookingAtSomething = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Head::calculateGeometry() {
|
void Head::calculateGeometry() {
|
||||||
//generate orientation directions
|
//generate orientation directions
|
||||||
glm::quat orientation = getOrientation();
|
glm::quat orientation = getOrientation();
|
||||||
|
@ -220,7 +217,7 @@ void Head::render(bool lookingInMirror, float alpha) {
|
||||||
renderMouth();
|
renderMouth();
|
||||||
renderEyeBrows();
|
renderEyeBrows();
|
||||||
|
|
||||||
if (_renderLookatVectors && _lookingAtSomething) {
|
if (_renderLookatVectors) {
|
||||||
renderLookatVectors(_leftEyePosition, _rightEyePosition, _lookAtPosition);
|
renderLookatVectors(_leftEyePosition, _rightEyePosition, _lookAtPosition);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -469,116 +466,74 @@ void Head::renderEyeBrows() {
|
||||||
|
|
||||||
void Head::renderEyeBalls() {
|
void Head::renderEyeBalls() {
|
||||||
|
|
||||||
if (::irisTexture.size() == 0) {
|
|
||||||
switchToResourcesParentIfRequired();
|
|
||||||
unsigned error = lodepng::decode(::irisTexture, IRIS_TEXTURE_WIDTH, IRIS_TEXTURE_HEIGHT, IRIS_TEXTURE_FILENAME);
|
|
||||||
if (error != 0) {
|
|
||||||
printLog("error %u: %s\n", error, lodepng_error_text(error));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// setup the texutre to be used on each iris
|
|
||||||
GLUquadric* irisQuadric = gluNewQuadric();
|
|
||||||
gluQuadricTexture(irisQuadric, GL_TRUE);
|
|
||||||
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
|
||||||
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
|
||||||
gluQuadricOrientation(irisQuadric, GLU_OUTSIDE);
|
|
||||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, IRIS_TEXTURE_WIDTH, IRIS_TEXTURE_HEIGHT,
|
|
||||||
0, GL_RGBA, GL_UNSIGNED_BYTE, &::irisTexture[0]);
|
|
||||||
|
|
||||||
// render white ball of left eyeball
|
// render white ball of left eyeball
|
||||||
glPushMatrix();
|
glPushMatrix();
|
||||||
glColor3fv(EYEBALL_COLOR);
|
glColor3fv(EYEBALL_COLOR);
|
||||||
glTranslatef(_leftEyePosition.x, _leftEyePosition.y, _leftEyePosition.z);
|
glTranslatef(_leftEyePosition.x, _leftEyePosition.y, _leftEyePosition.z);
|
||||||
gluSphere(irisQuadric, EYEBALL_RADIUS, 30, 30);
|
glutSolidSphere(EYEBALL_RADIUS, 30, 30);
|
||||||
glPopMatrix();
|
|
||||||
|
|
||||||
glm::vec3 front = getFrontDirection();
|
|
||||||
|
|
||||||
// render left iris
|
|
||||||
glPushMatrix(); {
|
|
||||||
glTranslatef(_leftEyePosition.x, _leftEyePosition.y, _leftEyePosition.z); //translate to eyeball position
|
|
||||||
|
|
||||||
glPushMatrix();
|
|
||||||
|
|
||||||
if (_lookingAtSomething) {
|
|
||||||
|
|
||||||
//rotate the eyeball to aim towards the lookat position
|
|
||||||
glm::vec3 targetLookatAxis = glm::normalize(_lookAtPosition - _leftEyePosition); // the lookat direction
|
|
||||||
glm::vec3 rotationAxis = glm::cross(targetLookatAxis, IDENTITY_UP);
|
|
||||||
float angle = 180.0f - angleBetween(targetLookatAxis, IDENTITY_UP);
|
|
||||||
glRotatef(angle, rotationAxis.x, rotationAxis.y, rotationAxis.z);
|
|
||||||
glRotatef(180.0, 0.0f, 1.0f, 0.0f); //adjust roll to correct after previous rotations
|
|
||||||
} else {
|
|
||||||
|
|
||||||
//rotate the eyeball to aim straight ahead
|
|
||||||
glm::vec3 rotationAxisToHeadFront = glm::cross(front, IDENTITY_UP);
|
|
||||||
float angleToHeadFront = 180.0f - angleBetween(front, IDENTITY_UP);
|
|
||||||
glRotatef(angleToHeadFront, rotationAxisToHeadFront.x, rotationAxisToHeadFront.y, rotationAxisToHeadFront.z);
|
|
||||||
|
|
||||||
//set the amount of roll (for correction after previous rotations)
|
|
||||||
float rollRotation = angleBetween(front, IDENTITY_FRONT);
|
|
||||||
float dot = glm::dot(front, -IDENTITY_RIGHT);
|
|
||||||
if ( dot < 0.0f ) { rollRotation = -rollRotation; }
|
|
||||||
glRotatef(rollRotation, 0.0f, 1.0f, 0.0f); //roll the iris or correct roll about the lookat vector
|
|
||||||
}
|
|
||||||
|
|
||||||
glTranslatef( 0.0f, -IRIS_PROTRUSION, 0.0f);//push the iris out a bit (otherwise - inside of eyeball!)
|
|
||||||
glScalef( 1.0f, 0.5f, 1.0f); // flatten the iris
|
|
||||||
glEnable(GL_TEXTURE_2D);
|
|
||||||
gluSphere(irisQuadric, IRIS_RADIUS, 15, 15);
|
|
||||||
glDisable(GL_TEXTURE_2D);
|
|
||||||
glPopMatrix();
|
|
||||||
}
|
|
||||||
glPopMatrix();
|
glPopMatrix();
|
||||||
|
|
||||||
//render white ball of right eyeball
|
//render white ball of right eyeball
|
||||||
glPushMatrix();
|
glPushMatrix();
|
||||||
glColor3fv(EYEBALL_COLOR);
|
glColor3fv(EYEBALL_COLOR);
|
||||||
glTranslatef(_rightEyePosition.x, _rightEyePosition.y, _rightEyePosition.z);
|
glTranslatef(_rightEyePosition.x, _rightEyePosition.y, _rightEyePosition.z);
|
||||||
gluSphere(irisQuadric, EYEBALL_RADIUS, 30, 30);
|
glutSolidSphere(EYEBALL_RADIUS, 30, 30);
|
||||||
|
glPopMatrix();
|
||||||
|
|
||||||
|
_irisProgram->bind();
|
||||||
|
glBindTexture(GL_TEXTURE_2D, _irisTextureID);
|
||||||
|
glEnable(GL_TEXTURE_2D);
|
||||||
|
|
||||||
|
glm::quat orientation = getOrientation();
|
||||||
|
glm::vec3 front = orientation * IDENTITY_FRONT;
|
||||||
|
|
||||||
|
// render left iris
|
||||||
|
glPushMatrix(); {
|
||||||
|
glTranslatef(_leftEyePosition.x, _leftEyePosition.y, _leftEyePosition.z); //translate to eyeball position
|
||||||
|
|
||||||
|
//rotate the eyeball to aim towards the lookat position
|
||||||
|
glm::vec3 targetLookatVector = _lookAtPosition + _saccade - _leftEyePosition;
|
||||||
|
glm::quat rotation = rotationBetween(front, targetLookatVector) * orientation;
|
||||||
|
glm::vec3 rotationAxis = glm::axis(rotation);
|
||||||
|
glRotatef(glm::angle(rotation), rotationAxis.x, rotationAxis.y, rotationAxis.z);
|
||||||
|
glTranslatef(0.0f, 0.0f, -IRIS_PROTRUSION);
|
||||||
|
glScalef(IRIS_RADIUS * 2.0f, IRIS_RADIUS * 2.0f, IRIS_RADIUS); // flatten the iris
|
||||||
|
|
||||||
|
// this ugliness is simply to invert the model transform and get the eye position in model space
|
||||||
|
_irisProgram->setUniform(_eyePositionLocation, (glm::inverse(rotation) *
|
||||||
|
(Application::getInstance()->getCamera()->getPosition() - _leftEyePosition) +
|
||||||
|
glm::vec3(0.0f, 0.0f, IRIS_PROTRUSION)) * glm::vec3(1.0f / (IRIS_RADIUS * 2.0f),
|
||||||
|
1.0f / (IRIS_RADIUS * 2.0f), 1.0f / IRIS_RADIUS));
|
||||||
|
|
||||||
|
glutSolidSphere(0.5f, 15, 15);
|
||||||
|
}
|
||||||
glPopMatrix();
|
glPopMatrix();
|
||||||
|
|
||||||
// render right iris
|
// render right iris
|
||||||
glPushMatrix(); {
|
glPushMatrix(); {
|
||||||
glTranslatef(_rightEyePosition.x, _rightEyePosition.y, _rightEyePosition.z); //translate to eyeball position
|
glTranslatef(_rightEyePosition.x, _rightEyePosition.y, _rightEyePosition.z); //translate to eyeball position
|
||||||
|
|
||||||
glPushMatrix();
|
|
||||||
|
|
||||||
if (_lookingAtSomething) {
|
|
||||||
|
|
||||||
//rotate the eyeball to aim towards the lookat position
|
//rotate the eyeball to aim towards the lookat position
|
||||||
glm::vec3 targetLookatAxis = glm::normalize(_lookAtPosition - _rightEyePosition);
|
glm::vec3 targetLookatVector = _lookAtPosition + _saccade - _rightEyePosition;
|
||||||
glm::vec3 rotationAxis = glm::cross(targetLookatAxis, IDENTITY_UP);
|
glm::quat rotation = rotationBetween(front, targetLookatVector) * orientation;
|
||||||
float angle = 180.0f - angleBetween(targetLookatAxis, IDENTITY_UP);
|
glm::vec3 rotationAxis = glm::axis(rotation);
|
||||||
glRotatef(angle, rotationAxis.x, rotationAxis.y, rotationAxis.z);
|
glRotatef(glm::angle(rotation), rotationAxis.x, rotationAxis.y, rotationAxis.z);
|
||||||
glRotatef(180.0f, 0.0f, 1.0f, 0.0f); //adjust roll to correct after previous rotations
|
glTranslatef(0.0f, 0.0f, -IRIS_PROTRUSION);
|
||||||
|
glScalef(IRIS_RADIUS * 2.0f, IRIS_RADIUS * 2.0f, IRIS_RADIUS); // flatten the iris
|
||||||
|
|
||||||
} else {
|
// this ugliness is simply to invert the model transform and get the eye position in model space
|
||||||
|
_irisProgram->setUniform(_eyePositionLocation, (glm::inverse(rotation) *
|
||||||
|
(Application::getInstance()->getCamera()->getPosition() - _rightEyePosition) +
|
||||||
|
glm::vec3(0.0f, 0.0f, IRIS_PROTRUSION)) * glm::vec3(1.0f / (IRIS_RADIUS * 2.0f),
|
||||||
|
1.0f / (IRIS_RADIUS * 2.0f), 1.0f / IRIS_RADIUS));
|
||||||
|
|
||||||
//rotate the eyeball to aim straight ahead
|
glutSolidSphere(0.5f, 15, 15);
|
||||||
glm::vec3 rotationAxisToHeadFront = glm::cross(front, IDENTITY_UP);
|
|
||||||
float angleToHeadFront = 180.0f - angleBetween(front, IDENTITY_UP);
|
|
||||||
glRotatef(angleToHeadFront, rotationAxisToHeadFront.x, rotationAxisToHeadFront.y, rotationAxisToHeadFront.z);
|
|
||||||
|
|
||||||
//set the amount of roll (for correction after previous rotations)
|
|
||||||
float rollRotation = angleBetween(front, IDENTITY_FRONT);
|
|
||||||
float dot = glm::dot(front, -IDENTITY_RIGHT);
|
|
||||||
if ( dot < 0.0f ) { rollRotation = -rollRotation; }
|
|
||||||
glRotatef(rollRotation, 0.0f, 1.0f, 0.0f); //roll the iris or correct roll about the lookat vector
|
|
||||||
}
|
}
|
||||||
|
glPopMatrix();
|
||||||
|
|
||||||
glTranslatef( 0.0f, -IRIS_PROTRUSION, 0.0f);//push the iris out a bit (otherwise - inside of eyeball!)
|
_irisProgram->release();
|
||||||
glScalef( 1.0f, 0.5f, 1.0f); // flatten the iris
|
glBindTexture(GL_TEXTURE_2D, 0);
|
||||||
glEnable(GL_TEXTURE_2D);
|
|
||||||
gluSphere(irisQuadric, IRIS_RADIUS, 15, 15);
|
|
||||||
glDisable(GL_TEXTURE_2D);
|
glDisable(GL_TEXTURE_2D);
|
||||||
glPopMatrix();
|
|
||||||
}
|
|
||||||
|
|
||||||
// delete the iris quadric now that we're done with it
|
|
||||||
gluDeleteQuadric(irisQuadric);
|
|
||||||
glPopMatrix();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Head::renderLookatVectors(glm::vec3 leftEyePosition, glm::vec3 rightEyePosition, glm::vec3 lookatPosition) {
|
void Head::renderLookatVectors(glm::vec3 leftEyePosition, glm::vec3 rightEyePosition, glm::vec3 lookatPosition) {
|
||||||
|
|
|
@ -26,11 +26,13 @@ enum eyeContactTargets
|
||||||
const int NUM_HAIR_TUFTS = 4;
|
const int NUM_HAIR_TUFTS = 4;
|
||||||
|
|
||||||
class Avatar;
|
class Avatar;
|
||||||
|
class ProgramObject;
|
||||||
|
|
||||||
class Head : public HeadData {
|
class Head : public HeadData {
|
||||||
public:
|
public:
|
||||||
Head(Avatar* owningAvatar);
|
Head(Avatar* owningAvatar);
|
||||||
|
|
||||||
|
void init();
|
||||||
void reset();
|
void reset();
|
||||||
void simulate(float deltaTime, bool isMine);
|
void simulate(float deltaTime, bool isMine);
|
||||||
void render(bool lookingInMirror, float alpha);
|
void render(bool lookingInMirror, float alpha);
|
||||||
|
@ -91,7 +93,6 @@ private:
|
||||||
glm::vec3 _mouthPosition;
|
glm::vec3 _mouthPosition;
|
||||||
float _scale;
|
float _scale;
|
||||||
float _browAudioLift;
|
float _browAudioLift;
|
||||||
bool _lookingAtSomething;
|
|
||||||
glm::vec3 _gravity;
|
glm::vec3 _gravity;
|
||||||
float _lastLoudness;
|
float _lastLoudness;
|
||||||
float _averageLoudness;
|
float _averageLoudness;
|
||||||
|
@ -103,6 +104,12 @@ private:
|
||||||
HairTuft _hairTuft[NUM_HAIR_TUFTS];
|
HairTuft _hairTuft[NUM_HAIR_TUFTS];
|
||||||
glm::vec3* _mohawkTriangleFan;
|
glm::vec3* _mohawkTriangleFan;
|
||||||
glm::vec3* _mohawkColors;
|
glm::vec3* _mohawkColors;
|
||||||
|
glm::vec3 _saccade;
|
||||||
|
glm::vec3 _saccadeTarget;
|
||||||
|
|
||||||
|
static ProgramObject* _irisProgram;
|
||||||
|
static GLuint _irisTextureID;
|
||||||
|
static int _eyePositionLocation;
|
||||||
|
|
||||||
// private methods
|
// private methods
|
||||||
void createMohawk();
|
void createMohawk();
|
||||||
|
@ -113,7 +120,6 @@ private:
|
||||||
void renderMouth();
|
void renderMouth();
|
||||||
void renderLookatVectors(glm::vec3 leftEyePosition, glm::vec3 rightEyePosition, glm::vec3 lookatPosition);
|
void renderLookatVectors(glm::vec3 leftEyePosition, glm::vec3 rightEyePosition, glm::vec3 lookatPosition);
|
||||||
void calculateGeometry();
|
void calculateGeometry();
|
||||||
void determineIfLookingAtSomething();
|
|
||||||
void resetHairPhysics();
|
void resetHairPhysics();
|
||||||
void updateHairPhysics(float deltaTime);
|
void updateHairPhysics(float deltaTime);
|
||||||
};
|
};
|
||||||
|
|
|
@ -85,7 +85,7 @@ glm::quat rotationBetween(const glm::vec3& v1, const glm::vec3& v2) {
|
||||||
if (isnan(angle) || angle < EPSILON) {
|
if (isnan(angle) || angle < EPSILON) {
|
||||||
return glm::quat();
|
return glm::quat();
|
||||||
}
|
}
|
||||||
glm::vec3 axis = glm::cross(v1, v2);
|
glm::vec3 axis;
|
||||||
if (angle > 179.99f) { // 180 degree rotation; must use another axis
|
if (angle > 179.99f) { // 180 degree rotation; must use another axis
|
||||||
axis = glm::cross(v1, glm::vec3(1.0f, 0.0f, 0.0f));
|
axis = glm::cross(v1, glm::vec3(1.0f, 0.0f, 0.0f));
|
||||||
float axisLength = glm::length(axis);
|
float axisLength = glm::length(axis);
|
||||||
|
@ -267,6 +267,11 @@ double diffclock(timeval *clock1,timeval *clock2)
|
||||||
return diffms;
|
return diffms;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Return a random vector of average length 1
|
||||||
|
const glm::vec3 randVector() {
|
||||||
|
return glm::vec3(randFloat() - 0.5f, randFloat() - 0.5f, randFloat() - 0.5f) * 2.f;
|
||||||
|
}
|
||||||
|
|
||||||
static TextRenderer* textRenderer(int mono) {
|
static TextRenderer* textRenderer(int mono) {
|
||||||
static TextRenderer* monoRenderer = new TextRenderer(MONO_FONT_FAMILY);
|
static TextRenderer* monoRenderer = new TextRenderer(MONO_FONT_FAMILY);
|
||||||
static TextRenderer* proportionalRenderer = new TextRenderer(SANS_FONT_FAMILY, -1, -1, false, TextRenderer::SHADOW_EFFECT);
|
static TextRenderer* proportionalRenderer = new TextRenderer(SANS_FONT_FAMILY, -1, -1, false, TextRenderer::SHADOW_EFFECT);
|
||||||
|
|
|
@ -32,6 +32,8 @@ float azimuth_to(glm::vec3 head_pos, glm::vec3 source_pos);
|
||||||
float angle_to(glm::vec3 head_pos, glm::vec3 source_pos, float render_yaw, float head_yaw);
|
float angle_to(glm::vec3 head_pos, glm::vec3 source_pos, float render_yaw, float head_yaw);
|
||||||
|
|
||||||
float randFloat();
|
float randFloat();
|
||||||
|
const glm::vec3 randVector();
|
||||||
|
|
||||||
void render_world_box();
|
void render_world_box();
|
||||||
int widthText(float scale, int mono, char const* string);
|
int widthText(float scale, int mono, char const* string);
|
||||||
float widthChar(float scale, int mono, char ch);
|
float widthChar(float scale, int mono, char ch);
|
||||||
|
|
|
@ -16,7 +16,3 @@ include_glm(${TARGET_NAME} ${ROOT_DIR})
|
||||||
|
|
||||||
include(${MACRO_DIR}/LinkHifiLibrary.cmake)
|
include(${MACRO_DIR}/LinkHifiLibrary.cmake)
|
||||||
link_hifi_library(shared ${TARGET_NAME} ${ROOT_DIR})
|
link_hifi_library(shared ${TARGET_NAME} ${ROOT_DIR})
|
||||||
|
|
||||||
# link the threads library
|
|
||||||
find_package(Threads REQUIRED)
|
|
||||||
target_link_libraries(${TARGET_NAME} ${CMAKE_THREAD_LIBS_INIT})
|
|
45
libraries/shared/src/Logstash.cpp
Normal file
45
libraries/shared/src/Logstash.cpp
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
//
|
||||||
|
// Logstash.cpp
|
||||||
|
// hifi
|
||||||
|
//
|
||||||
|
// Created by Stephen Birarda on 6/11/13.
|
||||||
|
// Copyright (c) 2013 HighFidelity, Inc. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include <cstring>
|
||||||
|
#include <cstdio>
|
||||||
|
#include <netdb.h>
|
||||||
|
|
||||||
|
#include "SharedUtil.h"
|
||||||
|
|
||||||
|
#include "Logstash.h"
|
||||||
|
|
||||||
|
sockaddr_in Logstash::logstashSocket = {};
|
||||||
|
|
||||||
|
sockaddr* Logstash::socket() {
|
||||||
|
|
||||||
|
if (logstashSocket.sin_addr.s_addr == 0) {
|
||||||
|
// we need to construct the socket object
|
||||||
|
|
||||||
|
// assume IPv4
|
||||||
|
logstashSocket.sin_family = AF_INET;
|
||||||
|
|
||||||
|
// use the constant port
|
||||||
|
logstashSocket.sin_port = htons(LOGSTASH_UDP_PORT);
|
||||||
|
|
||||||
|
// lookup the IP address for the constant hostname
|
||||||
|
struct hostent* logstashHostInfo;
|
||||||
|
if ((logstashHostInfo = gethostbyname(LOGSTASH_HOSTNAME))) {
|
||||||
|
memcpy(&logstashSocket.sin_addr, logstashHostInfo->h_addr_list[0], logstashHostInfo->h_length);
|
||||||
|
} else {
|
||||||
|
printf("Failed to lookup logstash IP - will try again on next log attempt.\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (sockaddr*) &logstashSocket;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Logstash::shouldSendStats() {
|
||||||
|
static bool shouldSendStats = isInEnvironment("production");
|
||||||
|
return shouldSendStats;
|
||||||
|
}
|
25
libraries/shared/src/Logstash.h
Normal file
25
libraries/shared/src/Logstash.h
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
//
|
||||||
|
// Logstash.h
|
||||||
|
// hifi
|
||||||
|
//
|
||||||
|
// Created by Stephen Birarda on 6/11/13.
|
||||||
|
// Copyright (c) 2013 HighFidelity, Inc. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef __hifi__Logstash__
|
||||||
|
#define __hifi__Logstash__
|
||||||
|
|
||||||
|
#include <netinet/in.h>
|
||||||
|
|
||||||
|
const int LOGSTASH_UDP_PORT = 9500;
|
||||||
|
const char LOGSTASH_HOSTNAME[] = "graphite.highfidelity.io";
|
||||||
|
|
||||||
|
class Logstash {
|
||||||
|
public:
|
||||||
|
static sockaddr* socket();
|
||||||
|
static bool shouldSendStats();
|
||||||
|
private:
|
||||||
|
static sockaddr_in logstashSocket;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* defined(__hifi__Logstash__) */
|
|
@ -110,6 +110,15 @@ void setSemiNibbleAt(unsigned char& byte, int bitIndex, int value) {
|
||||||
byte += ((value & 3) << (6 - bitIndex)); // semi-nibbles store 00, 01, 10, or 11
|
byte += ((value & 3) << (6 - bitIndex)); // semi-nibbles store 00, 01, 10, or 11
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool isInEnvironment(const char* environment) {
|
||||||
|
char* environmentString = getenv("HIFI_ENVIRONMENT");
|
||||||
|
|
||||||
|
if (environmentString && strcmp(environmentString, environment) == 0) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void switchToResourcesParentIfRequired() {
|
void switchToResourcesParentIfRequired() {
|
||||||
#ifdef __APPLE__
|
#ifdef __APPLE__
|
||||||
|
|
|
@ -57,6 +57,7 @@ void setAtBit(unsigned char& byte, int bitIndex);
|
||||||
int getSemiNibbleAt(unsigned char& byte, int bitIndex);
|
int getSemiNibbleAt(unsigned char& byte, int bitIndex);
|
||||||
void setSemiNibbleAt(unsigned char& byte, int bitIndex, int value);
|
void setSemiNibbleAt(unsigned char& byte, int bitIndex, int value);
|
||||||
|
|
||||||
|
bool isInEnvironment(const char* environment);
|
||||||
|
|
||||||
void switchToResourcesParentIfRequired();
|
void switchToResourcesParentIfRequired();
|
||||||
|
|
||||||
|
|
|
@ -1034,22 +1034,21 @@ int VoxelTree::searchForColoredNodesRecursion(int maxSearchLevel, int& currentSe
|
||||||
return maxChildLevel;
|
return maxChildLevel;
|
||||||
}
|
}
|
||||||
|
|
||||||
int VoxelTree::encodeTreeBitstream(int maxEncodeLevel, VoxelNode* node, unsigned char* outputBuffer, int availableBytes,
|
int VoxelTree::encodeTreeBitstream(VoxelNode* node, unsigned char* outputBuffer, int availableBytes, VoxelNodeBag& bag,
|
||||||
VoxelNodeBag& bag, const ViewFrustum* viewFrustum, bool includeColor, bool includeExistsBits,
|
EncodeBitstreamParams& params) const {
|
||||||
int chopLevels, bool deltaViewFrustum, const ViewFrustum* lastViewFrustum) const {
|
|
||||||
|
|
||||||
// How many bytes have we written so far at this level;
|
// How many bytes have we written so far at this level;
|
||||||
int bytesWritten = 0;
|
int bytesWritten = 0;
|
||||||
|
|
||||||
// If we're at a node that is out of view, then we can return, because no nodes below us will be in view!
|
// If we're at a node that is out of view, then we can return, because no nodes below us will be in view!
|
||||||
if (viewFrustum && !node->isInView(*viewFrustum)) {
|
if (params.viewFrustum && !node->isInView(*params.viewFrustum)) {
|
||||||
return bytesWritten;
|
return bytesWritten;
|
||||||
}
|
}
|
||||||
|
|
||||||
// write the octal code
|
// write the octal code
|
||||||
int codeLength;
|
int codeLength;
|
||||||
if (chopLevels) {
|
if (params.chopLevels) {
|
||||||
unsigned char* newCode = chopOctalCode(node->getOctalCode(), chopLevels);
|
unsigned char* newCode = chopOctalCode(node->getOctalCode(), params.chopLevels);
|
||||||
if (newCode) {
|
if (newCode) {
|
||||||
codeLength = bytesRequiredForCodeLength(numberOfThreeBitSectionsInCode(newCode));
|
codeLength = bytesRequiredForCodeLength(numberOfThreeBitSectionsInCode(newCode));
|
||||||
memcpy(outputBuffer, newCode, codeLength);
|
memcpy(outputBuffer, newCode, codeLength);
|
||||||
|
@ -1068,16 +1067,14 @@ int VoxelTree::encodeTreeBitstream(int maxEncodeLevel, VoxelNode* node, unsigned
|
||||||
availableBytes -= codeLength; // keep track or remaining space
|
availableBytes -= codeLength; // keep track or remaining space
|
||||||
|
|
||||||
int currentEncodeLevel = 0;
|
int currentEncodeLevel = 0;
|
||||||
int childBytesWritten = encodeTreeBitstreamRecursion(maxEncodeLevel, currentEncodeLevel, node, outputBuffer, availableBytes,
|
int childBytesWritten = encodeTreeBitstreamRecursion(node, outputBuffer, availableBytes, bag, params, currentEncodeLevel);
|
||||||
bag, viewFrustum, includeColor, includeExistsBits, chopLevels,
|
|
||||||
deltaViewFrustum, lastViewFrustum);
|
|
||||||
|
|
||||||
// if childBytesWritten == 1 then something went wrong... that's not possible
|
// if childBytesWritten == 1 then something went wrong... that's not possible
|
||||||
assert(childBytesWritten != 1);
|
assert(childBytesWritten != 1);
|
||||||
|
|
||||||
// if includeColor and childBytesWritten == 2, then it can only mean that the lower level trees don't exist or for some reason
|
// if includeColor and childBytesWritten == 2, then it can only mean that the lower level trees don't exist or for some reason
|
||||||
// couldn't be written... so reset them here... This isn't true for the non-color included case
|
// couldn't be written... so reset them here... This isn't true for the non-color included case
|
||||||
if (includeColor && childBytesWritten == 2) {
|
if (params.includeColor && childBytesWritten == 2) {
|
||||||
childBytesWritten = 0;
|
childBytesWritten = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1091,10 +1088,8 @@ int VoxelTree::encodeTreeBitstream(int maxEncodeLevel, VoxelNode* node, unsigned
|
||||||
return bytesWritten;
|
return bytesWritten;
|
||||||
}
|
}
|
||||||
|
|
||||||
int VoxelTree::encodeTreeBitstreamRecursion(int maxEncodeLevel, int& currentEncodeLevel, VoxelNode* node,
|
int VoxelTree::encodeTreeBitstreamRecursion(VoxelNode* node, unsigned char* outputBuffer, int availableBytes, VoxelNodeBag& bag,
|
||||||
unsigned char* outputBuffer, int availableBytes, VoxelNodeBag& bag,
|
EncodeBitstreamParams& params, int& currentEncodeLevel) const {
|
||||||
const ViewFrustum* viewFrustum, bool includeColor, bool includeExistsBits,
|
|
||||||
int chopLevels, bool deltaViewFrustum, const ViewFrustum* lastViewFrustum) const {
|
|
||||||
|
|
||||||
// How many bytes have we written so far at this level;
|
// How many bytes have we written so far at this level;
|
||||||
int bytesAtThisLevel = 0;
|
int bytesAtThisLevel = 0;
|
||||||
|
@ -1103,13 +1098,13 @@ int VoxelTree::encodeTreeBitstreamRecursion(int maxEncodeLevel, int& currentEnco
|
||||||
currentEncodeLevel++;
|
currentEncodeLevel++;
|
||||||
|
|
||||||
// If we've reached our max Search Level, then stop searching.
|
// If we've reached our max Search Level, then stop searching.
|
||||||
if (currentEncodeLevel >= maxEncodeLevel) {
|
if (currentEncodeLevel >= params.maxEncodeLevel) {
|
||||||
return bytesAtThisLevel;
|
return bytesAtThisLevel;
|
||||||
}
|
}
|
||||||
|
|
||||||
// caller can pass NULL as viewFrustum if they want everything
|
// caller can pass NULL as viewFrustum if they want everything
|
||||||
if (viewFrustum) {
|
if (params.viewFrustum) {
|
||||||
float distance = node->distanceToCamera(*viewFrustum);
|
float distance = node->distanceToCamera(*params.viewFrustum);
|
||||||
float boundaryDistance = boundaryDistanceForRenderLevel(*node->getOctalCode() + 1);
|
float boundaryDistance = boundaryDistanceForRenderLevel(*node->getOctalCode() + 1);
|
||||||
|
|
||||||
// If we're too far away for our render level, then just return
|
// If we're too far away for our render level, then just return
|
||||||
|
@ -1120,7 +1115,7 @@ int VoxelTree::encodeTreeBitstreamRecursion(int maxEncodeLevel, int& currentEnco
|
||||||
// If we're at a node that is out of view, then we can return, because no nodes below us will be in view!
|
// If we're at a node that is out of view, then we can return, because no nodes below us will be in view!
|
||||||
// although technically, we really shouldn't ever be here, because our callers shouldn't be calling us if
|
// although technically, we really shouldn't ever be here, because our callers shouldn't be calling us if
|
||||||
// we're out of view
|
// we're out of view
|
||||||
if (!node->isInView(*viewFrustum)) {
|
if (!node->isInView(*params.viewFrustum)) {
|
||||||
return bytesAtThisLevel;
|
return bytesAtThisLevel;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1153,16 +1148,16 @@ int VoxelTree::encodeTreeBitstreamRecursion(int maxEncodeLevel, int& currentEnco
|
||||||
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 (includeExistsBits && childNode) {
|
if (params.includeExistsBits && childNode) {
|
||||||
childrenExistInTreeBits += (1 << (7 - i));
|
childrenExistInTreeBits += (1 << (7 - i));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool childIsInView = (childNode && (!viewFrustum || childNode->isInView(*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 = viewFrustum ? childNode->distanceToCamera(*viewFrustum) : 0;
|
float distance = params.viewFrustum ? childNode->distanceToCamera(*params.viewFrustum) : 0;
|
||||||
float boundaryDistance = viewFrustum ? boundaryDistanceForRenderLevel(*childNode->getOctalCode() + 1) : 1;
|
float boundaryDistance = params.viewFrustum ? boundaryDistanceForRenderLevel(*childNode->getOctalCode() + 1) : 1;
|
||||||
|
|
||||||
if (distance < boundaryDistance) {
|
if (distance < boundaryDistance) {
|
||||||
inViewCount++;
|
inViewCount++;
|
||||||
|
@ -1175,8 +1170,8 @@ int VoxelTree::encodeTreeBitstreamRecursion(int maxEncodeLevel, int& currentEnco
|
||||||
inViewNotLeafCount++;
|
inViewNotLeafCount++;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool childWasInView = (childNode && deltaViewFrustum &&
|
bool childWasInView = (childNode && params.deltaViewFrustum &&
|
||||||
(lastViewFrustum && ViewFrustum::INSIDE == childNode->inFrustum(*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) {
|
||||||
|
@ -1191,7 +1186,7 @@ int VoxelTree::encodeTreeBitstreamRecursion(int maxEncodeLevel, int& currentEnco
|
||||||
bytesAtThisLevel += sizeof(childrenColoredBits); // keep track of byte count
|
bytesAtThisLevel += sizeof(childrenColoredBits); // keep track of byte count
|
||||||
|
|
||||||
// write the color data...
|
// write the color data...
|
||||||
if (includeColor) {
|
if (params.includeColor) {
|
||||||
for (int i = 0; i < NUMBER_OF_CHILDREN; i++) {
|
for (int i = 0; i < NUMBER_OF_CHILDREN; i++) {
|
||||||
if (oneAtBit(childrenColoredBits, i)) {
|
if (oneAtBit(childrenColoredBits, i)) {
|
||||||
memcpy(writeToThisLevelBuffer, &node->getChildAtIndex(i)->getColor(), BYTES_PER_COLOR);
|
memcpy(writeToThisLevelBuffer, &node->getChildAtIndex(i)->getColor(), BYTES_PER_COLOR);
|
||||||
|
@ -1203,7 +1198,7 @@ int VoxelTree::encodeTreeBitstreamRecursion(int maxEncodeLevel, int& currentEnco
|
||||||
|
|
||||||
// if the caller wants to include childExistsBits, then include them even if not in view, put them before the
|
// if the caller wants to include childExistsBits, then include them even if not in view, put them before the
|
||||||
// childrenExistInPacketBits, so that the lower code can properly repair the packet exists bits
|
// childrenExistInPacketBits, so that the lower code can properly repair the packet exists bits
|
||||||
if (includeExistsBits) {
|
if (params.includeExistsBits) {
|
||||||
*writeToThisLevelBuffer = childrenExistInTreeBits;
|
*writeToThisLevelBuffer = childrenExistInTreeBits;
|
||||||
writeToThisLevelBuffer += sizeof(childrenExistInTreeBits); // move the pointer
|
writeToThisLevelBuffer += sizeof(childrenExistInTreeBits); // move the pointer
|
||||||
bytesAtThisLevel += sizeof(childrenExistInTreeBits); // keep track of byte count
|
bytesAtThisLevel += sizeof(childrenExistInTreeBits); // keep track of byte count
|
||||||
|
@ -1247,10 +1242,8 @@ int VoxelTree::encodeTreeBitstreamRecursion(int maxEncodeLevel, int& currentEnco
|
||||||
VoxelNode* childNode = node->getChildAtIndex(i);
|
VoxelNode* childNode = node->getChildAtIndex(i);
|
||||||
|
|
||||||
int thisLevel = currentEncodeLevel;
|
int thisLevel = currentEncodeLevel;
|
||||||
int childTreeBytesOut = encodeTreeBitstreamRecursion(maxEncodeLevel, thisLevel, childNode,
|
int childTreeBytesOut = encodeTreeBitstreamRecursion(childNode, outputBuffer, availableBytes, bag,
|
||||||
outputBuffer, availableBytes, bag,
|
params, thisLevel);
|
||||||
viewFrustum, includeColor, includeExistsBits, chopLevels,
|
|
||||||
deltaViewFrustum, lastViewFrustum);
|
|
||||||
|
|
||||||
// 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.
|
||||||
|
@ -1268,7 +1261,7 @@ int VoxelTree::encodeTreeBitstreamRecursion(int maxEncodeLevel, int& currentEnco
|
||||||
// so, if the child returns 2 bytes out, we can actually consider that an empty tree also!!
|
// so, if the child returns 2 bytes out, we can actually consider that an empty tree also!!
|
||||||
//
|
//
|
||||||
// we can make this act like no bytes out, by just resetting the bytes out in this case
|
// we can make this act like no bytes out, by just resetting the bytes out in this case
|
||||||
if (includeColor && childTreeBytesOut == 2) {
|
if (params.includeColor && childTreeBytesOut == 2) {
|
||||||
childTreeBytesOut = 0; // this is the degenerate case of a tree with no colors and no child trees
|
childTreeBytesOut = 0; // this is the degenerate case of a tree with no colors and no child trees
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1333,8 +1326,9 @@ void VoxelTree::writeToSVOFile(const char* fileName, VoxelNode* node) const {
|
||||||
|
|
||||||
while (!nodeBag.isEmpty()) {
|
while (!nodeBag.isEmpty()) {
|
||||||
VoxelNode* subTree = nodeBag.extract();
|
VoxelNode* subTree = nodeBag.extract();
|
||||||
bytesWritten = encodeTreeBitstream(INT_MAX, subTree, &outputBuffer[0],
|
|
||||||
MAX_VOXEL_PACKET_SIZE - 1, nodeBag, IGNORE_VIEW_FRUSTUM, WANT_COLOR, NO_EXISTS_BITS);
|
EncodeBitstreamParams params(INT_MAX, IGNORE_VIEW_FRUSTUM, WANT_COLOR, NO_EXISTS_BITS);
|
||||||
|
bytesWritten = encodeTreeBitstream(subTree, &outputBuffer[0], MAX_VOXEL_PACKET_SIZE - 1, nodeBag, params);
|
||||||
|
|
||||||
file.write((const char*)&outputBuffer[0], bytesWritten);
|
file.write((const char*)&outputBuffer[0], bytesWritten);
|
||||||
}
|
}
|
||||||
|
@ -1368,8 +1362,8 @@ void VoxelTree::copySubTreeIntoNewTree(VoxelNode* startNode, VoxelTree* destinat
|
||||||
VoxelNode* subTree = nodeBag.extract();
|
VoxelNode* subTree = nodeBag.extract();
|
||||||
|
|
||||||
// ask our tree to write a bitsteam
|
// ask our tree to write a bitsteam
|
||||||
bytesWritten = encodeTreeBitstream(INT_MAX, subTree, &outputBuffer[0],
|
EncodeBitstreamParams params(INT_MAX, IGNORE_VIEW_FRUSTUM, WANT_COLOR, NO_EXISTS_BITS, chopLevels);
|
||||||
MAX_VOXEL_PACKET_SIZE - 1, nodeBag, IGNORE_VIEW_FRUSTUM, WANT_COLOR, NO_EXISTS_BITS, chopLevels);
|
bytesWritten = encodeTreeBitstream(subTree, &outputBuffer[0], MAX_VOXEL_PACKET_SIZE - 1, nodeBag, params);
|
||||||
|
|
||||||
// ask destination tree to read the bitstream
|
// ask destination tree to read the bitstream
|
||||||
destinationTree->readBitstreamToTree(&outputBuffer[0], bytesWritten, WANT_COLOR, NO_EXISTS_BITS);
|
destinationTree->readBitstreamToTree(&outputBuffer[0], bytesWritten, WANT_COLOR, NO_EXISTS_BITS);
|
||||||
|
@ -1388,8 +1382,8 @@ void VoxelTree::copyFromTreeIntoSubTree(VoxelTree* sourceTree, VoxelNode* destin
|
||||||
VoxelNode* subTree = nodeBag.extract();
|
VoxelNode* subTree = nodeBag.extract();
|
||||||
|
|
||||||
// ask our tree to write a bitsteam
|
// ask our tree to write a bitsteam
|
||||||
bytesWritten = sourceTree->encodeTreeBitstream(INT_MAX, subTree, &outputBuffer[0],
|
EncodeBitstreamParams params(INT_MAX, IGNORE_VIEW_FRUSTUM, WANT_COLOR, NO_EXISTS_BITS);
|
||||||
MAX_VOXEL_PACKET_SIZE - 1, nodeBag, IGNORE_VIEW_FRUSTUM, WANT_COLOR, NO_EXISTS_BITS);
|
bytesWritten = sourceTree->encodeTreeBitstream(subTree, &outputBuffer[0], MAX_VOXEL_PACKET_SIZE - 1, nodeBag, params);
|
||||||
|
|
||||||
// ask destination tree to read the bitstream
|
// ask destination tree to read the bitstream
|
||||||
readBitstreamToTree(&outputBuffer[0], bytesWritten, WANT_COLOR, NO_EXISTS_BITS, destinationNode);
|
readBitstreamToTree(&outputBuffer[0], bytesWritten, WANT_COLOR, NO_EXISTS_BITS, destinationNode);
|
||||||
|
|
|
@ -28,6 +28,35 @@ typedef enum {GRADIENT, RANDOM, NATURAL} creationMode;
|
||||||
#define COLLAPSE_EMPTY_TREE true
|
#define COLLAPSE_EMPTY_TREE true
|
||||||
#define DONT_COLLAPSE false
|
#define DONT_COLLAPSE false
|
||||||
|
|
||||||
|
class EncodeBitstreamParams {
|
||||||
|
public:
|
||||||
|
int maxEncodeLevel;
|
||||||
|
const ViewFrustum* viewFrustum;
|
||||||
|
bool includeColor;
|
||||||
|
bool includeExistsBits;
|
||||||
|
int chopLevels;
|
||||||
|
bool deltaViewFrustum;
|
||||||
|
const ViewFrustum* lastViewFrustum;
|
||||||
|
|
||||||
|
EncodeBitstreamParams(
|
||||||
|
int maxEncodeLevel = INT_MAX,
|
||||||
|
const ViewFrustum* viewFrustum = IGNORE_VIEW_FRUSTUM,
|
||||||
|
bool includeColor = WANT_COLOR,
|
||||||
|
bool includeExistsBits = WANT_EXISTS_BITS,
|
||||||
|
int chopLevels = 0,
|
||||||
|
bool deltaViewFrustum = false,
|
||||||
|
const ViewFrustum* lastViewFrustum = IGNORE_VIEW_FRUSTUM) :
|
||||||
|
|
||||||
|
maxEncodeLevel (maxEncodeLevel),
|
||||||
|
viewFrustum (viewFrustum),
|
||||||
|
includeColor (includeColor),
|
||||||
|
includeExistsBits (includeExistsBits),
|
||||||
|
chopLevels (chopLevels),
|
||||||
|
deltaViewFrustum (deltaViewFrustum),
|
||||||
|
lastViewFrustum (lastViewFrustum)
|
||||||
|
{}
|
||||||
|
};
|
||||||
|
|
||||||
class VoxelTree {
|
class VoxelTree {
|
||||||
public:
|
public:
|
||||||
// when a voxel is created in the tree (object new'd)
|
// when a voxel is created in the tree (object new'd)
|
||||||
|
@ -70,10 +99,8 @@ public:
|
||||||
void recurseTreeWithOperationDistanceSorted(RecurseVoxelTreeOperation operation,
|
void recurseTreeWithOperationDistanceSorted(RecurseVoxelTreeOperation operation,
|
||||||
const glm::vec3& point, void* extraData=NULL);
|
const glm::vec3& point, void* extraData=NULL);
|
||||||
|
|
||||||
int encodeTreeBitstream(int maxEncodeLevel, VoxelNode* node, unsigned char* outputBuffer, int availableBytes,
|
int encodeTreeBitstream(VoxelNode* node, unsigned char* outputBuffer, int availableBytes, VoxelNodeBag& bag,
|
||||||
VoxelNodeBag& bag, const ViewFrustum* viewFrustum,
|
EncodeBitstreamParams& params) const;
|
||||||
bool includeColor = WANT_COLOR, bool includeExistsBits = WANT_EXISTS_BITS, int chopLevels = 0,
|
|
||||||
bool deltaViewFrustum = false, const ViewFrustum* lastViewFrustum = NULL) const;
|
|
||||||
|
|
||||||
int searchForColoredNodes(int maxSearchLevel, VoxelNode* node, const ViewFrustum& viewFrustum, VoxelNodeBag& bag,
|
int searchForColoredNodes(int maxSearchLevel, VoxelNode* node, const ViewFrustum& viewFrustum, VoxelNodeBag& bag,
|
||||||
bool deltaViewFrustum = false, const ViewFrustum* lastViewFrustum = NULL);
|
bool deltaViewFrustum = false, const ViewFrustum* lastViewFrustum = NULL);
|
||||||
|
@ -111,10 +138,8 @@ private:
|
||||||
void deleteVoxelCodeFromTreeRecursion(VoxelNode* node, void* extraData);
|
void deleteVoxelCodeFromTreeRecursion(VoxelNode* node, void* extraData);
|
||||||
void readCodeColorBufferToTreeRecursion(VoxelNode* node, void* extraData);
|
void readCodeColorBufferToTreeRecursion(VoxelNode* node, void* extraData);
|
||||||
|
|
||||||
int encodeTreeBitstreamRecursion(int maxEncodeLevel, int& currentEncodeLevel,
|
int encodeTreeBitstreamRecursion(VoxelNode* node, unsigned char* outputBuffer, int availableBytes, VoxelNodeBag& bag,
|
||||||
VoxelNode* node, unsigned char* outputBuffer, int availableBytes, VoxelNodeBag& bag,
|
EncodeBitstreamParams& params, int& currentEncodeLevel) const;
|
||||||
const ViewFrustum* viewFrustum, bool includeColor, bool includeExistsBits,
|
|
||||||
int chopLevels, bool deltaViewFrustum, const ViewFrustum* lastViewFrustum) const;
|
|
||||||
|
|
||||||
int searchForColoredNodesRecursion(int maxSearchLevel, int& currentSearchLevel,
|
int searchForColoredNodesRecursion(int maxSearchLevel, int& currentSearchLevel,
|
||||||
VoxelNode* node, const ViewFrustum& viewFrustum, VoxelNodeBag& bag,
|
VoxelNode* node, const ViewFrustum& viewFrustum, VoxelNodeBag& bag,
|
||||||
|
|
|
@ -167,11 +167,13 @@ void resInVoxelDistributor(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();
|
||||||
bytesWritten = serverTree.encodeTreeBitstream(agentData->getMaxSearchLevel(), subTree,
|
|
||||||
&tempOutputBuffer[0], MAX_VOXEL_PACKET_SIZE - 1,
|
EncodeBitstreamParams params(agentData->getMaxSearchLevel(), &viewFrustum,
|
||||||
agentData->nodeBag, &viewFrustum,
|
|
||||||
agentData->getWantColor(), WANT_EXISTS_BITS);
|
agentData->getWantColor(), WANT_EXISTS_BITS);
|
||||||
|
|
||||||
|
bytesWritten = serverTree.encodeTreeBitstream(subTree, &tempOutputBuffer[0], MAX_VOXEL_PACKET_SIZE - 1,
|
||||||
|
agentData->nodeBag, params);
|
||||||
|
|
||||||
if (agentData->getAvailable() >= bytesWritten) {
|
if (agentData->getAvailable() >= bytesWritten) {
|
||||||
agentData->writeToPacket(&tempOutputBuffer[0], bytesWritten);
|
agentData->writeToPacket(&tempOutputBuffer[0], bytesWritten);
|
||||||
} else {
|
} else {
|
||||||
|
@ -310,11 +312,12 @@ 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();
|
||||||
bytesWritten = serverTree.encodeTreeBitstream(INT_MAX, subTree,
|
|
||||||
&tempOutputBuffer[0], MAX_VOXEL_PACKET_SIZE - 1,
|
EncodeBitstreamParams params(INT_MAX, &agentData->getCurrentViewFrustum(), agentData->getWantColor(),
|
||||||
agentData->nodeBag, &agentData->getCurrentViewFrustum(),
|
WANT_EXISTS_BITS, wantDelta, lastViewFrustum);
|
||||||
agentData->getWantColor(), WANT_EXISTS_BITS,
|
|
||||||
wantDelta, lastViewFrustum);
|
bytesWritten = serverTree.encodeTreeBitstream(subTree, &tempOutputBuffer[0], MAX_VOXEL_PACKET_SIZE - 1,
|
||||||
|
agentData->nodeBag, params);
|
||||||
|
|
||||||
if (agentData->getAvailable() >= bytesWritten) {
|
if (agentData->getAvailable() >= bytesWritten) {
|
||||||
agentData->writeToPacket(&tempOutputBuffer[0], bytesWritten);
|
agentData->writeToPacket(&tempOutputBuffer[0], bytesWritten);
|
||||||
|
|
Loading…
Reference in a new issue