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

This commit is contained in:
ZappoMan 2013-06-14 19:50:38 -07:00
commit c9e1c898c2
21 changed files with 453 additions and 218 deletions

View file

@ -18,4 +18,10 @@ include_glm(${TARGET_NAME} ${ROOT_DIR})
# link the shared hifi library
include(${MACRO_DIR}/LinkHifiLibrary.cmake)
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})

View file

@ -11,10 +11,18 @@
#include "AvatarAudioRingBuffer.h"
AvatarAudioRingBuffer::AvatarAudioRingBuffer() :
_twoPoles(),
_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) {
_shouldLoopbackForAgent = (sourceBuffer[0] == PACKET_HEADER_MICROPHONE_AUDIO_WITH_ECHO);
return PositionalAudioRingBuffer::parseData(sourceBuffer, numBytes);

View file

@ -9,20 +9,29 @@
#ifndef __hifi__AvatarAudioRingBuffer__
#define __hifi__AvatarAudioRingBuffer__
#include <Stk.h>
#include <TwoPole.h>
#include "PositionalAudioRingBuffer.h"
typedef std::map<uint16_t, stk::TwoPole*> TwoPoleAgentMap;
class AvatarAudioRingBuffer : public PositionalAudioRingBuffer {
public:
AvatarAudioRingBuffer();
~AvatarAudioRingBuffer();
int parseData(unsigned char* sourceBuffer, int numBytes);
TwoPoleAgentMap& getTwoPoles() { return _twoPoles; }
bool shouldLoopbackForAgent() const { return _shouldLoopbackForAgent; }
private:
// disallow copying of AvatarAudioRingBuffer objects
AvatarAudioRingBuffer(const AvatarAudioRingBuffer&);
AvatarAudioRingBuffer& operator= (const AvatarAudioRingBuffer&);
TwoPoleAgentMap _twoPoles;
bool _shouldLoopbackForAgent;
};

View file

@ -24,6 +24,7 @@
#include <AgentTypes.h>
#include <SharedUtil.h>
#include <StdDev.h>
#include <Logstash.h>
#include "InjectedAudioRingBuffer.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_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 MIN_SAMPLE_VALUE = std::numeric_limits<int16_t>::min();
@ -57,7 +58,7 @@ void plateauAdditionOfSamples(int16_t &mixSample, int16_t sampleToAdd) {
long normalizedSample = std::min(MAX_SAMPLE_VALUE, sumSample);
normalizedSample = std::max(MIN_SAMPLE_VALUE, sumSample);
mixSample = normalizedSample;
mixSample = normalizedSample;
}
void attachNewBufferToAgent(Agent *newAgent) {
@ -70,9 +71,20 @@ void attachNewBufferToAgent(Agent *newAgent) {
}
}
bool wantLocalDomain = false;
int main(int argc, const char* argv[]) {
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);
ssize_t receivedBytes = 0;
@ -100,12 +112,42 @@ int main(int argc, const char* argv[]) {
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) {
if (Logstash::shouldSendStats()) {
gettimeofday(&beginSendTime, NULL);
}
// send a check in packet to the domain server if DOMAIN_SERVER_CHECK_IN_USECS has elapsed
if (usecTimestampNow() - usecTimestamp(&lastDomainServerCheckIn) >= DOMAIN_SERVER_CHECK_IN_USECS) {
gettimeofday(&lastDomainServerCheckIn, NULL);
AgentList::getInstance()->sendDomainServerCheckIn();
if (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++) {
@ -136,6 +178,8 @@ int main(int argc, const char* argv[]) {
int numSamplesDelay = 0;
float weakChannelAmplitudeRatio = 1.0f;
stk::TwoPole* otherAgentTwoPole = NULL;
if (otherAgent != agent) {
glm::vec3 listenerPosition = agentRingBuffer->getPosition();
@ -212,6 +256,26 @@ int main(int argc, const char* argv[]) {
float sinRatio = fabsf(sinf(glm::radians(bearingRelativeAngleToSource)));
numSamplesDelay = PHASE_DELAY_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);
}
if (otherAgentTwoPole) {
otherAgentBuffer->getNextOutput()[s] = otherAgentTwoPole->tick(otherAgentBuffer->getNextOutput()[s]);
}
int16_t currentSample = otherAgentBuffer->getNextOutput()[s] * attenuationCoefficient;
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();
if (usecToSleep > 0) {

Binary file not shown.

Before

Width:  |  Height:  |  Size: 144 KiB

After

Width:  |  Height:  |  Size: 71 KiB

View 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;
}

View 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();
}

View file

@ -305,6 +305,8 @@ void Application::paintGL() {
glEnable(GL_LINE_SMOOTH);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
float headCameraScale = _serialHeadSensor.active ? _headCameraPitchYawScale : 1.0f;
if (_myCamera.getMode() == CAMERA_MODE_MIRROR) {
_myCamera.setTightness (100.0f);
_myCamera.setTargetPosition(_myAvatar.getUprightHeadPosition());
@ -320,11 +322,11 @@ void Application::paintGL() {
} else if (_myCamera.getMode() == CAMERA_MODE_FIRST_PERSON) {
_myCamera.setTightness(0.0f); // In first person, camera follows head exactly without delay
_myCamera.setTargetPosition(_myAvatar.getUprightHeadPosition());
_myCamera.setTargetRotation(_myAvatar.getHead().getCameraOrientation(_headCameraPitchYawScale));
_myCamera.setTargetRotation(_myAvatar.getHead().getCameraOrientation(headCameraScale));
} else if (_myCamera.getMode() == CAMERA_MODE_THIRD_PERSON) {
_myCamera.setTargetPosition(_myAvatar.getUprightHeadPosition());
_myCamera.setTargetRotation(_myAvatar.getHead().getCameraOrientation(_headCameraPitchYawScale));
_myCamera.setTargetRotation(_myAvatar.getHead().getCameraOrientation(headCameraScale));
}
// Update camera position
@ -1454,6 +1456,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
_myAvatar.setMouseRay(mouseRayOrigin, mouseRayDirection);
// Set where I am looking based on my mouse ray (so that other people can see)
glm::vec3 myLookAtFromMouse(mouseRayOrigin + mouseRayDirection);
_myAvatar.getHead().setLookAtPosition(myLookAtFromMouse);
// If we are dragging on a voxel, add thrust according to the amount the mouse is dragging
const float VOXEL_GRAB_THRUST = 5.0f;
@ -2123,11 +2129,15 @@ void Application::displaySide(Camera& whichCamera) {
avatar->init();
}
avatar->render(false, _renderAvatarBalls->isChecked());
avatar->setDisplayingLookatVectors(_renderLookatOn->isChecked());
}
}
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.setDisplayingLookatVectors(_renderLookatOn->isChecked());
}

View file

@ -271,6 +271,7 @@ Avatar::~Avatar() {
}
void Avatar::init() {
_head.init();
_voxels.init();
_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.setPosition(_bodyBall[ BODY_BALL_HEAD_BASE ].position);
_head.setScale (_bodyBall[ BODY_BALL_HEAD_BASE ].radius);

View file

@ -5,13 +5,16 @@
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
#include <glm/gtx/quaternion.hpp>
#include <QImage>
#include <AgentList.h>
#include "Application.h"
#include "Avatar.h"
#include "Head.h"
#include "Util.h"
#include <vector>
#include <lodepng.h>
#include <AgentList.h>
#include "renderer/ProgramObject.h"
using namespace std;
@ -36,9 +39,9 @@ const float IRIS_RADIUS = 0.007;
const float IRIS_PROTRUSION = 0.0145f;
const char IRIS_TEXTURE_FILENAME[] = "resources/images/iris.png";
unsigned int IRIS_TEXTURE_WIDTH = 768;
unsigned int IRIS_TEXTURE_HEIGHT = 498;
vector<unsigned char> irisTexture;
ProgramObject* Head::_irisProgram = 0;
GLuint Head::_irisTextureID;
int Head::_eyePositionLocation;
Head::Head(Avatar* owningAvatar) :
HeadData((AvatarData*)owningAvatar),
@ -57,7 +60,6 @@ Head::Head(Avatar* owningAvatar) :
_mouthPosition(0.0f, 0.0f, 0.0f),
_scale(1.0f),
_browAudioLift(0.0f),
_lookingAtSomething(false),
_gravity(0.0f, -1.0f, 0.0f),
_lastLoudness(0.0f),
_averageLoudness(0.0f),
@ -67,13 +69,38 @@ Head::Head(Avatar* owningAvatar) :
_lookingInMirror(false),
_renderLookatVectors(false),
_mohawkTriangleFan(NULL),
_mohawkColors(NULL)
_mohawkColors(NULL),
_saccade(0.0f, 0.0f, 0.0f),
_saccadeTarget(0.0f, 0.0f, 0.0f)
{
if (USING_PHYSICAL_MOHAWK) {
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() {
_yaw = _pitch = _roll = 0.0f;
_leanForward = _leanSideways = 0.0f;
@ -102,32 +129,19 @@ void Head::resetHairPhysics() {
void Head::simulate(float deltaTime, bool isMine) {
const float HEAD_MOTION_DECAY = 0.00;
/*
// Decay head back to center if turned on
if (isMine && _returnHeadToCenter) {
// 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;
// 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);
if (randFloat() < deltaTime / AVERAGE_MICROSACCADE_INTERVAL) {
_saccadeTarget = MICROSACCADE_MAGNITUDE * randVector();
} else if (randFloat() < deltaTime / AVERAGE_SACCADE_INTERVAL) {
_saccadeTarget = SACCADE_MAGNITUDE * randVector();
}
// 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);
_saccade += (_saccadeTarget - _saccade) * 0.50f;
// Update audio trailing average for rendering facial animations
const float AUDIO_AVERAGING_SECS = 0.05;
@ -148,30 +162,13 @@ void Head::simulate(float deltaTime, bool isMine) {
_browAudioLift *= 0.7f;
// based on the nature of the lookat position, determine if the eyes can look / are looking at it.
determineIfLookingAtSomething();
// based on the nature of the lookat position, determine if the eyes can look / are looking at it.
if (USING_PHYSICAL_MOHAWK) {
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() {
//generate orientation directions
glm::quat orientation = getOrientation();
@ -218,9 +215,9 @@ void Head::render(bool lookingInMirror, float alpha) {
renderEyeBalls();
renderEars();
renderMouth();
renderEyeBrows();
renderEyeBrows();
if (_renderLookatVectors && _lookingAtSomething) {
if (_renderLookatVectors) {
renderLookatVectors(_leftEyePosition, _rightEyePosition, _lookAtPosition);
}
}
@ -469,116 +466,74 @@ void Head::renderEyeBrows() {
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
glPushMatrix();
glColor3fv(EYEBALL_COLOR);
glTranslatef(_leftEyePosition.x, _leftEyePosition.y, _leftEyePosition.z);
gluSphere(irisQuadric, EYEBALL_RADIUS, 30, 30);
glutSolidSphere(EYEBALL_RADIUS, 30, 30);
glPopMatrix();
glm::vec3 front = getFrontDirection();
//render white ball of right eyeball
glPushMatrix();
glColor3fv(EYEBALL_COLOR);
glTranslatef(_rightEyePosition.x, _rightEyePosition.y, _rightEyePosition.z);
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
glPushMatrix();
//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
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();
// 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();
//render white ball of right eyeball
glPushMatrix();
glColor3fv(EYEBALL_COLOR);
glTranslatef(_rightEyePosition.x, _rightEyePosition.y, _rightEyePosition.z);
gluSphere(irisQuadric, EYEBALL_RADIUS, 30, 30);
glPopMatrix();
// render right iris
glPushMatrix(); {
glTranslatef(_rightEyePosition.x, _rightEyePosition.y, _rightEyePosition.z); //translate to eyeball position
glPushMatrix();
if (_lookingAtSomething) {
//rotate the eyeball to aim towards the lookat position
glm::vec3 targetLookatAxis = glm::normalize(_lookAtPosition - _rightEyePosition);
glm::vec3 rotationAxis = glm::cross(targetLookatAxis, IDENTITY_UP);
float angle = 180.0f - angleBetween(targetLookatAxis, IDENTITY_UP);
glRotatef(angle, rotationAxis.x, rotationAxis.y, rotationAxis.z);
glRotatef(180.0f, 0.0f, 1.0f, 0.0f); //adjust roll to correct after previous rotations
} else {
//rotate the eyeball to aim 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();
//rotate the eyeball to aim towards the lookat position
glm::vec3 targetLookatVector = _lookAtPosition + _saccade - _rightEyePosition;
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() - _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));
glutSolidSphere(0.5f, 15, 15);
}
// delete the iris quadric now that we're done with it
gluDeleteQuadric(irisQuadric);
glPopMatrix();
_irisProgram->release();
glBindTexture(GL_TEXTURE_2D, 0);
glDisable(GL_TEXTURE_2D);
}
void Head::renderLookatVectors(glm::vec3 leftEyePosition, glm::vec3 rightEyePosition, glm::vec3 lookatPosition) {

View file

@ -26,11 +26,13 @@ enum eyeContactTargets
const int NUM_HAIR_TUFTS = 4;
class Avatar;
class ProgramObject;
class Head : public HeadData {
public:
Head(Avatar* owningAvatar);
void init();
void reset();
void simulate(float deltaTime, bool isMine);
void render(bool lookingInMirror, float alpha);
@ -91,7 +93,6 @@ private:
glm::vec3 _mouthPosition;
float _scale;
float _browAudioLift;
bool _lookingAtSomething;
glm::vec3 _gravity;
float _lastLoudness;
float _averageLoudness;
@ -103,6 +104,12 @@ private:
HairTuft _hairTuft[NUM_HAIR_TUFTS];
glm::vec3* _mohawkTriangleFan;
glm::vec3* _mohawkColors;
glm::vec3 _saccade;
glm::vec3 _saccadeTarget;
static ProgramObject* _irisProgram;
static GLuint _irisTextureID;
static int _eyePositionLocation;
// private methods
void createMohawk();
@ -113,7 +120,6 @@ private:
void renderMouth();
void renderLookatVectors(glm::vec3 leftEyePosition, glm::vec3 rightEyePosition, glm::vec3 lookatPosition);
void calculateGeometry();
void determineIfLookingAtSomething();
void resetHairPhysics();
void updateHairPhysics(float deltaTime);
};

View file

@ -85,7 +85,7 @@ glm::quat rotationBetween(const glm::vec3& v1, const glm::vec3& v2) {
if (isnan(angle) || angle < EPSILON) {
return glm::quat();
}
glm::vec3 axis = glm::cross(v1, v2);
glm::vec3 axis;
if (angle > 179.99f) { // 180 degree rotation; must use another axis
axis = glm::cross(v1, glm::vec3(1.0f, 0.0f, 0.0f));
float axisLength = glm::length(axis);
@ -267,6 +267,11 @@ double diffclock(timeval *clock1,timeval *clock2)
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* monoRenderer = new TextRenderer(MONO_FONT_FAMILY);
static TextRenderer* proportionalRenderer = new TextRenderer(SANS_FONT_FAMILY, -1, -1, false, TextRenderer::SHADOW_EFFECT);

View file

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

View file

@ -15,8 +15,4 @@ include(${MACRO_DIR}/IncludeGLM.cmake)
include_glm(${TARGET_NAME} ${ROOT_DIR})
include(${MACRO_DIR}/LinkHifiLibrary.cmake)
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})
link_hifi_library(shared ${TARGET_NAME} ${ROOT_DIR})

View 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;
}

View 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__) */

View file

@ -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
}
bool isInEnvironment(const char* environment) {
char* environmentString = getenv("HIFI_ENVIRONMENT");
if (environmentString && strcmp(environmentString, environment) == 0) {
return true;
} else {
return false;
}
}
void switchToResourcesParentIfRequired() {
#ifdef __APPLE__

View file

@ -57,6 +57,7 @@ void setAtBit(unsigned char& byte, int bitIndex);
int getSemiNibbleAt(unsigned char& byte, int bitIndex);
void setSemiNibbleAt(unsigned char& byte, int bitIndex, int value);
bool isInEnvironment(const char* environment);
void switchToResourcesParentIfRequired();

View file

@ -1034,22 +1034,21 @@ int VoxelTree::searchForColoredNodesRecursion(int maxSearchLevel, int& currentSe
return maxChildLevel;
}
int VoxelTree::encodeTreeBitstream(int maxEncodeLevel, VoxelNode* node, unsigned char* outputBuffer, int availableBytes,
VoxelNodeBag& bag, const ViewFrustum* viewFrustum, bool includeColor, bool includeExistsBits,
int chopLevels, bool deltaViewFrustum, const ViewFrustum* lastViewFrustum) const {
int VoxelTree::encodeTreeBitstream(VoxelNode* node, unsigned char* outputBuffer, int availableBytes, VoxelNodeBag& bag,
EncodeBitstreamParams& params) const {
// How many bytes have we written so far at this level;
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 (viewFrustum && !node->isInView(*viewFrustum)) {
if (params.viewFrustum && !node->isInView(*params.viewFrustum)) {
return bytesWritten;
}
// write the octal code
int codeLength;
if (chopLevels) {
unsigned char* newCode = chopOctalCode(node->getOctalCode(), chopLevels);
if (params.chopLevels) {
unsigned char* newCode = chopOctalCode(node->getOctalCode(), params.chopLevels);
if (newCode) {
codeLength = bytesRequiredForCodeLength(numberOfThreeBitSectionsInCode(newCode));
memcpy(outputBuffer, newCode, codeLength);
@ -1068,16 +1067,14 @@ int VoxelTree::encodeTreeBitstream(int maxEncodeLevel, VoxelNode* node, unsigned
availableBytes -= codeLength; // keep track or remaining space
int currentEncodeLevel = 0;
int childBytesWritten = encodeTreeBitstreamRecursion(maxEncodeLevel, currentEncodeLevel, node, outputBuffer, availableBytes,
bag, viewFrustum, includeColor, includeExistsBits, chopLevels,
deltaViewFrustum, lastViewFrustum);
int childBytesWritten = encodeTreeBitstreamRecursion(node, outputBuffer, availableBytes, bag, params, currentEncodeLevel);
// if childBytesWritten == 1 then something went wrong... that's not possible
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
// 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;
}
@ -1091,10 +1088,8 @@ int VoxelTree::encodeTreeBitstream(int maxEncodeLevel, VoxelNode* node, unsigned
return bytesWritten;
}
int VoxelTree::encodeTreeBitstreamRecursion(int maxEncodeLevel, int& currentEncodeLevel, VoxelNode* node,
unsigned char* outputBuffer, int availableBytes, VoxelNodeBag& bag,
const ViewFrustum* viewFrustum, bool includeColor, bool includeExistsBits,
int chopLevels, bool deltaViewFrustum, const ViewFrustum* lastViewFrustum) const {
int VoxelTree::encodeTreeBitstreamRecursion(VoxelNode* node, unsigned char* outputBuffer, int availableBytes, VoxelNodeBag& bag,
EncodeBitstreamParams& params, int& currentEncodeLevel) const {
// How many bytes have we written so far at this level;
int bytesAtThisLevel = 0;
@ -1103,13 +1098,13 @@ int VoxelTree::encodeTreeBitstreamRecursion(int maxEncodeLevel, int& currentEnco
currentEncodeLevel++;
// If we've reached our max Search Level, then stop searching.
if (currentEncodeLevel >= maxEncodeLevel) {
if (currentEncodeLevel >= params.maxEncodeLevel) {
return bytesAtThisLevel;
}
// caller can pass NULL as viewFrustum if they want everything
if (viewFrustum) {
float distance = node->distanceToCamera(*viewFrustum);
if (params.viewFrustum) {
float distance = node->distanceToCamera(*params.viewFrustum);
float boundaryDistance = boundaryDistanceForRenderLevel(*node->getOctalCode() + 1);
// 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!
// although technically, we really shouldn't ever be here, because our callers shouldn't be calling us if
// we're out of view
if (!node->isInView(*viewFrustum)) {
if (!node->isInView(*params.viewFrustum)) {
return bytesAtThisLevel;
}
}
@ -1153,16 +1148,16 @@ int VoxelTree::encodeTreeBitstreamRecursion(int maxEncodeLevel, int& currentEnco
VoxelNode* childNode = node->getChildAtIndex(i);
// 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));
}
bool childIsInView = (childNode && (!viewFrustum || childNode->isInView(*viewFrustum)));
bool childIsInView = (childNode && (!params.viewFrustum || childNode->isInView(*params.viewFrustum)));
if (childIsInView) {
// Before we determine consider this further, let's see if it's in our LOD scope...
float distance = viewFrustum ? childNode->distanceToCamera(*viewFrustum) : 0;
float boundaryDistance = viewFrustum ? boundaryDistanceForRenderLevel(*childNode->getOctalCode() + 1) : 1;
float distance = params.viewFrustum ? childNode->distanceToCamera(*params.viewFrustum) : 0;
float boundaryDistance = params.viewFrustum ? boundaryDistanceForRenderLevel(*childNode->getOctalCode() + 1) : 1;
if (distance < boundaryDistance) {
inViewCount++;
@ -1175,8 +1170,8 @@ int VoxelTree::encodeTreeBitstreamRecursion(int maxEncodeLevel, int& currentEnco
inViewNotLeafCount++;
}
bool childWasInView = (childNode && deltaViewFrustum &&
(lastViewFrustum && ViewFrustum::INSIDE == childNode->inFrustum(*lastViewFrustum)));
bool childWasInView = (childNode && params.deltaViewFrustum &&
(params.lastViewFrustum && ViewFrustum::INSIDE == childNode->inFrustum(*params.lastViewFrustum)));
// track children with actual color, only if the child wasn't previously in view!
if (childNode && childNode->isColored() && !childWasInView) {
@ -1191,7 +1186,7 @@ int VoxelTree::encodeTreeBitstreamRecursion(int maxEncodeLevel, int& currentEnco
bytesAtThisLevel += sizeof(childrenColoredBits); // keep track of byte count
// write the color data...
if (includeColor) {
if (params.includeColor) {
for (int i = 0; i < NUMBER_OF_CHILDREN; i++) {
if (oneAtBit(childrenColoredBits, i)) {
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
// childrenExistInPacketBits, so that the lower code can properly repair the packet exists bits
if (includeExistsBits) {
if (params.includeExistsBits) {
*writeToThisLevelBuffer = childrenExistInTreeBits;
writeToThisLevelBuffer += sizeof(childrenExistInTreeBits); // move the pointer
bytesAtThisLevel += sizeof(childrenExistInTreeBits); // keep track of byte count
@ -1247,10 +1242,8 @@ int VoxelTree::encodeTreeBitstreamRecursion(int maxEncodeLevel, int& currentEnco
VoxelNode* childNode = node->getChildAtIndex(i);
int thisLevel = currentEncodeLevel;
int childTreeBytesOut = encodeTreeBitstreamRecursion(maxEncodeLevel, thisLevel, childNode,
outputBuffer, availableBytes, bag,
viewFrustum, includeColor, includeExistsBits, chopLevels,
deltaViewFrustum, lastViewFrustum);
int childTreeBytesOut = encodeTreeBitstreamRecursion(childNode, outputBuffer, availableBytes, bag,
params, thisLevel);
// 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.
@ -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!!
//
// 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
}
@ -1333,8 +1326,9 @@ void VoxelTree::writeToSVOFile(const char* fileName, VoxelNode* node) const {
while (!nodeBag.isEmpty()) {
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);
}
@ -1368,8 +1362,8 @@ void VoxelTree::copySubTreeIntoNewTree(VoxelNode* startNode, VoxelTree* destinat
VoxelNode* subTree = nodeBag.extract();
// ask our tree to write a bitsteam
bytesWritten = encodeTreeBitstream(INT_MAX, subTree, &outputBuffer[0],
MAX_VOXEL_PACKET_SIZE - 1, nodeBag, IGNORE_VIEW_FRUSTUM, WANT_COLOR, NO_EXISTS_BITS, chopLevels);
EncodeBitstreamParams params(INT_MAX, 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
destinationTree->readBitstreamToTree(&outputBuffer[0], bytesWritten, WANT_COLOR, NO_EXISTS_BITS);
@ -1388,9 +1382,9 @@ void VoxelTree::copyFromTreeIntoSubTree(VoxelTree* sourceTree, VoxelNode* destin
VoxelNode* subTree = nodeBag.extract();
// ask our tree to write a bitsteam
bytesWritten = sourceTree->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 = sourceTree->encodeTreeBitstream(subTree, &outputBuffer[0], MAX_VOXEL_PACKET_SIZE - 1, nodeBag, params);
// ask destination tree to read the bitstream
readBitstreamToTree(&outputBuffer[0], bytesWritten, WANT_COLOR, NO_EXISTS_BITS, destinationNode);
}

View file

@ -28,6 +28,35 @@ typedef enum {GRADIENT, RANDOM, NATURAL} creationMode;
#define COLLAPSE_EMPTY_TREE true
#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 {
public:
// when a voxel is created in the tree (object new'd)
@ -70,10 +99,8 @@ public:
void recurseTreeWithOperationDistanceSorted(RecurseVoxelTreeOperation operation,
const glm::vec3& point, void* extraData=NULL);
int encodeTreeBitstream(int maxEncodeLevel, VoxelNode* node, unsigned char* outputBuffer, int availableBytes,
VoxelNodeBag& bag, const ViewFrustum* viewFrustum,
bool includeColor = WANT_COLOR, bool includeExistsBits = WANT_EXISTS_BITS, int chopLevels = 0,
bool deltaViewFrustum = false, const ViewFrustum* lastViewFrustum = NULL) const;
int encodeTreeBitstream(VoxelNode* node, unsigned char* outputBuffer, int availableBytes, VoxelNodeBag& bag,
EncodeBitstreamParams& params) const;
int searchForColoredNodes(int maxSearchLevel, VoxelNode* node, const ViewFrustum& viewFrustum, VoxelNodeBag& bag,
bool deltaViewFrustum = false, const ViewFrustum* lastViewFrustum = NULL);
@ -111,10 +138,8 @@ private:
void deleteVoxelCodeFromTreeRecursion(VoxelNode* node, void* extraData);
void readCodeColorBufferToTreeRecursion(VoxelNode* node, void* extraData);
int encodeTreeBitstreamRecursion(int maxEncodeLevel, int& currentEncodeLevel,
VoxelNode* node, unsigned char* outputBuffer, int availableBytes, VoxelNodeBag& bag,
const ViewFrustum* viewFrustum, bool includeColor, bool includeExistsBits,
int chopLevels, bool deltaViewFrustum, const ViewFrustum* lastViewFrustum) const;
int encodeTreeBitstreamRecursion(VoxelNode* node, unsigned char* outputBuffer, int availableBytes, VoxelNodeBag& bag,
EncodeBitstreamParams& params, int& currentEncodeLevel) const;
int searchForColoredNodesRecursion(int maxSearchLevel, int& currentSearchLevel,
VoxelNode* node, const ViewFrustum& viewFrustum, VoxelNodeBag& bag,

View file

@ -167,10 +167,12 @@ void resInVoxelDistributor(AgentList* agentList,
while (packetsSentThisInterval < PACKETS_PER_CLIENT_PER_INTERVAL - (shouldSendEnvironments ? 1 : 0)) {
if (!agentData->nodeBag.isEmpty()) {
VoxelNode* subTree = agentData->nodeBag.extract();
bytesWritten = serverTree.encodeTreeBitstream(agentData->getMaxSearchLevel(), subTree,
&tempOutputBuffer[0], MAX_VOXEL_PACKET_SIZE - 1,
agentData->nodeBag, &viewFrustum,
agentData->getWantColor(), WANT_EXISTS_BITS);
EncodeBitstreamParams params(agentData->getMaxSearchLevel(), &viewFrustum,
agentData->getWantColor(), WANT_EXISTS_BITS);
bytesWritten = serverTree.encodeTreeBitstream(subTree, &tempOutputBuffer[0], MAX_VOXEL_PACKET_SIZE - 1,
agentData->nodeBag, params);
if (agentData->getAvailable() >= bytesWritten) {
agentData->writeToPacket(&tempOutputBuffer[0], bytesWritten);
@ -310,12 +312,13 @@ void deepestLevelVoxelDistributor(AgentList* agentList,
while (packetsSentThisInterval < PACKETS_PER_CLIENT_PER_INTERVAL - (shouldSendEnvironments ? 1 : 0)) {
if (!agentData->nodeBag.isEmpty()) {
VoxelNode* subTree = agentData->nodeBag.extract();
bytesWritten = serverTree.encodeTreeBitstream(INT_MAX, subTree,
&tempOutputBuffer[0], MAX_VOXEL_PACKET_SIZE - 1,
agentData->nodeBag, &agentData->getCurrentViewFrustum(),
agentData->getWantColor(), WANT_EXISTS_BITS,
wantDelta, lastViewFrustum);
EncodeBitstreamParams params(INT_MAX, &agentData->getCurrentViewFrustum(), 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) {
agentData->writeToPacket(&tempOutputBuffer[0], bytesWritten);
} else {