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

Conflicts:
	interface/src/Application.h
This commit is contained in:
Andrzej Kapolka 2013-07-12 15:34:55 -07:00
commit 763e6a2fa1
19 changed files with 522 additions and 159 deletions

42
interface/src/Application.cpp Executable file → Normal file
View file

@ -73,6 +73,8 @@ using namespace std;
static char STAR_FILE[] = "https://s3-us-west-1.amazonaws.com/highfidelity/stars.txt";
static char STAR_CACHE_FILE[] = "cachedStars.txt";
static const bool TESTING_PARTICLE_SYSTEM = false;
static const int BANDWIDTH_METER_CLICK_MAX_DRAG_LENGTH = 6; // farther dragged clicks are ignored
const glm::vec3 START_LOCATION(4.f, 0.f, 5.f); // Where one's own node begins in the world
@ -183,6 +185,8 @@ Application::Application(int& argc, char** argv, timeval &startup_time) :
_touchAvgX(0.0f),
_touchAvgY(0.0f),
_isTouchPressed(false),
_yawFromTouch(0.0f),
_pitchFromTouch(0.0f),
_mousePressed(false),
_mouseVoxelScale(1.0f / 1024.0f),
_justEditedVoxel(false),
@ -969,6 +973,9 @@ void Application::idle() {
gettimeofday(&check, NULL);
// Only run simulation code if more than IDLE_SIMULATE_MSECS have passed since last time we ran
sendPostedEvents(NULL, QEvent::TouchBegin);
sendPostedEvents(NULL, QEvent::TouchUpdate);
sendPostedEvents(NULL, QEvent::TouchEnd);
double timeSinceLastUpdate = diffclock(&_lastTimeUpdated, &check);
if (timeSinceLastUpdate > IDLE_SIMULATE_MSECS) {
@ -978,9 +985,6 @@ void Application::idle() {
// This is necessary because id the idle() call takes longer than the
// interval between idle() calls, the event loop never gets to run,
// and touch events get delayed.
sendPostedEvents(NULL, QEvent::TouchBegin);
sendPostedEvents(NULL, QEvent::TouchUpdate);
sendPostedEvents(NULL, QEvent::TouchEnd);
const float BIGGEST_DELTA_TIME_SECS = 0.25f;
update(glm::clamp((float)timeSinceLastUpdate / 1000.f, 0.f, BIGGEST_DELTA_TIME_SECS));
@ -1902,12 +1906,9 @@ void Application::update(float deltaTime) {
if (_isTouchPressed) {
float TOUCH_YAW_SCALE = -50.0f;
float TOUCH_PITCH_SCALE = -50.0f;
_myAvatar.getHead().addYaw((_touchAvgX - _lastTouchAvgX)
* TOUCH_YAW_SCALE
* deltaTime);
_myAvatar.getHead().addPitch((_touchAvgY - _lastTouchAvgY)
* TOUCH_PITCH_SCALE
* deltaTime);
_yawFromTouch += ((_touchAvgX - _lastTouchAvgX) * TOUCH_YAW_SCALE * deltaTime);
_pitchFromTouch += ((_touchAvgY - _lastTouchAvgY) * TOUCH_PITCH_SCALE * deltaTime);
_lastTouchAvgX = _touchAvgX;
_lastTouchAvgY = _touchAvgY;
}
@ -2007,15 +2008,28 @@ void Application::update(float deltaTime) {
_audio.setLastVelocity(_myAvatar.getVelocity());
_audio.eventuallyAnalyzePing();
#endif
if (TESTING_PARTICLE_SYSTEM) {
_particleSystem.simulate(deltaTime);
}
}
void Application::updateAvatar(float deltaTime) {
// When head is rotated via touch/mouse look, slowly turn body to follow
const float BODY_FOLLOW_HEAD_RATE = 0.5f;
// update body yaw by body yaw delta
_myAvatar.setOrientation(_myAvatar.getOrientation()
* glm::quat(glm::vec3(0, _yawFromTouch * deltaTime * BODY_FOLLOW_HEAD_RATE, 0) * deltaTime));
_yawFromTouch -= _yawFromTouch * deltaTime * BODY_FOLLOW_HEAD_RATE;
// Update my avatar's state from gyros and/or webcam
_myAvatar.updateFromGyrosAndOrWebcam(_gyroLook->isChecked(),
glm::vec3(_headCameraPitchYawScale,
_headCameraPitchYawScale,
_headCameraPitchYawScale));
_headCameraPitchYawScale),
_yawFromTouch,
_pitchFromTouch);
if (_serialHeadSensor.isActive()) {
@ -2055,8 +2069,8 @@ void Application::updateAvatar(float deltaTime) {
float yaw, pitch, roll;
OculusManager::getEulerAngles(yaw, pitch, roll);
_myAvatar.getHead().setYaw(yaw);
_myAvatar.getHead().setPitch(pitch);
_myAvatar.getHead().setYaw(yaw + _yawFromTouch);
_myAvatar.getHead().setPitch(pitch + _pitchFromTouch);
_myAvatar.getHead().setRoll(roll);
}
@ -2441,6 +2455,10 @@ void Application::displaySide(Camera& whichCamera) {
_myAvatar.render(_lookingInMirror->isChecked(), _renderAvatarBalls->isChecked());
_myAvatar.setDisplayingLookatVectors(_renderLookatOn->isChecked());
}
if (TESTING_PARTICLE_SYSTEM) {
_particleSystem.render();
}
// Render the world box
if (!_lookingInMirror->isChecked() && _renderStatsOn->isChecked()) { render_world_box(); }

View file

@ -31,6 +31,7 @@
#include "BandwidthMeter.h"
#include "Camera.h"
#include "Environment.h"
#include "ParticleSystem.h"
#include "SerialInterface.h"
#include "Stars.h"
#include "Swatch.h"
@ -332,6 +333,8 @@ private:
float _touchDragStartedAvgX;
float _touchDragStartedAvgY;
bool _isTouchPressed; // true if multitouch has been pressed (clear when finished)
float _yawFromTouch;
float _pitchFromTouch;
VoxelDetail _mouseVoxelDragging;
glm::vec3 _voxelThrust;
@ -364,6 +367,8 @@ private:
GeometryCache _geometryCache;
ParticleSystem _particleSystem;
#ifndef _WIN32
Audio _audio;
#endif

View file

@ -448,7 +448,7 @@ void Audio::addReceivedAudioToBuffer(unsigned char* receivedData, int receivedBy
//printf("Got audio packet %d\n", _packetsReceivedThisPlayback);
_ringBuffer.parseData((unsigned char*) receivedData, PACKET_LENGTH_BYTES + sizeof(PACKET_TYPE));
_ringBuffer.parseData((unsigned char*) receivedData, receivedBytes);
Application::getInstance()->getBandwidthMeter()->inputStream(BandwidthMeter::AUDIO)
.updateValue(PACKET_LENGTH_BYTES + sizeof(PACKET_TYPE));

View file

@ -0,0 +1,183 @@
//
// ParticleSystem.cpp
// hifi
//
// Created by Jeffrey on July 10, 2013
//
#include <glm/glm.hpp>
#include "InterfaceConfig.h"
#include <SharedUtil.h>
#include "ParticleSystem.h"
ParticleSystem::ParticleSystem() {
_numberOfParticles = 1500;
assert(_numberOfParticles <= MAX_PARTICLES);
_bounce = 0.9f;
_timer = 0.0f;
_airFriction = 6.0f;
_jitter = 0.1f;
_homeAttraction = 0.0f;
_tornadoForce = 0.0f;
_neighborAttraction = 0.02f;
_neighborRepulsion = 0.9f;
_tornadoAxis = glm::normalize(glm::vec3(0.1f, 1.0f, 0.1f));
_home = glm::vec3(5.0f, 1.0f, 5.0f);
_TEST_bigSphereRadius = 0.5f;
_TEST_bigSpherePosition = glm::vec3( 5.0f, _TEST_bigSphereRadius, 5.0f);
for (unsigned int p = 0; p < _numberOfParticles; p++) {
_particle[p].position = _home;
_particle[p].velocity = glm::vec3(0.0f, 0.0f, 0.0f);
float radian = ((float)p / (float)_numberOfParticles) * PI_TIMES_TWO;
float wave = sinf(radian);
float red = 0.5f + 0.5f * wave;
float green = 0.3f + 0.3f * wave;
float blue = 0.2f - 0.2f * wave;
_particle[p].color = glm::vec3(red, green, blue);
_particle[p].age = 0.0f;
_particle[p].radius = 0.01f;
}
}
void ParticleSystem::simulate(float deltaTime) {
runSpecialEffectsTest(deltaTime);
for (unsigned int p = 0; p < _numberOfParticles; p++) {
updateParticle(p, deltaTime);
}
}
void ParticleSystem::runSpecialEffectsTest(float deltaTime) {
_timer += deltaTime;
_gravity = 0.01f + 0.01f * sinf( _timer * 0.52f );
_airFriction = 3.0f + 2.0f * sinf( _timer * 0.32f );
_jitter = 0.05f + 0.05f * sinf( _timer * 0.42f );
_homeAttraction = 0.01f + 0.01f * cosf( _timer * 0.6f );
_tornadoForce = 0.0f + 0.03f * sinf( _timer * 0.7f );
_neighborAttraction = 0.1f + 0.1f * cosf( _timer * 0.8f );
_neighborRepulsion = 0.4f + 0.3f * sinf( _timer * 0.4f );
_tornadoAxis = glm::vec3
(
0.0f + 0.5f * sinf( _timer * 0.55f ),
1.0f,
0.0f + 0.5f * cosf( _timer * 0.75f )
);
}
void ParticleSystem::updateParticle(int p, float deltaTime) {
_particle[p].age += deltaTime;
// apply random jitter
_particle[p].velocity +=
glm::vec3
(
-_jitter * ONE_HALF + _jitter * randFloat(),
-_jitter * ONE_HALF + _jitter * randFloat(),
-_jitter * ONE_HALF + _jitter * randFloat()
) * deltaTime;
// apply attraction to home position
glm::vec3 vectorToHome = _home - _particle[p].position;
_particle[p].velocity += vectorToHome * _homeAttraction * deltaTime;
// apply neighbor attraction
int neighbor = p + 1;
if (neighbor == _numberOfParticles ) {
neighbor = 0;
}
glm::vec3 vectorToNeighbor = _particle[p].position - _particle[neighbor].position;
_particle[p].velocity -= vectorToNeighbor * _neighborAttraction * deltaTime;
float distanceToNeighbor = glm::length(vectorToNeighbor);
if (distanceToNeighbor > 0.0f) {
_particle[neighbor].velocity += (vectorToNeighbor / ( 1.0f + distanceToNeighbor * distanceToNeighbor)) * _neighborRepulsion * deltaTime;
}
// apply tornado force
glm::vec3 tornadoDirection = glm::cross(vectorToHome, _tornadoAxis);
_particle[p].velocity += tornadoDirection * _tornadoForce * deltaTime;
// apply air friction
float drag = 1.0 - _airFriction * deltaTime;
if (drag < 0.0f) {
_particle[p].velocity = glm::vec3(0.0f, 0.0f, 0.0f);
} else {
_particle[p].velocity *= drag;
}
// apply gravity
_particle[p].velocity.y -= _gravity * deltaTime;
// update position by velocity
_particle[p].position += _particle[p].velocity;
// collision with ground
if (_particle[p].position.y < _particle[p].radius) {
_particle[p].position.y = _particle[p].radius;
if (_particle[p].velocity.y < 0.0f) {
_particle[p].velocity.y *= -_bounce;
}
}
// collision with sphere
glm::vec3 vectorToSphereCenter = _TEST_bigSpherePosition - _particle[p].position;
float distanceToSphereCenter = glm::length(vectorToSphereCenter);
float combinedRadius = _TEST_bigSphereRadius + _particle[p].radius;
if (distanceToSphereCenter < combinedRadius) {
if (distanceToSphereCenter > 0.0f){
glm::vec3 directionToSphereCenter = vectorToSphereCenter / distanceToSphereCenter;
_particle[p].position = _TEST_bigSpherePosition - directionToSphereCenter * combinedRadius;
}
}
}
void ParticleSystem::render() {
for (unsigned int p = 0; p < _numberOfParticles; p++) {
glColor3f(_particle[p].color.x, _particle[p].color.y, _particle[p].color.z);
glPushMatrix();
glTranslatef(_particle[p].position.x, _particle[p].position.y, _particle[p].position.z);
glutSolidSphere(_particle[p].radius, 6, 6);
glPopMatrix();
// render velocity lines
glColor4f( _particle[p].color.x, _particle[p].color.y, _particle[p].color.z, 0.5f);
glm::vec3 end = _particle[p].position - _particle[p].velocity * 2.0f;
glBegin(GL_LINES);
glVertex3f(_particle[p].position.x, _particle[p].position.y, _particle[p].position.z);
glVertex3f(end.x, end.y, end.z);
glEnd();
}
}

View file

@ -0,0 +1,59 @@
//
// ParticleSystem.h
// hifi
//
// Created by Jeffrey on July 10, 2013
//
//
#ifndef hifi_ParticleSystem_h
#define hifi_ParticleSystem_h
const int MAX_PARTICLES = 5000;
const int MAX_EMITTERS = 10;
class ParticleSystem {
public:
ParticleSystem();
void simulate(float deltaTime);
void render();
private:
struct Particle {
glm::vec3 position;
glm::vec3 velocity;
glm::vec3 color;
float age;
float radius;
};
struct Emitter {
glm::vec3 position;
glm::vec3 direction;
};
float _bounce;
float _gravity;
float _timer;
Emitter _emitter[MAX_EMITTERS];
Particle _particle[MAX_PARTICLES];
int _numberOfParticles;
glm::vec3 _home;
glm::vec3 _tornadoAxis;
float _airFriction;
float _jitter;
float _homeAttraction;
float _tornadoForce;
float _neighborAttraction;
float _neighborRepulsion;
float _TEST_bigSphereRadius;
glm::vec3 _TEST_bigSpherePosition;
// private methods
void updateParticle(int index, float deltaTime);
void runSpecialEffectsTest(float deltaTime);
};
#endif

40
interface/src/Physics.cpp Normal file
View file

@ -0,0 +1,40 @@
//
// Physics.cpp
// hifi
//
// Created by Philip on July 11, 2013
//
// Routines to help with doing virtual world physics
//
#include <glm/glm.hpp>
#include <SharedUtil.h>
#include "Util.h"
#include "world.h"
#include "Physics.h"
//
// Applies static friction: maxVelocity is the largest velocity for which there
// there is friction, and strength is the amount of friction force applied to reduce
// velocity.
//
void applyStaticFriction(float deltaTime, glm::vec3& velocity, float maxVelocity, float strength) {
float v = glm::length(velocity);
if (v < maxVelocity) {
velocity *= glm::clamp((1.0f - deltaTime * strength * (1.f - v / maxVelocity)), 0.0f, 1.0f);
}
}
//
// Applies velocity damping, with a strength value for linear and squared velocity damping
//
void applyDamping(float deltaTime, glm::vec3& velocity, float linearStrength, float squaredStrength) {
if (squaredStrength == 0.f) {
velocity *= glm::clamp(1.f - deltaTime * linearStrength, 0.f, 1.f);
} else {
velocity *= glm::clamp(1.f - deltaTime * (linearStrength + glm::length(velocity) * squaredStrength), 0.f, 1.f);
}
}

15
interface/src/Physics.h Normal file
View file

@ -0,0 +1,15 @@
//
// Balls.h
// hifi
//
// Created by Philip on 4/25/13.
//
//
#ifndef hifi_Physics_h
#define hifi_Physics_h
void applyStaticFriction(float deltaTime, glm::vec3& velocity, float maxVelocity, float strength);
void applyDamping(float deltaTime, glm::vec3& velocity, float linearStrength, float squaredStrength);
#endif

View file

@ -30,7 +30,7 @@ int jointVectorMetaType = qRegisterMetaType<JointVector>("JointVector");
int matMetaType = qRegisterMetaType<Mat>("cv::Mat");
int rotatedRectMetaType = qRegisterMetaType<RotatedRect>("cv::RotatedRect");
Webcam::Webcam() : _enabled(false), _active(false), _frameTextureID(0), _depthTextureID(0) {
Webcam::Webcam() : _enabled(false), _active(false), _colorTextureID(0), _depthTextureID(0) {
// the grabber simply runs as fast as possible
_grabber = new FrameGrabber();
_grabber->moveToThread(&_grabberThread);
@ -65,13 +65,13 @@ void Webcam::reset() {
}
void Webcam::renderPreview(int screenWidth, int screenHeight) {
if (_enabled && _frameTextureID != 0) {
glBindTexture(GL_TEXTURE_2D, _frameTextureID);
if (_enabled && _colorTextureID != 0) {
glBindTexture(GL_TEXTURE_2D, _colorTextureID);
glEnable(GL_TEXTURE_2D);
glColor3f(1.0f, 1.0f, 1.0f);
glBegin(GL_QUADS);
const int PREVIEW_HEIGHT = 200;
int previewWidth = _frameWidth * PREVIEW_HEIGHT / _frameHeight;
int previewWidth = _textureSize.width * PREVIEW_HEIGHT / _textureSize.height;
int top = screenHeight - 600;
int left = screenWidth - previewWidth - 10;
@ -88,16 +88,14 @@ void Webcam::renderPreview(int screenWidth, int screenHeight) {
if (_depthTextureID != 0) {
glBindTexture(GL_TEXTURE_2D, _depthTextureID);
glBegin(GL_QUADS);
int depthPreviewWidth = _depthWidth * PREVIEW_HEIGHT / _depthHeight;
int depthLeft = screenWidth - depthPreviewWidth - 10;
glTexCoord2f(0, 0);
glVertex2f(depthLeft, top - PREVIEW_HEIGHT);
glVertex2f(left, top - PREVIEW_HEIGHT);
glTexCoord2f(1, 0);
glVertex2f(depthLeft + depthPreviewWidth, top - PREVIEW_HEIGHT);
glVertex2f(left + previewWidth, top - PREVIEW_HEIGHT);
glTexCoord2f(1, 1);
glVertex2f(depthLeft + depthPreviewWidth, top);
glVertex2f(left + previewWidth, top);
glTexCoord2f(0, 1);
glVertex2f(depthLeft, top);
glVertex2f(left, top);
glEnd();
glBindTexture(GL_TEXTURE_2D, 0);
@ -107,10 +105,10 @@ void Webcam::renderPreview(int screenWidth, int screenHeight) {
glColor3f(1.0f, 0.0f, 0.0f);
glPointSize(4.0f);
glBegin(GL_POINTS);
float projectedScale = PREVIEW_HEIGHT / (float)_depthHeight;
float projectedScale = PREVIEW_HEIGHT / _textureSize.height;
foreach (const Joint& joint, _joints) {
if (joint.isValid) {
glVertex2f(depthLeft + joint.projected.x * projectedScale,
glVertex2f(left + joint.projected.x * projectedScale,
top - PREVIEW_HEIGHT + joint.projected.y * projectedScale);
}
}
@ -126,8 +124,8 @@ void Webcam::renderPreview(int screenWidth, int screenHeight) {
glBegin(GL_LINE_LOOP);
Point2f facePoints[4];
_faceRect.points(facePoints);
float xScale = previewWidth / (float)_frameWidth;
float yScale = PREVIEW_HEIGHT / (float)_frameHeight;
float xScale = previewWidth / _textureSize.width;
float yScale = PREVIEW_HEIGHT / _textureSize.height;
glVertex2f(left + facePoints[0].x * xScale, top + facePoints[0].y * yScale);
glVertex2f(left + facePoints[1].x * xScale, top + facePoints[1].y * yScale);
glVertex2f(left + facePoints[2].x * xScale, top + facePoints[2].y * yScale);
@ -148,21 +146,22 @@ Webcam::~Webcam() {
delete _grabber;
}
void Webcam::setFrame(const Mat& frame, int format, const Mat& depth, const Mat& depthPreview,
void Webcam::setFrame(const Mat& color, int format, const Mat& depth, const Mat& depthPreview,
const RotatedRect& faceRect, const JointVector& joints) {
IplImage image = frame;
glPixelStorei(GL_UNPACK_ROW_LENGTH, image.widthStep / 3);
if (_frameTextureID == 0) {
glGenTextures(1, &_frameTextureID);
glBindTexture(GL_TEXTURE_2D, _frameTextureID);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, _frameWidth = image.width, _frameHeight = image.height, 0, format,
GL_UNSIGNED_BYTE, image.imageData);
IplImage colorImage = color;
glPixelStorei(GL_UNPACK_ROW_LENGTH, colorImage.widthStep / 3);
if (_colorTextureID == 0) {
glGenTextures(1, &_colorTextureID);
glBindTexture(GL_TEXTURE_2D, _colorTextureID);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, _textureSize.width = colorImage.width, _textureSize.height = colorImage.height,
0, format, GL_UNSIGNED_BYTE, colorImage.imageData);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
printLog("Capturing video at %dx%d.\n", _frameWidth, _frameHeight);
printLog("Capturing video at %gx%g.\n", _textureSize.width, _textureSize.height);
} else {
glBindTexture(GL_TEXTURE_2D, _frameTextureID);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, _frameWidth, _frameHeight, format, GL_UNSIGNED_BYTE, image.imageData);
glBindTexture(GL_TEXTURE_2D, _colorTextureID);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, _textureSize.width, _textureSize.height, format,
GL_UNSIGNED_BYTE, colorImage.imageData);
}
if (!depthPreview.empty()) {
@ -171,14 +170,13 @@ void Webcam::setFrame(const Mat& frame, int format, const Mat& depth, const Mat&
if (_depthTextureID == 0) {
glGenTextures(1, &_depthTextureID);
glBindTexture(GL_TEXTURE_2D, _depthTextureID);
glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, _depthWidth = depthImage.width, _depthHeight = depthImage.height, 0,
glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, depthImage.width, depthImage.height, 0,
GL_LUMINANCE, GL_UNSIGNED_BYTE, depthImage.imageData);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
printLog("Capturing depth at %dx%d.\n", _depthWidth, _depthHeight);
} else {
glBindTexture(GL_TEXTURE_2D, _depthTextureID);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, _depthWidth, _depthHeight, GL_LUMINANCE,
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, _textureSize.width, _textureSize.height, GL_LUMINANCE,
GL_UNSIGNED_BYTE, depthImage.imageData);
}
}
@ -201,6 +199,27 @@ void Webcam::setFrame(const Mat& frame, int format, const Mat& depth, const Mat&
}
_lastFrameTimestamp = now;
// correct for 180 degree rotations
if (_faceRect.angle < -90.0f) {
_faceRect.angle += 180.0f;
} else if (_faceRect.angle > 90.0f) {
_faceRect.angle -= 180.0f;
}
// compute the smoothed face rect
if (_estimatedFaceRect.size.area() == 0) {
_estimatedFaceRect = _faceRect;
} else {
const float FACE_RECT_SMOOTHING = 0.9f;
_estimatedFaceRect.center.x = glm::mix(_faceRect.center.x, _estimatedFaceRect.center.x, FACE_RECT_SMOOTHING);
_estimatedFaceRect.center.y = glm::mix(_faceRect.center.y, _estimatedFaceRect.center.y, FACE_RECT_SMOOTHING);
_estimatedFaceRect.size.width = glm::mix(_faceRect.size.width, _estimatedFaceRect.size.width, FACE_RECT_SMOOTHING);
_estimatedFaceRect.size.height = glm::mix(_faceRect.size.height, _estimatedFaceRect.size.height, FACE_RECT_SMOOTHING);
_estimatedFaceRect.angle = glm::mix(_faceRect.angle, _estimatedFaceRect.angle, FACE_RECT_SMOOTHING);
}
// see if we have joint data
if (!_joints.isEmpty()) {
_estimatedJoints.resize(NUM_AVATAR_JOINTS);
@ -227,30 +246,23 @@ void Webcam::setFrame(const Mat& frame, int format, const Mat& depth, const Mat&
_estimatedPosition = _estimatedJoints[AVATAR_JOINT_HEAD_BASE].position;
} else {
// roll is just the angle of the face rect (correcting for 180 degree rotations)
float roll = faceRect.angle;
if (roll < -90.0f) {
roll += 180.0f;
} else if (roll > 90.0f) {
roll -= 180.0f;
}
// roll is just the angle of the face rect
const float ROTATION_SMOOTHING = 0.95f;
_estimatedRotation.z = glm::mix(roll, _estimatedRotation.z, ROTATION_SMOOTHING);
_estimatedRotation.z = glm::mix(_faceRect.angle, _estimatedRotation.z, ROTATION_SMOOTHING);
// determine position based on translation and scaling of the face rect
if (_initialFaceRect.size.area() == 0) {
_initialFaceRect = faceRect;
_initialFaceRect = _faceRect;
_estimatedPosition = glm::vec3();
} else {
float proportion = sqrtf(_initialFaceRect.size.area() / (float)faceRect.size.area());
float proportion = sqrtf(_initialFaceRect.size.area() / (float)_faceRect.size.area());
const float DISTANCE_TO_CAMERA = 0.333f;
const float POSITION_SCALE = 0.5f;
float z = DISTANCE_TO_CAMERA * proportion - DISTANCE_TO_CAMERA;
glm::vec3 position = glm::vec3(
(faceRect.center.x - _initialFaceRect.center.x) * proportion * POSITION_SCALE / _frameWidth,
(faceRect.center.y - _initialFaceRect.center.y) * proportion * POSITION_SCALE / _frameWidth,
(_faceRect.center.x - _initialFaceRect.center.x) * proportion * POSITION_SCALE / _textureSize.width,
(_faceRect.center.y - _initialFaceRect.center.y) * proportion * POSITION_SCALE / _textureSize.width,
z);
const float POSITION_SMOOTHING = 0.95f;
_estimatedPosition = glm::mix(position, _estimatedPosition, POSITION_SMOOTHING);
@ -382,13 +394,13 @@ void FrameGrabber::grabFrame() {
return;
}
int format = GL_BGR;
Mat frame, depth;
Mat color, depth;
JointVector joints;
#ifdef HAVE_OPENNI
if (_depthGenerator.IsValid()) {
_xnContext.WaitAnyUpdateAll();
frame = Mat(_imageMetaData.YRes(), _imageMetaData.XRes(), CV_8UC3, (void*)_imageGenerator.GetImageMap());
color = Mat(_imageMetaData.YRes(), _imageMetaData.XRes(), CV_8UC3, (void*)_imageGenerator.GetImageMap());
format = GL_RGB;
depth = Mat(_depthMetaData.YRes(), _depthMetaData.XRes(), CV_16UC1, (void*)_depthGenerator.GetDepthMap());
@ -430,7 +442,7 @@ void FrameGrabber::grabFrame() {
}
#endif
if (frame.empty()) {
if (color.empty()) {
IplImage* image = cvQueryFrame(_capture);
if (image == 0) {
// try again later
@ -443,7 +455,7 @@ void FrameGrabber::grabFrame() {
printLog("Invalid webcam image format.\n");
return;
}
frame = image;
color = image;
}
// if we don't have a search window (yet), try using the face cascade
@ -452,10 +464,10 @@ void FrameGrabber::grabFrame() {
const float* range = ranges;
if (_searchWindow.area() == 0) {
vector<cv::Rect> faces;
_faceCascade.detectMultiScale(frame, faces, 1.1, 6);
_faceCascade.detectMultiScale(color, faces, 1.1, 6);
if (!faces.empty()) {
_searchWindow = faces.front();
updateHSVFrame(frame, format);
updateHSVFrame(color, format);
Mat faceHsv(_hsvFrame, _searchWindow);
Mat faceMask(_mask, _searchWindow);
@ -468,7 +480,7 @@ void FrameGrabber::grabFrame() {
}
RotatedRect faceRect;
if (_searchWindow.area() > 0) {
updateHSVFrame(frame, format);
updateHSVFrame(color, format);
calcBackProject(&_hsvFrame, 1, &channels, _histogram, _backProject, &range);
bitwise_and(_backProject, _mask, _backProject);
@ -477,7 +489,7 @@ void FrameGrabber::grabFrame() {
_searchWindow = faceRect.boundingRect();
}
QMetaObject::invokeMethod(Application::getInstance()->getWebcam(), "setFrame",
Q_ARG(cv::Mat, frame), Q_ARG(int, format), Q_ARG(cv::Mat, depth), Q_ARG(cv::Mat, _grayDepthFrame),
Q_ARG(cv::Mat, color), Q_ARG(int, format), Q_ARG(cv::Mat, depth), Q_ARG(cv::Mat, _grayDepthFrame),
Q_ARG(cv::RotatedRect, faceRect), Q_ARG(JointVector, joints));
}

View file

@ -42,7 +42,13 @@ public:
Webcam();
~Webcam();
const bool isActive() const { return _active; }
bool isActive() const { return _active; }
GLuint getColorTextureID() const { return _colorTextureID; }
GLuint getDepthTextureID() const { return _depthTextureID; }
const cv::Size2f& getTextureSize() const { return _textureSize; }
const cv::RotatedRect& getEstimatedFaceRect() const { return _estimatedFaceRect; }
const glm::vec3& getEstimatedPosition() const { return _estimatedPosition; }
const glm::vec3& getEstimatedRotation() const { return _estimatedRotation; }
const JointVector& getEstimatedJoints() const { return _estimatedJoints; }
@ -53,7 +59,7 @@ public:
public slots:
void setEnabled(bool enabled);
void setFrame(const cv::Mat& video, int format, const cv::Mat& depth,
void setFrame(const cv::Mat& color, int format, const cv::Mat& depth,
const cv::Mat& depthPreview, const cv::RotatedRect& faceRect, const JointVector& joints);
private:
@ -63,12 +69,9 @@ private:
bool _enabled;
bool _active;
int _frameWidth;
int _frameHeight;
int _depthWidth;
int _depthHeight;
GLuint _frameTextureID;
GLuint _colorTextureID;
GLuint _depthTextureID;
cv::Size2f _textureSize;
cv::RotatedRect _faceRect;
cv::RotatedRect _initialFaceRect;
JointVector _joints;
@ -78,6 +81,7 @@ private:
uint64_t _lastFrameTimestamp;
cv::RotatedRect _estimatedFaceRect;
glm::vec3 _estimatedPosition;
glm::vec3 _estimatedRotation;
JointVector _estimatedJoints;

View file

@ -16,12 +16,14 @@
#include "Hand.h"
#include "Head.h"
#include "Log.h"
#include "Physics.h"
#include "ui/TextRenderer.h"
#include <NodeList.h>
#include <NodeTypes.h>
#include <PacketHeaders.h>
#include <OculusManager.h>
using namespace std;
const bool BALLS_ON = false;
@ -94,6 +96,8 @@ Avatar::Avatar(Node* owningNode) :
_elapsedTimeMoving(0.0f),
_elapsedTimeStopped(0.0f),
_elapsedTimeSinceCollision(0.0f),
_speedBrakes(false),
_isThrustOn(false),
_voxels(this)
{
// give the pointer to our head to inherited _headData variable from AvatarData
@ -285,7 +289,10 @@ void Avatar::reset() {
}
// Update avatar head rotation with sensor data
void Avatar::updateFromGyrosAndOrWebcam(bool gyroLook, const glm::vec3& amplifyAngle) {
void Avatar::updateFromGyrosAndOrWebcam(bool gyroLook,
const glm::vec3& amplifyAngle,
float yawFromTouch,
float pitchFromTouch) {
SerialInterface* gyros = Application::getInstance()->getSerialHeadSensor();
Webcam* webcam = Application::getInstance()->getWebcam();
glm::vec3 estimatedPosition, estimatedRotation;
@ -296,6 +303,8 @@ void Avatar::updateFromGyrosAndOrWebcam(bool gyroLook, const glm::vec3& amplifyA
estimatedRotation = webcam->getEstimatedRotation();
} else {
_head.setPitch(pitchFromTouch);
_head.setYaw(yawFromTouch);
return;
}
if (webcam->isActive()) {
@ -316,8 +325,8 @@ void Avatar::updateFromGyrosAndOrWebcam(bool gyroLook, const glm::vec3& amplifyA
}
}
}
_head.setPitch(estimatedRotation.x * amplifyAngle.x);
_head.setYaw(estimatedRotation.y * amplifyAngle.y);
_head.setPitch(estimatedRotation.x * amplifyAngle.x + pitchFromTouch);
_head.setYaw(estimatedRotation.y * amplifyAngle.y + yawFromTouch);
_head.setRoll(estimatedRotation.z * amplifyAngle.z);
_head.setCameraFollowsHead(gyroLook);
@ -356,16 +365,16 @@ void Avatar::updateThrust(float deltaTime, Transmitter * transmitter) {
//
// Gather thrust information from keyboard and sensors to apply to avatar motion
//
glm::quat orientation = getOrientation();
glm::quat orientation = getHead().getOrientation();
glm::vec3 front = orientation * IDENTITY_FRONT;
glm::vec3 right = orientation * IDENTITY_RIGHT;
glm::vec3 up = orientation * IDENTITY_UP;
const float THRUST_MAG_UP = 800.0f;
const float THRUST_MAG_DOWN = 200.f;
const float THRUST_MAG_FWD = 300.f;
const float THRUST_MAG_BACK = 150.f;
const float THRUST_MAG_LATERAL = 200.f;
const float THRUST_MAG_DOWN = 300.f;
const float THRUST_MAG_FWD = 500.f;
const float THRUST_MAG_BACK = 300.f;
const float THRUST_MAG_LATERAL = 250.f;
const float THRUST_JUMP = 120.f;
// Add Thrusts from keyboard
@ -413,14 +422,27 @@ void Avatar::updateThrust(float deltaTime, Transmitter * transmitter) {
up;
}
}
}
// Update speed brake status
const float MIN_SPEED_BRAKE_VELOCITY = 0.4f;
if ((glm::length(_thrust) == 0.0f) && _isThrustOn && (glm::length(_velocity) > MIN_SPEED_BRAKE_VELOCITY)) {
_speedBrakes = true;
}
if (_speedBrakes && (glm::length(_velocity) < MIN_SPEED_BRAKE_VELOCITY)) {
_speedBrakes = false;
}
_isThrustOn = (glm::length(_thrust) > EPSILON);
}
void Avatar::simulate(float deltaTime, Transmitter* transmitter) {
glm::quat orientation = getOrientation();
glm::vec3 front = orientation * IDENTITY_FRONT;
glm::vec3 right = orientation * IDENTITY_RIGHT;
// Update movement timers
if (isMyAvatar()) {
_elapsedTimeSinceCollision += deltaTime;
@ -443,9 +465,6 @@ void Avatar::simulate(float deltaTime, Transmitter* transmitter) {
glm::vec3 oldVelocity = getVelocity();
if (isMyAvatar()) {
// update position by velocity
_position += _velocity * deltaTime;
// calculate speed
_speed = glm::length(_velocity);
}
@ -480,7 +499,7 @@ void Avatar::simulate(float deltaTime, Transmitter* transmitter) {
enableHandMovement &= (it->jointID != AVATAR_JOINT_RIGHT_WRIST);
}
// update avatar skeleton
// update avatar skeleton
_skeleton.update(deltaTime, getOrientation(), _position);
//determine the lengths of the body springs now that we have updated the skeleton at least once
@ -501,51 +520,49 @@ void Avatar::simulate(float deltaTime, Transmitter* transmitter) {
_ballSpringsInitialized = true;
}
// if this is not my avatar, then hand position comes from transmitted data
if (!isMyAvatar()) {
_skeleton.joint[ AVATAR_JOINT_RIGHT_FINGERTIPS ].position = _handPosition;
}
//detect and respond to collisions with other avatars...
if (isMyAvatar()) {
updateAvatarCollisions(deltaTime);
}
//update the movement of the hand and process handshaking with other avatars...
updateHandMovementAndTouching(deltaTime, enableHandMovement);
_avatarTouch.simulate(deltaTime);
// apply gravity and collision with the ground/floor
if (isMyAvatar() && USING_AVATAR_GRAVITY) {
_velocity += _gravity * (GRAVITY_EARTH * deltaTime);
}
if (isMyAvatar()) {
// apply gravity
if (USING_AVATAR_GRAVITY) {
// For gravity, always move the avatar by the amount driven by gravity, so that the collision
// routines will detect it and collide every frame when pulled by gravity to a surface
//
_velocity += _gravity * (GRAVITY_EARTH * deltaTime);
_position += _gravity * (GRAVITY_EARTH * deltaTime) * deltaTime;
}
updateCollisionWithEnvironment();
updateCollisionWithVoxels();
updateAvatarCollisions(deltaTime);
}
// update body balls
updateBodyBalls(deltaTime);
// test for avatar collision response with the big sphere
if (usingBigSphereCollisionTest) {
updateCollisionWithSphere(_TEST_bigSpherePosition, _TEST_bigSphereRadius, deltaTime);
}
// collision response with voxels
if (isMyAvatar()) {
updateCollisionWithVoxels();
}
if (isMyAvatar()) {
// add thrust to velocity
_velocity += _thrust * deltaTime;
// update body yaw by body yaw delta
orientation = orientation * glm::quat(glm::radians(
glm::vec3(_bodyPitchDelta, _bodyYawDelta, _bodyRollDelta) * deltaTime));
// decay body rotation momentum
float bodySpinMomentum = 1.0 - BODY_SPIN_FRICTION * deltaTime;
if (bodySpinMomentum < 0.0f) { bodySpinMomentum = 0.0f; }
@ -553,22 +570,19 @@ void Avatar::simulate(float deltaTime, Transmitter* transmitter) {
_bodyYawDelta *= bodySpinMomentum;
_bodyRollDelta *= bodySpinMomentum;
// Decay velocity. If velocity is really low, increase decay to simulate static friction
const float VELOCITY_DECAY_UNDER_THRUST = 0.2;
const float VELOCITY_FAST_DECAY = 0.6;
const float VELOCITY_SLOW_DECAY = 3.0;
const float VELOCITY_FAST_THRESHOLD = 2.0f;
float decayConstant, decay;
if (glm::length(_thrust) > 0.f) {
decayConstant = VELOCITY_DECAY_UNDER_THRUST;
} else if (glm::length(_velocity) > VELOCITY_FAST_THRESHOLD) {
decayConstant = VELOCITY_FAST_DECAY;
const float MAX_STATIC_FRICTION_VELOCITY = 0.5f;
const float STATIC_FRICTION_STRENGTH = 20.f;
applyStaticFriction(deltaTime, _velocity, MAX_STATIC_FRICTION_VELOCITY, STATIC_FRICTION_STRENGTH);
const float LINEAR_DAMPING_STRENGTH = 1.0f;
const float SPEED_BRAKE_POWER = 10.0f;
const float SQUARED_DAMPING_STRENGTH = 0.2f;
if (_speedBrakes) {
applyDamping(deltaTime, _velocity, LINEAR_DAMPING_STRENGTH * SPEED_BRAKE_POWER, SQUARED_DAMPING_STRENGTH * SPEED_BRAKE_POWER);
} else {
decayConstant = VELOCITY_SLOW_DECAY;
}
decay = glm::clamp(1.0f - decayConstant * deltaTime, 0.0f, 1.0f);
_velocity *= decay;
applyDamping(deltaTime, _velocity, LINEAR_DAMPING_STRENGTH, SQUARED_DAMPING_STRENGTH);
}
//pitch and roll the body as a function of forward speed and turning delta
const float BODY_PITCH_WHILE_WALKING = -20.0;
const float BODY_ROLL_WHILE_TURNING = 0.2;
@ -659,6 +673,9 @@ void Avatar::simulate(float deltaTime, Transmitter* transmitter) {
_mode = AVATAR_MODE_INTERACTING;
}
// update position by velocity, and subtract the change added earlier for gravity
_position += _velocity * deltaTime;
// Zero thrust out now that we've added it to velocity in this frame
_thrust = glm::vec3(0, 0, 0);

View file

@ -87,7 +87,10 @@ public:
void reset();
void simulate(float deltaTime, Transmitter* transmitter);
void updateThrust(float deltaTime, Transmitter * transmitter);
void updateFromGyrosAndOrWebcam(bool gyroLook, const glm::vec3& amplifyAngles);
void updateFromGyrosAndOrWebcam(bool gyroLook,
const glm::vec3& amplifyAngle,
float yawFromTouch,
float pitchFromTouch);
void addBodyYaw(float y) {_bodyYaw += y;};
void render(bool lookingInMirror, bool renderAvatarBalls);
@ -210,6 +213,8 @@ private:
float _elapsedTimeMoving; // Timers to drive camera transitions when moving
float _elapsedTimeStopped;
float _elapsedTimeSinceCollision;
bool _speedBrakes;
bool _isThrustOn;
AvatarVoxelSystem _voxels;

View file

@ -54,7 +54,7 @@ void Hand::calculateGeometry() {
_position = head.getPosition() + head.getOrientation() * offset;
_orientation = head.getOrientation();
int numLeapBalls = _fingerTips.size() + _fingerRoots.size();
int numLeapBalls = _fingerTips.size();
_leapBalls.resize(numLeapBalls);
for (int i = 0; i < _fingerTips.size(); ++i) {

View file

@ -82,7 +82,8 @@ Head::Head(Avatar* owningAvatar) :
_cameraYaw(_yaw),
_isCameraMoving(false),
_cameraFollowsHead(false),
_cameraFollowHeadRate(0.0f)
_cameraFollowHeadRate(0.0f),
_face(this)
{
if (USING_PHYSICAL_MOHAWK) {
resetHairPhysics();
@ -289,17 +290,19 @@ void Head::render(float alpha) {
_renderAlpha = alpha;
calculateGeometry();
if (!_face.render(alpha)) {
calculateGeometry();
glEnable(GL_DEPTH_TEST);
glEnable(GL_RESCALE_NORMAL);
glEnable(GL_DEPTH_TEST);
glEnable(GL_RESCALE_NORMAL);
renderMohawk();
renderHeadSphere();
renderEyeBalls();
renderEars();
renderMouth();
renderEyeBrows();
renderMohawk();
renderHeadSphere();
renderEyeBalls();
renderEars();
renderMouth();
renderEyeBrows();
}
if (_renderLookatVectors) {
renderLookatVectors(_leftEyePosition, _rightEyePosition, _lookAtPosition);

View file

@ -10,11 +10,17 @@
#include <glm/glm.hpp>
#include <glm/gtc/quaternion.hpp>
#include <SharedUtil.h>
#include <AvatarData.h>
#include "world.h"
#include <VoxelConstants.h>
#include "Face.h"
#include "InterfaceConfig.h"
#include "SerialInterface.h"
#include <SharedUtil.h>
#include "world.h"
enum eyeContactTargets
{
@ -53,12 +59,15 @@ public:
glm::quat getOrientation() const;
glm::quat getCameraOrientation () const;
float getScale() const { return _scale; }
glm::vec3 getPosition() const { return _position; }
const glm::vec3& getEyeLevelPosition() const { return _eyeLevelPosition; }
glm::vec3 getRightDirection() const { return getOrientation() * IDENTITY_RIGHT; }
glm::vec3 getUpDirection () const { return getOrientation() * IDENTITY_UP; }
glm::vec3 getFrontDirection() const { return getOrientation() * IDENTITY_FRONT; }
Face& getFace() { return _face; }
const bool getReturnToCenter() const { return _returnHeadToCenter; } // Do you want head to try to return to center (depends on interface detected)
float getAverageLoudness() {return _averageLoudness;};
glm::vec3 calculateAverageEyePosition() { return _leftEyePosition + (_rightEyePosition - _leftEyePosition ) * ONE_HALF; }
@ -120,6 +129,7 @@ private:
bool _isCameraMoving;
bool _cameraFollowsHead;
float _cameraFollowHeadRate;
Face _face;
static ProgramObject* _irisProgram;
static GLuint _irisTextureID;

View file

@ -137,6 +137,11 @@ int AvatarData::getBroadcastData(unsigned char* destinationBuffer) {
if (numFingerVectors > 255)
numFingerVectors = 0; // safety. We shouldn't ever get over 255, so consider that invalid.
/////////////////////////////////
// Temporarily disable Leap finger sending, as it's causing a crash whenever someone's got a Leap connected
numFingerVectors = 0;
/////////////////////////////////
*destinationBuffer++ = (unsigned char)numFingerVectors;
if (numFingerVectors > 0) {
@ -255,8 +260,8 @@ int AvatarData::parseData(unsigned char* sourceBuffer, int numBytes) {
// leap hand data
if (sourceBuffer - startPosition < numBytes) // safety check
{
std::vector<glm::vec3> fingerTips = _handData->getFingerTips();
std::vector<glm::vec3> fingerRoots = _handData->getFingerRoots();
std::vector<glm::vec3> fingerTips;
std::vector<glm::vec3> fingerRoots;
unsigned int numFingerVectors = *sourceBuffer++;
unsigned int numFingerTips = numFingerVectors / 2;
unsigned int numFingerRoots = numFingerVectors - numFingerTips;
@ -267,6 +272,11 @@ int AvatarData::parseData(unsigned char* sourceBuffer, int numBytes) {
sourceBuffer += unpackFloatScalarFromSignedTwoByteFixed((int16_t*) sourceBuffer, &(fingerTips[i].y), 4);
sourceBuffer += unpackFloatScalarFromSignedTwoByteFixed((int16_t*) sourceBuffer, &(fingerTips[i].z), 4);
}
for (size_t i = 0; i < numFingerRoots; ++i) {
sourceBuffer += unpackFloatScalarFromSignedTwoByteFixed((int16_t*) sourceBuffer, &(fingerRoots[i].x), 4);
sourceBuffer += unpackFloatScalarFromSignedTwoByteFixed((int16_t*) sourceBuffer, &(fingerRoots[i].y), 4);
sourceBuffer += unpackFloatScalarFromSignedTwoByteFixed((int16_t*) sourceBuffer, &(fingerRoots[i].z), 4);
}
_handData->setFingerTips(fingerTips);
_handData->setFingerRoots(fingerRoots);
}

View file

@ -20,6 +20,8 @@ const glm::vec3 IDENTITY_RIGHT = glm::vec3( 1.0f, 0.0f, 0.0f);
const glm::vec3 IDENTITY_UP = glm::vec3( 0.0f, 1.0f, 0.0f);
const glm::vec3 IDENTITY_FRONT = glm::vec3( 0.0f, 0.0f,-1.0f);
const bool LOW_RES_MONO = false; // while in "low res mode" do voxels switch to monochrome
const int TREE_SCALE = 128;
const int NUMBER_OF_CHILDREN = 8;

View file

@ -1346,33 +1346,13 @@ int VoxelTree::encodeTreeBitstreamRecursion(VoxelNode* node, unsigned char* outp
// There are two types of nodes for which we want to send colors:
// 1) Leaves - obviously
// 2) Non-leaves who's children would be visible and beyond our LOD.
// NOTE: This code works, but it's pretty expensive, because we're calculating distances for all the grand
// children, which we'll end up doing again later in the next level of recursion. We need to optimize this
// in the future.
// 2) Non-leaves who's children would be visible but are beyond our LOD.
bool isLeafOrLOD = childNode->isLeaf();
if (params.viewFrustum && childNode->isColored() && !childNode->isLeaf()) {
int grandChildrenInView = 0;
int grandChildrenInLOD = 0;
float grandChildBoundaryDistance = boundaryDistanceForRenderLevel(childNode->getLevel() +
1 + params.boundaryLevelAdjust);
for (int grandChildIndex = 0; grandChildIndex < NUMBER_OF_CHILDREN; grandChildIndex++) {
VoxelNode* grandChild = childNode->getChildAtIndex(grandChildIndex);
if (grandChild && grandChild->isColored() && grandChild->isInView(*params.viewFrustum)) {
grandChildrenInView++;
float grandChildDistance = grandChild->distanceToCamera(*params.viewFrustum);
if (grandChildDistance < grandChildBoundaryDistance) {
grandChildrenInLOD++;
}
}
}
// if any of our grandchildren ARE in view, then we don't want to include our color. If none are, then
// we do want to include our color
if (grandChildrenInView > 0 && grandChildrenInLOD == 0) {
isLeafOrLOD = true;
}
int childLevel = childNode->getLevel();
float childBoundary = boundaryDistanceForRenderLevel(childLevel + params.boundaryLevelAdjust);
float grandChildBoundary = boundaryDistanceForRenderLevel(childLevel + 1 + params.boundaryLevelAdjust);
isLeafOrLOD = ((distance <= childBoundary) && !(distance <= grandChildBoundary));
}
// track children with actual color, only if the child wasn't previously in view!

View file

@ -31,7 +31,7 @@ VoxelNodeData::VoxelNodeData(Node* owningNode) :
void VoxelNodeData::resetVoxelPacket() {
// If we're moving, and the client asked for low res, then we force monochrome, otherwise, use
// the clients requested color state.
_currentPacketIsColor = (getWantLowResMoving() && _viewFrustumChanging) ? false : getWantColor();
_currentPacketIsColor = (LOW_RES_MONO && getWantLowResMoving() && _viewFrustumChanging) ? false : getWantColor();
PACKET_TYPE voxelPacketType = _currentPacketIsColor ? PACKET_TYPE_VOXEL_DATA : PACKET_TYPE_VOXEL_DATA_MONOCHROME;
int numBytesPacketHeader = populateTypeAndVersion(_voxelPacket, voxelPacketType);
_voxelPacketAt = _voxelPacket + numBytesPacketHeader;

View file

@ -133,7 +133,7 @@ void deepestLevelVoxelDistributor(NodeList* nodeList,
// If we're starting a fresh packet, then...
// If we're moving, and the client asked for low res, then we force monochrome, otherwise, use
// the clients requested color state.
bool wantColor = ((nodeData->getWantLowResMoving() && viewFrustumChanged) ? false : nodeData->getWantColor());
bool wantColor = LOW_RES_MONO && nodeData->getWantLowResMoving() && viewFrustumChanged ? false : nodeData->getWantColor();
// If we have a packet waiting, and our desired want color, doesn't match the current waiting packets color
// then let's just send that waiting packet.