mirror of
https://github.com/overte-org/overte.git
synced 2025-06-05 04:00:38 +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
|
@ -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})
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
|
@ -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 |
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);
|
||||
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());
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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})
|
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
|
||||
}
|
||||
|
||||
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__
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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 {
|
||||
|
|
Loading…
Reference in a new issue