mirror of
https://github.com/JulianGro/overte.git
synced 2025-04-25 17:14:59 +02:00
Merge branch 'master' of github.com:worklist/hifi into assignee
This commit is contained in:
commit
955b7f0d24
36 changed files with 1418 additions and 493 deletions
|
@ -36,6 +36,7 @@ bool includeBorderTracer = true;
|
|||
bool includeMovingBug = true;
|
||||
bool includeBlinkingVoxel = false;
|
||||
bool includeDanceFloor = true;
|
||||
bool buildStreet = false;
|
||||
|
||||
|
||||
const int ANIMATION_LISTEN_PORT = 40107;
|
||||
|
@ -616,6 +617,61 @@ static void sendBillboard() {
|
|||
}
|
||||
}
|
||||
|
||||
bool roadInitialized = false;
|
||||
const int ROAD_WIDTH_METERS = 3.0f;
|
||||
const int BRICKS_ACROSS_ROAD = 32;
|
||||
const float ROAD_BRICK_SIZE = 0.125f/TREE_SCALE; //(ROAD_WIDTH_METERS / TREE_SCALE) / BRICKS_ACROSS_ROAD; // in voxel units
|
||||
const int ROAD_LENGTH = 1.0f / ROAD_BRICK_SIZE; // in bricks
|
||||
const int ROAD_WIDTH = BRICKS_ACROSS_ROAD; // in bricks
|
||||
glm::vec3 roadPosition(0.5f - (ROAD_BRICK_SIZE * BRICKS_ACROSS_ROAD), 0.0f, 0.0f);
|
||||
const int BRICKS_PER_PACKET = 32; // guessing
|
||||
const int PACKETS_PER_ROAD = VOXELS_PER_PACKET / (ROAD_LENGTH * ROAD_WIDTH);
|
||||
|
||||
void doBuildStreet() {
|
||||
if (roadInitialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
PACKET_TYPE message = PACKET_TYPE_SET_VOXEL_DESTRUCTIVE; // we're a bully!
|
||||
static VoxelDetail details[BRICKS_PER_PACKET];
|
||||
unsigned char* bufferOut;
|
||||
int sizeOut;
|
||||
|
||||
for (int z = 0; z < ROAD_LENGTH; z++) {
|
||||
for (int x = 0; x < ROAD_WIDTH; x++) {
|
||||
|
||||
int nthVoxel = ((z * ROAD_WIDTH) + x);
|
||||
int item = nthVoxel % BRICKS_PER_PACKET;
|
||||
|
||||
glm::vec3 brick = roadPosition + glm::vec3(x * ROAD_BRICK_SIZE, 0, z * ROAD_BRICK_SIZE);
|
||||
|
||||
details[item].s = ROAD_BRICK_SIZE;
|
||||
details[item].x = brick.x;
|
||||
details[item].y = brick.y;
|
||||
details[item].z = brick.z;
|
||||
|
||||
unsigned char randomTone = randIntInRange(118,138);
|
||||
details[item].red = randomTone;
|
||||
details[item].green = randomTone;
|
||||
details[item].blue = randomTone;
|
||||
|
||||
if (item == BRICKS_PER_PACKET - 1) {
|
||||
if (createVoxelEditMessage(message, 0, BRICKS_PER_PACKET, (VoxelDetail*)&details, bufferOut, sizeOut)){
|
||||
::packetsSent++;
|
||||
::bytesSent += sizeOut;
|
||||
if (true || ::shouldShowPacketsPerSecond) {
|
||||
printf("building road sending packet of size=%d\n", sizeOut);
|
||||
}
|
||||
NodeList::getInstance()->broadcastToNodes(bufferOut, sizeOut, &NODE_TYPE_VOXEL_SERVER, 1);
|
||||
delete[] bufferOut;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
roadInitialized = true;
|
||||
}
|
||||
|
||||
|
||||
double start = 0;
|
||||
|
||||
|
||||
|
@ -645,6 +701,10 @@ void* animateVoxels(void* args) {
|
|||
sendDanceFloor();
|
||||
}
|
||||
|
||||
if (::buildStreet) {
|
||||
doBuildStreet();
|
||||
}
|
||||
|
||||
uint64_t end = usecTimestampNow();
|
||||
uint64_t elapsedSeconds = (end - ::start) / 1000000;
|
||||
if (::shouldShowPacketsPerSecond) {
|
||||
|
@ -688,6 +748,9 @@ int main(int argc, const char * argv[])
|
|||
const char* NO_DANCE_FLOOR = "--NoDanceFloor";
|
||||
::includeDanceFloor = !cmdOptionExists(argc, argv, NO_DANCE_FLOOR);
|
||||
|
||||
const char* BUILD_STREET = "--BuildStreet";
|
||||
::buildStreet = cmdOptionExists(argc, argv, BUILD_STREET);
|
||||
|
||||
// Handle Local Domain testing with the --local command line
|
||||
const char* showPPS = "--showPPS";
|
||||
::shouldShowPacketsPerSecond = cmdOptionExists(argc, argv, showPPS);
|
||||
|
@ -700,6 +763,11 @@ int main(int argc, const char * argv[])
|
|||
nodeList->setDomainIPToLocalhost();
|
||||
}
|
||||
|
||||
const char* domainIP = getCmdOption(argc, argv, "--domain");
|
||||
if (domainIP) {
|
||||
NodeList::getInstance()->setDomainIP(domainIP);
|
||||
}
|
||||
|
||||
nodeList->linkedDataCreateCallback = NULL; // do we need a callback?
|
||||
nodeList->startSilentNodeRemovalThread();
|
||||
|
||||
|
|
83
interface/resources/shaders/face_textured.frag
Normal file
83
interface/resources/shaders/face_textured.frag
Normal file
|
@ -0,0 +1,83 @@
|
|||
#version 120
|
||||
|
||||
//
|
||||
// face_textured.frag
|
||||
// fragment shader
|
||||
//
|
||||
// Created by Andrzej Kapolka on 8/6/13.
|
||||
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
// the texture coordinate vector from left to right
|
||||
uniform vec2 texCoordRight;
|
||||
|
||||
// the texture coordinate vector from bottom to the top
|
||||
uniform vec2 texCoordUp;
|
||||
|
||||
// the permutation/normal texture
|
||||
uniform sampler2D permutationNormalTexture;
|
||||
|
||||
// the depth texture
|
||||
uniform sampler2D depthTexture;
|
||||
|
||||
// the position in model space
|
||||
varying vec3 position;
|
||||
|
||||
// returns the gradient at a single corner of our sampling cube
|
||||
vec3 grad(vec3 location) {
|
||||
float p1 = texture2D(permutationNormalTexture, vec2(location.x / 256.0, 0.25)).r;
|
||||
float p2 = texture2D(permutationNormalTexture, vec2(p1 + location.y / 256.0, 0.25)).r;
|
||||
return texture2D(permutationNormalTexture, vec2(p2 + location.z / 256.0, 0.75)).xyz * 2.0 - vec3(1.0, 1.0, 1.0);
|
||||
}
|
||||
|
||||
// returns the perlin noise value for the specified location
|
||||
float perlin(vec3 location) {
|
||||
vec3 floors = floor(location);
|
||||
vec3 ceils = ceil(location);
|
||||
vec3 fff = grad(floors);
|
||||
vec3 ffc = grad(vec3(floors.x, floors.y, ceils.z));
|
||||
vec3 fcf = grad(vec3(floors.x, ceils.y, floors.z));
|
||||
vec3 fcc = grad(vec3(floors.x, ceils.y, ceils.z));
|
||||
vec3 cff = grad(vec3(ceils.x, floors.y, floors.z));
|
||||
vec3 cfc = grad(vec3(ceils.x, floors.y, ceils.z));
|
||||
vec3 ccf = grad(vec3(ceils.x, ceils.y, floors.z));
|
||||
vec3 ccc = grad(ceils);
|
||||
vec3 ffracts = fract(location);
|
||||
vec3 cfracts = ffracts - vec3(1.0, 1.0, 1.0);
|
||||
vec3 params = ffracts*ffracts*(3.0 - 2.0*ffracts);
|
||||
|
||||
float fffv = dot(fff, ffracts);
|
||||
float ffcv = dot(ffc, vec3(ffracts.x, ffracts.y, cfracts.z));
|
||||
float fcfv = dot(fcf, vec3(ffracts.x, cfracts.y, ffracts.z));
|
||||
float fccv = dot(fcc, vec3(ffracts.x, cfracts.y, cfracts.z));
|
||||
float cffv = dot(cff, vec3(cfracts.x, ffracts.y, ffracts.z));
|
||||
float cfcv = dot(cfc, vec3(cfracts.x, ffracts.y, cfracts.z));
|
||||
float ccfv = dot(ccf, vec3(cfracts.x, cfracts.y, ffracts.z));
|
||||
float cccv = dot(ccc, cfracts);
|
||||
|
||||
return mix(
|
||||
mix(mix(fffv, cffv, params.x), mix(fcfv, ccfv, params.x), params.y),
|
||||
mix(mix(ffcv, cfcv, params.x), mix(fccv, cccv, params.x), params.y),
|
||||
params.z);
|
||||
}
|
||||
|
||||
void main(void) {
|
||||
// compute normal from adjacent depth values
|
||||
float left = texture2D(depthTexture, gl_TexCoord[0].st - texCoordRight * 0.01).r;
|
||||
float right = texture2D(depthTexture, gl_TexCoord[0].st + texCoordRight * 0.01).r;
|
||||
float bottom = texture2D(depthTexture, gl_TexCoord[0].st - texCoordUp * 0.01).r;
|
||||
float top = texture2D(depthTexture, gl_TexCoord[0].st + texCoordUp * 0.01).r;
|
||||
vec3 normal = normalize(gl_NormalMatrix * vec3(left - right, top - bottom, -0.05));
|
||||
|
||||
// compute the specular component (sans exponent) based on the normal OpenGL lighting model
|
||||
float specular = max(0.0, dot(normalize(gl_LightSource[0].position.xyz + vec3(0.0, 0.0, 1.0)), normal));
|
||||
|
||||
// the base color is a subtle marble texture produced by modulating the phase of a sine wave by perlin noise
|
||||
vec3 color = mix(vec3(1.0, 1.0, 1.0), vec3(0.75, 0.75, 0.75),
|
||||
sin(dot(position, vec3(25.0, 25.0, 25.0)) + 2.0 * perlin(position * 10.0)));
|
||||
|
||||
// standard lighting
|
||||
gl_FragColor = vec4(color * ( gl_LightModel.ambient.rgb + /* gl_LightSource[0].ambient.rgb + */
|
||||
gl_LightSource[0].diffuse.rgb * max(0.0, dot(normal, gl_LightSource[0].position.xyz))) +
|
||||
pow(specular, gl_FrontMaterial.shininess) * gl_FrontLightProduct[0].specular.rgb, gl_Color.a);
|
||||
}
|
38
interface/resources/shaders/face_textured.vert
Normal file
38
interface/resources/shaders/face_textured.vert
Normal file
|
@ -0,0 +1,38 @@
|
|||
#version 120
|
||||
|
||||
//
|
||||
// face_textured.vert
|
||||
// vertex shader
|
||||
//
|
||||
// Created by Andrzej Kapolka on 8/6/13.
|
||||
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
// the lower left texture coordinate
|
||||
uniform vec2 texCoordCorner;
|
||||
|
||||
// the texture coordinate vector from left to right
|
||||
uniform vec2 texCoordRight;
|
||||
|
||||
// the texture coordinate vector from bottom to the top
|
||||
uniform vec2 texCoordUp;
|
||||
|
||||
// the depth texture
|
||||
uniform sampler2D depthTexture;
|
||||
|
||||
// the position in model space
|
||||
varying vec3 position;
|
||||
|
||||
void main(void) {
|
||||
gl_TexCoord[0] = vec4(texCoordCorner + gl_Vertex.x * texCoordRight + gl_Vertex.y * texCoordUp, 0.0, 1.0);
|
||||
float depth = texture2D(depthTexture, gl_TexCoord[0].st).r;
|
||||
|
||||
// store the model space vertex
|
||||
position = gl_Vertex.xyz;
|
||||
|
||||
// set alpha to zero for invalid depth values
|
||||
const float MIN_VISIBLE_DEPTH = 1.0 / 255.0;
|
||||
const float MAX_VISIBLE_DEPTH = 254.0 / 255.0;
|
||||
gl_FrontColor = vec4(1.0, 1.0, 1.0, step(MIN_VISIBLE_DEPTH, depth) * (1.0 - step(MAX_VISIBLE_DEPTH, depth)));
|
||||
gl_Position = gl_ModelViewProjectionMatrix * vec4(0.5 - gl_Vertex.x, gl_Vertex.y - 0.5, depth - 0.5, 1.0);
|
||||
}
|
|
@ -77,8 +77,10 @@ static char STAR_CACHE_FILE[] = "cachedStars.txt";
|
|||
|
||||
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
|
||||
// (will be overwritten if avatar data file is found)
|
||||
// Where one's own Avatar begins in the world (will be overwritten if avatar data file is found)
|
||||
// this is basically in the center of the ground plane. Slightly adjusted. This was asked for by
|
||||
// Grayson as he's building a street around here for demo dinner 2
|
||||
const glm::vec3 START_LOCATION(0.485f * TREE_SCALE, 0.f, 0.5f * TREE_SCALE);
|
||||
|
||||
const int IDLE_SIMULATE_MSECS = 16; // How often should call simulate and other stuff
|
||||
// in the idle loop? (60 FPS is default)
|
||||
|
@ -234,6 +236,10 @@ Application::Application(int& argc, char** argv, timeval &startup_time) :
|
|||
|
||||
NodeList::createInstance(NODE_TYPE_AGENT, listenPort);
|
||||
|
||||
NodeList::getInstance()->addHook(&_voxels);
|
||||
NodeList::getInstance()->addHook(this);
|
||||
|
||||
|
||||
_enableNetworkThread = !cmdOptionExists(argc, constArgv, "--nonblocking");
|
||||
if (!_enableNetworkThread) {
|
||||
NodeList::getInstance()->getNodeSocket()->setBlocking(false);
|
||||
|
@ -317,6 +323,11 @@ Application::Application(int& argc, char** argv, timeval &startup_time) :
|
|||
// initialization continues in initializeGL when OpenGL context is ready
|
||||
}
|
||||
|
||||
Application::~Application() {
|
||||
NodeList::getInstance()->removeHook(&_voxels);
|
||||
NodeList::getInstance()->removeHook(this);
|
||||
}
|
||||
|
||||
void Application::initializeGL() {
|
||||
qDebug( "Created Display Window.\n" );
|
||||
|
||||
|
@ -1290,9 +1301,9 @@ void Application::editPreferences() {
|
|||
horizontalFieldOfView->setValue(_horizontalFieldOfView);
|
||||
form->addRow("Horizontal field of view (degrees):", horizontalFieldOfView);
|
||||
|
||||
QDoubleSpinBox* headCameraPitchYawScale = new QDoubleSpinBox();
|
||||
headCameraPitchYawScale->setValue(_headCameraPitchYawScale);
|
||||
form->addRow("Head Camera Pitch/Yaw Scale:", headCameraPitchYawScale);
|
||||
QDoubleSpinBox* gyroCameraSensitivity = new QDoubleSpinBox();
|
||||
gyroCameraSensitivity->setValue(_gyroCameraSensitivity);
|
||||
form->addRow("Gyro Camera Sensitivity (0 - 1):", gyroCameraSensitivity);
|
||||
|
||||
QDoubleSpinBox* leanScale = new QDoubleSpinBox();
|
||||
leanScale->setValue(_myAvatar.getLeanScale());
|
||||
|
@ -1342,7 +1353,7 @@ void Application::editPreferences() {
|
|||
_myAvatar.getVoxels()->setVoxelURL(url);
|
||||
sendAvatarVoxelURLMessage(url);
|
||||
|
||||
_headCameraPitchYawScale = headCameraPitchYawScale->value();
|
||||
_gyroCameraSensitivity = gyroCameraSensitivity->value();
|
||||
_myAvatar.setLeanScale(leanScale->value());
|
||||
_audioJitterBufferSamples = audioJitterBufferSamples->value();
|
||||
if (!shouldDynamicallySetJitterBuffer()) {
|
||||
|
@ -1943,8 +1954,11 @@ void Application::initMenu() {
|
|||
_testPing->setChecked(true);
|
||||
(_fullScreenMode = optionsMenu->addAction("Fullscreen", this, SLOT(setFullscreen(bool)), Qt::Key_F))->setCheckable(true);
|
||||
optionsMenu->addAction("Webcam", &_webcam, SLOT(setEnabled(bool)))->setCheckable(true);
|
||||
optionsMenu->addAction("Toggle Skeleton Tracking", &_webcam, SLOT(setSkeletonTrackingOn(bool)))->setCheckable(true);
|
||||
optionsMenu->addAction("Skeleton Tracking", &_webcam, SLOT(setSkeletonTrackingOn(bool)))->setCheckable(true);
|
||||
(_wantCollisionsOn = optionsMenu->addAction("Turn collisions On", this, SLOT(toggleWantCollisionsOn())))->setCheckable(true);
|
||||
_wantCollisionsOn->setChecked(true);
|
||||
optionsMenu->addAction("Cycle Webcam Send Mode", _webcam.getGrabber(), SLOT(cycleVideoSendMode()));
|
||||
optionsMenu->addAction("Webcam Texture", _webcam.getGrabber(), SLOT(setDepthOnly(bool)))->setCheckable(true);
|
||||
optionsMenu->addAction("Go Home", this, SLOT(goHome()), Qt::CTRL | Qt::Key_G);
|
||||
|
||||
QMenu* audioMenu = menuBar->addMenu("Audio");
|
||||
|
@ -2137,6 +2151,10 @@ void Application::toggleMixedSong() {
|
|||
}
|
||||
}
|
||||
|
||||
void Application::toggleWantCollisionsOn() {
|
||||
_myAvatar.setWantCollisionsOn(_wantCollisionsOn->isChecked());
|
||||
}
|
||||
|
||||
void Application::resetSongMixMenuItem() {
|
||||
if (_audio.getSongFileBytes() == 0) {
|
||||
_rawAudioMicrophoneMix->setText("Mix RAW Song");
|
||||
|
@ -2287,6 +2305,56 @@ void Application::renderLookatIndicator(glm::vec3 pointOfInterest, Camera& which
|
|||
renderCircle(haloOrigin, INDICATOR_RADIUS, IDENTITY_UP, NUM_SEGMENTS);
|
||||
}
|
||||
|
||||
void Application::renderFollowIndicator() {
|
||||
NodeList* nodeList = NodeList::getInstance();
|
||||
|
||||
glLineWidth(5);
|
||||
glBegin(GL_LINES);
|
||||
for (NodeList::iterator node = nodeList->begin(); node != nodeList->end(); ++node) {
|
||||
if (node->getLinkedData() != NULL && node->getType() == NODE_TYPE_AGENT) {
|
||||
Avatar* avatar = (Avatar *) node->getLinkedData();
|
||||
Avatar* leader = NULL;
|
||||
|
||||
if (avatar->getLeaderID() != UNKNOWN_NODE_ID) {
|
||||
if (avatar->getLeaderID() == NodeList::getInstance()->getOwnerID()) {
|
||||
leader = &_myAvatar;
|
||||
} else {
|
||||
for (NodeList::iterator it = nodeList->begin(); it != nodeList->end(); ++it) {
|
||||
if(it->getNodeID() == avatar->getLeaderID()
|
||||
&& it->getType() == NODE_TYPE_AGENT) {
|
||||
leader = (Avatar*) it->getLinkedData();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (leader != NULL) {
|
||||
glColor3f(1.f, 0.f, 0.f);
|
||||
glVertex3f(avatar->getPosition().x,
|
||||
avatar->getPosition().y,
|
||||
avatar->getPosition().z);
|
||||
glColor3f(0.f, 1.f, 0.f);
|
||||
glVertex3f(leader->getPosition().x,
|
||||
leader->getPosition().y,
|
||||
leader->getPosition().z);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (_myAvatar.getLeadingAvatar() != NULL) {
|
||||
glColor3f(1.f, 0.f, 0.f);
|
||||
glVertex3f(_myAvatar.getPosition().x,
|
||||
_myAvatar.getPosition().y,
|
||||
_myAvatar.getPosition().z);
|
||||
glColor3f(0.f, 1.f, 0.f);
|
||||
glVertex3f(_myAvatar.getLeadingAvatar()->getPosition().x,
|
||||
_myAvatar.getLeadingAvatar()->getPosition().y,
|
||||
_myAvatar.getLeadingAvatar()->getPosition().z);
|
||||
}
|
||||
|
||||
glEnd();
|
||||
}
|
||||
|
||||
void Application::update(float deltaTime) {
|
||||
|
||||
// Use Transmitter Hand to move hand if connected, else use mouse
|
||||
|
@ -2500,7 +2568,7 @@ void Application::update(float deltaTime) {
|
|||
if (!avatar->isInitialized()) {
|
||||
avatar->init();
|
||||
}
|
||||
avatar->simulate(deltaTime, NULL);
|
||||
avatar->simulate(deltaTime, NULL, 0.f);
|
||||
avatar->setMouseRay(mouseRayOrigin, mouseRayDirection);
|
||||
}
|
||||
node->unlock();
|
||||
|
@ -2515,13 +2583,11 @@ void Application::update(float deltaTime) {
|
|||
}
|
||||
|
||||
if (_transmitterDrives->isChecked() && _myTransmitter.isConnected()) {
|
||||
_myAvatar.simulate(deltaTime, &_myTransmitter);
|
||||
_myAvatar.simulate(deltaTime, &_myTransmitter, _gyroCameraSensitivity);
|
||||
} else {
|
||||
_myAvatar.simulate(deltaTime, NULL);
|
||||
_myAvatar.simulate(deltaTime, NULL, _gyroCameraSensitivity);
|
||||
}
|
||||
|
||||
_myAvatar.getHand().simulate(deltaTime, true);
|
||||
|
||||
if (!OculusManager::isConnected()) {
|
||||
if (_lookingInMirror->isChecked()) {
|
||||
if (_myCamera.getMode() != CAMERA_MODE_MIRROR) {
|
||||
|
@ -2574,7 +2640,7 @@ void Application::update(float deltaTime) {
|
|||
|
||||
if (_renderParticleSystemOn->isChecked()) {
|
||||
updateParticleSystem(deltaTime);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Application::updateAvatar(float deltaTime) {
|
||||
|
@ -2586,10 +2652,6 @@ void Application::updateAvatar(float deltaTime) {
|
|||
|
||||
// Update my avatar's state from gyros and/or webcam
|
||||
_myAvatar.updateFromGyrosAndOrWebcam(_gyroLook->isChecked(),
|
||||
glm::vec3(_headCameraPitchYawScale,
|
||||
_headCameraPitchYawScale,
|
||||
_headCameraPitchYawScale),
|
||||
0.f,
|
||||
_pitchFromTouch);
|
||||
|
||||
if (_serialHeadSensor.isActive()) {
|
||||
|
@ -2876,6 +2938,27 @@ void Application::displayOculus(Camera& whichCamera) {
|
|||
glPopMatrix();
|
||||
}
|
||||
|
||||
void Application::setupWorldLight(Camera& whichCamera) {
|
||||
|
||||
// Setup 3D lights (after the camera transform, so that they are positioned in world space)
|
||||
glEnable(GL_COLOR_MATERIAL);
|
||||
glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE);
|
||||
|
||||
glm::vec3 relativeSunLoc = glm::normalize(_environment.getClosestData(whichCamera.getPosition()).getSunLocation() -
|
||||
whichCamera.getPosition());
|
||||
GLfloat light_position0[] = { relativeSunLoc.x, relativeSunLoc.y, relativeSunLoc.z, 0.0 };
|
||||
glLightfv(GL_LIGHT0, GL_POSITION, light_position0);
|
||||
GLfloat ambient_color[] = { 0.7, 0.7, 0.8 };
|
||||
glLightfv(GL_LIGHT0, GL_AMBIENT, ambient_color);
|
||||
GLfloat diffuse_color[] = { 0.8, 0.7, 0.7 };
|
||||
glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuse_color);
|
||||
GLfloat specular_color[] = { 1.0, 1.0, 1.0, 1.0};
|
||||
glLightfv(GL_LIGHT0, GL_SPECULAR, specular_color);
|
||||
|
||||
glMaterialfv(GL_FRONT, GL_SPECULAR, specular_color);
|
||||
glMateriali(GL_FRONT, GL_SHININESS, 96);
|
||||
}
|
||||
|
||||
void Application::displaySide(Camera& whichCamera) {
|
||||
// transform by eye offset
|
||||
|
||||
|
@ -2905,22 +2988,7 @@ void Application::displaySide(Camera& whichCamera) {
|
|||
glTranslatef(-whichCamera.getPosition().x, -whichCamera.getPosition().y, -whichCamera.getPosition().z);
|
||||
|
||||
// Setup 3D lights (after the camera transform, so that they are positioned in world space)
|
||||
glEnable(GL_COLOR_MATERIAL);
|
||||
glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE);
|
||||
|
||||
glm::vec3 relativeSunLoc = glm::normalize(_environment.getClosestData(whichCamera.getPosition()).getSunLocation() -
|
||||
whichCamera.getPosition());
|
||||
GLfloat light_position0[] = { relativeSunLoc.x, relativeSunLoc.y, relativeSunLoc.z, 0.0 };
|
||||
glLightfv(GL_LIGHT0, GL_POSITION, light_position0);
|
||||
GLfloat ambient_color[] = { 0.7, 0.7, 0.8 };
|
||||
glLightfv(GL_LIGHT0, GL_AMBIENT, ambient_color);
|
||||
GLfloat diffuse_color[] = { 0.8, 0.7, 0.7 };
|
||||
glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuse_color);
|
||||
GLfloat specular_color[] = { 1.0, 1.0, 1.0, 1.0};
|
||||
glLightfv(GL_LIGHT0, GL_SPECULAR, specular_color);
|
||||
|
||||
glMaterialfv(GL_FRONT, GL_SPECULAR, specular_color);
|
||||
glMateriali(GL_FRONT, GL_SHININESS, 96);
|
||||
setupWorldLight(whichCamera);
|
||||
|
||||
if (_renderStarsOn->isChecked()) {
|
||||
if (!_stars.getFileLoaded()) {
|
||||
|
@ -2986,7 +3054,7 @@ void Application::displaySide(Camera& whichCamera) {
|
|||
glDisable(GL_FOG);
|
||||
glDisable(GL_NORMALIZE);
|
||||
|
||||
renderGroundPlaneGrid(EDGE_SIZE_GROUND_PLANE, _audio.getCollisionSoundMagnitude());
|
||||
//renderGroundPlaneGrid(EDGE_SIZE_GROUND_PLANE, _audio.getCollisionSoundMagnitude());
|
||||
}
|
||||
// Draw voxels
|
||||
if (_renderVoxels->isChecked()) {
|
||||
|
@ -3015,6 +3083,8 @@ void Application::displaySide(Camera& whichCamera) {
|
|||
glEnable(GL_LIGHTING);
|
||||
}
|
||||
|
||||
_myAvatar.renderScreenTint(SCREEN_TINT_BEFORE_AVATARS, whichCamera);
|
||||
|
||||
if (_renderAvatarsOn->isChecked()) {
|
||||
// Render avatars of other nodes
|
||||
NodeList* nodeList = NodeList::getInstance();
|
||||
|
@ -3049,6 +3119,8 @@ void Application::displaySide(Camera& whichCamera) {
|
|||
}
|
||||
}
|
||||
|
||||
_myAvatar.renderScreenTint(SCREEN_TINT_AFTER_AVATARS, whichCamera);
|
||||
|
||||
if (_renderParticleSystemOn->isChecked()) {
|
||||
if (_particleSystemInitialized) {
|
||||
_particleSystem.render();
|
||||
|
@ -3060,6 +3132,20 @@ void Application::displaySide(Camera& whichCamera) {
|
|||
|
||||
// brad's frustum for debugging
|
||||
if (_frustumOn->isChecked()) renderViewFrustum(_viewFrustum);
|
||||
|
||||
// render voxel fades if they exist
|
||||
if (_voxelFades.size() > 0) {
|
||||
for(std::vector<VoxelFade>::iterator fade = _voxelFades.begin(); fade != _voxelFades.end();) {
|
||||
fade->render();
|
||||
if(fade->isDone()) {
|
||||
fade = _voxelFades.erase(fade);
|
||||
} else {
|
||||
++fade;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
renderFollowIndicator();
|
||||
}
|
||||
|
||||
void Application::displayOverlay() {
|
||||
|
@ -3627,6 +3713,62 @@ void Application::shiftPaintingColor() {
|
|||
}
|
||||
|
||||
|
||||
void Application::injectVoxelAddedSoundEffect() {
|
||||
AudioInjector* voxelInjector = AudioInjectionManager::injectorWithCapacity(11025);
|
||||
|
||||
if (voxelInjector) {
|
||||
voxelInjector->setPosition(glm::vec3(_mouseVoxel.x, _mouseVoxel.y, _mouseVoxel.z));
|
||||
//voxelInjector->setBearing(-1 * _myAvatar.getAbsoluteHeadYaw());
|
||||
voxelInjector->setVolume (16 * pow (_mouseVoxel.s, 2) / .0000001); //255 is max, and also default value
|
||||
|
||||
/* for (int i = 0; i
|
||||
< 22050; i++) {
|
||||
if (i % 4 == 0) {
|
||||
voxelInjector->addSample(4000);
|
||||
} else if (i % 4 == 1) {
|
||||
voxelInjector->addSample(0);
|
||||
} else if (i % 4 == 2) {
|
||||
voxelInjector->addSample(-4000);
|
||||
} else {
|
||||
voxelInjector->addSample(0);
|
||||
}
|
||||
*/
|
||||
|
||||
const float BIG_VOXEL_MIN_SIZE = .01f;
|
||||
|
||||
for (int i = 0; i < 11025; i++) {
|
||||
|
||||
/*
|
||||
A440 square wave
|
||||
if (sin(i * 2 * PIE / 50)>=0) {
|
||||
voxelInjector->addSample(4000);
|
||||
} else {
|
||||
voxelInjector->addSample(-4000);
|
||||
}
|
||||
*/
|
||||
|
||||
if (_mouseVoxel.s > BIG_VOXEL_MIN_SIZE) {
|
||||
voxelInjector->addSample(20000 * sin((i * 2 * PIE) / (500 * sin((i + 1) / 200))));
|
||||
} else {
|
||||
voxelInjector->addSample(16000 * sin(i / (1.5 * log (_mouseVoxel.s / .0001) * ((i + 11025) / 5512.5)))); //808
|
||||
}
|
||||
}
|
||||
|
||||
//voxelInjector->addSample(32500 * sin(i/(2 * 1 * ((i+5000)/5512.5)))); //80
|
||||
//voxelInjector->addSample(20000 * sin(i/(6 * (_mouseVoxel.s/.001) *((i+5512.5)/5512.5)))); //808
|
||||
//voxelInjector->addSample(20000 * sin(i/(6 * ((i+5512.5)/5512.5)))); //808
|
||||
//voxelInjector->addSample(4000 * sin(i * 2 * PIE /50)); //A440 sine wave
|
||||
//voxelInjector->addSample(4000 * sin(i * 2 * PIE /50) * sin (i/500)); //A440 sine wave with amplitude modulation
|
||||
|
||||
//FM library
|
||||
//voxelInjector->addSample(20000 * sin((i * 2 * PIE) /(500*sin((i+1)/200)))); //FM 1 dubstep
|
||||
//voxelInjector->addSample(20000 * sin((i * 2 * PIE) /(300*sin((i+1)/5.0)))); //FM 2 flange sweep
|
||||
//voxelInjector->addSample(10000 * sin((i * 2 * PIE) /(500*sin((i+1)/500.0)))); //FM 3 resonant pulse
|
||||
|
||||
AudioInjectionManager::threadInjector(voxelInjector);
|
||||
}
|
||||
}
|
||||
|
||||
bool Application::maybeEditVoxelUnderCursor() {
|
||||
if (_addVoxelMode->isChecked() || _colorVoxelMode->isChecked()) {
|
||||
if (_mouseVoxel.s != 0) {
|
||||
|
@ -3637,65 +3779,35 @@ bool Application::maybeEditVoxelUnderCursor() {
|
|||
// create the voxel locally so it appears immediately
|
||||
_voxels.createVoxel(_mouseVoxel.x, _mouseVoxel.y, _mouseVoxel.z, _mouseVoxel.s,
|
||||
_mouseVoxel.red, _mouseVoxel.green, _mouseVoxel.blue, _destructiveAddVoxel->isChecked());
|
||||
|
||||
// Implement voxel fade effect
|
||||
VoxelFade fade(VoxelFade::FADE_OUT, 1.0f, 1.0f, 1.0f);
|
||||
const float VOXEL_BOUNDS_ADJUST = 0.01f;
|
||||
float slightlyBigger = _mouseVoxel.s * VOXEL_BOUNDS_ADJUST;
|
||||
fade.voxelDetails.x = _mouseVoxel.x - slightlyBigger;
|
||||
fade.voxelDetails.y = _mouseVoxel.y - slightlyBigger;
|
||||
fade.voxelDetails.z = _mouseVoxel.z - slightlyBigger;
|
||||
fade.voxelDetails.s = _mouseVoxel.s + slightlyBigger + slightlyBigger;
|
||||
_voxelFades.push_back(fade);
|
||||
|
||||
// inject a sound effect
|
||||
injectVoxelAddedSoundEffect();
|
||||
|
||||
// remember the position for drag detection
|
||||
_justEditedVoxel = true;
|
||||
|
||||
AudioInjector* voxelInjector = AudioInjectionManager::injectorWithCapacity(11025);
|
||||
voxelInjector->setPosition(glm::vec3(_mouseVoxel.x, _mouseVoxel.y, _mouseVoxel.z));
|
||||
//_myAvatar.getPosition()
|
||||
// voxelInjector->setBearing(-1 * _myAvatar.getAbsoluteHeadYaw());
|
||||
voxelInjector->setVolume (16 * pow (_mouseVoxel.s, 2) / .0000001); //255 is max, and also default value
|
||||
|
||||
/* for (int i = 0; i
|
||||
< 22050; i++) {
|
||||
if (i % 4 == 0) {
|
||||
voxelInjector->addSample(4000);
|
||||
} else if (i % 4 == 1) {
|
||||
voxelInjector->addSample(0);
|
||||
} else if (i % 4 == 2) {
|
||||
voxelInjector->addSample(-4000);
|
||||
} else {
|
||||
voxelInjector->addSample(0);
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
const float BIG_VOXEL_MIN_SIZE = .01f;
|
||||
|
||||
for (int i = 0; i < 11025; i++) {
|
||||
|
||||
/*
|
||||
A440 square wave
|
||||
if (sin(i * 2 * PIE / 50)>=0) {
|
||||
voxelInjector->addSample(4000);
|
||||
} else {
|
||||
voxelInjector->addSample(-4000);
|
||||
}
|
||||
*/
|
||||
|
||||
if (_mouseVoxel.s > BIG_VOXEL_MIN_SIZE) {
|
||||
voxelInjector->addSample(20000 * sin((i * 2 * PIE) / (500 * sin((i + 1) / 200))));
|
||||
} else {
|
||||
voxelInjector->addSample(16000 * sin(i / (1.5 * log (_mouseVoxel.s / .0001) * ((i + 11025) / 5512.5)))); //808
|
||||
}
|
||||
}
|
||||
|
||||
//voxelInjector->addSample(32500 * sin(i/(2 * 1 * ((i+5000)/5512.5)))); //80
|
||||
//voxelInjector->addSample(20000 * sin(i/(6 * (_mouseVoxel.s/.001) *((i+5512.5)/5512.5)))); //808
|
||||
//voxelInjector->addSample(20000 * sin(i/(6 * ((i+5512.5)/5512.5)))); //808
|
||||
//voxelInjector->addSample(4000 * sin(i * 2 * PIE /50)); //A440 sine wave
|
||||
//voxelInjector->addSample(4000 * sin(i * 2 * PIE /50) * sin (i/500)); //A440 sine wave with amplitude modulation
|
||||
|
||||
//FM library
|
||||
//voxelInjector->addSample(20000 * sin((i * 2 * PIE) /(500*sin((i+1)/200)))); //FM 1 dubstep
|
||||
//voxelInjector->addSample(20000 * sin((i * 2 * PIE) /(300*sin((i+1)/5.0)))); //FM 2 flange sweep
|
||||
//voxelInjector->addSample(10000 * sin((i * 2 * PIE) /(500*sin((i+1)/500.0)))); //FM 3 resonant pulse
|
||||
|
||||
AudioInjectionManager::threadInjector(voxelInjector);
|
||||
}
|
||||
} else if (_deleteVoxelMode->isChecked()) {
|
||||
deleteVoxelUnderCursor();
|
||||
VoxelFade fade(VoxelFade::FADE_OUT, NODE_KILLED_RED, NODE_KILLED_GREEN, NODE_KILLED_BLUE);
|
||||
const float VOXEL_BOUNDS_ADJUST = 0.01f;
|
||||
float slightlyBigger = _mouseVoxel.s * VOXEL_BOUNDS_ADJUST;
|
||||
fade.voxelDetails.x = _mouseVoxel.x - slightlyBigger;
|
||||
fade.voxelDetails.y = _mouseVoxel.y - slightlyBigger;
|
||||
fade.voxelDetails.z = _mouseVoxel.z - slightlyBigger;
|
||||
fade.voxelDetails.s = _mouseVoxel.s + slightlyBigger + slightlyBigger;
|
||||
_voxelFades.push_back(fade);
|
||||
|
||||
} else if (_eyedropperMode->isChecked()) {
|
||||
eyedropperVoxelUnderCursor();
|
||||
} else {
|
||||
|
@ -3710,17 +3822,20 @@ void Application::deleteVoxelUnderCursor() {
|
|||
// sending delete to the server is sufficient, server will send new version so we see updates soon enough
|
||||
sendVoxelEditMessage(PACKET_TYPE_ERASE_VOXEL, _mouseVoxel);
|
||||
AudioInjector* voxelInjector = AudioInjectionManager::injectorWithCapacity(5000);
|
||||
voxelInjector->setPosition(glm::vec3(_mouseVoxel.x, _mouseVoxel.y, _mouseVoxel.z));
|
||||
// voxelInjector->setBearing(0); //straight down the z axis
|
||||
voxelInjector->setVolume (255); //255 is max, and also default value
|
||||
|
||||
|
||||
for (int i = 0; i < 5000; i++) {
|
||||
voxelInjector->addSample(10000 * sin((i * 2 * PIE) / (500 * sin((i + 1) / 500.0)))); //FM 3 resonant pulse
|
||||
//voxelInjector->addSample(20000 * sin((i) /((4 / _mouseVoxel.s) * sin((i)/(20 * _mouseVoxel.s / .001))))); //FM 2 comb filter
|
||||
if (voxelInjector) {
|
||||
voxelInjector->setPosition(glm::vec3(_mouseVoxel.x, _mouseVoxel.y, _mouseVoxel.z));
|
||||
//voxelInjector->setBearing(0); //straight down the z axis
|
||||
voxelInjector->setVolume (255); //255 is max, and also default value
|
||||
|
||||
|
||||
for (int i = 0; i < 5000; i++) {
|
||||
voxelInjector->addSample(10000 * sin((i * 2 * PIE) / (500 * sin((i + 1) / 500.0)))); //FM 3 resonant pulse
|
||||
//voxelInjector->addSample(20000 * sin((i) /((4 / _mouseVoxel.s) * sin((i)/(20 * _mouseVoxel.s / .001))))); //FM 2 comb filter
|
||||
}
|
||||
|
||||
AudioInjectionManager::threadInjector(voxelInjector);
|
||||
}
|
||||
|
||||
AudioInjectionManager::threadInjector(voxelInjector);
|
||||
}
|
||||
// remember the position for drag detection
|
||||
_justEditedVoxel = true;
|
||||
|
@ -3817,6 +3932,57 @@ void Application::attachNewHeadToNode(Node* newNode) {
|
|||
}
|
||||
}
|
||||
|
||||
void Application::nodeAdded(Node* node) {
|
||||
}
|
||||
|
||||
void Application::nodeKilled(Node* node) {
|
||||
if (node->getType() == NODE_TYPE_VOXEL_SERVER) {
|
||||
uint16_t nodeID = node->getNodeID();
|
||||
// see if this is the first we've heard of this node...
|
||||
if (_voxelServerJurisdictions.find(nodeID) != _voxelServerJurisdictions.end()) {
|
||||
VoxelPositionSize jurisditionDetails;
|
||||
jurisditionDetails = _voxelServerJurisdictions[nodeID];
|
||||
|
||||
printf("voxel server going away...... v[%f, %f, %f, %f]\n",
|
||||
jurisditionDetails.x, jurisditionDetails.y, jurisditionDetails.z, jurisditionDetails.s);
|
||||
|
||||
// Add the jurisditionDetails object to the list of "fade outs"
|
||||
VoxelFade fade(VoxelFade::FADE_OUT, NODE_KILLED_RED, NODE_KILLED_GREEN, NODE_KILLED_BLUE);
|
||||
fade.voxelDetails = jurisditionDetails;
|
||||
_voxelFades.push_back(fade);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int Application::parseVoxelStats(unsigned char* messageData, ssize_t messageLength, sockaddr senderAddress) {
|
||||
|
||||
// parse the incoming stats data, and stick it into our averaging stats object for now... even though this
|
||||
// means mixing in stats from potentially multiple servers.
|
||||
int statsMessageLength = _voxelSceneStats.unpackFromMessage(messageData, messageLength);
|
||||
|
||||
// But, also identify the sender, and keep track of the contained jurisdiction root for this server
|
||||
Node* voxelServer = NodeList::getInstance()->nodeWithAddress(&senderAddress);
|
||||
uint16_t nodeID = voxelServer->getNodeID();
|
||||
|
||||
VoxelPositionSize jurisditionDetails;
|
||||
voxelDetailsForCode(_voxelSceneStats.getJurisdictionRoot(), jurisditionDetails);
|
||||
|
||||
// see if this is the first we've heard of this node...
|
||||
if (_voxelServerJurisdictions.find(nodeID) == _voxelServerJurisdictions.end()) {
|
||||
printf("stats from new voxel server... v[%f, %f, %f, %f]\n",
|
||||
jurisditionDetails.x, jurisditionDetails.y, jurisditionDetails.z, jurisditionDetails.s);
|
||||
|
||||
// Add the jurisditionDetails object to the list of "fade outs"
|
||||
VoxelFade fade(VoxelFade::FADE_OUT, NODE_ADDED_RED, NODE_ADDED_GREEN, NODE_ADDED_BLUE);
|
||||
fade.voxelDetails = jurisditionDetails;
|
||||
_voxelFades.push_back(fade);
|
||||
}
|
||||
// store jurisdiction details for later use
|
||||
_voxelServerJurisdictions[nodeID] = jurisditionDetails;
|
||||
|
||||
return statsMessageLength;
|
||||
}
|
||||
|
||||
// Receive packets from other nodes/servers and decide what to do with them!
|
||||
void* Application::networkReceive(void* args) {
|
||||
sockaddr senderAddress;
|
||||
|
@ -3860,7 +4026,8 @@ void* Application::networkReceive(void* args) {
|
|||
// immediately following them inside the same packet. So, we process the PACKET_TYPE_VOXEL_STATS first
|
||||
// then process any remaining bytes as if it was another packet
|
||||
if (messageData[0] == PACKET_TYPE_VOXEL_STATS) {
|
||||
int statsMessageLength = app->_voxelSceneStats.unpackFromMessage(messageData, messageLength);
|
||||
|
||||
int statsMessageLength = app->parseVoxelStats(messageData, messageLength, senderAddress);
|
||||
if (messageLength > statsMessageLength) {
|
||||
messageData += statsMessageLength;
|
||||
messageLength -= statsMessageLength;
|
||||
|
@ -3954,11 +4121,11 @@ void Application::saveAction(QSettings* set, QAction* action) {
|
|||
}
|
||||
|
||||
void Application::loadSettings(QSettings* settings) {
|
||||
if (!settings) {
|
||||
if (!settings) {
|
||||
settings = getSettings();
|
||||
}
|
||||
|
||||
_headCameraPitchYawScale = loadSetting(settings, "headCameraPitchYawScale", 0.0f);
|
||||
_gyroCameraSensitivity = loadSetting(settings, "gyroCameraSensitivity", 0.5f);
|
||||
_audioJitterBufferSamples = loadSetting(settings, "audioJitterBufferSamples", 0);
|
||||
_horizontalFieldOfView = loadSetting(settings, "horizontalFieldOfView", HORIZONTAL_FIELD_OF_VIEW_DEGREES);
|
||||
|
||||
|
@ -3982,7 +4149,7 @@ void Application::saveSettings(QSettings* settings) {
|
|||
settings = getSettings();
|
||||
}
|
||||
|
||||
settings->setValue("headCameraPitchYawScale", _headCameraPitchYawScale);
|
||||
settings->setValue("gyroCameraSensitivity", _gyroCameraSensitivity);
|
||||
settings->setValue("audioJitterBufferSamples", _audioJitterBufferSamples);
|
||||
settings->setValue("horizontalFieldOfView", _horizontalFieldOfView);
|
||||
settings->beginGroup("View Frustum Offset Camera");
|
||||
|
|
|
@ -31,17 +31,19 @@
|
|||
#include "Environment.h"
|
||||
#include "PacketHeaders.h"
|
||||
#include "ParticleSystem.h"
|
||||
#include "renderer/GeometryCache.h"
|
||||
#include "SerialInterface.h"
|
||||
#include "Stars.h"
|
||||
#include "Swatch.h"
|
||||
#include "ToolsPalette.h"
|
||||
#include "ViewFrustum.h"
|
||||
#include "VoxelFade.h"
|
||||
#include "VoxelSystem.h"
|
||||
#include "Webcam.h"
|
||||
#include "PieMenu.h"
|
||||
#include "avatar/Avatar.h"
|
||||
#include "avatar/HandControl.h"
|
||||
#include "renderer/GeometryCache.h"
|
||||
#include "renderer/TextureCache.h"
|
||||
#include "ui/BandwidthDialog.h"
|
||||
#include "ui/ChatEntry.h"
|
||||
#include "ui/VoxelStatsDialog.h"
|
||||
|
@ -59,13 +61,23 @@ class QWheelEvent;
|
|||
class Node;
|
||||
class ProgramObject;
|
||||
|
||||
class Application : public QApplication {
|
||||
static const float NODE_ADDED_RED = 0.0f;
|
||||
static const float NODE_ADDED_GREEN = 1.0f;
|
||||
static const float NODE_ADDED_BLUE = 0.0f;
|
||||
static const float NODE_KILLED_RED = 1.0f;
|
||||
static const float NODE_KILLED_GREEN = 0.0f;
|
||||
static const float NODE_KILLED_BLUE = 0.0f;
|
||||
|
||||
|
||||
|
||||
class Application : public QApplication, public NodeListHook {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
static Application* getInstance() { return static_cast<Application*>(QCoreApplication::instance()); }
|
||||
|
||||
Application(int& argc, char** argv, timeval &startup_time);
|
||||
~Application();
|
||||
|
||||
void initializeGL();
|
||||
void paintGL();
|
||||
|
@ -105,8 +117,13 @@ public:
|
|||
|
||||
QNetworkAccessManager* getNetworkAccessManager() { return _networkAccessManager; }
|
||||
GeometryCache* getGeometryCache() { return &_geometryCache; }
|
||||
TextureCache* getTextureCache() { return &_textureCache; }
|
||||
|
||||
void resetSongMixMenuItem();
|
||||
void setupWorldLight(Camera& whichCamera);
|
||||
|
||||
virtual void nodeAdded(Node* node);
|
||||
virtual void nodeKilled(Node* node);
|
||||
|
||||
public slots:
|
||||
void sendAvatarFaceVideoMessage(int frameCount, const QByteArray& data);
|
||||
|
@ -179,6 +196,7 @@ private slots:
|
|||
void setListenModePoint();
|
||||
void setListenModeSingleSource();
|
||||
void toggleMixedSong();
|
||||
void toggleWantCollisionsOn();
|
||||
|
||||
|
||||
void renderCoverageMap();
|
||||
|
@ -216,6 +234,7 @@ private:
|
|||
bool isLookingAtMyAvatar(Avatar* avatar);
|
||||
|
||||
void renderLookatIndicator(glm::vec3 pointOfInterest, Camera& whichCamera);
|
||||
void renderFollowIndicator();
|
||||
void updateAvatar(float deltaTime);
|
||||
void loadViewFrustum(Camera& camera, ViewFrustum& viewFrustum);
|
||||
|
||||
|
@ -233,6 +252,7 @@ private:
|
|||
void deleteVoxelUnderCursor();
|
||||
void eyedropperVoxelUnderCursor();
|
||||
void resetSensors();
|
||||
void injectVoxelAddedSoundEffect();
|
||||
|
||||
void setMenuShortcutsEnabled(bool enabled);
|
||||
|
||||
|
@ -294,6 +314,7 @@ private:
|
|||
QAction* _rawAudioMicrophoneMix; // Mixing of a RAW audio file with microphone stream for rave gloves
|
||||
QAction* _noise;
|
||||
QAction* _occlusionCulling;
|
||||
QAction* _wantCollisionsOn;
|
||||
|
||||
QAction* _renderCoverageMapV2;
|
||||
QAction* _renderCoverageMap;
|
||||
|
@ -358,7 +379,7 @@ private:
|
|||
Environment _environment;
|
||||
|
||||
int _headMouseX, _headMouseY;
|
||||
float _headCameraPitchYawScale;
|
||||
float _gyroCameraSensitivity;
|
||||
|
||||
int _audioJitterBufferSamples; // Number of extra samples to wait before starting audio playback
|
||||
|
||||
|
@ -419,6 +440,7 @@ private:
|
|||
int _hmdWarpParamLocation;
|
||||
|
||||
GeometryCache _geometryCache;
|
||||
TextureCache _textureCache;
|
||||
|
||||
ParticleSystem _particleSystem;
|
||||
|
||||
|
@ -445,6 +467,11 @@ private:
|
|||
PieMenu _pieMenu;
|
||||
|
||||
VoxelSceneStats _voxelSceneStats;
|
||||
int parseVoxelStats(unsigned char* messageData, ssize_t messageLength, sockaddr senderAddress);
|
||||
|
||||
std::map<uint16_t,VoxelPositionSize> _voxelServerJurisdictions;
|
||||
|
||||
std::vector<VoxelFade> _voxelFades;
|
||||
};
|
||||
|
||||
#endif /* defined(__interface__Application__) */
|
||||
|
|
58
interface/src/VoxelFade.cpp
Normal file
58
interface/src/VoxelFade.cpp
Normal file
|
@ -0,0 +1,58 @@
|
|||
//
|
||||
// VoxelFade.cpp
|
||||
// interface
|
||||
//
|
||||
// Created by Brad Hefta-Gaub on 8/6/13.
|
||||
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#include "InterfaceConfig.h"
|
||||
|
||||
#include <VoxelConstants.h>
|
||||
|
||||
#include "VoxelFade.h"
|
||||
|
||||
const float VoxelFade::FADE_OUT_START = 0.5f;
|
||||
const float VoxelFade::FADE_OUT_END = 0.0f;
|
||||
const float VoxelFade::FADE_OUT_STEP = -0.005f;
|
||||
const float VoxelFade::FADE_IN_START = 0.0f;
|
||||
const float VoxelFade::FADE_IN_END = 0.5f;
|
||||
const float VoxelFade::FADE_IN_STEP = 0.005f;
|
||||
const float VoxelFade::DEFAULT_RED = 0.5f;
|
||||
const float VoxelFade::DEFAULT_GREEN = 0.5f;
|
||||
const float VoxelFade::DEFAULT_BLUE = 0.5f;
|
||||
|
||||
VoxelFade::VoxelFade(FadeDirection direction, float red, float green, float blue) :
|
||||
direction(direction),
|
||||
red(red),
|
||||
green(green),
|
||||
blue(blue)
|
||||
{
|
||||
opacity = (direction == FADE_OUT) ? FADE_OUT_START : FADE_IN_START;
|
||||
}
|
||||
|
||||
void VoxelFade::render() {
|
||||
glDisable(GL_LIGHTING);
|
||||
glPushMatrix();
|
||||
glScalef(TREE_SCALE, TREE_SCALE, TREE_SCALE);
|
||||
glColor4f(red, green, blue, opacity);
|
||||
glTranslatef(voxelDetails.x + voxelDetails.s * 0.5f,
|
||||
voxelDetails.y + voxelDetails.s * 0.5f,
|
||||
voxelDetails.z + voxelDetails.s * 0.5f);
|
||||
glLineWidth(1.0f);
|
||||
glutSolidCube(voxelDetails.s);
|
||||
glLineWidth(1.0f);
|
||||
glPopMatrix();
|
||||
glEnable(GL_LIGHTING);
|
||||
|
||||
opacity += (direction == FADE_OUT) ? FADE_OUT_STEP : FADE_IN_STEP;
|
||||
}
|
||||
|
||||
bool VoxelFade::isDone() const {
|
||||
if (direction == FADE_OUT) {
|
||||
return opacity <= FADE_OUT_END;
|
||||
} else {
|
||||
return opacity >= FADE_IN_END;
|
||||
}
|
||||
return true; // unexpected case, assume we're done
|
||||
}
|
43
interface/src/VoxelFade.h
Normal file
43
interface/src/VoxelFade.h
Normal file
|
@ -0,0 +1,43 @@
|
|||
//
|
||||
// VoxelFade.h
|
||||
// interface
|
||||
//
|
||||
// Created by Brad Hefta-Gaub on 8/6/13.
|
||||
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef __interface__VoxelFade__
|
||||
#define __interface__VoxelFade__
|
||||
|
||||
#include <OctalCode.h> // for VoxelPositionSize
|
||||
|
||||
class VoxelFade {
|
||||
public:
|
||||
|
||||
enum FadeDirection { FADE_OUT, FADE_IN};
|
||||
static const float FADE_OUT_START;
|
||||
static const float FADE_OUT_END;
|
||||
static const float FADE_OUT_STEP;
|
||||
static const float FADE_IN_START;
|
||||
static const float FADE_IN_END;
|
||||
static const float FADE_IN_STEP;
|
||||
static const float DEFAULT_RED;
|
||||
static const float DEFAULT_GREEN;
|
||||
static const float DEFAULT_BLUE;
|
||||
|
||||
VoxelPositionSize voxelDetails;
|
||||
FadeDirection direction;
|
||||
float opacity;
|
||||
|
||||
float red;
|
||||
float green;
|
||||
float blue;
|
||||
|
||||
VoxelFade(FadeDirection direction = FADE_OUT, float red = DEFAULT_RED,
|
||||
float green = DEFAULT_GREEN, float blue = DEFAULT_BLUE);
|
||||
|
||||
void render();
|
||||
bool isDone() const;
|
||||
};
|
||||
|
||||
#endif // __interface__VoxelFade__
|
|
@ -17,8 +17,6 @@
|
|||
#include <fstream> // to load voxels from file
|
||||
#include <pthread.h>
|
||||
|
||||
#include <glm/gtc/random.hpp>
|
||||
|
||||
#include <OctalCode.h>
|
||||
#include <PacketHeaders.h>
|
||||
#include <PerfStat.h>
|
||||
|
@ -493,7 +491,6 @@ glm::vec3 VoxelSystem::computeVoxelVertex(const glm::vec3& startVertex, float vo
|
|||
}
|
||||
|
||||
ProgramObject* VoxelSystem::_perlinModulateProgram = 0;
|
||||
GLuint VoxelSystem::_permutationNormalTextureID = 0;
|
||||
|
||||
void VoxelSystem::init() {
|
||||
|
||||
|
@ -585,29 +582,9 @@ void VoxelSystem::init() {
|
|||
_perlinModulateProgram->addShaderFromSourceFile(QGLShader::Fragment, "resources/shaders/perlin_modulate.frag");
|
||||
_perlinModulateProgram->link();
|
||||
|
||||
_perlinModulateProgram->bind();
|
||||
_perlinModulateProgram->setUniformValue("permutationNormalTexture", 0);
|
||||
|
||||
// create the permutation/normal texture
|
||||
glGenTextures(1, &_permutationNormalTextureID);
|
||||
glBindTexture(GL_TEXTURE_2D, _permutationNormalTextureID);
|
||||
|
||||
// the first line consists of random permutation offsets
|
||||
unsigned char data[256 * 2 * 3];
|
||||
for (int i = 0; i < 256 * 3; i++) {
|
||||
data[i] = rand() % 256;
|
||||
}
|
||||
// the next, random unit normals
|
||||
for (int i = 256 * 3; i < 256 * 3 * 2; i += 3) {
|
||||
glm::vec3 randvec = glm::sphericalRand(1.0f);
|
||||
data[i] = ((randvec.x + 1.0f) / 2.0f) * 255.0f;
|
||||
data[i + 1] = ((randvec.y + 1.0f) / 2.0f) * 255.0f;
|
||||
data[i + 2] = ((randvec.z + 1.0f) / 2.0f) * 255.0f;
|
||||
}
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 256, 2, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
_perlinModulateProgram->release();
|
||||
}
|
||||
|
||||
void VoxelSystem::updateFullVBOs() {
|
||||
|
@ -734,7 +711,7 @@ void VoxelSystem::applyScaleAndBindProgram(bool texture) {
|
|||
|
||||
if (texture) {
|
||||
_perlinModulateProgram->bind();
|
||||
glBindTexture(GL_TEXTURE_2D, _permutationNormalTextureID);
|
||||
glBindTexture(GL_TEXTURE_2D, Application::getInstance()->getTextureCache()->getPermutationNormalTextureID());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1553,10 +1530,38 @@ void VoxelSystem::falseColorizeOccludedV2() {
|
|||
VoxelProjectedPolygon::intersects_calls
|
||||
);
|
||||
//myCoverageMapV2.erase();
|
||||
|
||||
|
||||
_tree->setDirtyBit();
|
||||
setupNewVoxelsForDrawing();
|
||||
}
|
||||
|
||||
void VoxelSystem::nodeAdded(Node* node) {
|
||||
if (node->getType() == NODE_TYPE_VOXEL_SERVER) {
|
||||
uint16_t nodeID = node->getNodeID();
|
||||
printf("VoxelSystem... voxel server %u added...\n", nodeID);
|
||||
}
|
||||
}
|
||||
|
||||
bool VoxelSystem::killSourceVoxelsOperation(VoxelNode* node, void* extraData) {
|
||||
uint16_t killedNodeID = *(uint16_t*)extraData;
|
||||
for (int i = 0; i < NUMBER_OF_CHILDREN; i++) {
|
||||
VoxelNode* childNode = node->getChildAtIndex(i);
|
||||
if (childNode && childNode->getSourceID()== killedNodeID) {
|
||||
node->safeDeepDeleteChildAtIndex(i);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void VoxelSystem::nodeKilled(Node* node) {
|
||||
if (node->getType() == NODE_TYPE_VOXEL_SERVER) {
|
||||
uint16_t nodeID = node->getNodeID();
|
||||
printf("VoxelSystem... voxel server %u removed...\n", nodeID);
|
||||
|
||||
// Kill any voxels from the local tree
|
||||
_tree->recurseTreeWithOperation(killSourceVoxelsOperation, &nodeID);
|
||||
_tree->setDirtyBit();
|
||||
setupNewVoxelsForDrawing();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -28,7 +28,7 @@ class ProgramObject;
|
|||
|
||||
const int NUM_CHILDREN = 8;
|
||||
|
||||
class VoxelSystem : public NodeData, public VoxelNodeDeleteHook {
|
||||
class VoxelSystem : public NodeData, public VoxelNodeDeleteHook, public NodeListHook {
|
||||
public:
|
||||
VoxelSystem(float treeScale = TREE_SCALE, int maxVoxels = MAX_VOXELS_PER_SYSTEM);
|
||||
~VoxelSystem();
|
||||
|
@ -99,6 +99,8 @@ public:
|
|||
CoverageMap myCoverageMap;
|
||||
|
||||
virtual void nodeDeleted(VoxelNode* node);
|
||||
virtual void nodeAdded(Node* node);
|
||||
virtual void nodeKilled(Node* node);
|
||||
|
||||
protected:
|
||||
float _treeScale;
|
||||
|
@ -140,6 +142,7 @@ private:
|
|||
static bool falseColorizeSubTreeOperation(VoxelNode* node, void* extraData);
|
||||
static bool falseColorizeOccludedV2Operation(VoxelNode* node, void* extraData);
|
||||
static bool falseColorizeBySourceOperation(VoxelNode* node, void* extraData);
|
||||
static bool killSourceVoxelsOperation(VoxelNode* node, void* extraData);
|
||||
|
||||
int updateNodeInArraysAsFullVBO(VoxelNode* node);
|
||||
int updateNodeInArraysAsPartialVBO(VoxelNode* node);
|
||||
|
@ -193,7 +196,6 @@ private:
|
|||
bool _voxelsDirty;
|
||||
|
||||
static ProgramObject* _perlinModulateProgram;
|
||||
static GLuint _permutationNormalTextureID;
|
||||
|
||||
int _hookID;
|
||||
std::vector<glBufferIndex> _freeIndexes;
|
||||
|
|
|
@ -71,26 +71,28 @@ void Webcam::reset() {
|
|||
}
|
||||
|
||||
void Webcam::renderPreview(int screenWidth, int screenHeight) {
|
||||
if (_enabled && _colorTextureID != 0) {
|
||||
glBindTexture(GL_TEXTURE_2D, _colorTextureID);
|
||||
if (_enabled) {
|
||||
glEnable(GL_TEXTURE_2D);
|
||||
glColor3f(1.0f, 1.0f, 1.0f);
|
||||
glBegin(GL_QUADS);
|
||||
const int PREVIEW_HEIGHT = 200;
|
||||
int previewWidth = _textureSize.width * PREVIEW_HEIGHT / _textureSize.height;
|
||||
int top = screenHeight - 600;
|
||||
int left = screenWidth - previewWidth - 10;
|
||||
|
||||
glTexCoord2f(0, 0);
|
||||
glVertex2f(left, top);
|
||||
glTexCoord2f(1, 0);
|
||||
glVertex2f(left + previewWidth, top);
|
||||
glTexCoord2f(1, 1);
|
||||
glVertex2f(left + previewWidth, top + PREVIEW_HEIGHT);
|
||||
glTexCoord2f(0, 1);
|
||||
glVertex2f(left, top + PREVIEW_HEIGHT);
|
||||
glEnd();
|
||||
|
||||
|
||||
const int PREVIEW_HEIGHT = 200;
|
||||
int previewWidth = _textureSize.width * PREVIEW_HEIGHT / _textureSize.height;
|
||||
int top = screenHeight - 600;
|
||||
int left = screenWidth - previewWidth - 10;
|
||||
if (_colorTextureID != 0) {
|
||||
glBindTexture(GL_TEXTURE_2D, _colorTextureID);
|
||||
glBegin(GL_QUADS);
|
||||
glTexCoord2f(0, 0);
|
||||
glVertex2f(left, top);
|
||||
glTexCoord2f(1, 0);
|
||||
glVertex2f(left + previewWidth, top);
|
||||
glTexCoord2f(1, 1);
|
||||
glVertex2f(left + previewWidth, top + PREVIEW_HEIGHT);
|
||||
glTexCoord2f(0, 1);
|
||||
glVertex2f(left, top + PREVIEW_HEIGHT);
|
||||
glEnd();
|
||||
}
|
||||
|
||||
if (_depthTextureID != 0) {
|
||||
glBindTexture(GL_TEXTURE_2D, _depthTextureID);
|
||||
glBegin(GL_QUADS);
|
||||
|
@ -157,22 +159,26 @@ const float METERS_PER_MM = 1.0f / 1000.0f;
|
|||
|
||||
void Webcam::setFrame(const Mat& color, int format, const Mat& depth, float midFaceDepth,
|
||||
float aspectRatio, const RotatedRect& faceRect, bool sending, const JointVector& joints) {
|
||||
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);
|
||||
qDebug("Capturing video at %gx%g.\n", _textureSize.width, _textureSize.height);
|
||||
|
||||
} else {
|
||||
glBindTexture(GL_TEXTURE_2D, _colorTextureID);
|
||||
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, _textureSize.width, _textureSize.height, format,
|
||||
GL_UNSIGNED_BYTE, colorImage.imageData);
|
||||
if (!color.empty()) {
|
||||
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);
|
||||
|
||||
} else {
|
||||
glBindTexture(GL_TEXTURE_2D, _colorTextureID);
|
||||
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, _textureSize.width, _textureSize.height, format,
|
||||
GL_UNSIGNED_BYTE, colorImage.imageData);
|
||||
}
|
||||
} else if (_colorTextureID != 0) {
|
||||
glDeleteTextures(1, &_colorTextureID);
|
||||
_colorTextureID = 0;
|
||||
}
|
||||
|
||||
|
||||
if (!depth.empty()) {
|
||||
IplImage depthImage = depth;
|
||||
glPixelStorei(GL_UNPACK_ROW_LENGTH, depthImage.widthStep);
|
||||
|
@ -189,6 +195,9 @@ void Webcam::setFrame(const Mat& color, int format, const Mat& depth, float midF
|
|||
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, _textureSize.width, _textureSize.height, GL_LUMINANCE,
|
||||
GL_UNSIGNED_BYTE, depthImage.imageData);
|
||||
}
|
||||
} else if (_depthTextureID != 0) {
|
||||
glDeleteTextures(1, &_depthTextureID);
|
||||
_depthTextureID = 0;
|
||||
}
|
||||
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
|
@ -273,8 +282,8 @@ void Webcam::setFrame(const Mat& color, int format, const Mat& depth, float midF
|
|||
QTimer::singleShot(qMax((int)remaining / 1000, 0), _grabber, SLOT(grabFrame()));
|
||||
}
|
||||
|
||||
FrameGrabber::FrameGrabber() : _initialized(false), _videoSendMode(FULL_FRAME_VIDEO), _capture(0), _searchWindow(0, 0, 0, 0),
|
||||
_smoothedMidFaceDepth(UNINITIALIZED_FACE_DEPTH), _colorCodec(), _depthCodec(), _frameCount(0) {
|
||||
FrameGrabber::FrameGrabber() : _initialized(false), _videoSendMode(FULL_FRAME_VIDEO), _depthOnly(false), _capture(0),
|
||||
_searchWindow(0, 0, 0, 0), _smoothedMidFaceDepth(UNINITIALIZED_FACE_DEPTH), _colorCodec(), _depthCodec(), _frameCount(0) {
|
||||
}
|
||||
|
||||
FrameGrabber::~FrameGrabber() {
|
||||
|
@ -374,6 +383,11 @@ void FrameGrabber::cycleVideoSendMode() {
|
|||
destroyCodecs();
|
||||
}
|
||||
|
||||
void FrameGrabber::setDepthOnly(bool depthOnly) {
|
||||
_depthOnly = depthOnly;
|
||||
destroyCodecs();
|
||||
}
|
||||
|
||||
void FrameGrabber::reset() {
|
||||
_searchWindow = cv::Rect(0, 0, 0, 0);
|
||||
|
||||
|
@ -479,7 +493,7 @@ void FrameGrabber::grabFrame() {
|
|||
encodedWidth = color.cols;
|
||||
encodedHeight = color.rows;
|
||||
aspectRatio = FULL_FRAME_ASPECT;
|
||||
colorBitrateMultiplier = 4.0f;
|
||||
colorBitrateMultiplier = depthBitrateMultiplier = 4.0f;
|
||||
|
||||
} else {
|
||||
// if we don't have a search window (yet), try using the face cascade
|
||||
|
@ -591,108 +605,129 @@ void FrameGrabber::grabFrame() {
|
|||
depth.convertTo(_grayDepthFrame, CV_8UC1, 1.0, depthOffset);
|
||||
}
|
||||
|
||||
QByteArray payload;
|
||||
// increment the frame count that identifies frames
|
||||
_frameCount++;
|
||||
|
||||
QByteArray payload;
|
||||
if (_videoSendMode != NO_VIDEO) {
|
||||
if (_colorCodec.name == 0) {
|
||||
// initialize encoder context(s)
|
||||
vpx_codec_enc_cfg_t codecConfig;
|
||||
vpx_codec_enc_config_default(vpx_codec_vp8_cx(), &codecConfig, 0);
|
||||
codecConfig.rc_target_bitrate = ENCODED_FACE_WIDTH * ENCODED_FACE_HEIGHT * colorBitrateMultiplier *
|
||||
codecConfig.rc_target_bitrate / codecConfig.g_w / codecConfig.g_h;
|
||||
codecConfig.g_w = encodedWidth;
|
||||
codecConfig.g_h = encodedHeight;
|
||||
vpx_codec_enc_init(&_colorCodec, vpx_codec_vp8_cx(), &codecConfig, 0);
|
||||
|
||||
if (!depth.empty()) {
|
||||
codecConfig.rc_target_bitrate *= depthBitrateMultiplier;
|
||||
vpx_codec_enc_init(&_depthCodec, vpx_codec_vp8_cx(), &codecConfig, 0);
|
||||
}
|
||||
}
|
||||
|
||||
Mat transform;
|
||||
if (_videoSendMode == FACE_VIDEO) {
|
||||
// resize/rotate face into encoding rectangle
|
||||
_faceColor.create(encodedHeight, encodedWidth, CV_8UC3);
|
||||
warpAffine(color, _faceColor, faceTransform, _faceColor.size());
|
||||
|
||||
} else {
|
||||
_faceColor = color;
|
||||
}
|
||||
|
||||
// convert from RGB to YV12: see http://www.fourcc.org/yuv.php and
|
||||
// http://docs.opencv.org/modules/imgproc/doc/miscellaneous_transformations.html#cvtcolor
|
||||
// start the payload off with the aspect ratio (zero for full frame)
|
||||
payload.append((const char*)&aspectRatio, sizeof(float));
|
||||
|
||||
// prepare the image in which we'll store the data
|
||||
const int ENCODED_BITS_PER_Y = 8;
|
||||
const int ENCODED_BITS_PER_VU = 2;
|
||||
const int ENCODED_BITS_PER_PIXEL = ENCODED_BITS_PER_Y + 2 * ENCODED_BITS_PER_VU;
|
||||
const int BITS_PER_BYTE = 8;
|
||||
_encodedFace.resize(encodedWidth * encodedHeight * ENCODED_BITS_PER_PIXEL / BITS_PER_BYTE);
|
||||
vpx_image_t vpxImage;
|
||||
vpx_img_wrap(&vpxImage, VPX_IMG_FMT_YV12, encodedWidth, encodedHeight, 1,
|
||||
(unsigned char*)_encodedFace.data());
|
||||
uchar* yline = vpxImage.planes[0];
|
||||
uchar* vline = vpxImage.planes[1];
|
||||
uchar* uline = vpxImage.planes[2];
|
||||
const int Y_RED_WEIGHT = (int)(0.299 * 256);
|
||||
const int Y_GREEN_WEIGHT = (int)(0.587 * 256);
|
||||
const int Y_BLUE_WEIGHT = (int)(0.114 * 256);
|
||||
const int V_RED_WEIGHT = (int)(0.713 * 256);
|
||||
const int U_BLUE_WEIGHT = (int)(0.564 * 256);
|
||||
int redIndex = 0;
|
||||
int greenIndex = 1;
|
||||
int blueIndex = 2;
|
||||
if (format == GL_BGR) {
|
||||
redIndex = 2;
|
||||
blueIndex = 0;
|
||||
}
|
||||
for (int i = 0; i < encodedHeight; i += 2) {
|
||||
uchar* ydest = yline;
|
||||
uchar* vdest = vline;
|
||||
uchar* udest = uline;
|
||||
for (int j = 0; j < encodedWidth; j += 2) {
|
||||
uchar* tl = _faceColor.ptr(i, j);
|
||||
uchar* tr = _faceColor.ptr(i, j + 1);
|
||||
uchar* bl = _faceColor.ptr(i + 1, j);
|
||||
uchar* br = _faceColor.ptr(i + 1, j + 1);
|
||||
|
||||
ydest[0] = (tl[redIndex] * Y_RED_WEIGHT + tl[1] * Y_GREEN_WEIGHT + tl[blueIndex] * Y_BLUE_WEIGHT) >> 8;
|
||||
ydest[1] = (tr[redIndex] * Y_RED_WEIGHT + tr[1] * Y_GREEN_WEIGHT + tr[blueIndex] * Y_BLUE_WEIGHT) >> 8;
|
||||
ydest[vpxImage.stride[0]] = (bl[redIndex] * Y_RED_WEIGHT + bl[greenIndex] *
|
||||
Y_GREEN_WEIGHT + bl[blueIndex] * Y_BLUE_WEIGHT) >> 8;
|
||||
ydest[vpxImage.stride[0] + 1] = (br[redIndex] * Y_RED_WEIGHT + br[greenIndex] *
|
||||
Y_GREEN_WEIGHT + br[blueIndex] * Y_BLUE_WEIGHT) >> 8;
|
||||
ydest += 2;
|
||||
|
||||
int totalRed = tl[redIndex] + tr[redIndex] + bl[redIndex] + br[redIndex];
|
||||
int totalGreen = tl[greenIndex] + tr[greenIndex] + bl[greenIndex] + br[greenIndex];
|
||||
int totalBlue = tl[blueIndex] + tr[blueIndex] + bl[blueIndex] + br[blueIndex];
|
||||
int totalY = (totalRed * Y_RED_WEIGHT + totalGreen * Y_GREEN_WEIGHT + totalBlue * Y_BLUE_WEIGHT) >> 8;
|
||||
|
||||
*vdest++ = (((totalRed - totalY) * V_RED_WEIGHT) >> 10) + 128;
|
||||
*udest++ = (((totalBlue - totalY) * U_BLUE_WEIGHT) >> 10) + 128;
|
||||
vpx_img_wrap(&vpxImage, VPX_IMG_FMT_YV12, encodedWidth, encodedHeight, 1, (unsigned char*)_encodedFace.data());
|
||||
|
||||
if (!_depthOnly || depth.empty()) {
|
||||
if (_colorCodec.name == 0) {
|
||||
// initialize encoder context
|
||||
vpx_codec_enc_cfg_t codecConfig;
|
||||
vpx_codec_enc_config_default(vpx_codec_vp8_cx(), &codecConfig, 0);
|
||||
codecConfig.rc_target_bitrate = ENCODED_FACE_WIDTH * ENCODED_FACE_HEIGHT * colorBitrateMultiplier *
|
||||
codecConfig.rc_target_bitrate / codecConfig.g_w / codecConfig.g_h;
|
||||
codecConfig.g_w = encodedWidth;
|
||||
codecConfig.g_h = encodedHeight;
|
||||
vpx_codec_enc_init(&_colorCodec, vpx_codec_vp8_cx(), &codecConfig, 0);
|
||||
}
|
||||
yline += vpxImage.stride[0] * 2;
|
||||
vline += vpxImage.stride[1];
|
||||
uline += vpxImage.stride[2];
|
||||
}
|
||||
|
||||
// encode the frame
|
||||
vpx_codec_encode(&_colorCodec, &vpxImage, ++_frameCount, 1, 0, VPX_DL_REALTIME);
|
||||
if (_videoSendMode == FACE_VIDEO) {
|
||||
// resize/rotate face into encoding rectangle
|
||||
_faceColor.create(encodedHeight, encodedWidth, CV_8UC3);
|
||||
warpAffine(color, _faceColor, faceTransform, _faceColor.size());
|
||||
|
||||
// start the payload off with the aspect ratio (zero for full frame)
|
||||
payload.append((const char*)&aspectRatio, sizeof(float));
|
||||
|
||||
// extract the encoded frame
|
||||
vpx_codec_iter_t iterator = 0;
|
||||
const vpx_codec_cx_pkt_t* packet;
|
||||
while ((packet = vpx_codec_get_cx_data(&_colorCodec, &iterator)) != 0) {
|
||||
if (packet->kind == VPX_CODEC_CX_FRAME_PKT) {
|
||||
// prepend the length, which will indicate whether there's a depth frame too
|
||||
payload.append((const char*)&packet->data.frame.sz, sizeof(packet->data.frame.sz));
|
||||
payload.append((const char*)packet->data.frame.buf, packet->data.frame.sz);
|
||||
} else {
|
||||
_faceColor = color;
|
||||
}
|
||||
}
|
||||
|
||||
// convert from RGB to YV12: see http://www.fourcc.org/yuv.php and
|
||||
// http://docs.opencv.org/modules/imgproc/doc/miscellaneous_transformations.html#cvtcolor
|
||||
uchar* yline = vpxImage.planes[0];
|
||||
uchar* vline = vpxImage.planes[1];
|
||||
uchar* uline = vpxImage.planes[2];
|
||||
const int Y_RED_WEIGHT = (int)(0.299 * 256);
|
||||
const int Y_GREEN_WEIGHT = (int)(0.587 * 256);
|
||||
const int Y_BLUE_WEIGHT = (int)(0.114 * 256);
|
||||
const int V_RED_WEIGHT = (int)(0.713 * 256);
|
||||
const int U_BLUE_WEIGHT = (int)(0.564 * 256);
|
||||
int redIndex = 0;
|
||||
int greenIndex = 1;
|
||||
int blueIndex = 2;
|
||||
if (format == GL_BGR) {
|
||||
redIndex = 2;
|
||||
blueIndex = 0;
|
||||
}
|
||||
for (int i = 0; i < encodedHeight; i += 2) {
|
||||
uchar* ydest = yline;
|
||||
uchar* vdest = vline;
|
||||
uchar* udest = uline;
|
||||
for (int j = 0; j < encodedWidth; j += 2) {
|
||||
uchar* tl = _faceColor.ptr(i, j);
|
||||
uchar* tr = _faceColor.ptr(i, j + 1);
|
||||
uchar* bl = _faceColor.ptr(i + 1, j);
|
||||
uchar* br = _faceColor.ptr(i + 1, j + 1);
|
||||
|
||||
ydest[0] = (tl[redIndex] * Y_RED_WEIGHT + tl[1] * Y_GREEN_WEIGHT + tl[blueIndex] * Y_BLUE_WEIGHT) >> 8;
|
||||
ydest[1] = (tr[redIndex] * Y_RED_WEIGHT + tr[1] * Y_GREEN_WEIGHT + tr[blueIndex] * Y_BLUE_WEIGHT) >> 8;
|
||||
ydest[vpxImage.stride[0]] = (bl[redIndex] * Y_RED_WEIGHT + bl[greenIndex] *
|
||||
Y_GREEN_WEIGHT + bl[blueIndex] * Y_BLUE_WEIGHT) >> 8;
|
||||
ydest[vpxImage.stride[0] + 1] = (br[redIndex] * Y_RED_WEIGHT + br[greenIndex] *
|
||||
Y_GREEN_WEIGHT + br[blueIndex] * Y_BLUE_WEIGHT) >> 8;
|
||||
ydest += 2;
|
||||
|
||||
int totalRed = tl[redIndex] + tr[redIndex] + bl[redIndex] + br[redIndex];
|
||||
int totalGreen = tl[greenIndex] + tr[greenIndex] + bl[greenIndex] + br[greenIndex];
|
||||
int totalBlue = tl[blueIndex] + tr[blueIndex] + bl[blueIndex] + br[blueIndex];
|
||||
int totalY = (totalRed * Y_RED_WEIGHT + totalGreen * Y_GREEN_WEIGHT + totalBlue * Y_BLUE_WEIGHT) >> 8;
|
||||
|
||||
*vdest++ = (((totalRed - totalY) * V_RED_WEIGHT) >> 10) + 128;
|
||||
*udest++ = (((totalBlue - totalY) * U_BLUE_WEIGHT) >> 10) + 128;
|
||||
}
|
||||
yline += vpxImage.stride[0] * 2;
|
||||
vline += vpxImage.stride[1];
|
||||
uline += vpxImage.stride[2];
|
||||
}
|
||||
|
||||
// encode the frame
|
||||
vpx_codec_encode(&_colorCodec, &vpxImage, _frameCount, 1, 0, VPX_DL_REALTIME);
|
||||
|
||||
// extract the encoded frame
|
||||
vpx_codec_iter_t iterator = 0;
|
||||
const vpx_codec_cx_pkt_t* packet;
|
||||
while ((packet = vpx_codec_get_cx_data(&_colorCodec, &iterator)) != 0) {
|
||||
if (packet->kind == VPX_CODEC_CX_FRAME_PKT) {
|
||||
// prepend the length, which will indicate whether there's a depth frame too
|
||||
payload.append((const char*)&packet->data.frame.sz, sizeof(packet->data.frame.sz));
|
||||
payload.append((const char*)packet->data.frame.buf, packet->data.frame.sz);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// zero length indicates no color info
|
||||
const size_t ZERO_SIZE = 0;
|
||||
payload.append((const char*)&ZERO_SIZE, sizeof(size_t));
|
||||
|
||||
// we can use more bits for depth
|
||||
depthBitrateMultiplier *= 2.0f;
|
||||
|
||||
// don't bother reporting the color
|
||||
color = Mat();
|
||||
}
|
||||
|
||||
if (!depth.empty()) {
|
||||
if (_depthCodec.name == 0) {
|
||||
// initialize encoder context
|
||||
vpx_codec_enc_cfg_t codecConfig;
|
||||
vpx_codec_enc_config_default(vpx_codec_vp8_cx(), &codecConfig, 0);
|
||||
codecConfig.rc_target_bitrate = ENCODED_FACE_WIDTH * ENCODED_FACE_HEIGHT * depthBitrateMultiplier *
|
||||
codecConfig.rc_target_bitrate / codecConfig.g_w / codecConfig.g_h;
|
||||
codecConfig.g_w = encodedWidth;
|
||||
codecConfig.g_h = encodedHeight;
|
||||
vpx_codec_enc_init(&_depthCodec, vpx_codec_vp8_cx(), &codecConfig, 0);
|
||||
}
|
||||
|
||||
// convert with mask
|
||||
uchar* yline = vpxImage.planes[0];
|
||||
uchar* vline = vpxImage.planes[1];
|
||||
|
|
|
@ -112,6 +112,7 @@ public:
|
|||
public slots:
|
||||
|
||||
void cycleVideoSendMode();
|
||||
void setDepthOnly(bool depthOnly);
|
||||
void reset();
|
||||
void shutdown();
|
||||
void grabFrame();
|
||||
|
@ -126,6 +127,7 @@ private:
|
|||
|
||||
bool _initialized;
|
||||
VideoSendMode _videoSendMode;
|
||||
bool _depthOnly;
|
||||
CvCapture* _capture;
|
||||
cv::CascadeClassifier _faceCascade;
|
||||
cv::Mat _hsvFrame;
|
||||
|
|
|
@ -101,6 +101,7 @@ Avatar::Avatar(Node* owningNode) :
|
|||
_lastCollisionPosition(0, 0, 0),
|
||||
_speedBrakes(false),
|
||||
_isThrustOn(false),
|
||||
_isCollisionsOn(true),
|
||||
_leadingAvatar(NULL),
|
||||
_voxels(this)
|
||||
{
|
||||
|
@ -294,9 +295,8 @@ void Avatar::reset() {
|
|||
|
||||
// Update avatar head rotation with sensor data
|
||||
void Avatar::updateFromGyrosAndOrWebcam(bool gyroLook,
|
||||
const glm::vec3& amplifyAngle,
|
||||
float yawFromTouch,
|
||||
float pitchFromTouch) {
|
||||
_head.setMousePitch(pitchFromTouch);
|
||||
SerialInterface* gyros = Application::getInstance()->getSerialHeadSensor();
|
||||
Webcam* webcam = Application::getInstance()->getWebcam();
|
||||
glm::vec3 estimatedPosition, estimatedRotation;
|
||||
|
@ -308,7 +308,6 @@ void Avatar::updateFromGyrosAndOrWebcam(bool gyroLook,
|
|||
|
||||
} else {
|
||||
_head.setPitch(pitchFromTouch);
|
||||
_head.setYaw(yawFromTouch);
|
||||
return;
|
||||
}
|
||||
if (webcam->isActive()) {
|
||||
|
@ -334,9 +333,15 @@ void Avatar::updateFromGyrosAndOrWebcam(bool gyroLook,
|
|||
} else {
|
||||
_head.getFace().clearFrame();
|
||||
}
|
||||
_head.setPitch(estimatedRotation.x * amplifyAngle.x + pitchFromTouch);
|
||||
_head.setYaw(estimatedRotation.y * amplifyAngle.y + yawFromTouch);
|
||||
_head.setRoll(estimatedRotation.z * amplifyAngle.z);
|
||||
|
||||
// Set the rotation of the avatar's head (as seen by others, not affecting view frustum)
|
||||
// to be scaled. Pitch is greater to emphasize nodding behavior / synchrony.
|
||||
const float AVATAR_HEAD_PITCH_MAGNIFY = 1.0f;
|
||||
const float AVATAR_HEAD_YAW_MAGNIFY = 1.0f;
|
||||
const float AVATAR_HEAD_ROLL_MAGNIFY = 1.0f;
|
||||
_head.setPitch(estimatedRotation.x * AVATAR_HEAD_PITCH_MAGNIFY);
|
||||
_head.setYaw(estimatedRotation.y * AVATAR_HEAD_YAW_MAGNIFY);
|
||||
_head.setRoll(estimatedRotation.z * AVATAR_HEAD_ROLL_MAGNIFY);
|
||||
_head.setCameraFollowsHead(gyroLook);
|
||||
|
||||
// Update torso lean distance based on accelerometer data
|
||||
|
@ -391,7 +396,7 @@ void Avatar::updateThrust(float deltaTime, Transmitter * transmitter) {
|
|||
//
|
||||
// Gather thrust information from keyboard and sensors to apply to avatar motion
|
||||
//
|
||||
glm::quat orientation = getHead().getOrientation();
|
||||
glm::quat orientation = getHead().getCameraOrientation();
|
||||
glm::vec3 front = orientation * IDENTITY_FRONT;
|
||||
glm::vec3 right = orientation * IDENTITY_RIGHT;
|
||||
glm::vec3 up = orientation * IDENTITY_UP;
|
||||
|
@ -493,14 +498,17 @@ void Avatar::follow(Avatar* leadingAvatar) {
|
|||
|
||||
_leadingAvatar = leadingAvatar;
|
||||
if (_leadingAvatar != NULL) {
|
||||
_leaderID = leadingAvatar->getOwningNode()->getNodeID();
|
||||
_stringLength = glm::length(_position - _leadingAvatar->getPosition()) / _scale;
|
||||
if (_stringLength > MAX_STRING_LENGTH) {
|
||||
_stringLength = MAX_STRING_LENGTH;
|
||||
}
|
||||
} else {
|
||||
_leaderID = UNKNOWN_NODE_ID;
|
||||
}
|
||||
}
|
||||
|
||||
void Avatar::simulate(float deltaTime, Transmitter* transmitter) {
|
||||
void Avatar::simulate(float deltaTime, Transmitter* transmitter, float gyroCameraSensitivity) {
|
||||
|
||||
glm::quat orientation = getOrientation();
|
||||
glm::vec3 front = orientation * IDENTITY_FRONT;
|
||||
|
@ -623,9 +631,12 @@ void Avatar::simulate(float deltaTime, Transmitter* transmitter) {
|
|||
_velocity += _scale * _gravity * (GRAVITY_EARTH * deltaTime);
|
||||
}
|
||||
}
|
||||
updateCollisionWithEnvironment(deltaTime);
|
||||
updateCollisionWithVoxels(deltaTime);
|
||||
updateAvatarCollisions(deltaTime);
|
||||
|
||||
if (_isCollisionsOn) {
|
||||
updateCollisionWithEnvironment(deltaTime);
|
||||
updateCollisionWithVoxels(deltaTime);
|
||||
updateAvatarCollisions(deltaTime);
|
||||
}
|
||||
}
|
||||
|
||||
// update body balls
|
||||
|
@ -633,7 +644,7 @@ void Avatar::simulate(float deltaTime, Transmitter* transmitter) {
|
|||
|
||||
|
||||
// test for avatar collision response with the big sphere
|
||||
if (usingBigSphereCollisionTest) {
|
||||
if (usingBigSphereCollisionTest && _isCollisionsOn) {
|
||||
updateCollisionWithSphere(_TEST_bigSpherePosition, _TEST_bigSphereRadius, deltaTime);
|
||||
}
|
||||
|
||||
|
@ -747,7 +758,9 @@ void Avatar::simulate(float deltaTime, Transmitter* transmitter) {
|
|||
_head.setPosition(_bodyBall[ BODY_BALL_HEAD_BASE ].position);
|
||||
_head.setScale(_scale);
|
||||
_head.setSkinColor(glm::vec3(SKIN_COLOR[0], SKIN_COLOR[1], SKIN_COLOR[2]));
|
||||
_head.simulate(deltaTime, isMyAvatar());
|
||||
_head.simulate(deltaTime, isMyAvatar(), gyroCameraSensitivity);
|
||||
_hand.simulate(deltaTime, isMyAvatar());
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -1146,6 +1159,10 @@ void Avatar::render(bool lookingInMirror, bool renderAvatarBalls) {
|
|||
glPopMatrix();
|
||||
}
|
||||
|
||||
if (Application::getInstance()->getAvatar()->getHand().isRaveGloveActive()) {
|
||||
_hand.setRaveLights(RAVE_LIGHTS_AVATAR);
|
||||
}
|
||||
|
||||
// render a simple round on the ground projected down from the avatar's position
|
||||
renderDiskShadow(_position, glm::vec3(0.0f, 1.0f, 0.0f), _scale * 0.1f, 0.2f);
|
||||
|
||||
|
@ -1207,6 +1224,21 @@ void Avatar::render(bool lookingInMirror, bool renderAvatarBalls) {
|
|||
}
|
||||
}
|
||||
|
||||
void Avatar::renderScreenTint(ScreenTintLayer layer, Camera& whichCamera) {
|
||||
|
||||
if (layer == SCREEN_TINT_BEFORE_AVATARS) {
|
||||
if (_hand.isRaveGloveActive()) {
|
||||
_hand.renderRaveGloveStage();
|
||||
}
|
||||
}
|
||||
else if (layer == SCREEN_TINT_BEFORE_AVATARS) {
|
||||
if (_hand.isRaveGloveActive()) {
|
||||
// Restore the world lighting
|
||||
Application::getInstance()->setupWorldLight(whichCamera);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Avatar::resetBodyBalls() {
|
||||
for (int b = 0; b < NUM_AVATAR_BODY_BALLS; b++) {
|
||||
|
||||
|
@ -1372,6 +1404,17 @@ void Avatar::renderBody(bool lookingInMirror, bool renderAvatarBalls) {
|
|||
for (int b = 0; b < NUM_AVATAR_BODY_BALLS; b++) {
|
||||
float alpha = getBallRenderAlpha(b, lookingInMirror);
|
||||
|
||||
// When in rave glove mode, don't show the arms at all.
|
||||
if (_hand.isRaveGloveActive()) {
|
||||
if (b == BODY_BALL_LEFT_ELBOW
|
||||
|| b == BODY_BALL_LEFT_WRIST
|
||||
|| b == BODY_BALL_LEFT_FINGERTIPS
|
||||
|| b == BODY_BALL_RIGHT_ELBOW
|
||||
|| b == BODY_BALL_RIGHT_WRIST
|
||||
|| b == BODY_BALL_RIGHT_FINGERTIPS) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
// Always render other people, and render myself when beyond threshold distance
|
||||
if (b == BODY_BALL_HEAD_BASE) { // the head is rendered as a special
|
||||
if (alpha > 0.0f) {
|
||||
|
|
|
@ -27,7 +27,7 @@
|
|||
#include "world.h"
|
||||
|
||||
|
||||
static const float MAX_SCALE = 5.f;
|
||||
static const float MAX_SCALE = 10.f;
|
||||
static const float MIN_SCALE = .5f;
|
||||
static const float SCALING_RATIO = .05f;
|
||||
static const float SMOOTHING_RATIO = .05f; // 0 < ratio < 1
|
||||
|
@ -112,6 +112,15 @@ enum AvatarMode
|
|||
NUM_AVATAR_MODES
|
||||
};
|
||||
|
||||
enum ScreenTintLayer
|
||||
{
|
||||
SCREEN_TINT_BEFORE_LANDSCAPE = 0,
|
||||
SCREEN_TINT_BEFORE_AVATARS,
|
||||
SCREEN_TINT_BEFORE_MY_AVATAR,
|
||||
SCREEN_TINT_AFTER_AVATARS,
|
||||
NUM_SCREEN_TINT_LAYERS
|
||||
};
|
||||
|
||||
class Avatar : public AvatarData {
|
||||
public:
|
||||
Avatar(Node* owningNode = NULL);
|
||||
|
@ -119,16 +128,15 @@ public:
|
|||
|
||||
void init();
|
||||
void reset();
|
||||
void simulate(float deltaTime, Transmitter* transmitter);
|
||||
void simulate(float deltaTime, Transmitter* transmitter, float gyroCameraSensitivity);
|
||||
void updateThrust(float deltaTime, Transmitter * transmitter);
|
||||
void follow(Avatar* leadingAvatar);
|
||||
void updateFromGyrosAndOrWebcam(bool gyroLook,
|
||||
const glm::vec3& amplifyAngle,
|
||||
float yawFromTouch,
|
||||
float pitchFromTouch);
|
||||
void addBodyYaw(float bodyYaw) {_bodyYaw += bodyYaw;};
|
||||
void addBodyYawDelta(float bodyYawDelta) {_bodyYawDelta += bodyYawDelta;}
|
||||
void render(bool lookingInMirror, bool renderAvatarBalls);
|
||||
void renderScreenTint(ScreenTintLayer layer, Camera& whichCamera);
|
||||
|
||||
//setters
|
||||
void setMousePressed (bool mousePressed ) { _mousePressed = mousePressed;}
|
||||
|
@ -142,6 +150,7 @@ public:
|
|||
void setMouseRay (const glm::vec3 &origin, const glm::vec3 &direction);
|
||||
void setOrientation (const glm::quat& orientation);
|
||||
void setNewScale (const float scale);
|
||||
void setWantCollisionsOn (bool wantCollisionsOn ) { _isCollisionsOn = wantCollisionsOn; }
|
||||
|
||||
//getters
|
||||
bool isInitialized () const { return _initialized;}
|
||||
|
@ -263,6 +272,7 @@ private:
|
|||
glm::vec3 _lastCollisionPosition;
|
||||
bool _speedBrakes;
|
||||
bool _isThrustOn;
|
||||
bool _isCollisionsOn;
|
||||
|
||||
Avatar* _leadingAvatar;
|
||||
float _stringLength;
|
||||
|
|
|
@ -22,10 +22,10 @@
|
|||
|
||||
using namespace cv;
|
||||
|
||||
ProgramObject* Face::_program = 0;
|
||||
int Face::_texCoordCornerLocation;
|
||||
int Face::_texCoordRightLocation;
|
||||
int Face::_texCoordUpLocation;
|
||||
ProgramObject* Face::_videoProgram = 0;
|
||||
Face::Locations Face::_videoProgramLocations;
|
||||
ProgramObject* Face::_texturedProgram = 0;
|
||||
Face::Locations Face::_texturedProgramLocations;
|
||||
GLuint Face::_vboID;
|
||||
GLuint Face::_iboID;
|
||||
|
||||
|
@ -71,6 +71,7 @@ void Face::setFrameFromWebcam() {
|
|||
|
||||
void Face::clearFrame() {
|
||||
_colorTextureID = 0;
|
||||
_depthTextureID = 0;
|
||||
}
|
||||
|
||||
int Face::processVideoMessage(unsigned char* packetData, size_t dataBytes) {
|
||||
|
@ -111,128 +112,140 @@ int Face::processVideoMessage(unsigned char* packetData, size_t dataBytes) {
|
|||
return dataBytes;
|
||||
}
|
||||
|
||||
// the switch from full frame to not (or vice versa) requires us to reinit the codecs
|
||||
// the switch between full frame or depth only modes requires us to reinit the codecs
|
||||
float aspectRatio = *(const float*)_arrivingFrame.constData();
|
||||
size_t colorSize = *(const size_t*)(_arrivingFrame.constData() + sizeof(float));
|
||||
bool fullFrame = (aspectRatio == FULL_FRAME_ASPECT);
|
||||
if (fullFrame != _lastFullFrame) {
|
||||
bool depthOnly = (colorSize == 0);
|
||||
if (fullFrame != _lastFullFrame || depthOnly != _lastDepthOnly) {
|
||||
destroyCodecs();
|
||||
_lastFullFrame = fullFrame;
|
||||
_lastDepthOnly = depthOnly;
|
||||
}
|
||||
|
||||
if (_colorCodec.name == 0) {
|
||||
// initialize decoder context
|
||||
vpx_codec_dec_init(&_colorCodec, vpx_codec_vp8_dx(), 0, 0);
|
||||
}
|
||||
|
||||
size_t colorSize = *(const size_t*)(_arrivingFrame.constData() + sizeof(float));
|
||||
// read the color data, if non-empty
|
||||
Mat color;
|
||||
const uint8_t* colorData = (const uint8_t*)(_arrivingFrame.constData() + sizeof(float) + sizeof(size_t));
|
||||
vpx_codec_decode(&_colorCodec, colorData, colorSize, 0, 0);
|
||||
vpx_codec_iter_t iterator = 0;
|
||||
vpx_image_t* image;
|
||||
while ((image = vpx_codec_get_frame(&_colorCodec, &iterator)) != 0) {
|
||||
// convert from YV12 to RGB: see http://www.fourcc.org/yuv.php and
|
||||
// http://docs.opencv.org/modules/imgproc/doc/miscellaneous_transformations.html#cvtcolor
|
||||
Mat color(image->d_h, image->d_w, CV_8UC3);
|
||||
uchar* yline = image->planes[0];
|
||||
uchar* vline = image->planes[1];
|
||||
uchar* uline = image->planes[2];
|
||||
const int RED_V_WEIGHT = (int)(1.403 * 256);
|
||||
const int GREEN_V_WEIGHT = (int)(0.714 * 256);
|
||||
const int GREEN_U_WEIGHT = (int)(0.344 * 256);
|
||||
const int BLUE_U_WEIGHT = (int)(1.773 * 256);
|
||||
for (int i = 0; i < image->d_h; i += 2) {
|
||||
uchar* ysrc = yline;
|
||||
uchar* vsrc = vline;
|
||||
uchar* usrc = uline;
|
||||
for (int j = 0; j < image->d_w; j += 2) {
|
||||
uchar* tl = color.ptr(i, j);
|
||||
uchar* tr = color.ptr(i, j + 1);
|
||||
uchar* bl = color.ptr(i + 1, j);
|
||||
uchar* br = color.ptr(i + 1, j + 1);
|
||||
|
||||
int v = *vsrc++ - 128;
|
||||
int u = *usrc++ - 128;
|
||||
|
||||
int redOffset = (RED_V_WEIGHT * v) >> 8;
|
||||
int greenOffset = (GREEN_V_WEIGHT * v + GREEN_U_WEIGHT * u) >> 8;
|
||||
int blueOffset = (BLUE_U_WEIGHT * u) >> 8;
|
||||
|
||||
int ytl = ysrc[0];
|
||||
int ytr = ysrc[1];
|
||||
int ybl = ysrc[image->w];
|
||||
int ybr = ysrc[image->w + 1];
|
||||
ysrc += 2;
|
||||
|
||||
tl[0] = saturate_cast<uchar>(ytl + redOffset);
|
||||
tl[1] = saturate_cast<uchar>(ytl - greenOffset);
|
||||
tl[2] = saturate_cast<uchar>(ytl + blueOffset);
|
||||
|
||||
tr[0] = saturate_cast<uchar>(ytr + redOffset);
|
||||
tr[1] = saturate_cast<uchar>(ytr - greenOffset);
|
||||
tr[2] = saturate_cast<uchar>(ytr + blueOffset);
|
||||
|
||||
bl[0] = saturate_cast<uchar>(ybl + redOffset);
|
||||
bl[1] = saturate_cast<uchar>(ybl - greenOffset);
|
||||
bl[2] = saturate_cast<uchar>(ybl + blueOffset);
|
||||
|
||||
br[0] = saturate_cast<uchar>(ybr + redOffset);
|
||||
br[1] = saturate_cast<uchar>(ybr - greenOffset);
|
||||
br[2] = saturate_cast<uchar>(ybr + blueOffset);
|
||||
}
|
||||
yline += image->stride[0] * 2;
|
||||
vline += image->stride[1];
|
||||
uline += image->stride[2];
|
||||
if (colorSize > 0) {
|
||||
if (_colorCodec.name == 0) {
|
||||
// initialize decoder context
|
||||
vpx_codec_dec_init(&_colorCodec, vpx_codec_vp8_dx(), 0, 0);
|
||||
}
|
||||
Mat depth;
|
||||
|
||||
const uint8_t* depthData = colorData + colorSize;
|
||||
int depthSize = _arrivingFrame.size() - ((const char*)depthData - _arrivingFrame.constData());
|
||||
if (depthSize > 0) {
|
||||
if (_depthCodec.name == 0) {
|
||||
// initialize decoder context
|
||||
vpx_codec_dec_init(&_depthCodec, vpx_codec_vp8_dx(), 0, 0);
|
||||
}
|
||||
vpx_codec_decode(&_depthCodec, depthData, depthSize, 0, 0);
|
||||
vpx_codec_iter_t iterator = 0;
|
||||
vpx_image_t* image;
|
||||
while ((image = vpx_codec_get_frame(&_depthCodec, &iterator)) != 0) {
|
||||
depth.create(image->d_h, image->d_w, CV_8UC1);
|
||||
uchar* yline = image->planes[0];
|
||||
uchar* vline = image->planes[1];
|
||||
const uchar EIGHT_BIT_MAXIMUM = 255;
|
||||
const uchar MASK_THRESHOLD = 192;
|
||||
for (int i = 0; i < image->d_h; i += 2) {
|
||||
uchar* ysrc = yline;
|
||||
uchar* vsrc = vline;
|
||||
for (int j = 0; j < image->d_w; j += 2) {
|
||||
if (*vsrc++ < MASK_THRESHOLD) {
|
||||
*depth.ptr(i, j) = EIGHT_BIT_MAXIMUM;
|
||||
*depth.ptr(i, j + 1) = EIGHT_BIT_MAXIMUM;
|
||||
*depth.ptr(i + 1, j) = EIGHT_BIT_MAXIMUM;
|
||||
*depth.ptr(i + 1, j + 1) = EIGHT_BIT_MAXIMUM;
|
||||
|
||||
} else {
|
||||
*depth.ptr(i, j) = ysrc[0];
|
||||
*depth.ptr(i, j + 1) = ysrc[1];
|
||||
*depth.ptr(i + 1, j) = ysrc[image->stride[0]];
|
||||
*depth.ptr(i + 1, j + 1) = ysrc[image->stride[0] + 1];
|
||||
}
|
||||
ysrc += 2;
|
||||
}
|
||||
yline += image->stride[0] * 2;
|
||||
vline += image->stride[1];
|
||||
vpx_codec_decode(&_colorCodec, colorData, colorSize, 0, 0);
|
||||
vpx_codec_iter_t iterator = 0;
|
||||
vpx_image_t* image;
|
||||
while ((image = vpx_codec_get_frame(&_colorCodec, &iterator)) != 0) {
|
||||
// convert from YV12 to RGB: see http://www.fourcc.org/yuv.php and
|
||||
// http://docs.opencv.org/modules/imgproc/doc/miscellaneous_transformations.html#cvtcolor
|
||||
color.create(image->d_h, image->d_w, CV_8UC3);
|
||||
uchar* yline = image->planes[0];
|
||||
uchar* vline = image->planes[1];
|
||||
uchar* uline = image->planes[2];
|
||||
const int RED_V_WEIGHT = (int)(1.403 * 256);
|
||||
const int GREEN_V_WEIGHT = (int)(0.714 * 256);
|
||||
const int GREEN_U_WEIGHT = (int)(0.344 * 256);
|
||||
const int BLUE_U_WEIGHT = (int)(1.773 * 256);
|
||||
for (int i = 0; i < image->d_h; i += 2) {
|
||||
uchar* ysrc = yline;
|
||||
uchar* vsrc = vline;
|
||||
uchar* usrc = uline;
|
||||
for (int j = 0; j < image->d_w; j += 2) {
|
||||
uchar* tl = color.ptr(i, j);
|
||||
uchar* tr = color.ptr(i, j + 1);
|
||||
uchar* bl = color.ptr(i + 1, j);
|
||||
uchar* br = color.ptr(i + 1, j + 1);
|
||||
|
||||
int v = *vsrc++ - 128;
|
||||
int u = *usrc++ - 128;
|
||||
|
||||
int redOffset = (RED_V_WEIGHT * v) >> 8;
|
||||
int greenOffset = (GREEN_V_WEIGHT * v + GREEN_U_WEIGHT * u) >> 8;
|
||||
int blueOffset = (BLUE_U_WEIGHT * u) >> 8;
|
||||
|
||||
int ytl = ysrc[0];
|
||||
int ytr = ysrc[1];
|
||||
int ybl = ysrc[image->w];
|
||||
int ybr = ysrc[image->w + 1];
|
||||
ysrc += 2;
|
||||
|
||||
tl[0] = saturate_cast<uchar>(ytl + redOffset);
|
||||
tl[1] = saturate_cast<uchar>(ytl - greenOffset);
|
||||
tl[2] = saturate_cast<uchar>(ytl + blueOffset);
|
||||
|
||||
tr[0] = saturate_cast<uchar>(ytr + redOffset);
|
||||
tr[1] = saturate_cast<uchar>(ytr - greenOffset);
|
||||
tr[2] = saturate_cast<uchar>(ytr + blueOffset);
|
||||
|
||||
bl[0] = saturate_cast<uchar>(ybl + redOffset);
|
||||
bl[1] = saturate_cast<uchar>(ybl - greenOffset);
|
||||
bl[2] = saturate_cast<uchar>(ybl + blueOffset);
|
||||
|
||||
br[0] = saturate_cast<uchar>(ybr + redOffset);
|
||||
br[1] = saturate_cast<uchar>(ybr - greenOffset);
|
||||
br[2] = saturate_cast<uchar>(ybr + blueOffset);
|
||||
}
|
||||
yline += image->stride[0] * 2;
|
||||
vline += image->stride[1];
|
||||
uline += image->stride[2];
|
||||
}
|
||||
}
|
||||
QMetaObject::invokeMethod(this, "setFrame", Q_ARG(cv::Mat, color),
|
||||
Q_ARG(cv::Mat, depth), Q_ARG(float, aspectRatio));
|
||||
} else if (_colorCodec.name != 0) {
|
||||
vpx_codec_destroy(&_colorCodec);
|
||||
_colorCodec.name = 0;
|
||||
}
|
||||
|
||||
// read the depth data, if non-empty
|
||||
Mat depth;
|
||||
const uint8_t* depthData = colorData + colorSize;
|
||||
int depthSize = _arrivingFrame.size() - ((const char*)depthData - _arrivingFrame.constData());
|
||||
if (depthSize > 0) {
|
||||
if (_depthCodec.name == 0) {
|
||||
// initialize decoder context
|
||||
vpx_codec_dec_init(&_depthCodec, vpx_codec_vp8_dx(), 0, 0);
|
||||
}
|
||||
vpx_codec_decode(&_depthCodec, depthData, depthSize, 0, 0);
|
||||
vpx_codec_iter_t iterator = 0;
|
||||
vpx_image_t* image;
|
||||
while ((image = vpx_codec_get_frame(&_depthCodec, &iterator)) != 0) {
|
||||
depth.create(image->d_h, image->d_w, CV_8UC1);
|
||||
uchar* yline = image->planes[0];
|
||||
uchar* vline = image->planes[1];
|
||||
const uchar EIGHT_BIT_MAXIMUM = 255;
|
||||
const uchar MASK_THRESHOLD = 192;
|
||||
for (int i = 0; i < image->d_h; i += 2) {
|
||||
uchar* ysrc = yline;
|
||||
uchar* vsrc = vline;
|
||||
for (int j = 0; j < image->d_w; j += 2) {
|
||||
if (*vsrc++ < MASK_THRESHOLD) {
|
||||
*depth.ptr(i, j) = EIGHT_BIT_MAXIMUM;
|
||||
*depth.ptr(i, j + 1) = EIGHT_BIT_MAXIMUM;
|
||||
*depth.ptr(i + 1, j) = EIGHT_BIT_MAXIMUM;
|
||||
*depth.ptr(i + 1, j + 1) = EIGHT_BIT_MAXIMUM;
|
||||
|
||||
} else {
|
||||
*depth.ptr(i, j) = ysrc[0];
|
||||
*depth.ptr(i, j + 1) = ysrc[1];
|
||||
*depth.ptr(i + 1, j) = ysrc[image->stride[0]];
|
||||
*depth.ptr(i + 1, j + 1) = ysrc[image->stride[0] + 1];
|
||||
}
|
||||
ysrc += 2;
|
||||
}
|
||||
yline += image->stride[0] * 2;
|
||||
vline += image->stride[1];
|
||||
}
|
||||
}
|
||||
} else if (_depthCodec.name != 0) {
|
||||
vpx_codec_destroy(&_depthCodec);
|
||||
_depthCodec.name = 0;
|
||||
}
|
||||
QMetaObject::invokeMethod(this, "setFrame", Q_ARG(cv::Mat, color),
|
||||
Q_ARG(cv::Mat, depth), Q_ARG(float, aspectRatio));
|
||||
|
||||
return dataBytes;
|
||||
}
|
||||
|
||||
bool Face::render(float alpha) {
|
||||
if (_colorTextureID == 0 || _textureRect.size.area() == 0) {
|
||||
if (!isActive()) {
|
||||
return false;
|
||||
}
|
||||
glPushMatrix();
|
||||
|
@ -275,20 +288,9 @@ bool Face::render(float alpha) {
|
|||
const int INDICES_PER_TRIANGLE = 3;
|
||||
const int INDEX_COUNT = QUAD_COUNT * TRIANGLES_PER_QUAD * INDICES_PER_TRIANGLE;
|
||||
|
||||
if (_program == 0) {
|
||||
_program = new ProgramObject();
|
||||
_program->addShaderFromSourceFile(QGLShader::Vertex, "resources/shaders/face.vert");
|
||||
_program->addShaderFromSourceFile(QGLShader::Fragment, "resources/shaders/face.frag");
|
||||
_program->link();
|
||||
|
||||
_program->bind();
|
||||
_program->setUniformValue("depthTexture", 0);
|
||||
_program->setUniformValue("colorTexture", 1);
|
||||
_program->release();
|
||||
|
||||
_texCoordCornerLocation = _program->uniformLocation("texCoordCorner");
|
||||
_texCoordRightLocation = _program->uniformLocation("texCoordRight");
|
||||
_texCoordUpLocation = _program->uniformLocation("texCoordUp");
|
||||
if (_videoProgram == 0) {
|
||||
_videoProgram = loadProgram(QString(), "colorTexture", _videoProgramLocations);
|
||||
_texturedProgram = loadProgram("_textured", "permutationNormalTexture", _texturedProgramLocations);
|
||||
|
||||
glGenBuffers(1, &_vboID);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, _vboID);
|
||||
|
@ -328,14 +330,23 @@ bool Face::render(float alpha) {
|
|||
glBindTexture(GL_TEXTURE_2D, _depthTextureID);
|
||||
|
||||
glActiveTexture(GL_TEXTURE1);
|
||||
glBindTexture(GL_TEXTURE_2D, _colorTextureID);
|
||||
|
||||
_program->bind();
|
||||
_program->setUniformValue(_texCoordCornerLocation,
|
||||
ProgramObject* program = _videoProgram;
|
||||
Locations* locations = &_videoProgramLocations;
|
||||
if (_colorTextureID != 0) {
|
||||
glBindTexture(GL_TEXTURE_2D, _colorTextureID);
|
||||
|
||||
} else {
|
||||
glBindTexture(GL_TEXTURE_2D, Application::getInstance()->getTextureCache()->getPermutationNormalTextureID());
|
||||
program = _texturedProgram;
|
||||
locations = &_texturedProgramLocations;
|
||||
}
|
||||
program->bind();
|
||||
program->setUniformValue(locations->texCoordCorner,
|
||||
points[0].x / _textureSize.width, points[0].y / _textureSize.height);
|
||||
_program->setUniformValue(_texCoordRightLocation,
|
||||
program->setUniformValue(locations->texCoordRight,
|
||||
(points[3].x - points[0].x) / _textureSize.width, (points[3].y - points[0].y) / _textureSize.height);
|
||||
_program->setUniformValue(_texCoordUpLocation,
|
||||
program->setUniformValue(locations->texCoordUp,
|
||||
(points[1].x - points[0].x) / _textureSize.width, (points[1].y - points[0].y) / _textureSize.height);
|
||||
glEnableClientState(GL_VERTEX_ARRAY);
|
||||
glVertexPointer(2, GL_FLOAT, 0, 0);
|
||||
|
@ -357,7 +368,7 @@ bool Face::render(float alpha) {
|
|||
|
||||
glDisableClientState(GL_VERTEX_ARRAY);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||
_program->release();
|
||||
program->release();
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
|
@ -392,53 +403,52 @@ void Face::cycleRenderMode() {
|
|||
}
|
||||
|
||||
void Face::setFrame(const cv::Mat& color, const cv::Mat& depth, float aspectRatio) {
|
||||
if (color.empty()) {
|
||||
// release our textures, if any; there's no more video
|
||||
if (_colorTextureID != 0) {
|
||||
glDeleteTextures(1, &_colorTextureID);
|
||||
_colorTextureID = 0;
|
||||
Size2f textureSize = _textureSize;
|
||||
if (!color.empty()) {
|
||||
bool generate = (_colorTextureID == 0);
|
||||
if (generate) {
|
||||
glGenTextures(1, &_colorTextureID);
|
||||
}
|
||||
if (_depthTextureID != 0) {
|
||||
glDeleteTextures(1, &_depthTextureID);
|
||||
_depthTextureID = 0;
|
||||
glBindTexture(GL_TEXTURE_2D, _colorTextureID);
|
||||
if (_textureSize.width != color.cols || _textureSize.height != color.rows || generate) {
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, color.cols, color.rows, 0, GL_RGB, GL_UNSIGNED_BYTE, color.ptr());
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
textureSize = color.size();
|
||||
_textureRect = RotatedRect(Point2f(color.cols * 0.5f, color.rows * 0.5f), textureSize, 0.0f);
|
||||
|
||||
} else {
|
||||
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, color.cols, color.rows, GL_RGB, GL_UNSIGNED_BYTE, color.ptr());
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (_colorTextureID == 0) {
|
||||
glGenTextures(1, &_colorTextureID);
|
||||
}
|
||||
glBindTexture(GL_TEXTURE_2D, _colorTextureID);
|
||||
bool recreateTextures = (_textureSize.width != color.cols || _textureSize.height != color.rows);
|
||||
if (recreateTextures) {
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, color.cols, color.rows, 0, GL_RGB, GL_UNSIGNED_BYTE, color.ptr());
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
_textureSize = color.size();
|
||||
_textureRect = RotatedRect(Point2f(color.cols * 0.5f, color.rows * 0.5f), _textureSize, 0.0f);
|
||||
|
||||
} else {
|
||||
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, color.cols, color.rows, GL_RGB, GL_UNSIGNED_BYTE, color.ptr());
|
||||
} else if (_colorTextureID != 0) {
|
||||
glDeleteTextures(1, &_colorTextureID);
|
||||
_colorTextureID = 0;
|
||||
}
|
||||
|
||||
if (!depth.empty()) {
|
||||
if (_depthTextureID == 0) {
|
||||
bool generate = (_depthTextureID == 0);
|
||||
if (generate) {
|
||||
glGenTextures(1, &_depthTextureID);
|
||||
}
|
||||
glBindTexture(GL_TEXTURE_2D, _depthTextureID);
|
||||
if (recreateTextures) {
|
||||
if (_textureSize.width != depth.cols || _textureSize.height != depth.rows || generate) {
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, depth.cols, depth.rows, 0,
|
||||
GL_LUMINANCE, GL_UNSIGNED_BYTE, depth.ptr());
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
textureSize = depth.size();
|
||||
_textureRect = RotatedRect(Point2f(depth.cols * 0.5f, depth.rows * 0.5f), textureSize, 0.0f);
|
||||
|
||||
} else {
|
||||
glBindTexture(GL_TEXTURE_2D, _depthTextureID);
|
||||
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, depth.cols, depth.rows, GL_LUMINANCE, GL_UNSIGNED_BYTE, depth.ptr());
|
||||
}
|
||||
} else if (_depthTextureID != 0) {
|
||||
glDeleteTextures(1, &_depthTextureID);
|
||||
_depthTextureID = 0;
|
||||
}
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
|
||||
_aspectRatio = aspectRatio;
|
||||
_textureSize = textureSize;
|
||||
}
|
||||
|
||||
void Face::destroyCodecs() {
|
||||
|
@ -451,3 +461,21 @@ void Face::destroyCodecs() {
|
|||
_depthCodec.name = 0;
|
||||
}
|
||||
}
|
||||
|
||||
ProgramObject* Face::loadProgram(const QString& suffix, const char* secondTextureUniform, Locations& locations) {
|
||||
ProgramObject* program = new ProgramObject();
|
||||
program->addShaderFromSourceFile(QGLShader::Vertex, "resources/shaders/face" + suffix + ".vert");
|
||||
program->addShaderFromSourceFile(QGLShader::Fragment, "resources/shaders/face" + suffix + ".frag");
|
||||
program->link();
|
||||
|
||||
program->bind();
|
||||
program->setUniformValue("depthTexture", 0);
|
||||
program->setUniformValue(secondTextureUniform, 1);
|
||||
program->release();
|
||||
|
||||
locations.texCoordCorner = program->uniformLocation("texCoordCorner");
|
||||
locations.texCoordRight = program->uniformLocation("texCoordRight");
|
||||
locations.texCoordUp = program->uniformLocation("texCoordUp");
|
||||
|
||||
return program;
|
||||
}
|
||||
|
|
|
@ -30,7 +30,8 @@ public:
|
|||
Face(Head* owningHead);
|
||||
~Face();
|
||||
|
||||
bool isFullFrame() const { return _colorTextureID != 0 && _aspectRatio == FULL_FRAME_ASPECT; }
|
||||
bool isActive() const { return _colorTextureID != 0 || _depthTextureID != 0; }
|
||||
bool isFullFrame() const { return isActive() && _aspectRatio == FULL_FRAME_ASPECT; }
|
||||
|
||||
void setFrameFromWebcam();
|
||||
void clearFrame();
|
||||
|
@ -64,15 +65,26 @@ private:
|
|||
vpx_codec_ctx_t _colorCodec;
|
||||
vpx_codec_ctx_t _depthCodec;
|
||||
bool _lastFullFrame;
|
||||
bool _lastDepthOnly;
|
||||
|
||||
QByteArray _arrivingFrame;
|
||||
int _frameCount;
|
||||
int _frameBytesRemaining;
|
||||
|
||||
static ProgramObject* _program;
|
||||
static int _texCoordCornerLocation;
|
||||
static int _texCoordRightLocation;
|
||||
static int _texCoordUpLocation;
|
||||
struct Locations {
|
||||
int texCoordCorner;
|
||||
int texCoordRight;
|
||||
int texCoordUp;
|
||||
};
|
||||
|
||||
static ProgramObject* loadProgram(const QString& suffix, const char* secondTextureUniform, Locations& locations);
|
||||
|
||||
static ProgramObject* _videoProgram;
|
||||
static Locations _videoProgramLocations;
|
||||
|
||||
static ProgramObject* _texturedProgram;
|
||||
static Locations _texturedProgramLocations;
|
||||
|
||||
static GLuint _vboID;
|
||||
static GLuint _iboID;
|
||||
};
|
||||
|
|
|
@ -42,6 +42,9 @@ void Hand::init() {
|
|||
else {
|
||||
_ballColor = glm::vec3(0.0, 0.0, 0.4);
|
||||
}
|
||||
|
||||
_raveGloveEffectsMode = RAVE_GLOVE_EFFECTS_MODE_FIRE;
|
||||
_raveGloveEffectsModeChanged = false;
|
||||
}
|
||||
|
||||
void Hand::reset() {
|
||||
|
@ -51,15 +54,20 @@ void Hand::reset() {
|
|||
void Hand::simulate(float deltaTime, bool isMine) {
|
||||
|
||||
if (_isRaveGloveActive) {
|
||||
if (_raveGloveEffectsModeChanged && _raveGloveInitialized) {
|
||||
activateNewRaveGloveMode();
|
||||
_raveGloveEffectsModeChanged = false;
|
||||
}
|
||||
|
||||
updateRaveGloveParticles(deltaTime);
|
||||
}
|
||||
}
|
||||
|
||||
void Hand::calculateGeometry() {
|
||||
glm::vec3 offset(0.2, -0.2, -0.3); // place the hand in front of the face where we can see it
|
||||
const glm::vec3 leapHandsOffsetFromFace(0.0, -0.2, -0.3); // place the hand in front of the face where we can see it
|
||||
|
||||
Head& head = _owningAvatar->getHead();
|
||||
_basePosition = head.getPosition() + head.getOrientation() * offset;
|
||||
_basePosition = head.getPosition() + head.getOrientation() * leapHandsOffsetFromFace;
|
||||
_baseOrientation = head.getOrientation();
|
||||
|
||||
// generate finger tip balls....
|
||||
|
@ -106,18 +114,21 @@ void Hand::calculateGeometry() {
|
|||
}
|
||||
|
||||
void Hand::setRaveGloveEffectsMode(QKeyEvent* event) {
|
||||
|
||||
_raveGloveEffectsModeChanged = true;
|
||||
|
||||
switch (event->key()) {
|
||||
|
||||
case Qt::Key_0: setRaveGloveMode(RAVE_GLOVE_EFFECTS_MODE_THROBBING_COLOR); break;
|
||||
case Qt::Key_1: setRaveGloveMode(RAVE_GLOVE_EFFECTS_MODE_TRAILS ); break;
|
||||
case Qt::Key_2: setRaveGloveMode(RAVE_GLOVE_EFFECTS_MODE_FIRE ); break;
|
||||
case Qt::Key_3: setRaveGloveMode(RAVE_GLOVE_EFFECTS_MODE_WATER ); break;
|
||||
case Qt::Key_4: setRaveGloveMode(RAVE_GLOVE_EFFECTS_MODE_FLASHY ); break;
|
||||
case Qt::Key_5: setRaveGloveMode(RAVE_GLOVE_EFFECTS_MODE_BOZO_SPARKLER ); break;
|
||||
case Qt::Key_6: setRaveGloveMode(RAVE_GLOVE_EFFECTS_MODE_LONG_SPARKLER ); break;
|
||||
case Qt::Key_7: setRaveGloveMode(RAVE_GLOVE_EFFECTS_MODE_SNAKE ); break;
|
||||
case Qt::Key_8: setRaveGloveMode(RAVE_GLOVE_EFFECTS_MODE_PULSE ); break;
|
||||
case Qt::Key_9: setRaveGloveMode(RAVE_GLOVE_EFFECTS_MODE_THROB ); break;
|
||||
case Qt::Key_0: _raveGloveEffectsMode = RAVE_GLOVE_EFFECTS_MODE_THROBBING_COLOR; break;
|
||||
case Qt::Key_1: _raveGloveEffectsMode = RAVE_GLOVE_EFFECTS_MODE_TRAILS; break;
|
||||
case Qt::Key_2: _raveGloveEffectsMode = RAVE_GLOVE_EFFECTS_MODE_FIRE; break;
|
||||
case Qt::Key_3: _raveGloveEffectsMode = RAVE_GLOVE_EFFECTS_MODE_WATER; break;
|
||||
case Qt::Key_4: _raveGloveEffectsMode = RAVE_GLOVE_EFFECTS_MODE_FLASHY; break;
|
||||
case Qt::Key_5: _raveGloveEffectsMode = RAVE_GLOVE_EFFECTS_MODE_BOZO_SPARKLER; break;
|
||||
case Qt::Key_6: _raveGloveEffectsMode = RAVE_GLOVE_EFFECTS_MODE_LONG_SPARKLER; break;
|
||||
case Qt::Key_7: _raveGloveEffectsMode = RAVE_GLOVE_EFFECTS_MODE_SNAKE; break;
|
||||
case Qt::Key_8: _raveGloveEffectsMode = RAVE_GLOVE_EFFECTS_MODE_PULSE; break;
|
||||
case Qt::Key_9: _raveGloveEffectsMode = RAVE_GLOVE_EFFECTS_MODE_THROB; break;
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -129,10 +140,14 @@ void Hand::render(bool lookingInMirror) {
|
|||
calculateGeometry();
|
||||
|
||||
if (_isRaveGloveActive) {
|
||||
renderRaveGloveStage();
|
||||
// Disable raveGloveStage while we work on the network glove features
|
||||
// renderRaveGloveStage();
|
||||
|
||||
if (_raveGloveInitialized) {
|
||||
updateRaveGloveEmitters(); // do this after calculateGeometry
|
||||
|
||||
// Use normal lighting for the particles
|
||||
setRaveLights(RAVE_LIGHTS_PARTICLES);
|
||||
_raveGloveParticleSystem.render();
|
||||
}
|
||||
}
|
||||
|
@ -142,8 +157,35 @@ void Hand::render(bool lookingInMirror) {
|
|||
|
||||
if ( SHOW_LEAP_HAND ) {
|
||||
//renderLeapHands();
|
||||
renderLeapFingerTrails();
|
||||
renderLeapHandSpheres();
|
||||
if (!isRaveGloveActive()) {
|
||||
renderLeapFingerTrails();
|
||||
renderLeapHandSpheres();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Hand::setRaveLights(RaveLightsSetting setting) {
|
||||
if (setting == RAVE_LIGHTS_AVATAR) {
|
||||
// Set some mood lighting
|
||||
GLfloat ambient_color[] = { 0.0, 0.0, 0.0 };
|
||||
glLightfv(GL_LIGHT0, GL_AMBIENT, ambient_color);
|
||||
GLfloat diffuse_color[] = { 0.4, 0.0, 0.0 };
|
||||
glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuse_color);
|
||||
GLfloat specular_color[] = { 0.0, 0.0, 0.0, 0.0};
|
||||
glLightfv(GL_LIGHT0, GL_SPECULAR, specular_color);
|
||||
glMaterialfv(GL_FRONT, GL_SPECULAR, specular_color);
|
||||
glMateriali(GL_FRONT, GL_SHININESS, 0);
|
||||
}
|
||||
else if (setting == RAVE_LIGHTS_PARTICLES) {
|
||||
// particles use a brighter light setting
|
||||
GLfloat ambient_color[] = { 0.7, 0.7, 0.8 };
|
||||
glLightfv(GL_LIGHT0, GL_AMBIENT, ambient_color);
|
||||
GLfloat diffuse_color[] = { 0.8, 0.7, 0.7 };
|
||||
glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuse_color);
|
||||
GLfloat specular_color[] = { 1.0, 1.0, 1.0, 1.0};
|
||||
glLightfv(GL_LIGHT0, GL_SPECULAR, specular_color);
|
||||
glMaterialfv(GL_FRONT, GL_SPECULAR, specular_color);
|
||||
glMateriali(GL_FRONT, GL_SHININESS, 96);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -160,6 +202,7 @@ void Hand::renderRaveGloveStage() {
|
|||
glm::vec3 v3 = headOrientation * (glm::vec3(-1.0f, 1.0f, 0.0f) * scale) + vc;
|
||||
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
glDepthMask(GL_FALSE);
|
||||
glEnable(GL_BLEND);
|
||||
glBegin(GL_TRIANGLE_FAN);
|
||||
glColor4f(0.0f, 0.0f, 0.0f, 1.0f);
|
||||
|
@ -171,6 +214,7 @@ void Hand::renderRaveGloveStage() {
|
|||
glVertex3fv((float*)&v3);
|
||||
glVertex3fv((float*)&v0);
|
||||
glEnd();
|
||||
glDepthMask(GL_TRUE);
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
}
|
||||
}
|
||||
|
@ -340,6 +384,7 @@ void Hand::updateRaveGloveParticles(float deltaTime) {
|
|||
}
|
||||
|
||||
setRaveGloveMode(RAVE_GLOVE_EFFECTS_MODE_FIRE);
|
||||
activateNewRaveGloveMode();
|
||||
_raveGloveParticleSystem.setUpDirection(glm::vec3(0.0f, 1.0f, 0.0f));
|
||||
_raveGloveInitialized = true;
|
||||
} else {
|
||||
|
@ -347,12 +392,14 @@ void Hand::updateRaveGloveParticles(float deltaTime) {
|
|||
}
|
||||
}
|
||||
|
||||
// The rave glove mode has changed, so activate the effects.
|
||||
void Hand::activateNewRaveGloveMode() {
|
||||
|
||||
|
||||
void Hand::setRaveGloveMode(int mode) {
|
||||
|
||||
_raveGloveMode = mode;
|
||||
|
||||
if (!_raveGloveInitialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
int mode = _raveGloveEffectsMode;
|
||||
_raveGloveParticleSystem.killAllParticles();
|
||||
|
||||
for ( int f = 0; f< NUM_FINGERS; f ++ ) {
|
||||
|
|
|
@ -20,6 +20,11 @@
|
|||
#include <SharedUtil.h>
|
||||
#include <vector>
|
||||
|
||||
enum RaveLightsSetting {
|
||||
RAVE_LIGHTS_AVATAR = 0,
|
||||
RAVE_LIGHTS_PARTICLES
|
||||
};
|
||||
|
||||
class Avatar;
|
||||
class ProgramObject;
|
||||
|
||||
|
@ -41,6 +46,8 @@ public:
|
|||
void reset();
|
||||
void simulate(float deltaTime, bool isMine);
|
||||
void render(bool lookingInMirror);
|
||||
void renderRaveGloveStage();
|
||||
void setRaveLights(RaveLightsSetting setting);
|
||||
|
||||
void setBallColor (glm::vec3 ballColor ) { _ballColor = ballColor; }
|
||||
void updateRaveGloveParticles(float deltaTime);
|
||||
|
@ -72,8 +79,8 @@ private:
|
|||
void setLeapHands(const std::vector<glm::vec3>& handPositions,
|
||||
const std::vector<glm::vec3>& handNormals);
|
||||
|
||||
void renderRaveGloveStage();
|
||||
virtual void setRaveGloveMode(int mode);
|
||||
void activateNewRaveGloveMode();
|
||||
|
||||
void renderLeapHandSpheres();
|
||||
void renderLeapHands();
|
||||
void renderLeapHand(PalmData& hand);
|
||||
|
|
|
@ -84,6 +84,7 @@ Head::Head(Avatar* owningAvatar) :
|
|||
_rightEyeBlinkVelocity(0.0f),
|
||||
_timeWithoutTalking(0.0f),
|
||||
_cameraPitch(_pitch),
|
||||
_mousePitch(0.f),
|
||||
_cameraYaw(_yaw),
|
||||
_isCameraMoving(false),
|
||||
_cameraFollowsHead(false),
|
||||
|
@ -145,7 +146,7 @@ void Head::resetHairPhysics() {
|
|||
}
|
||||
|
||||
|
||||
void Head::simulate(float deltaTime, bool isMine) {
|
||||
void Head::simulate(float deltaTime, bool isMine, float gyroCameraSensitivity) {
|
||||
|
||||
// Update eye saccades
|
||||
const float AVERAGE_MICROSACCADE_INTERVAL = 0.50f;
|
||||
|
@ -227,15 +228,18 @@ void Head::simulate(float deltaTime, bool isMine) {
|
|||
}
|
||||
|
||||
// Update camera pitch and yaw independently from motion of head (for gyro-based interface)
|
||||
if (isMine && _cameraFollowsHead) {
|
||||
if (isMine && _cameraFollowsHead && (gyroCameraSensitivity > 0.f)) {
|
||||
// If we are using gyros and using gyroLook, have the camera follow head but with a null region
|
||||
// to create stable rendering view with small head movements.
|
||||
const float CAMERA_FOLLOW_HEAD_RATE_START = 0.01f;
|
||||
const float CAMERA_FOLLOW_HEAD_RATE_MAX = 0.5f;
|
||||
const float CAMERA_FOLLOW_HEAD_RATE_START = 0.1f;
|
||||
const float CAMERA_FOLLOW_HEAD_RATE_MAX = 1.0f;
|
||||
const float CAMERA_FOLLOW_HEAD_RATE_RAMP_RATE = 1.05f;
|
||||
const float CAMERA_STOP_TOLERANCE_DEGREES = 0.5f;
|
||||
const float CAMERA_PITCH_START_TOLERANCE_DEGREES = 20.0f;
|
||||
const float CAMERA_YAW_START_TOLERANCE_DEGREES = 10.0f;
|
||||
const float PITCH_START_RANGE = 20.f;
|
||||
const float YAW_START_RANGE = 10.f;
|
||||
float pitchStartTolerance = PITCH_START_RANGE * (1.f - gyroCameraSensitivity);
|
||||
float yawStartTolerance = YAW_START_RANGE * (1.f - gyroCameraSensitivity);
|
||||
|
||||
float cameraHeadAngleDifference = glm::length(glm::vec2(_pitch - _cameraPitch, _yaw - _cameraYaw));
|
||||
if (_isCameraMoving) {
|
||||
_cameraFollowHeadRate = glm::clamp(_cameraFollowHeadRate * CAMERA_FOLLOW_HEAD_RATE_RAMP_RATE,
|
||||
|
@ -248,17 +252,13 @@ void Head::simulate(float deltaTime, bool isMine) {
|
|||
_isCameraMoving = false;
|
||||
}
|
||||
} else {
|
||||
if ((fabs(_pitch - _cameraPitch) > CAMERA_PITCH_START_TOLERANCE_DEGREES) ||
|
||||
(fabs(_yaw - _cameraYaw) > CAMERA_YAW_START_TOLERANCE_DEGREES)) {
|
||||
if ((fabs(_pitch - _cameraPitch) > pitchStartTolerance) ||
|
||||
(fabs(_yaw - _cameraYaw) > yawStartTolerance)) {
|
||||
_isCameraMoving = true;
|
||||
_cameraFollowHeadRate = CAMERA_FOLLOW_HEAD_RATE_START;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Camera always locked to head
|
||||
_cameraPitch = _pitch;
|
||||
_cameraYaw = _yaw;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Head::calculateGeometry() {
|
||||
|
@ -338,7 +338,7 @@ void Head::setScale (float scale) {
|
|||
}
|
||||
|
||||
void Head::createMohawk() {
|
||||
uint16_t nodeId = 0;
|
||||
uint16_t nodeId = UNKNOWN_NODE_ID;
|
||||
if (_owningAvatar->getOwningNode()) {
|
||||
nodeId = _owningAvatar->getOwningNode()->getNodeID();
|
||||
} else {
|
||||
|
@ -428,7 +428,7 @@ glm::quat Head::getOrientation() const {
|
|||
glm::quat Head::getCameraOrientation () const {
|
||||
Avatar* owningAvatar = static_cast<Avatar*>(_owningAvatar);
|
||||
return owningAvatar->getWorldAlignedOrientation()
|
||||
* glm::quat(glm::radians(glm::vec3(_cameraPitch, _cameraYaw, 0.0f)));
|
||||
* glm::quat(glm::radians(glm::vec3(_cameraPitch + _mousePitch, _cameraYaw, 0.0f)));
|
||||
}
|
||||
|
||||
void Head::renderHeadSphere() {
|
||||
|
|
|
@ -41,7 +41,7 @@ public:
|
|||
|
||||
void init();
|
||||
void reset();
|
||||
void simulate(float deltaTime, bool isMine);
|
||||
void simulate(float deltaTime, bool isMine, float gyroCameraSensitivity);
|
||||
void render(float alpha);
|
||||
void renderMohawk();
|
||||
|
||||
|
@ -57,6 +57,8 @@ public:
|
|||
|
||||
void setCameraFollowsHead(bool cameraFollowsHead) { _cameraFollowsHead = cameraFollowsHead; }
|
||||
|
||||
void setMousePitch(float mousePitch) { _mousePitch = mousePitch; }
|
||||
|
||||
glm::quat getOrientation() const;
|
||||
glm::quat getCameraOrientation () const;
|
||||
|
||||
|
@ -123,6 +125,7 @@ private:
|
|||
float _rightEyeBlinkVelocity;
|
||||
float _timeWithoutTalking;
|
||||
float _cameraPitch; // Used to position the camera differently from the head
|
||||
float _mousePitch;
|
||||
float _cameraYaw;
|
||||
bool _isCameraMoving;
|
||||
bool _cameraFollowsHead;
|
||||
|
|
45
interface/src/renderer/TextureCache.cpp
Normal file
45
interface/src/renderer/TextureCache.cpp
Normal file
|
@ -0,0 +1,45 @@
|
|||
//
|
||||
// TextureCache.cpp
|
||||
// interface
|
||||
//
|
||||
// Created by Andrzej Kapolka on 8/6/13.
|
||||
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
|
||||
|
||||
#include <glm/gtc/random.hpp>
|
||||
|
||||
#include "TextureCache.h"
|
||||
|
||||
TextureCache::TextureCache() : _permutationNormalTextureID(0) {
|
||||
}
|
||||
|
||||
TextureCache::~TextureCache() {
|
||||
if (_permutationNormalTextureID != 0) {
|
||||
glDeleteTextures(1, &_permutationNormalTextureID);
|
||||
}
|
||||
}
|
||||
|
||||
GLuint TextureCache::getPermutationNormalTextureID() {
|
||||
if (_permutationNormalTextureID == 0) {
|
||||
glGenTextures(1, &_permutationNormalTextureID);
|
||||
glBindTexture(GL_TEXTURE_2D, _permutationNormalTextureID);
|
||||
|
||||
// the first line consists of random permutation offsets
|
||||
unsigned char data[256 * 2 * 3];
|
||||
for (int i = 0; i < 256 * 3; i++) {
|
||||
data[i] = rand() % 256;
|
||||
}
|
||||
// the next, random unit normals
|
||||
for (int i = 256 * 3; i < 256 * 3 * 2; i += 3) {
|
||||
glm::vec3 randvec = glm::sphericalRand(1.0f);
|
||||
data[i] = ((randvec.x + 1.0f) / 2.0f) * 255.0f;
|
||||
data[i + 1] = ((randvec.y + 1.0f) / 2.0f) * 255.0f;
|
||||
data[i + 2] = ((randvec.z + 1.0f) / 2.0f) * 255.0f;
|
||||
}
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 256, 2, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
}
|
||||
return _permutationNormalTextureID;
|
||||
}
|
27
interface/src/renderer/TextureCache.h
Normal file
27
interface/src/renderer/TextureCache.h
Normal file
|
@ -0,0 +1,27 @@
|
|||
//
|
||||
// TextureCache.h
|
||||
// interface
|
||||
//
|
||||
// Created by Andrzej Kapolka on 8/6/13.
|
||||
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef __interface__TextureCache__
|
||||
#define __interface__TextureCache__
|
||||
|
||||
#include "InterfaceConfig.h"
|
||||
|
||||
class TextureCache {
|
||||
public:
|
||||
|
||||
TextureCache();
|
||||
~TextureCache();
|
||||
|
||||
GLuint getPermutationNormalTextureID();
|
||||
|
||||
private:
|
||||
|
||||
GLuint _permutationNormalTextureID;
|
||||
};
|
||||
|
||||
#endif /* defined(__interface__TextureCache__) */
|
|
@ -28,6 +28,7 @@ AvatarData::AvatarData(Node* owningNode) :
|
|||
_bodyPitch(0.0),
|
||||
_bodyRoll(0.0),
|
||||
_newScale(1.0f),
|
||||
_leaderID(UNKNOWN_NODE_ID),
|
||||
_handState(0),
|
||||
_cameraPosition(0,0,0),
|
||||
_cameraOrientation(),
|
||||
|
@ -107,8 +108,14 @@ int AvatarData::getBroadcastData(unsigned char* destinationBuffer) {
|
|||
destinationBuffer += packFloatAngleToTwoByte(destinationBuffer, _bodyYaw);
|
||||
destinationBuffer += packFloatAngleToTwoByte(destinationBuffer, _bodyPitch);
|
||||
destinationBuffer += packFloatAngleToTwoByte(destinationBuffer, _bodyRoll);
|
||||
|
||||
// Body scale
|
||||
destinationBuffer += packFloatRatioToTwoByte(destinationBuffer, _newScale);
|
||||
|
||||
// Follow mode info
|
||||
memcpy(destinationBuffer, &_leaderID, sizeof(uint16_t));
|
||||
destinationBuffer += sizeof(uint16_t);
|
||||
|
||||
// Head rotation (NOTE: This needs to become a quaternion to save two bytes)
|
||||
destinationBuffer += packFloatAngleToTwoByte(destinationBuffer, _headData->_yaw);
|
||||
destinationBuffer += packFloatAngleToTwoByte(destinationBuffer, _headData->_pitch);
|
||||
|
@ -204,8 +211,14 @@ int AvatarData::parseData(unsigned char* sourceBuffer, int numBytes) {
|
|||
sourceBuffer += unpackFloatAngleFromTwoByte((uint16_t*) sourceBuffer, &_bodyYaw);
|
||||
sourceBuffer += unpackFloatAngleFromTwoByte((uint16_t*) sourceBuffer, &_bodyPitch);
|
||||
sourceBuffer += unpackFloatAngleFromTwoByte((uint16_t*) sourceBuffer, &_bodyRoll);
|
||||
|
||||
// Body scale
|
||||
sourceBuffer += unpackFloatRatioFromTwoByte( sourceBuffer, _newScale);
|
||||
|
||||
// Follow mode info
|
||||
memcpy(&_leaderID, sourceBuffer, sizeof(uint16_t));
|
||||
sourceBuffer += sizeof(uint16_t);
|
||||
|
||||
// Head rotation (NOTE: This needs to become a quaternion to save two bytes)
|
||||
float headYaw, headPitch, headRoll;
|
||||
sourceBuffer += unpackFloatAngleFromTwoByte((uint16_t*) sourceBuffer, &headYaw);
|
||||
|
|
|
@ -106,10 +106,11 @@ public:
|
|||
QString getQStringChatMessage() { return QString(_chatMessage.data()); }
|
||||
|
||||
// related to Voxel Sending strategies
|
||||
bool getWantColor() const { return _wantColor; }
|
||||
bool getWantDelta() const { return _wantDelta; }
|
||||
bool getWantLowResMoving() const { return _wantLowResMoving; }
|
||||
bool getWantColor() const { return _wantColor; }
|
||||
bool getWantDelta() const { return _wantDelta; }
|
||||
bool getWantLowResMoving() const { return _wantLowResMoving; }
|
||||
bool getWantOcclusionCulling() const { return _wantOcclusionCulling; }
|
||||
uint16_t getLeaderID() const { return _leaderID; }
|
||||
|
||||
void setWantColor(bool wantColor) { _wantColor = wantColor; }
|
||||
void setWantDelta(bool wantDelta) { _wantDelta = wantDelta; }
|
||||
|
@ -131,8 +132,13 @@ protected:
|
|||
float _bodyYaw;
|
||||
float _bodyPitch;
|
||||
float _bodyRoll;
|
||||
|
||||
// Body scale
|
||||
float _newScale;
|
||||
|
||||
// Following mode infos
|
||||
uint16_t _leaderID;
|
||||
|
||||
// Hand state (are we grabbing something or not)
|
||||
char _handState;
|
||||
|
||||
|
|
|
@ -20,7 +20,8 @@ HandData::HandData(AvatarData* owningAvatar) :
|
|||
_baseOrientation(0.0f, 0.0f, 0.0f, 1.0f),
|
||||
_owningAvatarData(owningAvatar),
|
||||
_isRaveGloveActive(false),
|
||||
_raveGloveMode(RAVE_GLOVE_EFFECTS_MODE_THROBBING_COLOR)
|
||||
_raveGloveEffectsMode(RAVE_GLOVE_EFFECTS_MODE_THROBBING_COLOR),
|
||||
_raveGloveEffectsModeChanged(false)
|
||||
{
|
||||
// Start with two palms
|
||||
addNewPalm();
|
||||
|
@ -160,8 +161,9 @@ int HandData::decodeRemoteData(unsigned char* sourceBuffer) {
|
|||
}
|
||||
|
||||
setRaveGloveActive((gloveFlags & GLOVE_FLAG_RAVE) != 0);
|
||||
// This is disabled for crash tracing.
|
||||
// setRaveGloveMode(effectsMode);
|
||||
if (numHands > 0) {
|
||||
setRaveGloveMode(effectsMode);
|
||||
}
|
||||
|
||||
// One byte for error checking safety.
|
||||
unsigned char requiredLength = (unsigned char)(sourceBuffer - startPosition);
|
||||
|
@ -171,6 +173,13 @@ int HandData::decodeRemoteData(unsigned char* sourceBuffer) {
|
|||
return sourceBuffer - startPosition;
|
||||
}
|
||||
|
||||
void HandData::setRaveGloveMode(int effectsMode) {
|
||||
if (effectsMode != _raveGloveEffectsMode) {
|
||||
_raveGloveEffectsModeChanged = true;
|
||||
}
|
||||
_raveGloveEffectsMode = effectsMode;
|
||||
}
|
||||
|
||||
void HandData::setFingerTrailLength(unsigned int length) {
|
||||
for (size_t i = 0; i < getNumPalms(); ++i) {
|
||||
PalmData& palm = getPalms()[i];
|
||||
|
|
|
@ -70,9 +70,9 @@ public:
|
|||
int decodeRemoteData(unsigned char* sourceBuffer);
|
||||
|
||||
void setRaveGloveActive(bool active) { _isRaveGloveActive = active; }
|
||||
virtual void setRaveGloveMode(int effectsMode) { _raveGloveMode = effectsMode; }
|
||||
void setRaveGloveMode(int effectsMode);
|
||||
bool isRaveGloveActive() const { return _isRaveGloveActive; }
|
||||
int getRaveGloveMode() { return _raveGloveMode; }
|
||||
int getRaveGloveMode() { return _raveGloveEffectsMode; }
|
||||
|
||||
friend class AvatarData;
|
||||
protected:
|
||||
|
@ -81,7 +81,8 @@ protected:
|
|||
AvatarData* _owningAvatarData;
|
||||
std::vector<PalmData> _palms;
|
||||
bool _isRaveGloveActive;
|
||||
int _raveGloveMode;
|
||||
int _raveGloveEffectsMode;
|
||||
bool _raveGloveEffectsModeChanged;
|
||||
private:
|
||||
// privatize copy ctor and assignment operator so copies of this object cannot be made
|
||||
HandData(const HandData&);
|
||||
|
|
|
@ -435,6 +435,8 @@ void NodeList::addNodeToList(Node* newNode) {
|
|||
++_numNodes;
|
||||
|
||||
qDebug() << "Added" << *newNode << "\n";
|
||||
|
||||
notifyHooksOfAddedNode(newNode);
|
||||
}
|
||||
|
||||
unsigned NodeList::broadcastToNodes(unsigned char *broadcastData, size_t dataBytes, const char* nodeTypes, int numNodeTypes) {
|
||||
|
@ -476,7 +478,7 @@ Node* NodeList::soloNodeOfType(char nodeType) {
|
|||
return NULL;
|
||||
}
|
||||
|
||||
void *removeSilentNodes(void *args) {
|
||||
void* removeSilentNodes(void *args) {
|
||||
NodeList* nodeList = (NodeList*) args;
|
||||
uint64_t checkTimeUSecs;
|
||||
int sleepTime;
|
||||
|
@ -490,6 +492,8 @@ void *removeSilentNodes(void *args) {
|
|||
|
||||
qDebug() << "Killed" << *node << "\n";
|
||||
|
||||
nodeList->notifyHooksOfKilledNode(&*node);
|
||||
|
||||
node->setAlive(false);
|
||||
}
|
||||
}
|
||||
|
@ -619,3 +623,30 @@ void NodeListIterator::skipDeadAndStopIncrement() {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
void NodeList::addHook(NodeListHook* hook) {
|
||||
_hooks.push_back(hook);
|
||||
}
|
||||
|
||||
void NodeList::removeHook(NodeListHook* hook) {
|
||||
for (int i = 0; i < _hooks.size(); i++) {
|
||||
if (_hooks[i] == hook) {
|
||||
_hooks.erase(_hooks.begin() + i);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void NodeList::notifyHooksOfAddedNode(Node* node) {
|
||||
for (int i = 0; i < _hooks.size(); i++) {
|
||||
printf("NodeList::notifyHooksOfAddedNode() i=%d\n", i);
|
||||
_hooks[i]->nodeAdded(node);
|
||||
}
|
||||
}
|
||||
|
||||
void NodeList::notifyHooksOfKilledNode(Node* node) {
|
||||
for (int i = 0; i < _hooks.size(); i++) {
|
||||
printf("NodeList::notifyHooksOfKilledNode() i=%d\n", i);
|
||||
_hooks[i]->nodeKilled(node);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -45,6 +45,14 @@ const int UNKNOWN_NODE_ID = -1;
|
|||
|
||||
class NodeListIterator;
|
||||
|
||||
// Callers who want to hook add/kill callbacks should implement this class
|
||||
class NodeListHook {
|
||||
public:
|
||||
virtual void nodeAdded(Node* node) = 0;
|
||||
virtual void nodeKilled(Node* node) = 0;
|
||||
};
|
||||
|
||||
|
||||
class NodeList {
|
||||
public:
|
||||
static NodeList* createInstance(char ownerType, unsigned int socketListenPort = NODE_SOCKET_LISTEN_PORT);
|
||||
|
@ -66,7 +74,7 @@ public:
|
|||
void setDomainIPToLocalhost();
|
||||
|
||||
uint16_t getLastNodeID() const { return _lastNodeID; }
|
||||
void increaseNodeID() { ++_lastNodeID; }
|
||||
void increaseNodeID() { (++_lastNodeID == UNKNOWN_NODE_ID) ? ++_lastNodeID : _lastNodeID; }
|
||||
|
||||
uint16_t getOwnerID() const { return _ownerID; }
|
||||
void setOwnerID(uint16_t ownerID) { _ownerID = ownerID; }
|
||||
|
@ -111,6 +119,12 @@ public:
|
|||
void saveData(QSettings* settings);
|
||||
|
||||
friend class NodeListIterator;
|
||||
|
||||
void addHook(NodeListHook* hook);
|
||||
void removeHook(NodeListHook* hook);
|
||||
void notifyHooksOfAddedNode(Node* node);
|
||||
void notifyHooksOfKilledNode(Node* node);
|
||||
|
||||
private:
|
||||
static NodeList* _sharedInstance;
|
||||
|
||||
|
@ -136,6 +150,8 @@ private:
|
|||
|
||||
void handlePingReply(sockaddr *nodeAddress);
|
||||
void timePingReply(sockaddr *nodeAddress, unsigned char *packetData);
|
||||
|
||||
std::vector<NodeListHook*> _hooks;
|
||||
};
|
||||
|
||||
class NodeListIterator : public std::iterator<std::input_iterator_tag, Node> {
|
||||
|
|
|
@ -115,6 +115,29 @@ unsigned char * childOctalCode(unsigned char * parentOctalCode, char childNumber
|
|||
return newCode;
|
||||
}
|
||||
|
||||
void voxelDetailsForCode(unsigned char * octalCode, VoxelPositionSize& voxelPositionSize) {
|
||||
float output[3];
|
||||
memset(&output[0], 0, 3 * sizeof(float));
|
||||
float currentScale = 1.0;
|
||||
|
||||
if (octalCode) {
|
||||
for (int i = 0; i < numberOfThreeBitSectionsInCode(octalCode); i++) {
|
||||
currentScale *= 0.5;
|
||||
int sectionIndex = sectionValue(octalCode + 1 + (BITS_IN_OCTAL * i / BITS_IN_BYTE),
|
||||
(BITS_IN_OCTAL * i) % BITS_IN_BYTE);
|
||||
|
||||
for (int j = 0; j < BITS_IN_OCTAL; j++) {
|
||||
output[j] += currentScale * (int)oneAtBit(sectionIndex, (BITS_IN_BYTE - BITS_IN_OCTAL) + j);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
voxelPositionSize.x = output[0];
|
||||
voxelPositionSize.y = output[1];
|
||||
voxelPositionSize.z = output[2];
|
||||
voxelPositionSize.s = currentScale;
|
||||
}
|
||||
|
||||
void copyFirstVertexForCode(unsigned char * octalCode, float* output) {
|
||||
memset(output, 0, 3 * sizeof(float));
|
||||
|
||||
|
|
|
@ -36,6 +36,11 @@ bool isAncestorOf(unsigned char* possibleAncestor, unsigned char* possibleDescen
|
|||
float * firstVertexForCode(unsigned char * octalCode);
|
||||
void copyFirstVertexForCode(unsigned char * octalCode, float* output);
|
||||
|
||||
struct VoxelPositionSize {
|
||||
float x, y, z, s;
|
||||
};
|
||||
void voxelDetailsForCode(unsigned char * octalCode, VoxelPositionSize& voxelPositionSize);
|
||||
|
||||
typedef enum {
|
||||
ILLEGAL_CODE = -2,
|
||||
LESS_THAN = -1,
|
||||
|
|
|
@ -20,11 +20,13 @@ PACKET_VERSION versionForPacketType(PACKET_TYPE type) {
|
|||
return 1;
|
||||
|
||||
case PACKET_TYPE_HEAD_DATA:
|
||||
return 3;
|
||||
return 4;
|
||||
|
||||
case PACKET_TYPE_AVATAR_FACE_VIDEO:
|
||||
return 1;
|
||||
|
||||
|
||||
case PACKET_TYPE_VOXEL_STATS:
|
||||
return 1;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -215,7 +215,6 @@ bool createVoxelEditMessage(unsigned char command, short int sequence,
|
|||
|
||||
bool success = true; // assume the best
|
||||
int messageSize = MAXIMUM_EDIT_VOXEL_MESSAGE_SIZE; // just a guess for now
|
||||
int actualMessageSize = 3;
|
||||
unsigned char* messageBuffer = new unsigned char[messageSize];
|
||||
|
||||
int numBytesPacketHeader = populateTypeAndVersion(messageBuffer, command);
|
||||
|
@ -223,6 +222,7 @@ bool createVoxelEditMessage(unsigned char command, short int sequence,
|
|||
|
||||
*sequenceAt = sequence;
|
||||
unsigned char* copyAt = &messageBuffer[numBytesPacketHeader + sizeof(sequence)];
|
||||
int actualMessageSize = numBytesPacketHeader + sizeof(sequence);
|
||||
|
||||
for (int i = 0; i < voxelCount && success; i++) {
|
||||
// get the coded voxel
|
||||
|
@ -232,7 +232,7 @@ bool createVoxelEditMessage(unsigned char command, short int sequence,
|
|||
int lengthOfVoxelData = bytesRequiredForCodeLength(*voxelData)+SIZE_OF_COLOR_DATA;
|
||||
|
||||
// make sure we have room to copy this voxel
|
||||
if (actualMessageSize+lengthOfVoxelData > MAXIMUM_EDIT_VOXEL_MESSAGE_SIZE) {
|
||||
if (actualMessageSize + lengthOfVoxelData > MAXIMUM_EDIT_VOXEL_MESSAGE_SIZE) {
|
||||
success = false;
|
||||
} else {
|
||||
// add it to our message
|
||||
|
|
|
@ -30,6 +30,8 @@ public:
|
|||
|
||||
bool writeToFile(const char* filename);
|
||||
bool readFromFile(const char* filename);
|
||||
|
||||
unsigned char* getRootOctalCode() const { return _rootOctalCode; }
|
||||
|
||||
private:
|
||||
void clear();
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
|
||||
#include <limits.h>
|
||||
#include <OctalCode.h>
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
// this is where the coordinate system is represented
|
||||
const glm::vec3 IDENTITY_RIGHT = glm::vec3( 1.0f, 0.0f, 0.0f);
|
||||
|
|
|
@ -22,9 +22,17 @@ VoxelSceneStats::VoxelSceneStats() :
|
|||
reset();
|
||||
_isReadyToSend = false;
|
||||
_isStarted = false;
|
||||
_jurisdictionRoot = NULL;
|
||||
}
|
||||
|
||||
void VoxelSceneStats::sceneStarted(bool isFullScene, bool isMoving, VoxelNode* root) {
|
||||
VoxelSceneStats::~VoxelSceneStats() {
|
||||
if (_jurisdictionRoot) {
|
||||
delete[] _jurisdictionRoot;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void VoxelSceneStats::sceneStarted(bool isFullScene, bool isMoving, VoxelNode* root, JurisdictionMap* jurisdictionMap) {
|
||||
reset(); // resets packet and voxel stats
|
||||
_isStarted = true;
|
||||
_start = usecTimestampNow();
|
||||
|
@ -34,6 +42,19 @@ void VoxelSceneStats::sceneStarted(bool isFullScene, bool isMoving, VoxelNode* r
|
|||
|
||||
_isFullScene = isFullScene;
|
||||
_isMoving = isMoving;
|
||||
|
||||
if (_jurisdictionRoot) {
|
||||
delete[] _jurisdictionRoot;
|
||||
_jurisdictionRoot = NULL;
|
||||
}
|
||||
if (jurisdictionMap) {
|
||||
unsigned char* jurisdictionRoot = jurisdictionMap->getRootOctalCode();
|
||||
if (jurisdictionRoot) {
|
||||
int bytes = bytesRequiredForCodeLength(numberOfThreeBitSectionsInCode(jurisdictionRoot));
|
||||
_jurisdictionRoot = new unsigned char[bytes];
|
||||
memcpy(_jurisdictionRoot, jurisdictionRoot, bytes);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void VoxelSceneStats::sceneCompleted() {
|
||||
|
@ -273,6 +294,20 @@ int VoxelSceneStats::packIntoMessage(unsigned char* destinationBuffer, int avail
|
|||
destinationBuffer += sizeof(_existsInPacketBitsWritten);
|
||||
memcpy(destinationBuffer, &_treesRemoved, sizeof(_treesRemoved));
|
||||
destinationBuffer += sizeof(_treesRemoved);
|
||||
|
||||
// add the root jurisdiction
|
||||
if (_jurisdictionRoot) {
|
||||
// copy the
|
||||
int bytes = bytesRequiredForCodeLength(numberOfThreeBitSectionsInCode(_jurisdictionRoot));
|
||||
memcpy(destinationBuffer, &bytes, sizeof(bytes));
|
||||
destinationBuffer += sizeof(bytes);
|
||||
memcpy(destinationBuffer, _jurisdictionRoot, bytes);
|
||||
destinationBuffer += bytes;
|
||||
} else {
|
||||
int bytes = 0;
|
||||
memcpy(destinationBuffer, &bytes, sizeof(bytes));
|
||||
destinationBuffer += sizeof(bytes);
|
||||
}
|
||||
|
||||
return destinationBuffer - bufferStart; // includes header!
|
||||
}
|
||||
|
@ -363,6 +398,19 @@ int VoxelSceneStats::unpackFromMessage(unsigned char* sourceBuffer, int availabl
|
|||
sourceBuffer += sizeof(_existsInPacketBitsWritten);
|
||||
memcpy(&_treesRemoved, sourceBuffer, sizeof(_treesRemoved));
|
||||
sourceBuffer += sizeof(_treesRemoved);
|
||||
|
||||
// read the root jurisdiction
|
||||
int bytes = 0;
|
||||
memcpy(&bytes, sourceBuffer, sizeof(bytes));
|
||||
sourceBuffer += sizeof(bytes);
|
||||
|
||||
if (bytes == 0) {
|
||||
_jurisdictionRoot = NULL;
|
||||
} else {
|
||||
_jurisdictionRoot = new unsigned char[bytes];
|
||||
memcpy(_jurisdictionRoot, sourceBuffer, bytes);
|
||||
sourceBuffer += bytes;
|
||||
}
|
||||
|
||||
// running averages
|
||||
_elapsedAverage.updateAverage((float)_elapsed);
|
||||
|
|
|
@ -12,14 +12,16 @@
|
|||
|
||||
#include <stdint.h>
|
||||
#include <NodeList.h>
|
||||
#include "JurisdictionMap.h"
|
||||
|
||||
class VoxelNode;
|
||||
|
||||
class VoxelSceneStats {
|
||||
public:
|
||||
VoxelSceneStats();
|
||||
~VoxelSceneStats();
|
||||
void reset();
|
||||
void sceneStarted(bool fullScene, bool moving, VoxelNode* root);
|
||||
void sceneStarted(bool fullScene, bool moving, VoxelNode* root, JurisdictionMap* jurisdictionMap);
|
||||
void sceneCompleted();
|
||||
|
||||
void printDebugDetails();
|
||||
|
@ -78,6 +80,9 @@ public:
|
|||
ItemInfo& getItemInfo(int item) { return _ITEMS[item]; };
|
||||
char* getItemValue(int item);
|
||||
|
||||
unsigned char* getJurisdictionRoot() const { return _jurisdictionRoot; }
|
||||
|
||||
|
||||
private:
|
||||
bool _isReadyToSend;
|
||||
unsigned char _statsMessage[MAX_PACKET_SIZE];
|
||||
|
@ -165,6 +170,8 @@ private:
|
|||
static ItemInfo _ITEMS[];
|
||||
static int const MAX_ITEM_VALUE_LENGTH = 128;
|
||||
char _itemValueBuffer[MAX_ITEM_VALUE_LENGTH];
|
||||
|
||||
unsigned char* _jurisdictionRoot;
|
||||
};
|
||||
|
||||
#endif /* defined(__hifi__VoxelSceneStats__) */
|
||||
|
|
|
@ -49,6 +49,7 @@ const float MAX_CUBE = 0.05f;
|
|||
const int VOXEL_SEND_INTERVAL_USECS = 17 * 1000; // approximately 60fps
|
||||
int PACKETS_PER_CLIENT_PER_INTERVAL = 20;
|
||||
const int SENDING_TIME_TO_SPARE = 5 * 1000; // usec of sending interval to spare for calculating voxels
|
||||
const int INTERVALS_PER_SECOND = 1000 * 1000 / VOXEL_SEND_INTERVAL_USECS;
|
||||
|
||||
const int MAX_VOXEL_TREE_DEPTH_LEVELS = 4;
|
||||
|
||||
|
@ -64,6 +65,7 @@ bool debugVoxelSending = false;
|
|||
bool shouldShowAnimationDebug = false;
|
||||
bool displayVoxelStats = false;
|
||||
bool debugVoxelReceiving = false;
|
||||
bool sendEnvironments = true;
|
||||
|
||||
EnvironmentData environmentData[3];
|
||||
|
||||
|
@ -255,7 +257,7 @@ void deepestLevelVoxelDistributor(NodeList* nodeList,
|
|||
|
||||
// start tracking our stats
|
||||
bool isFullScene = (!viewFrustumChanged || !nodeData->getWantDelta()) && nodeData->getViewFrustumJustStoppedChanging();
|
||||
nodeData->stats.sceneStarted(isFullScene, viewFrustumChanged, ::serverTree.rootNode);
|
||||
nodeData->stats.sceneStarted(isFullScene, viewFrustumChanged, ::serverTree.rootNode, ::jurisdiction);
|
||||
}
|
||||
|
||||
// If we have something in our nodeBag, then turn them into packets and send them out...
|
||||
|
@ -265,7 +267,7 @@ void deepestLevelVoxelDistributor(NodeList* nodeList,
|
|||
int packetsSentThisInterval = 0;
|
||||
uint64_t start = usecTimestampNow();
|
||||
|
||||
bool shouldSendEnvironments = shouldDo(ENVIRONMENT_SEND_INTERVAL_USECS, VOXEL_SEND_INTERVAL_USECS);
|
||||
bool shouldSendEnvironments = ::sendEnvironments && shouldDo(ENVIRONMENT_SEND_INTERVAL_USECS, VOXEL_SEND_INTERVAL_USECS);
|
||||
while (packetsSentThisInterval < PACKETS_PER_CLIENT_PER_INTERVAL - (shouldSendEnvironments ? 1 : 0)) {
|
||||
// Check to see if we're taking too long, and if so bail early...
|
||||
uint64_t now = usecTimestampNow();
|
||||
|
@ -473,6 +475,15 @@ int main(int argc, const char * argv[]) {
|
|||
jurisdiction = new JurisdictionMap(jurisdictionRoot, jurisdictionEndNodes);
|
||||
}
|
||||
}
|
||||
|
||||
// should we send environments? Default is yes, but this command line suppresses sending
|
||||
const char* DONT_SEND_ENVIRONMENTS = "--dontSendEnvironments";
|
||||
bool dontSendEnvironments = cmdOptionExists(argc, argv, DONT_SEND_ENVIRONMENTS);
|
||||
if (dontSendEnvironments) {
|
||||
printf("Sending environments suppressed...\n");
|
||||
::sendEnvironments = false;
|
||||
}
|
||||
printf("Sending environments=%s\n", debug::valueOf(::sendEnvironments));
|
||||
|
||||
NodeList* nodeList = NodeList::createInstance(NODE_TYPE_VOXEL_SERVER, listenPort);
|
||||
setvbuf(stdout, NULL, _IOLBF, 0);
|
||||
|
@ -562,7 +573,7 @@ int main(int argc, const char * argv[]) {
|
|||
const char* PACKETS_PER_SECOND = "--packetsPerSecond";
|
||||
const char* packetsPerSecond = getCmdOption(argc, argv, PACKETS_PER_SECOND);
|
||||
if (packetsPerSecond) {
|
||||
PACKETS_PER_CLIENT_PER_INTERVAL = atoi(packetsPerSecond)/10;
|
||||
PACKETS_PER_CLIENT_PER_INTERVAL = atoi(packetsPerSecond)/INTERVALS_PER_SECOND;
|
||||
if (PACKETS_PER_CLIENT_PER_INTERVAL < 1) {
|
||||
PACKETS_PER_CLIENT_PER_INTERVAL = 1;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue